#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/dirent.h>
#include <sys/stat.h>
#include <sys/buf.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <sys/attr.h>
#include <sys/ubc.h>
#include <sys/utfconv.h>
#include <miscfs/specfs/specdev.h>
#include <miscfs/fifofs/fifo.h>
#include <machine/spl.h>
#include <sys/kdebug.h>
#include "hfs.h"
#include "hfs_lockf.h"
#include "hfs_dbg.h"
#include "hfs_mount.h"
#include "hfscommon/headers/CatalogPrivate.h"
#include "hfscommon/headers/BTreesInternal.h"
#include "hfscommon/headers/FileMgrInternal.h"
#include "hfscommon/headers/HFSUnicodeWrappers.h"
#define OWNERSHIP_ONLY_ATTRS (ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS)
#define MAKE_DELETED_NAME(NAME,FID) \
(void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
extern uid_t console_user;
int (**hfs_vnodeop_p)(void *);
extern void hfs_vhashrem(struct hfsnode *hp);
extern int vinvalbuf_vhash(register struct vnode *vp, int flags, struct ucred *cred, struct proc *p);
extern void hfs_vhashmove( struct hfsnode *hp,UInt32 nodeID);
extern struct vnode * hfs_vhashget(dev_t dev, UInt32 nodeID, UInt8 forkType);
extern OSErr PositionIterator(CatalogIterator *cip, UInt32 offset, BTreeIterator *bip, UInt16 *op);
extern void hfs_name_CatToMeta(CatalogNodeData *nodeData, struct hfsfilemeta *fm);
extern groupmember(gid_t gid, struct ucred *cred);
static int hfs_makenode( int mode,
dev_t rawdev, struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp, struct proc *p);
static void hfs_chid(struct hfsnode *hp, u_int32_t fid, u_int32_t pid, char* name);
static int hfs_write_access(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean considerFlags);
static int hfs_chown( struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, struct proc *p);
static int hfs_chmod( struct vnode *vp, int mode, struct ucred *cred, struct proc *p);
static int hfs_chflags( struct vnode *vp, u_long flags, struct ucred *cred, struct proc *p);
int hfs_cache_lookup();
int hfs_lookup();
int hfs_read();
int hfs_write();
int hfs_ioctl();
int hfs_select();
int hfs_mmap();
int hfs_seek();
int hfs_bmap();
int hfs_strategy();
int hfs_reallocblks();
int hfs_truncate();
int hfs_allocate();
int hfs_pagein();
int hfs_pageout();
int hfs_search();
int hfs_bwrite();
int hfs_link();
int hfs_blktooff();
int hfs_offtoblk();
int hfs_cmap();
static int
hfs_create(ap)
struct vop_create_args *ap;
{
struct proc *p = current_proc();
int retval;
int mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
DBG_FUNC_NAME("create");
DBG_VOP_LOCKS_DECL(2);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);
DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
DBG_VOP_CONT(("\tva_type %d va_mode 0x%x\n",
ap->a_vap->va_type, ap->a_vap->va_mode));
#if HFS_DIAGNOSTIC
DBG_HFS_NODE_CHECK(ap->a_dvp);
DBG_ASSERT(ap->a_dvp->v_type == VDIR);
if(ap->a_vap == NULL) {
panic("NULL attr on create");
}
switch(ap->a_vap->va_type) {
case VDIR:
VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
vput(ap->a_dvp);
DBG_VOP_LOCKS_TEST(EISDIR);
return (EISDIR);
case VREG:
case VLNK:
break;
default:
DBG_ERR(("%s: INVALID va_type: %d, %s, %s\n", funcname, ap->a_vap->va_type, H_NAME(VTOH(ap->a_dvp)), ap->a_cnp->cn_nameptr));
VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
vput(ap->a_dvp);
DBG_VOP_LOCKS_TEST(EINVAL);
return (EINVAL);
}
#endif
retval = hfs_makenode(mode, 0, ap->a_dvp, ap->a_vpp, ap->a_cnp, p);
DBG_VOP_UPDATE_VP(1, *ap->a_vpp);
if (retval != E_NONE) {
DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname, ap->a_cnp->cn_nameptr, H_NAME(VTOH(ap->a_dvp))));
}
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
static int
hfs_mknod(ap)
struct vop_mknod_args *ap;
{
struct vattr *vap = ap->a_vap;
struct vnode **vpp = ap->a_vpp;
struct proc *p = current_proc();
dev_t rawdev = 0;
int error;
if (VTOVCB(ap->a_dvp)->vcbSigWord != kHFSPlusSigWord) {
VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
vput(ap->a_dvp);
return (EOPNOTSUPP);
}
if (vap->va_rdev != VNOVAL) {
rawdev = vap->va_rdev;
}
error = hfs_makenode(MAKEIMODE(vap->va_type, vap->va_mode),
rawdev, ap->a_dvp, vpp, ap->a_cnp, p);
if (error != E_NONE) {
return (error);
}
vput(*vpp);
vgone(*vpp);
*vpp = 0;
return (0);
}
static int
hfs_mkcomplex(ap)
struct vop_mkcomplex_args *ap;
{
int retval = E_NONE;
DBG_FUNC_NAME("make_complex");
DBG_VOP_LOCKS_DECL(2);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
retval = VOP_CREATE(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap);
DBG_VOP_LOCKS_TEST(retval);
return retval;
}
static int
hfs_open(ap)
struct vop_open_args *ap;
{
struct hfsnode *hp = VTOH(ap->a_vp);
int retval = E_NONE;
DBG_FUNC_NAME("open");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_CONT((" "));DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
if (ap->a_vp->v_type == VREG)
{
if ((hp->h_meta->h_pflags & APPEND) &&
(ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
retval = EPERM;
}
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
static int
hfs_close(ap)
struct vop_close_args *ap;
{
register struct vnode *vp = ap->a_vp;
struct hfsnode *hp = VTOH(vp);
struct proc *p = ap->a_p;
FCB *fcb;
struct timeval tv;
off_t leof;
u_long blks, blocksize;
int retval = E_NONE;
DBG_FUNC_NAME("close");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_CONT((" "));DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
simple_lock(&vp->v_interlock);
if (vp->v_usecount > (UBCINFOEXISTS(vp) ? 2 : 1)) {
tv = time;
HFSTIMES(hp, &tv, &tv);
}
simple_unlock(&vp->v_interlock);
if (H_FORKTYPE(hp) == kDirectory || VOP_ISLOCKED(vp)) {
DBG_VOP_LOCKS_TEST(E_NONE);
return E_NONE;
};
fcb = HTOFCB(hp);
leof = fcb->fcbEOF;
if (leof != 0) {
enum vtype our_type = vp->v_type;
u_long our_id = vp->v_id;
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
if (vp->v_type != our_type || vp->v_id != our_id) {
VOP_UNLOCK(vp, 0, p);
DBG_VOP_LOCKS_TEST(E_NONE);
return(E_NONE);
}
blocksize = HTOVCB(hp)->blockSize;
blks = leof / blocksize;
if (((off_t)blks * (off_t)blocksize) != leof)
blks++;
if (((off_t)blks * (off_t)blocksize) < fcb->fcbPLen) {
retval = VOP_TRUNCATE(vp, leof, IO_NDELAY, ap->a_cred, p);
}
cluster_push(vp);
VOP_UNLOCK(vp, 0, p);
}
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
static int
hfs_access(ap)
struct vop_access_args *ap;
{
struct vnode *vp = ap->a_vp;
struct ucred *cred = ap->a_cred;
struct hfsnode *hp = VTOH(vp);
ExtendedVCB *vcb = HTOVCB(hp);
register gid_t *gp;
mode_t mask, mode;
Boolean isHFSPlus;
int retval = E_NONE;
int i;
DBG_FUNC_NAME("access");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
mode = ap->a_mode;
isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord );
if (mode & VWRITE) {
switch (vp->v_type) {
case VDIR:
case VLNK:
case VREG:
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
return (EROFS);
break;
default:
break;
}
}
if ((mode & VWRITE) && (hp->h_meta->h_pflags & IMMUTABLE))
return (EPERM);
if (ap->a_cred->cr_uid == 0) {
retval = 0;
goto Exit;
};
mask = 0;
if (hfs_owner_rights(vp, cred, ap->a_p, false) == 0) {
if (mode & VEXEC)
mask |= S_IXUSR;
if (mode & VREAD)
mask |= S_IRUSR;
if (mode & VWRITE)
mask |= S_IWUSR;
retval = ((hp->h_meta->h_mode & mask) == mask ? 0 : EACCES);
goto Exit;
}
if (! (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS)) {
for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) {
if (hp->h_meta->h_gid == *gp) {
if (mode & VEXEC)
mask |= S_IXGRP;
if (mode & VREAD)
mask |= S_IRGRP;
if (mode & VWRITE)
mask |= S_IWGRP;
retval = ((hp->h_meta->h_mode & mask) == mask ? 0 : EACCES);
goto Exit;
}
};
};
if (mode & VEXEC)
mask |= S_IXOTH;
if (mode & VREAD)
mask |= S_IROTH;
if (mode & VWRITE)
mask |= S_IWOTH;
retval = ((hp->h_meta->h_mode & mask) == mask ? 0 : EACCES);
Exit:
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
static int
hfs_getattr(ap)
struct vop_getattr_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct hfsnode *hp = VTOH(vp);
register struct vattr *vap = ap->a_vap;
struct timeval tv;
DBG_FUNC_NAME("getattr");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_POS);
DBG_HFS_NODE_CHECK(ap->a_vp);
tv = time;
HFSTIMES(hp, &tv, &tv);
vap->va_fsid = H_DEV(hp);
vap->va_fileid = H_FILEID(hp);
vap->va_mode = hp->h_meta->h_mode;
if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
vap->va_uid = (VTOHFS(vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(vp)->hfs_uid;
} else {
vap->va_uid = (hp->h_meta->h_uid == UNKNOWNUID) ? console_user : hp->h_meta->h_uid;
};
vap->va_gid = hp->h_meta->h_gid;
if (vp->v_type == VDIR) {
vap->va_size = hp->h_meta->h_size;
vap->va_bytes = 0;
vap->va_rdev = 0;
vap->va_nlink = hp->h_meta->h_nlink;
if ((H_FILEID(hp) == kRootDirID) &&
(VTOHFS(vp)->hfs_private_metadata_dir != 0)) {
vap->va_size -= AVERAGE_HFSDIRENTRY_SIZE;
vap->va_nlink--;
}
}
else {
vap->va_size = hp->fcbEOF;
vap->va_bytes = hp->h_meta->h_size;
if (vp->v_type == VBLK || vp->v_type == VCHR)
vap->va_rdev = hp->h_meta->h_rdev;
else
vap->va_rdev = 0;
if (hp->h_meta->h_metaflags & IN_DELETED)
vap->va_nlink = 0;
#if HFS_HARDLINKS
else if ((hp->h_meta->h_metaflags & IN_DATANODE) &&
(hp->h_meta->h_nlink > 0))
vap->va_nlink = hp->h_meta->h_nlink;
#endif
else
vap->va_nlink = 1;
}
vap->va_atime.tv_nsec = 0;
vap->va_atime.tv_sec = hp->h_meta->h_atime;
vap->va_mtime.tv_nsec = 0;
vap->va_mtime.tv_sec = hp->h_meta->h_mtime;
vap->va_ctime.tv_nsec = 0;
vap->va_ctime.tv_sec = hp->h_meta->h_ctime;
vap->va_flags = hp->h_meta->h_pflags;
vap->va_gen = 0;
if (vp->v_type == VBLK)
vap->va_blocksize = BLKDEV_IOSIZE;
else if (vp->v_type == VCHR)
vap->va_blocksize = MAXPHYSIO;
else
vap->va_blocksize = VTOVFS(vp)->mnt_stat.f_iosize;
vap->va_type = vp->v_type;
vap->va_filerev = 0;
DBG_VOP_LOCKS_TEST(E_NONE);
return (E_NONE);
}
static int
hfs_setattr(ap)
struct vop_setattr_args *ap;
{
struct vnode *vp = ap->a_vp;
struct hfsnode *hp = VTOH(vp);
struct vattr *vap = ap->a_vap;
struct ucred *cred = ap->a_cred;
struct proc *p = ap->a_p;
struct timeval atimeval, mtimeval;
int retval;
DBG_FUNC_NAME("setattr");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
WRITE_CK(vp, funcname);
DBG_HFS_NODE_CHECK(ap->a_vp);
if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
(vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
(vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
retval = EINVAL;
goto ErrorExit;
}
if (vap->va_flags != VNOVAL) {
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
retval = EROFS;
goto ErrorExit;
};
if ((retval = hfs_chflags(vp, vap->va_flags, cred, p))) {
goto ErrorExit;
};
if (vap->va_flags & (IMMUTABLE | APPEND)) {
retval = 0;
goto ErrorExit;
};
}
if (hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) {
retval = EPERM;
goto ErrorExit;
};
if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
retval = EROFS;
goto ErrorExit;
};
if ((retval = hfs_chown(vp, vap->va_uid, vap->va_gid, cred, p))) {
goto ErrorExit;
};
}
if (vap->va_size != VNOVAL) {
switch (vp->v_type) {
case VDIR:
retval = EISDIR;
goto ErrorExit;
case VLNK:
case VREG:
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
retval = EROFS;
goto ErrorExit;
};
break;
default:
break;
}
if ((retval = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p))) {
goto ErrorExit;
};
}
hp = VTOH(vp);
if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
retval = EROFS;
goto ErrorExit;
};
if (((retval = hfs_owner_rights(vp, cred, p, true)) != 0) &&
((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
(retval = VOP_ACCESS(vp, VWRITE, cred, p)))) {
goto ErrorExit;
};
if (vap->va_atime.tv_sec != VNOVAL)
hp->h_nodeflags |= IN_ACCESS;
if (vap->va_mtime.tv_sec != VNOVAL) {
hp->h_nodeflags |= IN_CHANGE | IN_UPDATE;
if (( VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord ) &&
( H_FILEID(hp) != kRootDirID ) &&
( vap->va_mtime.tv_sec < hp->h_meta->h_crtime ))
hp->h_meta->h_crtime = vap->va_mtime.tv_sec;
}
atimeval.tv_sec = vap->va_atime.tv_sec;
atimeval.tv_usec = 0;
mtimeval.tv_sec = vap->va_mtime.tv_sec;
mtimeval.tv_usec = 0;
if ((retval = VOP_UPDATE(vp, &atimeval, &mtimeval, 1))) {
goto ErrorExit;
};
}
retval = 0;
if (vap->va_mode != (mode_t)VNOVAL) {
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
retval = EROFS;
goto ErrorExit;
};
retval = hfs_chmod(vp, (int)vap->va_mode, cred, p);
};
ErrorExit: ;
DBG_VOP(("hfs_setattr: returning %d...\n", retval));
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
static int
hfs_getattrlist(ap)
struct vop_getattrlist_args *ap;
{
struct vnode *vp = ap->a_vp;
struct hfsnode *hp = VTOH(vp);
struct attrlist *alist = ap->a_alist;
int error = 0;
struct hfsCatalogInfo catInfo;
struct hfsCatalogInfo *catInfoPtr = NULL;
struct timeval tv;
int fixedblocksize;
int attrblocksize;
int attrbufsize;
void *attrbufptr;
void *attrptr;
void *varptr;
u_int32_t fileID;
DBG_FUNC_NAME("getattrlist");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_POS);
DBG_HFS_NODE_CHECK(ap->a_vp);
DBG_VOP(("%s: Common attr:0x%lx, buff size Ox%lX,\n",funcname, (u_long)alist->commonattr,(u_long)ap->a_uio->uio_resid));
DBG_ASSERT(ap->a_uio->uio_rw == UIO_READ);
if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
((alist->commonattr & ~ATTR_CMN_VALIDMASK) != 0) ||
((alist->volattr & ~ATTR_VOL_VALIDMASK) != 0) ||
((alist->dirattr & ~ATTR_DIR_VALIDMASK) != 0) ||
((alist->fileattr & ~ATTR_FILE_VALIDMASK) != 0) ||
((alist->forkattr & ~ATTR_FORK_VALIDMASK) != 0)) {
DBG_ERR(("%s: bad attrlist\n", funcname));
DBG_VOP_LOCKS_TEST(EINVAL);
return EINVAL;
};
if ((alist->volattr != 0) && (((alist->volattr & ATTR_VOL_INFO) == 0) ||
(alist->dirattr != 0) || (alist->fileattr != 0) || (alist->forkattr != 0)
)) {
DBG_ERR(("%s: conflicting information requested\n", funcname));
DBG_VOP_LOCKS_TEST(EINVAL);
return EINVAL;
};
if ((alist->commonattr & (ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST)) ||
(alist->fileattr & (ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST))) {
DBG_ERR(("%s: illegal bits in attlist\n", funcname));
DBG_VOP_LOCKS_TEST(EINVAL);
return EINVAL;
};
if ((alist->volattr) && (H_FILEID(hp) != kRootDirID)) {
DBG_ERR(("%s: not root vnode\n", funcname));
DBG_VOP_LOCKS_TEST(EINVAL);
return EINVAL;
};
tv = time;
HFSTIMES(hp, &tv, &tv);
if ((vp->v_type == VREG) &&
(alist->commonattr & ATTR_CMN_OBJPERMANENTID)) {
if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord) {
error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE, ap->a_p);
error = hfsCreateFileID(VTOVCB(vp), H_DIRID(hp), H_NAME(hp), H_HINT(hp), &fileID);
(void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, ap->a_p);
if (error) {
DBG_VOP_LOCKS_TEST(error);
DBG_ERR(("hfs_getattrlist: error %d on CreateFileIDRef.\n", error));
return error;
};
DBG_ASSERT(fileID == H_FILEID(hp));
};
};
if ((H_FORKTYPE(hp) == kRsrcFork) && (alist->fileattr & HFS_ATTR_FILE_LOOKUPMASK)) {
return (EINVAL);
}
INIT_CATALOGDATA(&catInfo.nodeData, 0);
catInfo.hint = kNoHint;
if (((alist->volattr == 0) && ((alist->commonattr & HFS_ATTR_CMN_LOOKUPMASK) != 0)) ||
((alist->dirattr & HFS_ATTR_DIR_LOOKUPMASK) != 0) ||
((alist->fileattr & HFS_ATTR_FILE_LOOKUPMASK) != 0)) {
error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, ap->a_p);
if (error) goto GetCatalogErr_Exit;
if (alist->volattr != 0) {
error = hfs_getcatalog(VTOVCB(vp), 2, NULL, -1, &catInfo);
} else {
error = hfs_getcatalog(VTOVCB(vp), H_DIRID(hp), H_NAME(hp), -1, &catInfo);
if (error == 0) H_HINT(hp) = catInfo.hint;
};
(void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, ap->a_p);
if ((hp->h_meta->h_usecount > 1) &&
(H_FORKTYPE(hp) == kDataFork) &&
(alist->fileattr & HFS_ATTR_FILE_LOOKUPMASK)) {
struct vnode *sib_vp = NULL;
struct hfsnode *nhp;
struct proc *p = current_proc();
DBG_ASSERT(hp->h_meta->h_siblinghead.cqh_first &&
(hp->h_meta->h_siblinghead.cqh_first != hp->h_meta->h_siblinghead.cqh_last));
DBG_ASSERT(H_FORKTYPE(hp)==kDataFork || H_FORKTYPE(hp)==kRsrcFork);
simple_lock(&hp->h_meta->h_siblinglock);
CIRCLEQ_FOREACH(nhp, &hp->h_meta->h_siblinghead, h_sibling) {
if (nhp == hp)
continue;
sib_vp = HTOV(nhp);
};
simple_unlock(&hp->h_meta->h_siblinglock);
if (vget(sib_vp, LK_EXCLUSIVE | LK_RETRY, p) == 0) {
if (VTOH(sib_vp)->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
CopyVNodeToCatalogNode (sib_vp, &catInfo.nodeData);
};
vput(sib_vp);
};
};
if (VTOH(vp)->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
CopyVNodeToCatalogNode (vp, &catInfo.nodeData);
};
catInfoPtr = &catInfo;
};
fixedblocksize = AttributeBlockSize(alist);
attrblocksize = fixedblocksize + (sizeof(u_long));
if (alist->commonattr & ATTR_CMN_NAME) attrblocksize += kHFSPlusMaxFileNameBytes + 1;
if (alist->commonattr & ATTR_CMN_NAMEDATTRLIST) attrblocksize += 0;
if (alist->volattr & ATTR_VOL_MOUNTPOINT) attrblocksize += PATH_MAX;
if (alist->volattr & ATTR_VOL_NAME) attrblocksize += kHFSPlusMaxFileNameBytes + 1;
if (alist->fileattr & ATTR_FILE_FORKLIST) attrblocksize += 0;
attrbufsize = MIN(ap->a_uio->uio_resid, attrblocksize);
DBG_VOP(("hfs_getattrlist: allocating Ox%X byte buffer (Ox%X + Ox%X) for attributes...\n",
attrblocksize,
fixedblocksize,
attrblocksize - fixedblocksize));
MALLOC(attrbufptr, void *, attrblocksize, M_TEMP, M_WAITOK);
attrptr = attrbufptr;
*((u_long *)attrptr) = 0;
++((u_long *)attrptr);
varptr = ((char *)attrptr) + fixedblocksize;
DBG_VOP(("hfs_getattrlist: attrptr = 0x%08X, varptr = 0x%08X...\n", (u_int)attrptr, (u_int)varptr));
PackAttributeBlock(alist, vp, catInfoPtr, &attrptr, &varptr);
attrbufsize = MIN(attrbufsize, (u_int)varptr - (u_int)attrbufptr);
*((u_long *)attrbufptr) = attrbufsize;
DBG_VOP(("hfs_getattrlist: copying Ox%X bytes to user address 0x%08X.\n", attrbufsize, (u_int)ap->a_uio->uio_iov->iov_base));
error = uiomove((caddr_t)attrbufptr, attrbufsize, ap->a_uio);
if (error != E_NONE) {
DBG_ERR(("hfs_getattrlist: error %d on uiomove.\n", error));
};
FREE(attrbufptr, M_TEMP);
GetCatalogErr_Exit:
CLEAN_CATALOGDATA(&catInfo.nodeData);
DBG_VOP_LOCKS_TEST(error);
return error;
}
static int
hfs_setattrlist(ap)
struct vop_setattrlist_args *ap;
{
struct vnode *vp = ap->a_vp;
struct hfsnode *hp = VTOH(vp);
struct attrlist *alist = ap->a_alist;
struct ucred *cred = ap->a_cred;
struct proc *p = ap->a_p;
int error;
struct hfsCatalogInfo catInfo;
int attrblocksize;
void *attrbufptr = NULL;
void *attrptr;
void *varptr = NULL;
uid_t saved_uid;
gid_t saved_gid;
mode_t saved_mode;
u_long saved_flags;
char * filename;
char iNodeName[32];
u_int32_t pid;
int retval = 0;
DBG_FUNC_NAME("setattrlist");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_POS);
DBG_HFS_NODE_CHECK(ap->a_vp);
DBG_VOP(("%s: Common attr:0x%x, buff size Ox%X,\n",funcname, (u_int)alist->commonattr,(u_int)ap->a_uio->uio_resid));
DBG_ASSERT(ap->a_uio->uio_rw == UIO_WRITE);
if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
((alist->commonattr & ~ATTR_CMN_SETMASK) != 0) ||
((alist->volattr & ~ATTR_VOL_SETMASK) != 0) ||
((alist->dirattr & ~ATTR_DIR_SETMASK) != 0) ||
((alist->fileattr & ~ATTR_FILE_SETMASK) != 0) ||
((alist->forkattr & ~ATTR_FORK_SETMASK) != 0)) {
DBG_ERR(("%s: Bad attrlist\n", funcname));
DBG_VOP_LOCKS_TEST(EINVAL);
return EINVAL;
};
if ((alist->volattr != 0) &&
(((alist->volattr & ATTR_VOL_INFO) == 0) ||
(alist->commonattr & ~ATTR_CMN_VOLSETMASK)))
{
DBG_ERR(("%s: Bad attrlist\n", funcname));
DBG_VOP_LOCKS_TEST(EINVAL);
return EINVAL;
};
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
DBG_VOP_LOCKS_TEST(EROFS);
return EROFS;
};
if ((alist->commonattr & (OWNERSHIP_ONLY_ATTRS & ~ATTR_CMN_FLAGS)) ||
((alist->commonattr & ATTR_CMN_FLAGS) && (VTOVCB(vp)->vcbSigWord != kHFSSigWord))) {
if ((retval = hfs_owner_rights(vp, cred, p, true)) != 0) {
DBG_VOP_LOCKS_TEST(retval);
return retval;
};
} else {
DBG_ASSERT(((alist->commonattr & OWNERSHIP_ONLY_ATTRS) == 0) ||
(((alist->commonattr & OWNERSHIP_ONLY_ATTRS) == ATTR_CMN_FLAGS) &&
(VTOVCB(vp)->vcbSigWord == kHFSSigWord)));
};
if ((((alist->commonattr & ~(OWNERSHIP_ONLY_ATTRS)) != 0) ||
(alist->volattr != 0) ||
(alist->dirattr != 0) ||
(alist->fileattr != 0) ||
(alist->forkattr != 0)) &&
((retval = hfs_write_access(vp, cred, p, false)) != 0)) {
DBG_VOP_LOCKS_TEST(retval);
return retval;
};
attrblocksize = ap->a_uio->uio_resid;
if (attrblocksize < AttributeBlockSize(alist)) {
DBG_ERR(("%s: bad attrblocksize\n", funcname));
DBG_VOP_LOCKS_TEST(EINVAL);
return EINVAL;
};
MALLOC(attrbufptr, void *, attrblocksize, M_TEMP, M_WAITOK);
INIT_CATALOGDATA(&catInfo.nodeData, kCatNameNoCopyName);
catInfo.hint = kNoHint;
error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (error != E_NONE) {
goto FreeBuffer;
};
filename = H_NAME(hp);
pid = H_DIRID(hp);
#if HFS_HARDLINKS
if (hp->h_meta->h_metaflags & IN_DATANODE) {
MAKE_INODE_NAME(iNodeName, hp->h_meta->h_indnodeno);
filename = iNodeName;
pid = VTOHFS(vp)->hfs_private_metadata_dir;
}
#endif
error = hfs_getcatalog(VTOVCB(vp), pid, filename, -1, &catInfo);
if (error != E_NONE) {
DBG_ERR(("%s: Lookup failed on file '%s'\n", funcname, filename));
goto ErrorExit;
};
H_HINT(hp) = catInfo.hint;
error = uiomove((caddr_t)attrbufptr, attrblocksize, ap->a_uio);
if (error) goto ErrorExit;
if ((alist->volattr) && (H_FILEID(hp) != kRootDirID)) {
error = EINVAL;
goto ErrorExit;
};
if (alist->commonattr & (ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME)) {
if ((error = hfs_owner_rights(vp, cred, p, true)) != 0) {
goto ErrorExit;
};
};
saved_uid = hp->h_meta->h_uid;
saved_gid = hp->h_meta->h_gid;
saved_mode = hp->h_meta->h_mode;
saved_flags = hp->h_meta->h_pflags;
attrptr = attrbufptr;
UnpackAttributeBlock(alist, vp, &catInfo, &attrptr, &varptr);
if (saved_uid != hp->h_meta->h_uid || saved_gid != hp->h_meta->h_gid) {
uid_t uid;
gid_t gid;
uid = hp->h_meta->h_uid;
hp->h_meta->h_uid = saved_uid;
gid = hp->h_meta->h_gid;
hp->h_meta->h_gid = saved_gid;
if ((error = hfs_chown(vp, uid, gid, cred, p)))
goto ErrorExit;
}
if (saved_mode != hp->h_meta->h_mode) {
mode_t mode;
mode = hp->h_meta->h_mode;
hp->h_meta->h_mode = saved_mode;
if ((error = hfs_chmod(vp, mode, cred, p)))
goto ErrorExit;
};
if (saved_flags != hp->h_meta->h_pflags) {
u_long flags;
flags = hp->h_meta->h_pflags;
hp->h_meta->h_pflags = saved_flags;
if ((error = hfs_chflags(vp, flags, cred, p)))
goto ErrorExit;
};
if (alist->volattr == 0) {
error = MacToVFSError( UpdateCatalogNode(HTOVCB(hp), pid, filename, H_HINT(hp), &catInfo.nodeData));
}
if (alist->volattr & ATTR_VOL_NAME) {
ExtendedVCB *vcb = VTOVCB(vp);
int namelen = strlen(vcb->vcbVN);
if (vcb->vcbVN[0] == 0) {
copystr(H_NAME(hp), vcb->vcbVN, sizeof(vcb->vcbVN), NULL);
} else {
error = MoveRenameCatalogNode(vcb, kRootParID, H_NAME(hp), H_HINT(hp), kRootParID, vcb->vcbVN, &H_HINT(hp));
if (error) {
VCB_LOCK(vcb);
copystr(H_NAME(hp), vcb->vcbVN, sizeof(vcb->vcbVN), NULL);
vcb->vcbFlags |= 0xFF00; VCB_UNLOCK(vcb);
goto ErrorExit;
};
hfs_set_metaname(vcb->vcbVN, hp->h_meta, HTOHFS(hp));
hp->h_nodeflags |= IN_CHANGE;
#if 0
if (vcb->vcbSigWord == kHFSPlusSigWord && vcb->vcbAlBlSt != 0) {
HFSMasterDirectoryBlock *mdb;
struct buf *bp = NULL;
int size = kMDBSize;
int volnamelen = MIN(sizeof(Str27), namelen);
if ( bread(VTOHFS(vp)->hfs_devvp, IOBLKNOFORBLK(kMasterDirectoryBlock, size),
IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size), NOCRED, &bp) == 0) {
mdb = (HFSMasterDirectoryBlock *)((char *)bp->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, size));
if (SWAP_BE16 (mdb->drSigWord) == kHFSSigWord) {
(void) utf8_to_hfs(vcb, volnamelen, vcb->vcbVN, Str31 mdb->drVN)
bawrite(bp);
bp = NULL;
}
}
if (bp) brelse(bp);
}
#endif
};
};
ErrorExit:
(void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
CLEAN_CATALOGDATA(&catInfo.nodeData);
FreeBuffer:
if (attrbufptr) FREE(attrbufptr, M_TEMP);
DBG_VOP_LOCKS_TEST(error);
return error;
}
static int
hfs_chmod(vp, mode, cred, p)
register struct vnode *vp;
register int mode;
register struct ucred *cred;
struct proc *p;
{
register struct hfsnode *hp = VTOH(vp);
int retval;
if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
return E_NONE;
#if OVERRIDE_UNKNOWN_PERMISSIONS
if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
return E_NONE;
};
#endif
if ((retval = hfs_owner_rights(vp, cred, p, true)) != 0)
return (retval);
if (cred->cr_uid) {
if (vp->v_type != VDIR && (mode & S_ISTXT))
return (EFTYPE);
if (!groupmember(hp->h_meta->h_gid, cred) && (mode & ISGID))
return (EPERM);
}
hp->h_meta->h_mode &= ~ALLPERMS;
hp->h_meta->h_mode |= (mode & ALLPERMS);
hp->h_meta->h_metaflags &= ~IN_UNSETACCESS;
hp->h_nodeflags |= IN_CHANGE;
return (0);
}
static int
hfs_write_access(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean considerFlags)
{
struct hfsnode *hp = VTOH(vp);
ExtendedVCB *vcb = HTOVCB(hp);
gid_t *gp;
Boolean isHFSPlus;
int retval = E_NONE;
int i;
isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord );
switch (vp->v_type) {
case VDIR:
case VLNK:
case VREG:
if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
return (EROFS);
break;
default:
break;
}
if (considerFlags && (hp->h_meta->h_pflags & IMMUTABLE))
return (EPERM);
if (cred->cr_uid == 0) {
retval = 0;
goto Exit;
};
if ((retval = hfs_owner_rights(vp, cred, p, false)) == 0) {
retval = ((hp->h_meta->h_mode & S_IWUSR) == S_IWUSR ? 0 : EACCES);
goto Exit;
}
for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
if (hp->h_meta->h_gid == *gp) {
retval = ((hp->h_meta->h_mode & S_IWGRP) == S_IWGRP ? 0 : EACCES);
goto Exit;
}
retval = ((hp->h_meta->h_mode & S_IWOTH) == S_IWOTH ? 0 : EACCES);
Exit:
return (retval);
}
static int
hfs_chflags(vp, flags, cred, p)
register struct vnode *vp;
register u_long flags;
register struct ucred *cred;
struct proc *p;
{
register struct hfsnode *hp = VTOH(vp);
int retval;
if (VTOVCB(vp)->vcbSigWord == kHFSSigWord) {
if ((retval = hfs_write_access(vp, cred, p, false)) != 0) {
return retval;
};
} else if ((retval = hfs_owner_rights(vp, cred, p, true)) != 0) {
return retval;
};
if (cred->cr_uid == 0) {
if ((hp->h_meta->h_pflags & (SF_IMMUTABLE | SF_APPEND)) &&
securelevel > 0) {
return EPERM;
};
hp->h_meta->h_pflags = flags;
} else {
if (hp->h_meta->h_pflags & (SF_IMMUTABLE | SF_APPEND) ||
(flags & UF_SETTABLE) != flags) {
return EPERM;
};
hp->h_meta->h_pflags &= SF_SETTABLE;
hp->h_meta->h_pflags |= (flags & UF_SETTABLE);
}
hp->h_meta->h_metaflags &= ~IN_UNSETACCESS;
hp->h_nodeflags |= IN_CHANGE;
return 0;
}
static int
hfs_chown(vp, uid, gid, cred, p)
register struct vnode *vp;
uid_t uid;
gid_t gid;
struct ucred *cred;
struct proc *p;
{
register struct hfsnode *hp = VTOH(vp);
uid_t ouid;
gid_t ogid;
int retval = 0;
if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
return EOPNOTSUPP;
if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
return E_NONE;
};
if (uid == (uid_t)VNOVAL)
uid = hp->h_meta->h_uid;
if (gid == (gid_t)VNOVAL)
gid = hp->h_meta->h_gid;
if ((cred->cr_uid != hp->h_meta->h_uid || uid != hp->h_meta->h_uid ||
(gid != hp->h_meta->h_gid && !groupmember((gid_t)gid, cred))) &&
(retval = suser(cred, &p->p_acflag)))
return (retval);
ogid = hp->h_meta->h_gid;
ouid = hp->h_meta->h_uid;
hp->h_meta->h_gid = gid;
hp->h_meta->h_uid = uid;
hp->h_meta->h_metaflags &= ~IN_UNSETACCESS;
if (ouid != uid || ogid != gid)
hp->h_nodeflags |= IN_CHANGE;
if (ouid != uid && cred->cr_uid != 0)
hp->h_meta->h_mode &= ~ISUID;
if (ogid != gid && cred->cr_uid != 0)
hp->h_meta->h_mode &= ~ISGID;
return (0);
}
static int
hfs_exchange(ap)
struct vop_exchange_args *ap;
{
struct hfsnode *from_hp, *to_hp, *nhp;
struct hfsnode *fromFirst, *fromSecond, *toFirst, *toSecond;
struct vnode *from_vp, *to_vp;
struct hfsmount *hfsmp;
u_char tmp_name[kHFSPlusMaxFileNameBytes+1];
ExtendedVCB *vcb;
u_int32_t fromFileID, toFileID;
u_int32_t fromParID;
u_int32_t tmpLong;
int retval = E_NONE;
DBG_FUNC_NAME("exchange");
DBG_VOP_LOCKS_DECL(2);
DBG_VOP_LOCKS_INIT(0,ap->a_fvp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
DBG_VOP_LOCKS_INIT(1,ap->a_tvp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
from_vp = ap->a_fvp;
to_vp = ap->a_tvp;
from_hp = VTOH(from_vp);
to_hp = VTOH(to_vp);
hfsmp = VTOHFS(from_vp);
vcb = HTOVCB(from_hp);
toFileID = H_FILEID(to_hp);
fromFileID = H_FILEID(from_hp);
fromParID = H_DIRID(from_hp);
if (from_vp->v_mount != to_vp->v_mount) {
DBG_VOP_LOCKS_TEST(EXDEV);
return EXDEV;
}
if (from_vp->v_type != VREG || to_vp->v_type != VREG) {
DBG_VOP_LOCKS_TEST(EINVAL);
return EINVAL;
}
DBG_ASSERT(H_FORKTYPE(from_hp)==kDataFork && H_FORKTYPE(to_hp)==kDataFork);
fromFirst = fromSecond = toFirst = toSecond = NULL;
if (from_hp->h_meta->h_usecount > 1) {
simple_lock(&from_hp->h_meta->h_siblinglock);
fromFirst = from_hp->h_meta->h_siblinghead.cqh_first;
fromSecond = fromFirst->h_sibling.cqe_next;
simple_unlock(&from_hp->h_meta->h_siblinglock);
if (fromFirst == from_hp) {
if (vget(HTOV(fromSecond), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
fromSecond = NULL;
} else {
VOP_UNLOCK(HTOV(from_hp), 0, ap->a_p);
if (vget(HTOV(fromFirst), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
fromFirst = NULL;
if (vget(HTOV(fromSecond), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
fromSecond = NULL;
};
} else {
fromFirst = from_hp;
};
if (to_hp->h_meta->h_usecount > 1) {
simple_lock(&to_hp->h_meta->h_siblinglock);
toFirst = to_hp->h_meta->h_siblinghead.cqh_first;
toSecond = toFirst->h_sibling.cqe_next;
simple_unlock(&to_hp->h_meta->h_siblinglock);
if (toFirst == to_hp) {
if (vget(HTOV(toSecond), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
toSecond = NULL;
} else {
VOP_UNLOCK(HTOV(to_hp), 0, ap->a_p);
if (vget(HTOV(toFirst), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
toFirst = NULL;
if (vget(HTOV(toSecond), LK_EXCLUSIVE | LK_RETRY, ap->a_p))
toSecond = NULL;
};
} else {
toFirst = to_hp;
};
if (fromFirst)
(void) vinvalbuf(HTOV(fromFirst), V_SAVE, ap->a_cred, ap->a_p, 0, 0);
if (fromSecond)
(void) vinvalbuf(HTOV(fromSecond), V_SAVE, ap->a_cred, ap->a_p, 0, 0);
if (toFirst)
(void) vinvalbuf(HTOV(toFirst), V_SAVE, ap->a_cred, ap->a_p, 0, 0);
if (toSecond)
(void) vinvalbuf(HTOV(toSecond), V_SAVE, ap->a_cred, ap->a_p, 0, 0);
retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, ap->a_p);
if (retval) goto Err_Exit;
retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
if (retval) goto Err_Exit_Relse;
retval = MacToVFSError( ExchangeFileIDs(vcb, H_NAME(from_hp), H_NAME(to_hp), H_DIRID(from_hp), H_DIRID(to_hp), H_HINT(from_hp), H_HINT(to_hp) ));
(void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, ap->a_p);
if (retval != E_NONE) {
DBG_ERR(("/tError trying to exchange: %d\n", retval));
goto Err_Exit_Relse;
}
if (fromFirst)
cache_purge(HTOV(fromFirst));
if (fromSecond)
cache_purge(HTOV(fromSecond));
if (toFirst)
cache_purge(HTOV(toFirst));
if (toSecond)
cache_purge(HTOV(toSecond));
copystr(H_NAME(from_hp), (char*) tmp_name, strlen(H_NAME(from_hp))+1, NULL);
hfs_chid(from_hp, toFileID, H_DIRID(to_hp), H_NAME(to_hp));
hfs_chid(to_hp, fromFileID, fromParID, (char*) tmp_name);
tmpLong = HTOFCB(from_hp)->fcbFlags;
HTOFCB(from_hp)->fcbFlags = HTOFCB(to_hp)->fcbFlags;
HTOFCB(to_hp)->fcbFlags = tmpLong;
tmpLong = from_hp->h_meta->h_crtime;
from_hp->h_meta->h_crtime = to_hp->h_meta->h_crtime;
to_hp->h_meta->h_crtime = tmpLong;
tmpLong = from_hp->h_meta->h_butime;
from_hp->h_meta->h_butime = to_hp->h_meta->h_butime;
to_hp->h_meta->h_butime = tmpLong;
tmpLong = from_hp->h_meta->h_atime;
from_hp->h_meta->h_atime = to_hp->h_meta->h_atime;
to_hp->h_meta->h_atime = tmpLong;
tmpLong = from_hp->h_meta->h_ctime;
from_hp->h_meta->h_ctime = to_hp->h_meta->h_ctime;
to_hp->h_meta->h_ctime = tmpLong;
tmpLong = from_hp->h_meta->h_gid;
from_hp->h_meta->h_gid = to_hp->h_meta->h_gid;
to_hp->h_meta->h_gid = tmpLong;
tmpLong = from_hp->h_meta->h_uid;
from_hp->h_meta->h_uid = to_hp->h_meta->h_uid;
to_hp->h_meta->h_uid = tmpLong;
tmpLong = from_hp->h_meta->h_pflags;
from_hp->h_meta->h_pflags = to_hp->h_meta->h_pflags;
to_hp->h_meta->h_pflags = tmpLong;
tmpLong = from_hp->h_meta->h_mode;
from_hp->h_meta->h_mode = to_hp->h_meta->h_mode;
to_hp->h_meta->h_mode = tmpLong;
tmpLong = from_hp->h_meta->h_rdev;
from_hp->h_meta->h_rdev = to_hp->h_meta->h_rdev;
to_hp->h_meta->h_rdev = tmpLong;
tmpLong = from_hp->h_meta->h_size;
from_hp->h_meta->h_size = to_hp->h_meta->h_size;
to_hp->h_meta->h_size = tmpLong;
Err_Exit_Relse:
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, ap->a_p);
Err_Exit:
hfs_vhashmove(from_hp, fromFileID);
hfs_vhashmove(to_hp, toFileID);
#if HFS_DIAGNOSTIC
if (fromFirst)
debug_check_vnode(HTOV(fromFirst), 0);
if (fromSecond)
debug_check_vnode(HTOV(fromSecond), 0);
if (toFirst)
debug_check_vnode(HTOV(toFirst), 0);
if (toSecond)
debug_check_vnode(HTOV(toSecond), 0);
#endif
if (to_hp->h_meta->h_usecount > 1) {
if (to_hp == toFirst) {
if (toSecond)
vput(HTOV(toSecond));
} else {
if (toSecond)
vrele(HTOV(toSecond));
if (toFirst)
vput(HTOV(toFirst));
}
}
if (from_hp->h_meta->h_usecount > 1) {
if (from_hp == fromFirst) {
if (fromSecond)
vput(HTOV(fromSecond));
} else {
if (fromSecond)
vrele(HTOV(fromSecond));
if (fromFirst)
vput(HTOV(fromFirst));
}
}
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
static void
hfs_chid(struct hfsnode *hp, u_int32_t fid, u_int32_t pid, char* name)
{
DBG_ASSERT(HTOV(hp)->v_type == VREG);
H_HINT(hp) = 0;
H_FILEID(hp) = fid;
H_DIRID(hp) = pid;
hfs_set_metaname(name, hp->h_meta, HTOHFS(hp));
}
static int
hfs_fsync(ap)
struct vop_fsync_args *ap;
{
struct vnode *vp = ap->a_vp ;
struct hfsnode *hp = VTOH(vp);
int retval = 0;
register struct buf *bp;
struct timeval tv;
struct buf *nbp;
int s;
DBG_FUNC_NAME("fsync");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);
DBG_HFS_NODE_CHECK(ap->a_vp);
#if HFS_DIAGNOSTIC
DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
#endif
cluster_push(vp);
loop:
s = splbio();
for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
nbp = bp->b_vnbufs.le_next;
if ((bp->b_flags & B_BUSY))
continue;
if ((bp->b_flags & B_DELWRI) == 0)
panic("hfs_fsync: not dirty");
bremfree(bp);
bp->b_flags |= B_BUSY;
bp->b_flags &= ~B_LOCKED;
splx(s);
DBG_VOP(("\t\t\tFlushing out phys block %d == log block %d\n", bp->b_blkno, bp->b_lblkno));
if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT) {
(void) bawrite(bp);
} else {
(void) VOP_BWRITE(bp);
}
goto loop;
}
if (vp->v_flag & VHASDIRTY)
ubc_pushdirty(vp);
if (ap->a_waitfor == MNT_WAIT) {
while (vp->v_numoutput) {
vp->v_flag |= VBWAIT;
tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "hfs_fsync", 0);
}
if (vp->v_dirtyblkhd.lh_first || (vp->v_flag & VHASDIRTY)) {
vprint("hfs_fsync: dirty", vp);
splx(s);
goto loop;
}
}
splx(s);
#if HFS_DIAGNOSTIC
DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
#endif
tv = time;
if ((vp->v_flag & VSYSTEM) && (hp->fcbBTCBPtr!=NULL))
BTSetLastSync(HTOFCB(hp), tv.tv_sec);
if (H_FORKTYPE(hp) != kSysFile) {
retval = VOP_UPDATE(ap->a_vp, &tv, &tv, ap->a_waitfor == MNT_WAIT);
if (retval != E_NONE) {
DBG_ERR(("%s: FLUSH FAILED: %s\n", funcname, H_NAME(hp)));
}
}
else
hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
if (ap->a_waitfor == MNT_WAIT) {
DBG_ASSERT(vp->v_dirtyblkhd.lh_first == NULL);
};
DBG_VOP_LOCKS_TEST(retval);
DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
return (retval);
}
int
hfs_fsync_transaction(struct vnode *vp)
{
struct hfsnode *hp = VTOH(vp);
register struct buf *bp;
struct timeval tv;
struct buf *nbp;
int s;
loop:
s = splbio();
for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
nbp = bp->b_vnbufs.le_next;
if ((bp->b_flags & B_BUSY))
continue;
if ((bp->b_flags & B_DELWRI) == 0)
panic("hfs_fsync: not dirty");
if ( !(bp->b_flags & B_LOCKED))
continue;
bremfree(bp);
bp->b_flags |= B_BUSY;
bp->b_flags &= ~B_LOCKED;
splx(s);
(void) bawrite(bp);
goto loop;
}
splx(s);
tv = time;
if ((vp->v_flag & VSYSTEM) && (hp->fcbBTCBPtr!=NULL))
(void) BTSetLastSync(VTOFCB(vp), tv.tv_sec);
hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
return 0;
}
int
hfs_remove(ap)
struct vop_remove_args *ap;
{
struct vnode *vp = ap->a_vp;
struct vnode *dvp = ap->a_dvp;
struct hfsnode *hp = VTOH(ap->a_vp);
struct hfsmount *hfsmp = HTOHFS(hp);
struct proc *p = current_proc();
struct timeval tv;
int retval, use_count;
int filebusy = 0;
DBG_FUNC_NAME("remove");
DBG_VOP_LOCKS_DECL(2);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
DBG_VOP_LOCKS_INIT(1,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
retval = E_NONE;
if ((hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) ||
(VTOH(dvp)->h_meta->h_pflags & APPEND)) {
retval = EPERM;
goto out;
}
if (vp->v_usecount > 1) {
if(UBCISVALID(vp) && !ubc_isinuse(vp, 1))
goto hfs_nobusy;
if ((ap->a_cnp->cn_flags & NODELETEBUSY)
|| (hfsmp->hfs_private_metadata_dir == 0)) {
retval = EBUSY;
goto out;
} else
filebusy = 1;
}
hfs_nobusy:
tv = time;
if (hp->h_meta->h_usecount > 1) {
struct vnode *sib_vp = NULL;
struct hfsnode *nhp;
DBG_ASSERT(hp->h_meta->h_siblinghead.cqh_first &&
(hp->h_meta->h_siblinghead.cqh_first != hp->h_meta->h_siblinghead.cqh_last));
DBG_ASSERT(H_FORKTYPE(hp)==kDataFork || H_FORKTYPE(hp)==kRsrcFork);
simple_lock(&hp->h_meta->h_siblinglock);
CIRCLEQ_FOREACH(nhp, &hp->h_meta->h_siblinghead, h_sibling) {
if (nhp == hp)
continue;
sib_vp = HTOV(nhp);
};
simple_unlock(&hp->h_meta->h_siblinglock);
DBG_ASSERT(sib_vp != NULL);
simple_lock(&sib_vp->v_interlock);
use_count = sib_vp->v_usecount;
simple_unlock(&sib_vp->v_interlock);
if (use_count > 0) {
if(UBCISVALID(sib_vp) && !ubc_isinuse(sib_vp, 0))
goto hfs_nobusy2;
if ((ap->a_cnp->cn_flags & NODELETEBUSY)
|| (hfsmp->hfs_private_metadata_dir == 0)) {
retval = EBUSY;
goto out;
} else
filebusy = 1;
}
hfs_nobusy2:
if (vget(sib_vp, LK_EXCLUSIVE | LK_RETRY, p) == 0) {
if ((VTOH(sib_vp)->h_nodeflags & IN_MODIFIED) || (VTOFCB(sib_vp)->fcbFlags
& fcbModifiedMask)) {
DBG_ASSERT((VTOH(sib_vp)->h_nodeflags & IN_MODIFIED) != 0);
VOP_UPDATE(sib_vp, &tv, &tv, 0);
};
(void) vinvalbuf(sib_vp, 0, NOCRED, p, 0, 0);
vput(sib_vp);
};
};
cache_purge(vp);
if ((hp->h_nodeflags & IN_MODIFIED) || (HTOFCB(hp)->fcbFlags & fcbModifiedMask))
{
DBG_ASSERT((hp->h_nodeflags & IN_MODIFIED) != 0);
VOP_UPDATE(vp, &tv, &tv, 0);
}
retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (retval != E_NONE) {
retval = EBUSY;
goto out;
}
#if HFS_HARDLINKS
if (hp->h_meta->h_metaflags & IN_DATANODE) {
if ((ap->a_cnp->cn_flags & HASBUF) == 0 ||
ap->a_cnp->cn_nameptr[0] == '\0') {
retval = ENOENT;
goto out2;
}
retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, p);
if (retval != E_NONE) {
retval = EBUSY;
goto out2;
}
retval = hfsDelete (HTOVCB(hp), H_FILEID(VTOH(dvp)),
ap->a_cnp->cn_nameptr, TRUE, H_HINT(hp));
(void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, p);
if (retval != 0)
goto out2;
hp->h_meta->h_metaflags |= IN_NOEXISTS;
hp->h_nodeflags |= IN_CHANGE;
if (--hp->h_meta->h_nlink < 1)
hp->h_meta->h_metaflags |= IN_DELETED;
H_DIRID(hp) = kUnknownID;
hfs_set_metaname("\0", hp->h_meta, HTOHFS(hp));
if ((ap->a_cnp->cn_flags & (HASBUF | SAVENAME)) == (HASBUF | SAVENAME))
FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
goto out2;
}
#endif
if (filebusy) {
UInt32 hint = H_HINT(hp);
char nodeName[32];
MAKE_DELETED_NAME(nodeName, H_FILEID(hp));
retval = hfsMoveRename (HTOVCB(hp), H_DIRID(hp), H_NAME(hp),
hfsmp->hfs_private_metadata_dir, nodeName, &hint);
if (retval) goto out2;
hp->h_meta->h_metaflags |= IN_DELETED;
hp->h_nodeflags |= IN_CHANGE;
H_HINT(hp) = hint;
H_DIRID(hp) = hfsmp->hfs_private_metadata_dir;
hfs_set_metaname(nodeName, hp->h_meta, HTOHFS(hp));
goto out2;
}
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if ((retval= vinvalbuf(vp, 0, NOCRED, p, 0, 0)))
goto out;
if(UBCINFOEXISTS(vp))
(void)ubc_setsize(vp, (off_t)0);
retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (retval != E_NONE) {
retval = EBUSY;
goto out;
}
retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, p);
if (retval != E_NONE) {
retval = EBUSY;
goto out2;
}
retval = hfsDelete (HTOVCB(hp), H_DIRID(hp), H_NAME(hp), TRUE, H_HINT(hp));
(void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, p);
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if (retval != 0)
goto out;
hp->h_meta->h_metaflags |= IN_NOEXISTS;
hp->h_meta->h_mode = 0;
hp->fcbPLen = (u_int64_t)0;
bzero(&hp->fcbExtents, sizeof(HFSPlusExtentRecord));
VTOH(dvp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
if (dvp == vp) {
vrele(vp);
} else {
vput(vp);
};
vput(dvp);
DBG_VOP_LOCKS_TEST(retval);
if (UBCINFOEXISTS(vp)) {
(void) ubc_uncache(vp);
ubc_release(vp);
}
return (retval);
out2:
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
out:;
if (! retval)
VTOH(dvp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
if (dvp == vp) {
vrele(vp);
} else {
vput(vp);
};
vput(dvp);
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
static int
hfs_rename(ap)
struct vop_rename_args *ap;
{
struct vnode *target_vp = ap->a_tvp;
struct vnode *targetPar_vp = ap->a_tdvp;
struct vnode *source_vp = ap->a_fvp;
struct vnode *sourcePar_vp = ap->a_fdvp;
struct componentname *target_cnp = ap->a_tcnp;
struct componentname *source_cnp = ap->a_fcnp;
struct proc *p = source_cnp->cn_proc;
struct hfsnode *target_hp, *targetPar_hp, *source_hp, *sourcePar_hp;
u_int32_t oldparent = 0, newparent = 0;
int doingdirectory = 0;
int retval = 0;
struct timeval tv;
struct hfsCatalogInfo catInfo;
DBG_VOP_LOCKS_DECL(4);
DBG_FUNC_NAME("rename");DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n"));
DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Source:\t"));DBG_VOP_PRINT_VNODE_INFO(ap->a_fvp);DBG_VOP_CONT(("\n"));
DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourcePar: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_fdvp);DBG_VOP_CONT(("\n"));
DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Target:\t"));DBG_VOP_PRINT_VNODE_INFO(ap->a_tvp);DBG_VOP_CONT(("\n"));
DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetPar: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_tdvp);DBG_VOP_CONT(("\n"));
DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourceName:\t"));DBG_VOP_PRINT_CPN_INFO(ap->a_fcnp);DBG_VOP_CONT(("\n"));
DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetName:\t"));DBG_VOP_PRINT_CPN_INFO(ap->a_tcnp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_fdvp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
DBG_VOP_LOCKS_INIT(1,ap->a_fvp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
DBG_VOP_LOCKS_INIT(2,ap->a_tdvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
DBG_VOP_LOCKS_INIT(3,ap->a_tvp, VOPDBG_LOCKNOTNIL, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
WRITE_CK(ap->a_fdvp, funcname);
DBG_HFS_NODE_CHECK(ap->a_fdvp);
DBG_HFS_NODE_CHECK(ap->a_tdvp);
#if HFS_DIAGNOSTIC
if ((target_cnp->cn_flags & HASBUF) == 0 ||
(source_cnp->cn_flags & HASBUF) == 0)
panic("hfs_rename: no name");
#endif
DBG_ASSERT((ap->a_fdvp->v_type == VDIR) && (ap->a_tdvp->v_type == VDIR));
target_hp = targetPar_hp = source_hp = sourcePar_hp = 0;
if ((source_vp->v_mount != targetPar_vp->v_mount) ||
(target_vp && (source_vp->v_mount != target_vp->v_mount))) {
retval = EXDEV;
goto abortit;
}
if (target_vp && ((VTOH(target_vp)->h_meta->h_pflags & (IMMUTABLE | APPEND)) ||
(VTOH(targetPar_vp)->h_meta->h_pflags & APPEND))) {
retval = EPERM;
goto abortit;
}
if ((retval = vn_lock(source_vp, LK_EXCLUSIVE, p)))
goto abortit;
sourcePar_hp = VTOH(sourcePar_vp);
source_hp = VTOH(source_vp);
oldparent = H_FILEID(sourcePar_hp);
if ((source_hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) || (sourcePar_hp->h_meta->h_pflags & APPEND)) {
VOP_UNLOCK(source_vp, 0, p);
retval = EPERM;
goto abortit;
}
if ((source_hp->h_meta->h_mode & IFMT) == IFDIR) {
if ((source_cnp->cn_namelen == 1 && source_cnp->cn_nameptr[0] == '.')
|| sourcePar_hp == source_hp
|| (source_cnp->cn_flags&ISDOTDOT)
|| (source_hp->h_nodeflags & IN_RENAME)) {
VOP_UNLOCK(source_vp, 0, p);
retval = EINVAL;
goto abortit;
}
source_hp->h_nodeflags |= IN_RENAME;
doingdirectory = TRUE;
}
targetPar_hp = VTOH(targetPar_vp);
if (target_vp)
target_hp = VTOH(target_vp);
else
DBG_ASSERT(target_hp == NULL);
newparent = H_FILEID(targetPar_hp);
if (target_vp) {
if (H_DEV(target_hp) != H_DEV(targetPar_hp) || H_DEV(target_hp) != H_DEV(source_hp))
panic("rename: EXDEV");
}
else {
if (H_DEV(targetPar_hp) != H_DEV(source_hp))
panic("rename: EXDEV");
};
retval = VOP_ACCESS(source_vp, VWRITE, target_cnp->cn_cred, target_cnp->cn_proc);
if (doingdirectory && (newparent != oldparent)) {
if (retval)
goto bad;
}
retval = 0;
if (target_vp) {
if ((targetPar_hp->h_meta->h_mode & S_ISTXT) && (target_cnp->cn_cred->cr_uid != 0) &&
target_cnp->cn_cred->cr_uid != targetPar_hp->h_meta->h_uid &&
target_cnp->cn_cred->cr_uid != target_hp->h_meta->h_uid) {
retval = EPERM;
goto bad;
}
VREF(targetPar_vp);
cache_purge(target_vp);
#if HFS_HARDLINKS
target_cnp->cn_flags &= ~SAVENAME;
#endif
retval = VOP_REMOVE(targetPar_vp, target_vp, target_cnp);
(void) vn_lock(targetPar_vp, LK_EXCLUSIVE | LK_RETRY, p);
target_vp = NULL;
target_hp = NULL;
if (retval) goto bad;
};
if (newparent != oldparent)
vn_lock(sourcePar_vp, LK_EXCLUSIVE | LK_RETRY, p);
cache_purge(source_vp);
INIT_CATALOGDATA(&catInfo.nodeData, 0);
retval = hfs_metafilelocking(VTOHFS(source_vp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (retval) {
if (newparent != oldparent)
VOP_UNLOCK(sourcePar_vp, 0, p);
goto bad;
};
retval = hfsMoveRename( HTOVCB(source_hp), H_DIRID(source_hp), source_cnp->cn_nameptr,
H_FILEID(VTOH(targetPar_vp)), target_cnp->cn_nameptr, &H_HINT(source_hp));
if (retval == 0) {
catInfo.hint = H_HINT(source_hp);
retval = hfs_getcatalog(HTOVCB(source_hp), H_FILEID(targetPar_hp), target_cnp->cn_nameptr, target_cnp->cn_namelen, &catInfo);
}
(void) hfs_metafilelocking(VTOHFS(source_vp), kHFSCatalogFileID, LK_RELEASE, p);
if (newparent != oldparent)
VOP_UNLOCK(sourcePar_vp, 0, p);
if (retval) goto bad;
H_DIRID(source_hp) = H_FILEID(targetPar_hp);
hfs_name_CatToMeta(&catInfo.nodeData, source_hp->h_meta);
CLEAN_CATALOGDATA(&catInfo.nodeData);
source_hp->h_nodeflags &= ~IN_RENAME;
targetPar_hp->h_nodeflags |= IN_UPDATE;
sourcePar_hp->h_nodeflags |= IN_UPDATE;
tv = time;
HFSTIMES(targetPar_hp, &tv, &tv);
HFSTIMES(sourcePar_hp, &tv, &tv);
vput(targetPar_vp);
vrele(sourcePar_vp);
vput(source_vp);
DBG_VOP_LOCKS_TEST(retval);
if (retval != E_NONE) {
DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval));
}
return (retval);
bad:;
if (retval && doingdirectory)
source_hp->h_nodeflags &= ~IN_RENAME;
if (targetPar_vp == target_vp)
vrele(targetPar_vp);
else
vput(targetPar_vp);
if (target_vp)
vput(target_vp);
vrele(sourcePar_vp);
if (VOP_ISLOCKED(source_vp))
vput(source_vp);
else
vrele(source_vp);
DBG_VOP_LOCKS_TEST(retval);
if (retval != E_NONE) {
DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval));
}
return (retval);
abortit:;
VOP_ABORTOP(targetPar_vp, target_cnp);
if (targetPar_vp == target_vp)
vrele(targetPar_vp);
else
vput(targetPar_vp);
if (target_vp)
vput(target_vp);
VOP_ABORTOP(sourcePar_vp, source_cnp);
vrele(sourcePar_vp);
vrele(source_vp);
DBG_VOP_LOCKS_TEST(retval);
if (retval != E_NONE) {
DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval));
}
return (retval);
}
int
hfs_mkdir(ap)
struct vop_mkdir_args *ap;
{
struct proc *p = current_proc();
int retval;
int mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
DBG_FUNC_NAME("mkdir");
DBG_VOP_LOCKS_DECL(2);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
DBG_VOP(("%s: parent 0x%x (%s) ap->a_cnp->cn_nameptr %s\n", funcname, (u_int)VTOH(ap->a_dvp), H_NAME(VTOH(ap->a_dvp)), ap->a_cnp->cn_nameptr));
WRITE_CK( ap->a_dvp, funcname);
DBG_HFS_NODE_CHECK(ap->a_dvp);
DBG_ASSERT(ap->a_dvp->v_type == VDIR);
DBG_ASSERT((ap->a_cnp->cn_flags & SAVESTART) == 0);
retval = hfs_makenode(mode, 0, ap->a_dvp, ap->a_vpp, ap->a_cnp, p);
DBG_VOP_UPDATE_VP(1, *ap->a_vpp);
if (retval != E_NONE) {
DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname, ap->a_cnp->cn_nameptr, H_NAME(VTOH(ap->a_dvp))));
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
DBG_VOP_LOCKS_TEST(E_NONE);
return (E_NONE);
}
int
hfs_rmdir(ap)
struct vop_rmdir_args *ap;
{
struct vnode *vp = ap->a_vp;
struct vnode *dvp = ap->a_dvp;
struct hfsnode *hp = VTOH(vp);
struct proc *p = current_proc();
int retval;
DBG_FUNC_NAME("rmdir");
DBG_VOP_LOCKS_DECL(2);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP(("\tParent: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);DBG_VOP_CONT(("\n"));
DBG_VOP(("\tTarget: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP(("\tTarget Name: "));DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
DBG_VOP_LOCKS_INIT(1,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
if (dvp == vp) {
vrele(vp);
vput(vp);
DBG_VOP_LOCKS_TEST(EINVAL);
return (EINVAL);
}
#if 0
if (vp->v_usecount > 1) {
DBG_ERR(("%s: dir is busy, usecount is %d\n", funcname, vp->v_usecount ));
retval = EBUSY;
goto Err_Exit;
}
#endif
cache_purge(vp);
retval = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (retval != E_NONE) {
goto Err_Exit;
}
retval = hfsDelete (HTOVCB(hp), H_DIRID(hp), H_NAME(hp), FALSE, H_HINT(hp));
(void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
if (! retval) {
VTOH(dvp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
hp->h_meta->h_mode = 0;
hp->h_meta->h_metaflags |= IN_NOEXISTS;
}
Err_Exit:;
if (dvp != 0)
vput(dvp);
vput(vp);
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
int
hfs_symlink(ap)
struct vop_symlink_args *ap;
{
register struct vnode *vp, **vpp = ap->a_vpp;
struct proc *p = current_proc();
struct hfsnode *hp;
u_int32_t dfltClump;
int len, retval;
DBG_FUNC_NAME("symlink");
DBG_VOP_LOCKS_DECL(2);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_POS);
if (VTOVCB(ap->a_dvp)->vcbSigWord != kHFSPlusSigWord) {
VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
vput(ap->a_dvp);
DBG_VOP((" ...sorry HFS disks don't support symbolic links.\n"));
DBG_VOP_LOCKS_TEST(EOPNOTSUPP);
return (EOPNOTSUPP);
}
retval = hfs_makenode(IFLNK | ap->a_vap->va_mode, 0, ap->a_dvp,
vpp, ap->a_cnp, p);
DBG_VOP_UPDATE_VP(1, *ap->a_vpp);
if (retval != E_NONE) {
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
vp = *vpp;
len = strlen(ap->a_target);
hp = VTOH(vp);
dfltClump = hp->fcbClmpSize;
hp->fcbClmpSize = VTOVCB(vp)->blockSize;
retval = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0,
(struct proc *)0);
hp->fcbClmpSize = dfltClump;
vput(vp);
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
static hfsdotentry rootdots[2] = {
{
1,
sizeof(struct hfsdotentry),
DT_DIR,
1,
"."
},
{
1,
sizeof(struct hfsdotentry),
DT_DIR,
2,
".."
}
};
static hfsdotentry emptyentry = { 0 };
struct callbackstate {
u_int32_t cbs_parentID;
u_int32_t cbs_hiddenDirID;
off_t cbs_lastoffset;
struct uio * cbs_uio;
ExtendedVCB * cbs_vcb;
int16_t cbs_hfsPlus;
int16_t cbs_result;
};
SInt32
ProcessCatalogEntry(const CatalogKey *ckp, const CatalogRecord *crp,
u_int16_t recordLen, struct callbackstate *state)
{
CatalogName *cnp;
size_t utf8chars;
u_int32_t curID;
OSErr result;
struct dirent catent;
if (state->cbs_hfsPlus)
curID = ckp->hfsPlus.parentID;
else
curID = ckp->hfs.parentID;
if (state->cbs_parentID != curID) {
lastitem:
#if 0
catent.d_fileno = 0;
catent.d_reclen = 0;
catent.d_type = 0;
catent.d_namlen = 0;
*(int32_t*)&catent.d_name[0] = 0;
state->cbs_lastoffset = state->cbs_uio->uio_offset;
state->cbs_result = uiomove((caddr_t) &catent, 12, state->cbs_uio);
if (state->cbs_result == 0)
state->cbs_result = ENOENT;
#else
state->cbs_lastoffset = state->cbs_uio->uio_offset;
state->cbs_result = ENOENT;
#endif
return (0);
}
if (state->cbs_hfsPlus) {
switch(crp->recordType) {
case kHFSPlusFolderRecord:
catent.d_type = DT_DIR;
catent.d_fileno = crp->hfsPlusFolder.folderID;
break;
case kHFSPlusFileRecord:
catent.d_type = DT_REG;
catent.d_fileno = crp->hfsPlusFile.fileID;
break;
default:
return (0);
};
cnp = (CatalogName*) &ckp->hfsPlus.nodeName;
result = utf8_encodestr(cnp->ustr.unicode, cnp->ustr.length * sizeof(UniChar),
catent.d_name, &utf8chars, kdirentMaxNameBytes + 1, ':', 0);
if (result == ENAMETOOLONG) {
result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
cnp->ustr.unicode, kdirentMaxNameBytes + 1, (ByteCount*)&utf8chars, catent.d_name, catent.d_fileno);
}
} else {
switch(crp->recordType) {
case kHFSFolderRecord:
catent.d_type = DT_DIR;
catent.d_fileno = crp->hfsFolder.folderID;
break;
case kHFSFileRecord:
catent.d_type = DT_REG;
catent.d_fileno = crp->hfsFile.fileID;
break;
default:
return (0);
};
cnp = (CatalogName*) ckp->hfs.nodeName;
result = hfs_to_utf8(state->cbs_vcb, cnp->pstr, kdirentMaxNameBytes + 1,
(ByteCount *)&utf8chars, catent.d_name);
if (result)
result = mac_roman_to_utf8(cnp->pstr, kdirentMaxNameBytes + 1,
(ByteCount *)&utf8chars, catent.d_name);
}
catent.d_namlen = utf8chars;
catent.d_reclen = DIRENTRY_SIZE(utf8chars);
if (curID == kRootDirID &&
catent.d_fileno == state->cbs_hiddenDirID &&
catent.d_type == DT_DIR)
goto lastitem;
state->cbs_lastoffset = state->cbs_uio->uio_offset;
if (catent.d_reclen > state->cbs_uio->uio_resid)
return (0);
state->cbs_result = uiomove((caddr_t) &catent, catent.d_reclen, state->cbs_uio);
return (state->cbs_result == 0 &&
state->cbs_uio->uio_resid >= AVERAGE_HFSDIRENTRY_SIZE);
}
static int
hfs_readdir(ap)
struct vop_readdir_args *ap;
{
register struct uio *uio = ap->a_uio;
struct hfsnode *hp = VTOH(ap->a_vp);
struct proc *p = current_proc();
ExtendedVCB *vcb = HTOVCB(hp);
off_t off = uio->uio_offset;
u_int32_t dirID = H_FILEID(hp);
int retval = 0;
OSErr result = noErr;
u_int32_t diroffset;
BTreeIterator bi;
CatalogIterator *cip;
u_int16_t op;
struct callbackstate state;
int eofflag = 0;
DBG_FUNC_NAME("readdir");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_HFS_NODE_CHECK(ap->a_vp);
if (uio->uio_iovcnt > 1 || uio->uio_resid < AVERAGE_HFSDIRENTRY_SIZE) {
return EINVAL;
};
if (uio->uio_offset < sizeof(rootdots)) {
caddr_t dep;
size_t dotsize;
rootdots[0].d_fileno = dirID;
rootdots[1].d_fileno = H_DIRID(hp);
if (uio->uio_offset == 0) {
dep = (caddr_t) &rootdots[0];
dotsize = 2* sizeof(struct hfsdotentry);
} else if (uio->uio_offset == sizeof(struct hfsdotentry)) {
dep = (caddr_t) &rootdots[1];
dotsize = sizeof(struct hfsdotentry);
} else {
retval = EINVAL;
goto Exit;
}
retval = uiomove(dep, dotsize, uio);
if (retval != 0)
goto Exit;
}
diroffset = uio->uio_offset;
retval = hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_SHARED, p);
if (retval != E_NONE)
goto Exit;
cip = GetCatalogIterator(vcb, dirID, diroffset);
result = PositionIterator(cip, diroffset, &bi, &op);
if (result == cmNotFound) {
eofflag = 1;
retval = 0;
AgeCatalogIterator(cip);
goto cleanup;
} else if ((retval = MacToVFSError(result)))
goto cleanup;
state.cbs_hiddenDirID = VCBTOHFS(vcb)->hfs_private_metadata_dir;
state.cbs_lastoffset = cip->currentOffset;
state.cbs_vcb = vcb;
state.cbs_uio = uio;
state.cbs_result = 0;
state.cbs_parentID = dirID;
if (vcb->vcbSigWord == kHFSPlusSigWord)
state.cbs_hfsPlus = 1;
else
state.cbs_hfsPlus = 0;
result = BTIterateRecords(GetFileControlBlock(vcb->catalogRefNum), op, &bi,
(IterateCallBackProcPtr)ProcessCatalogEntry, &state);
if (state.cbs_result)
retval = state.cbs_result;
else
retval = MacToVFSError(result);
if (retval == ENOENT) {
eofflag = 1;
retval = 0;
}
if (retval == 0) {
cip->currentOffset = state.cbs_lastoffset;
cip->nextOffset = uio->uio_offset;
UpdateCatalogIterator(&bi, cip);
}
cleanup:
if (retval) {
cip->volume = 0;
cip->folderID = 0;
AgeCatalogIterator(cip);
}
(void) ReleaseCatalogIterator(cip);
(void) hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_RELEASE, p);
if (retval != E_NONE) {
DBG_ERR(("%s: retval %d when trying to read directory %ld: %s\n",funcname, retval,
H_FILEID(hp), H_NAME(hp)));
goto Exit;
}
if (uio->uio_offset == off) {
retval = E_NONE;
goto Exit;
}
if (vcb->vcbSigWord == kHFSPlusSigWord)
hp->h_nodeflags |= IN_ACCESS;
if (!retval && ap->a_ncookies != NULL) {
struct dirent* dpStart;
struct dirent* dpEnd;
struct dirent* dp;
int ncookies;
u_long *cookies;
u_long *cookiep;
if (uio->uio_segflg != UIO_SYSSPACE)
panic("hfs_readdir: unexpected uio from NFS server");
dpStart = (struct dirent *)(uio->uio_iov->iov_base - (uio->uio_offset - off));
dpEnd = (struct dirent *) uio->uio_iov->iov_base;
for (dp = dpStart, ncookies = 0;
dp < dpEnd && dp->d_reclen != 0;
dp = (struct dirent *)((caddr_t)dp + dp->d_reclen))
ncookies++;
MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK);
for (dp = dpStart, cookiep = cookies;
dp < dpEnd;
dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) {
off += dp->d_reclen;
*cookiep++ = (u_long) off;
}
*ap->a_ncookies = ncookies;
*ap->a_cookies = cookies;
}
Exit:;
if (ap->a_eofflag)
*ap->a_eofflag = eofflag;
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
static int
hfs_readdirattr(ap)
struct vop_readdirattr_args *ap;
{
struct vnode *vp = ap->a_vp;
struct attrlist *alist = ap->a_alist;
register struct uio *uio = ap->a_uio;
u_long maxcount = ap->a_maxcount;
u_long ncookies;
ExtendedVCB *vcb = HTOVCB(VTOH(vp));
UInt32 dirID = H_FILEID(VTOH(vp));
struct proc *proc = current_proc();
off_t startoffset = uio->uio_offset;
struct hfsCatalogInfo catInfo;
UInt32 index;
int retval = 0;
u_long fixedblocksize;
u_long maxattrblocksize;
u_long currattrbufsize;
void *attrbufptr = NULL;
void *attrptr;
void *varptr;
struct vnode *entryvnode;
*(ap->a_actualcount) = 0;
*(ap->a_eofflag) = 0;
if (((ap->a_options & ~FSOPT_NOINMEMUPDATE) != 0) ||
(vp == NULL) ||
(uio->uio_resid <= 0) || (uio->uio_iovcnt > 1))
return EINVAL;
if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
((alist->commonattr & ~ATTR_CMN_VALIDMASK) != 0) ||
(alist->volattr != 0) ||
((alist->dirattr & ~ATTR_DIR_VALIDMASK) != 0) ||
((alist->fileattr & ~ATTR_FILE_VALIDMASK) != 0) ||
((alist->forkattr & ~ATTR_FORK_VALIDMASK) != 0))
return EINVAL;
if ((alist->commonattr & (ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST)) ||
(alist->fileattr & (ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST)) ||
(alist->commonattr & ATTR_CMN_OBJPERMANENTID) )
return EINVAL;
fixedblocksize = (sizeof(u_long) + AttributeBlockSize(alist));
maxattrblocksize = fixedblocksize;
if (alist->commonattr & ATTR_CMN_NAME)
maxattrblocksize += kHFSPlusMaxFileNameBytes + 1;
MALLOC(attrbufptr, void *, maxattrblocksize, M_TEMP, M_WAITOK);
attrptr = attrbufptr;
varptr = (char *)attrbufptr + fixedblocksize;
index = (uio->uio_offset / sizeof(struct dirent)) + 1;
INIT_CATALOGDATA(&catInfo.nodeData, 0);
if ((retval = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, proc)) != E_NONE)
goto exit;
catInfo.hint = kNoHint;
while ((uio->uio_resid >= 0) && (maxcount !=0 )) {
OSErr result = GetCatalogOffspring(vcb, dirID, index, &catInfo.nodeData, NULL, NULL);
if (result != noErr) {
if (result == cmNotFound) {
*(ap->a_eofflag) = TRUE;
retval = E_NONE;
}
else retval = MacToVFSError(result);
break;
}
if ((dirID == kRootDirID) &&
catInfo.nodeData.cnd_nodeID == VCBTOHFS(vcb)->hfs_private_metadata_dir &&
catInfo.nodeData.cnd_type == kCatalogFolderNode) {
++index;
continue;
}
*((u_long *)attrptr)++ = 0;
if (ap->a_options & FSOPT_NOINMEMUPDATE) {
PackCatalogInfoAttributeBlock(alist, vp, &catInfo, &attrptr, &varptr);
} else {
entryvnode = hfs_vhashget(H_DEV(VTOH(vp)), catInfo.nodeData.cnd_nodeID, kDefault);
if (entryvnode != NULL) {
PackAttributeBlock(alist, entryvnode, &catInfo, &attrptr, &varptr);
vput(entryvnode);
} else {
PackCatalogInfoAttributeBlock(alist, vp, &catInfo, &attrptr, &varptr);
};
};
currattrbufsize = *((u_long *)attrbufptr) = ((char *)varptr - (char *)attrbufptr);
if (currattrbufsize > uio->uio_resid)
break;
else {
retval = uiomove((caddr_t)attrbufptr, currattrbufsize, ap->a_uio);
if (retval != E_NONE)
break;
attrptr = attrbufptr;
varptr = (char *)attrbufptr + fixedblocksize;
index++;
*ap->a_actualcount += 1;
maxcount--;
}
};
*ap->a_newstate = VTOH(vp)->h_meta->h_mtime;
(void) hfs_metafilelocking( VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_RELEASE, proc );
CLEAN_CATALOGDATA(&catInfo.nodeData);
if (!retval && ap->a_cookies != NULL) {
void* dpStart;
void* dpEnd;
void* dp;
u_long *cookies;
u_long *cookiep;
if (uio->uio_segflg != UIO_SYSSPACE)
panic("hfs_readdirattr: unexpected uio from NFS server");
dpStart = uio->uio_iov->iov_base - (uio->uio_offset - startoffset);
dpEnd = uio->uio_iov->iov_base;
MALLOC(cookies, u_long *, (*ap->a_actualcount)*sizeof(u_long), M_TEMP, M_WAITOK);
for (dp = dpStart, cookiep = cookies;
dp < dpEnd;
dp = ((caddr_t) dp + *((u_long *)dp))) {
*cookiep++ = (u_long)((caddr_t)dp + sizeof(u_long));
}
*ap->a_cookies = cookies;
}
uio->uio_offset = startoffset + (*ap->a_actualcount)*sizeof(struct dirent);
exit:
if (attrbufptr != NULL)
FREE(attrbufptr, M_TEMP);
return (retval);
}
int
hfs_readlink(ap)
struct vop_readlink_args *ap;
{
int retval;
DBG_FUNC_NAME("readlink");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
retval = VOP_READ(ap->a_vp, ap->a_uio, 0, ap->a_cred);
VTOH(ap->a_vp)->h_nodeflags &= ~IN_ACCESS;
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
static int
hfs_abortop(ap)
struct vop_abortop_args *ap;
{
DBG_FUNC_NAME("abortop");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
}
DBG_VOP_LOCKS_TEST(E_NONE);
return (E_NONE);
}
static int
hfs_inactive(ap)
struct vop_inactive_args *ap;
{
struct vnode *vp = ap->a_vp;
struct hfsnode *hp = VTOH(vp);
struct proc *p = ap->a_p;
struct timeval tv;
int error = 0;
extern int prtactive;
DBG_FUNC_NAME("inactive");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_ZERO);
if (prtactive && vp->v_usecount <= 0)
vprint("hfs_inactive: pushing active", vp);
if (vp->v_usecount != 0)
DBG_VOP(("%s: bad usecount = %d\n",funcname,vp->v_usecount ));
if (hp->h_meta->h_mode == 0)
goto out;
if (hp->h_meta->h_metaflags & IN_DELETED) {
hp->h_meta->h_metaflags &= ~IN_DELETED;
error = vinvalbuf(vp, 0, NOCRED, p, 0, 0);
if (error) goto out;
if(UBCINFOEXISTS(vp))
(void)ubc_setsize(vp, (off_t)0);
error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE | LK_CANRECURSE, p);
if (error) goto out;
error = hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_EXCLUSIVE | LK_CANRECURSE, p);
if (error) {
(void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
goto out;
}
if (hp->h_meta->h_metaflags & IN_DATANODE) {
char iNodeName[32];
MAKE_INODE_NAME(iNodeName, hp->h_meta->h_indnodeno);
error = hfsDelete(HTOVCB(hp), VTOHFS(vp)->hfs_private_metadata_dir, iNodeName, TRUE, H_HINT(hp));
} else {
error = hfsDelete(HTOVCB(hp), H_DIRID(hp), H_NAME(hp), TRUE, H_HINT(hp));
if (error == ENOENT) {
error = hfsDelete(HTOVCB(hp), H_FILEID(hp), NULL, TRUE, H_HINT(hp));
}
}
(void) hfs_metafilelocking(VTOHFS(vp), kHFSExtentsFileID, LK_RELEASE, p);
(void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
if (error) goto out;
hp->h_meta->h_metaflags |= IN_NOEXISTS;
hp->h_meta->h_mode = 0;
hp->fcbPLen = (u_int64_t)0;
bzero(&hp->fcbExtents, sizeof(HFSPlusExtentRecord));
hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
}
if (hp->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
tv = time;
VOP_UPDATE(vp, &tv, &tv, 0);
}
out:
VOP_UNLOCK(vp, 0, p);
if (hp->h_meta->h_mode == 0)
vrecycle(vp, (struct slock *)0, p);
DBG_VOP_LOCKS_TEST(E_NONE);
return (E_NONE);
}
static int
hfs_reclaim(ap)
struct vop_reclaim_args *ap;
{
struct vnode *vp = ap->a_vp;
struct hfsnode *hp = VTOH(vp);
void *tdata = vp->v_data;
char *tname;
Boolean freeMeta = true;
struct vnode *devvp = NULL;
extern int prtactive;
DBG_FUNC_NAME("reclaim");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0, ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);
if (prtactive && vp->v_usecount != 0)
vprint("hfs_reclaim(): pushing active", vp);
hp->h_nodeflags |= IN_ALLOCATING;
hfs_vhashrem(hp);
DBG_ASSERT(tdata != NULL);
DBG_ASSERT(hp->h_meta != NULL);
devvp = hp->h_meta->h_devvp;
hp->h_meta->h_usecount--;
if (H_FORKTYPE(hp)==kDataFork || H_FORKTYPE(hp)==kRsrcFork) {
if (hp->h_meta->h_siblinghead.cqh_first != (void *) &hp->h_meta->h_siblinghead)
freeMeta = false;
};
if (freeMeta) {
DBG_ASSERT(hp->h_meta->h_usecount == 0);
if (hp->h_meta->h_metaflags & IN_LONGNAME) {
tname = H_NAME(hp);
DBG_ASSERT(tname != NULL);
FREE(tname, M_TEMP);
}
FREE_ZONE(hp->h_meta, sizeof(struct hfsfilemeta), M_HFSFMETA);
hp->h_meta = NULL;
}
else
DBG_ASSERT(hp->h_meta->h_usecount == 1);
cache_purge(vp);
if (devvp) {
vrele(devvp);
};
FREE_ZONE(tdata, sizeof(struct hfsnode), M_HFSNODE);
vp->v_data = NULL;
DBG_VOP_LOCKS_TEST(E_NONE);
return (E_NONE);
}
static int
hfs_lock(ap)
struct vop_lock_args *ap;
{
struct vnode * vp = ap->a_vp;
struct hfsnode *hp = VTOH(ap->a_vp);
int retval;
DBG_FUNC_NAME("lock");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT((" flags = 0x%08X.\n", ap->a_flags));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_ZERO);
retval = lockmgr(&hp->h_lock, ap->a_flags, &vp->v_interlock, ap->a_p);
if (retval != E_NONE) {
if ((ap->a_flags & LK_NOWAIT) == 0)
DBG_ERR(("hfs_lock: error %d trying to lock vnode (flags = 0x%08X).\n", retval, ap->a_flags));
goto Err_Exit;
};
Err_Exit:;
DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
int
hfs_unlock(ap)
struct vop_unlock_args *ap;
{
struct hfsnode *hp = VTOH(ap->a_vp);
struct vnode *vp = ap->a_vp;
int retval = E_NONE;
DBG_FUNC_NAME("unlock");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(vp);DBG_VOP_CONT((" flags = 0x%08X.\n", ap->a_flags));
DBG_VOP_LOCKS_INIT(0,vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);
DBG_ASSERT((ap->a_flags & (LK_EXCLUSIVE|LK_SHARED)) == 0);
retval = lockmgr(&hp->h_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p);
if (retval != E_NONE) {
DEBUG_BREAK_MSG(("hfs_unlock: error %d trying to unlock vnode (forktype = %d).\n", retval, H_FORKTYPE(hp)));
};
DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
int
hfs_print(ap)
struct vop_print_args *ap;
{
register struct vnode * vp = ap->a_vp;
register struct hfsnode *hp = VTOH( vp);
DBG_FUNC_NAME("print");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);
printf("tag VT_HFS, dirID %d, on dev %d, %d", H_DIRID(hp),
major(H_DEV(hp)), minor(H_DEV(hp)));
printf("\n");
DBG_VOP_LOCKS_TEST(E_NONE);
return (E_NONE);
}
int
hfs_islocked(ap)
struct vop_islocked_args *ap;
{
int lockStatus;
lockStatus = lockstatus(&VTOH( ap->a_vp)->h_lock);
return (lockStatus);
}
static int
hfs_pathconf(ap)
struct vop_pathconf_args *ap;
{
int retval = E_NONE;
DBG_FUNC_NAME("pathconf");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
DBG_HFS_NODE_CHECK (ap->a_vp);
switch (ap->a_name) {
case _PC_LINK_MAX:
#if HFS_HARDLINKS
if (VTOVCB(ap->a_vp)->vcbSigWord == kHFSPlusSigWord)
*ap->a_retval = HFS_LINK_MAX;
else
*ap->a_retval = 1;
#else
*ap->a_retval = 1;
#endif
break;
case _PC_NAME_MAX:
*ap->a_retval = kHFSPlusMaxFileNameBytes;
break;
case _PC_PATH_MAX:
*ap->a_retval = PATH_MAX;
break;
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = 1;
break;
case _PC_NO_TRUNC:
*ap->a_retval = 0;
break;
default:
retval = EINVAL;
}
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
int
hfs_advlock(ap)
struct vop_advlock_args *ap;
{
register struct hfsnode *hp = VTOH(ap->a_vp);
register struct flock *fl = ap->a_fl;
register struct hfslockf *lock;
off_t start, end;
int retval;
DBG_FUNC_NAME("advlock");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
if (hp->h_lockf == (struct hfslockf *)0) {
if (ap->a_op != F_SETLK) {
fl->l_type = F_UNLCK;
return (0);
}
}
start = 0;
switch (fl->l_whence) {
case SEEK_SET:
case SEEK_CUR:
start = fl->l_start;
break;
case SEEK_END:
start = HTOFCB(hp)->fcbEOF + fl->l_start;
break;
default:
return (EINVAL);
}
if (start < 0)
return (EINVAL);
if (fl->l_len == 0)
end = -1;
else
end = start + fl->l_len - 1;
MALLOC(lock, struct hfslockf *, sizeof *lock, M_LOCKF, M_WAITOK);
lock->lf_start = start;
lock->lf_end = end;
lock->lf_id = ap->a_id;
lock->lf_hfsnode = hp;
lock->lf_type = fl->l_type;
lock->lf_next = (struct hfslockf *)0;
TAILQ_INIT(&lock->lf_blkhd);
lock->lf_flags = ap->a_flags;
switch(ap->a_op) {
case F_SETLK:
retval = hfs_setlock(lock);
break;
case F_UNLCK:
retval = hfs_clearlock(lock);
FREE(lock, M_LOCKF);
break;
case F_GETLK:
retval = hfs_getlock(lock, fl);
FREE(lock, M_LOCKF);
break;
default:
retval = EINVAL;
_FREE(lock, M_LOCKF);
break;
}
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
int
hfs_update(ap)
struct vop_update_args *ap;
{
struct hfsnode *hp;
struct proc *p;
hfsCatalogInfo catInfo;
char *filename;
char iNodeName[32];
u_int32_t pid;
int retval;
ExtendedVCB *vcb;
DBG_FUNC_NAME("update");
DBG_VOP_LOCKS_DECL(1);
DBG_VOP_PRINT_FUNCNAME();
DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);
hp = VTOH(ap->a_vp);
DBG_ASSERT(hp && hp->h_meta);
DBG_ASSERT(*((int*)&ap->a_vp->v_interlock) == 0);
if ((H_FORKTYPE(hp) == kSysFile) ||
(VTOVFS(ap->a_vp)->mnt_flag & MNT_RDONLY) ||
(hp->h_meta->h_mode == 0)) {
hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
DBG_VOP_LOCKS_TEST(0);
return (0);
}
if (H_FORKTYPE(hp) == kSysFile) {
hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
DBG_VOP_LOCKS_TEST(0);
return (0);
}
if (VTOVFS(ap->a_vp)->mnt_flag & MNT_RDONLY) {
hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
DBG_VOP_LOCKS_TEST(0);
return (0);
}
if (HTOFCB(hp)->fcbFlags &fcbModifiedMask)
hp->h_nodeflags |= IN_MODIFIED;
if ((hp->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0) {
DBG_VOP_LOCKS_TEST(0);
return (0);
};
if (hp->h_nodeflags & IN_ACCESS)
hp->h_meta->h_atime = ap->a_access->tv_sec;
if (hp->h_nodeflags & IN_UPDATE)
hp->h_meta->h_mtime = ap->a_modify->tv_sec;
if (hp->h_nodeflags & IN_CHANGE) {
hp->h_meta->h_ctime = time.tv_sec;
if ((HTOVCB(hp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
hp->h_meta->h_ctime += 3600;
hp->h_meta->h_mtime = hp->h_meta->h_ctime;
}
}
p = current_proc();
filename = H_NAME(hp);
pid = H_DIRID(hp);
vcb = HTOVCB(hp);
catInfo.hint = H_HINT(hp);
#if HFS_HARDLINKS
if (hp->h_meta->h_metaflags & IN_DATANODE) {
MAKE_INODE_NAME(iNodeName, hp->h_meta->h_indnodeno);
filename = iNodeName;
pid = VCBTOHFS(vcb)->hfs_private_metadata_dir;
}
#endif
INIT_CATALOGDATA(&catInfo.nodeData, kCatNameNoCopyName);
retval = hfs_metafilelocking(HTOHFS(hp), kHFSCatalogFileID, LK_EXCLUSIVE | LK_CANRECURSE, p);
if (retval) {
DBG_VOP_LOCKS_TEST(retval);
return (retval);
};
retval = hfs_getcatalog(vcb, pid, filename, -1, &catInfo);
if (retval != noErr) {
(void) hfs_metafilelocking(HTOHFS(hp), kHFSCatalogFileID, LK_RELEASE, p);
retval = MacToVFSError(retval);
goto Err_Exit;
};
H_HINT(hp) = catInfo.hint;
CopyVNodeToCatalogNode (HTOV(hp), &catInfo.nodeData);
retval = UpdateCatalogNode(vcb, pid, filename, H_HINT(hp), &catInfo.nodeData);
(void) hfs_metafilelocking(HTOHFS(hp), kHFSCatalogFileID, LK_RELEASE, p);
if (retval != noErr) {
retval = MacToVFSError(retval);
goto Err_Exit;
};
hp->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
HTOFCB(hp)->fcbFlags &= ~fcbModifiedMask;
if (ap->a_vp->v_type == VDIR) {
hp->h_meta->h_nlink = 2 + catInfo.nodeData.cnd_valence;
hp->h_meta->h_size = sizeof(rootdots) +
(catInfo.nodeData.cnd_valence * AVERAGE_HFSDIRENTRY_SIZE);
if (hp->h_meta->h_size < MAX_HFSDIRENTRY_SIZE)
hp->h_meta->h_size < MAX_HFSDIRENTRY_SIZE;
} else {
hp->h_meta->h_size = (off_t)vcb->blockSize *
(off_t)(catInfo.nodeData.cnd_rsrcfork.totalBlocks +
catInfo.nodeData.cnd_datafork.totalBlocks);
}
Err_Exit:
CLEAN_CATALOGDATA(&catInfo.nodeData);
DBG_VOP_LOCKS_TEST(retval);
return (retval);
}
int
hfs_vinit(mntp, specops, fifoops, vpp)
struct mount *mntp;
int (**specops)(void *);
int (**fifoops)(void *);
struct vnode **vpp;
{
struct hfsnode *hp;
struct vnode *vp, *nvp;
vp = *vpp;
hp = VTOH(vp);
switch(vp->v_type) {
case VCHR:
case VBLK:
vp->v_op = specops;
if ((nvp = checkalias(vp, hp->h_meta->h_rdev, mntp))) {
nvp->v_data = vp->v_data;
vp->v_data = NULL;
vp->v_op = spec_vnodeop_p;
vrele(vp);
vgone(vp);
hp->h_vp = nvp;
vp = nvp;
}
break;
case VFIFO:
#if FIFO
vp->v_op = fifoops;
break;
#else
return (EOPNOTSUPP);
#endif
default:
break;
}
if (H_FILEID(hp) == kRootDirID)
vp->v_flag |= VROOT;
*vpp = vp;
return (0);
}
static int
hfs_makenode(mode, rawdev, dvp, vpp, cnp, p)
int mode;
dev_t rawdev;
struct vnode *dvp;
struct vnode **vpp;
struct componentname *cnp;
struct proc *p;
{
register struct hfsnode *hp, *parhp;
struct timeval tv;
struct vnode *tvp;
struct hfsCatalogInfo catInfo;
ExtendedVCB *vcb;
UInt8 forkType;
int retval;
int hasmetalock = 0;
DBG_FUNC_NAME("makenode");
parhp = VTOH(dvp);
vcb = HTOVCB(parhp);
*vpp = NULL;
tvp = NULL;
if ((mode & IFMT) == 0)
mode |= IFREG;
#if HFS_DIAGNOSTIC
if ((cnp->cn_flags & HASBUF) == 0)
panic("hfs_makenode: no name");
#endif
retval = hfs_metafilelocking(VTOHFS(dvp),
kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (retval != E_NONE)
goto bad1;
else
hasmetalock = 1;
retval = hfsCreate(vcb, H_FILEID(parhp), cnp->cn_nameptr, mode);
if (retval != E_NONE) {
DBG_ERR(("%s: hfsCreate FAILED: %s, %s\n", funcname, cnp->cn_nameptr, H_NAME(parhp)));
goto bad1;
}
INIT_CATALOGDATA(&catInfo.nodeData, 0);
catInfo.hint = kNoHint;
retval = hfs_getcatalog(vcb, H_FILEID(parhp), cnp->cn_nameptr, cnp->cn_namelen, &catInfo);
if (retval != E_NONE) {
DBG_ERR(("%s: hfs_getcatalog FAILED: %s, %s\n", funcname, cnp->cn_nameptr, H_NAME(parhp)));
goto bad1;
}
hasmetalock = 0;
(void) hfs_metafilelocking(VTOHFS(dvp),
kHFSCatalogFileID, LK_RELEASE, p);
if (vcb->vcbSigWord == kHFSPlusSigWord) {
u_int32_t pflags;
int catmode;
if (VTOVFS(dvp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
catInfo.nodeData.cnd_ownerID = VTOHFS(dvp)->hfs_uid;
catInfo.nodeData.cnd_groupID = VTOHFS(dvp)->hfs_gid;
catmode = mode;
} else {
catInfo.nodeData.cnd_ownerID = cnp->cn_cred->cr_uid;
catInfo.nodeData.cnd_groupID = parhp->h_meta->h_gid;
catmode = mode;
}
switch (catmode & IFMT) {
case IFLNK:
catInfo.nodeData.cnd_ownerID = parhp->h_meta->h_uid;
break;
case IFCHR:
case IFBLK:
catInfo.nodeData.cnd_rawDevice = rawdev;
catmode = (catmode & ~IFMT) | IFREG;
break;
}
if ((catmode & ISGID) && !groupmember(parhp->h_meta->h_gid, cnp->cn_cred) &&
suser(cnp->cn_cred, NULL))
catmode &= ~ISGID;
if (cnp->cn_flags & ISWHITEOUT)
pflags = UF_OPAQUE;
else
pflags = 0;
catInfo.nodeData.cnd_adminFlags = (pflags >> 16) & 0x00FF;
catInfo.nodeData.cnd_ownerFlags = pflags & 0x00FF;
catInfo.nodeData.cnd_mode = catmode;
}
forkType = (catInfo.nodeData.cnd_type == kCatalogFolderNode) ? kDirectory : kDataFork;
retval = hfs_vcreate(vcb, &catInfo, forkType, &tvp);
CLEAN_CATALOGDATA(&catInfo.nodeData);
if (retval) goto bad1;
tv = time;
if (vcb->vcbSigWord == kHFSPlusSigWord) {
hp = VTOH(tvp);
hp->h_meta->h_mode = mode;
tvp->v_type = IFTOVT(mode);
hp->h_meta->h_metaflags &= ~IN_UNSETACCESS;
hp->h_nodeflags |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
if ((retval = VOP_UPDATE(tvp, &tv, &tv, 1)))
goto bad2;
}
VTOH(dvp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
if ((retval = VOP_UPDATE(dvp, &tv, &tv, 1)))
goto bad2;
if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
};
vput(dvp);
if (UBCINFOMISSING(tvp) || UBCINFORECLAIMED(tvp))
ubc_info_init(tvp);
*vpp = tvp;
return (0);
bad2:
vput(tvp);
bad1:
if (hasmetalock) {
hasmetalock = 0;
(void) hfs_metafilelocking(VTOHFS(dvp),
kHFSCatalogFileID, LK_RELEASE, p);
}
if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
};
vput(dvp);
return (retval);
}
#if DBG_VOP_TEST_LOCKS
void DbgVopTest( int maxSlots,
int retval,
VopDbgStoreRec *VopDbgStore,
char *funcname)
{
int index;
for (index = 0; index < maxSlots; index++)
{
if (VopDbgStore[index].id != index) {
DEBUG_BREAK_MSG(("%s: DBG_VOP_LOCK: invalid id field (%d) in target entry (#%d).\n", funcname, VopDbgStore[index].id, index));
};
if ((VopDbgStore[index].vp != NULL) &&
((VopDbgStore[index].vp->v_data==NULL) || (VTOH(VopDbgStore[index].vp)->h_valid != HFS_VNODE_MAGIC)))
continue;
if (VopDbgStore[index].vp != NULL)
debug_check_vnode(VopDbgStore[index].vp, 0);
switch (VopDbgStore[index].inState)
{
case VOPDBG_IGNORE:
case VOPDBG_SAME:
break;
case VOPDBG_LOCKED:
case VOPDBG_UNLOCKED:
case VOPDBG_LOCKNOTNIL:
{
if (VopDbgStore[index].vp == NULL && (VopDbgStore[index].inState != VOPDBG_LOCKNOTNIL)) {
DBG_ERR (("%s: InState check: Null vnode ptr in entry #%d\n", funcname, index));
} else if (VopDbgStore[index].vp != NULL) {
switch (VopDbgStore[index].inState)
{
case VOPDBG_LOCKED:
case VOPDBG_LOCKNOTNIL:
if (VopDbgStore[index].inValue == 0)
{
DBG_ERR (("%s: Entry: not LOCKED:", funcname));
DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
DBG_ERR (("\n"));
}
break;
case VOPDBG_UNLOCKED:
if (VopDbgStore[index].inValue != 0)
{
DBG_ERR (("%s: Entry: not UNLOCKED:", funcname));
DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
DBG_ERR (("\n"));
}
break;
}
}
break;
}
default:
DBG_ERR (("%s: DBG_VOP_LOCK on entry: bad lock test value: %d\n", funcname, VopDbgStore[index].errState));
}
if (retval != 0)
{
switch (VopDbgStore[index].errState)
{
case VOPDBG_IGNORE:
break;
case VOPDBG_LOCKED:
case VOPDBG_UNLOCKED:
case VOPDBG_SAME:
{
if (VopDbgStore[index].vp == NULL) {
DBG_ERR (("%s: ErrState check: Null vnode ptr in entry #%d\n", funcname, index));
} else {
VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
switch (VopDbgStore[index].errState)
{
case VOPDBG_LOCKED:
if (VopDbgStore[index].outValue == 0)
{
DBG_ERR (("%s: Error: not LOCKED:", funcname));
DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
DBG_ERR(("\n"));
}
break;
case VOPDBG_UNLOCKED:
if (VopDbgStore[index].outValue != 0)
{
DBG_ERR (("%s: Error: not UNLOCKED:", funcname));
DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
DBG_ERR(("\n"));
}
break;
case VOPDBG_SAME:
if (VopDbgStore[index].outValue != VopDbgStore[index].inValue)
DBG_ERR (("%s: Error: In/Out locks are DIFFERENT: 0x%x, inis %d and out is %d\n", funcname, (u_int)VopDbgStore[index].vp, VopDbgStore[index].inValue, VopDbgStore[index].outValue));
break;
}
}
break;
}
case VOPDBG_LOCKNOTNIL:
if (VopDbgStore[index].vp != NULL) {
VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
if (VopDbgStore[index].outValue == 0)
DBG_ERR (("%s: Error: Not LOCKED: 0x%x\n", funcname, (u_int)VopDbgStore[index].vp));
}
break;
default:
DBG_ERR (("%s: Error: bad lock test value: %d\n", funcname, VopDbgStore[index].errState));
}
}
else
{
switch (VopDbgStore[index].outState)
{
case VOPDBG_IGNORE:
break;
case VOPDBG_LOCKED:
case VOPDBG_UNLOCKED:
case VOPDBG_SAME:
if (VopDbgStore[index].vp == NULL) {
DBG_ERR (("%s: OutState: Null vnode ptr in entry #%d\n", funcname, index));
};
if (VopDbgStore[index].vp != NULL)
{
VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
switch (VopDbgStore[index].outState)
{
case VOPDBG_LOCKED:
if (VopDbgStore[index].outValue == 0)
{
DBG_ERR (("%s: Out: not LOCKED:", funcname));
DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
DBG_ERR (("\n"));
}
break;
case VOPDBG_UNLOCKED:
if (VopDbgStore[index].outValue != 0)
{
DBG_ERR (("%s: Out: not UNLOCKED:", funcname));
DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp);
DBG_ERR (("\n"));
}
break;
case VOPDBG_SAME:
if (VopDbgStore[index].outValue != VopDbgStore[index].inValue)
DBG_ERR (("%s: Out: In/Out locks are DIFFERENT: 0x%x, in is %d and out is %d\n", funcname, (u_int)VopDbgStore[index].vp, VopDbgStore[index].inValue, VopDbgStore[index].outValue));
break;
}
}
break;
case VOPDBG_LOCKNOTNIL:
if (VopDbgStore[index].vp != NULL) {
if (&VTOH(VopDbgStore[index].vp)->h_lock == NULL) {
DBG_ERR (("%s: DBG_VOP_LOCK on out: Null lock on vnode 0x%x\n", funcname, (u_int)VopDbgStore[index].vp));
}
else {
VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
if (VopDbgStore[index].outValue == 0)
{
DBG_ERR (("%s: DBG_VOP_LOCK on out: Should be LOCKED:", funcname));
DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp); DBG_ERR (("\n"));
}
}
}
break;
default:
DBG_ERR (("%s: DBG_VOP_LOCK on out: bad lock test value: %d\n", funcname, VopDbgStore[index].outState));
}
}
VopDbgStore[index].id = -1;
}
}
#endif
int
hfsspec_read(ap)
struct vop_read_args *ap;
{
VTOH(ap->a_vp)->h_nodeflags |= IN_ACCESS;
return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
}
int
hfsspec_write(ap)
struct vop_write_args *ap;
{
VTOH(ap->a_vp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
}
int
hfsspec_close(ap)
struct vop_close_args *ap;
{
struct vnode *vp = ap->a_vp;
struct hfsnode *hp = VTOH(vp);
simple_lock(&vp->v_interlock);
if (ap->a_vp->v_usecount > 1)
HFSTIMES(hp, &time, &time);
simple_unlock(&vp->v_interlock);
return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap));
}
#if FIFO
int
hfsfifo_read(ap)
struct vop_read_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
VTOH(ap->a_vp)->h_nodeflags |= IN_ACCESS;
return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap));
}
int
hfsfifo_write(ap)
struct vop_write_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
VTOH(ap->a_vp)->h_nodeflags |= IN_CHANGE | IN_UPDATE;
return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap));
}
int
hfsfifo_close(ap)
struct vop_close_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
struct vnode *vp = ap->a_vp;
struct hfsnode *hp = VTOH(vp);
simple_lock(&vp->v_interlock);
if (ap->a_vp->v_usecount > 1)
HFSTIMES(hp, &time, &time);
simple_unlock(&vp->v_interlock);
return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_close), ap));
}
#endif
#define VOPFUNC int (*)(void *)
struct vnodeopv_entry_desc hfs_vnodeop_entries[] = {
{ &vop_default_desc, (VOPFUNC)vn_default_error },
{ &vop_lookup_desc, (VOPFUNC)hfs_cache_lookup },
{ &vop_create_desc, (VOPFUNC)hfs_create },
{ &vop_mknod_desc, (VOPFUNC)hfs_mknod },
{ &vop_open_desc, (VOPFUNC)hfs_open },
{ &vop_close_desc, (VOPFUNC)hfs_close },
{ &vop_access_desc, (VOPFUNC)hfs_access },
{ &vop_getattr_desc, (VOPFUNC)hfs_getattr },
{ &vop_setattr_desc, (VOPFUNC)hfs_setattr },
{ &vop_read_desc, (VOPFUNC)hfs_read },
{ &vop_write_desc, (VOPFUNC)hfs_write },
{ &vop_ioctl_desc, (VOPFUNC)hfs_ioctl },
{ &vop_select_desc, (VOPFUNC)hfs_select },
{ &vop_exchange_desc, (VOPFUNC)hfs_exchange },
{ &vop_mmap_desc, (VOPFUNC)hfs_mmap },
{ &vop_fsync_desc, (VOPFUNC)hfs_fsync },
{ &vop_seek_desc, (VOPFUNC)hfs_seek },
{ &vop_remove_desc, (VOPFUNC)hfs_remove },
#if HFS_HARDLINKS
{ &vop_link_desc, (VOPFUNC)hfs_link },
#else
{ &vop_link_desc, (VOPFUNC)err_link },
#endif
{ &vop_rename_desc, (VOPFUNC)hfs_rename },
{ &vop_mkdir_desc, (VOPFUNC)hfs_mkdir },
{ &vop_rmdir_desc, (VOPFUNC)hfs_rmdir },
{ &vop_mkcomplex_desc, (VOPFUNC)hfs_mkcomplex },
{ &vop_getattrlist_desc, (VOPFUNC)hfs_getattrlist },
{ &vop_setattrlist_desc, (VOPFUNC)hfs_setattrlist },
{ &vop_symlink_desc, (VOPFUNC)hfs_symlink },
{ &vop_readdir_desc, (VOPFUNC)hfs_readdir },
{ &vop_readdirattr_desc, (VOPFUNC)hfs_readdirattr },
{ &vop_readlink_desc, (VOPFUNC)hfs_readlink },
{ &vop_abortop_desc, (VOPFUNC)hfs_abortop },
{ &vop_inactive_desc, (VOPFUNC)hfs_inactive },
{ &vop_reclaim_desc, (VOPFUNC)hfs_reclaim },
{ &vop_lock_desc, (VOPFUNC)hfs_lock },
{ &vop_unlock_desc, (VOPFUNC)hfs_unlock },
{ &vop_bmap_desc, (VOPFUNC)hfs_bmap },
{ &vop_strategy_desc, (VOPFUNC)hfs_strategy },
{ &vop_print_desc, (VOPFUNC)hfs_print },
{ &vop_islocked_desc, (VOPFUNC)hfs_islocked },
{ &vop_pathconf_desc, (VOPFUNC)hfs_pathconf },
{ &vop_advlock_desc, (VOPFUNC)hfs_advlock },
{ &vop_reallocblks_desc, (VOPFUNC)hfs_reallocblks },
{ &vop_truncate_desc, (VOPFUNC)hfs_truncate },
{ &vop_allocate_desc, (VOPFUNC)hfs_allocate },
{ &vop_update_desc, (VOPFUNC)hfs_update },
{ &vop_searchfs_desc, (VOPFUNC)hfs_search },
{ &vop_bwrite_desc, (VOPFUNC)hfs_bwrite },
{ &vop_pagein_desc, (VOPFUNC)hfs_pagein },
{ &vop_pageout_desc,(VOPFUNC) hfs_pageout },
{ &vop_copyfile_desc, (VOPFUNC)err_copyfile },
{ &vop_blktooff_desc, (VOPFUNC)hfs_blktooff },
{ &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk },
{ &vop_cmap_desc, (VOPFUNC)hfs_cmap },
{ NULL, (VOPFUNC)NULL }
};
struct vnodeopv_desc hfs_vnodeop_opv_desc =
{ &hfs_vnodeop_p, hfs_vnodeop_entries };
int (**hfs_specop_p)(void *);
struct vnodeopv_entry_desc hfs_specop_entries[] = {
{ &vop_default_desc, (VOPFUNC)vn_default_error },
{ &vop_lookup_desc, (VOPFUNC)spec_lookup },
{ &vop_create_desc, (VOPFUNC)spec_create },
{ &vop_mknod_desc, (VOPFUNC)spec_mknod },
{ &vop_open_desc, (VOPFUNC)spec_open },
{ &vop_close_desc, (VOPFUNC)hfsspec_close },
{ &vop_access_desc, (VOPFUNC)hfs_access },
{ &vop_getattr_desc, (VOPFUNC)hfs_getattr },
{ &vop_setattr_desc, (VOPFUNC)hfs_setattr },
{ &vop_read_desc, (VOPFUNC)hfsspec_read },
{ &vop_write_desc, (VOPFUNC)hfsspec_write },
{ &vop_lease_desc, (VOPFUNC)spec_lease_check },
{ &vop_ioctl_desc, (VOPFUNC)spec_ioctl },
{ &vop_select_desc, (VOPFUNC)spec_select },
{ &vop_revoke_desc, (VOPFUNC)spec_revoke },
{ &vop_mmap_desc, (VOPFUNC)spec_mmap },
{ &vop_fsync_desc, (VOPFUNC)hfs_fsync },
{ &vop_seek_desc, (VOPFUNC)spec_seek },
{ &vop_remove_desc, (VOPFUNC)spec_remove },
{ &vop_link_desc, (VOPFUNC)spec_link },
{ &vop_rename_desc, (VOPFUNC)spec_rename },
{ &vop_mkdir_desc, (VOPFUNC)spec_mkdir },
{ &vop_rmdir_desc, (VOPFUNC)spec_rmdir },
{ &vop_symlink_desc, (VOPFUNC)spec_symlink },
{ &vop_readdir_desc, (VOPFUNC)spec_readdir },
{ &vop_readlink_desc, (VOPFUNC)spec_readlink },
{ &vop_abortop_desc, (VOPFUNC)spec_abortop },
{ &vop_inactive_desc, (VOPFUNC)hfs_inactive },
{ &vop_reclaim_desc, (VOPFUNC)hfs_reclaim },
{ &vop_lock_desc, (VOPFUNC)hfs_lock },
{ &vop_unlock_desc, (VOPFUNC)hfs_unlock },
{ &vop_bmap_desc, (VOPFUNC)spec_bmap },
{ &vop_strategy_desc, (VOPFUNC)spec_strategy },
{ &vop_print_desc, (VOPFUNC)hfs_print },
{ &vop_islocked_desc, (VOPFUNC)hfs_islocked },
{ &vop_pathconf_desc, (VOPFUNC)spec_pathconf },
{ &vop_advlock_desc, (VOPFUNC)spec_advlock },
{ &vop_blkatoff_desc, (VOPFUNC)spec_blkatoff },
{ &vop_valloc_desc, (VOPFUNC)spec_valloc },
{ &vop_reallocblks_desc, (VOPFUNC)spec_reallocblks },
{ &vop_vfree_desc, (VOPFUNC)err_vfree },
{ &vop_truncate_desc, (VOPFUNC)spec_truncate },
{ &vop_update_desc, (VOPFUNC)hfs_update },
{ &vop_bwrite_desc, (VOPFUNC)hfs_bwrite },
{ &vop_devblocksize_desc, (VOPFUNC)spec_devblocksize },
{ &vop_pagein_desc, (VOPFUNC)hfs_pagein },
{ &vop_pageout_desc, (VOPFUNC)hfs_pageout },
{ &vop_copyfile_desc, (VOPFUNC)err_copyfile },
{ &vop_blktooff_desc, (VOPFUNC)hfs_blktooff },
{ &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk },
{ (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
};
struct vnodeopv_desc hfs_specop_opv_desc =
{ &hfs_specop_p, hfs_specop_entries };
#if FIFO
int (**hfs_fifoop_p)(void *);
struct vnodeopv_entry_desc hfs_fifoop_entries[] = {
{ &vop_default_desc, (VOPFUNC)vn_default_error },
{ &vop_lookup_desc, (VOPFUNC)fifo_lookup },
{ &vop_create_desc, (VOPFUNC)fifo_create },
{ &vop_mknod_desc, (VOPFUNC)fifo_mknod },
{ &vop_open_desc, (VOPFUNC)fifo_open },
{ &vop_close_desc, (VOPFUNC)hfsfifo_close },
{ &vop_access_desc, (VOPFUNC)hfs_access },
{ &vop_getattr_desc, (VOPFUNC)hfs_getattr },
{ &vop_setattr_desc, (VOPFUNC)hfs_setattr },
{ &vop_read_desc, (VOPFUNC)hfsfifo_read },
{ &vop_write_desc, (VOPFUNC)hfsfifo_write },
{ &vop_lease_desc, (VOPFUNC)fifo_lease_check },
{ &vop_ioctl_desc, (VOPFUNC)fifo_ioctl },
{ &vop_select_desc, (VOPFUNC)fifo_select },
{ &vop_revoke_desc, (VOPFUNC)fifo_revoke },
{ &vop_mmap_desc, (VOPFUNC)fifo_mmap },
{ &vop_fsync_desc, (VOPFUNC)hfs_fsync },
{ &vop_seek_desc, (VOPFUNC)fifo_seek },
{ &vop_remove_desc, (VOPFUNC)fifo_remove },
{ &vop_link_desc, (VOPFUNC)fifo_link },
{ &vop_rename_desc, (VOPFUNC)fifo_rename },
{ &vop_mkdir_desc, (VOPFUNC)fifo_mkdir },
{ &vop_rmdir_desc, (VOPFUNC)fifo_rmdir },
{ &vop_symlink_desc, (VOPFUNC)fifo_symlink },
{ &vop_readdir_desc, (VOPFUNC)fifo_readdir },
{ &vop_readlink_desc, (VOPFUNC)fifo_readlink },
{ &vop_abortop_desc, (VOPFUNC)fifo_abortop },
{ &vop_inactive_desc, (VOPFUNC)hfs_inactive },
{ &vop_reclaim_desc, (VOPFUNC)hfs_reclaim },
{ &vop_lock_desc, (VOPFUNC)hfs_lock },
{ &vop_unlock_desc, (VOPFUNC)hfs_unlock },
{ &vop_bmap_desc, (VOPFUNC)fifo_bmap },
{ &vop_strategy_desc, (VOPFUNC)fifo_strategy },
{ &vop_print_desc, (VOPFUNC)hfs_print },
{ &vop_islocked_desc, (VOPFUNC)hfs_islocked },
{ &vop_pathconf_desc, (VOPFUNC)fifo_pathconf },
{ &vop_advlock_desc, (VOPFUNC)fifo_advlock },
{ &vop_blkatoff_desc, (VOPFUNC)fifo_blkatoff },
{ &vop_valloc_desc, (VOPFUNC)fifo_valloc },
{ &vop_reallocblks_desc, (VOPFUNC)fifo_reallocblks },
{ &vop_vfree_desc, (VOPFUNC)err_vfree },
{ &vop_truncate_desc, (VOPFUNC)fifo_truncate },
{ &vop_update_desc, (VOPFUNC)hfs_update },
{ &vop_bwrite_desc, (VOPFUNC)hfs_bwrite },
{ &vop_pagein_desc, (VOPFUNC)hfs_pagein },
{ &vop_pageout_desc, (VOPFUNC)hfs_pageout },
{ &vop_copyfile_desc, (VOPFUNC)err_copyfile },
{ &vop_blktooff_desc, (VOPFUNC)hfs_blktooff },
{ &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk },
{ &vop_cmap_desc, (VOPFUNC)hfs_cmap },
{ (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
};
struct vnodeopv_desc hfs_fifoop_opv_desc =
{ &hfs_fifoop_p, hfs_fifoop_entries };
#endif