#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/file_internal.h>
#include <sys/dirent.h>
#include <sys/stat.h>
#include <sys/buf.h>
#include <sys/mount.h>
#include <sys/vnode_internal.h>
#include <sys/malloc.h>
#include <sys/ubc.h>
#include <sys/paths.h>
#include <sys/quota.h>
#include <sys/time.h>
#include <sys/disk.h>
#include <sys/kauth.h>
#include <sys/uio_internal.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 <sys/sysctl.h>
#include "hfs.h"
#include "hfs_catalog.h"
#include "hfs_cnode.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"
int always_do_fullfsync = 0;
SYSCTL_INT (_kern, OID_AUTO, always_do_fullfsync, CTLFLAG_RW, &always_do_fullfsync, 0, "always F_FULLFSYNC when fsync is called");
extern unsigned long strtoul(const char *, char **, int);
static int hfs_makenode(struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp, struct vnode_attr *vap,
vfs_context_t ctx);
static int hfs_metasync(struct hfsmount *hfsmp, daddr64_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, int);
static int hfs_vnop_close(struct vnop_close_args*);
static int hfs_vnop_create(struct vnop_create_args*);
static int hfs_vnop_exchange(struct vnop_exchange_args*);
static int hfs_vnop_fsync(struct vnop_fsync_args*);
static int hfs_vnop_mkdir(struct vnop_mkdir_args*);
static int hfs_vnop_mknod(struct vnop_mknod_args*);
static int hfs_vnop_getattr(struct vnop_getattr_args*);
static int hfs_vnop_open(struct vnop_open_args*);
static int hfs_vnop_readdir(struct vnop_readdir_args*);
static int hfs_vnop_remove(struct vnop_remove_args*);
static int hfs_vnop_rename(struct vnop_rename_args*);
static int hfs_vnop_rmdir(struct vnop_rmdir_args*);
static int hfs_vnop_symlink(struct vnop_symlink_args*);
static int hfs_vnop_setattr(struct vnop_setattr_args*);
#define HFSRM_SKIP_RESERVE 0x01
int hfs_write_access(struct vnode *vp, kauth_cred_t cred, struct proc *p, Boolean considerFlags);
int hfs_chmod(struct vnode *vp, int mode, kauth_cred_t cred,
struct proc *p);
int hfs_chown(struct vnode *vp, uid_t uid, gid_t gid,
kauth_cred_t cred, struct proc *p);
static int
hfs_vnop_create(struct vnop_create_args *ap)
{
return hfs_makenode(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_context);
}
static int
hfs_vnop_mknod(struct vnop_mknod_args *ap)
{
struct vnode_attr *vap = ap->a_vap;
struct vnode *dvp = ap->a_dvp;
struct vnode **vpp = ap->a_vpp;
struct cnode *cp;
int error;
if (VTOVCB(dvp)->vcbSigWord != kHFSPlusSigWord) {
return (ENOTSUP);
}
error = hfs_makenode(dvp, vpp, ap->a_cnp, vap, ap->a_context);
if (error)
return (error);
cp = VTOC(*vpp);
cp->c_touch_acctime = TRUE;
cp->c_touch_chgtime = TRUE;
cp->c_touch_modtime = TRUE;
if ((vap->va_rdev != VNOVAL) &&
(vap->va_type == VBLK || vap->va_type == VCHR))
cp->c_rdev = vap->va_rdev;
return (0);
}
static int
hfs_vnop_open(struct vnop_open_args *ap)
{
struct vnode *vp = ap->a_vp;
struct filefork *fp;
struct timeval tv;
int error;
if ((VTOC(vp)->c_flags & APPEND) && !vnode_isdir(vp) &&
(ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
return (EPERM);
if (vnode_isreg(vp) && !UBCINFOEXISTS(vp))
return (EBUSY);
if (VTOC(vp)->c_fileid == VTOHFS(vp)->hfs_jnlfileid)
return (EPERM);
if ((VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) ||
(VTOHFS(vp)->jnl == NULL) ||
!vnode_isreg(vp) || vnode_isinuse(vp, 0)) {
return (0);
}
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
return (error);
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)) {
(void) hfs_relocate(vp, VTOVCB(vp)->nextAllocation + 4096,
vfs_context_ucred(ap->a_context),
vfs_context_proc(ap->a_context));
}
}
hfs_unlock(VTOC(vp));
return (0);
}
static int
hfs_vnop_close(ap)
struct vnop_close_args *ap;
{
register struct vnode *vp = ap->a_vp;
register struct cnode *cp;
struct proc *p = vfs_context_proc(ap->a_context);
struct hfsmount *hfsmp;
int busy;
if ( hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) != 0)
return (0);
cp = VTOC(vp);
hfsmp = VTOHFS(vp);
if (hfsmp->hfs_freezing_proc == p && proc_exiting(p)) {
hfsmp->hfs_freezing_proc = NULL;
hfs_global_exclusive_lock_release(hfsmp);
lck_rw_unlock_exclusive(&hfsmp->hfs_insync);
}
busy = vnode_isinuse(vp, 1);
if (busy) {
hfs_touchtimes(VTOHFS(vp), cp);
}
if (vnode_isdir(vp)) {
hfs_reldirhints(cp, busy);
} else if (vnode_issystem(vp) && !busy) {
vnode_recycle(vp);
}
hfs_unlock(cp);
return (0);
}
static int
hfs_vnop_getattr(struct vnop_getattr_args *ap)
{
struct vnode *vp = ap->a_vp;
struct vnode_attr *vap = ap->a_vap;
struct vnode *rvp = NULL;
struct hfsmount *hfsmp;
struct cnode *cp;
enum vtype v_type;
int error = 0;
if ((error = hfs_lock(VTOC(vp), HFS_SHARED_LOCK))) {
return (error);
}
cp = VTOC(vp);
hfsmp = VTOHFS(vp);
hfs_touchtimes(hfsmp, cp);
v_type = vnode_vtype(vp);
VATTR_RETURN(vap, va_rdev, (v_type == VBLK || v_type == VCHR) ? cp->c_rdev : 0);
if (v_type == VDIR) {
if (VATTR_IS_ACTIVE(vap, va_nlink)) {
int entries;
entries = cp->c_nlink;
if (vnode_isvroot(vp)) {
if (hfsmp->hfs_privdir_desc.cd_cnid != 0)
--entries;
if (hfsmp->jnl)
entries -= 2;
}
VATTR_RETURN(vap, va_nlink, (uint64_t)entries);
}
if (VATTR_IS_ACTIVE(vap, va_nchildren)) {
int entries;
entries = cp->c_entries;
if (vnode_isvroot(vp)) {
if (hfsmp->hfs_privdir_desc.cd_cnid != 0)
--entries;
if (hfsmp->jnl)
entries -= 2;
}
VATTR_RETURN(vap, va_nchildren, entries);
}
} else {
VATTR_RETURN(vap, va_nlink, (uint64_t)cp->c_nlink);
}
if (VATTR_IS_ACTIVE(vap, va_total_size)) {
if (v_type == VDIR) {
VATTR_RETURN(vap, va_total_size, cp->c_nlink * AVERAGE_HFSDIRENTRY_SIZE);
} else {
uint64_t total_size = 0;
struct cnode *rcp;
if (cp->c_datafork) {
total_size = cp->c_datafork->ff_size;
}
if (cp->c_blocks - VTOF(vp)->ff_blocks) {
error = hfs_vgetrsrc(hfsmp, vp, &rvp, NULL);
if (error) {
goto out;
}
rcp = VTOC(rvp);
if (rcp && rcp->c_rsrcfork) {
total_size += rcp->c_rsrcfork->ff_size;
}
}
VATTR_RETURN(vap, va_total_size, total_size);
if (cp->c_attrblks) {
vap->va_total_size += (uint64_t)cp->c_attrblks * (uint64_t)hfsmp->blockSize;
}
}
}
if (VATTR_IS_ACTIVE(vap, va_total_alloc)) {
if (v_type == VDIR) {
VATTR_RETURN(vap, va_total_alloc, 0);
} else {
VATTR_RETURN(vap, va_total_alloc, (uint64_t)cp->c_blocks * (uint64_t)hfsmp->blockSize);
if (cp->c_attrblks) {
vap->va_total_alloc += (uint64_t)cp->c_attrblks * (uint64_t)hfsmp->blockSize;
}
}
}
if (v_type == VDIR) {
VATTR_RETURN(vap, va_data_size, cp->c_nlink * AVERAGE_HFSDIRENTRY_SIZE);
} else {
VATTR_RETURN(vap, va_data_size, VTOF(vp)->ff_size);
}
if (VATTR_IS_ACTIVE(vap, va_data_alloc) && (v_type != VDIR)) {
VATTR_RETURN(vap, va_data_alloc, (uint64_t)VTOF(vp)->ff_blocks * (uint64_t)hfsmp->blockSize);
}
VATTR_RETURN(vap, va_iosize, hfsmp->hfs_logBlockSize);
VATTR_RETURN(vap, va_uid, cp->c_uid);
VATTR_RETURN(vap, va_gid, cp->c_gid);
VATTR_RETURN(vap, va_mode, cp->c_mode);
#if 0
if (VNODE_IS_RSRC(vp))
vap->va_mode |= S_IFXATTR;
#endif
VATTR_RETURN(vap, va_flags, cp->c_flags);
if (VATTR_IS_ACTIVE(vap, va_acl)) {
if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
vap->va_acl = KAUTH_FILESEC_NONE;
VATTR_SET_SUPPORTED(vap, va_acl);
}
}
vap->va_create_time.tv_sec = cp->c_itime;
vap->va_create_time.tv_nsec = 0;
VATTR_SET_SUPPORTED(vap, va_create_time);
if (VATTR_IS_ACTIVE(vap, va_access_time)) {
if (cp->c_touch_acctime) {
struct timeval tv;
microtime(&tv);
vap->va_access_time.tv_sec = tv.tv_sec;
} else {
vap->va_access_time.tv_sec = cp->c_atime;
}
vap->va_access_time.tv_nsec = 0;
VATTR_SET_SUPPORTED(vap, va_access_time);
}
vap->va_modify_time.tv_sec = cp->c_mtime;
vap->va_modify_time.tv_nsec = 0;
VATTR_SET_SUPPORTED(vap, va_modify_time);
vap->va_change_time.tv_sec = cp->c_ctime;
vap->va_change_time.tv_nsec = 0;
VATTR_SET_SUPPORTED(vap, va_change_time);
vap->va_backup_time.tv_sec = cp->c_btime;
vap->va_backup_time.tv_nsec = 0;
VATTR_SET_SUPPORTED(vap, va_backup_time);
VATTR_RETURN(vap, va_fileid, (uint64_t)cp->c_fileid);
VATTR_RETURN(vap, va_linkid, (uint64_t)cp->c_cnid);
VATTR_RETURN(vap, va_parentid, (uint64_t)cp->c_parentcnid);
VATTR_RETURN(vap, va_fsid, cp->c_dev);
VATTR_RETURN(vap, va_filerev, 0);
VATTR_RETURN(vap, va_encoding, cp->c_encoding);
if (VATTR_IS_ACTIVE(vap, va_name) && !vnode_isvroot(vp)) {
if (cp->c_desc.cd_namelen == 0) {
error = ENOENT;
goto out;
}
strncpy(vap->va_name, cp->c_desc.cd_nameptr, MAXPATHLEN);
vap->va_name[MAXPATHLEN-1] = '\0';
VATTR_SET_SUPPORTED(vap, va_name);
}
out:
hfs_unlock(cp);
if (rvp) {
vnode_put(rvp);
}
return (error);
}
static int
hfs_vnop_setattr(ap)
struct vnop_setattr_args *ap;
{
struct vnode_attr *vap = ap->a_vap;
struct vnode *vp = ap->a_vp;
struct cnode *cp = NULL;
struct hfsmount *hfsmp;
kauth_cred_t cred = vfs_context_ucred(ap->a_context);
struct proc *p = vfs_context_proc(ap->a_context);
int error = 0;
uid_t nuid;
gid_t ngid;
hfsmp = VTOHFS(vp);
if (hfsmp->hfs_jnlfileid == VTOC(vp)->c_fileid) {
return (EPERM);
}
VATTR_SET_SUPPORTED(vap, va_data_size);
if (VATTR_IS_ACTIVE(vap, va_data_size) && !vnode_islnk(vp)) {
hfs_lock_truncate(VTOC(vp), TRUE);
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
hfs_unlock_truncate(VTOC(vp));
return (error);
}
cp = VTOC(vp);
error = hfs_truncate(vp, vap->va_data_size, vap->va_vaflags & 0xffff, 0, ap->a_context);
hfs_unlock_truncate(cp);
if (error)
goto out;
}
if (cp == NULL) {
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
return (error);
cp = VTOC(vp);
}
VATTR_SET_SUPPORTED(vap, va_uid);
VATTR_SET_SUPPORTED(vap, va_gid);
nuid = VATTR_IS_ACTIVE(vap, va_uid) ? vap->va_uid : (uid_t)VNOVAL;
ngid = VATTR_IS_ACTIVE(vap, va_gid) ? vap->va_gid : (gid_t)VNOVAL;
if (((nuid != (uid_t)VNOVAL) || (ngid != (gid_t)VNOVAL)) &&
((error = hfs_chown(vp, nuid, ngid, cred, p)) != 0))
goto out;
VATTR_SET_SUPPORTED(vap, va_mode);
if (VATTR_IS_ACTIVE(vap, va_mode) &&
((error = hfs_chmod(vp, (int)vap->va_mode, cred, p)) != 0))
goto out;
VATTR_SET_SUPPORTED(vap, va_flags);
if (VATTR_IS_ACTIVE(vap, va_flags)) {
cp->c_flags = vap->va_flags;
cp->c_touch_chgtime = TRUE;
}
if (VATTR_IS_ACTIVE(vap, va_acl)) {
if (vap->va_acl == NULL) {
if (cp->c_attr.ca_recflags & kHFSHasSecurityMask) {
cp->c_attr.ca_recflags &= ~kHFSHasSecurityMask;
cp->c_touch_chgtime = TRUE;
}
} else {
if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
cp->c_touch_chgtime = TRUE;
}
}
}
VATTR_SET_SUPPORTED(vap, va_create_time);
VATTR_SET_SUPPORTED(vap, va_access_time);
VATTR_SET_SUPPORTED(vap, va_modify_time);
VATTR_SET_SUPPORTED(vap, va_backup_time);
VATTR_SET_SUPPORTED(vap, va_change_time);
if (VATTR_IS_ACTIVE(vap, va_create_time) ||
VATTR_IS_ACTIVE(vap, va_access_time) ||
VATTR_IS_ACTIVE(vap, va_modify_time) ||
VATTR_IS_ACTIVE(vap, va_backup_time)) {
if (vnode_islnk(vp))
goto done;
if (VATTR_IS_ACTIVE(vap, va_create_time))
cp->c_itime = vap->va_create_time.tv_sec;
if (VATTR_IS_ACTIVE(vap, va_access_time)) {
cp->c_atime = vap->va_access_time.tv_sec;
cp->c_touch_acctime = FALSE;
}
if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
cp->c_mtime = vap->va_modify_time.tv_sec;
cp->c_touch_modtime = FALSE;
cp->c_touch_chgtime = TRUE;
if ((VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) &&
(cp->c_cnid != kHFSRootFolderID) &&
(cp->c_mtime < cp->c_itime)) {
cp->c_itime = cp->c_mtime;
}
}
if (VATTR_IS_ACTIVE(vap, va_backup_time))
cp->c_btime = vap->va_backup_time.tv_sec;
cp->c_flag |= C_MODIFIED;
}
VATTR_SET_SUPPORTED(vap, va_encoding);
if (VATTR_IS_ACTIVE(vap, va_encoding)) {
cp->c_encoding = vap->va_encoding;
hfs_setencodingbits(hfsmp, cp->c_encoding);
}
done:
if ((error = hfs_update(vp, TRUE)) != 0)
goto out;
HFS_KNOTE(vp, NOTE_ATTRIB);
out:
if (cp)
hfs_unlock(cp);
return (error);
}
__private_extern__
int
hfs_chmod(struct vnode *vp, int mode, kauth_cred_t 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 (((unsigned int)vfs_flags(VTOVFS(vp))) & MNT_UNKNOWNPERMISSIONS) {
return (0);
};
#endif
cp->c_mode &= ~ALLPERMS;
cp->c_mode |= (mode & ALLPERMS);
cp->c_touch_chgtime = TRUE;
return (0);
}
__private_extern__
int
hfs_write_access(struct vnode *vp, kauth_cred_t cred, struct proc *p, Boolean considerFlags)
{
struct cnode *cp = VTOC(vp);
int retval = 0;
int is_member;
switch (vnode_vtype(vp)) {
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 (!suser(cred, NULL))
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);
if (kauth_cred_ismember_gid(cred, cp->c_gid, &is_member) == 0 && is_member) {
return ((cp->c_mode & S_IWGRP) == S_IWGRP ? 0 : EACCES);
}
return ((cp->c_mode & S_IWOTH) == S_IWOTH ? 0 : EACCES);
}
__private_extern__
int
hfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred,
struct proc *p)
{
register struct cnode *cp = VTOC(vp);
uid_t ouid;
gid_t ogid;
int error = 0;
int is_member;
#if QUOTA
register int i;
int64_t change;
#endif
if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
return (ENOTSUP);
if (((unsigned int)vfs_flags(VTOVFS(vp))) & MNT_UNKNOWNPERMISSIONS)
return (0);
if (uid == (uid_t)VNOVAL)
uid = cp->c_uid;
if (gid == (gid_t)VNOVAL)
gid = cp->c_gid;
#if 0
if ((kauth_cred_getuid(cred) != cp->c_uid || uid != cp->c_uid ||
(gid != cp->c_gid &&
(kauth_cred_ismember_gid(cred, gid, &is_member) || !is_member))) &&
(error = suser(cred, 0)))
return (error);
#endif
ogid = cp->c_gid;
ouid = cp->c_uid;
#if QUOTA
if ((error = hfs_getinoquota(cp)))
return (error);
if (ouid == uid) {
dqrele(cp->c_dquot[USRQUOTA]);
cp->c_dquot[USRQUOTA] = NODQUOT;
}
if (ogid == gid) {
dqrele(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(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(cp->c_dquot[USRQUOTA]);
cp->c_dquot[USRQUOTA] = NODQUOT;
}
if (ogid == gid) {
dqrele(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(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(cp->c_dquot[USRQUOTA]);
cp->c_dquot[USRQUOTA] = NODQUOT;
}
if (ogid == gid) {
dqrele(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_touch_chgtime = TRUE;
return (0);
}
static int
hfs_vnop_exchange(ap)
struct vnop_exchange_args *ap;
{
struct vnode *from_vp = ap->a_fvp;
struct vnode *to_vp = ap->a_tvp;
struct cnode *from_cp;
struct cnode *to_cp;
struct hfsmount *hfsmp;
struct cat_desc tempdesc;
struct cat_attr tempattr;
int lockflags;
int error = 0, started_tr = 0, got_cookie = 0;
cat_cookie_t cookie;
if (vnode_mount(from_vp) != vnode_mount(to_vp))
return (EXDEV);
if (from_vp == to_vp)
return (EINVAL);
if ((error = hfs_lockpair(VTOC(from_vp), VTOC(to_vp), HFS_EXCLUSIVE_LOCK)))
return (error);
from_cp = VTOC(from_vp);
to_cp = VTOC(to_vp);
hfsmp = VTOHFS(from_vp);
if (!vnode_isreg(from_vp) || !vnode_isreg(to_vp) ||
(from_cp->c_flag & C_HARDLINK) || (to_cp->c_flag & C_HARDLINK) ||
VNODE_IS_RSRC(from_vp) || VNODE_IS_RSRC(to_vp)) {
error = EINVAL;
goto exit;
}
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) {
error = EPERM;
goto exit;
}
}
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) {
error = EPERM;
goto exit;
}
}
}
if ((error = hfs_start_transaction(hfsmp)) != 0) {
goto exit;
}
started_tr = 1;
bzero(&cookie, sizeof(cookie));
if ((error = cat_preflight(hfsmp, CAT_EXCHANGE, &cookie, vfs_context_proc(ap->a_context)))) {
goto exit;
}
got_cookie = 1;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
error = ExchangeFileIDs(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);
hfs_systemfile_unlock(hfsmp, lockflags);
if (error != E_NONE) {
error = MacToVFSError(error);
goto 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;
from_cp->c_attr.ca_recflags = to_cp->c_attr.ca_recflags;
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;
to_cp->c_attr.ca_recflags = tempattr.ca_recflags;
bcopy(tempattr.ca_finderinfo, to_cp->c_finderinfo, 32);
hfs_chash_rehash(from_cp, 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_touch_chgtime = TRUE;
}
if ((to_cp->c_flags & UF_NODUMP) &&
(to_cp->c_parentcnid != from_cp->c_parentcnid)) {
to_cp->c_flags &= ~UF_NODUMP;
to_cp->c_touch_chgtime = TRUE;
}
HFS_KNOTE(from_vp, NOTE_ATTRIB);
HFS_KNOTE(to_vp, NOTE_ATTRIB);
exit:
if (got_cookie) {
cat_postflight(hfsmp, &cookie, vfs_context_proc(ap->a_context));
}
if (started_tr) {
hfs_end_transaction(hfsmp);
}
hfs_unlockpair(from_cp, to_cp);
return (error);
}
__private_extern__
int
hfs_fsync(struct vnode *vp, int waitfor, int fullsync, struct proc *p)
{
struct cnode *cp = VTOC(vp);
struct filefork *fp = NULL;
int retval = 0;
struct hfsmount *hfsmp = VTOHFS(vp);
struct timeval tv;
int wait;
int lockflag;
int took_trunc_lock = 0;
wait = (waitfor == MNT_WAIT);
if (vnode_isdir(vp))
goto metasync;
if (vnode_issystem(vp)) {
if (VTOF(vp)->fcbBTCBPtr != NULL) {
if (hfsmp->jnl == NULL) {
BTFlushPath(VTOF(vp));
}
}
} else if (UBCINFOEXISTS(vp)) {
hfs_unlock(cp);
hfs_lock_truncate(cp, TRUE);
took_trunc_lock = 1;
(void) cluster_push(vp, 0);
hfs_lock(cp, HFS_FORCE_LOCK);
}
if ((wait || (cp->c_flag & C_ZFWANTSYNC)) &&
((cp->c_flags & UF_NODUMP) == 0) &&
UBCINFOEXISTS(vp) && (fp = VTOF(vp)) &&
cp->c_zftimeout != 0) {
microuptime(&tv);
if (tv.tv_sec < cp->c_zftimeout) {
cp->c_flag |= C_ZFWANTSYNC;
goto datasync;
}
if (!took_trunc_lock) {
hfs_unlock(cp);
hfs_lock_truncate(cp, TRUE);
hfs_lock(cp, HFS_FORCE_LOCK);
took_trunc_lock = 1;
}
while (!CIRCLEQ_EMPTY(&fp->ff_invalidranges)) {
struct rl_entry *invalid_range = CIRCLEQ_FIRST(&fp->ff_invalidranges);
off_t start = invalid_range->rl_start;
off_t end = invalid_range->rl_end;
rl_remove(start, end, &fp->ff_invalidranges);
hfs_unlock(cp);
(void) cluster_write(vp, (struct uio *) 0,
fp->ff_size, end + 1, start, (off_t)0,
IO_HEADZEROFILL | IO_NOZERODIRTY | IO_NOCACHE);
hfs_lock(cp, HFS_FORCE_LOCK);
cp->c_flag |= C_MODIFIED;
}
hfs_unlock(cp);
(void) cluster_push(vp, 0);
hfs_lock(cp, HFS_FORCE_LOCK);
cp->c_flag &= ~C_ZFWANTSYNC;
cp->c_zftimeout = 0;
}
datasync:
if (took_trunc_lock)
hfs_unlock_truncate(cp);
if (hfsmp->jnl)
lockflag = BUF_SKIP_LOCKED;
else
lockflag = 0;
buf_flushdirtyblks(vp, wait, lockflag, "hfs_fsync");
metasync:
if (vnode_isreg(vp) && vnode_issystem(vp)) {
if (VTOF(vp)->fcbBTCBPtr != NULL) {
microuptime(&tv);
BTSetLastSync(VTOF(vp), tv.tv_sec);
}
cp->c_touch_acctime = FALSE;
cp->c_touch_chgtime = FALSE;
cp->c_touch_modtime = FALSE;
} else if ( !(vp->v_flag & VSWAP) ) {
retval = hfs_update(vp, wait);
if ((retval == 0) && wait && cp->c_hint &&
!ISSET(cp->c_flag, C_DELETED | C_NOEXISTS)) {
hfs_metasync(VTOHFS(vp), (daddr64_t)cp->c_hint, p);
}
if (fullsync || always_do_fullfsync) {
if (hfsmp->jnl) {
journal_flush(hfsmp->jnl);
} else {
VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, NULL);
}
}
}
return (retval);
}
static int
hfs_metasync(struct hfsmount *hfsmp, daddr64_t node, struct proc *p)
{
vnode_t vp;
buf_t bp;
int lockflags;
vp = HFSTOVCB(hfsmp)->catalogRefNum;
if (hfsmp->jnl) {
return 0;
}
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
bp = buf_getblk(vp, node, 0, 0, 0, BLK_META | BLK_ONLYVALID);
if (bp) {
if ((buf_flags(bp) & (B_LOCKED | B_DELWRI)) == B_DELWRI)
(void) VNOP_BWRITE(bp);
else
buf_brelse(bp);
}
hfs_systemfile_unlock(hfsmp, lockflags);
return (0);
}
static int
hfs_btsync_callback(struct buf *bp, void *dummy)
{
buf_clearflags(bp, B_LOCKED);
(void) buf_bawrite(bp);
return(BUF_CLAIMED);
}
__private_extern__
int
hfs_btsync(struct vnode *vp, int sync_transaction)
{
struct cnode *cp = VTOC(vp);
struct timeval tv;
int flags = 0;
if (sync_transaction)
flags |= BUF_SKIP_NONLOCKED;
buf_iterate(vp, hfs_btsync_callback, flags, 0);
microuptime(&tv);
if (vnode_issystem(vp) && (VTOF(vp)->fcbBTCBPtr != NULL))
(void) BTSetLastSync(VTOF(vp), tv.tv_sec);
cp->c_touch_acctime = FALSE;
cp->c_touch_chgtime = FALSE;
cp->c_touch_modtime = FALSE;
return 0;
}
static int
hfs_vnop_rmdir(ap)
struct vnop_rmdir_args *ap;
{
struct vnode *dvp = ap->a_dvp;
struct vnode *vp = ap->a_vp;
int error;
if (!vnode_isdir(vp)) {
return (ENOTDIR);
}
if (dvp == vp) {
return (EINVAL);
}
if ((error = hfs_lockpair(VTOC(dvp), VTOC(vp), HFS_EXCLUSIVE_LOCK)))
return (error);
error = hfs_removedir(dvp, vp, ap->a_cnp, 0);
hfs_unlockpair(VTOC(dvp), VTOC(vp));
return (error);
}
static int
hfs_removedir(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
int skip_reserve)
{
vfs_context_t ctx = cnp->cn_context;
struct proc *p = vfs_context_proc(ctx);
struct cnode *cp;
struct cnode *dcp;
struct hfsmount * hfsmp;
struct cat_desc desc;
cat_cookie_t cookie;
int lockflags;
int error = 0, started_tr = 0, got_cookie = 0;
cp = VTOC(vp);
dcp = VTOC(dvp);
hfsmp = VTOHFS(vp);
if (dcp == cp)
return (EINVAL);
#if QUOTA
(void)hfs_getinoquota(cp);
#endif
if ((error = hfs_start_transaction(hfsmp)) != 0) {
goto out;
}
started_tr = 1;
if (!skip_reserve) {
bzero(&cookie, sizeof(cookie));
if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) {
goto out;
}
got_cookie = 1;
}
if (cp->c_entries != 0) {
error = ENOTEMPTY;
goto out;
}
if ((dcp->c_flags & APPEND) || (cp->c_flags & (IMMUTABLE | APPEND))) {
error = EPERM;
goto out;
}
if (cp->c_entries > 0)
panic("hfs_rmdir: attempting to delete a non-empty directory!");
cache_purge(vp);
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;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
error = cat_delete(hfsmp, &desc, &cp->c_attr);
if (error == 0) {
(void) hfs_removeallattr(hfsmp, cp->c_fileid);
}
hfs_systemfile_unlock(hfsmp, lockflags);
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_touch_chgtime = TRUE;
dcp->c_touch_modtime = TRUE;
dcp->c_flag |= C_FORCEUPDATE;
(void) hfs_update(dvp, 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:
HFS_KNOTE(vp, NOTE_DELETE);
if (got_cookie) {
cat_postflight(hfsmp, &cookie, p);
}
if (started_tr) {
hfs_end_transaction(hfsmp);
}
return (error);
}
static int
hfs_vnop_remove(ap)
struct vnop_remove_args *ap;
{
struct vnode *dvp = ap->a_dvp;
struct vnode *vp = ap->a_vp;
int error;
if (dvp == vp) {
return (EINVAL);
}
hfs_lock_truncate(VTOC(vp), TRUE);
if ((error = hfs_lockpair(VTOC(dvp), VTOC(vp), HFS_EXCLUSIVE_LOCK)))
goto out;
error = hfs_removefile(dvp, vp, ap->a_cnp, ap->a_flags, 0);
hfs_unlockpair(VTOC(dvp), VTOC(vp));
out:
hfs_unlock_truncate(VTOC(vp));
return (error);
}
static int
hfs_removefile_callback(struct buf *bp, void *hfsmp) {
if ( !(buf_flags(bp) & B_META))
panic("hfs: symlink bp @ 0x%x is not marked meta-data!\n", bp);
journal_kill_block(((struct hfsmount *)hfsmp)->jnl, bp);
return (BUF_CLAIMED);
}
static int
hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
int flags, int skip_reserve)
{
struct vnode *rvp = NULL;
struct cnode *cp;
struct cnode *dcp;
struct hfsmount *hfsmp;
struct cat_desc desc;
struct timeval tv;
vfs_context_t ctx = cnp->cn_context;
int dataforkbusy = 0;
int rsrcforkbusy = 0;
int truncated = 0;
cat_cookie_t cookie;
int lockflags;
int error = 0;
int started_tr = 0, got_cookie = 0;
int isbigfile = 0;
cnid_t real_cnid = 0;
if (vnode_isdir(vp)) {
return (EISDIR);
}
cp = VTOC(vp);
dcp = VTOC(dvp);
hfsmp = VTOHFS(vp);
if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
return 0;
}
if ( (cp->c_flag & C_HARDLINK) == 0
&& (cp->c_parentcnid != hfsmp->hfs_privdir_desc.cd_cnid)
&& (cp->c_parentcnid != dcp->c_cnid)) {
error = EINVAL;
goto out;
}
if (VNODE_IS_RSRC(vp)) {
error = EPERM;
goto out;
}
if (cp->c_blocks - VTOF(vp)->ff_blocks) {
error = hfs_vgetrsrc(hfsmp, vp, &rvp, 0);
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 (vnode_isinuse(vp, 0))
dataforkbusy = 1;
if (rvp && vnode_isinuse(rvp, 0))
rsrcforkbusy = 1;
isbigfile = (VTOC(vp)->c_datafork->ff_size >= HFS_BIGFILE_SIZE);
if (dataforkbusy || rsrcforkbusy) {
if ((flags & VNODE_REMOVE_NODELETEBUSY) ||
(hfsmp->hfs_privdir_desc.cd_cnid == 0)) {
error = EBUSY;
goto out;
}
}
#if QUOTA
(void)hfs_getinoquota(cp);
#endif
if ((cp->c_flag & C_HARDLINK) == 0 &&
(!dataforkbusy || !rsrcforkbusy)) {
hfs_unlock(cp);
if (!dataforkbusy && cp->c_datafork->ff_blocks && !isbigfile) {
ubc_setsize(vp, 0);
}
if (!rsrcforkbusy && rvp) {
ubc_setsize(rvp, 0);
}
hfs_lock(cp, HFS_FORCE_LOCK);
} else {
struct cat_desc cndesc;
bzero(&cndesc, sizeof(cndesc));
cndesc.cd_nameptr = cnp->cn_nameptr;
cndesc.cd_namelen = cnp->cn_namelen;
cndesc.cd_parentcnid = VTOC(dvp)->c_cnid;
cndesc.cd_hint = VTOC(dvp)->c_childhint;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
if (cat_lookup(hfsmp, &cndesc, 0, NULL, NULL, NULL, &real_cnid) != 0) {
hfs_systemfile_unlock(hfsmp, lockflags);
error = ENOENT;
goto out;
}
hfs_systemfile_unlock(hfsmp, lockflags);
}
if ((error = hfs_start_transaction(hfsmp)) != 0) {
goto out;
}
started_tr = 1;
if (!skip_reserve) {
if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, 0))) {
goto out;
}
got_cookie = 1;
}
cache_purge(vp);
if (hfsmp->jnl && vnode_islnk(vp))
buf_iterate(vp, hfs_removefile_callback, BUF_SKIP_NONLOCKED, (void *)hfsmp);
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 = hfs_truncate(vp, (off_t)0, IO_NDELAY, 1, ctx);
cp->c_mode = mode;
if (error)
goto out;
truncated = 1;
}
if (!rsrcforkbusy && rvp) {
cp->c_mode = 0;
error = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 1, ctx);
cp->c_mode = mode;
if (error)
goto out;
truncated = 1;
}
}
desc.cd_flags = 0;
desc.cd_encoding = cp->c_desc.cd_encoding;
desc.cd_nameptr = cnp->cn_nameptr;
desc.cd_namelen = cnp->cn_namelen;
desc.cd_parentcnid = dcp->c_cnid;
desc.cd_hint = cp->c_desc.cd_hint;
if (real_cnid) {
desc.cd_cnid = real_cnid;
} else {
desc.cd_cnid = cp->c_cnid;
}
microtime(&tv);
if (cp->c_flag & C_HARDLINK) {
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
error = cat_delete(hfsmp, &desc, &cp->c_attr);
if (error == 0) {
if (dcp->c_entries > 0)
dcp->c_entries--;
if (dcp->c_nlink > 0)
dcp->c_nlink--;
dcp->c_ctime = tv.tv_sec;
dcp->c_mtime = tv.tv_sec;
(void ) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
if (--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;
error = cat_rename(hfsmp, &from_desc, &hfsmp->hfs_privdir_desc,
&to_desc, (struct cat_desc *)NULL);
if (error != 0) {
panic("hfs_removefile: error %d from cat_rename(%s %s) cp 0x%x\n",
inodename, delname, cp);
}
if (error == 0) {
cp->c_flag |= C_DELETED;
cp->c_ctime = tv.tv_sec;
(void) cat_update(hfsmp, &to_desc, &cp->c_attr, NULL, NULL);
}
} else {
cp->c_ctime = tv.tv_sec;
(void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
}
}
hfs_systemfile_unlock(hfsmp, lockflags);
if (error != 0)
goto out;
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;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
error = cat_rename(hfsmp, &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);
if (dcp->c_entries > 0)
dcp->c_entries--;
if (dcp->c_nlink > 0)
dcp->c_nlink--;
dcp->c_ctime = tv.tv_sec;
dcp->c_mtime = tv.tv_sec;
(void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
cp->c_flag |= C_DELETED;
cp->c_ctime = tv.tv_sec;
--cp->c_nlink;
(void) cat_update(hfsmp, &to_desc, &cp->c_attr, NULL, NULL);
}
hfs_systemfile_unlock(hfsmp, lockflags);
if (error)
goto out;
} else {
if (cp->c_blocks > 0) {
printf("hfs_remove: attempting to delete a non-empty file %s\n",
cp->c_desc.cd_nameptr);
error = EBUSY;
goto out;
}
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
error = cat_delete(hfsmp, &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);
}
}
if (error == 0) {
(void) hfs_removeallattr(hfsmp, cp->c_fileid);
if (dcp->c_entries > 0)
dcp->c_entries--;
if (dcp->c_nlink > 0)
dcp->c_nlink--;
dcp->c_ctime = tv.tv_sec;
dcp->c_mtime = tv.tv_sec;
(void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
}
hfs_systemfile_unlock(hfsmp, lockflags);
if (error)
goto out;
#if QUOTA
(void)hfs_chkiq(cp, -1, NOCRED, 0);
#endif
cp->c_mode = 0;
truncated = 0; cp->c_flag |= C_NOEXISTS;
cp->c_touch_chgtime = TRUE;
--cp->c_nlink;
hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
}
cat_releasedesc(&cp->c_desc);
HFS_KNOTE(dvp, NOTE_WRITE);
out:
if (got_cookie) {
cat_postflight(hfsmp, &cookie, 0);
}
if (truncated) {
cp->c_flag |= C_FORCEUPDATE;
cp->c_touch_chgtime = TRUE;
cp->c_touch_modtime = TRUE;
(void) hfs_update(vp, 0);
}
if (started_tr) {
hfs_end_transaction(hfsmp);
}
HFS_KNOTE(vp, NOTE_DELETE);
if (rvp) {
HFS_KNOTE(rvp, NOTE_DELETE);
cp->c_flag |= C_NEED_RVNODE_PUT;
};
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;
vfs_removename(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_vnop_rename(ap)
struct vnop_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 = vfs_context_proc(ap->a_context);
struct cnode *fcp;
struct cnode *fdcp;
struct cnode *tdcp;
struct cnode *tcp;
struct cat_desc from_desc;
struct cat_desc to_desc;
struct cat_desc out_desc;
struct hfsmount *hfsmp;
cat_cookie_t cookie;
int tvp_deleted = 0;
int started_tr = 0, got_cookie = 0;
int took_trunc_lock = 0;
int lockflags;
int error;
if (tvp && vnode_isreg(tvp)) {
hfs_lock_truncate(VTOC(tvp), TRUE);
took_trunc_lock = 1;
}
error = hfs_lockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL,
HFS_EXCLUSIVE_LOCK);
if (error) {
if (took_trunc_lock)
hfs_unlock_truncate(VTOC(tvp));
return (error);
}
fdcp = VTOC(fdvp);
fcp = VTOC(fvp);
tdcp = VTOC(tdvp);
tcp = tvp ? VTOC(tvp) : NULL;
hfsmp = VTOHFS(tdvp);
if (fcp->c_flag & C_NOEXISTS) {
error = ENOENT;
goto out;
}
if (tdcp->c_parentcnid == fcp->c_cnid) {
error = EINVAL;
goto out;
}
if (tvp && vnode_isdir(tvp) && (tcp->c_entries != 0) && fvp != tvp) {
error = ENOTEMPTY;
goto out;
}
if (fdvp == fvp) {
error = EINVAL;
goto out;
}
if ((fcp->c_flags & (IMMUTABLE | APPEND)) || (fdcp->c_flags & APPEND)) {
error = EPERM;
goto out;
}
if (tvp && (tdcp->c_mode & S_ISTXT) &&
(suser(vfs_context_ucred(tcnp->cn_context), NULL)) &&
(kauth_cred_getuid(vfs_context_ucred(tcnp->cn_context)) != tdcp->c_uid) &&
(hfs_owner_rights(hfsmp, tcp->c_uid, vfs_context_ucred(tcnp->cn_context), p, false)) ) {
error = EPERM;
goto out;
}
#if QUOTA
if (tvp)
(void)hfs_getinoquota(tcp);
#endif
cache_purge(fvp);
if ((fcp->c_flags & UF_NODUMP) &&
vnode_isreg(fvp) &&
(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_touch_chgtime = TRUE;
(void) hfs_update(fvp, 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;
if ((error = hfs_start_transaction(hfsmp)) != 0) {
goto out;
}
started_tr = 1;
if (fcp->c_flag & C_HARDLINK) {
struct cat_desc tmpdesc;
cnid_t real_cnid;
bzero(&tmpdesc, sizeof(tmpdesc));
tmpdesc.cd_nameptr = fcnp->cn_nameptr;
tmpdesc.cd_namelen = fcnp->cn_namelen;
tmpdesc.cd_parentcnid = fdcp->c_cnid;
tmpdesc.cd_hint = fdcp->c_childhint;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
if (cat_lookup(hfsmp, &tmpdesc, 0, NULL, NULL, NULL, &real_cnid) != 0) {
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
from_desc.cd_cnid = real_cnid;
hfs_systemfile_unlock(hfsmp, lockflags);
}
bzero(&cookie, sizeof(cookie));
if ((error = cat_preflight(hfsmp, CAT_RENAME + CAT_DELETE, &cookie, p))) {
goto out;
}
got_cookie = 1;
if (tvp) {
if (fvp == tvp) {
if (!((fcp->c_flag & C_HARDLINK) &&
((fdvp != tdvp) ||
(hfs_namecmp(fcnp->cn_nameptr, fcnp->cn_namelen,
tcnp->cn_nameptr, tcnp->cn_namelen) != 0)))) {
goto skip;
}
} else {
cache_purge(tvp);
}
if (vnode_isdir(tvp))
error = hfs_removedir(tdvp, tvp, tcnp, HFSRM_SKIP_RESERVE);
else {
error = hfs_removefile(tdvp, tvp, tcnp, 0, HFSRM_SKIP_RESERVE);
}
if (error)
goto out;
tvp_deleted = 1;
}
skip:
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
error = cat_rename(hfsmp, &from_desc, &tdcp->c_desc, &to_desc, &out_desc);
hfs_systemfile_unlock(hfsmp, lockflags);
if (error) {
goto out;
}
if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
cache_purge_negatives(tdvp);
replace_desc(fcp, &out_desc);
fcp->c_parentcnid = tdcp->c_cnid;
fcp->c_hint = 0;
hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_RMDIR : VOL_RMFILE,
(fdcp->c_cnid == kHFSRootFolderID));
hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_MKDIR : VOL_MKFILE,
(tdcp->c_cnid == kHFSRootFolderID));
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_touch_chgtime = TRUE;
fdcp->c_touch_modtime = TRUE;
fdcp->c_flag |= C_FORCEUPDATE; (void) hfs_update(fdvp, 0);
}
tdcp->c_childhint = out_desc.cd_hint;
tdcp->c_touch_chgtime = TRUE;
tdcp->c_touch_modtime = TRUE;
tdcp->c_flag |= C_FORCEUPDATE; (void) hfs_update(tdvp, 0);
out:
if (got_cookie) {
cat_postflight(hfsmp, &cookie, p);
}
if (started_tr) {
hfs_end_transaction(hfsmp);
}
if (error == 0) {
HFS_KNOTE(fvp, NOTE_RENAME);
HFS_KNOTE(fdvp, NOTE_WRITE);
if (tdvp != fdvp) HFS_KNOTE(tdvp, NOTE_WRITE);
};
if (took_trunc_lock)
hfs_unlock_truncate(VTOC(tvp));
hfs_unlockfour(fdcp, fcp, tdcp, tcp);
if (error && tvp_deleted)
error = EIO;
return (error);
}
static int
hfs_vnop_mkdir(struct vnop_mkdir_args *ap)
{
ap->a_cnp->cn_flags |= MAKEENTRY;
return hfs_makenode(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_context);
}
static int
hfs_vnop_symlink(struct vnop_symlink_args *ap)
{
struct vnode **vpp = ap->a_vpp;
struct vnode *dvp = ap->a_dvp;
struct vnode *vp = NULL;
struct hfsmount *hfsmp;
struct filefork *fp;
struct buf *bp = NULL;
char *datap;
int started_tr = 0;
int len, error;
if (VTOVCB(dvp)->vcbSigWord != kHFSPlusSigWord)
return (ENOTSUP);
if (ap->a_target[0] == 0)
return (EINVAL);
ap->a_vap->va_mode |= S_IFLNK;
if ((error = hfs_makenode(dvp, vpp, ap->a_cnp, ap->a_vap, ap->a_context))) {
goto out;
}
vp = *vpp;
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
return (error);
fp = VTOF(vp);
hfsmp = VTOHFS(dvp);
len = strlen(ap->a_target);
#if QUOTA
(void)hfs_getinoquota(VTOC(vp));
#endif
if ((error = hfs_start_transaction(hfsmp)) != 0) {
goto out;
}
started_tr = 1;
error = hfs_truncate(vp, len, IO_NOZEROFILL, 1, ap->a_context);
if (error)
goto out;
bp = buf_getblk(vp, (daddr64_t)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);
}
datap = (char *)buf_dataptr(bp);
bzero(datap, buf_size(bp));
bcopy(ap->a_target, datap, len);
if (hfsmp->jnl) {
journal_modify_block_end(hfsmp->jnl, bp);
} else {
buf_bawrite(bp);
}
ubc_setsize(vp, len);
out:
if (started_tr)
hfs_end_transaction(hfsmp);
if (vp) {
hfs_unlock(VTOC(vp));
}
return (error);
}
struct hfs_stddotentry {
u_int32_t d_fileno;
u_int16_t d_reclen;
u_int8_t d_type;
u_int8_t d_namlen;
char d_name[4];
};
struct hfs_extdotentry {
u_int64_t d_fileno;
u_int64_t d_seekoff;
u_int16_t d_reclen;
u_int16_t d_namlen;
u_int8_t d_type;
u_char d_name[3];
};
typedef union {
struct hfs_stddotentry std;
struct hfs_extdotentry ext;
} hfs_dotentry_t;
static int
hfs_vnop_readdir(ap)
struct vnop_readdir_args *ap;
{
struct vnode *vp = ap->a_vp;
uio_t uio = ap->a_uio;
struct cnode *cp;
struct hfsmount *hfsmp;
directoryhint_t *dirhint = NULL;
directoryhint_t localhint;
off_t offset;
off_t startoffset;
int error = 0;
int eofflag = 0;
user_addr_t user_start = 0;
user_size_t user_len = 0;
int index;
unsigned int tag;
int items;
int lockflags;
int extended;
int nfs_cookies;
caddr_t bufstart;
cnid_t cnid_hint = 0;
items = 0;
startoffset = offset = uio_offset(uio);
bufstart = CAST_DOWN(caddr_t, uio_iov_base(uio));
extended = (ap->a_flags & VNODE_READDIR_EXTENDED);
nfs_cookies = extended && (ap->a_flags & VNODE_READDIR_REQSEEKOFF);
if ((uio_iovcnt(uio) > 1) ||
(uio_resid(uio) < (int)sizeof(struct dirent))) {
return (EINVAL);
}
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
return (error);
cp = VTOC(vp);
hfsmp = VTOHFS(vp);
if (nfs_cookies) {
cnid_hint = (cnid_t)(uio_offset(uio) >> 32);
uio_setoffset(uio, uio_offset(uio) & 0x00000000ffffffffLL);
if (cnid_hint == INT_MAX) {
eofflag = 1;
goto out;
}
}
if (offset == 0) {
hfs_dotentry_t dotentry[2];
size_t uiosize;
if (extended) {
struct hfs_extdotentry *entry = &dotentry[0].ext;
entry->d_fileno = cp->c_cnid;
entry->d_reclen = sizeof(struct hfs_extdotentry);
entry->d_type = DT_DIR;
entry->d_namlen = 1;
entry->d_name[0] = '.';
entry->d_name[1] = '\0';
entry->d_name[2] = '\0';
entry->d_seekoff = 1;
++entry;
entry->d_fileno = cp->c_parentcnid;
entry->d_reclen = sizeof(struct hfs_extdotentry);
entry->d_type = DT_DIR;
entry->d_namlen = 2;
entry->d_name[0] = '.';
entry->d_name[1] = '.';
entry->d_name[2] = '\0';
entry->d_seekoff = 2;
uiosize = 2 * sizeof(struct hfs_extdotentry);
} else {
struct hfs_stddotentry *entry = &dotentry[0].std;
entry->d_fileno = cp->c_cnid;
entry->d_reclen = sizeof(struct hfs_stddotentry);
entry->d_type = DT_DIR;
entry->d_namlen = 1;
*(int *)&entry->d_name[0] = 0;
entry->d_name[0] = '.';
++entry;
entry->d_fileno = cp->c_parentcnid;
entry->d_reclen = sizeof(struct hfs_stddotentry);
entry->d_type = DT_DIR;
entry->d_namlen = 2;
*(int *)&entry->d_name[0] = 0;
entry->d_name[0] = '.';
entry->d_name[1] = '.';
uiosize = 2 * sizeof(struct hfs_stddotentry);
}
if ((error = uiomove((caddr_t)&dotentry, uiosize, uio))) {
goto out;
}
offset += 2;
}
if (cp->c_entries == 0) {
error = 0;
eofflag = 1;
uio_setoffset(uio, offset);
goto seekoffcalc;
}
if (hfsmp->jnl && uio_isuserspace(uio)) {
user_start = uio_curriovbase(uio);
user_len = uio_curriovlen(uio);
if ((error = vslock(user_start, user_len)) != 0) {
user_start = 0;
goto out;
}
}
index = (offset & HFS_INDEX_MASK) - 2;
tag = offset & ~HFS_INDEX_MASK;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
if (nfs_cookies && cnid_hint != 0) {
if (cat_findname(hfsmp, cnid_hint, &localhint.dh_desc) == 0) {
if ( localhint.dh_desc.cd_parentcnid == cp->c_cnid) {
localhint.dh_index = index - 1;
localhint.dh_time = 0;
bzero(&localhint.dh_link, sizeof(localhint.dh_link));
dirhint = &localhint;
} else {
cat_releasedesc(&localhint.dh_desc);
}
}
}
if (dirhint == NULL) {
dirhint = hfs_getdirhint(cp, ((index - 1) & HFS_INDEX_MASK) | tag);
dirhint->dh_index &= HFS_INDEX_MASK;
if (dirhint->dh_index == HFS_INDEX_MASK) {
dirhint->dh_index = -1;
}
}
error = cat_getdirentries(hfsmp, cp->c_entries, dirhint, uio, extended, &items, &eofflag);
hfs_systemfile_unlock(hfsmp, lockflags);
if (error != 0) {
goto out;
}
index += items;
if (items >= (int)cp->c_entries) {
eofflag = 1;
}
while (tag == 0)
tag = (++cp->c_dirhinttag) << HFS_INDEX_BITS;
uio_setoffset(uio, (index + 2) | tag);
dirhint->dh_index |= tag;
seekoffcalc:
cp->c_touch_acctime = TRUE;
if (ap->a_numdirent) {
if (startoffset == 0)
items += 2;
*ap->a_numdirent = items;
}
out:
if (hfsmp->jnl && user_start) {
vsunlock(user_start, user_len, TRUE);
}
if ((dirhint != NULL) &&
(dirhint != &localhint) &&
(uio_offset(uio) == startoffset)) {
hfs_reldirhint(cp, dirhint);
eofflag = 1;
}
if (ap->a_eofflag) {
*ap->a_eofflag = eofflag;
}
if (dirhint == &localhint) {
cat_releasedesc(&localhint.dh_desc);
}
hfs_unlock(cp);
return (error);
}
static int
hfs_vnop_readlink(ap)
struct vnop_readlink_args *ap;
{
struct vnode *vp = ap->a_vp;
struct cnode *cp;
struct filefork *fp;
int error;
if (!vnode_islnk(vp))
return (EINVAL);
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
return (error);
cp = VTOC(vp);
fp = VTOF(vp);
if (fp->ff_size == 0 || fp->ff_size > MAXPATHLEN) {
VTOVCB(vp)->vcbFlags |= kHFS_DamagedVolume;
error = EINVAL;
goto exit;
}
if (fp->ff_symlinkptr == NULL) {
struct buf *bp = NULL;
MALLOC(fp->ff_symlinkptr, char *, fp->ff_size, M_TEMP, M_WAITOK);
error = (int)buf_meta_bread(vp, (daddr64_t)0,
roundup((int)fp->ff_size,
VTOHFS(vp)->hfs_phys_block_size),
vfs_context_ucred(ap->a_context), &bp);
if (error) {
if (bp)
buf_brelse(bp);
if (fp->ff_symlinkptr) {
FREE(fp->ff_symlinkptr, M_TEMP);
fp->ff_symlinkptr = NULL;
}
goto exit;
}
bcopy((char *)buf_dataptr(bp), fp->ff_symlinkptr, (size_t)fp->ff_size);
if (VTOHFS(vp)->jnl && (buf_flags(bp) & B_LOCKED) == 0) {
buf_markinvalid(bp);
}
buf_brelse(bp);
}
error = uiomove((caddr_t)fp->ff_symlinkptr, (int)fp->ff_size, ap->a_uio);
if ((VTOHFS(vp)->hfc_stage == HFC_RECORDING) && (error == 0)) {
if (cp->c_atime < VTOHFS(vp)->hfc_timebase)
VTOF(vp)->ff_bytesread = fp->ff_size;
else
VTOF(vp)->ff_bytesread += fp->ff_size;
}
exit:
hfs_unlock(cp);
return (error);
}
static int
hfs_vnop_pathconf(ap)
struct vnop_pathconf_args *ap;
{
switch (ap->a_name) {
case _PC_LINK_MAX:
if (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD)
*ap->a_retval = 1;
else
*ap->a_retval = HFS_LINK_MAX;
break;
case _PC_NAME_MAX:
if (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD)
*ap->a_retval = kHFSMaxFileNameChars;
else
*ap->a_retval = kHFSPlusMaxFileNameChars;
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:
return (EINVAL);
}
return (0);
}
__private_extern__
int
hfs_update(struct vnode *vp, __unused int waitfor)
{
struct cnode *cp = VTOC(vp);
struct proc *p;
struct cat_fork *dataforkp = NULL;
struct cat_fork *rsrcforkp = NULL;
struct cat_fork datafork;
struct hfsmount *hfsmp;
int lockflags;
int error;
p = current_proc();
hfsmp = VTOHFS(vp);
if (vnode_issystem(vp) && (cp->c_cnid < kHFSFirstUserCatalogNodeID)) {
return (0);
}
if ((hfsmp->hfs_flags & HFS_READ_ONLY) || (cp->c_mode == 0)) {
cp->c_flag &= ~C_MODIFIED;
cp->c_touch_acctime = 0;
cp->c_touch_chgtime = 0;
cp->c_touch_modtime = 0;
return (0);
}
hfs_touchtimes(hfsmp, cp);
if ((cp->c_flag & (C_MODIFIED | C_FORCEUPDATE)) == 0) {
return (0);
}
if (cp->c_datafork)
dataforkp = &cp->c_datafork->ff_data;
if (cp->c_rsrcfork)
rsrcforkp = &cp->c_rsrcfork->ff_data;
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))) {
cp->c_flag |= C_MODIFIED;
HFS_KNOTE(vp, NOTE_ATTRIB);
return (0);
}
if ((error = hfs_start_transaction(hfsmp)) != 0) {
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;
}
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
error = cat_update(hfsmp, &cp->c_desc, &cp->c_attr, dataforkp, rsrcforkp);
hfs_systemfile_unlock(hfsmp, lockflags);
cp->c_flag &= ~(C_MODIFIED | C_FORCEUPDATE);
hfs_end_transaction(hfsmp);
HFS_KNOTE(vp, NOTE_ATTRIB);
return (error);
}
static int
hfs_makenode(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp,
struct vnode_attr *vap, vfs_context_t ctx)
{
struct cnode *cp = NULL;
struct cnode *dcp;
struct vnode *tvp;
struct hfsmount *hfsmp;
struct cat_desc in_desc, out_desc;
struct cat_attr attr;
struct timeval tv;
cat_cookie_t cookie;
int lockflags;
int error, started_tr = 0, got_cookie = 0;
enum vtype vnodetype;
int mode;
if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK)))
return (error);
dcp = VTOC(dvp);
hfsmp = VTOHFS(dvp);
*vpp = NULL;
tvp = NULL;
out_desc.cd_flags = 0;
out_desc.cd_nameptr = NULL;
mode = MAKEIMODE(vap->va_type, vap->va_mode);
if ((mode & S_IFMT) == 0)
mode |= S_IFREG;
vnodetype = IFTOVT(mode);
if ((hfs_freeblks(hfsmp, 1) <= 0) && (suser(vfs_context_ucred(ctx), NULL) != 0)) {
error = ENOSPC;
goto exit;
}
microtime(&tv);
bzero(&attr, sizeof(attr));
attr.ca_mode = mode;
attr.ca_nlink = vnodetype == VDIR ? 2 : 1;
attr.ca_mtime = tv.tv_sec;
if ((VTOVCB(dvp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
attr.ca_mtime += 3600;
}
attr.ca_atime = attr.ca_ctime = attr.ca_itime = attr.ca_mtime;
attr.ca_atimeondisk = attr.ca_atime;
if (vnodetype != VDIR && (hfsmp->hfs_flags & HFS_STANDARD) == 0)
attr.ca_recflags = kHFSThreadExistsMask;
attr.ca_uid = vap->va_uid;
attr.ca_gid = vap->va_gid;
VATTR_SET_SUPPORTED(vap, va_mode);
VATTR_SET_SUPPORTED(vap, va_uid);
VATTR_SET_SUPPORTED(vap, va_gid);
if (vnodetype == VLNK) {
struct FndrFileInfo *fip;
fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
fip->fdType = SWAP_BE32(kSymLinkFileType);
fip->fdCreator = SWAP_BE32(kSymLinkCreator);
}
if (cnp->cn_flags & ISWHITEOUT)
attr.ca_flags |= UF_OPAQUE;
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;
in_desc.cd_hint = dcp->c_childhint;
in_desc.cd_encoding = 0;
if ((error = hfs_start_transaction(hfsmp)) != 0) {
goto exit;
}
started_tr = 1;
if ((error = cat_preflight(hfsmp, CAT_CREATE | CAT_DELETE, &cookie, 0))) {
goto exit;
}
got_cookie = 1;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
error = cat_create(hfsmp, &in_desc, &attr, &out_desc);
if (error == 0) {
dcp->c_childhint = out_desc.cd_hint;
dcp->c_nlink++;
dcp->c_entries++;
dcp->c_ctime = tv.tv_sec;
dcp->c_mtime = tv.tv_sec;
(void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
HFS_KNOTE(dvp, NOTE_ATTRIB);
}
hfs_systemfile_unlock(hfsmp, lockflags);
if (error)
goto exit;
if (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)
cache_purge_negatives(dvp);
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) {
hfs_end_transaction(hfsmp);
started_tr = 0;
}
error = hfs_getnewvnode(hfsmp, dvp, cnp, &out_desc, 0, &attr, NULL, &tvp);
if (error)
goto exit;
cp = VTOC(tvp);
#if QUOTA
if (vfs_flags(HFSTOVFS(hfsmp)) & MNT_QUOTA) {
if ((error = hfs_getinoquota(cp)) ||
(error = hfs_chkiq(cp, 1, vfs_context_ucred(ctx), FORCE))) {
if (vnode_isdir(tvp))
(void) hfs_removedir(dvp, tvp, cnp, 0);
else {
hfs_unlock(cp);
hfs_lock_truncate(cp, TRUE);
hfs_lock(cp, HFS_FORCE_LOCK);
(void) hfs_removefile(dvp, tvp, cnp, 0, 0);
hfs_unlock_truncate(cp);
}
hfs_unlock(cp);
vnode_put(tvp);
goto exit;
}
}
#endif
if (VATTR_IS_ACTIVE(vap, va_acl) &&
(vap->va_acl != NULL)) {
cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
cp->c_touch_chgtime = TRUE;
(void) hfs_update(tvp, TRUE);
}
*vpp = tvp;
exit:
cat_releasedesc(&out_desc);
if (got_cookie) {
cat_postflight(hfsmp, &cookie, 0);
}
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;
hfs_unlock(dcp);
dvp = NULL;
if (hfs_vget(hfsmp, dcp->c_parentcnid, &ddvp, 0) == 0) {
if (VTOC(ddvp)->c_desc.cd_nameptr) {
uid_t uid;
uid = strtoul(VTOC(ddvp)->c_desc.cd_nameptr, 0, 0);
if ((uid == cp->c_uid) ||
(uid == vfs_context_ucred(ctx)->cr_uid)) {
cp->c_flags |= UF_NODUMP;
cp->c_touch_chgtime = TRUE;
}
}
hfs_unlock(VTOC(ddvp));
vnode_put(ddvp);
}
}
if (dvp) {
hfs_unlock(dcp);
}
if (error == 0 && cp != NULL) {
hfs_unlock(cp);
}
if (started_tr) {
hfs_end_transaction(hfsmp);
started_tr = 0;
}
return (error);
}
__private_extern__
int
hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp, struct vnode **rvpp, __unused struct proc *p)
{
struct vnode *rvp;
struct cnode *cp = VTOC(vp);
int error;
int vid;
if ((rvp = cp->c_rsrc_vp)) {
vid = vnode_vid(rvp);
error = vnode_getwithvid(rvp, vid);
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;
struct componentname cn;
int lockflags;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
error = cat_lookup(hfsmp, &cp->c_desc, 1, (struct cat_desc *)0,
(struct cat_attr *)0, &rsrcfork, NULL);
hfs_systemfile_unlock(hfsmp, lockflags);
if (error)
return (error);
cn.cn_pnbuf = NULL;
if (cp->c_desc.cd_nameptr) {
MALLOC_ZONE(cn.cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
cn.cn_nameiop = LOOKUP;
cn.cn_flags = ISLASTCN | HASBUF;
cn.cn_context = NULL;
cn.cn_pnlen = MAXPATHLEN;
cn.cn_nameptr = cn.cn_pnbuf;
cn.cn_hash = 0;
cn.cn_consume = 0;
cn.cn_namelen = sprintf(cn.cn_nameptr, "%s%s", cp->c_desc.cd_nameptr, _PATH_RSRCFORKSPEC);
}
error = hfs_getnewvnode(hfsmp, vnode_parent(vp), cn.cn_pnbuf ? &cn : NULL,
&cp->c_desc, 2, &cp->c_attr, &rsrcfork, &rvp);
if (cn.cn_pnbuf)
FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
if (error)
return (error);
}
*rvpp = rvp;
return (0);
}
static void
filt_hfsdetach(struct knote *kn)
{
struct vnode *vp;
vp = (struct vnode *)kn->kn_hook;
if (vnode_getwithvid(vp, kn->kn_hookid))
return;
if (1) {
if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) {
(void) KNOTE_DETACH(&VTOC(vp)->c_knotes, kn);
hfs_unlock(VTOC(vp));
}
}
vnode_put(vp);
}
static int
filt_hfsread(struct knote *kn, long hint)
{
struct vnode *vp = (struct vnode *)kn->kn_hook;
int dropvp = 0;
if (hint == 0) {
if ((vnode_getwithvid(vp, kn->kn_hookid) != 0)) {
hint = NOTE_REVOKE;
} else
dropvp = 1;
}
if (hint == NOTE_REVOKE) {
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
return (1);
}
kn->kn_data = (!(kn->kn_flags & EV_POLL)) ?
VTOF(vp)->ff_size - kn->kn_fp->f_fglob->fg_offset : 1;
if (dropvp)
vnode_put(vp);
return (kn->kn_data != 0);
}
static int
filt_hfswrite(struct knote *kn, long hint)
{
int dropvp = 0;
if (hint == 0) {
if ((vnode_getwithvid(kn->kn_hook, kn->kn_hookid) != 0)) {
hint = NOTE_REVOKE;
} else
vnode_put(kn->kn_hook);
}
if (hint == NOTE_REVOKE) {
kn->kn_data = 0;
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
return (1);
}
kn->kn_data = 0;
return (1);
}
static int
filt_hfsvnode(struct knote *kn, long hint)
{
if (hint == 0) {
if ((vnode_getwithvid(kn->kn_hook, kn->kn_hookid) != 0)) {
hint = NOTE_REVOKE;
} else
vnode_put(kn->kn_hook);
}
if (kn->kn_sfflags & hint)
kn->kn_fflags |= hint;
if ((hint == NOTE_REVOKE)) {
kn->kn_flags |= (EV_EOF | EV_ONESHOT);
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_vnop_kqfiltadd(
struct vnop_kqfilt_add_args *ap)
{
struct vnode *vp = ap->a_vp;
struct knote *kn = ap->a_kn;
int error;
switch (kn->kn_filter) {
case EVFILT_READ:
if (vnode_isreg(vp)) {
kn->kn_fop = &hfsread_filtops;
} else {
return EINVAL;
};
break;
case EVFILT_WRITE:
if (vnode_isreg(vp)) {
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;
kn->kn_hookid = vnode_vid(vp);
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
return (error);
KNOTE_ATTACH(&VTOC(vp)->c_knotes, kn);
hfs_unlock(VTOC(vp));
return (0);
}
static int
hfs_vnop_kqfiltremove(ap)
struct vnop_kqfilt_remove_args *ap;
{
int result;
result = ENOTSUP;
return (result);
}
static int
hfsspec_read(ap)
struct vnop_read_args *ap;
{
VTOC(ap->a_vp)->c_touch_acctime = TRUE;
return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_read), ap));
}
static int
hfsspec_write(ap)
struct vnop_write_args *ap;
{
VTOC(ap->a_vp)->c_touch_chgtime = TRUE;
VTOC(ap->a_vp)->c_touch_modtime = TRUE;
return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_write), ap));
}
static int
hfsspec_close(ap)
struct vnop_close_args *ap;
{
struct vnode *vp = ap->a_vp;
struct cnode *cp;
if (vnode_isinuse(ap->a_vp, 1)) {
if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) {
cp = VTOC(vp);
hfs_touchtimes(VTOHFS(vp), cp);
hfs_unlock(cp);
}
}
return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_close), ap));
}
#if FIFO
static int
hfsfifo_read(ap)
struct vnop_read_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
VTOC(ap->a_vp)->c_touch_acctime = TRUE;
return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_read), ap));
}
static int
hfsfifo_write(ap)
struct vnop_write_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
VTOC(ap->a_vp)->c_touch_chgtime = TRUE;
VTOC(ap->a_vp)->c_touch_modtime = TRUE;
return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_write), ap));
}
static int
hfsfifo_close(ap)
struct vnop_close_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
struct vnode *vp = ap->a_vp;
struct cnode *cp;
if (vnode_isinuse(ap->a_vp, 1)) {
if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) == 0) {
cp = VTOC(vp);
hfs_touchtimes(VTOHFS(vp), cp);
hfs_unlock(cp);
}
}
return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_close), ap));
}
int
hfsfifo_kqfilt_add(ap)
struct vnop_kqfilt_add_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
int error;
error = VOCALL(fifo_vnodeop_p, VOFFSET(vnop_kqfilt_add), ap);
if (error)
error = hfs_vnop_kqfiltadd(ap);
return (error);
}
int
hfsfifo_kqfilt_remove(ap)
struct vnop_kqfilt_remove_args *ap;
{
extern int (**fifo_vnodeop_p)(void *);
int error;
error = VOCALL(fifo_vnodeop_p, VOFFSET(vnop_kqfilt_remove), ap);
if (error)
error = hfs_vnop_kqfiltremove(ap);
return (error);
}
#endif
static int
hfs_vnop_fsync(ap)
struct vnop_fsync_args *ap;
{
struct vnode* vp = ap->a_vp;
int error;
error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK);
if (error)
return (0);
error = hfs_fsync(vp, ap->a_waitfor, 0, vfs_context_proc(ap->a_context));
hfs_unlock(VTOC(vp));
return (error);
}
int hfs_vnop_readdirattr(struct vnop_readdirattr_args *);
int hfs_vnop_inactive(struct vnop_inactive_args *);
int hfs_vnop_reclaim(struct vnop_reclaim_args *);
int hfs_vnop_link(struct vnop_link_args *);
int hfs_vnop_lookup(struct vnop_lookup_args *);
int hfs_vnop_search(struct vnop_searchfs_args *);
int hfs_vnop_read(struct vnop_read_args *);
int hfs_vnop_write(struct vnop_write_args *);
int hfs_vnop_ioctl(struct vnop_ioctl_args *);
int hfs_vnop_select(struct vnop_select_args *);
int hfs_vnop_strategy(struct vnop_strategy_args *);
int hfs_vnop_allocate(struct vnop_allocate_args *);
int hfs_vnop_pagein(struct vnop_pagein_args *);
int hfs_vnop_pageout(struct vnop_pageout_args *);
int hfs_vnop_bwrite(struct vnop_bwrite_args *);
int hfs_vnop_blktooff(struct vnop_blktooff_args *);
int hfs_vnop_offtoblk(struct vnop_offtoblk_args *);
int hfs_vnop_blockmap(struct vnop_blockmap_args *);
int hfs_vnop_getxattr(struct vnop_getxattr_args *);
int hfs_vnop_setxattr(struct vnop_setxattr_args *);
int hfs_vnop_removexattr(struct vnop_removexattr_args *);
int hfs_vnop_listxattr(struct vnop_listxattr_args *);
int (**hfs_vnodeop_p)(void *);
#define VOPFUNC int (*)(void *)
struct vnodeopv_entry_desc hfs_vnodeop_entries[] = {
{ &vnop_default_desc, (VOPFUNC)vn_default_error },
{ &vnop_lookup_desc, (VOPFUNC)hfs_vnop_lookup },
{ &vnop_create_desc, (VOPFUNC)hfs_vnop_create },
{ &vnop_mknod_desc, (VOPFUNC)hfs_vnop_mknod },
{ &vnop_open_desc, (VOPFUNC)hfs_vnop_open },
{ &vnop_close_desc, (VOPFUNC)hfs_vnop_close },
{ &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr },
{ &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr },
{ &vnop_read_desc, (VOPFUNC)hfs_vnop_read },
{ &vnop_write_desc, (VOPFUNC)hfs_vnop_write },
{ &vnop_ioctl_desc, (VOPFUNC)hfs_vnop_ioctl },
{ &vnop_select_desc, (VOPFUNC)hfs_vnop_select },
{ &vnop_revoke_desc, (VOPFUNC)nop_revoke },
{ &vnop_exchange_desc, (VOPFUNC)hfs_vnop_exchange },
{ &vnop_mmap_desc, (VOPFUNC)err_mmap },
{ &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync },
{ &vnop_remove_desc, (VOPFUNC)hfs_vnop_remove },
{ &vnop_link_desc, (VOPFUNC)hfs_vnop_link },
{ &vnop_rename_desc, (VOPFUNC)hfs_vnop_rename },
{ &vnop_mkdir_desc, (VOPFUNC)hfs_vnop_mkdir },
{ &vnop_rmdir_desc, (VOPFUNC)hfs_vnop_rmdir },
{ &vnop_symlink_desc, (VOPFUNC)hfs_vnop_symlink },
{ &vnop_readdir_desc, (VOPFUNC)hfs_vnop_readdir },
{ &vnop_readdirattr_desc, (VOPFUNC)hfs_vnop_readdirattr },
{ &vnop_readlink_desc, (VOPFUNC)hfs_vnop_readlink },
{ &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive },
{ &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim },
{ &vnop_strategy_desc, (VOPFUNC)hfs_vnop_strategy },
{ &vnop_pathconf_desc, (VOPFUNC)hfs_vnop_pathconf },
{ &vnop_advlock_desc, (VOPFUNC)err_advlock },
{ &vnop_allocate_desc, (VOPFUNC)hfs_vnop_allocate },
{ &vnop_searchfs_desc, (VOPFUNC)hfs_vnop_search },
{ &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite },
{ &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein },
{ &vnop_pageout_desc,(VOPFUNC) hfs_vnop_pageout },
{ &vnop_copyfile_desc, (VOPFUNC)err_copyfile },
{ &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff },
{ &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk },
{ &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap },
{ &vnop_kqfilt_add_desc, (VOPFUNC)hfs_vnop_kqfiltadd },
{ &vnop_kqfilt_remove_desc, (VOPFUNC)hfs_vnop_kqfiltremove },
{ &vnop_getxattr_desc, (VOPFUNC)hfs_vnop_getxattr},
{ &vnop_setxattr_desc, (VOPFUNC)hfs_vnop_setxattr},
{ &vnop_removexattr_desc, (VOPFUNC)hfs_vnop_removexattr},
{ &vnop_listxattr_desc, (VOPFUNC)hfs_vnop_listxattr},
{ 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[] = {
{ &vnop_default_desc, (VOPFUNC)vn_default_error },
{ &vnop_lookup_desc, (VOPFUNC)spec_lookup },
{ &vnop_create_desc, (VOPFUNC)spec_create },
{ &vnop_mknod_desc, (VOPFUNC)spec_mknod },
{ &vnop_open_desc, (VOPFUNC)spec_open },
{ &vnop_close_desc, (VOPFUNC)hfsspec_close },
{ &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr },
{ &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr },
{ &vnop_read_desc, (VOPFUNC)hfsspec_read },
{ &vnop_write_desc, (VOPFUNC)hfsspec_write },
{ &vnop_ioctl_desc, (VOPFUNC)spec_ioctl },
{ &vnop_select_desc, (VOPFUNC)spec_select },
{ &vnop_revoke_desc, (VOPFUNC)spec_revoke },
{ &vnop_mmap_desc, (VOPFUNC)spec_mmap },
{ &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync },
{ &vnop_remove_desc, (VOPFUNC)spec_remove },
{ &vnop_link_desc, (VOPFUNC)spec_link },
{ &vnop_rename_desc, (VOPFUNC)spec_rename },
{ &vnop_mkdir_desc, (VOPFUNC)spec_mkdir },
{ &vnop_rmdir_desc, (VOPFUNC)spec_rmdir },
{ &vnop_symlink_desc, (VOPFUNC)spec_symlink },
{ &vnop_readdir_desc, (VOPFUNC)spec_readdir },
{ &vnop_readlink_desc, (VOPFUNC)spec_readlink },
{ &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive },
{ &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim },
{ &vnop_strategy_desc, (VOPFUNC)spec_strategy },
{ &vnop_pathconf_desc, (VOPFUNC)spec_pathconf },
{ &vnop_advlock_desc, (VOPFUNC)err_advlock },
{ &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite },
{ &vnop_devblocksize_desc, (VOPFUNC)spec_devblocksize },
{ &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein },
{ &vnop_pageout_desc, (VOPFUNC)hfs_vnop_pageout },
{ &vnop_copyfile_desc, (VOPFUNC)err_copyfile },
{ &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff },
{ &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_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[] = {
{ &vnop_default_desc, (VOPFUNC)vn_default_error },
{ &vnop_lookup_desc, (VOPFUNC)fifo_lookup },
{ &vnop_create_desc, (VOPFUNC)fifo_create },
{ &vnop_mknod_desc, (VOPFUNC)fifo_mknod },
{ &vnop_open_desc, (VOPFUNC)fifo_open },
{ &vnop_close_desc, (VOPFUNC)hfsfifo_close },
{ &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr },
{ &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr },
{ &vnop_read_desc, (VOPFUNC)hfsfifo_read },
{ &vnop_write_desc, (VOPFUNC)hfsfifo_write },
{ &vnop_ioctl_desc, (VOPFUNC)fifo_ioctl },
{ &vnop_select_desc, (VOPFUNC)fifo_select },
{ &vnop_revoke_desc, (VOPFUNC)fifo_revoke },
{ &vnop_mmap_desc, (VOPFUNC)fifo_mmap },
{ &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync },
{ &vnop_remove_desc, (VOPFUNC)fifo_remove },
{ &vnop_link_desc, (VOPFUNC)fifo_link },
{ &vnop_rename_desc, (VOPFUNC)fifo_rename },
{ &vnop_mkdir_desc, (VOPFUNC)fifo_mkdir },
{ &vnop_rmdir_desc, (VOPFUNC)fifo_rmdir },
{ &vnop_symlink_desc, (VOPFUNC)fifo_symlink },
{ &vnop_readdir_desc, (VOPFUNC)fifo_readdir },
{ &vnop_readlink_desc, (VOPFUNC)fifo_readlink },
{ &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive },
{ &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim },
{ &vnop_strategy_desc, (VOPFUNC)fifo_strategy },
{ &vnop_pathconf_desc, (VOPFUNC)fifo_pathconf },
{ &vnop_advlock_desc, (VOPFUNC)err_advlock },
{ &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite },
{ &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein },
{ &vnop_pageout_desc, (VOPFUNC)hfs_vnop_pageout },
{ &vnop_copyfile_desc, (VOPFUNC)err_copyfile },
{ &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff },
{ &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk },
{ &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap },
{ &vnop_kqfilt_add_desc, (VOPFUNC)hfsfifo_kqfilt_add },
{ &vnop_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