#include <sys/systm.h>
#include <sys/param.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_if.h>
#include <sys/vnode_internal.h>
#include <sys/malloc.h>
#include <sys/ubc.h>
#include <sys/ubc_internal.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 KNDETACH_VNLOCKED 0x00000001
int always_do_fullfsync = 0;
SYSCTL_DECL(_vfs_generic);
SYSCTL_INT (_vfs_generic, OID_AUTO, always_do_fullfsync, CTLFLAG_RW, &always_do_fullfsync, 0, "always F_FULLFSYNC when fsync is called");
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, __unused struct proc *p);
static int hfs_metasync_all(struct hfsmount *hfsmp);
static int hfs_removedir(struct vnode *, struct vnode *, struct componentname *,
int);
static int hfs_removefile(struct vnode *, struct vnode *, struct componentname *,
int, int, int, struct vnode *);
#if FIFO
static int hfsfifo_read(struct vnop_read_args *);
static int hfsfifo_write(struct vnop_write_args *);
static int hfsfifo_close(struct vnop_close_args *);
extern int (**fifo_vnodeop_p)(void *);
#endif
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*);
static int hfs_vnop_readlink(struct vnop_readlink_args *);
static int hfs_vnop_pathconf(struct vnop_pathconf_args *);
static int hfs_vnop_whiteout(struct vnop_whiteout_args *);
static int hfsspec_read(struct vnop_read_args *);
static int hfsspec_write(struct vnop_write_args *);
static int hfsspec_close(struct vnop_close_args *);
#define HFSRM_SKIP_RESERVE 0x01
static int
hfs_vnop_create(struct vnop_create_args *ap)
{
int error;
again:
error = hfs_makenode(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_context);
if ((error == EEXIST) && !(ap->a_vap->va_vaflags & VA_EXCLUSIVE)) {
struct vnop_lookup_args args;
args.a_desc = &vnop_lookup_desc;
args.a_dvp = ap->a_dvp;
args.a_vpp = ap->a_vpp;
args.a_cnp = ap->a_cnp;
args.a_context = ap->a_context;
args.a_cnp->cn_nameiop = LOOKUP;
error = hfs_vnop_lookup(&args);
if (error == ENOENT) {
goto again;
}
if ((error == 0) && !vnode_isreg(*args.a_vpp)) {
vnode_put(*args.a_vpp);
error = EEXIST;
}
args.a_cnp->cn_nameiop = CREATE;
}
return (error);
}
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);
}
#if HFS_COMPRESSION
static int
hfs_ref_data_vp(struct cnode *cp, struct vnode **data_vp, int skiplock)
{
if (!data_vp || !cp)
return EINVAL;
if (!skiplock) hfs_lock(cp, HFS_SHARED_LOCK);
struct vnode *c_vp = cp->c_vp;
if (c_vp) {
*data_vp = c_vp;
vnode_ref(*data_vp);
if (!skiplock) hfs_unlock(cp);
return 0;
}
if (!cp->c_rsrc_vp) {
*data_vp = NULL;
if (!skiplock) hfs_unlock(cp);
return EINVAL;
}
if (0 == hfs_vget(VTOHFS(cp->c_rsrc_vp), cp->c_cnid, data_vp, 1) &&
0 != data_vp) {
vnode_ref(*data_vp);
vnode_put(*data_vp);
if (!skiplock) hfs_unlock(cp);
return 0;
}
*data_vp = NULL;
if (!skiplock) hfs_unlock(cp);
return EINVAL;
}
static decmpfs_cnode *
hfs_lazy_init_decmpfs_cnode(struct cnode *cp)
{
if (!cp->c_decmp) {
decmpfs_cnode *dp = NULL;
MALLOC_ZONE(dp, decmpfs_cnode *, sizeof(decmpfs_cnode), M_DECMPFS_CNODE, M_WAITOK);
if (!dp) {
return NULL;
}
decmpfs_cnode_init(dp);
if (!OSCompareAndSwapPtr(NULL, dp, (void * volatile *)&cp->c_decmp)) {
decmpfs_cnode_destroy(dp);
FREE_ZONE(dp, sizeof(*dp), M_DECMPFS_CNODE);
}
}
return cp->c_decmp;
}
int
hfs_file_is_compressed(struct cnode *cp, int skiplock)
{
int ret = 0;
if (!(cp->c_flags & UF_COMPRESSED)) {
return 0;
}
decmpfs_cnode *dp = hfs_lazy_init_decmpfs_cnode(cp);
if (!dp) {
return 0;
}
uint32_t decmpfs_state = decmpfs_cnode_get_vnode_state(dp);
switch(decmpfs_state) {
case FILE_IS_COMPRESSED:
case FILE_IS_CONVERTING:
return 1;
case FILE_IS_NOT_COMPRESSED:
return 0;
}
struct vnode *data_vp = NULL;
if (0 == hfs_ref_data_vp(cp, &data_vp, skiplock)) {
if (data_vp) {
ret = decmpfs_file_is_compressed(data_vp, VTOCMP(data_vp)); vnode_rele(data_vp);
}
}
return ret;
}
int
hfs_uncompressed_size_of_compressed_file(struct hfsmount *hfsmp, struct vnode *vp, cnid_t fid, off_t *size, int skiplock)
{
int ret = 0;
int putaway = 0;
if (!size) {
return EINVAL;
}
if (NULL == vp) {
if (!hfsmp || !fid) {
return EINVAL;
}
if (0 != hfs_vget(hfsmp, fid, &vp, skiplock)) {
vp = NULL;
} else {
putaway = 1;
}
}
if ( ( NULL != vp ) && hfs_file_is_compressed(VTOC(vp), skiplock) ) {
*size = decmpfs_cnode_get_vnode_cached_size(VTOCMP(vp));
} else {
ret = EINVAL;
}
if (putaway) {
vnode_put(vp);
vp = NULL;
}
return ret;
}
int
hfs_hides_rsrc(vfs_context_t ctx, struct cnode *cp, int skiplock)
{
if (ctx == decmpfs_ctx)
return 0;
if (!hfs_file_is_compressed(cp, skiplock))
return 0;
return decmpfs_hides_rsrc(ctx, cp->c_decmp);
}
int
hfs_hides_xattr(vfs_context_t ctx, struct cnode *cp, const char *name, int skiplock)
{
if (ctx == decmpfs_ctx)
return 0;
if (!hfs_file_is_compressed(cp, skiplock))
return 0;
return decmpfs_hides_xattr(ctx, cp->c_decmp, name);
}
#endif
static int
hfs_vnop_open(struct vnop_open_args *ap)
{
struct vnode *vp = ap->a_vp;
struct filefork *fp;
struct timeval tv;
int error;
static int past_bootup = 0;
struct cnode *cp = VTOC(vp);
struct hfsmount *hfsmp = VTOHFS(vp);
#if HFS_COMPRESSION
if (ap->a_mode & FWRITE) {
if ( hfs_file_is_compressed(cp, 1) ) {
struct vnode *data_vp = NULL;
error = hfs_ref_data_vp(cp, &data_vp, 1);
if (0 == error) {
if (data_vp) {
error = decmpfs_decompress_file(data_vp, VTOCMP(data_vp), -1, 1, 0);
vnode_rele(data_vp);
} else {
error = EINVAL;
}
}
if (error != 0)
return error;
}
} else {
if (hfs_file_is_compressed(cp, 1) ) {
if (VNODE_IS_RSRC(vp)) {
} else {
error = decmpfs_validate_compressed_file(vp, VTOCMP(vp));
if (error != 0)
return error;
}
}
}
#endif
if ((cp->c_flags & APPEND) && !vnode_isdir(vp) &&
(ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
return (EPERM);
if (vnode_isreg(vp) && !UBCINFOEXISTS(vp))
return (EBUSY);
if (cp->c_fileid == hfsmp->hfs_jnlfileid)
return (EPERM);
#if QUOTA
if ((ap->a_mode & FWRITE) && (hfsmp->hfs_flags & HFS_QUOTAS))
(void)hfs_getinoquota(cp);
#endif
if ((hfsmp->hfs_flags & HFS_READ_ONLY) ||
(hfsmp->jnl == NULL) ||
#if NAMEDSTREAMS
!vnode_isreg(vp) || vnode_isinuse(vp, 0) || vnode_isnamedstream(vp)) {
#else
!vnode_isreg(vp) || vnode_isinuse(vp, 0)) {
#endif
return (0);
}
if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK)))
return (error);
fp = VTOF(vp);
if (fp->ff_blocks &&
fp->ff_extents[7].blockCount != 0 &&
fp->ff_size <= (20 * 1024 * 1024)) {
int no_mods = 0;
struct timeval now;
if (!past_bootup) {
microuptime(&tv);
if (tv.tv_sec > (60*3)) {
past_bootup = 1;
}
}
microtime(&now);
if ((now.tv_sec - cp->c_mtime) > 60) {
no_mods = 1;
}
if (past_bootup && no_mods) {
(void) hfs_relocate(vp, hfsmp->nextAllocation + 4096,
vfs_context_ucred(ap->a_context),
vfs_context_proc(ap->a_context));
}
}
hfs_unlock(cp);
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;
int tooktrunclock = 0;
int knownrefs = 0;
if ( hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) != 0)
return (0);
cp = VTOC(vp);
hfsmp = VTOHFS(vp);
if ((vp->v_type == VREG) && (cp->c_rsrc_vp)
&& (vnode_isnamedstream(cp->c_rsrc_vp))) {
uint32_t blks;
blks = howmany(VTOF(vp)->ff_size, VTOVCB(vp)->blockSize);
if ((blks < VTOF(vp)->ff_blocks) && (!vnode_isinuse(vp, 2))) {
hfs_unlock(cp);
hfs_lock_truncate(cp, TRUE);
tooktrunclock = 1;
if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK) != 0) {
hfs_unlock_truncate(cp, TRUE);
return 0;
}
if (cp->c_rsrc_vp) {
knownrefs = 1 + vnode_isnamedstream(cp->c_rsrc_vp);
if (!vnode_isinuse(vp, knownrefs)){
blks = howmany(VTOF(vp)->ff_size, VTOVCB(vp)->blockSize);
if (blks < VTOF(vp)->ff_blocks){
(void) hfs_truncate(vp, VTOF(vp)->ff_size, IO_NDELAY, 0, 0, ap->a_context);
}
}
}
}
}
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);
}
if (tooktrunclock){
hfs_unlock_truncate(cp, TRUE);
}
hfs_unlock(cp);
if (ap->a_fflag & FWASWRITTEN) {
hfs_sync_ejectable(hfsmp);
}
return (0);
}
static int
hfs_vnop_getattr(struct vnop_getattr_args *ap)
{
#define VNODE_ATTR_TIMES \
(VNODE_ATTR_va_access_time|VNODE_ATTR_va_change_time|VNODE_ATTR_va_modify_time)
#define VNODE_ATTR_AUTH \
(VNODE_ATTR_va_mode | VNODE_ATTR_va_uid | VNODE_ATTR_va_gid | \
VNODE_ATTR_va_flags | VNODE_ATTR_va_acl)
struct vnode *vp = ap->a_vp;
struct vnode_attr *vap = ap->a_vap;
struct vnode *rvp = NULLVP;
struct hfsmount *hfsmp;
struct cnode *cp;
uint64_t data_size;
enum vtype v_type;
int error = 0;
cp = VTOC(vp);
#if HFS_COMPRESSION
int compressed = 0;
int hide_size = 0;
off_t uncompressed_size = -1;
if (VATTR_IS_ACTIVE(vap, va_data_size) || VATTR_IS_ACTIVE(vap, va_total_alloc) || VATTR_IS_ACTIVE(vap, va_data_alloc) || VATTR_IS_ACTIVE(vap, va_total_size)) {
if (VNODE_IS_RSRC(vp)) {
hide_size = hfs_hides_rsrc(ap->a_context, cp, 0);
} else {
compressed = hfs_file_is_compressed(cp, 0);
}
if (compressed && (VATTR_IS_ACTIVE(vap, va_data_size) || VATTR_IS_ACTIVE(vap, va_total_size))) {
if (0 != hfs_uncompressed_size_of_compressed_file(NULL, vp, 0, &uncompressed_size, 0)) {
uncompressed_size = -1;
}
}
}
#endif
if ((vap->va_active & ~VNODE_ATTR_AUTH) == 0) {
if (cp->c_flag & C_NOEXISTS)
return (ENOENT);
vap->va_uid = cp->c_uid;
vap->va_gid = cp->c_gid;
vap->va_mode = cp->c_mode;
vap->va_flags = cp->c_flags;
vap->va_supported |= VNODE_ATTR_AUTH & ~VNODE_ATTR_va_acl;
if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
vap->va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
VATTR_SET_SUPPORTED(vap, va_acl);
}
return (0);
}
hfsmp = VTOHFS(vp);
v_type = vnode_vtype(vp);
if ((vap->va_active & VNODE_ATTR_TIMES) &&
(cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime)) {
if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK)))
return (error);
hfs_touchtimes(hfsmp, cp);
}
else {
if ((error = hfs_lock(cp, HFS_SHARED_LOCK)))
return (error);
}
if (v_type == VDIR) {
data_size = (cp->c_entries + 2) * AVERAGE_HFSDIRENTRY_SIZE;
if (VATTR_IS_ACTIVE(vap, va_nlink)) {
int nlink;
if ((hfsmp->hfs_flags & HFS_FOLDERCOUNT) &&
(cp->c_attr.ca_recflags & kHFSHasFolderCountMask))
nlink = cp->c_attr.ca_dircount;
else
nlink = cp->c_entries;
nlink += 2;
if (cp->c_cnid == kHFSRootFolderID) {
if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0) {
--nlink;
}
if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0) {
--nlink;
}
}
VATTR_RETURN(vap, va_nlink, (u_int64_t)nlink);
}
if (VATTR_IS_ACTIVE(vap, va_nchildren)) {
int entries;
entries = cp->c_entries;
if (cp->c_cnid == kHFSRootFolderID) {
if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0)
--entries;
if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0)
--entries;
if (hfsmp->jnl || ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))
entries -= 2;
}
VATTR_RETURN(vap, va_nchildren, entries);
}
if (VATTR_IS_ACTIVE(vap, va_dirlinkcount)) {
VATTR_RETURN(vap, va_dirlinkcount, (uint32_t)cp->c_linkcount);
}
} else {
data_size = VCTOF(vp, cp)->ff_size;
VATTR_RETURN(vap, va_nlink, (u_int64_t)cp->c_linkcount);
if (VATTR_IS_ACTIVE(vap, va_data_alloc)) {
u_int64_t blocks;
#if HFS_COMPRESSION
if (hide_size) {
VATTR_RETURN(vap, va_data_alloc, 0);
} else if (compressed) {
blocks = cp->c_blocks;
VATTR_RETURN(vap, va_data_alloc, blocks * (u_int64_t)hfsmp->blockSize);
}
else
#endif
{
blocks = VCTOF(vp, cp)->ff_blocks;
VATTR_RETURN(vap, va_data_alloc, blocks * (u_int64_t)hfsmp->blockSize);
}
}
}
if (VATTR_IS_ACTIVE(vap, va_total_size)) {
if (v_type == VDIR) {
VATTR_RETURN(vap, va_total_size, (cp->c_entries + 2) * AVERAGE_HFSDIRENTRY_SIZE);
} else {
u_int64_t total_size = ~0ULL;
struct cnode *rcp;
#if HFS_COMPRESSION
if (hide_size) {
total_size = 0;
} else if (compressed) {
if (uncompressed_size == -1) {
} else {
total_size = uncompressed_size;
}
}
#endif
if (total_size == ~0ULL) {
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, TRUE);
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 (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, (u_int64_t)cp->c_blocks * (u_int64_t)hfsmp->blockSize);
}
}
if (VATTR_IS_ACTIVE(vap, va_acl)) {
if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
vap->va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
VATTR_SET_SUPPORTED(vap, va_acl);
}
}
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_create_time.tv_sec = cp->c_itime;
vap->va_create_time.tv_nsec = 0;
vap->va_modify_time.tv_sec = cp->c_mtime;
vap->va_modify_time.tv_nsec = 0;
vap->va_change_time.tv_sec = cp->c_ctime;
vap->va_change_time.tv_nsec = 0;
vap->va_backup_time.tv_sec = cp->c_btime;
vap->va_backup_time.tv_nsec = 0;
vap->va_iosize = hfsmp->hfs_logBlockSize;
vap->va_uid = cp->c_uid;
vap->va_gid = cp->c_gid;
vap->va_mode = cp->c_mode;
vap->va_flags = cp->c_flags;
vap->va_fileid = (u_int64_t)cp->c_fileid;
if (cp->c_flag & C_HARDLINK) {
vap->va_linkid = (u_int64_t)hfs_currentcnid(cp);
vap->va_parentid = (u_int64_t)hfs_currentparent(cp);
} else {
vap->va_linkid = (u_int64_t)cp->c_cnid;
vap->va_parentid = (u_int64_t)cp->c_parentcnid;
}
vap->va_fsid = hfsmp->hfs_raw_dev;
vap->va_filerev = 0;
vap->va_encoding = cp->c_encoding;
vap->va_rdev = (v_type == VBLK || v_type == VCHR) ? cp->c_rdev : 0;
#if HFS_COMPRESSION
if (VATTR_IS_ACTIVE(vap, va_data_size)) {
if (hide_size)
vap->va_data_size = 0;
else if (compressed) {
if (uncompressed_size == -1) {
vap->va_data_size = data_size;
} else {
vap->va_data_size = uncompressed_size;
}
} else
vap->va_data_size = data_size;
VATTR_SET_SUPPORTED(vap, va_data_size);
}
#else
vap->va_data_size = data_size;
vap->va_supported |= VNODE_ATTR_va_data_size;
#endif
vap->va_supported |= VNODE_ATTR_va_create_time | VNODE_ATTR_va_modify_time |
VNODE_ATTR_va_change_time| VNODE_ATTR_va_backup_time |
VNODE_ATTR_va_iosize | VNODE_ATTR_va_uid |
VNODE_ATTR_va_gid | VNODE_ATTR_va_mode |
VNODE_ATTR_va_flags |VNODE_ATTR_va_fileid |
VNODE_ATTR_va_linkid | VNODE_ATTR_va_parentid |
VNODE_ATTR_va_fsid | VNODE_ATTR_va_filerev |
VNODE_ATTR_va_encoding | VNODE_ATTR_va_rdev;
if (VATTR_IS_ACTIVE(vap, va_name) && (cp->c_cnid != kHFSRootFolderID)) {
struct cat_desc linkdesc;
int lockflags;
int uselinkdesc = 0;
cnid_t nextlinkid = 0;
cnid_t prevlinkid = 0;
if ((cp->c_flag & C_HARDLINK) &&
((cp->c_desc.cd_namelen == 0) || (vap->va_linkid != cp->c_cnid))) {
if ((cp->c_desc.cd_namelen == 0) && (vap->va_linkid == cp->c_fileid)) {
if ((error = hfs_lookuplink(hfsmp, vap->va_linkid, &prevlinkid, &nextlinkid))){
goto out;
}
}
else {
nextlinkid = vap->va_linkid;
}
if (nextlinkid){
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
error = cat_findname(hfsmp, nextlinkid, &linkdesc);
hfs_systemfile_unlock(hfsmp, lockflags);
if (error == 0) {
uselinkdesc = 1;
}
}
}
if (uselinkdesc) {
strlcpy(vap->va_name, (const char*) linkdesc.cd_nameptr, MAXPATHLEN);
VATTR_SET_SUPPORTED(vap, va_name);
cat_releasedesc(&linkdesc);
}
else if (cp->c_desc.cd_namelen) {
strlcpy(vap->va_name, (const char*) cp->c_desc.cd_nameptr, MAXPATHLEN);
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;
#if HFS_COMPRESSION
int decmpfs_reset_state = 0;
error = decmpfs_update_attributes(vp, vap);
if (error)
return error;
#endif
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)) {
#if HFS_COMPRESSION
decmpfs_cnode *dp = VTOCMP(vp);
if (!dp) {
dp = hfs_lazy_init_decmpfs_cnode(VTOC(vp));
if (!dp) {
return ENOMEM;
}
}
decmpfs_lock_compressed_data(dp, 1);
if (hfs_file_is_compressed(VTOC(vp), 1)) {
error = decmpfs_decompress_file(vp, dp, -1, 0, 1);
if (error != 0) {
decmpfs_unlock_compressed_data(dp, 1);
return error;
}
}
#endif
hfs_lock_truncate(VTOC(vp), TRUE);
ubc_setsize(vp, vap->va_data_size);
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
hfs_unlock_truncate(VTOC(vp), TRUE);
#if HFS_COMPRESSION
decmpfs_unlock_compressed_data(dp, 1);
#endif
return (error);
}
cp = VTOC(vp);
error = hfs_truncate(vp, vap->va_data_size, vap->va_vaflags & 0xffff, 1, 0, ap->a_context);
hfs_unlock_truncate(cp, TRUE);
#if HFS_COMPRESSION
decmpfs_unlock_compressed_data(dp, 1);
#endif
if (error)
goto out;
}
if (cp == NULL) {
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK)))
return (error);
cp = VTOC(vp);
}
if (vap->va_active == VNODE_ATTR_va_access_time) {
cp->c_touch_acctime=TRUE;
goto out;
}
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)) {
u_int16_t *fdFlags;
#if HFS_COMPRESSION
if ((cp->c_flags ^ vap->va_flags) & UF_COMPRESSED) {
decmpfs_reset_state = 1;
}
#endif
cp->c_flags = vap->va_flags;
cp->c_touch_chgtime = TRUE;
fdFlags = (u_int16_t *) &cp->c_finderinfo[8];
if (vap->va_flags & UF_HIDDEN)
*fdFlags |= OSSwapHostToBigConstInt16(kFinderInvisibleMask);
else
*fdFlags &= ~OSSwapHostToBigConstInt16(kFinderInvisibleMask);
}
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 (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);
}
if ((error = hfs_update(vp, TRUE)) != 0)
goto out;
out:
if (cp) {
if ((cp->c_flag & C_HARDLINK) && (vp->v_type != VDIR)){
hfs_relorigin(cp, 0);
}
hfs_unlock(cp);
#if HFS_COMPRESSION
if (decmpfs_reset_state) {
decmpfs_cnode *dp = VTOCMP(vp);
if (!dp) {
dp = hfs_lazy_init_decmpfs_cnode(VTOC(vp));
if (!dp) {
return ENOMEM;
}
}
decmpfs_cnode_set_vnode_state(dp, FILE_TYPE_UNKNOWN, 0);
}
#endif
}
return (error);
}
__private_extern__
int
hfs_chmod(struct vnode *vp, int mode, __unused kauth_cred_t cred, __unused struct proc *p)
{
register struct cnode *cp = VTOC(vp);
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
#if !QUOTA
hfs_chown(struct vnode *vp, uid_t uid, gid_t gid, __unused kauth_cred_t cred,
__unused struct proc *p)
#else
hfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred,
__unused struct proc *p)
#endif
{
register struct cnode *cp = VTOC(vp);
uid_t ouid;
gid_t ogid;
#if QUOTA
int error = 0;
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
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;
const unsigned char *from_nameptr;
const unsigned char *to_nameptr;
char from_iname[32];
char to_iname[32];
u_int32_t tempflag;
cnid_t from_parid;
cnid_t to_parid;
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 HFS_COMPRESSION
if ( hfs_file_is_compressed(VTOC(from_vp), 0) ) {
if ( 0 != ( error = decmpfs_decompress_file(from_vp, VTOCMP(from_vp), -1, 0, 1) ) ) {
return error;
}
}
if ( hfs_file_is_compressed(VTOC(to_vp), 0) ) {
if ( 0 != ( error = decmpfs_decompress_file(to_vp, VTOCMP(to_vp), -1, 0, 1) ) ) {
return error;
}
}
#endif // HFS_COMPRESSION
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) ||
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;
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);
if (from_cp->c_flag & C_HARDLINK) {
MAKE_INODE_NAME(from_iname, sizeof(from_iname),
from_cp->c_attr.ca_linkref);
from_nameptr = (unsigned char *)from_iname;
from_parid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
from_cp->c_hint = 0;
} else {
from_nameptr = from_cp->c_desc.cd_nameptr;
from_parid = from_cp->c_parentcnid;
}
if (to_cp->c_flag & C_HARDLINK) {
MAKE_INODE_NAME(to_iname, sizeof(to_iname),
to_cp->c_attr.ca_linkref);
to_nameptr = (unsigned char *)to_iname;
to_parid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
to_cp->c_hint = 0;
} else {
to_nameptr = to_cp->c_desc.cd_nameptr;
to_parid = to_cp->c_parentcnid;
}
error = ExchangeFileIDs(hfsmp, from_nameptr, to_nameptr, from_parid,
to_parid, 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));
tempflag = from_cp->c_flag & (C_HARDLINK | C_HASXATTRS);
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_linkcount = to_cp->c_linkcount;
from_cp->c_flag = to_cp->c_flag & (C_HARDLINK | C_HASXATTRS);
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_linkcount = tempattr.ca_linkcount;
to_cp->c_flag = tempflag;
to_cp->c_attr.ca_recflags = tempattr.ca_recflags;
bcopy(tempattr.ca_finderinfo, to_cp->c_finderinfo, 32);
hfs_chash_rehash(hfsmp, 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;
}
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 rl_entry *invalid_range;
struct timeval tv;
int waitdata;
int wait;
int lockflag;
int took_trunc_lock = 0;
boolean_t trunc_lock_exclusive = FALSE;
wait = (waitfor == MNT_WAIT);
waitdata = (waitfor == MNT_DWAIT) | wait;
if (always_do_fullfsync)
fullsync = 1;
if (vnode_isdir(vp))
goto metasync;
fp = VTOF(vp);
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, trunc_lock_exclusive);
took_trunc_lock = 1;
if (fp->ff_unallocblocks != 0) {
hfs_unlock_truncate(cp, trunc_lock_exclusive);
trunc_lock_exclusive = TRUE;
hfs_lock_truncate(cp, trunc_lock_exclusive);
}
(void) cluster_push(vp, waitdata ? IO_SYNC : 0);
hfs_lock(cp, HFS_FORCE_LOCK);
}
if (fp && (((cp->c_flag & C_ALWAYS_ZEROFILL) && !TAILQ_EMPTY(&fp->ff_invalidranges)) ||
((wait || (cp->c_flag & C_ZFWANTSYNC)) &&
((cp->c_flags & UF_NODUMP) == 0) &&
UBCINFOEXISTS(vp) && (vnode_issystem(vp) ==0) &&
cp->c_zftimeout != 0))) {
microuptime(&tv);
if ((cp->c_flag & C_ALWAYS_ZEROFILL) == 0 && !fullsync && tv.tv_sec < (long)cp->c_zftimeout) {
cp->c_flag |= C_ZFWANTSYNC;
goto datasync;
}
if (!TAILQ_EMPTY(&fp->ff_invalidranges)) {
if (!took_trunc_lock || trunc_lock_exclusive == FALSE) {
hfs_unlock(cp);
if (took_trunc_lock)
hfs_unlock_truncate(cp, trunc_lock_exclusive);
trunc_lock_exclusive = TRUE;
hfs_lock_truncate(cp, trunc_lock_exclusive);
hfs_lock(cp, HFS_FORCE_LOCK);
took_trunc_lock = 1;
}
while ((invalid_range = TAILQ_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, waitdata ? IO_SYNC : 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, trunc_lock_exclusive);
took_trunc_lock = 0;
}
if (hfsmp->jnl)
lockflag = BUF_SKIP_LOCKED;
else
lockflag = 0;
buf_flushdirtyblks(vp, waitdata, 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 && !fullsync && cp->c_hint &&
!ISSET(cp->c_flag, C_DELETED | C_NOEXISTS)) {
hfs_metasync(VTOHFS(vp), (daddr64_t)cp->c_hint, p);
}
if (fullsync) {
if (hfsmp->jnl) {
hfs_journal_flush(hfsmp);
} else {
retval = hfs_metasync_all(hfsmp);
VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, NULL);
}
}
}
return (retval);
}
static int
hfs_metasync(struct hfsmount *hfsmp, daddr64_t node, __unused 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_metasync_all(struct hfsmount *hfsmp)
{
int lockflags;
lockflags = hfs_systemfile_lock(hfsmp,
SFL_CATALOG|SFL_EXTENTS|SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
if (hfsmp->hfs_catalog_vp)
hfs_btsync(hfsmp->hfs_catalog_vp, 0);
if (hfsmp->hfs_extents_vp)
hfs_btsync(hfsmp->hfs_extents_vp, 0);
if (hfsmp->hfs_attribute_vp)
hfs_btsync(hfsmp->hfs_attribute_vp, 0);
if (hfsmp->hfs_catalog_vp)
vnode_waitforwrites(hfsmp->hfs_catalog_vp, 0, 0, 0, "hfs_metasync_all");
if (hfsmp->hfs_extents_vp)
vnode_waitforwrites(hfsmp->hfs_extents_vp, 0, 0, 0, "hfs_metasync_all");
if (hfsmp->hfs_attribute_vp)
vnode_waitforwrites(hfsmp->hfs_attribute_vp, 0, 0, 0, "hfs_metasync_all");
hfs_systemfile_unlock(hfsmp, lockflags);
return 0;
}
static int
hfs_btsync_callback(struct buf *bp, __unused 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;
struct cnode *dcp = VTOC(dvp);
struct cnode *cp = VTOC(vp);
int error;
if (!S_ISDIR(cp->c_mode)) {
return (ENOTDIR);
}
if (dvp == vp) {
return (EINVAL);
}
if ((error = hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK))) {
return (error);
}
if (dcp->c_flag & (C_DELETED | C_NOEXISTS)) {
hfs_unlockpair (dcp, cp);
return ENOENT;
}
error = hfs_removedir(dvp, vp, ap->a_cnp, 0);
hfs_unlockpair(dcp, cp);
return (error);
}
static int
hfs_removedir(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
int skip_reserve)
{
struct cnode *cp;
struct cnode *dcp;
struct hfsmount * hfsmp;
struct cat_desc desc;
int lockflags;
int error = 0, started_tr = 0;
cp = VTOC(vp);
dcp = VTOC(dvp);
hfsmp = VTOHFS(vp);
if (dcp == cp) {
return (EINVAL);
}
if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
return (0);
}
if (cp->c_entries != 0) {
return (ENOTEMPTY);
}
if (cp->c_flag & C_HARDLINK) {
return hfs_unlink(hfsmp, dvp, vp, cnp, skip_reserve);
}
if ((hfsmp->hfs_attribute_vp != NULL) &&
(cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0) {
return hfs_removefile(dvp, vp, cnp, 0, 0, 1, NULL);
}
dcp->c_flag |= C_DIR_MODIFICATION;
#if QUOTA
if (hfsmp->hfs_flags & HFS_QUOTAS)
(void)hfs_getinoquota(cp);
#endif
if ((error = hfs_start_transaction(hfsmp)) != 0) {
goto out;
}
started_tr = 1;
if ((dcp->c_flags & APPEND) || (cp->c_flags & (IMMUTABLE | APPEND))) {
error = EPERM;
goto out;
}
cache_purge(vp);
desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
desc.cd_namelen = cnp->cn_namelen;
desc.cd_parentcnid = dcp->c_fileid;
desc.cd_cnid = cp->c_cnid;
desc.cd_flags = CD_ISDIR;
desc.cd_encoding = cp->c_encoding;
desc.cd_hint = 0;
if (!hfs_valid_cnode(hfsmp, dvp, cnp, cp->c_fileid)) {
error = 0;
goto out;
}
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
if (!skip_reserve) {
if ((error = cat_preflight(hfsmp, CAT_DELETE, NULL, 0))) {
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
}
error = cat_delete(hfsmp, &desc, &cp->c_attr);
if (error == 0) {
if (dcp->c_entries > 0)
dcp->c_entries--;
DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
dcp->c_dirchangecnt++;
dcp->c_touch_chgtime = TRUE;
dcp->c_touch_modtime = TRUE;
hfs_touchtimes(hfsmp, cp);
(void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
cp->c_flag &= ~(C_MODIFIED | C_FORCEUPDATE);
}
hfs_systemfile_unlock(hfsmp, lockflags);
if (error)
goto out;
#if QUOTA
if (hfsmp->hfs_flags & HFS_QUOTAS)
(void)hfs_chkiq(cp, -1, NOCRED, 0);
#endif
hfs_volupdate(hfsmp, VOL_RMDIR, (dcp->c_cnid == kHFSRootFolderID));
if (vnode_isinuse(vp, 0)) {
cp->c_flag |= C_DELETED;
} else {
cp->c_flag |= C_NOEXISTS;
}
out:
dcp->c_flag &= ~C_DIR_MODIFICATION;
wakeup((caddr_t)&dcp->c_flag);
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;
struct cnode *dcp = VTOC(dvp);
struct cnode *cp = VTOC(vp);
struct vnode *rvp = NULL;
struct hfsmount *hfsmp = VTOHFS(vp);
int error=0, recycle_rsrc=0;
int drop_rsrc_vnode = 0;
int vref;
if (dvp == vp) {
return (EINVAL);
}
if ((vp->v_type == VLNK) || (vp->v_type == VREG)) {
if ((error = hfs_lock (cp, HFS_EXCLUSIVE_LOCK))) {
return (error);
}
error = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE);
hfs_unlock(cp);
if (error) {
return (error);
}
drop_rsrc_vnode = 1;
}
hfs_lock_truncate(cp, TRUE);
if ((error = hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK))) {
hfs_unlock_truncate(cp, TRUE);
if (drop_rsrc_vnode) {
vnode_put (rvp);
}
return (error);
}
if (dcp->c_flag & (C_DELETED | C_NOEXISTS)) {
error = ENOENT;
goto rm_done;
}
error = hfs_removefile(dvp, vp, ap->a_cnp, ap->a_flags, 0, 0, rvp);
if (error == 0 && (cp->c_flag & C_DELETED) &&
(rvp) && !vnode_isinuse(rvp, 0)) {
recycle_rsrc = 1;
}
rm_done:
hfs_unlock_truncate(cp, TRUE);
hfs_unlockpair(dcp, cp);
if (recycle_rsrc) {
vref = vnode_ref(rvp);
if (vref == 0) {
vnode_rele(rvp);
}
vnode_recycle(rvp);
}
if (drop_rsrc_vnode) {
vnode_put(rvp);
}
return (error);
}
static int
hfs_removefile_callback(struct buf *bp, void *hfsmp) {
if ( !(buf_flags(bp) & B_META))
panic("hfs: symlink bp @ %p 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, int allow_dirs, struct vnode *rvp)
{
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;
int lockflags;
int error = 0;
int started_tr = 0;
int isbigfile = 0, defer_remove=0, isdir=0;
cp = VTOC(vp);
dcp = VTOC(dvp);
hfsmp = VTOHFS(vp);
if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
return (0);
}
if (!hfs_valid_cnode(hfsmp, dvp, cnp, cp->c_fileid)) {
return 0;
}
if (VNODE_IS_RSRC(vp)) {
return (EPERM);
}
if (hfsmp->jnl &&
(cp->c_fileid == hfsmp->hfs_jnlfileid || cp->c_fileid == hfsmp->hfs_jnlinfoblkid)) {
return (EPERM);
}
if (cp->c_flag & C_HARDLINK) {
if ((flags & VNODE_REMOVE_NODELETEBUSY) && vnode_isinuse(vp, 0)) {
return (EBUSY);
} else {
if ((vnode_isdir(vp) == 1) && (cp->c_linkcount == 1) &&
(allow_dirs == 0)) {
return (EPERM);
}
return hfs_unlink(hfsmp, dvp, vp, cnp, skip_reserve);
}
}
if (vnode_isdir(vp)) {
if (allow_dirs == 0)
return (EPERM);
isdir = 1;
}
if ((cp->c_parentcnid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
(cp->c_parentcnid != dcp->c_fileid)) {
return (EINVAL);
}
dcp->c_flag |= C_DIR_MODIFICATION;
cp->c_flag |= C_DELETED;
cache_purge(vp);
if (isdir == 0) {
dataforkbusy = vnode_isinuse(vp, 0);
if (rvp && (cp->c_blocks - VTOF(vp)->ff_blocks)) {
rsrcforkbusy = vnode_isinuse(rvp, 0);
}
}
if (isdir == 0) {
isbigfile = ((cp->c_datafork->ff_size >= HFS_BIGFILE_SIZE) && overflow_extents(VTOF(vp)));
}
if ((hfsmp->hfs_attribute_vp != NULL) &&
(cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0) {
defer_remove = 1;
}
if (dataforkbusy || rsrcforkbusy) {
if ((flags & VNODE_REMOVE_NODELETEBUSY) ||
(hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid == 0)) {
error = EBUSY;
goto out;
}
}
#if QUOTA
if (hfsmp->hfs_flags & HFS_QUOTAS)
(void)hfs_getinoquota(cp);
#endif
if (isdir == 0 && (!dataforkbusy || !rsrcforkbusy)) {
if (!dataforkbusy && cp->c_datafork->ff_blocks && !isbigfile) {
cp->c_flag |= C_NEED_DATA_SETSIZE;
}
if (!rsrcforkbusy && rvp) {
cp->c_flag |= C_NEED_RSRC_SETSIZE;
}
}
if ((error = hfs_start_transaction(hfsmp)) != 0) {
goto out;
}
started_tr = 1;
if (hfsmp->jnl && vnode_islnk(vp))
buf_iterate(vp, hfs_removefile_callback, BUF_SKIP_NONLOCKED, (void *)hfsmp);
if (isdir == 0 && (!dataforkbusy && !rsrcforkbusy)) {
if (!dataforkbusy && !isbigfile && cp->c_datafork->ff_blocks != 0) {
error = hfs_truncate(vp, (off_t)0, IO_NDELAY, 1, 1, ctx);
if (error)
goto out;
truncated = 1;
}
if (!rsrcforkbusy && rvp) {
error = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 1, 1, ctx);
if (error)
goto out;
truncated = 1;
}
}
if (isdir) {
desc.cd_flags = CD_ISDIR;
}
else {
desc.cd_flags = 0;
}
desc.cd_encoding = cp->c_desc.cd_encoding;
desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
desc.cd_namelen = cnp->cn_namelen;
desc.cd_parentcnid = dcp->c_fileid;
desc.cd_hint = cp->c_desc.cd_hint;
desc.cd_cnid = cp->c_cnid;
microtime(&tv);
if (dataforkbusy || rsrcforkbusy || isbigfile || defer_remove) {
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, sizeof(delname), cp->c_fileid);
bzero(&to_desc, sizeof(to_desc));
to_desc.cd_nameptr = (const u_int8_t *)delname;
to_desc.cd_namelen = strlen(delname);
to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
if (isdir) {
to_desc.cd_flags = CD_ISDIR;
}
else {
to_desc.cd_flags = 0;
}
to_desc.cd_cnid = cp->c_cnid;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
if (!skip_reserve) {
if ((error = cat_preflight(hfsmp, CAT_RENAME, NULL, 0))) {
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
}
error = cat_rename(hfsmp, &desc, &todir_desc,
&to_desc, (struct cat_desc *)NULL);
if (error == 0) {
hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries++;
if (isdir == 1) {
INC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
}
(void) cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
&hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
if (dcp->c_entries > 0)
dcp->c_entries--;
if (isdir == 1) {
DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
}
dcp->c_dirchangecnt++;
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_linkcount;
(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 | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
if (!skip_reserve) {
if ((error = cat_preflight(hfsmp, CAT_DELETE, NULL, 0))) {
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
}
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)) {
printf("hfs: remove: couldn't delete a truncated file (%s)"
"(error %d, data sz %lld; rsrc sz %lld)",
cp->c_desc.cd_nameptr, error, cp->c_datafork->ff_size,
cp->c_rsrcfork->ff_size);
hfs_mark_volume_inconsistent(hfsmp);
} 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) {
if (dcp->c_entries > 0)
dcp->c_entries--;
dcp->c_dirchangecnt++;
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
if (hfsmp->hfs_flags & HFS_QUOTAS)
(void)hfs_chkiq(cp, -1, NOCRED, 0);
#endif
cp->c_flag |= C_NOEXISTS;
cp->c_flag &= ~C_DELETED;
truncated = 0;
cp->c_touch_chgtime = TRUE;
--cp->c_linkcount;
hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
}
cat_releasedesc(&cp->c_desc);
out:
if (error) {
cp->c_flag &= ~C_DELETED;
}
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);
}
dcp->c_flag &= ~C_DIR_MODIFICATION;
wakeup((caddr_t)&dcp->c_flag);
return (error);
}
__private_extern__ void
replace_desc(struct cnode *cp, struct cat_desc *cdp)
{
if (&cp->c_desc == cdp) {
return;
}
if (cp->c_desc.cd_flags & CD_HASBUF && cp->c_desc.cd_nameptr != 0) {
const u_int8_t *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((const char *)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 vnode *fvp_rsrc = NULLVP;
struct vnode *tvp_rsrc = NULLVP;
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 cnode *error_cnode;
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;
int recycle_rsrc = 0;
if ((vnode_isreg(fvp)) || (vnode_islnk(fvp))) {
if ((error = hfs_lock (VTOC(fvp), HFS_EXCLUSIVE_LOCK))) {
return (error);
}
error = hfs_vgetrsrc(VTOHFS(fvp), fvp, &fvp_rsrc, TRUE);
hfs_unlock (VTOC(fvp));
if (error) {
return error;
}
}
if (tvp && (vnode_isreg(tvp) || vnode_islnk(tvp))) {
if (hfs_lock (VTOC(tvp), HFS_EXCLUSIVE_LOCK) == 0) {
error = hfs_vgetrsrc(VTOHFS(tvp), tvp, &tvp_rsrc, TRUE);
hfs_unlock (VTOC(tvp));
if (error) {
if (fvp_rsrc) {
vnode_put (fvp_rsrc);
}
return error;
}
}
}
if (tvp && (vnode_isreg(tvp) || vnode_islnk(tvp))) {
hfs_lock_truncate(VTOC(tvp), TRUE);
took_trunc_lock = 1;
}
retry:
error = hfs_lockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL,
HFS_EXCLUSIVE_LOCK, &error_cnode);
if (error) {
if (took_trunc_lock) {
hfs_unlock_truncate(VTOC(tvp), TRUE);
took_trunc_lock = 0;
}
if ((error == ENOENT) && (tvp != NULL) && (error_cnode == VTOC(tvp))) {
tcp = NULL;
tvp = NULL;
goto retry;
}
if (fvp_rsrc) {
vnode_put (fvp_rsrc);
}
if (tvp_rsrc) {
vnode_put (tvp_rsrc);
}
return (error);
}
fdcp = VTOC(fdvp);
fcp = VTOC(fvp);
tdcp = VTOC(tdvp);
tcp = tvp ? VTOC(tvp) : NULL;
hfsmp = VTOHFS(tdvp);
if (fdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
error = ENOENT;
goto out;
}
if (tdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
error = ENOENT;
goto out;
}
if ((fcp->c_flag & (C_NOEXISTS | C_DELETED)) || !hfs_valid_cnode(hfsmp, fdvp, fcnp, fcp->c_fileid)) {
error = ENOENT;
goto out;
}
if (tcp && ((tcp->c_flag & (C_NOEXISTS | C_DELETED)) || !hfs_valid_cnode(hfsmp, tdvp, tcnp, tcp->c_fileid))) {
if (took_trunc_lock) {
hfs_unlock_truncate(VTOC(tvp), TRUE);
took_trunc_lock = 0;
}
hfs_unlockfour(fdcp, fcp, tdcp, tcp);
tcp = NULL;
tvp = NULL;
goto retry;
}
fdcp->c_flag |= C_DIR_MODIFICATION;
if (fdvp != tdvp) {
tdcp->c_flag |= C_DIR_MODIFICATION;
}
if (vnode_isdir(fvp) && (fdvp != tdvp)) {
if (fcp->c_flag & C_HARDLINK) {
error = EPERM;
goto out;
}
if (fcp->c_attr.ca_recflags & kHFSHasChildLinkMask) {
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
if (cat_check_link_ancestry(hfsmp, tdcp->c_fileid, 0)) {
error = EPERM;
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
hfs_systemfile_unlock(hfsmp, lockflags);
}
}
if (tdcp->c_parentcnid == fcp->c_fileid) {
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);
bzero(&from_desc, sizeof(from_desc));
from_desc.cd_nameptr = (const u_int8_t *)fcnp->cn_nameptr;
from_desc.cd_namelen = fcnp->cn_namelen;
from_desc.cd_parentcnid = fdcp->c_fileid;
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 = (const u_int8_t *)tcnp->cn_nameptr;
to_desc.cd_namelen = tcnp->cn_namelen;
to_desc.cd_parentcnid = tdcp->c_fileid;
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 (vnode_isdir(fvp) && (fdvp != tdvp)) {
if (fcp->c_attr.ca_recflags & kHFSHasChildLinkMask) {
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
if (cat_check_link_ancestry(hfsmp, tdcp->c_fileid, 0)) {
error = EPERM;
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
hfs_systemfile_unlock(hfsmp, lockflags);
}
}
if (fcp->c_flag & C_HARDLINK) {
struct cat_desc tmpdesc;
cnid_t real_cnid;
tmpdesc.cd_nameptr = (const u_int8_t *)fcnp->cn_nameptr;
tmpdesc.cd_namelen = fcnp->cn_namelen;
tmpdesc.cd_parentcnid = fdcp->c_fileid;
tmpdesc.cd_hint = fdcp->c_childhint;
tmpdesc.cd_flags = fcp->c_desc.cd_flags & CD_ISDIR;
tmpdesc.cd_encoding = 0;
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);
}
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)) {
goto skip_rm;
} else if ((fdvp != tdvp) ||
(hfsmp->hfs_flags & HFS_CASE_SENSITIVE)) {
goto out;
} else if (hfs_namecmp((const u_int8_t *)fcnp->cn_nameptr, fcnp->cn_namelen,
(const u_int8_t *)tcnp->cn_nameptr, tcnp->cn_namelen) == 0) {
goto skip_rm;
} else {
goto out;
}
}
if (vnode_isdir(tvp))
error = hfs_removedir(tdvp, tvp, tcnp, HFSRM_SKIP_RESERVE);
else {
error = hfs_removefile(tdvp, tvp, tcnp, 0, HFSRM_SKIP_RESERVE, 0, tvp_rsrc);
if ((error == 0) && (tcp->c_flag & C_DELETED) && tvp_rsrc && !vnode_isinuse(tvp_rsrc, 0)) {
recycle_rsrc = 1;
}
}
if (error)
goto out;
tvp_deleted = 1;
}
skip_rm:
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) {
if (error == EEXIST) {
error = ERECYCLE;
}
goto out;
}
if (tdcp->c_flag & C_NEG_ENTRIES) {
cache_purge_negatives(tdvp);
tdcp->c_flag &= ~C_NEG_ENTRIES;
}
replace_desc(fcp, &out_desc);
fcp->c_parentcnid = tdcp->c_fileid;
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) {
if (vnode_isdir(fvp)) {
if ((fcp->c_attr.ca_recflags & kHFSHasChildLinkMask) &&
!(tdcp->c_attr.ca_recflags & kHFSHasChildLinkMask)) {
tdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
error = cat_set_childlinkbit(hfsmp, tdcp->c_parentcnid);
if (error) {
printf ("hfs_vnop_rename: error updating parent chain for %u\n", tdcp->c_cnid);
error = 0;
}
}
INC_FOLDERCOUNT(hfsmp, tdcp->c_attr);
DEC_FOLDERCOUNT(hfsmp, fdcp->c_attr);
}
tdcp->c_entries++;
tdcp->c_dirchangecnt++;
if (fdcp->c_entries > 0)
fdcp->c_entries--;
fdcp->c_dirchangecnt++;
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);
}
fdcp->c_flag &= ~C_DIR_MODIFICATION;
wakeup((caddr_t)&fdcp->c_flag);
if (fdvp != tdvp) {
tdcp->c_flag &= ~C_DIR_MODIFICATION;
wakeup((caddr_t)&tdcp->c_flag);
}
if (took_trunc_lock)
hfs_unlock_truncate(VTOC(tvp), TRUE);
hfs_unlockfour(fdcp, fcp, tdcp, tcp);
if (recycle_rsrc) {
int vref;
vref = vnode_ref(tvp_rsrc);
if (vref == 0) {
vnode_rele(tvp_rsrc);
}
vnode_recycle(tvp_rsrc);
}
if (tvp_rsrc) {
vnode_put(tvp_rsrc);
}
if (fvp_rsrc) {
vnode_put(fvp_rsrc);
}
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 cnode *cp = NULL;
struct hfsmount *hfsmp;
struct filefork *fp;
struct buf *bp = NULL;
char *datap;
int started_tr = 0;
u_int32_t len;
int error;
if (VTOVCB(dvp)->vcbSigWord != kHFSPlusSigWord)
return (ENOTSUP);
if (ap->a_target[0] == 0)
return (EINVAL);
hfsmp = VTOHFS(dvp);
len = strlen(ap->a_target);
if (((u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize) < len) {
return (ENOSPC);
}
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))) {
goto out;
}
cp = VTOC(vp);
fp = VTOF(vp);
if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
goto out;
}
#if QUOTA
(void)hfs_getinoquota(cp);
#endif
if ((error = hfs_start_transaction(hfsmp)) != 0) {
goto out;
}
started_tr = 1;
error = hfs_truncate(vp, len, IO_NOZEROFILL, 1, 0, ap->a_context);
if (error) {
hfs_end_transaction(hfsmp);
hfs_unlock(cp);
hfs_lock_truncate(cp, TRUE);
hfs_lock(cp, HFS_FORCE_LOCK);
if (hfs_start_transaction(hfsmp) != 0) {
started_tr = 0;
hfs_unlock_truncate(cp, TRUE);
goto out;
}
(void) hfs_removefile(dvp, vp, ap->a_cnp, 0, 0, 0, NULL);
hfs_unlock_truncate(cp, TRUE);
goto out;
}
bp = buf_getblk(vp, (daddr64_t)0, roundup((int)fp->ff_size, hfsmp->hfs_physical_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, NULL, NULL);
} else {
buf_bawrite(bp);
}
ubc_setsize(vp, len);
out:
if (started_tr)
hfs_end_transaction(hfsmp);
if ((cp != NULL) && (vp != NULL)) {
hfs_unlock(cp);
}
if (error) {
if (vp) {
vnode_put(vp);
}
*vpp = NULL;
}
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;
cnid_t cnid_hint = 0;
items = 0;
startoffset = offset = uio_offset(uio);
extended = (ap->a_flags & VNODE_READDIR_EXTENDED);
nfs_cookies = extended && (ap->a_flags & VNODE_READDIR_REQSEEKOFF);
if (uio_iovcnt(uio) > 1)
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 && !(cp->c_flag & C_DELETED)) {
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_fileid) {
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, 0);
dirhint->dh_index &= HFS_INDEX_MASK;
if (dirhint->dh_index == HFS_INDEX_MASK) {
dirhint->dh_index = -1;
}
}
if (index == 0) {
dirhint->dh_threadhint = cp->c_dirthreadhint;
}
error = cat_getdirentries(hfsmp, cp->c_entries, dirhint, uio, extended, &items, &eofflag);
if (index == 0 && error == 0) {
cp->c_dirthreadhint = dirhint->dh_threadhint;
}
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) {
printf("hfs: zero length symlink on fileid %d\n", cp->c_fileid);
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);
if (fp->ff_symlinkptr == NULL) {
error = ENOMEM;
goto exit;
}
error = (int)buf_meta_bread(vp, (daddr64_t)0,
roundup((int)fp->ff_size, VTOHFS(vp)->hfs_physical_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 = 200112;
break;
case _PC_NO_TRUNC:
*ap->a_retval = 200112;
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;
case _PC_FILESIZEBITS:
if (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD)
*ap->a_retval = 32;
else
*ap->a_retval = 64;
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 cat_fork rsrcfork;
struct hfsmount *hfsmp;
int lockflags;
int error;
p = current_proc();
hfsmp = VTOHFS(vp);
if (((vnode_issystem(vp) && (cp->c_cnid < kHFSFirstUserCatalogNodeID))) ||
hfsmp->hfs_catalog_vp == NULL){
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;
return (0);
}
if ((error = hfs_start_transaction(hfsmp)) != 0) {
return error;
}
if (dataforkp && !TAILQ_EMPTY(&cp->c_datafork->ff_invalidranges)) {
bcopy(dataforkp, &datafork, sizeof(datafork));
datafork.cf_size = TAILQ_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;
}
if (rsrcforkp && (cp->c_rsrcfork->ff_unallocblocks != 0)) {
bcopy(rsrcforkp, &rsrcfork, sizeof(rsrcfork));
rsrcfork.cf_blocks = (cp->c_rsrcfork->ff_blocks - cp->c_rsrcfork->ff_unallocblocks);
rsrcfork.cf_size = rsrcfork.cf_blocks * HFSTOVCB(hfsmp)->blockSize;
rsrcforkp = &rsrcfork;
}
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_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);
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 = NULL;
struct vnode *tvp;
struct hfsmount *hfsmp;
struct cat_desc in_desc, out_desc;
struct cat_attr attr;
struct timeval tv;
int lockflags;
int error, started_tr = 0;
enum vtype vnodetype;
int mode;
if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK)))
return (error);
dcp = VTOC(dvp);
dcp->c_flag |= C_DIR_MODIFICATION;
hfsmp = VTOHFS(dvp);
*vpp = NULL;
tvp = NULL;
out_desc.cd_flags = 0;
out_desc.cd_nameptr = NULL;
vnodetype = vap->va_type;
if (vnodetype == VNON)
vnodetype = VREG;
mode = MAKEIMODE(vnodetype, vap->va_mode);
if ((hfs_freeblks(hfsmp, 1) == 0) && (vfs_context_suser(ctx) != 0)) {
error = ENOSPC;
goto exit;
}
microtime(&tv);
bzero(&attr, sizeof(attr));
attr.ca_mode = mode;
attr.ca_linkcount = 1;
if (VATTR_IS_ACTIVE(vap, va_rdev)) {
attr.ca_rdev = vap->va_rdev;
}
if (VATTR_IS_ACTIVE(vap, va_create_time)) {
VATTR_SET_SUPPORTED(vap, va_create_time);
attr.ca_itime = vap->va_create_time.tv_sec;
} else {
attr.ca_itime = tv.tv_sec;
}
if ((hfsmp->hfs_flags & HFS_STANDARD) && gTimeZone.tz_dsttime) {
attr.ca_itime += 3600;
}
attr.ca_atime = attr.ca_ctime = attr.ca_mtime = attr.ca_itime;
attr.ca_atimeondisk = attr.ca_atime;
if (VATTR_IS_ACTIVE(vap, va_flags)) {
VATTR_SET_SUPPORTED(vap, va_flags);
attr.ca_flags = vap->va_flags;
}
if (!(hfsmp->hfs_flags & HFS_STANDARD)) {
if (vnodetype == VDIR) {
if (hfsmp->hfs_flags & HFS_FOLDERCOUNT)
attr.ca_recflags = kHFSHasFolderCountMask;
} else {
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 QUOTA
if (hfsmp->hfs_flags & HFS_QUOTAS) {
if ((error = hfs_quotacheck(hfsmp, 1, attr.ca_uid, attr.ca_gid,
vfs_context_ucred(ctx)))) {
goto exit;
}
}
#endif
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 = (const u_int8_t *)cnp->cn_nameptr;
in_desc.cd_namelen = cnp->cn_namelen;
in_desc.cd_parentcnid = dcp->c_fileid;
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;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
if ((error = cat_preflight(hfsmp, CAT_CREATE, NULL, 0))) {
hfs_systemfile_unlock(hfsmp, lockflags);
goto exit;
}
error = cat_create(hfsmp, &in_desc, &attr, &out_desc);
if (error == 0) {
dcp->c_childhint = out_desc.cd_hint;
dcp->c_entries++;
if (vnodetype == VDIR) {
INC_FOLDERCOUNT(hfsmp, dcp->c_attr);
}
dcp->c_dirchangecnt++;
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 exit;
if (dcp->c_flag & C_NEG_ENTRIES) {
cache_purge_negatives(dvp);
dcp->c_flag &= ~C_NEG_ENTRIES;
}
hfs_volupdate(hfsmp, vnodetype == VDIR ? VOL_MKDIR : VOL_MKFILE,
(dcp->c_cnid == kHFSRootFolderID));
if (started_tr) {
hfs_end_transaction(hfsmp);
started_tr = 0;
}
if (S_ISWHT(mode)) {
goto exit;
}
if (dcp) {
dcp->c_flag &= ~C_DIR_MODIFICATION;
wakeup((caddr_t)&dcp->c_flag);
hfs_unlock(dcp);
dcp = NULL;
}
error = hfs_getnewvnode(hfsmp, dvp, cnp, &out_desc, GNV_CREATE, &attr, NULL, &tvp);
if (error)
goto exit;
cp = VTOC(tvp);
*vpp = tvp;
exit:
cat_releasedesc(&out_desc);
if (dcp) {
dcp->c_flag &= ~C_DIR_MODIFICATION;
wakeup((caddr_t)&dcp->c_flag);
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, int can_drop_lock)
{
struct vnode *rvp;
struct vnode *dvp = NULLVP;
struct cnode *cp = VTOC(vp);
int error;
int vid;
restart:
if ((rvp = cp->c_rsrc_vp)) {
vid = vnode_vid(rvp);
if (can_drop_lock)
hfs_unlock(cp);
error = vnode_getwithvid(rvp, vid);
if (can_drop_lock) {
(void) hfs_lock(cp, HFS_FORCE_LOCK);
if (error == ENOENT)
goto restart;
}
if (error) {
const char * name = (const char *)VTOC(vp)->c_desc.cd_nameptr;
if (name)
printf("hfs_vgetrsrc: couldn't get resource"
" fork for %s, err %d\n", name, error);
return (error);
}
} else {
struct cat_fork rsrcfork;
struct componentname cn;
struct cat_desc *descptr = NULL;
struct cat_desc to_desc;
char delname[32];
int lockflags;
if (cp->c_lockowner != current_thread()) {
if (!can_drop_lock) {
return (EINVAL);
}
if (lck_rw_lock_shared_to_exclusive(&cp->c_rwlock) == FALSE)
lck_rw_lock_exclusive(&cp->c_rwlock);
cp->c_lockowner = current_thread();
}
if ((cp->c_flag & C_DELETED ) && (cp->c_desc.cd_namelen == 0)) {
bzero (&to_desc, sizeof(to_desc));
bzero (delname, 32);
MAKE_DELETED_NAME(delname, sizeof(delname), cp->c_fileid);
to_desc.cd_nameptr = (const u_int8_t*) delname;
to_desc.cd_namelen = strlen(delname);
to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
to_desc.cd_flags = 0;
to_desc.cd_cnid = cp->c_cnid;
descptr = &to_desc;
}
else {
descptr = &cp->c_desc;
}
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
error = cat_lookup(hfsmp, descptr, 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 (descptr->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 = snprintf(cn.cn_nameptr, MAXPATHLEN,
"%s%s", descptr->cd_nameptr,
_PATH_RSRCFORKSPEC);
}
dvp = vnode_getparent(vp);
error = hfs_getnewvnode(hfsmp, dvp, cn.cn_pnbuf ? &cn : NULL,
descptr, GNV_WANTRSRC | GNV_SKIPLOCK, &cp->c_attr,
&rsrcfork, &rvp);
if (dvp)
vnode_put(dvp);
if (cn.cn_pnbuf)
FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
if (error)
return (error);
}
*rvpp = rvp;
return (0);
}
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, 0)) {
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;
{
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;
{
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;
{
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));
}
#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);
}
static int
hfs_vnop_whiteout(ap)
struct vnop_whiteout_args *ap;
{
int error = 0;
struct vnode *vp = NULL;
struct vnode_attr va;
struct vnop_lookup_args lookup_args;
struct vnop_remove_args remove_args;
struct hfsmount *hfsmp;
hfsmp = VTOHFS(ap->a_dvp);
if (hfsmp->hfs_flags & HFS_STANDARD) {
error = ENOTSUP;
goto exit;
}
switch (ap->a_flags) {
case LOOKUP:
error = 0;
break;
case CREATE:
VATTR_INIT(&va);
VATTR_SET(&va, va_type, VREG);
VATTR_SET(&va, va_mode, S_IFWHT);
VATTR_SET(&va, va_uid, 0);
VATTR_SET(&va, va_gid, 0);
error = hfs_makenode(ap->a_dvp, &vp, ap->a_cnp, &va, ap->a_context);
break;
case DELETE:
lookup_args.a_dvp = ap->a_dvp;
lookup_args.a_vpp = &vp;
lookup_args.a_cnp = ap->a_cnp;
lookup_args.a_context = ap->a_context;
error = hfs_vnop_lookup(&lookup_args);
if (error) {
break;
}
remove_args.a_dvp = ap->a_dvp;
remove_args.a_vp = vp;
remove_args.a_cnp = ap->a_cnp;
remove_args.a_flags = 0;
remove_args.a_context = ap->a_context;
error = hfs_vnop_remove(&remove_args);
vnode_put(vp);
break;
default:
panic("hfs_vnop_whiteout: unknown operation (flag = %x)\n", ap->a_flags);
};
exit:
return (error);
}
int (**hfs_vnodeop_p)(void *);
int (**hfs_std_vnodeop_p) (void *);
#define VOPFUNC int (*)(void *)
static int hfs_readonly_op (__unused void* ap) { return (EROFS); }
struct vnodeopv_entry_desc hfs_standard_vnodeop_entries[] = {
{ &vnop_default_desc, (VOPFUNC)vn_default_error },
{ &vnop_lookup_desc, (VOPFUNC)hfs_vnop_lookup },
{ &vnop_create_desc, (VOPFUNC)hfs_readonly_op },
{ &vnop_mknod_desc, (VOPFUNC)hfs_readonly_op },
{ &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_readonly_op },
{ &vnop_read_desc, (VOPFUNC)hfs_vnop_read },
{ &vnop_write_desc, (VOPFUNC)hfs_readonly_op },
{ &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_readonly_op },
{ &vnop_mmap_desc, (VOPFUNC)err_mmap },
{ &vnop_fsync_desc, (VOPFUNC)hfs_readonly_op},
{ &vnop_remove_desc, (VOPFUNC)hfs_readonly_op },
{ &vnop_link_desc, (VOPFUNC)hfs_readonly_op },
{ &vnop_rename_desc, (VOPFUNC)hfs_readonly_op },
{ &vnop_mkdir_desc, (VOPFUNC)hfs_readonly_op },
{ &vnop_rmdir_desc, (VOPFUNC)hfs_readonly_op },
{ &vnop_symlink_desc, (VOPFUNC)hfs_readonly_op },
{ &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_readonly_op },
{ &vnop_searchfs_desc, (VOPFUNC)hfs_vnop_search },
{ &vnop_bwrite_desc, (VOPFUNC)hfs_readonly_op },
{ &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein },
{ &vnop_pageout_desc,(VOPFUNC) hfs_readonly_op },
{ &vnop_copyfile_desc, (VOPFUNC)hfs_readonly_op },
{ &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff },
{ &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk },
{ &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap },
{ &vnop_getxattr_desc, (VOPFUNC)hfs_vnop_getxattr},
{ &vnop_setxattr_desc, (VOPFUNC)hfs_readonly_op},
{ &vnop_removexattr_desc, (VOPFUNC)hfs_readonly_op},
{ &vnop_listxattr_desc, (VOPFUNC)hfs_vnop_listxattr},
{ &vnop_whiteout_desc, (VOPFUNC)hfs_readonly_op},
#if NAMEDSTREAMS
{ &vnop_getnamedstream_desc, (VOPFUNC)hfs_vnop_getnamedstream },
{ &vnop_makenamedstream_desc, (VOPFUNC)hfs_readonly_op },
{ &vnop_removenamedstream_desc, (VOPFUNC)hfs_readonly_op },
#endif
{ NULL, (VOPFUNC)NULL }
};
struct vnodeopv_desc hfs_std_vnodeop_opv_desc =
{ &hfs_std_vnodeop_p, hfs_standard_vnodeop_entries };
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_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},
{ &vnop_whiteout_desc, (VOPFUNC)hfs_vnop_whiteout},
#if NAMEDSTREAMS
{ &vnop_getnamedstream_desc, (VOPFUNC)hfs_vnop_getnamedstream },
{ &vnop_makenamedstream_desc, (VOPFUNC)hfs_vnop_makenamedstream },
{ &vnop_removenamedstream_desc, (VOPFUNC)hfs_vnop_removenamedstream },
#endif
{ 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_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 },
{ (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
};
struct vnodeopv_desc hfs_fifoop_opv_desc =
{ &hfs_fifoop_p, hfs_fifoop_entries };
#endif