#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/time.h>
#include <sys/ubc.h>
#include <sys/quota.h>
#include <sys/kdebug.h>
#include <kern/locks.h>
#include <miscfs/specfs/specdev.h>
#include <miscfs/fifofs/fifo.h>
#include <hfs/hfs.h>
#include <hfs/hfs_catalog.h>
#include <hfs/hfs_cnode.h>
#include <hfs/hfs_quota.h>
extern int prtactive;
extern lck_attr_t * hfs_lock_attr;
extern lck_grp_t * hfs_mutex_group;
extern lck_grp_t * hfs_rwlock_group;
static int hfs_filedone(struct vnode *vp, vfs_context_t context);
static void hfs_reclaim_cnode(struct cnode *);
static int hfs_isordered(struct cnode *, struct cnode *);
__private_extern__
int
hfs_vnop_inactive(struct vnop_inactive_args *ap)
{
struct vnode *vp = ap->a_vp;
struct cnode *cp;
struct hfsmount *hfsmp = VTOHFS(vp);
struct proc *p = vfs_context_proc(ap->a_context);
int error = 0;
int recycle = 0;
int forkcount = 0;
int truncated = 0;
int started_tr = 0;
int took_trunc_lock = 0;
cat_cookie_t cookie;
int cat_reserve = 0;
int lockflags;
enum vtype v_type;
v_type = vnode_vtype(vp);
cp = VTOC(vp);
if ((hfsmp->hfs_flags & HFS_READ_ONLY) || vnode_issystem(vp) ||
(hfsmp->hfs_freezing_proc == p)) {
return (0);
}
if (cp->c_mode == 0) {
vnode_recycle(vp);
return (0);
}
if ((v_type == VREG || v_type == VLNK)) {
hfs_lock_truncate(cp, TRUE);
took_trunc_lock = 1;
}
(void) hfs_lock(cp, HFS_FORCE_LOCK);
if ((v_type == VREG || v_type == VLNK) &&
(cp->c_flag & C_DELETED) &&
(VTOF(vp)->ff_blocks != 0)) {
hfs_unlock(cp);
ubc_setsize(vp, 0);
(void) hfs_lock(cp, HFS_FORCE_LOCK);
}
if (v_type == VREG && !ISSET(cp->c_flag, C_DELETED) && VTOF(vp)->ff_blocks) {
hfs_filedone(vp, ap->a_context);
}
if (v_type == VDIR) {
hfs_reldirhints(cp, 0);
if (cp->c_flag & C_HARDLINK)
hfs_relorigins(cp);
}
if (cp->c_datafork)
++forkcount;
if (cp->c_rsrcfork)
++forkcount;
if ((v_type == VREG || v_type == VLNK) && (cp->c_flag & C_DELETED)) {
if (VTOF(vp)->ff_blocks != 0) {
error = hfs_truncate(vp, (off_t)0, IO_NDELAY, 1, ap->a_context);
if (error)
goto out;
truncated = 1;
}
recycle = 1;
if ((cp->c_blocks > 0) && (forkcount == 1) && (vp != cp->c_rsrc_vp)) {
struct vnode *rvp = NULLVP;
error = hfs_vgetrsrc(hfsmp, vp, &rvp, FALSE);
if (error)
goto out;
cp->c_flag |= C_NEED_RVNODE_PUT | C_NEED_RSRC_SETSIZE;
error = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 1, ap->a_context);
if (error)
goto out;
vnode_recycle(rvp);
}
}
if ((cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0 && (cp->c_flag & C_DELETED)) {
hfs_removeallattr(hfsmp, cp->c_fileid);
}
if ((cp->c_flag & C_DELETED) && (forkcount <= 1)) {
hfs_chashremove(cp);
cp->c_flag |= C_NOEXISTS; cp->c_rdev = 0;
if (started_tr == 0) {
if (hfs_start_transaction(hfsmp) != 0) {
error = EINVAL;
goto out;
}
started_tr = 1;
}
if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) {
goto out;
}
cat_reserve = 1;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
if (cp->c_blocks > 0)
printf("hfs_inactive: attempting to delete a non-empty file!");
cat_releasedesc(&cp->c_desc);
error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
if (error && truncated && (error != ENXIO))
printf("hfs_inactive: couldn't delete a truncated file!");
if (error == 0) {
hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries--;
if (vnode_isdir(vp)) {
DEC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
}
(void)cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
&hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
}
hfs_systemfile_unlock(hfsmp, lockflags);
if (error)
goto out;
#if QUOTA
if (hfsmp->hfs_flags & HFS_QUOTAS)
(void)hfs_chkiq(cp, -1, NOCRED, 0);
#endif
cp->c_mode = 0;
cp->c_flag &= ~C_DELETED;
cp->c_touch_chgtime = TRUE;
cp->c_touch_modtime = TRUE;
if (error == 0)
hfs_volupdate(hfsmp, (v_type == VDIR) ? VOL_RMDIR : VOL_RMFILE, 0);
}
if ((cp->c_flag & C_MODIFIED) ||
cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime) {
hfs_update(vp, 0);
}
out:
if (cat_reserve)
cat_postflight(hfsmp, &cookie, p);
if (started_tr) {
hfs_end_transaction(hfsmp);
started_tr = 0;
}
hfs_unlock(cp);
if (took_trunc_lock)
hfs_unlock_truncate(cp, TRUE);
if (cp->c_mode == 0 || recycle)
vnode_recycle(vp);
return (error);
}
static int
hfs_filedone(struct vnode *vp, vfs_context_t context)
{
struct cnode *cp;
struct filefork *fp;
struct hfsmount *hfsmp;
off_t leof;
u_long blks, blocksize;
cp = VTOC(vp);
fp = VTOF(vp);
hfsmp = VTOHFS(vp);
leof = fp->ff_size;
if ((hfsmp->hfs_flags & HFS_READ_ONLY) || (fp->ff_blocks == 0))
return (0);
hfs_unlock(cp);
(void) cluster_push(vp, IO_CLOSE);
hfs_lock(cp, HFS_FORCE_LOCK);
while (!CIRCLEQ_EMPTY(&fp->ff_invalidranges)) {
struct rl_entry *invalid_range = CIRCLEQ_FIRST(&fp->ff_invalidranges);
off_t start = invalid_range->rl_start;
off_t end = invalid_range->rl_end;
rl_remove(start, end, &fp->ff_invalidranges);
hfs_unlock(cp);
(void) cluster_write(vp, (struct uio *) 0,
leof, end + 1, start, (off_t)0,
IO_HEADZEROFILL | IO_NOZERODIRTY | IO_NOCACHE);
hfs_lock(cp, HFS_FORCE_LOCK);
cp->c_flag |= C_MODIFIED;
}
cp->c_flag &= ~C_ZFWANTSYNC;
cp->c_zftimeout = 0;
blocksize = VTOVCB(vp)->blockSize;
blks = leof / blocksize;
if (((off_t)blks * (off_t)blocksize) != leof)
blks++;
if (blks < fp->ff_blocks)
(void) hfs_truncate(vp, leof, IO_NDELAY, 0, context);
hfs_unlock(cp);
(void) cluster_push(vp, IO_CLOSE);
hfs_lock(cp, HFS_FORCE_LOCK);
if (cp->c_flag & C_MODIFIED) {
hfs_update(vp, 0);
}
return (0);
}
__private_extern__
int
hfs_vnop_reclaim(struct vnop_reclaim_args *ap)
{
struct vnode *vp = ap->a_vp;
struct cnode *cp;
struct filefork *fp = NULL;
struct filefork *altfp = NULL;
int reclaim_cnode = 0;
(void) hfs_lock(VTOC(vp), HFS_FORCE_LOCK);
cp = VTOC(vp);
if (!vnode_isdir(vp) &&
!vnode_issystem(vp) &&
!(cp->c_flag & (C_DELETED | C_NOEXISTS)) ) {
(void) hfs_addhotfile(vp);
}
vnode_removefsref(vp);
if (cp->c_vp == vp) {
fp = cp->c_datafork;
altfp = cp->c_rsrcfork;
cp->c_datafork = NULL;
cp->c_vp = NULL;
} else if (cp->c_rsrc_vp == vp) {
fp = cp->c_rsrcfork;
altfp = cp->c_datafork;
cp->c_rsrcfork = NULL;
cp->c_rsrc_vp = NULL;
} else {
panic("hfs_vnop_reclaim: vp points to wrong cnode\n");
}
if (altfp == NULL) {
if (hfs_chashremove(cp) == 0)
reclaim_cnode = 1;
if (vnode_isdir(vp)) {
hfs_reldirhints(cp, 0);
}
}
if (fp) {
if (vnode_islnk(vp) && (fp->ff_symlinkptr != NULL)) {
FREE(fp->ff_symlinkptr, M_TEMP);
}
FREE_ZONE(fp, sizeof(struct filefork), M_HFSFORK);
}
if (reclaim_cnode) {
hfs_chashwakeup(cp, H_ALLOC | H_TRANSIT);
hfs_reclaim_cnode(cp);
} else {
hfs_unlock(cp);
}
vnode_clearfsnode(vp);
return (0);
}
extern int (**hfs_vnodeop_p) (void *);
extern int (**hfs_specop_p) (void *);
#if FIFO
extern int (**hfs_fifoop_p) (void *);
#endif
__private_extern__
int
hfs_getnewvnode(
struct hfsmount *hfsmp,
struct vnode *dvp,
struct componentname *cnp,
struct cat_desc *descp,
int flags,
struct cat_attr *attrp,
struct cat_fork *forkp,
struct vnode **vpp)
{
struct mount *mp = HFSTOVFS(hfsmp);
struct vnode *vp = NULL;
struct vnode **cvpp;
struct vnode *tvp = NULLVP;
struct cnode *cp = NULL;
struct filefork *fp = NULL;
int retval;
int issystemfile;
int wantrsrc;
struct vnode_fsparam vfsp;
enum vtype vtype;
#if QUOTA
int i;
#endif
if (attrp->ca_fileid == 0) {
*vpp = NULL;
return (ENOENT);
}
#if !FIFO
if (IFTOVT(attrp->ca_mode) == VFIFO) {
*vpp = NULL;
return (ENOTSUP);
}
#endif
vtype = IFTOVT(attrp->ca_mode);
issystemfile = (descp->cd_flags & CD_ISMETA) && (vtype == VREG);
wantrsrc = flags & GNV_WANTRSRC;
#ifdef HFS_CHECK_LOCK_ORDER
if ((dvp != NULL) &&
(flags & (GNV_CREATE | GNV_SKIPLOCK)) == 0 &&
VTOC(dvp)->c_lockowner == current_thread()) {
panic("hfs_getnewvnode: unexpected hold of parent cnode %p", VTOC(dvp));
}
#endif
cp = hfs_chash_getcnode(hfsmp->hfs_raw_dev, attrp->ca_fileid, vpp, wantrsrc, (flags & GNV_SKIPLOCK));
if (cp == NULL) {
return (ENOENT);
}
if ((cp->c_flag & C_HARDLINK) && descp->cd_nameptr && descp->cd_namelen > 0) {
replace_desc(cp, descp);
}
if (*vpp != NULL)
return (0);
if (ISSET(cp->c_hflag, H_ALLOC)) {
lck_rw_init(&cp->c_truncatelock, hfs_rwlock_group, hfs_lock_attr);
if (!(flags & GNV_CREATE) &&
!hfs_valid_cnode(hfsmp, dvp, (wantrsrc ? NULL : cnp), cp->c_fileid)) {
hfs_chash_abort(cp);
hfs_reclaim_cnode(cp);
*vpp = NULL;
return (ENOENT);
}
bcopy(attrp, &cp->c_attr, sizeof(struct cat_attr));
bcopy(descp, &cp->c_desc, sizeof(struct cat_desc));
descp->cd_namelen = 0;
descp->cd_nameptr = NULL;
descp->cd_flags &= ~CD_HASBUF;
if ((vtype == VREG || vtype == VDIR) &&
((descp->cd_cnid != attrp->ca_fileid) ||
(attrp->ca_recflags & kHFSHasLinkChainMask))) {
cp->c_flag |= C_HARDLINK;
}
if ((hfsmp->hfs_flags & HFS_FOLDERCOUNT) &&
(vtype == VDIR) &&
!(attrp->ca_recflags & kHFSHasFolderCountMask) &&
(cp->c_attr.ca_linkcount > 1)) {
if (cp->c_attr.ca_entries == 0)
cp->c_attr.ca_dircount = 0;
else
cp->c_attr.ca_dircount = cp->c_attr.ca_linkcount - 2;
cp->c_attr.ca_linkcount = 1;
cp->c_attr.ca_recflags |= kHFSHasFolderCountMask;
if ( !(hfsmp->hfs_flags & HFS_READ_ONLY) )
cp->c_flag |= C_MODIFIED;
}
#if QUOTA
if (hfsmp->hfs_flags & HFS_QUOTAS) {
for (i = 0; i < MAXQUOTAS; i++)
cp->c_dquot[i] = NODQUOT;
}
#endif
}
if (vtype == VDIR) {
if (cp->c_vp != NULL)
panic("hfs_getnewvnode: orphaned vnode (data)");
cvpp = &cp->c_vp;
} else {
if (forkp && attrp->ca_blocks < forkp->cf_blocks)
panic("hfs_getnewvnode: bad ca_blocks (too small)");
MALLOC_ZONE(fp, struct filefork *, sizeof(struct filefork),
M_HFSFORK, M_WAITOK);
fp->ff_cp = cp;
if (forkp)
bcopy(forkp, &fp->ff_data, sizeof(struct cat_fork));
else
bzero(&fp->ff_data, sizeof(struct cat_fork));
rl_init(&fp->ff_invalidranges);
fp->ff_sysfileinfo = 0;
if (wantrsrc) {
if (cp->c_rsrcfork != NULL)
panic("hfs_getnewvnode: orphaned rsrc fork");
if (cp->c_rsrc_vp != NULL)
panic("hfs_getnewvnode: orphaned vnode (rsrc)");
cp->c_rsrcfork = fp;
cvpp = &cp->c_rsrc_vp;
if ( (tvp = cp->c_vp) != NULLVP )
cp->c_flag |= C_NEED_DVNODE_PUT;
} else {
if (cp->c_datafork != NULL)
panic("hfs_getnewvnode: orphaned data fork");
if (cp->c_vp != NULL)
panic("hfs_getnewvnode: orphaned vnode (data)");
cp->c_datafork = fp;
cvpp = &cp->c_vp;
if ( (tvp = cp->c_rsrc_vp) != NULLVP)
cp->c_flag |= C_NEED_RVNODE_PUT;
}
}
if (tvp != NULLVP) {
if ( vnode_get(tvp) != 0)
cp->c_flag &= ~(C_NEED_RVNODE_PUT | C_NEED_DVNODE_PUT);
}
vfsp.vnfs_mp = mp;
vfsp.vnfs_vtype = vtype;
vfsp.vnfs_str = "hfs";
if ((cp->c_flag & C_HARDLINK) && (vtype == VDIR)) {
vfsp.vnfs_dvp = NULL;
vfsp.vnfs_cnp = NULL;
} else {
vfsp.vnfs_dvp = dvp;
vfsp.vnfs_cnp = cnp;
}
vfsp.vnfs_fsnode = cp;
#if FIFO
if (vtype == VFIFO )
vfsp.vnfs_vops = hfs_fifoop_p;
else
#endif
if (vtype == VBLK || vtype == VCHR)
vfsp.vnfs_vops = hfs_specop_p;
else
vfsp.vnfs_vops = hfs_vnodeop_p;
if (vtype == VBLK || vtype == VCHR)
vfsp.vnfs_rdev = attrp->ca_rdev;
else
vfsp.vnfs_rdev = 0;
if (forkp)
vfsp.vnfs_filesize = forkp->cf_size;
else
vfsp.vnfs_filesize = 0;
vfsp.vnfs_flags = VNFS_ADDFSREF;
if (dvp == NULLVP || cnp == NULL || !(cnp->cn_flags & MAKEENTRY))
vfsp.vnfs_flags |= VNFS_NOCACHE;
vfsp.vnfs_marksystem = issystemfile;
if (descp->cd_cnid == kHFSRootFolderID)
vfsp.vnfs_markroot = 1;
else
vfsp.vnfs_markroot = 0;
if ((retval = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, cvpp))) {
if (fp) {
if (fp == cp->c_datafork)
cp->c_datafork = NULL;
else
cp->c_rsrcfork = NULL;
FREE_ZONE(fp, sizeof(struct filefork), M_HFSFORK);
}
if ((cp->c_vp == NULL) && (cp->c_rsrc_vp == NULL)) {
hfs_chash_abort(cp);
hfs_reclaim_cnode(cp);
} else {
hfs_chashwakeup(cp, H_ALLOC | H_ATTACH);
hfs_unlock(cp);
}
*vpp = NULL;
return (retval);
}
vp = *cvpp;
vnode_settag(vp, VT_HFS);
if (cp->c_flag & C_HARDLINK) {
vnode_setmultipath(vp);
}
hfs_chashwakeup(cp, H_ALLOC | H_ATTACH);
if (!(flags & GNV_CREATE) && (vtype != VDIR) && !issystemfile) {
(void) hfs_removehotfile(vp);
}
*vpp = vp;
return (0);
}
static void
hfs_reclaim_cnode(struct cnode *cp)
{
#if QUOTA
int i;
for (i = 0; i < MAXQUOTAS; i++) {
if (cp->c_dquot[i] != NODQUOT) {
dqreclaim(cp->c_dquot[i]);
cp->c_dquot[i] = NODQUOT;
}
}
#endif
if ((cp->c_desc.cd_flags & CD_HASBUF) && (cp->c_desc.cd_nameptr != 0)) {
const char *nameptr;
nameptr = (const char *) cp->c_desc.cd_nameptr;
cp->c_desc.cd_nameptr = 0;
cp->c_desc.cd_flags &= ~CD_HASBUF;
cp->c_desc.cd_namelen = 0;
vfs_removename(nameptr);
}
lck_rw_destroy(&cp->c_rwlock, hfs_rwlock_group);
lck_rw_destroy(&cp->c_truncatelock, hfs_rwlock_group);
bzero(cp, sizeof(struct cnode));
FREE_ZONE(cp, sizeof(struct cnode), M_HFSNODE);
}
__private_extern__
int
hfs_valid_cnode(struct hfsmount *hfsmp, struct vnode *dvp, struct componentname *cnp, cnid_t cnid)
{
struct cat_attr attr;
struct cat_desc cndesc;
int stillvalid = 0;
int lockflags;
if (cnid < kHFSFirstUserCatalogNodeID)
return (1);
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
if (dvp && cnp) {
bzero(&cndesc, sizeof(cndesc));
cndesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
cndesc.cd_namelen = cnp->cn_namelen;
cndesc.cd_parentcnid = VTOC(dvp)->c_fileid;
cndesc.cd_hint = VTOC(dvp)->c_childhint;
if ((cat_lookup(hfsmp, &cndesc, 0, NULL, &attr, NULL, NULL) == 0) &&
(cnid == attr.ca_fileid)) {
stillvalid = 1;
}
} else {
if (cat_idlookup(hfsmp, cnid, 0, NULL, NULL, NULL) == 0) {
stillvalid = 1;
}
}
hfs_systemfile_unlock(hfsmp, lockflags);
return (stillvalid);
}
__private_extern__
void
hfs_touchtimes(struct hfsmount *hfsmp, struct cnode* cp)
{
if (hfsmp->hfs_flags & HFS_READ_ONLY) {
cp->c_touch_acctime = FALSE;
cp->c_touch_chgtime = FALSE;
cp->c_touch_modtime = FALSE;
}
else if (hfsmp->hfs_flags & HFS_STANDARD) {
cp->c_touch_acctime = FALSE;
}
if (cp->c_touch_acctime) {
if ((vfs_flags(hfsmp->hfs_mp) & MNT_NOATIME) ||
(hfsmp->hfs_freezing_proc != NULL) ||
(hfsmp->hfs_flags & HFS_RESIZE_IN_PROGRESS))
cp->c_touch_acctime = FALSE;
}
if (cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime) {
struct timeval tv;
int touchvol = 0;
microtime(&tv);
if (cp->c_touch_acctime) {
cp->c_atime = tv.tv_sec;
if ((((u_int32_t)cp->c_atime - (u_int32_t)(cp)->c_attr.ca_atimeondisk) >
ATIME_ONDISK_ACCURACY)) {
cp->c_flag |= C_MODIFIED;
}
cp->c_touch_acctime = FALSE;
}
if (cp->c_touch_modtime) {
cp->c_mtime = tv.tv_sec;
cp->c_touch_modtime = FALSE;
cp->c_flag |= C_MODIFIED;
touchvol = 1;
#if 1
if ((hfsmp->hfs_flags & HFS_STANDARD) && gTimeZone.tz_dsttime) {
cp->c_mtime += 3600;
}
#endif
}
if (cp->c_touch_chgtime) {
cp->c_ctime = tv.tv_sec;
cp->c_touch_chgtime = FALSE;
cp->c_flag |= C_MODIFIED;
touchvol = 1;
}
if (touchvol) {
MarkVCBDirty(hfsmp);
HFSTOVCB(hfsmp)->vcbLsMod = tv.tv_sec;
}
}
}
__private_extern__
int
hfs_lock(struct cnode *cp, enum hfslocktype locktype)
{
void * thread = current_thread();
if (cp->c_lockowner == thread) {
if ((cp->c_fileid == kHFSExtentsFileID) ||
(cp->c_fileid == kHFSAllocationFileID)) {
cp->c_syslockcount++;
} else {
panic("hfs_lock: locking against myself!");
}
} else if (locktype == HFS_SHARED_LOCK) {
lck_rw_lock_shared(&cp->c_rwlock);
cp->c_lockowner = HFS_SHARED_OWNER;
} else {
lck_rw_lock_exclusive(&cp->c_rwlock);
cp->c_lockowner = thread;
if ((cp->c_fileid == kHFSExtentsFileID) ||
(cp->c_fileid == kHFSAllocationFileID)) {
cp->c_syslockcount = 1;
}
}
#ifdef HFS_CHECK_LOCK_ORDER
if (!(cp->c_desc.cd_flags & CD_ISMETA) &&
((cp->c_fileid > kHFSFirstUserCatalogNodeID) || (cp->c_fileid == kHFSRootFolderID))) {
vnode_t vp = NULLVP;
if (cp->c_vp != NULLVP && VTOC(cp->c_vp) == cp) {
vp = cp->c_vp;
} else if (cp->c_rsrc_vp != NULLVP && VTOC(cp->c_rsrc_vp) == cp) {
vp = cp->c_rsrc_vp;
}
if (vp != NULLVP) {
struct hfsmount *hfsmp = VTOHFS(vp);
if (hfsmp->jnl && (journal_owner(hfsmp->jnl) == thread)) {
printf("hfs_lock: bad lock order (cnode after journal)\n");
}
if (hfsmp->hfs_catalog_cp && hfsmp->hfs_catalog_cp->c_lockowner == thread) {
panic("hfs_lock: bad lock order (cnode after catalog)");
}
if (hfsmp->hfs_attribute_cp && hfsmp->hfs_attribute_cp->c_lockowner == thread) {
panic("hfs_lock: bad lock order (cnode after attribute)");
}
if (hfsmp->hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == thread) {
panic("hfs_lock: bad lock order (cnode after extents)");
}
}
}
#endif
if ((locktype != HFS_FORCE_LOCK) &&
((cp->c_desc.cd_flags & CD_ISMETA) == 0) &&
(cp->c_flag & C_NOEXISTS)) {
hfs_unlock(cp);
return (ENOENT);
}
return (0);
}
__private_extern__
int
hfs_lockpair(struct cnode *cp1, struct cnode *cp2, enum hfslocktype locktype)
{
struct cnode *first, *last;
int error;
if (cp1 == cp2) {
return hfs_lock(cp1, locktype);
}
if (cp1 < cp2) {
first = cp1;
last = cp2;
} else {
first = cp2;
last = cp1;
}
if ( (error = hfs_lock(first, locktype))) {
return (error);
}
if ( (error = hfs_lock(last, locktype))) {
hfs_unlock(first);
return (error);
}
return (0);
}
static int
hfs_isordered(struct cnode *cp1, struct cnode *cp2)
{
if (cp1 == cp2)
return (0);
if (cp1 == NULL || cp2 == (struct cnode *)0xffffffff)
return (1);
if (cp2 == NULL || cp1 == (struct cnode *)0xffffffff)
return (0);
return (cp1 < cp2);
}
__private_extern__
int
hfs_lockfour(struct cnode *cp1, struct cnode *cp2, struct cnode *cp3,
struct cnode *cp4, enum hfslocktype locktype)
{
struct cnode * a[3];
struct cnode * b[3];
struct cnode * list[4];
struct cnode * tmp;
int i, j, k;
int error;
if (hfs_isordered(cp1, cp2)) {
a[0] = cp1; a[1] = cp2;
} else {
a[0] = cp2; a[1] = cp1;
}
if (hfs_isordered(cp3, cp4)) {
b[0] = cp3; b[1] = cp4;
} else {
b[0] = cp4; b[1] = cp3;
}
a[2] = (struct cnode *)0xffffffff;
b[2] = (struct cnode *)0xffffffff;
for (i = 0, j = 0, k = 0; (i < 2 || j < 2); ) {
tmp = hfs_isordered(a[i], b[j]) ? a[i++] : b[j++];
if (k == 0 || tmp != list[k-1])
list[k++] = tmp;
}
for (i = 0; i < k; ++i) {
if (list[i])
if ((error = hfs_lock(list[i], locktype))) {
while (--i >= 0) {
if (list[i])
hfs_unlock(list[i]);
}
return (error);
}
}
return (0);
}
__private_extern__
void
hfs_unlock(struct cnode *cp)
{
vnode_t rvp = NULLVP;
vnode_t vp = NULLVP;
u_int32_t c_flag;
void *lockowner;
if ((cp->c_fileid == kHFSExtentsFileID) ||
(cp->c_fileid == kHFSAllocationFileID)) {
if (--cp->c_syslockcount > 0) {
return;
}
}
c_flag = cp->c_flag;
cp->c_flag &= ~(C_NEED_DVNODE_PUT | C_NEED_RVNODE_PUT | C_NEED_DATA_SETSIZE | C_NEED_RSRC_SETSIZE);
if (c_flag & (C_NEED_DVNODE_PUT | C_NEED_DATA_SETSIZE)) {
vp = cp->c_vp;
}
if (c_flag & (C_NEED_RVNODE_PUT | C_NEED_RSRC_SETSIZE)) {
rvp = cp->c_rsrc_vp;
}
lockowner = cp->c_lockowner;
if (lockowner == current_thread()) {
cp->c_lockowner = NULL;
lck_rw_unlock_exclusive(&cp->c_rwlock);
} else {
lck_rw_unlock_shared(&cp->c_rwlock);
}
if (vp) {
if (c_flag & C_NEED_DATA_SETSIZE)
ubc_setsize(vp, 0);
if (c_flag & C_NEED_DVNODE_PUT)
vnode_put(vp);
}
if (rvp) {
if (c_flag & C_NEED_RSRC_SETSIZE)
ubc_setsize(rvp, 0);
if (c_flag & C_NEED_RVNODE_PUT)
vnode_put(rvp);
}
}
__private_extern__
void
hfs_unlockpair(struct cnode *cp1, struct cnode *cp2)
{
hfs_unlock(cp1);
if (cp2 != cp1)
hfs_unlock(cp2);
}
__private_extern__
void
hfs_unlockfour(struct cnode *cp1, struct cnode *cp2, struct cnode *cp3, struct cnode *cp4)
{
struct cnode * list[4];
int i, k = 0;
if (cp1) {
hfs_unlock(cp1);
list[k++] = cp1;
}
if (cp2) {
for (i = 0; i < k; ++i) {
if (list[i] == cp2)
goto skip1;
}
hfs_unlock(cp2);
list[k++] = cp2;
}
skip1:
if (cp3) {
for (i = 0; i < k; ++i) {
if (list[i] == cp3)
goto skip2;
}
hfs_unlock(cp3);
list[k++] = cp3;
}
skip2:
if (cp4) {
for (i = 0; i < k; ++i) {
if (list[i] == cp4)
return;
}
hfs_unlock(cp4);
}
}
__private_extern__
void
hfs_lock_truncate(struct cnode *cp, int exclusive)
{
#ifdef HFS_CHECK_LOCK_ORDER
if (cp->c_lockowner == current_thread())
panic("hfs_lock_truncate: cnode %p locked!", cp);
#endif
if (exclusive)
lck_rw_lock_exclusive(&cp->c_truncatelock);
else
lck_rw_lock_shared(&cp->c_truncatelock);
}
__private_extern__
void
hfs_unlock_truncate(struct cnode *cp, int exclusive)
{
if (exclusive) {
lck_rw_unlock_exclusive(&cp->c_truncatelock);
} else {
lck_rw_unlock_shared(&cp->c_truncatelock);
}
}