#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/ubc.h>
#include <sys/quota.h>
#include <sys/time.h>
#include <sys/disk.h>
#include <miscfs/specfs/specdev.h>
#include <miscfs/fifofs/fifo.h>
#include <vfs/vfs_support.h>
#include <machine/spl.h>
#include <sys/kdebug.h>
#include "hfs.h"
#include "hfs_catalog.h"
#include "hfs_cnode.h"
#include "hfs_lockf.h"
#include "hfs_dbg.h"
#include "hfs_mount.h"
#include "hfs_quota.h"
#include "hfs_endian.h"
#include "hfscommon/headers/BTreesInternal.h"
#include "hfscommon/headers/FileMgrInternal.h"
#define MAKE_DELETED_NAME(NAME,FID) \
(void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
#define KNDETACH_VNLOCKED 0x00000001
#define CARBON_TEMP_DIR_NAME "Cleanup At Startup"
extern unsigned long strtoul(const char *, char **, int);
extern int groupmember(gid_t gid, struct ucred *cred);
static int hfs_makenode(int mode, struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp);
static int hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp,
struct vnode **rvpp, struct proc *p);
static int hfs_metasync(struct hfsmount *hfsmp, daddr_t node, struct proc *p);
static int hfs_removedir(struct vnode *, struct vnode *, struct componentname *,
int);
static int hfs_removefile(struct vnode *, struct vnode *, struct componentname *,
int);
#define HFSRM_PARENT_LOCKED 0x01
#define HFSRM_SKIP_RESERVE 0x02
#define HFSRM_SAVE_NAME 0x04
#define HFSRM_RENAMEOPTS 0x07
int hfs_write_access(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean considerFlags);
int hfs_chflags(struct vnode *vp, u_long flags, struct ucred *cred,
struct proc *p);
int hfs_chmod(struct vnode *vp, int mode, struct ucred *cred,
struct proc *p);
int hfs_chown(struct vnode *vp, uid_t uid, gid_t gid,
struct ucred *cred, struct proc *p);
static int
hfs_create(ap)
struct vop_create_args *ap;
{
struct vattr *vap = ap->a_vap;
return (hfs_makenode(MAKEIMODE(vap->va_type, vap->va_mode),
ap->a_dvp, ap->a_vpp, ap->a_cnp));
}
static int
hfs_mknod(ap)
struct vop_mknod_args *ap;
{
struct vattr *vap = ap->a_vap;
struct vnode **vpp = ap->a_vpp;
struct cnode *cp;
int error;
if (VTOVCB(ap->a_dvp)->vcbSigWord != kHFSPlusSigWord) {
VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
vput(ap->a_dvp);
return (EOPNOTSUPP);
}
error = hfs_makenode(MAKEIMODE(vap->va_type, vap->va_mode),
ap->a_dvp, vpp, ap->a_cnp);
if (error)
return (error);
cp = VTOC(*vpp);
cp->c_flag |= C_ACCESS | C_CHANGE | C_UPDATE;
if ((vap->va_rdev != VNOVAL) &&
(vap->va_type == VBLK || vap->va_type == VCHR))
cp->c_rdev = vap->va_rdev;
vput(*vpp);
vgone(*vpp);
*vpp = 0;
return (0);
}
static int
hfs_open(ap)
struct vop_open_args *ap;
{
struct vnode *vp = ap->a_vp;
struct filefork *fp = VTOF(vp);
struct timeval tv;
if ((vp->v_type != VDIR) && (VTOC(vp)->c_flags & APPEND) &&
(ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
return (EPERM);
if (ap->a_mode & O_EVTONLY) {
if (vp->v_type == VREG) {
++VTOF(vp)->ff_evtonly_refs;
} else {
++VTOC(vp)->c_evtonly_refs;
};
};
if ((VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) ||
!UBCISVALID(vp) || ubc_isinuse(vp, 1)) {
return (0);
}
fp = VTOF(vp);
if (fp->ff_blocks &&
fp->ff_extents[7].blockCount != 0 &&
fp->ff_size <= (20 * 1024 * 1024)) {
microuptime(&tv);
if (tv.tv_sec < (60 * 3)) {
return (0);
}
(void) hfs_relocate(vp, VTOVCB(vp)->nextAllocation + 4096, ap->a_cred, ap->a_p);
}
return (0);
}
static int
hfs_close(ap)
struct vop_close_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct cnode *cp = VTOC(vp);
register struct filefork *fp = VTOF(vp);
struct proc *p = ap->a_p;
struct timeval tv;
off_t leof;
u_long blks, blocksize;
int devBlockSize;
int error;
simple_lock(&vp->v_interlock);
if ((!UBCISVALID(vp) && vp->v_usecount > 1)
|| (UBCISVALID(vp) && ubc_isinuse(vp, 1))) {
tv = time;
CTIMES(cp, &tv, &tv);
}
simple_unlock(&vp->v_interlock);
if (ap->a_fflag & O_EVTONLY) {
if (vp->v_type == VREG) {
--VTOF(vp)->ff_evtonly_refs;
} else {
--VTOC(vp)->c_evtonly_refs;
};
};
if (vp->v_type == VDIR || VOP_ISLOCKED(vp))
return (0);
leof = fp->ff_size;
if ((fp->ff_blocks > 0) &&
!ISSET(cp->c_flag, C_DELETED) &&
((VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) == 0)) {
enum vtype our_type = vp->v_type;
u_long our_id = vp->v_id;
int was_nocache = ISSET(vp->v_flag, VNOCACHE_DATA);
error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
if (error)
return (0);
if (vp->v_type != our_type || vp->v_id != our_id
|| cp != VTOC(vp) || !UBCINFOEXISTS(vp)) {
VOP_UNLOCK(vp, 0, p);
return (0);
}
VOP_DEVBLOCKSIZE(cp->c_devvp, &devBlockSize);
(void) cluster_push(vp);
SET(vp->v_flag, VNOCACHE_DATA);
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);
(void) cluster_write(vp, (struct uio *) 0, leof,
invalid_range->rl_end + 1, invalid_range->rl_start,
(off_t)0, devBlockSize, IO_HEADZEROFILL | IO_NOZERODIRTY);
if (ISSET(vp->v_flag, VHASDIRTY))
(void) cluster_push(vp);
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) VOP_TRUNCATE(vp, leof, IO_NDELAY, ap->a_cred, p);
(void) cluster_push(vp);
if (!was_nocache)
CLR(vp->v_flag, VNOCACHE_DATA);
if (cp->c_flag & C_MODIFIED) {
tv = time;
VOP_UPDATE(vp, &tv, &tv, 0);
}
VOP_UNLOCK(vp, 0, p);
}
if ((vp->v_flag & VSYSTEM) && (vp->v_usecount == 1))
vgone(vp);
return (0);
}
static int
hfs_access(ap)
struct vop_access_args *ap;
{
struct vnode *vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
struct ucred *cred = ap->a_cred;
register gid_t *gp;
mode_t mode = ap->a_mode;
mode_t mask = 0;
int i;
int error;
if (mode & VWRITE) {
switch (vp->v_type) {
case VDIR:
case VLNK:
case VREG:
if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
return (EROFS);
#if QUOTA
if ((error = hfs_getinoquota(cp)))
return (error);
#endif
break;
}
if (cp->c_flags & IMMUTABLE)
return (EPERM);
}
if (cred->cr_uid == 0)
return (0);
mask = 0;
if ( (cp->c_uid == cred->cr_uid) || (cp->c_uid == UNKNOWNUID) ) {
if (mode & VEXEC)
mask |= S_IXUSR;
if (mode & VREAD)
mask |= S_IRUSR;
if (mode & VWRITE)
mask |= S_IWUSR;
return ((cp->c_mode & mask) == mask ? 0 : EACCES);
}
if (! (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS)) {
for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
if (cp->c_gid == *gp) {
if (mode & VEXEC)
mask |= S_IXGRP;
if (mode & VREAD)
mask |= S_IRGRP;
if (mode & VWRITE)
mask |= S_IWGRP;
return ((cp->c_mode & mask) == mask ? 0 : EACCES);
}
}
if (mode & VEXEC)
mask |= S_IXOTH;
if (mode & VREAD)
mask |= S_IROTH;
if (mode & VWRITE)
mask |= S_IWOTH;
return ((cp->c_mode & mask) == mask ? 0 : EACCES);
}
static int
hfs_getattr(ap)
struct vop_getattr_args *ap;
{
struct vnode *vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
struct vattr *vap = ap->a_vap;
struct timeval tv;
tv = time;
CTIMES(cp, &tv, &tv);
vap->va_type = vp->v_type;
vap->va_mode = cp->c_mode;
vap->va_nlink = cp->c_nlink;
if (cp->c_uid == UNKNOWNUID) {
vap->va_mode &= ~(S_ISUID | S_ISGID);
vap->va_uid = ap->a_cred->cr_uid;
} else {
vap->va_uid = cp->c_uid;
}
vap->va_gid = cp->c_gid;
vap->va_fsid = cp->c_dev;
vap->va_fileid = cp->c_fileid;
vap->va_atime.tv_sec = cp->c_atime;
vap->va_atime.tv_nsec = 0;
vap->va_mtime.tv_sec = cp->c_mtime;
vap->va_mtime.tv_nsec = cp->c_mtime_nsec;
vap->va_ctime.tv_sec = cp->c_ctime;
vap->va_ctime.tv_nsec = 0;
vap->va_gen = 0;
vap->va_flags = cp->c_flags;
vap->va_rdev = 0;
vap->va_blocksize = VTOVFS(vp)->mnt_stat.f_iosize;
vap->va_filerev = 0;
if (vp->v_type == VDIR) {
vap->va_size = cp->c_nlink * AVERAGE_HFSDIRENTRY_SIZE;
vap->va_bytes = 0;
} else {
vap->va_size = VTOF(vp)->ff_size;
vap->va_bytes = (u_quad_t)cp->c_blocks *
(u_quad_t)VTOVCB(vp)->blockSize;
if (vp->v_type == VBLK || vp->v_type == VCHR)
vap->va_rdev = cp->c_rdev;
}
return (0);
}
static int
hfs_setattr(ap)
struct vop_setattr_args *ap;
{
struct vattr *vap = ap->a_vap;
struct vnode *vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
struct ucred *cred = ap->a_cred;
struct proc *p = ap->a_p;
struct timeval atimeval, mtimeval;
int error;
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)) {
return (EINVAL);
}
if (vp->v_type == VLNK) {
return 0;
}
if (vap->va_flags != VNOVAL) {
if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
return (EROFS);
if ((error = hfs_chflags(vp, vap->va_flags, cred, p)))
return (error);
if (vap->va_flags & (IMMUTABLE | APPEND))
return (0);
}
if (cp->c_flags & (IMMUTABLE | APPEND))
return (EPERM);
if (VTOHFS(vp)->jnl && cp->c_datafork) {
struct HFSPlusExtentDescriptor *extd;
extd = &cp->c_datafork->ff_extents[0];
if (extd->startBlock == VTOVCB(vp)->vcbJinfoBlock || extd->startBlock == VTOHFS(vp)->jnl_start) {
return EPERM;
}
}
if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
return (EROFS);
if ((error = hfs_chown(vp, vap->va_uid, vap->va_gid, cred, p)))
return (error);
}
if (vap->va_size != VNOVAL) {
switch (vp->v_type) {
case VDIR:
return (EISDIR);
case VLNK:
case VREG:
if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
return (EROFS);
break;
default:
break;
}
if ((error = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p)))
return (error);
}
cp = VTOC(vp);
if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
return (EROFS);
if (((error = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, true)) != 0) &&
((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
(error = VOP_ACCESS(vp, VWRITE, cred, p)))) {
return (error);
}
if (vap->va_atime.tv_sec != VNOVAL)
cp->c_flag |= C_ACCESS;
if (vap->va_mtime.tv_sec != VNOVAL) {
cp->c_flag |= C_CHANGE | C_UPDATE;
if ((VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) &&
(cp->c_cnid != kRootDirID) &&
(vap->va_mtime.tv_sec < cp->c_itime)) {
cp->c_itime = 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 ((error = VOP_UPDATE(vp, &atimeval, &mtimeval, 1)))
return (error);
}
error = 0;
if (vap->va_mode != (mode_t)VNOVAL) {
if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
return (EROFS);
error = hfs_chmod(vp, (int)vap->va_mode, cred, p);
}
HFS_KNOTE(vp, NOTE_ATTRIB);
return (error);
}
__private_extern__
int
hfs_chmod(vp, mode, cred, p)
register struct vnode *vp;
register int mode;
register struct ucred *cred;
struct proc *p;
{
register struct cnode *cp = VTOC(vp);
int error;
if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
return (0);
if (VTOHFS(vp)->jnl && cp && cp->c_datafork) {
struct HFSPlusExtentDescriptor *extd;
extd = &cp->c_datafork->ff_extents[0];
if (extd->startBlock == VTOVCB(vp)->vcbJinfoBlock || extd->startBlock == VTOHFS(vp)->jnl_start) {
return EPERM;
}
}
#if OVERRIDE_UNKNOWN_PERMISSIONS
if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
return (0);
};
#endif
if ((error = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, true)) != 0)
return (error);
if (cred->cr_uid) {
if (vp->v_type != VDIR && (mode & S_ISTXT))
return (EFTYPE);
if (!groupmember(cp->c_gid, cred) && (mode & S_ISGID))
return (EPERM);
}
cp->c_mode &= ~ALLPERMS;
cp->c_mode |= (mode & ALLPERMS);
cp->c_flag |= C_CHANGE;
return (0);
}
__private_extern__
int
hfs_write_access(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean considerFlags)
{
struct cnode *cp = VTOC(vp);
gid_t *gp;
int retval = 0;
int i;
switch (vp->v_type) {
case VDIR:
case VLNK:
case VREG:
if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
return (EROFS);
break;
default:
break;
}
if (considerFlags && (cp->c_flags & IMMUTABLE))
return (EPERM);
if (cred->cr_uid == 0)
return (0);
if ((retval = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, false)) == 0)
return ((cp->c_mode & S_IWUSR) == S_IWUSR ? 0 : EACCES);
for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) {
if (cp->c_gid == *gp)
return ((cp->c_mode & S_IWGRP) == S_IWGRP ? 0 : EACCES);
}
return ((cp->c_mode & S_IWOTH) == S_IWOTH ? 0 : EACCES);
}
__private_extern__
int
hfs_chflags(vp, flags, cred, p)
register struct vnode *vp;
register u_long flags;
register struct ucred *cred;
struct proc *p;
{
register struct cnode *cp = VTOC(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(VTOHFS(vp), cp->c_uid, cred, p, true)) != 0) {
return retval;
};
if (cred->cr_uid == 0) {
if ((cp->c_flags & (SF_IMMUTABLE | SF_APPEND)) &&
securelevel > 0) {
return EPERM;
};
cp->c_flags = flags;
} else {
if (cp->c_flags & (SF_IMMUTABLE | SF_APPEND) ||
(flags & UF_SETTABLE) != flags) {
return EPERM;
};
cp->c_flags &= SF_SETTABLE;
cp->c_flags |= (flags & UF_SETTABLE);
}
cp->c_flag |= C_CHANGE;
return (0);
}
__private_extern__
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 cnode *cp = VTOC(vp);
uid_t ouid;
gid_t ogid;
int error = 0;
#if QUOTA
register int i;
int64_t change;
#endif
if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
return (EOPNOTSUPP);
if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS)
return (0);
if (uid == (uid_t)VNOVAL)
uid = cp->c_uid;
if (gid == (gid_t)VNOVAL)
gid = cp->c_gid;
if ((cred->cr_uid != cp->c_uid || uid != cp->c_uid ||
(gid != cp->c_gid && !groupmember((gid_t)gid, cred))) &&
(error = suser(cred, &p->p_acflag)))
return (error);
ogid = cp->c_gid;
ouid = cp->c_uid;
#if QUOTA
if ((error = hfs_getinoquota(cp)))
return (error);
if (ouid == uid) {
dqrele(vp, cp->c_dquot[USRQUOTA]);
cp->c_dquot[USRQUOTA] = NODQUOT;
}
if (ogid == gid) {
dqrele(vp, cp->c_dquot[GRPQUOTA]);
cp->c_dquot[GRPQUOTA] = NODQUOT;
}
change = (int64_t)(cp->c_blocks) * (int64_t)VTOVCB(vp)->blockSize;
(void) hfs_chkdq(cp, -change, cred, CHOWN);
(void) hfs_chkiq(cp, -1, cred, CHOWN);
for (i = 0; i < MAXQUOTAS; i++) {
dqrele(vp, cp->c_dquot[i]);
cp->c_dquot[i] = NODQUOT;
}
#endif
cp->c_gid = gid;
cp->c_uid = uid;
#if QUOTA
if ((error = hfs_getinoquota(cp)) == 0) {
if (ouid == uid) {
dqrele(vp, cp->c_dquot[USRQUOTA]);
cp->c_dquot[USRQUOTA] = NODQUOT;
}
if (ogid == gid) {
dqrele(vp, cp->c_dquot[GRPQUOTA]);
cp->c_dquot[GRPQUOTA] = NODQUOT;
}
if ((error = hfs_chkdq(cp, change, cred, CHOWN)) == 0) {
if ((error = hfs_chkiq(cp, 1, cred, CHOWN)) == 0)
goto good;
else
(void) hfs_chkdq(cp, -change, cred, CHOWN|FORCE);
}
for (i = 0; i < MAXQUOTAS; i++) {
dqrele(vp, cp->c_dquot[i]);
cp->c_dquot[i] = NODQUOT;
}
}
cp->c_gid = ogid;
cp->c_uid = ouid;
if (hfs_getinoquota(cp) == 0) {
if (ouid == uid) {
dqrele(vp, cp->c_dquot[USRQUOTA]);
cp->c_dquot[USRQUOTA] = NODQUOT;
}
if (ogid == gid) {
dqrele(vp, cp->c_dquot[GRPQUOTA]);
cp->c_dquot[GRPQUOTA] = NODQUOT;
}
(void) hfs_chkdq(cp, change, cred, FORCE|CHOWN);
(void) hfs_chkiq(cp, 1, cred, FORCE|CHOWN);
(void) hfs_getinoquota(cp);
}
return (error);
good:
if (hfs_getinoquota(cp))
panic("hfs_chown: lost quota");
#endif
if (ouid != uid || ogid != gid)
cp->c_flag |= C_CHANGE;
if (ouid != uid && cred->cr_uid != 0)
cp->c_mode &= ~S_ISUID;
if (ogid != gid && cred->cr_uid != 0)
cp->c_mode &= ~S_ISGID;
return (0);
}
static int
hfs_exchange(ap)
struct vop_exchange_args *ap;
{
struct vnode *from_vp = ap->a_fvp;
struct vnode *to_vp = ap->a_tvp;
struct cnode *from_cp = VTOC(from_vp);
struct cnode *to_cp = VTOC(to_vp);
struct hfsmount *hfsmp = VTOHFS(from_vp);
struct cat_desc tempdesc;
struct cat_attr tempattr;
int error = 0, started_tr = 0, grabbed_lock = 0;
cat_cookie_t cookie = {0};
if (from_vp->v_mount != to_vp->v_mount)
return (EXDEV);
if ((from_vp->v_type != VREG) || (to_vp->v_type != VREG) ||
(from_cp->c_flag & C_HARDLINK) || (to_cp->c_flag & C_HARDLINK) ||
VNODE_IS_RSRC(from_vp) || VNODE_IS_RSRC(to_vp))
return (EINVAL);
if (hfsmp->jnl) {
struct HFSPlusExtentDescriptor *extd;
if (from_cp->c_datafork) {
extd = &from_cp->c_datafork->ff_extents[0];
if (extd->startBlock == VTOVCB(from_vp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) {
return EPERM;
}
}
if (to_cp->c_datafork) {
extd = &to_cp->c_datafork->ff_extents[0];
if (extd->startBlock == VTOVCB(to_vp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) {
return EPERM;
}
}
}
hfs_global_shared_lock_acquire(hfsmp);
grabbed_lock = 1;
if (hfsmp->jnl) {
if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
goto Err_Exit;
}
started_tr = 1;
}
if ((error = cat_preflight(hfsmp, CAT_EXCHANGE, &cookie, ap->a_p))) {
goto Err_Exit;
}
error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, ap->a_p);
if (error) goto Err_Exit;
error = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
if (error) {
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, ap->a_p);
goto Err_Exit;
}
error = MacToVFSError(ExchangeFileIDs(HFSTOVCB(hfsmp),
from_cp->c_desc.cd_nameptr, to_cp->c_desc.cd_nameptr,
from_cp->c_parentcnid, to_cp->c_parentcnid,
from_cp->c_hint, to_cp->c_hint));
(void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, ap->a_p);
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, ap->a_p);
if (error != E_NONE) {
goto Err_Exit;
}
if (from_vp)
cache_purge(from_vp);
if (to_vp)
cache_purge(to_vp);
bcopy(&from_cp->c_desc, &tempdesc, sizeof(struct cat_desc));
bcopy(&from_cp->c_attr, &tempattr, sizeof(struct cat_attr));
bcopy(&to_cp->c_desc, &from_cp->c_desc, sizeof(struct cat_desc));
from_cp->c_hint = 0;
from_cp->c_fileid = from_cp->c_cnid;
from_cp->c_itime = to_cp->c_itime;
from_cp->c_btime = to_cp->c_btime;
from_cp->c_atime = to_cp->c_atime;
from_cp->c_ctime = to_cp->c_ctime;
from_cp->c_gid = to_cp->c_gid;
from_cp->c_uid = to_cp->c_uid;
from_cp->c_flags = to_cp->c_flags;
from_cp->c_mode = to_cp->c_mode;
bcopy(to_cp->c_finderinfo, from_cp->c_finderinfo, 32);
bcopy(&tempdesc, &to_cp->c_desc, sizeof(struct cat_desc));
to_cp->c_hint = 0;
to_cp->c_fileid = to_cp->c_cnid;
to_cp->c_itime = tempattr.ca_itime;
to_cp->c_btime = tempattr.ca_btime;
to_cp->c_atime = tempattr.ca_atime;
to_cp->c_ctime = tempattr.ca_ctime;
to_cp->c_gid = tempattr.ca_gid;
to_cp->c_uid = tempattr.ca_uid;
to_cp->c_flags = tempattr.ca_flags;
to_cp->c_mode = tempattr.ca_mode;
bcopy(tempattr.ca_finderinfo, to_cp->c_finderinfo, 32);
hfs_chashremove(from_cp);
hfs_chashremove(to_cp);
hfs_chashinsert(from_cp);
hfs_chashinsert(to_cp);
if ((from_cp->c_flags & UF_NODUMP) &&
(from_cp->c_parentcnid != to_cp->c_parentcnid)) {
from_cp->c_flags &= ~UF_NODUMP;
from_cp->c_flag |= C_CHANGE;
}
if ((to_cp->c_flags & UF_NODUMP) &&
(to_cp->c_parentcnid != from_cp->c_parentcnid)) {
to_cp->c_flags &= ~UF_NODUMP;
to_cp->c_flag |= C_CHANGE;
}
HFS_KNOTE(from_vp, NOTE_ATTRIB);
HFS_KNOTE(to_vp, NOTE_ATTRIB);
Err_Exit:
cat_postflight(hfsmp, &cookie, ap->a_p);
if (started_tr) {
journal_end_transaction(hfsmp->jnl);
}
if (grabbed_lock) {
hfs_global_shared_lock_release(hfsmp);
}
return (error);
}
static int
hfs_fsync(ap)
struct vop_fsync_args *ap;
{
struct vnode *vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
struct filefork *fp = NULL;
int retval = 0;
register struct buf *bp;
struct timeval tv;
struct buf *nbp;
struct hfsmount *hfsmp = VTOHFS(ap->a_vp);
int s;
int wait;
int retry = 0;
wait = (ap->a_waitfor == MNT_WAIT);
if (vp->v_type == VDIR)
goto metasync;
if (vp->v_flag & VSYSTEM) {
if (VTOF(vp)->fcbBTCBPtr != NULL) {
if (hfsmp->jnl == NULL) {
BTFlushPath(VTOF(vp));
}
}
} else if (UBCINFOEXISTS(vp))
(void) cluster_push(vp);
if ((wait || (cp->c_flag & C_ZFWANTSYNC)) &&
((cp->c_flags & UF_NODUMP) == 0) &&
UBCINFOEXISTS(vp) && (fp = VTOF(vp)) &&
cp->c_zftimeout != 0) {
int devblksize;
int was_nocache;
if (time.tv_sec < cp->c_zftimeout) {
cp->c_flag |= C_ZFWANTSYNC;
goto loop;
}
VOP_DEVBLOCKSIZE(cp->c_devvp, &devblksize);
was_nocache = ISSET(vp->v_flag, VNOCACHE_DATA);
SET(vp->v_flag, VNOCACHE_DATA);
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);
(void) cluster_write(vp, (struct uio *) 0,
fp->ff_size,
invalid_range->rl_end + 1,
invalid_range->rl_start,
(off_t)0, devblksize,
IO_HEADZEROFILL | IO_NOZERODIRTY);
cp->c_flag |= C_MODIFIED;
}
(void) cluster_push(vp);
if (!was_nocache)
CLR(vp->v_flag, VNOCACHE_DATA);
cp->c_flag &= ~C_ZFWANTSYNC;
cp->c_zftimeout = 0;
}
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: bp 0x% not dirty (hfsmp 0x%x)", bp, hfsmp);
if (hfsmp->jnl && (bp->b_flags & B_LOCKED)) {
if ((bp->b_flags & B_META) == 0) {
panic("hfs: bp @ 0x%x is locked but not meta! jnl 0x%x\n",
bp, hfsmp->jnl);
}
if (journal_active(hfsmp->jnl) >= 0) {
continue;
}
}
bremfree(bp);
bp->b_flags |= B_BUSY;
bp->b_flags &= ~B_LOCKED;
splx(s);
if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT)
(void) bawrite(bp);
else
(void) VOP_BWRITE(bp);
goto loop;
}
if (wait) {
while (vp->v_numoutput) {
vp->v_flag |= VBWAIT;
tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "hfs_fsync", 0);
}
if (hfsmp->jnl == NULL && vp->v_dirtyblkhd.lh_first) {
if (retry++ > 10) {
vprint("hfs_fsync: dirty", vp);
splx(s);
(void)tsleep((caddr_t)&vp->v_numoutput,
PRIBIO + 1, "hfs_fsync", hz/10);
retry = 0;
} else {
splx(s);
}
goto loop;
}
}
splx(s);
metasync:
tv = time;
if (vp->v_flag & VSYSTEM) {
if (VTOF(vp)->fcbBTCBPtr != NULL)
BTSetLastSync(VTOF(vp), tv.tv_sec);
cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE);
} else {
retval = VOP_UPDATE(ap->a_vp, &tv, &tv, wait);
if ((retval == 0) && wait && cp->c_hint &&
!ISSET(cp->c_flag, C_DELETED | C_NOEXISTS)) {
hfs_metasync(VTOHFS(vp), cp->c_hint, ap->a_p);
}
if (vp->v_flag & VFULLFSYNC) {
if (hfsmp->jnl) {
journal_flush(hfsmp->jnl);
} else {
VOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, NOCRED, ap->a_p);
}
}
}
return (retval);
}
static int
hfs_metasync(struct hfsmount *hfsmp, daddr_t node, struct proc *p)
{
struct vnode *vp;
struct buf *bp;
struct buf *nbp;
int s;
vp = HFSTOVCB(hfsmp)->catalogRefNum;
if (hfsmp->jnl) {
return 0;
}
if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p) != 0)
return (0);
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_lblkno == node) {
if (bp->b_flags & B_LOCKED)
break;
bremfree(bp);
bp->b_flags |= B_BUSY;
splx(s);
(void) VOP_BWRITE(bp);
goto exit;
}
}
splx(s);
exit:
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
return (0);
}
__private_extern__
int
hfs_btsync(struct vnode *vp, int sync_transaction)
{
struct cnode *cp = VTOC(vp);
register struct buf *bp;
struct timeval tv;
struct buf *nbp;
struct hfsmount *hfsmp = VTOHFS(vp);
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_btsync: not dirty (bp 0x%x hfsmp 0x%x)", bp, hfsmp);
if (hfsmp->jnl && (bp->b_flags & B_LOCKED)) {
if ((bp->b_flags & B_META) == 0) {
panic("hfs: bp @ 0x%x is locked but not meta! jnl 0x%x\n",
bp, hfsmp->jnl);
}
if (journal_active(hfsmp->jnl) >= 0) {
continue;
}
}
if (sync_transaction && !(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) && (VTOF(vp)->fcbBTCBPtr != NULL))
(void) BTSetLastSync(VTOF(vp), tv.tv_sec);
cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE);
return 0;
}
static int
hfs_rmdir(ap)
struct vop_rmdir_args *ap;
{
return (hfs_removedir(ap->a_dvp, ap->a_vp, ap->a_cnp, 0));
}
static int
hfs_removedir(dvp, vp, cnp, options)
struct vnode *dvp;
struct vnode *vp;
struct componentname *cnp;
int options;
{
struct proc *p = cnp->cn_proc;
struct cnode *cp;
struct cnode *dcp;
struct hfsmount * hfsmp;
struct timeval tv;
cat_cookie_t cookie = {0};
int error = 0, started_tr = 0, grabbed_lock = 0;
cp = VTOC(vp);
dcp = VTOC(dvp);
hfsmp = VTOHFS(vp);
if (dcp == cp) {
vrele(dvp);
vput(vp);
return (EINVAL);
}
#if QUOTA
(void)hfs_getinoquota(cp);
#endif
hfs_global_shared_lock_acquire(hfsmp);
grabbed_lock = 1;
if (hfsmp->jnl) {
if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
goto out;
}
started_tr = 1;
}
if (!(options & HFSRM_SKIP_RESERVE)) {
if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) {
goto out;
}
}
if (cp->c_entries != 0) {
error = ENOTEMPTY;
goto out;
}
if ((dcp->c_flags & APPEND) || (cp->c_flags & (IMMUTABLE | APPEND))) {
error = EPERM;
goto out;
}
cache_purge(vp);
error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (error) goto out;
if (cp->c_entries > 0)
panic("hfs_rmdir: attempting to delete a non-empty directory!");
error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if (error) goto out;
#if QUOTA
(void)hfs_chkiq(cp, -1, NOCRED, 0);
#endif
if (dcp->c_entries > 0)
dcp->c_entries--;
if (dcp->c_nlink > 0)
dcp->c_nlink--;
dcp->c_flag |= C_CHANGE | C_UPDATE;
tv = time;
(void) VOP_UPDATE(dvp, &tv, &tv, 0);
HFS_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
hfs_volupdate(hfsmp, VOL_RMDIR, (dcp->c_cnid == kHFSRootFolderID));
cp->c_mode = 0;
cp->c_flag |= C_NOEXISTS;
out:
if (!(options & HFSRM_PARENT_LOCKED)) {
vput(dvp);
}
HFS_KNOTE(vp, NOTE_DELETE);
vput(vp);
if (!(options & HFSRM_SKIP_RESERVE)) {
cat_postflight(hfsmp, &cookie, p);
}
if (started_tr) {
journal_end_transaction(hfsmp->jnl);
}
if (grabbed_lock) {
hfs_global_shared_lock_release(hfsmp);
}
return (error);
}
static int
hfs_remove(ap)
struct vop_remove_args *ap;
{
return (hfs_removefile(ap->a_dvp, ap->a_vp, ap->a_cnp, 0));
}
static int
hfs_removefile(dvp, vp, cnp, options)
struct vnode *dvp;
struct vnode *vp;
struct componentname *cnp;
int options;
{
struct vnode *rvp = NULL;
struct cnode *cp;
struct cnode *dcp;
struct hfsmount *hfsmp;
struct proc *p = cnp->cn_proc;
int dataforkbusy = 0;
int rsrcforkbusy = 0;
int truncated = 0;
struct timeval tv;
cat_cookie_t cookie = {0};
int error = 0;
int started_tr = 0, grabbed_lock = 0;
int refcount, isbigfile = 0;
if (vp->v_type == VDIR) {
error = EISDIR;
goto out;
}
cp = VTOC(vp);
dcp = VTOC(dvp);
hfsmp = VTOHFS(vp);
if (cp->c_parentcnid != dcp->c_cnid) {
error = EINVAL;
goto out;
}
if ((cp->c_flags & (IMMUTABLE | APPEND)) ||
(VTOC(dvp)->c_flags & APPEND) ||
VNODE_IS_RSRC(vp)) {
error = EPERM;
goto out;
}
if (cp->c_blocks - VTOF(vp)->ff_blocks) {
error = hfs_vgetrsrc(hfsmp, vp, &rvp, p);
if (error)
goto out;
}
if (hfsmp->jnl && cp->c_datafork) {
struct HFSPlusExtentDescriptor *extd;
extd = &cp->c_datafork->ff_extents[0];
if (extd->startBlock == HFSTOVCB(hfsmp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) {
error = EPERM;
goto out;
}
}
if (VTOC(vp)->c_flag & C_VPREFHELD) {
refcount = 2;
} else {
refcount = 1;
}
if (UBCISVALID(vp) && ubc_isinuse(vp, refcount))
dataforkbusy = 1;
if (rvp && UBCISVALID(rvp) && ubc_isinuse(rvp, 1))
rsrcforkbusy = 1;
isbigfile = (VTOC(vp)->c_datafork->ff_size >= HFS_BIGFILE_SIZE);
if ((dataforkbusy || rsrcforkbusy) &&
((cnp->cn_flags & NODELETEBUSY) ||
(hfsmp->hfs_privdir_desc.cd_cnid == 0))) {
error = EBUSY;
goto out;
}
#if QUOTA
(void)hfs_getinoquota(cp);
#endif
hfs_global_shared_lock_acquire(hfsmp);
grabbed_lock = 1;
if (hfsmp->jnl) {
if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
goto out;
}
started_tr = 1;
}
if (!(options & HFSRM_SKIP_RESERVE)) {
if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) {
goto out;
}
}
cache_purge(vp);
if (hfsmp->jnl && vp->v_type == VLNK && vp->v_dirtyblkhd.lh_first) {
struct buf *bp, *nbp;
recheck:
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_META)) {
panic("hfs: symlink bp @ 0x%x is not marked meta-data!\n", bp);
}
if (bp->b_flags & B_LOCKED) {
bremfree(bp);
bp->b_flags |= B_BUSY;
journal_kill_block(hfsmp->jnl, bp);
goto recheck;
}
}
}
if ((cp->c_flag & C_HARDLINK) == 0) {
int mode = cp->c_mode;
if (!dataforkbusy && !isbigfile && cp->c_datafork->ff_blocks != 0) {
cp->c_mode = 0;
error = VOP_TRUNCATE(vp, (off_t)0, IO_NDELAY, NOCRED, p);
cp->c_mode = mode;
if (error)
goto out;
truncated = 1;
}
if (!rsrcforkbusy && rvp) {
cp->c_mode = 0;
error = VOP_TRUNCATE(rvp, (off_t)0, IO_NDELAY, NOCRED, p);
cp->c_mode = mode;
if (error)
goto out;
truncated = 1;
}
}
if (cp->c_flag & C_HARDLINK) {
struct cat_desc desc;
if ((cnp->cn_flags & HASBUF) == 0 ||
cnp->cn_nameptr[0] == '\0') {
error = ENOENT;
goto out;
}
bzero(&desc, sizeof(desc));
desc.cd_nameptr = cnp->cn_nameptr;
desc.cd_namelen = cnp->cn_namelen;
desc.cd_parentcnid = dcp->c_cnid;
desc.cd_cnid = cp->c_cnid;
error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (error)
goto out;
error = cat_delete(hfsmp, &desc, &cp->c_attr);
if ((error == 0) && (--cp->c_nlink < 1)) {
char inodename[32];
char delname[32];
struct cat_desc to_desc;
struct cat_desc from_desc;
MAKE_INODE_NAME(inodename, cp->c_rdev);
bzero(&from_desc, sizeof(from_desc));
from_desc.cd_nameptr = inodename;
from_desc.cd_namelen = strlen(inodename);
from_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
from_desc.cd_flags = 0;
from_desc.cd_cnid = cp->c_fileid;
MAKE_DELETED_NAME(delname, cp->c_fileid);
bzero(&to_desc, sizeof(to_desc));
to_desc.cd_nameptr = delname;
to_desc.cd_namelen = strlen(delname);
to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
to_desc.cd_flags = 0;
to_desc.cd_cnid = cp->c_fileid;
(void) cat_rename(hfsmp, &from_desc, &hfsmp->hfs_privdir_desc,
&to_desc, (struct cat_desc *)NULL);
cp->c_flag |= C_DELETED;
}
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if (error != 0)
goto out;
cp->c_flag |= C_CHANGE;
tv = time;
(void) VOP_UPDATE(vp, &tv, &tv, 0);
hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
} else if (dataforkbusy || rsrcforkbusy || isbigfile) {
char delname[32];
struct cat_desc to_desc;
struct cat_desc todir_desc;
bzero(&todir_desc, sizeof(todir_desc));
todir_desc.cd_parentcnid = 2;
MAKE_DELETED_NAME(delname, cp->c_fileid);
bzero(&to_desc, sizeof(to_desc));
to_desc.cd_nameptr = delname;
to_desc.cd_namelen = strlen(delname);
to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
to_desc.cd_flags = 0;
to_desc.cd_cnid = cp->c_cnid;
error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (error)
goto out;
error = cat_rename(hfsmp, &cp->c_desc, &todir_desc,
&to_desc, (struct cat_desc *)NULL);
if (error == 0) {
hfsmp->hfs_privdir_attr.ca_entries++;
}
(void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
&hfsmp->hfs_privdir_attr, NULL, NULL);
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if (error) goto out;
cp->c_flag |= C_CHANGE | C_DELETED | C_NOEXISTS;
--cp->c_nlink;
tv = time;
(void) VOP_UPDATE(vp, &tv, &tv, 0);
} else {
if (cp->c_blocks > 0) {
#if 0
panic("hfs_remove: attempting to delete a non-empty file!");
#else
printf("hfs_remove: attempting to delete a non-empty file %s\n",
cp->c_desc.cd_nameptr);
error = EBUSY;
goto out;
#endif
}
error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (error)
goto out;
error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
if (error && error != ENXIO && error != ENOENT && truncated) {
if ((cp->c_datafork && cp->c_datafork->ff_size != 0) ||
(cp->c_rsrcfork && cp->c_rsrcfork->ff_size != 0)) {
panic("hfs: remove: couldn't delete a truncated file! (%d, data sz %lld; rsrc sz %lld)",
error, cp->c_datafork->ff_size, cp->c_rsrcfork->ff_size);
} else {
printf("hfs: remove: strangely enough, deleting truncated file %s (%d) got err %d\n",
cp->c_desc.cd_nameptr, cp->c_attr.ca_fileid, error);
}
}
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if (error) goto out;
#if QUOTA
(void)hfs_chkiq(cp, -1, NOCRED, 0);
#endif
cp->c_mode = 0;
truncated = 0; cp->c_flag |= C_CHANGE | C_NOEXISTS;
--cp->c_nlink;
hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
}
cat_releasedesc(&cp->c_desc);
if (dcp->c_entries > 0)
dcp->c_entries--;
if (dcp->c_nlink > 0)
dcp->c_nlink--;
dcp->c_flag |= C_CHANGE | C_UPDATE;
tv = time;
(void) VOP_UPDATE(dvp, &tv, &tv, 0);
HFS_KNOTE(dvp, NOTE_WRITE);
out:
if ((options & HFSRM_SAVE_NAME) == 0 &&
(cnp != 0) &&
(cnp->cn_flags & (HASBUF | SAVENAME)) == (HASBUF | SAVENAME)) {
char *tmp = cnp->cn_pnbuf;
cnp->cn_pnbuf = NULL;
cnp->cn_flags &= ~HASBUF;
FREE_ZONE(tmp, cnp->cn_pnlen, M_NAMEI);
}
if (!(options & HFSRM_SKIP_RESERVE)) {
cat_postflight(hfsmp, &cookie, p);
}
if (truncated) {
cp->c_flag |= C_CHANGE | C_UPDATE | C_FORCEUPDATE;
tv = time;
(void) VOP_UPDATE(vp, &tv, &tv, 0);
}
if (started_tr) {
journal_end_transaction(hfsmp->jnl);
}
if (grabbed_lock) {
hfs_global_shared_lock_release(hfsmp);
}
HFS_KNOTE(vp, NOTE_DELETE);
if (rvp) {
HFS_KNOTE(rvp, NOTE_DELETE);
vrele(rvp);
};
if (error) {
vput(vp);
} else {
VOP_UNLOCK(vp, 0, p);
if ((cp->c_flag & C_HARDLINK) == 0 || cp->c_nlink == 0) {
(void) ubc_uncache(vp);
}
vrele(vp);
}
if (!(options & HFSRM_PARENT_LOCKED)) {
vput(dvp);
}
return (error);
}
__private_extern__ void
replace_desc(struct cnode *cp, struct cat_desc *cdp)
{
if (cp->c_desc.cd_flags & CD_HASBUF && cp->c_desc.cd_nameptr != 0) {
char *name = cp->c_desc.cd_nameptr;
cp->c_desc.cd_nameptr = 0;
cp->c_desc.cd_namelen = 0;
cp->c_desc.cd_flags &= ~CD_HASBUF;
remove_name(name);
}
bcopy(cdp, &cp->c_desc, sizeof(cp->c_desc));
cdp->cd_nameptr = 0;
cdp->cd_namelen = 0;
cdp->cd_flags &= ~CD_HASBUF;
}
static int
hfs_rename(ap)
struct vop_rename_args *ap;
{
struct vnode *tvp = ap->a_tvp;
struct vnode *tdvp = ap->a_tdvp;
struct vnode *fvp = ap->a_fvp;
struct vnode *fdvp = ap->a_fdvp;
struct componentname *tcnp = ap->a_tcnp;
struct componentname *fcnp = ap->a_fcnp;
struct proc *p = fcnp->cn_proc;
struct cnode *fcp = NULL;
struct cnode *fdcp = NULL;
struct cnode *tdcp = VTOC(tdvp);
struct cat_desc from_desc;
struct cat_desc to_desc;
struct cat_desc out_desc;
struct hfsmount *hfsmp = NULL;
struct timeval tv;
cat_cookie_t cookie = {0};
int fdvp_locked, fvp_locked, tdvp_locked, tvp_locked;
int tvp_deleted;
int started_tr = 0, grabbed_lock = 0;
int error = 0;
tdvp_locked = 1;
tvp_locked = (tvp != 0);
fdvp_locked = 0;
fvp_locked = 0;
tvp_deleted = 0;
if ((fvp->v_mount != tdvp->v_mount) ||
(tvp && (fvp->v_mount != tvp->v_mount))) {
error = EXDEV;
goto out;
}
if (fvp == tvp) {
if (VOP_ISLOCKED(tvp) &&
(VTOC(tvp)->c_lock.lk_lockholder == p->p_pid) &&
(VTOC(tvp)->c_lock.lk_lockthread == current_thread())) {
vput(tvp);
}
tvp = NULL;
tvp_locked = 0;
if ((VTOC(fvp)->c_flag & C_HARDLINK) &&
((fdvp != tdvp) ||
(hfs_namecmp(fcnp->cn_nameptr, fcnp->cn_namelen,
tcnp->cn_nameptr, tcnp->cn_namelen) != 0))) {
tvp = fvp;
vref(tvp);
}
}
if (tdcp->c_parentcnid == VTOC(fvp)->c_cnid) {
error = EINVAL;
goto out;
}
if (tvp && (tvp->v_type == VDIR) && (VTOC(tvp)->c_entries != 0)) {
error = ENOTEMPTY;
goto out;
}
if (fdvp == fvp) {
error = EINVAL;
goto out;
}
if ((VTOC(fvp)->c_flags & (IMMUTABLE | APPEND)) ||
(VTOC(fdvp)->c_flags & APPEND)) {
error = EPERM;
goto out;
}
hfsmp = VTOHFS(tdvp);
if (tvp && (tdcp->c_mode & S_ISTXT) &&
(tcnp->cn_cred->cr_uid != 0) &&
(tcnp->cn_cred->cr_uid != tdcp->c_uid) &&
(hfs_owner_rights(hfsmp, VTOC(tvp)->c_uid, tcnp->cn_cred, p, false)) ) {
error = EPERM;
goto out;
}
#if QUOTA
if (tvp)
(void)hfs_getinoquota(VTOC(tvp));
#endif
if (fdvp == tdvp) {
if (error = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p))
goto out;
fvp_locked = 1;
goto vnlocked;
}
if ((VTOC(fdvp)->c_cnid == VTOC(tdvp)->c_parentcnid) ||
((VTOC(tdvp)->c_cnid != VTOC(fdvp)->c_parentcnid) &&
(fdvp < tdvp))) {
if (tvp_locked) {
VOP_UNLOCK(tvp, 0, p);
tvp_locked = 0;
}
VOP_UNLOCK(tdvp, 0, p);
tdvp_locked = 0;
if ((error = vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY, p)))
goto out;
fdvp_locked = 1;
if ((error = vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY, p)))
goto out;
tdvp_locked = 1;
if (tvp == fvp) {
if ((error = vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p)))
goto out;
tvp_locked = 1;
} else {
if (tvp) {
if ((error = vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p)))
goto out;
tvp_locked = 1;
}
if ((error = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p)))
goto out;
fvp_locked = 1;
}
} else {
if ((error = vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY, p)))
goto out;
fdvp_locked = 1;
if (error = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p))
goto out;
if (tvp == fvp)
tvp_locked = 1;
else
fvp_locked = 1;
}
vnlocked:
fdcp = VTOC(fdvp);
fcp = VTOC(fvp);
cache_purge(fvp);
if ((fcp->c_flags & UF_NODUMP) &&
(fvp->v_type == VREG) &&
(fdvp != tdvp) &&
(fdcp->c_desc.cd_nameptr != NULL) &&
(strcmp(fdcp->c_desc.cd_nameptr, CARBON_TEMP_DIR_NAME) == 0)) {
fcp->c_flags &= ~UF_NODUMP;
fcp->c_flag |= C_CHANGE;
tv = time;
(void) VOP_UPDATE(fvp, &tv, &tv, 0);
}
bzero(&from_desc, sizeof(from_desc));
from_desc.cd_nameptr = fcnp->cn_nameptr;
from_desc.cd_namelen = fcnp->cn_namelen;
from_desc.cd_parentcnid = fdcp->c_cnid;
from_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
from_desc.cd_cnid = fcp->c_cnid;
bzero(&to_desc, sizeof(to_desc));
to_desc.cd_nameptr = tcnp->cn_nameptr;
to_desc.cd_namelen = tcnp->cn_namelen;
to_desc.cd_parentcnid = tdcp->c_cnid;
to_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
to_desc.cd_cnid = fcp->c_cnid;
hfs_global_shared_lock_acquire(hfsmp);
grabbed_lock = 1;
if (hfsmp->jnl) {
if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
goto out;
}
started_tr = 1;
}
if ((error = cat_preflight(hfsmp, CAT_RENAME + CAT_DELETE, &cookie, p))) {
goto out;
}
if (tvp) {
if (tvp != fvp)
cache_purge(tvp);
if (tvp->v_type == VDIR)
error = hfs_removedir(tdvp, tvp, tcnp, HFSRM_RENAMEOPTS);
else
error = hfs_removefile(tdvp, tvp, tcnp, HFSRM_RENAMEOPTS);
if (tvp == fvp)
fvp_locked = 0;
tvp = NULL;
tvp_locked = 0;
tvp_deleted = 1;
if (error)
goto out;
}
error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (error)
goto out;
error = cat_rename(hfsmp, &from_desc, &tdcp->c_desc, &to_desc, &out_desc);
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if (error) {
goto out;
}
if (fvp_locked) {
replace_desc(fcp, &out_desc);
fcp->c_parentcnid = tdcp->c_cnid;
fcp->c_hint = 0;
}
hfs_volupdate(hfsmp, fvp->v_type == VDIR ? VOL_RMDIR : VOL_RMFILE,
(fdcp->c_cnid == kHFSRootFolderID));
hfs_volupdate(hfsmp, fvp->v_type == VDIR ? VOL_MKDIR : VOL_MKFILE,
(tdcp->c_cnid == kHFSRootFolderID));
tv = time;
if (fdvp != tdvp) {
tdcp->c_nlink++;
tdcp->c_entries++;
if (fdcp->c_nlink > 0)
fdcp->c_nlink--;
if (fdcp->c_entries > 0)
fdcp->c_entries--;
fdcp->c_flag |= C_CHANGE | C_UPDATE;
(void) VOP_UPDATE(fdvp, &tv, &tv, 0);
}
tdcp->c_childhint = out_desc.cd_hint;
tdcp->c_flag |= C_CHANGE | C_UPDATE;
(void) VOP_UPDATE(tdvp, &tv, &tv, 0);
out:
if (hfsmp) {
cat_postflight(hfsmp, &cookie, p);
}
if (started_tr) {
journal_end_transaction(hfsmp->jnl);
}
if (grabbed_lock) {
hfs_global_shared_lock_release(hfsmp);
}
if (error == 0) {
HFS_KNOTE(fvp, NOTE_RENAME);
HFS_KNOTE(fdvp, NOTE_WRITE);
if (tdvp != fdvp) HFS_KNOTE(tdvp, NOTE_WRITE);
};
if (fvp_locked) {
VOP_UNLOCK(fvp, 0, p);
}
if (fdvp_locked) {
VOP_UNLOCK(fdvp, 0, p);
}
if (tdvp_locked) {
VOP_UNLOCK(tdvp, 0, p);
}
if (tvp_locked) {
VOP_UNLOCK(tvp, 0, p);
}
vrele(fvp);
vrele(fdvp);
if (tvp)
vrele(tvp);
vrele(tdvp);
if (error && tvp_deleted)
error = EIO;
return (error);
}
static int
hfs_mkdir(ap)
struct vop_mkdir_args *ap;
{
struct vattr *vap = ap->a_vap;
return (hfs_makenode(MAKEIMODE(vap->va_type, vap->va_mode),
ap->a_dvp, ap->a_vpp, ap->a_cnp));
}
static int
hfs_symlink(ap)
struct vop_symlink_args *ap;
{
register struct vnode *vp, **vpp = ap->a_vpp;
struct hfsmount *hfsmp;
struct filefork *fp;
int len, error;
struct buf *bp = NULL;
if (VTOVCB(ap->a_dvp)->vcbSigWord != kHFSPlusSigWord) {
VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
vput(ap->a_dvp);
return (EOPNOTSUPP);
}
if (ap->a_target[0] == 0) {
VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
vput(ap->a_dvp);
return (EINVAL);
}
hfsmp = VTOHFS(ap->a_dvp);
if ((error = hfs_makenode(S_IFLNK | ap->a_vap->va_mode,
ap->a_dvp, vpp, ap->a_cnp))) {
return (error);
}
vp = *vpp;
len = strlen(ap->a_target);
fp = VTOF(vp);
#if QUOTA
(void)hfs_getinoquota(VTOC(vp));
#endif
hfs_global_shared_lock_acquire(hfsmp);
if (hfsmp->jnl) {
if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
hfs_global_shared_lock_release(hfsmp);
vput(vp);
return error;
}
}
error = VOP_TRUNCATE(vp, len, IO_NOZEROFILL,
ap->a_cnp->cn_cred, ap->a_cnp->cn_proc);
if (error)
goto out;
bp = getblk(vp, 0, roundup((int)fp->ff_size, VTOHFS(vp)->hfs_phys_block_size),
0, 0, BLK_META);
if (hfsmp->jnl) {
journal_modify_block_start(hfsmp->jnl, bp);
}
bzero(bp->b_data, bp->b_bufsize);
bcopy(ap->a_target, bp->b_data, len);
if (hfsmp->jnl) {
journal_modify_block_end(hfsmp->jnl, bp);
} else {
bawrite(bp);
}
out:
if (hfsmp->jnl) {
journal_end_transaction(hfsmp->jnl);
}
hfs_global_shared_lock_release(hfsmp);
vput(vp);
return (error);
}
static hfsdotentry rootdots[2] = {
{
1,
sizeof(struct hfsdotentry),
DT_DIR,
1,
"."
},
{
1,
sizeof(struct hfsdotentry),
DT_DIR,
2,
".."
}
};
static int
hfs_readdir(ap)
struct vop_readdir_args *ap;
{
register struct uio *uio = ap->a_uio;
struct cnode *cp = VTOC(ap->a_vp);
struct hfsmount *hfsmp = VTOHFS(ap->a_vp);
struct proc *p = current_proc();
off_t off = uio->uio_offset;
int retval = 0;
int eofflag = 0;
void *user_start = NULL;
int user_len;
int ncookies=0;
u_long *cookies=NULL;
u_long *cookiep=NULL;
if (uio->uio_iovcnt > 1 || uio->uio_resid < AVERAGE_HFSDIRENTRY_SIZE)
return EINVAL;
if (hfsmp->jnl && uio->uio_segflg == UIO_USERSPACE) {
user_start = uio->uio_iov->iov_base;
user_len = uio->uio_iov->iov_len;
if ((retval = vslock(user_start, user_len)) != 0) {
return retval;
}
}
if (uio->uio_offset < sizeof(rootdots)) {
caddr_t dep;
size_t dotsize;
rootdots[0].d_fileno = cp->c_cnid;
rootdots[1].d_fileno = cp->c_parentcnid;
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;
}
if (ap->a_ncookies != NULL) {
if (uio->uio_segflg != UIO_SYSSPACE)
panic("hfs_readdir: unexpected uio from NFS server");
ncookies = uio->uio_iov->iov_len / (AVERAGE_HFSDIRENTRY_SIZE/2);
MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK);
*ap->a_ncookies = ncookies;
*ap->a_cookies = cookies;
}
if (cp->c_entries == 0) {
eofflag = 1;
retval = 0;
if (cookies) {
cookies[0] = 0;
cookies[1] = sizeof(struct hfsdotentry);
}
goto Exit;
}
retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p);
if (retval) goto Exit;
retval = cat_getdirentries(hfsmp, &cp->c_desc, cp->c_entries, uio, &eofflag, cookies, ncookies);
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if (retval != E_NONE) {
goto Exit;
}
if (uio->uio_offset == off) {
retval = E_NONE;
goto Exit;
}
cp->c_flag |= C_ACCESS;
Exit:;
if (hfsmp->jnl && user_start) {
vsunlock(user_start, user_len, TRUE);
}
if (ap->a_eofflag)
*ap->a_eofflag = eofflag;
return (retval);
}
static int
hfs_readlink(ap)
struct vop_readlink_args *ap;
{
int retval;
struct vnode *vp = ap->a_vp;
struct cnode *cp;
struct filefork *fp;
if (vp->v_type != VLNK)
return (EINVAL);
cp = VTOC(vp);
fp = VTOF(vp);
if (fp->ff_size == 0 || fp->ff_size > MAXPATHLEN) {
VTOVCB(vp)->vcbFlags |= kHFS_DamagedVolume;
return (EINVAL);
}
if (fp->ff_symlinkptr == NULL) {
struct buf *bp = NULL;
MALLOC(fp->ff_symlinkptr, char *, fp->ff_size, M_TEMP, M_WAITOK);
retval = meta_bread(vp, 0,
roundup((int)fp->ff_size,
VTOHFS(vp)->hfs_phys_block_size),
ap->a_cred, &bp);
if (retval) {
if (bp)
brelse(bp);
if (fp->ff_symlinkptr) {
FREE(fp->ff_symlinkptr, M_TEMP);
fp->ff_symlinkptr = NULL;
}
return (retval);
}
bcopy(bp->b_data, fp->ff_symlinkptr, (size_t)fp->ff_size);
if (bp) {
if (VTOHFS(vp)->jnl && (bp->b_flags & B_LOCKED) == 0) {
bp->b_flags |= B_INVAL;
}
brelse(bp);
}
}
retval = uiomove((caddr_t)fp->ff_symlinkptr, (int)fp->ff_size, ap->a_uio);
#if 1
if ((VTOHFS(vp)->hfc_stage == HFC_RECORDING) && (retval == 0)) {
if (cp->c_atime < VTOHFS(vp)->hfc_timebase)
VTOF(vp)->ff_bytesread = fp->ff_size;
else
VTOF(vp)->ff_bytesread += fp->ff_size;
}
#endif
return (retval);
}
static int
hfs_lock(ap)
struct vop_lock_args *ap;
{
struct vnode *vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
return (lockmgr(&cp->c_lock, ap->a_flags, &vp->v_interlock, ap->a_p));
}
static int
hfs_unlock(ap)
struct vop_unlock_args *ap;
{
struct vnode *vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
#if 0
if (!lockstatus(&cp->c_lock)) {
printf("hfs_unlock: vnode %s wasn't locked!\n",
cp->c_desc.cd_nameptr ? cp->c_desc.cd_nameptr : "");
}
#endif
return (lockmgr(&cp->c_lock, ap->a_flags | LK_RELEASE,
&vp->v_interlock, ap->a_p));
}
static int
hfs_print(ap)
struct vop_print_args *ap;
{
struct vnode * vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
printf("tag VT_HFS, cnid %d, on dev %d, %d", cp->c_cnid,
major(cp->c_dev), minor(cp->c_dev));
#if FIFO
if (vp->v_type == VFIFO)
fifo_printinfo(vp);
#endif
lockmgr_printinfo(&cp->c_lock);
printf("\n");
return (0);
}
static int
hfs_islocked(ap)
struct vop_islocked_args *ap;
{
return (lockstatus(&VTOC(ap->a_vp)->c_lock));
}
static int
hfs_pathconf(ap)
struct vop_pathconf_args *ap;
{
int retval = 0;
switch (ap->a_name) {
case _PC_LINK_MAX:
if (VTOVCB(ap->a_vp)->vcbSigWord == kHFSPlusSigWord)
*ap->a_retval = HFS_LINK_MAX;
else
*ap->a_retval = 1;
break;
case _PC_NAME_MAX:
*ap->a_retval = kHFSPlusMaxFileNameBytes;
break;
case _PC_PATH_MAX:
*ap->a_retval = PATH_MAX;
break;
case _PC_PIPE_BUF:
*ap->a_retval = PIPE_BUF;
break;
case _PC_CHOWN_RESTRICTED:
*ap->a_retval = 1;
break;
case _PC_NO_TRUNC:
*ap->a_retval = 0;
break;
case _PC_NAME_CHARS_MAX:
*ap->a_retval = kHFSPlusMaxFileNameChars;
break;
case _PC_CASE_SENSITIVE:
if (VTOHFS(ap->a_vp)->hfs_flags & HFS_CASE_SENSITIVE)
*ap->a_retval = 1;
else
*ap->a_retval = 0;
break;
case _PC_CASE_PRESERVING:
*ap->a_retval = 1;
break;
default:
retval = EINVAL;
}
return (retval);
}
static int
hfs_advlock(ap)
struct vop_advlock_args *ap;
{
struct vnode *vp = ap->a_vp;
struct flock *fl = ap->a_fl;
struct hfslockf *lock;
struct filefork *fork;
off_t start, end;
int retval;
if (vp->v_type != VREG)
return (EISDIR);
fork = VTOF(ap->a_vp);
if (fork->ff_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 = fork->ff_size + fl->l_start;
break;
default:
return (EINVAL);
}
if (fl->l_len == 0)
end = -1;
else if (fl->l_len > 0)
end = start + fl->l_len - 1;
else {
end = start - 1;
start += fl->l_len;
}
if (start < 0)
return (EINVAL);
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_fork = fork;
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;
}
return (retval);
}
static int
hfs_update(ap)
struct vop_update_args *ap;
{
struct vnode *vp = ap->a_vp;
struct cnode *cp = VTOC(ap->a_vp);
struct proc *p;
struct cat_fork *dataforkp = NULL;
struct cat_fork *rsrcforkp = NULL;
struct cat_fork datafork;
int updateflag;
struct hfsmount *hfsmp;
int error;
hfsmp = VTOHFS(vp);
if (((vp->v_flag & VSYSTEM) && (cp->c_cnid < kHFSFirstUserCatalogNodeID))||
(VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) ||
(cp->c_mode == 0)) {
cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE);
return (0);
}
updateflag = cp->c_flag & (C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE | C_FORCEUPDATE);
if (updateflag == 0) {
return (0);
}
if ((updateflag == C_ACCESS) && (VTOVCB(vp)->vcbSigWord == kHFSSigWord)) {
return (0);
}
if (updateflag & C_ACCESS) {
if ((updateflag == C_ACCESS) &&
(ap->a_access->tv_sec < (cp->c_atime + ATIME_ONDISK_ACCURACY))) {
return (0);
}
cp->c_atime = ap->a_access->tv_sec;
}
if (updateflag & C_UPDATE) {
cp->c_mtime = ap->a_modify->tv_sec;
cp->c_mtime_nsec = ap->a_modify->tv_usec * 1000;
}
if (updateflag & C_CHANGE) {
cp->c_ctime = time.tv_sec;
if ((VTOVCB(vp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
cp->c_ctime += 3600;
cp->c_mtime = cp->c_ctime;
}
}
if (cp->c_datafork)
dataforkp = &cp->c_datafork->ff_data;
if (cp->c_rsrcfork)
rsrcforkp = &cp->c_rsrcfork->ff_data;
p = current_proc();
if (ISSET(cp->c_flag, C_FORCEUPDATE) == 0 &&
(ISSET(cp->c_flag, C_DELETED) ||
(dataforkp && cp->c_datafork->ff_unallocblocks) ||
(rsrcforkp && cp->c_rsrcfork->ff_unallocblocks))) {
if (updateflag & (C_CHANGE | C_UPDATE))
hfs_volupdate(hfsmp, VOL_UPDATE, 0);
cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_UPDATE);
cp->c_flag |= C_MODIFIED;
HFS_KNOTE(vp, NOTE_ATTRIB);
return (0);
}
hfs_global_shared_lock_acquire(hfsmp);
if (hfsmp->jnl) {
if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
hfs_global_shared_lock_release(hfsmp);
return error;
}
}
if (dataforkp && !CIRCLEQ_EMPTY(&cp->c_datafork->ff_invalidranges)) {
bcopy(dataforkp, &datafork, sizeof(datafork));
datafork.cf_size = CIRCLEQ_FIRST(&cp->c_datafork->ff_invalidranges)->rl_start;
dataforkp = &datafork;
} else if (dataforkp && (cp->c_datafork->ff_unallocblocks != 0)) {
bcopy(dataforkp, &datafork, sizeof(datafork));
if (cp->c_datafork->ff_blocks < cp->c_datafork->ff_unallocblocks) {
panic("hfs: ff_blocks %d is less than unalloc blocks %d\n",
cp->c_datafork->ff_blocks, cp->c_datafork->ff_unallocblocks);
}
datafork.cf_blocks = (cp->c_datafork->ff_blocks - cp->c_datafork->ff_unallocblocks);
datafork.cf_size = datafork.cf_blocks * HFSTOVCB(hfsmp)->blockSize;
dataforkp = &datafork;
}
error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p);
if (error) {
if (hfsmp->jnl) {
journal_end_transaction(hfsmp->jnl);
}
hfs_global_shared_lock_release(hfsmp);
return (error);
}
error = cat_update(hfsmp, &cp->c_desc, &cp->c_attr, dataforkp, rsrcforkp);
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if (updateflag & (C_CHANGE | C_UPDATE | C_FORCEUPDATE))
hfs_volupdate(hfsmp, VOL_UPDATE, 0);
cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE | C_FORCEUPDATE);
if (hfsmp->jnl) {
journal_end_transaction(hfsmp->jnl);
}
hfs_global_shared_lock_release(hfsmp);
HFS_KNOTE(vp, NOTE_ATTRIB);
return (error);
}
static int
hfs_makenode(mode, dvp, vpp, cnp)
int mode;
struct vnode *dvp;
struct vnode **vpp;
struct componentname *cnp;
{
struct cnode *cp;
struct cnode *dcp;
struct vnode *tvp;
struct hfsmount *hfsmp;
struct timeval tv;
struct proc *p;
struct cat_desc in_desc, out_desc;
struct cat_attr attr;
cat_cookie_t cookie = {0};
int error, started_tr = 0, grabbed_lock = 0;
enum vtype vnodetype;
p = cnp->cn_proc;
dcp = VTOC(dvp);
hfsmp = VTOHFS(dvp);
*vpp = NULL;
tvp = NULL;
bzero(&out_desc, sizeof(out_desc));
if ((mode & S_IFMT) == 0)
mode |= S_IFREG;
vnodetype = IFTOVT(mode);
if (VTOVFS(dvp)->mnt_kern_flag & MNTK_UNMOUNT) {
error = EPERM;
goto exit;
}
if ((suser(cnp->cn_cred, NULL) != 0) && (hfs_freeblks(hfsmp, 1) <= 0)) {
error = ENOSPC;
goto exit;
}
bzero(&attr, sizeof(attr));
attr.ca_mode = mode;
attr.ca_nlink = vnodetype == VDIR ? 2 : 1;
attr.ca_mtime = time.tv_sec;
attr.ca_mtime_nsec = time.tv_usec * 1000;
if ((VTOVCB(dvp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
attr.ca_mtime += 3600;
}
attr.ca_atime = attr.ca_ctime = attr.ca_itime = attr.ca_mtime;
if (VTOVFS(dvp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
attr.ca_uid = hfsmp->hfs_uid;
attr.ca_gid = hfsmp->hfs_gid;
} else {
if (vnodetype == VLNK)
attr.ca_uid = dcp->c_uid;
else
attr.ca_uid = cnp->cn_cred->cr_uid;
attr.ca_gid = dcp->c_gid;
}
if (vnodetype == VBLK || vnodetype == VCHR)
attr.ca_mode = (attr.ca_mode & ~S_IFMT) | S_IFREG;
if (vnodetype == VLNK) {
struct FndrFileInfo *fip;
fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
fip->fdType = SWAP_BE32(kSymLinkFileType);
fip->fdCreator = SWAP_BE32(kSymLinkCreator);
}
if ((attr.ca_mode & S_ISGID) &&
!groupmember(dcp->c_gid, cnp->cn_cred) &&
suser(cnp->cn_cred, NULL)) {
attr.ca_mode &= ~S_ISGID;
}
if (cnp->cn_flags & ISWHITEOUT)
attr.ca_flags |= UF_OPAQUE;
bzero(&in_desc, sizeof(in_desc));
in_desc.cd_nameptr = cnp->cn_nameptr;
in_desc.cd_namelen = cnp->cn_namelen;
in_desc.cd_parentcnid = dcp->c_cnid;
in_desc.cd_flags = S_ISDIR(mode) ? CD_ISDIR : 0;
hfs_global_shared_lock_acquire(hfsmp);
grabbed_lock = 1;
if (hfsmp->jnl) {
if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
goto exit;
}
started_tr = 1;
}
if ((error = cat_preflight(hfsmp, CAT_CREATE | CAT_DELETE, &cookie, p))) {
goto exit;
}
error = hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
if (error)
goto exit;
error = cat_create(hfsmp, &in_desc, &attr, &out_desc);
(void) hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_RELEASE, p);
if (error)
goto exit;
dcp->c_childhint = out_desc.cd_hint;
dcp->c_nlink++;
dcp->c_entries++;
dcp->c_flag |= C_CHANGE | C_UPDATE;
tv = time;
(void) VOP_UPDATE(dvp, &tv, &tv, 0);
if (vnodetype == VDIR) {
HFS_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
} else {
HFS_KNOTE(dvp, NOTE_WRITE);
};
hfs_volupdate(hfsmp, vnodetype == VDIR ? VOL_MKDIR : VOL_MKFILE,
(dcp->c_cnid == kHFSRootFolderID));
if (started_tr) {
journal_end_transaction(hfsmp->jnl);
started_tr = 0;
}
if (grabbed_lock) {
hfs_global_shared_lock_release(hfsmp);
grabbed_lock = 0;
}
error = hfs_getnewvnode(hfsmp, NULL, &out_desc, 0, &attr, NULL, &tvp);
if (error)
goto exit;
cache_enter(dvp, tvp, cnp);
#if QUOTA
cp = VTOC(tvp);
if ((error = hfs_getinoquota(cp)) ||
(error = hfs_chkiq(cp, 1, cnp->cn_cred, FORCE))) {
if (tvp->v_type == VDIR)
VOP_RMDIR(dvp,tvp, cnp);
else
VOP_REMOVE(dvp,tvp, cnp);
dvp = NULL;
goto exit;
}
#endif
if (vnodetype == VBLK || vnodetype == VCHR) {
struct cnode *cp;
cp = VTOC(tvp);
cp->c_mode = mode;
tvp->v_type = IFTOVT(mode);
cp->c_flag |= C_CHANGE;
tv = time;
if ((error = VOP_UPDATE(tvp, &tv, &tv, 1))) {
vput(tvp);
goto exit;
}
}
*vpp = tvp;
exit:
cat_releasedesc(&out_desc);
cat_postflight(hfsmp, &cookie, p);
if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
char *tmp = cnp->cn_pnbuf;
cnp->cn_pnbuf = NULL;
cnp->cn_flags &= ~HASBUF;
FREE_ZONE(tmp, cnp->cn_pnlen, M_NAMEI);
}
if ((error == 0) && dvp && (vnodetype == VREG) &&
(dcp->c_desc.cd_nameptr != NULL) &&
(strcmp(dcp->c_desc.cd_nameptr, CARBON_TEMP_DIR_NAME) == 0)) {
struct vnode *ddvp;
cnid_t parid;
parid = dcp->c_parentcnid;
vput(dvp);
dvp = NULL;
if (VFS_VGET(HFSTOVFS(hfsmp), &parid, &ddvp) == 0) {
if (VTOC(ddvp)->c_desc.cd_nameptr &&
(cp->c_uid == strtoul(VTOC(ddvp)->c_desc.cd_nameptr, 0, 0))) {
cp->c_flags |= UF_NODUMP;
cp->c_flag |= C_CHANGE;
}
vput(ddvp);
}
}
if (dvp)
vput(dvp);
if (started_tr) {
journal_end_transaction(hfsmp->jnl);
started_tr = 0;
}
if (grabbed_lock) {
hfs_global_shared_lock_release(hfsmp);
grabbed_lock = 0;
}
return (error);
}
static int
hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp, struct vnode **rvpp, struct proc *p)
{
struct vnode *rvp;
struct cnode *cp = VTOC(vp);
int error;
if ((rvp = cp->c_rsrc_vp)) {
error = vget(rvp, 0, p);
if (error) {
char * name = VTOC(vp)->c_desc.cd_nameptr;
if (name)
printf("hfs_vgetrsrc: couldn't get"
" resource fork for %s\n", name);
return (error);
}
} else {
struct cat_fork rsrcfork;
error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p);
if (error)
return (error);
error = cat_lookup(hfsmp, &cp->c_desc, 1, (struct cat_desc *)0,
(struct cat_attr *)0, &rsrcfork);
(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
if (error)
return (error);
error = hfs_getnewvnode(hfsmp, cp, &cp->c_desc, 1, &cp->c_attr,
&rsrcfork, &rvp);
if (error)
return (error);
}
*rvpp = rvp;
return (0);
}
static void
filt_hfsdetach(struct knote *kn)
{
struct vnode *vp;
int result;
struct proc *p = current_proc();
vp = (struct vnode *)kn->kn_hook;
if (1) {
result = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
if (result) return;
};
result = KNOTE_DETACH(&VTOC(vp)->c_knotes, kn);
if (1) {
VOP_UNLOCK(vp, 0, p);
};
}
static int
filt_hfsread(struct knote *kn, long hint)
{
struct vnode *vp = (struct vnode *)kn->kn_fp->f_data;
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
return (1);
}
kn->kn_data = VTOF(vp)->ff_size - kn->kn_fp->f_offset;
return (kn->kn_data != 0);
}
static int
filt_hfswrite(struct knote *kn, long hint)
{
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
}
kn->kn_data = 0;
return (1);
}
static int
filt_hfsvnode(struct knote *kn, long hint)
{
if (kn->kn_sfflags & hint)
kn->kn_fflags |= hint;
if (hint == NOTE_REVOKE) {
kn->kn_flags |= EV_EOF;
return (1);
}
return (kn->kn_fflags != 0);
}
static struct filterops hfsread_filtops =
{ 1, NULL, filt_hfsdetach, filt_hfsread };
static struct filterops hfswrite_filtops =
{ 1, NULL, filt_hfsdetach, filt_hfswrite };
static struct filterops hfsvnode_filtops =
{ 1, NULL, filt_hfsdetach, filt_hfsvnode };
static int
hfs_kqfilt_add(ap)
struct vop_kqfilt_add_args *ap;
{
struct vnode *vp = ap->a_vp;
struct knote *kn = ap->a_kn;
switch (kn->kn_filter) {
case EVFILT_READ:
if (vp->v_type == VREG) {
kn->kn_fop = &hfsread_filtops;
} else {
return EINVAL;
};
break;
case EVFILT_WRITE:
if (vp->v_type == VREG) {
kn->kn_fop = &hfswrite_filtops;
} else {
return EINVAL;
};
break;
case EVFILT_VNODE:
kn->kn_fop = &hfsvnode_filtops;
break;
default:
return (1);
}
kn->kn_hook = (caddr_t)vp;
KNOTE_ATTACH(&VTOC(vp)->c_knotes, kn);
return (0);
}
static int
hfs_kqfilt_remove(ap)
struct vop_kqfilt_remove_args *ap;
{
struct vnode *vp = ap->a_vp;
uintptr_t ident = ap->a_ident;
int result;
result = ENOTSUP;
return (result);
}
static int
hfsspec_read(ap)
struct vop_read_args *ap;
{
VTOC(ap->a_vp)->c_flag |= C_ACCESS;
return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
}
static int
hfsspec_write(ap)
struct vop_write_args *ap;
{
VTOC(ap->a_vp)->c_flag |= C_CHANGE | C_UPDATE;
return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
}
static int
hfsspec_close(ap)
struct vop_close_args *ap;
{
struct vnode *vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
simple_lock(&vp->v_interlock);
if (ap->a_vp->v_usecount > 1)
CTIMES(cp, &time, &time);
simple_unlock(&vp->v_interlock);
return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap));
}
#if FIFO
static int
hfsfifo_read(ap)
struct vop_read_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
VTOC(ap->a_vp)->c_flag |= C_ACCESS;
return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap));
}
static int
hfsfifo_write(ap)
struct vop_write_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
VTOC(ap->a_vp)->c_flag |= C_CHANGE | C_UPDATE;
return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap));
}
static int
hfsfifo_close(ap)
struct vop_close_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
struct vnode *vp = ap->a_vp;
struct cnode *cp = VTOC(vp);
simple_lock(&vp->v_interlock);
if (ap->a_vp->v_usecount > 1)
CTIMES(cp, &time, &time);
simple_unlock(&vp->v_interlock);
return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_close), ap));
}
int
hfsfifo_kqfilt_add(ap)
struct vop_kqfilt_add_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
int error;
error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_kqfilt_add), ap);
if (error)
error = hfs_kqfilt_add(ap);
return (error);
}
int
hfsfifo_kqfilt_remove(ap)
struct vop_kqfilt_remove_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
int error;
error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_kqfilt_remove), ap);
if (error)
error = hfs_kqfilt_remove(ap);
return (error);
}
#endif
int hfs_cache_lookup();
int hfs_lookup();
int hfs_read();
int hfs_write();
int hfs_ioctl();
int hfs_select();
int hfs_bmap();
int hfs_strategy();
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();
int hfs_getattrlist();
int hfs_setattrlist();
int hfs_readdirattr();
int hfs_inactive();
int hfs_reclaim();
int (**hfs_vnodeop_p)(void *);
#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_revoke_desc, (VOPFUNC)nop_revoke },
{ &vop_exchange_desc, (VOPFUNC)hfs_exchange },
{ &vop_mmap_desc, (VOPFUNC)err_mmap },
{ &vop_fsync_desc, (VOPFUNC)hfs_fsync },
{ &vop_seek_desc, (VOPFUNC)nop_seek },
{ &vop_remove_desc, (VOPFUNC)hfs_remove },
{ &vop_link_desc, (VOPFUNC)hfs_link },
{ &vop_rename_desc, (VOPFUNC)hfs_rename },
{ &vop_mkdir_desc, (VOPFUNC)hfs_mkdir },
{ &vop_rmdir_desc, (VOPFUNC)hfs_rmdir },
{ &vop_mkcomplex_desc, (VOPFUNC)err_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)nop_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)err_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 },
{ &vop_kqfilt_add_desc, (VOPFUNC)hfs_kqfilt_add },
{ &vop_kqfilt_remove_desc, (VOPFUNC)hfs_kqfilt_remove },
{ 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_getattrlist_desc, (VOPFUNC)hfs_getattrlist },
{ &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_getattrlist_desc, (VOPFUNC)hfs_getattrlist },
{ &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 },
{ &vop_kqfilt_add_desc, (VOPFUNC)hfsfifo_kqfilt_add },
{ &vop_kqfilt_remove_desc, (VOPFUNC)hfsfifo_kqfilt_remove },
{ (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
};
struct vnodeopv_desc hfs_fifoop_opv_desc =
{ &hfs_fifoop_p, hfs_fifoop_entries };
#endif