#include <stdbool.h>
#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/buf_internal.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 <sys/fsctl.h>
#include <sys/xattr.h>
#include <string.h>
#include <sys/fsevents.h>
#include <kern/kalloc.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 <stdbool.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 "hfs_kdebug.h"
#include "hfs_cprotect.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 | CTLFLAG_LOCKED, &always_do_fullfsync, 0, "always F_FULLFSYNC when fsync is called");
int hfs_makenode(struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp, struct vnode_attr *vap,
vfs_context_t ctx);
int hfs_metasync(struct hfsmount *hfsmp, daddr64_t node, __unused struct proc *p);
int hfs_metasync_all(struct hfsmount *hfsmp);
int hfs_removedir(struct vnode *, struct vnode *, struct componentname *,
int, int);
int hfs_removefile(struct vnode *, struct vnode *, struct componentname *,
int, int, int, struct vnode *, int);
int hfs_removefile_callback(struct buf *bp, void *hfsmp);
enum {
HFS_MOVE_DATA_INCLUDE_RSRC = 1,
};
typedef uint32_t hfs_move_data_options_t;
static int hfs_move_data(cnode_t *from_cp, cnode_t *to_cp,
hfs_move_data_options_t options);
static int hfs_move_fork(filefork_t *srcfork, cnode_t *src,
filefork_t *dstfork, cnode_t *dst);
#if HFS_COMPRESSION
static int hfs_move_compressed(cnode_t *from_vp, cnode_t *to_vp);
#endif
decmpfs_cnode* hfs_lazy_init_decmpfs_cnode (struct cnode *cp);
#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
int hfs_vnop_close(struct vnop_close_args*);
int hfs_vnop_create(struct vnop_create_args*);
int hfs_vnop_exchange(struct vnop_exchange_args*);
int hfs_vnop_fsync(struct vnop_fsync_args*);
int hfs_vnop_mkdir(struct vnop_mkdir_args*);
int hfs_vnop_mknod(struct vnop_mknod_args*);
int hfs_vnop_getattr(struct vnop_getattr_args*);
int hfs_vnop_open(struct vnop_open_args*);
int hfs_vnop_readdir(struct vnop_readdir_args*);
int hfs_vnop_remove(struct vnop_remove_args*);
int hfs_vnop_rename(struct vnop_rename_args*);
int hfs_vnop_rmdir(struct vnop_rmdir_args*);
int hfs_vnop_symlink(struct vnop_symlink_args*);
int hfs_vnop_setattr(struct vnop_setattr_args*);
int hfs_vnop_readlink(struct vnop_readlink_args *);
int hfs_vnop_pathconf(struct vnop_pathconf_args *);
int hfs_vnop_mmap(struct vnop_mmap_args *ap);
int hfsspec_read(struct vnop_read_args *);
int hfsspec_write(struct vnop_write_args *);
int hfsspec_close(struct vnop_close_args *);
#define HFSRM_SKIP_RESERVE 0x01
static _Bool
hfs_is_journal_file(struct hfsmount *hfsmp, struct cnode *cp)
{
if (hfsmp->jnl != NULL &&
(cp->c_fileid == hfsmp->hfs_jnlinfoblkid ||
cp->c_fileid == hfsmp->hfs_jnlfileid)) {
return true;
} else {
return false;
}
}
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);
}
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)
{
int vref = 0;
if (!data_vp || !cp)
return EINVAL;
if (!skiplock) hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
struct vnode *c_vp = cp->c_vp;
if (c_vp) {
*data_vp = c_vp;
vref = vnode_ref(*data_vp);
if (!skiplock) hfs_unlock(cp);
if (vref == 0) {
return 0;
}
return EINVAL;
}
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) &&
0 != data_vp) {
vref = vnode_ref(*data_vp);
vnode_put(*data_vp);
if (!skiplock) hfs_unlock(cp);
if (vref == 0) {
return 0;
}
return EINVAL;
}
*data_vp = NULL;
if (!skiplock) hfs_unlock(cp);
return EINVAL;
}
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_bsdflags & 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, 0)) {
vp = NULL;
} else {
putaway = 1;
}
}
if (vp) {
if (hfs_file_is_compressed(VTOC(vp), skiplock) ) {
*size = decmpfs_cnode_get_vnode_cached_size(VTOCMP(vp));
} else {
if (VTOCMP(vp) && VTOCMP(vp)->cmp_type >= CMP_MAX) {
if (VTOCMP(vp)->cmp_type != DATALESS_CMPFS_TYPE) {
if (VTOC(vp)->c_datafork) {
*size = VTOC(vp)->c_datafork->ff_size;
ret = 0;
} else {
ret = EINVAL;
}
} else {
*size = decmpfs_cnode_get_vnode_cached_size(VTOCMP(vp));
ret = 0;
}
} 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 struct doc_tombstone *
get_uthread_doc_tombstone(void)
{
struct uthread *ut;
ut = get_bsdthread_info(current_thread());
if (ut->t_tombstone == NULL) {
ut->t_tombstone = kalloc(sizeof(struct doc_tombstone));
if (ut->t_tombstone) {
memset(ut->t_tombstone, 0, sizeof(struct doc_tombstone));
}
}
return ut->t_tombstone;
}
static void
clear_tombstone_docid(struct doc_tombstone *ut, __unused struct hfsmount *hfsmp, struct cnode *dst_cnode)
{
uint32_t old_id = ut->t_lastop_document_id;
ut->t_lastop_document_id = 0;
ut->t_lastop_parent = NULL;
ut->t_lastop_parent_vid = 0;
ut->t_lastop_filename[0] = '\0';
if (dst_cnode && old_id && ut->t_lastop_item && vnode_vid(ut->t_lastop_item) == ut->t_lastop_item_vid) {
struct cnode *ocp = VTOC(ut->t_lastop_item);
struct FndrExtendedFileInfo *ofip = (struct FndrExtendedFileInfo *)((char *)&ocp->c_attr.ca_finderinfo + 16);
ofip->document_id = 0;
ocp->c_bsdflags &= ~UF_TRACKED;
ocp->c_flag |= C_MODIFIED;
}
#if CONFIG_FSE
if (dst_cnode && old_id) {
struct FndrExtendedFileInfo *fip = (struct FndrExtendedFileInfo *)((char *)&dst_cnode->c_attr.ca_finderinfo + 16);
add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
FSE_ARG_DEV, hfsmp->hfs_raw_dev,
FSE_ARG_INO, (ino64_t)ut->t_lastop_fileid, FSE_ARG_INO, (ino64_t)dst_cnode->c_fileid, FSE_ARG_INT32, (uint32_t)fip->document_id,
FSE_ARG_DONE);
}
#endif
ut->t_lastop_item = NULL;
ut->t_lastop_fileid = 0;
ut->t_lastop_item_vid = 0;
}
static int
is_ignorable_temp_name(const char *nameptr, int len)
{
if (len == 0) {
len = strlen(nameptr);
}
if ( strncmp(nameptr, "atmp", 4) == 0
|| (len > 4 && strncmp(nameptr+len-4, ".bak", 4) == 0)
|| (len > 4 && strncmp(nameptr+len-4, ".tmp", 4) == 0)) {
return 1;
}
return 0;
}
static int
should_save_docid_tombstone(struct doc_tombstone *ut, struct vnode *vp, struct componentname *cnp)
{
if (cnp->cn_nameptr == NULL) {
return 0;
}
if (ut->t_lastop_document_id && ut->t_lastop_item == vp && is_ignorable_temp_name(cnp->cn_nameptr, cnp->cn_namelen)) {
return 0;
}
return 1;
}
static void
save_tombstone(struct hfsmount *hfsmp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp, int for_unlink)
{
struct cnode *cp = VTOC(vp);
struct doc_tombstone *ut;
ut = get_uthread_doc_tombstone();
if (for_unlink && vp->v_type == VREG && cp->c_linkcount > 1) {
return;
}
ut->t_lastop_parent = dvp;
ut->t_lastop_parent_vid = vnode_vid(dvp);
ut->t_lastop_fileid = cp->c_fileid;
if (for_unlink) {
ut->t_lastop_item = NULL;
ut->t_lastop_item_vid = 0;
} else {
ut->t_lastop_item = vp;
ut->t_lastop_item_vid = vnode_vid(vp);
}
strlcpy((char *)&ut->t_lastop_filename[0], cnp->cn_nameptr, sizeof(ut->t_lastop_filename));
struct FndrExtendedFileInfo *fip = (struct FndrExtendedFileInfo *)((char *)&cp->c_attr.ca_finderinfo + 16);
ut->t_lastop_document_id = fip->document_id;
if (for_unlink) {
fip->document_id = 0;
cp->c_bsdflags &= ~UF_TRACKED;
if (ut->t_lastop_document_id) {
(void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
#if CONFIG_FSE
add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
FSE_ARG_DEV, hfsmp->hfs_raw_dev,
FSE_ARG_INO, (ino64_t)cp->c_fileid, FSE_ARG_INO, (ino64_t)0, FSE_ARG_INT32, ut->t_lastop_document_id, FSE_ARG_DONE);
#endif
}
}
}
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_bsdflags & APPEND) && !vnode_isdir(vp) &&
(ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
return (EPERM);
if (vnode_isreg(vp) && !UBCINFOEXISTS(vp))
return (EBUSY);
if (hfs_is_journal_file(hfsmp, cp))
return (EPERM);
bool have_lock = false;
#if CONFIG_PROTECT
if (ISSET(ap->a_mode, FENCRYPTED) && cp->c_cpentry && vnode_isreg(vp)) {
bool have_trunc_lock = false;
if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
if (have_trunc_lock)
hfs_unlock_truncate(cp, 0);
return error;
}
have_lock = true;
if (cp->c_cpentry->cp_raw_open_count + 1
< cp->c_cpentry->cp_raw_open_count) {
hfs_unlock(cp);
if (have_trunc_lock)
hfs_unlock_truncate(cp, 0);
return ENFILE;
}
if (have_trunc_lock)
hfs_unlock_truncate(cp, 0);
++cp->c_cpentry->cp_raw_open_count;
}
#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
#if CONFIG_PROTECT
if (have_lock)
hfs_unlock(cp);
#endif
return (0);
}
if (!have_lock && (error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
return (error);
#if QUOTA
if ((ap->a_mode & FWRITE) && (hfsmp->hfs_flags & HFS_QUOTAS))
(void)hfs_getinoquota(cp);
#endif
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);
}
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, HFS_LOCK_DEFAULT) != 0)
return (0);
cp = VTOC(vp);
hfsmp = VTOHFS(vp);
#if CONFIG_PROTECT
if (cp->c_cpentry && ISSET(ap->a_fflag, FENCRYPTED) && vnode_isreg(vp)) {
assert(cp->c_cpentry->cp_raw_open_count > 0);
--cp->c_cpentry->cp_raw_open_count;
}
#endif
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, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
tooktrunclock = 1;
if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
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, ap->a_context);
}
}
}
}
}
if (hfsmp->hfs_freeze_state == HFS_FROZEN
&& hfsmp->hfs_freezing_proc == p && proc_exiting(p)) {
hfs_thaw(hfsmp, p);
}
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, HFS_LOCK_DEFAULT);
}
hfs_unlock(cp);
if (ap->a_fflag & FWASWRITTEN) {
hfs_sync_ejectable(hfsmp);
}
return (0);
}
static bool hfs_should_generate_document_id(hfsmount_t *hfsmp, cnode_t *cp)
{
return (!ISSET(hfsmp->hfs_flags, HFS_READ_ONLY)
&& ISSET(cp->c_bsdflags, UF_TRACKED)
&& cp->c_desc.cd_cnid != kHFSRootFolderID
&& (S_ISDIR(cp->c_mode) || S_ISREG(cp->c_mode) || S_ISLNK(cp->c_mode)));
}
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 ((VATTR_IS_ACTIVE(vap, va_data_size) || VATTR_IS_ACTIVE(vap, va_total_size))) {
if (compressed || (!VNODE_IS_RSRC(vp) && cp->c_decmp && cp->c_decmp->cmp_type >= CMP_MAX)) {
if (0 != hfs_uncompressed_size_of_compressed_file(NULL, vp, 0, &uncompressed_size, 0)) {
uncompressed_size = -1;
} else {
compressed = 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_bsdflags;
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 (VATTR_IS_ACTIVE(vap, va_document_id)) {
uint32_t document_id;
if (cp->c_desc.cd_cnid == kHFSRootFolderID)
document_id = kHFSRootFolderID;
else {
document_id = hfs_get_document_id(cp);
if (!document_id && hfs_should_generate_document_id(hfsmp, cp)) {
uint32_t new_document_id;
error = hfs_generate_document_id(hfsmp, &new_document_id);
if (error)
return error;
error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
if (error)
return error;
bool want_docid_fsevent = false;
document_id = hfs_get_document_id(cp);
if (!document_id && hfs_should_generate_document_id(hfsmp, cp)) {
cp->c_attr.ca_finderextendeddirinfo.document_id = document_id = new_document_id;
want_docid_fsevent = true;
SET(cp->c_flag, C_MODIFIED);
}
hfs_unlock(cp);
if (want_docid_fsevent) {
#if CONFIG_FSE
add_fsevent(FSE_DOCID_CHANGED, ap->a_context,
FSE_ARG_DEV, hfsmp->hfs_raw_dev,
FSE_ARG_INO, (ino64_t)0, FSE_ARG_INO, (ino64_t)cp->c_fileid, FSE_ARG_INT32, document_id,
FSE_ARG_DONE);
if (need_fsevent(FSE_STAT_CHANGED, vp)) {
add_fsevent(FSE_STAT_CHANGED, ap->a_context,
FSE_ARG_VNODE, vp, FSE_ARG_DONE);
}
#endif
}
}
}
vap->va_document_id = document_id;
VATTR_SET_SUPPORTED(vap, va_document_id);
}
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, HFS_LOCK_DEFAULT)))
return (error);
hfs_touchtimes(hfsmp, cp);
cp->c_lockowner = HFS_SHARED_OWNER;
lck_rw_lock_exclusive_to_shared(&cp->c_rwlock);
} else if ((error = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
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);
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);
}
}
vap->va_access_time.tv_sec = cp->c_atime;
vap->va_access_time.tv_nsec = 0;
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;
if (VATTR_IS_ACTIVE(vap, va_addedtime)) {
u_int32_t dateadded = hfs_get_dateadded (cp);
if (dateadded) {
vap->va_addedtime.tv_sec = dateadded;
vap->va_addedtime.tv_nsec = 0;
VATTR_SET_SUPPORTED (vap, va_addedtime);
}
}
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_bsdflags;
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, true);
} 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
#if CONFIG_PROTECT
if (VATTR_IS_ACTIVE(vap, va_dataprotect_class)) {
vap->va_dataprotect_class = cp->c_cpentry ? CP_CLASS(cp->c_cpentry->cp_pclass) : 0;
VATTR_SET_SUPPORTED(vap, va_dataprotect_class);
}
#endif
if (VATTR_IS_ACTIVE(vap, va_write_gencount)) {
if (ubc_is_mapped_writable(vp)) {
vap->va_write_gencount = 0;
} else {
vap->va_write_gencount = hfs_get_gencount(cp);
}
VATTR_SET_SUPPORTED(vap, va_write_gencount);
}
vap->va_supported |= VNODE_ATTR_va_access_time |
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_lookup_siblinglinks(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);
}
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;
time_t orig_ctime;
orig_ctime = VTOC(vp)->c_ctime;
#if HFS_COMPRESSION
int decmpfs_reset_state = 0;
error = decmpfs_update_attributes(vp, vap);
if (error)
return error;
#endif
if (!VATTR_IS_ACTIVE(vap, va_data_size) && !(vap->va_active == VNODE_ATTR_va_access_time)) {
check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_MOD, NSPACE_REARM_NO_ARG);
}
#if CONFIG_PROTECT
if (VATTR_IS_ACTIVE(vap, va_data_size)) {
if ((error = cp_handle_vnop(vp, CP_WRITE_ACCESS, 0)) != 0) {
return (error);
}
}
#endif
hfsmp = VTOHFS(vp);
if (hfs_is_journal_file(hfsmp, VTOC(vp))) {
return (EPERM);
}
u_int32_t document_id=0;
if (VATTR_IS_ACTIVE(vap, va_flags) && (vap->va_flags & UF_TRACKED) && !(VTOC(vp)->c_bsdflags & UF_TRACKED)) {
struct FndrExtendedDirInfo *fip = (struct FndrExtendedDirInfo *)((char *)&(VTOC(vp)->c_attr.ca_finderinfo) + 16);
if (fip->document_id == 0) {
if (hfs_generate_document_id(hfsmp, &document_id) != 0) {
document_id = 0;
}
}
}
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;
}
}
check_for_tracked_file(vp, orig_ctime, vap->va_data_size == 0 ? NAMESPACE_HANDLER_TRUNCATE_OP|NAMESPACE_HANDLER_DELETE_OP : NAMESPACE_HANDLER_TRUNCATE_OP, NULL);
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), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
error = hfs_truncate(vp, vap->va_data_size, vap->va_vaflags & 0xffff,
0, ap->a_context);
hfs_unlock_truncate(VTOC(vp), HFS_LOCK_DEFAULT);
#if HFS_COMPRESSION
decmpfs_unlock_compressed_data(dp, 1);
#endif
if (error)
return error;
}
if (cp == NULL) {
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
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_bsdflags ^ vap->va_flags) & UF_COMPRESSED) {
decmpfs_reset_state = 1;
}
#endif
if ((vap->va_flags & UF_TRACKED) && !(cp->c_bsdflags & UF_TRACKED)) {
struct FndrExtendedDirInfo *fip = (struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16);
if (fip->document_id == 0) {
if (document_id != 0) {
fip->document_id = (uint32_t)document_id;
#if CONFIG_FSE
add_fsevent(FSE_DOCID_CHANGED, ap->a_context,
FSE_ARG_DEV, hfsmp->hfs_raw_dev,
FSE_ARG_INO, (ino64_t)0, FSE_ARG_INO, (ino64_t)cp->c_fileid, FSE_ARG_INT32, document_id,
FSE_ARG_DONE);
#endif
} else {
}
}
} else if (!(vap->va_flags & UF_TRACKED) && (cp->c_bsdflags & UF_TRACKED)) {
struct FndrExtendedDirInfo *fip = (struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16);
if (fip->document_id) {
#if CONFIG_FSE
add_fsevent(FSE_DOCID_CHANGED, ap->a_context,
FSE_ARG_DEV, hfsmp->hfs_raw_dev,
FSE_ARG_INO, (ino64_t)cp->c_fileid, FSE_ARG_INO, (ino64_t)0, FSE_ARG_INT32, fip->document_id, FSE_ARG_DONE);
#endif
fip->document_id = 0;
cp->c_bsdflags &= ~UF_TRACKED;
}
}
cp->c_bsdflags = vap->va_flags;
cp->c_flag |= C_MODIFIED;
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;
hfs_clear_might_be_dirty_flag(cp);
if ((VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) &&
(cp->c_cnid != kHFSRootFolderID) &&
!VATTR_IS_ACTIVE(vap, va_create_time) &&
(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_MINOR_MOD;
}
VATTR_SET_SUPPORTED(vap, va_encoding);
if (VATTR_IS_ACTIVE(vap, va_encoding)) {
cp->c_encoding = vap->va_encoding;
cp->c_flag |= C_MODIFIED;
hfs_setencodingbits(hfsmp, cp->c_encoding);
}
if ((error = hfs_update(vp, 0)) != 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);
}
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 (hfs_is_journal_file(VTOHFS(vp), cp)) {
return EPERM;
}
#if OVERRIDE_UNKNOWN_PERMISSIONS
if (((unsigned int)vfs_flags(VTOVFS(vp))) & MNT_UNKNOWNPERMISSIONS) {
return (0);
};
#endif
mode_t new_mode = (cp->c_mode & ~ALLPERMS) | (mode & ALLPERMS);
if (new_mode != cp->c_mode) {
cp->c_mode = new_mode;
cp->c_flag |= C_MINOR_MOD;
}
cp->c_touch_chgtime = TRUE;
return (0);
}
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_bsdflags & 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);
}
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 (ouid == uid && ogid == gid) {
cp->c_touch_chgtime = TRUE;
return 0;
}
#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_flag |= C_MODIFIED;
cp->c_touch_chgtime = TRUE;
return (0);
}
#if HFS_COMPRESSION
static int hfs_flush_rsrc(vnode_t vp, vfs_context_t ctx)
{
cnode_t *cp = VTOC(vp);
hfs_lock(cp, HFS_SHARED_LOCK, 0);
vnode_t rvp = cp->c_rsrc_vp;
if (!rvp) {
hfs_unlock(cp);
return 0;
}
int vid = vnode_vid(rvp);
hfs_unlock(cp);
int error = vnode_getwithvid(rvp, vid);
if (error)
return error == ENOENT ? 0 : error;
hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, 0);
hfs_lock_always(cp, HFS_EXCLUSIVE_LOCK);
hfs_filedone(rvp, ctx, HFS_FILE_DONE_NO_SYNC);
hfs_unlock(cp);
hfs_unlock_truncate(cp, 0);
error = ubc_msync(rvp, 0, ubc_getsize(rvp), NULL,
UBC_PUSHALL | UBC_SYNC);
vnode_put(rvp);
return error;
}
#endif // HFS_COMPRESSION
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];
uint32_t to_flag_special;
uint32_t from_flag_special;
cnid_t from_parid;
cnid_t to_parid;
int lockflags;
int error = 0, started_tr = 0, got_cookie = 0;
cat_cookie_t cookie;
time_t orig_from_ctime, orig_to_ctime;
bool have_cnode_locks = false, have_from_trunc_lock = false, have_to_trunc_lock = false;
from_cp = VTOC(from_vp);
to_cp = VTOC(to_vp);
hfsmp = VTOHFS(from_vp);
orig_from_ctime = from_cp->c_ctime;
orig_to_ctime = to_cp->c_ctime;
#if CONFIG_PROTECT
if (cp_fs_protected (vnode_mount(from_vp))) {
return EINVAL;
}
#endif
#if HFS_COMPRESSION
if (!ISSET(ap->a_options, FSOPT_EXCHANGE_DATA_ONLY)) {
if ( hfs_file_is_compressed(from_cp, 0) ) {
if ( 0 != ( error = decmpfs_decompress_file(from_vp, VTOCMP(from_vp), -1, 0, 1) ) ) {
return error;
}
}
if ( hfs_file_is_compressed(to_cp, 0) ) {
if ( 0 != ( error = decmpfs_decompress_file(to_vp, VTOCMP(to_vp), -1, 0, 1) ) ) {
return error;
}
}
}
#endif // HFS_COMPRESSION
if (VNODE_IS_RSRC(from_vp) || VNODE_IS_RSRC(to_vp))
return EINVAL;
if ((ap->a_options & FSOPT_EXCHANGE_DATA_ONLY) == 0) {
check_for_tracked_file(from_vp, orig_from_ctime, NAMESPACE_HANDLER_WRITE_OP, NULL);
check_for_tracked_file(to_vp, orig_to_ctime, NAMESPACE_HANDLER_WRITE_OP, NULL);
} else {
hfs_lock_truncate(from_cp, HFS_EXCLUSIVE_LOCK, 0);
hfs_lock_always(from_cp, HFS_EXCLUSIVE_LOCK);
hfs_filedone(from_vp, ap->a_context, HFS_FILE_DONE_NO_SYNC);
hfs_unlock(from_cp);
hfs_unlock_truncate(from_cp, 0);
error = ubc_msync(from_vp, 0, ubc_getsize(from_vp), NULL,
UBC_PUSHALL | UBC_SYNC);
if (error)
goto exit;
#if HFS_COMPRESSION
if (ISSET(from_cp->c_bsdflags, UF_COMPRESSED)) {
error = hfs_flush_rsrc(from_vp, ap->a_context);
if (error)
goto exit;
}
#endif
if (to_cp < from_cp) {
hfs_lock_truncate(to_cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
have_to_trunc_lock = true;
}
hfs_lock_truncate(from_cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
have_from_trunc_lock = true;
if (vnode_isinuse(from_vp, 0)) {
error = EBUSY;
goto exit;
}
if (to_cp >= from_cp) {
hfs_lock_truncate(to_cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
have_to_trunc_lock = true;
}
}
if ((error = hfs_lockpair(from_cp, to_cp, HFS_EXCLUSIVE_LOCK)))
goto exit;
have_cnode_locks = true;
if (hfs_is_journal_file(hfsmp, from_cp) ||
hfs_is_journal_file(hfsmp, to_cp)) {
error = EPERM;
goto exit;
}
if (ISSET(ap->a_options, FSOPT_EXCHANGE_DATA_ONLY)) {
#if HFS_COMPRESSION
if (ISSET(from_cp->c_bsdflags, UF_COMPRESSED)) {
error = hfs_move_compressed(from_cp, to_cp);
goto exit;
}
#endif
error = hfs_move_data(from_cp, to_cp, 0);
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);
{
hfs_incr_gencount (from_cp);
hfs_incr_gencount (to_cp);
}
bcopy(&from_cp->c_desc, &tempdesc, sizeof(struct cat_desc));
bcopy(&from_cp->c_attr, &tempattr, sizeof(struct cat_attr));
from_flag_special = from_cp->c_flag & (C_HARDLINK | C_HASXATTRS);
to_flag_special = to_cp->c_flag & (C_HARDLINK | C_HASXATTRS);
from_cp->c_flag &= ~(C_HARDLINK | C_HASXATTRS);
to_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 = to_cp->c_attr.ca_fileid;
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_bsdflags = to_cp->c_bsdflags;
from_cp->c_mode = to_cp->c_mode;
from_cp->c_linkcount = to_cp->c_linkcount;
from_cp->c_attr.ca_linkref = to_cp->c_attr.ca_linkref;
from_cp->c_attr.ca_firstlink = to_cp->c_attr.ca_firstlink;
from_cp->c_flag |= to_flag_special | C_MODIFIED;
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 = tempattr.ca_fileid;
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_bsdflags = tempattr.ca_flags;
to_cp->c_mode = tempattr.ca_mode;
to_cp->c_linkcount = tempattr.ca_linkcount;
to_cp->c_attr.ca_linkref = tempattr.ca_linkref;
to_cp->c_attr.ca_firstlink = tempattr.ca_firstlink;
to_cp->c_flag |= from_flag_special | C_MODIFIED;
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_bsdflags & UF_NODUMP) &&
(from_cp->c_parentcnid != to_cp->c_parentcnid)) {
from_cp->c_bsdflags &= ~UF_NODUMP;
from_cp->c_touch_chgtime = TRUE;
}
if ((to_cp->c_bsdflags & UF_NODUMP) &&
(to_cp->c_parentcnid != from_cp->c_parentcnid)) {
to_cp->c_bsdflags &= ~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);
}
if (have_cnode_locks)
hfs_unlockpair(from_cp, to_cp);
if (have_from_trunc_lock)
hfs_unlock_truncate(from_cp, 0);
if (have_to_trunc_lock)
hfs_unlock_truncate(to_cp, 0);
return (error);
}
#if HFS_COMPRESSION
static int hfs_move_compressed(cnode_t *from_cp, cnode_t *to_cp)
{
int ret;
void *data = NULL;
CLR(from_cp->c_bsdflags, UF_COMPRESSED);
SET(from_cp->c_flag, C_MODIFIED);
ret = hfs_move_data(from_cp, to_cp, HFS_MOVE_DATA_INCLUDE_RSRC);
if (ret)
goto exit;
size_t size = MAX_DECMPFS_XATTR_SIZE;
MALLOC(data, void *, size, M_TEMP, M_WAITOK);
ret = hfs_xattr_read(from_cp->c_vp, DECMPFS_XATTR_NAME, data, &size);
if (ret)
goto exit;
ret = hfs_xattr_write(to_cp->c_vp, DECMPFS_XATTR_NAME, data, size);
if (ret)
goto exit;
SET(to_cp->c_bsdflags, UF_COMPRESSED);
SET(to_cp->c_flag, C_MODIFIED);
exit:
if (data)
FREE(data, M_TEMP);
return ret;
}
#endif // HFS_COMPRESSION
int
hfs_vnop_mmap(struct vnop_mmap_args *ap)
{
struct vnode *vp = ap->a_vp;
cnode_t *cp = VTOC(vp);
int error;
if (VNODE_IS_RSRC(vp)) {
} else {
int compressed = hfs_file_is_compressed(cp, 1);
time_t orig_ctime = cp->c_ctime;
if (!compressed && (cp->c_bsdflags & UF_COMPRESSED)) {
error = check_for_dataless_file(vp, NAMESPACE_HANDLER_READ_OP);
if (error != 0) {
return error;
}
}
if (ap->a_fflags & PROT_WRITE) {
check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_WRITE_OP, NULL);
}
}
return (ENOTSUP);
}
static errno_t hfs_vnop_mnomap(struct vnop_mnomap_args *ap)
{
vnode_t vp = ap->a_vp;
if (ubc_is_mapped_writable(vp)) {
cnode_t *cp = VTOC(vp);
hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
hfs_incr_gencount(cp);
SET(cp->c_flag, C_MIGHT_BE_DIRTY_FROM_MAPPING);
hfs_unlock(cp);
}
return 0;
}
static void hfs_rsrc_setsize(cnode_t *cp)
{
if (cp->c_rsrc_vp && !vnode_get(cp->c_rsrc_vp)) {
if (ISSET(cp->c_flag, C_NEED_RVNODE_PUT))
vnode_put(cp->c_rsrc_vp);
SET(cp->c_flag, C_NEED_RVNODE_PUT | C_NEED_RSRC_SETSIZE);
}
}
int hfs_move_data(cnode_t *from_cp, cnode_t *to_cp,
hfs_move_data_options_t options)
{
hfsmount_t *hfsmp = VTOHFS(from_cp->c_vp);
int error = 0;
int lockflags = 0;
bool return_EIO_on_error = false;
const bool include_rsrc = ISSET(options, HFS_MOVE_DATA_INCLUDE_RSRC);
if (ISSET(from_cp->c_flag, C_DELETED | C_NOEXISTS)
|| ISSET(to_cp->c_flag, C_DELETED | C_NOEXISTS)) {
return EBUSY;
}
if (vnode_isinuse(from_cp->c_vp, 0))
return EBUSY;
if (include_rsrc && from_cp->c_rsrc_vp) {
if (vnode_isinuse(from_cp->c_rsrc_vp, 0))
return EBUSY;
if (!to_cp->c_rsrcfork
&& (!TAILQ_EMPTY(&from_cp->c_rsrcfork->ff_invalidranges)
|| from_cp->c_rsrcfork->ff_unallocblocks)) {
return EBUSY;
}
}
if (to_cp->c_datafork->ff_blocks
|| to_cp->c_datafork->ff_size
|| (include_rsrc
&& (to_cp->c_blocks
|| (to_cp->c_rsrcfork && to_cp->c_rsrcfork->ff_size)))) {
return EFBIG;
}
if ((error = hfs_start_transaction (hfsmp)))
return error;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS | SFL_ATTRIBUTE,
HFS_EXCLUSIVE_LOCK);
filefork_t rfork_buf, *from_rfork = NULL;
if (include_rsrc) {
from_rfork = from_cp->c_rsrcfork;
if (!from_rfork && hfs_has_rsrc(from_cp)) {
from_rfork = &rfork_buf;
from_rfork->ff_cp = from_cp;
TAILQ_INIT(&from_rfork->ff_invalidranges);
error = cat_idlookup(hfsmp, from_cp->c_fileid, 0, 1, NULL, NULL,
&from_rfork->ff_data);
if (error)
goto exit;
}
}
return_EIO_on_error = true;
bool data_overflow_extents = overflow_extents(from_cp->c_datafork);
if ((error = hfs_move_fork (from_cp->c_datafork, from_cp,
to_cp->c_datafork, to_cp))) {
goto exit;
}
SET(from_cp->c_flag, C_NEED_DATA_SETSIZE);
SET(to_cp->c_flag, C_NEED_DATA_SETSIZE);
if (data_overflow_extents) {
if ((error = MoveData(hfsmp, from_cp->c_cnid, to_cp->c_cnid, 0)))
goto exit;
}
if (from_rfork && overflow_extents(from_rfork)) {
if ((error = MoveData(hfsmp, from_cp->c_cnid, to_cp->c_cnid, 1)))
goto exit;
}
from_cp->c_touch_acctime = TRUE;
from_cp->c_touch_chgtime = TRUE;
from_cp->c_touch_modtime = TRUE;
hfs_touchtimes(hfsmp, from_cp);
to_cp->c_touch_acctime = TRUE;
to_cp->c_touch_chgtime = TRUE;
to_cp->c_touch_modtime = TRUE;
hfs_touchtimes(hfsmp, to_cp);
struct cat_fork dfork_buf;
const struct cat_fork *dfork, *rfork;
dfork = hfs_prepare_fork_for_update(to_cp->c_datafork, NULL,
&dfork_buf, hfsmp->blockSize);
rfork = hfs_prepare_fork_for_update(from_rfork, NULL,
&rfork_buf.ff_data, hfsmp->blockSize);
if ((error = cat_update(hfsmp, &to_cp->c_desc, &to_cp->c_attr,
dfork, rfork))) {
goto exit;
}
CLR(to_cp->c_flag, C_MODIFIED | C_MINOR_MOD);
if (from_rfork) {
uint32_t moving = from_rfork->ff_blocks + from_rfork->ff_unallocblocks;
from_cp->c_blocks -= moving;
to_cp->c_blocks += moving;
filefork_t *to_rfork = to_cp->c_rsrcfork;
if (to_rfork) {
to_rfork->ff_invalidranges = from_rfork->ff_invalidranges;
to_rfork->ff_data = from_rfork->ff_data;
hfs_rsrc_setsize(to_cp);
}
rl_init(&from_rfork->ff_invalidranges);
bzero(&from_rfork->ff_data, sizeof(from_rfork->ff_data));
hfs_rsrc_setsize(from_cp);
}
dfork = hfs_prepare_fork_for_update(from_cp->c_datafork, NULL, &dfork_buf,
hfsmp->blockSize);
rfork = hfs_prepare_fork_for_update(from_rfork, NULL, &rfork_buf.ff_data,
hfsmp->blockSize);
if ((error = cat_update(hfsmp, &from_cp->c_desc, &from_cp->c_attr,
dfork, rfork))) {
goto exit;
}
CLR(from_cp->c_flag, C_MODIFIED | C_MINOR_MOD);
exit:
if (lockflags) {
hfs_systemfile_unlock(hfsmp, lockflags);
hfs_end_transaction(hfsmp);
}
if (error && error != EIO && return_EIO_on_error) {
printf("hfs_move_data: encountered error %d\n", error);
error = EIO;
}
return error;
}
static int hfs_move_fork(filefork_t *srcfork, cnode_t *src_cp,
filefork_t *dstfork, cnode_t *dst_cp)
{
TAILQ_SWAP(&dstfork->ff_invalidranges, &srcfork->ff_invalidranges,
rl_entry, rl_link);
rl_remove_all(&srcfork->ff_invalidranges);
dstfork->ff_data = srcfork->ff_data;
bzero(&srcfork->ff_data, sizeof(srcfork->ff_data));
src_cp->c_blocks -= dstfork->ff_blocks + dstfork->ff_unallocblocks;
dst_cp->c_blocks += dstfork->ff_blocks + dstfork->ff_unallocblocks;
return 0;
}
#include <i386/panic_hooks.h>
struct hfs_fsync_panic_hook {
panic_hook_t hook;
struct cnode *cp;
};
static void hfs_fsync_panic_hook(panic_hook_t *hook_)
{
struct hfs_fsync_panic_hook *hook = (struct hfs_fsync_panic_hook *)hook_;
extern int kdb_log(const char *fmt, ...);
panic_phys_range_t range;
uint64_t phys;
if (panic_phys_range_before(hook->cp, &phys, &range)) {
kdb_log("cp = %p, phys = %p, prev (%p: %p-%p)\n",
hook->cp, phys, range.type, range.phys_start,
range.phys_start + range.len);
} else
kdb_log("cp = %p, phys = %p, prev (!)\n", hook->cp, phys);
panic_dump_mem((void *)(((vm_offset_t)hook->cp - 4096) & ~4095), 12288);
kdb_log("\n");
}
int
hfs_fsync(struct vnode *vp, int waitfor, hfs_fsync_mode_t fsyncmode, struct proc *p)
{
struct cnode *cp = VTOC(vp);
struct filefork *fp = NULL;
int retval = 0;
struct hfsmount *hfsmp = VTOHFS(vp);
struct timeval tv;
int waitdata;
int wait;
int lockflag;
int took_trunc_lock = 0;
int locked_buffers = 0;
int fsync_default = 1;
wait = (waitfor == MNT_WAIT);
waitdata = (waitfor == MNT_DWAIT) | wait;
if (always_do_fullfsync)
fsyncmode = HFS_FSYNC_FULL;
if (fsyncmode != HFS_FSYNC)
fsync_default = 0;
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, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
took_trunc_lock = 1;
struct hfs_fsync_panic_hook hook;
hook.cp = cp;
panic_hook(&hook.hook, hfs_fsync_panic_hook);
if (fp->ff_unallocblocks != 0) {
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
}
panic_unhook(&hook.hook);
(void) cluster_push(vp, waitdata ? IO_SYNC : 0);
hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
}
if (fp && (((cp->c_flag & C_ALWAYS_ZEROFILL) && !TAILQ_EMPTY(&fp->ff_invalidranges)) ||
((wait || (cp->c_flag & C_ZFWANTSYNC)) &&
((cp->c_bsdflags & UF_NODUMP) == 0) &&
UBCINFOEXISTS(vp) && (vnode_issystem(vp) ==0) &&
cp->c_zftimeout != 0))) {
microuptime(&tv);
if ((cp->c_flag & C_ALWAYS_ZEROFILL) == 0 && fsync_default && tv.tv_sec < (long)cp->c_zftimeout) {
cp->c_flag |= C_ZFWANTSYNC;
goto datasync;
}
if (!TAILQ_EMPTY(&fp->ff_invalidranges)) {
if (!took_trunc_lock || (cp->c_truncatelockowner == HFS_SHARED_OWNER)) {
hfs_unlock(cp);
if (took_trunc_lock) {
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
}
hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
took_trunc_lock = 1;
}
hfs_flush_invalid_ranges(vp);
hfs_unlock(cp);
(void) cluster_push(vp, waitdata ? IO_SYNC : 0);
hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
}
}
datasync:
if (took_trunc_lock) {
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
took_trunc_lock = 0;
}
if (hfsmp->jnl)
lockflag = BUF_SKIP_LOCKED;
else
lockflag = 0;
locked_buffers = buf_flushdirtyblks_skipinfo(vp, waitdata, lockflag, "hfs_fsync");
if ((lockflag & BUF_SKIP_LOCKED) && (locked_buffers) && (vnode_vtype(vp) == VLNK)) {
if (vnode_isrecycled(vp)) {
fsync_default = 0;
}
}
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, HFS_UPDATE_FORCE);
if ((retval == 0) && wait && fsync_default && cp->c_hint &&
!ISSET(cp->c_flag, C_DELETED | C_NOEXISTS)) {
hfs_metasync(VTOHFS(vp), (daddr64_t)cp->c_hint, p);
}
if (!fsync_default) {
if (hfsmp->jnl) {
if (fsyncmode == HFS_FSYNC_FULL)
hfs_flush(hfsmp, HFS_FLUSH_FULL);
else
hfs_flush(hfsmp,
HFS_FLUSH_JOURNAL_BARRIER);
} else {
retval = hfs_metasync_all(hfsmp);
hfs_flush(hfsmp, HFS_FLUSH_CACHE);
}
}
}
if (!hfs_is_dirty(cp) && !ISSET(cp->c_flag, C_DELETED))
vnode_cleardirty(vp);
return (retval);
}
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);
}
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);
}
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;
}
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;
time_t orig_ctime;
orig_ctime = VTOC(vp)->c_ctime;
if (!S_ISDIR(cp->c_mode)) {
return (ENOTDIR);
}
if (dvp == vp) {
return (EINVAL);
}
check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_DELETE_OP, NULL);
cp = VTOC(vp);
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;
}
if ((cp->c_bsdflags & UF_TRACKED) && ((struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16))->document_id == 0) {
uint32_t newid;
hfs_unlockpair(dcp, cp);
if (hfs_generate_document_id(VTOHFS(vp), &newid) == 0) {
hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK);
((struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16))->document_id = newid;
#if CONFIG_FSE
add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
FSE_ARG_DEV, VTOHFS(vp)->hfs_raw_dev,
FSE_ARG_INO, (ino64_t)0, FSE_ARG_INO, (ino64_t)cp->c_fileid, FSE_ARG_INT32, newid,
FSE_ARG_DONE);
#endif
} else {
hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK);
}
}
error = hfs_removedir(dvp, vp, ap->a_cnp, 0, 0);
hfs_unlockpair(dcp, cp);
return (error);
}
int
hfs_removedir(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
int skip_reserve, int only_unlink)
{
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 (vnode_isinuse(vp, 0)) {
only_unlink = 1;
}
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)) ||
(only_unlink != 0)) {
int ret = hfs_removefile(dvp, vp, cnp, 0, 0, 1, NULL, only_unlink);
vnode_recycle(vp);
return ret;
}
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_bsdflags & APPEND) || (cp->c_bsdflags & (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, NULL, &error)) {
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) {
struct doc_tombstone *ut;
ut = get_uthread_doc_tombstone();
if (!skip_reserve && (cp->c_bsdflags & UF_TRACKED) && should_save_docid_tombstone(ut, vp, cnp)) {
if (ut->t_lastop_document_id) {
clear_tombstone_docid(ut, hfsmp, NULL);
}
save_tombstone(hfsmp, dvp, vp, cnp, 1);
}
if (dcp->c_entries > 0)
dcp->c_entries--;
DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
dcp->c_dirchangecnt++;
hfs_incr_gencount(dcp);
dcp->c_touch_chgtime = TRUE;
dcp->c_touch_modtime = TRUE;
dcp->c_flag |= C_MODIFIED;
hfs_update(dcp->c_vp, 0);
}
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));
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);
}
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;
struct vnode *rvp = NULL;
int error=0, recycle_rsrc=0;
int recycle_vnode = 0;
uint32_t rsrc_vid = 0;
time_t orig_ctime;
if (dvp == vp) {
return (EINVAL);
}
orig_ctime = VTOC(vp)->c_ctime;
if (!vnode_isnamedstream(vp) && ((ap->a_flags & VNODE_REMOVE_SKIP_NAMESPACE_EVENT) == 0)) {
error = check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_DELETE_OP, NULL);
if (error) {
}
}
error = 0;
cp = VTOC(vp);
relock:
hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
if ((error = hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK))) {
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
if (rvp) {
vnode_put (rvp);
}
return (error);
}
if ((cp->c_bsdflags & UF_TRACKED) && ((struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16))->document_id == 0) {
uint32_t newid;
hfs_unlockpair(dcp, cp);
if (hfs_generate_document_id(VTOHFS(vp), &newid) == 0) {
hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK);
((struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16))->document_id = newid;
#if CONFIG_FSE
add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
FSE_ARG_DEV, VTOHFS(vp)->hfs_raw_dev,
FSE_ARG_INO, (ino64_t)0, FSE_ARG_INO, (ino64_t)cp->c_fileid, FSE_ARG_INT32, newid,
FSE_ARG_DONE);
#endif
} else {
hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK);
}
}
if ((vp->v_type == VLNK) || (vp->v_type == VREG)) {
if ((cp->c_rsrc_vp) && (rvp == NULL)) {
rvp = cp->c_rsrc_vp;
rsrc_vid = vnode_vid (rvp);
hfs_unlock_truncate (cp, HFS_LOCK_DEFAULT);
hfs_unlockpair (dcp, cp);
if (vnode_getwithvid(rvp, rsrc_vid)) {
rvp = NULL;
rsrc_vid = 0;
}
goto relock;
}
}
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, NULL, 0);
if (error == 0) {
hfs_hotfile_deleted(vp);
if (rvp) {
recycle_rsrc = 1;
}
if (cp->c_flag & C_NOEXISTS) {
recycle_vnode = 1;
}
}
rm_done:
hfs_unlockpair(dcp, cp);
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
if (recycle_rsrc) {
vnode_recycle(rvp);
}
if (recycle_vnode) {
vnode_recycle (vp);
}
if (rvp) {
vnode_put(rvp);
}
return (error);
}
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);
}
int
hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
int flags, int skip_reserve, int allow_dirs,
__unused struct vnode *rvp, int only_unlink)
{
struct cnode *cp;
struct cnode *dcp;
struct vnode *rsrc_vp = NULL;
struct hfsmount *hfsmp;
struct cat_desc desc;
struct timeval tv;
int dataforkbusy = 0;
int rsrcforkbusy = 0;
int lockflags;
int error = 0;
int started_tr = 0;
int isbigfile = 0, defer_remove=0, isdir=0;
int update_vh = 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, NULL, &error)) {
return 0;
}
if (VNODE_IS_RSRC(vp)) {
return (EPERM);
}
else {
rsrc_vp = cp->c_rsrc_vp;
}
if (hfs_is_journal_file(hfsmp, cp)) {
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 (rsrc_vp && (cp->c_blocks - VTOF(vp)->ff_blocks)) {
rsrcforkbusy = vnode_isinuse(rsrc_vp, 0);
}
}
if (isdir == 0)
isbigfile = cp->c_datafork->ff_size >= HFS_BIGFILE_SIZE;
if ((hfsmp->hfs_attribute_vp != NULL) &&
(cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0) {
defer_remove = 1;
}
if (only_unlink) {
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) && (defer_remove == 0)) {
if (!dataforkbusy && cp->c_datafork->ff_blocks && !isbigfile) {
cp->c_flag |= C_NEED_DATA_SETSIZE;
}
if (!rsrcforkbusy && rsrc_vp) {
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) && (defer_remove == 0)) {
buf_iterate(vp, hfs_removefile_callback, BUF_SKIP_NONLOCKED, (void *)hfsmp);
}
if (isdir == 0 && (!dataforkbusy && !rsrcforkbusy) && (only_unlink == 0)) {
if (!dataforkbusy && !isbigfile && cp->c_datafork->ff_blocks != 0) {
error = hfs_prepare_release_storage (hfsmp, vp);
if (error) {
goto out;
}
update_vh = 1;
}
if (!rsrcforkbusy && rsrc_vp) {
error = hfs_prepare_release_storage (hfsmp, rsrc_vp);
if (error) {
goto out;
}
update_vh = 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++;
hfs_incr_gencount(dcp);
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 {
struct filefork *temp_rsrc_fork = NULL;
#if QUOTA
off_t savedbytes;
int blksize = hfsmp->blockSize;
#endif
u_int32_t fileid = cp->c_fileid;
if ((isdir == 0) && (cp->c_rsrcfork == NULL) &&
(cp->c_blocks - VTOF(vp)->ff_blocks)) {
MALLOC_ZONE (temp_rsrc_fork, struct filefork *, sizeof (struct filefork), M_HFSFORK, M_WAITOK);
bzero(temp_rsrc_fork, sizeof(struct filefork));
temp_rsrc_fork->ff_cp = cp;
rl_init(&temp_rsrc_fork->ff_invalidranges);
}
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
if (temp_rsrc_fork) {
error = cat_lookup (hfsmp, &desc, 1, 0, (struct cat_desc*) NULL,
(struct cat_attr*) NULL, &temp_rsrc_fork->ff_data, NULL);
if (error) {
FREE_ZONE (temp_rsrc_fork, sizeof(struct filefork), M_HFSFORK);
hfs_systemfile_unlock (hfsmp, lockflags);
goto out;
}
}
if (!skip_reserve) {
if ((error = cat_preflight(hfsmp, CAT_DELETE, NULL, 0))) {
if (temp_rsrc_fork) {
FREE_ZONE (temp_rsrc_fork, sizeof(struct filefork), M_HFSFORK);
}
hfs_systemfile_unlock(hfsmp, lockflags);
goto out;
}
}
error = cat_delete(hfsmp, &desc, &cp->c_attr);
if (error && error != ENXIO && error != ENOENT) {
printf("hfs_removefile: deleting file %s (id=%d) vol=%s err=%d\n",
cp->c_desc.cd_nameptr, cp->c_attr.ca_fileid, hfsmp->vcbVN, error);
}
if (error == 0) {
if (dcp->c_entries > 0)
dcp->c_entries--;
dcp->c_dirchangecnt++;
hfs_incr_gencount(dcp);
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) {
if (temp_rsrc_fork) {
FREE_ZONE (temp_rsrc_fork, sizeof(struct filefork), M_HFSFORK);
}
goto out;
}
#if QUOTA
if (cp->c_datafork->ff_blocks > 0) {
savedbytes = ((off_t)cp->c_datafork->ff_blocks * (off_t)blksize);
(void) hfs_chkdq(cp, (int64_t)-(savedbytes), NOCRED, 0);
}
if (temp_rsrc_fork || cp->c_rsrcfork) {
if (cp->c_rsrcfork) {
if (cp->c_rsrcfork->ff_blocks > 0) {
savedbytes = ((off_t)cp->c_rsrcfork->ff_blocks * (off_t)blksize);
(void) hfs_chkdq(cp, (int64_t)-(savedbytes), NOCRED, 0);
}
}
else {
savedbytes = ((off_t)temp_rsrc_fork->ff_blocks * (off_t)blksize);
(void) hfs_chkdq(cp, (int64_t)-(savedbytes), NOCRED, 0);
}
}
if (hfsmp->hfs_flags & HFS_QUOTAS) {
(void)hfs_chkiq(cp, -1, NOCRED, 0);
}
#endif
if (temp_rsrc_fork) {
error = hfs_release_storage (hfsmp, cp->c_datafork, temp_rsrc_fork, fileid);
}
else {
error = hfs_release_storage (hfsmp, cp->c_datafork, cp->c_rsrcfork, fileid);
}
if (error) {
hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
}
else {
update_vh = 0;
}
if (temp_rsrc_fork) {
FREE_ZONE (temp_rsrc_fork, sizeof(struct filefork), M_HFSFORK);
}
cp->c_flag |= C_NOEXISTS;
cp->c_flag &= ~C_DELETED;
cp->c_touch_chgtime = TRUE;
--cp->c_linkcount;
hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
}
struct doc_tombstone *ut;
ut = get_uthread_doc_tombstone();
if (!error && !skip_reserve && (cp->c_bsdflags & UF_TRACKED) && should_save_docid_tombstone(ut, vp, cnp)) {
if (ut->t_lastop_document_id) {
clear_tombstone_docid(ut, hfsmp, NULL);
}
save_tombstone(hfsmp, dvp, vp, cnp, 1);
}
cat_releasedesc(&cp->c_desc);
out:
if (error) {
cp->c_flag &= ~C_DELETED;
}
if (update_vh) {
hfs_volupdate (hfsmp, VOL_UPDATE, 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;
}
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 *tvp_rsrc = NULLVP;
uint32_t tvp_rsrc_vid = 0;
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;
time_t orig_from_ctime, orig_to_ctime;
int emit_rename = 1;
int emit_delete = 1;
int is_tracked = 0;
int unlocked;
orig_from_ctime = VTOC(fvp)->c_ctime;
if (tvp && VTOC(tvp)) {
orig_to_ctime = VTOC(tvp)->c_ctime;
} else {
orig_to_ctime = ~0;
}
hfsmp = VTOHFS(tdvp);
if (fvp == tvp) {
int is_hardlink = 0;
emit_delete = 0;
if ((error = hfs_lock(VTOC(fvp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
return error;
}
is_hardlink = (VTOC(fvp)->c_flag & C_HARDLINK);
hfs_unlock (VTOC(fvp));
if (is_hardlink) {
emit_rename = 0;
if ((fdvp == tdvp) && ((hfsmp->hfs_flags & HFS_CASE_SENSITIVE) == 0)) {
if (hfs_namecmp((const u_int8_t *)fcnp->cn_nameptr, fcnp->cn_namelen,
(const u_int8_t *)tcnp->cn_nameptr, tcnp->cn_namelen) == 0) {
emit_rename = 1;
}
}
}
}
if (emit_rename) {
if (VTOC(fvp)->c_bsdflags & UF_TRACKED) {
is_tracked = 1;
}
check_for_tracked_file(fvp, orig_from_ctime, NAMESPACE_HANDLER_RENAME_OP, NULL);
}
if (tvp && VTOC(tvp)) {
if (emit_delete) {
check_for_tracked_file(tvp, orig_to_ctime, NAMESPACE_HANDLER_DELETE_OP, NULL);
}
}
retry:
if (tvp && (vnode_isreg(tvp) || vnode_islnk(tvp))) {
hfs_lock_truncate(VTOC(tvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
took_trunc_lock = 1;
}
relock:
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), HFS_LOCK_DEFAULT);
took_trunc_lock = 0;
}
if (tvp_rsrc) {
vnode_put (tvp_rsrc);
tvp_rsrc_vid = 0;
tvp_rsrc = NULL;
}
if ((error == ENOENT) && (tvp != NULL) && (error_cnode == VTOC(tvp))) {
tcp = NULL;
tvp = NULL;
goto retry;
}
return (error);
}
fdcp = VTOC(fdvp);
fcp = VTOC(fvp);
tdcp = VTOC(tdvp);
tcp = tvp ? VTOC(tvp) : NULL;
unlocked = 0;
if ((fcp->c_bsdflags & UF_TRACKED) && ((struct FndrExtendedDirInfo *)((char *)&fcp->c_attr.ca_finderinfo + 16))->document_id == 0) {
uint32_t newid;
hfs_unlockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL);
unlocked = 1;
if (hfs_generate_document_id(hfsmp, &newid) == 0) {
hfs_lock(fcp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
((struct FndrExtendedDirInfo *)((char *)&fcp->c_attr.ca_finderinfo + 16))->document_id = newid;
#if CONFIG_FSE
add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
FSE_ARG_DEV, hfsmp->hfs_raw_dev,
FSE_ARG_INO, (ino64_t)0, FSE_ARG_INO, (ino64_t)fcp->c_fileid, FSE_ARG_INT32, newid,
FSE_ARG_DONE);
#endif
hfs_unlock(fcp);
} else {
}
if (tcp == NULL || !(tcp->c_bsdflags & UF_TRACKED) || ((struct FndrExtendedDirInfo *)((char *)&tcp->c_attr.ca_finderinfo + 16))->document_id != 0) {
goto relock;
}
}
if (tcp && (tcp->c_bsdflags & UF_TRACKED) && ((struct FndrExtendedDirInfo *)((char *)&tcp->c_attr.ca_finderinfo + 16))->document_id == 0) {
uint32_t newid;
if (!unlocked) {
hfs_unlockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL);
unlocked = 1;
}
if (hfs_generate_document_id(hfsmp, &newid) == 0) {
hfs_lock(tcp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
((struct FndrExtendedDirInfo *)((char *)&tcp->c_attr.ca_finderinfo + 16))->document_id = newid;
#if CONFIG_FSE
add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
FSE_ARG_DEV, hfsmp->hfs_raw_dev,
FSE_ARG_INO, (ino64_t)0, FSE_ARG_INO, (ino64_t)tcp->c_fileid, FSE_ARG_INT32, newid,
FSE_ARG_DONE);
#endif
hfs_unlock(tcp);
} else {
}
goto relock;
}
if ((vnode_isreg(fvp)) || (vnode_islnk(fvp))) {
if ((tvp) && (tcp->c_rsrc_vp) && (tvp_rsrc == NULL)) {
tvp_rsrc = tcp->c_rsrc_vp;
tvp_rsrc_vid = vnode_vid (tvp_rsrc);
if (took_trunc_lock) {
hfs_unlock_truncate (VTOC(tvp), HFS_LOCK_DEFAULT);
took_trunc_lock = 0;
}
hfs_unlockfour(fdcp, fcp, tdcp, tcp);
if (vnode_getwithvid (tvp_rsrc, tvp_rsrc_vid)) {
tvp_rsrc_vid = 0;
tvp_rsrc = NULL;
}
goto retry;
}
}
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, NULL, &error)) {
error = ENOENT;
goto out;
}
if (tcp && ((tcp->c_flag & (C_NOEXISTS | C_DELETED)) || !hfs_valid_cnode(hfsmp, tdvp, tcnp, tcp->c_fileid, NULL, &error))) {
if (took_trunc_lock) {
hfs_unlock_truncate(VTOC(tvp), HFS_LOCK_DEFAULT);
took_trunc_lock = 0;
}
error = 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_bsdflags & (IMMUTABLE | APPEND)) || (fdcp->c_bsdflags & 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 (hfs_is_journal_file(hfsmp, fcp) ||
(tcp && hfs_is_journal_file(hfsmp, tcp))) {
error = EPERM;
goto out;
}
#if QUOTA
if (tvp)
(void)hfs_getinoquota(tcp);
#endif
cache_purge(fvp);
#if CONFIG_SECLUDED_RENAME
if (fcnp->cn_flags & CN_SECLUDE_RENAME) {
if (vnode_isdir(fvp)) {
error = EISDIR;
goto out;
}
if (fcp->c_linkcount != 1) {
error = EMLINK;
goto out;
}
if (fcp->c_rsrc_vp && (fcp->c_rsrc_vp->v_usecount > 0 ||
fcp->c_rsrc_vp->v_iocount > 0)) {
error = EBUSY;
goto out;
}
if (fcp->c_vp && (fcp->c_vp->v_usecount > (fcp->c_rsrc_vp ? 1 : 0) ||
fcp->c_vp->v_iocount > 1)) {
error = EBUSY;
goto out;
}
}
#endif
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, 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) {
uint32_t document_id;
struct FndrExtendedDirInfo *ffip = (struct FndrExtendedDirInfo *)((char *)&fcp->c_attr.ca_finderinfo + 16);
struct FndrExtendedDirInfo *tfip = (struct FndrExtendedDirInfo *)((char *)&tcp->c_attr.ca_finderinfo + 16);
if (ffip->document_id && tfip->document_id) {
save_tombstone(hfsmp, tdvp, tvp, tcnp, 0);
} else {
struct doc_tombstone *ut;
ut = get_uthread_doc_tombstone();
document_id = tfip->document_id;
tfip->document_id = 0;
if (document_id != 0) {
tcp->c_bsdflags &= ~UF_TRACKED;
(void) cat_update(hfsmp, &tcp->c_desc, &tcp->c_attr, NULL, NULL);
}
if (ffip->document_id == 0 && document_id != 0) {
fcp->c_bsdflags |= UF_TRACKED;
ffip->document_id = document_id;
(void) cat_update(hfsmp, &fcp->c_desc, &fcp->c_attr, NULL, NULL);
#if CONFIG_FSE
add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
FSE_ARG_DEV, hfsmp->hfs_raw_dev,
FSE_ARG_INO, (ino64_t)tcp->c_fileid, FSE_ARG_INO, (ino64_t)fcp->c_fileid, FSE_ARG_INT32, (uint32_t)ffip->document_id,
FSE_ARG_DONE);
#endif
} else if ((fcp->c_bsdflags & UF_TRACKED) && should_save_docid_tombstone(ut, fvp, fcnp)) {
if (ut->t_lastop_document_id) {
clear_tombstone_docid(ut, hfsmp, NULL);
}
save_tombstone(hfsmp, fdvp, fvp, fcnp, 0);
}
}
}
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, 1);
}
else {
error = hfs_removefile(tdvp, tvp, tcnp, 0, HFSRM_SKIP_RESERVE, 0, NULL, 1);
if ((error == 0) && (tcp->c_flag & C_DELETED) && (tvp_rsrc)) {
vnode_recycle(tvp_rsrc);
}
}
if (error) {
goto out;
}
tvp_deleted = 1;
tcp->c_flag |= C_RENAMED;
vnode_recycle(tvp);
} else {
struct doc_tombstone *ut;
ut = get_uthread_doc_tombstone();
if (fcp->c_bsdflags & UF_TRACKED) {
if (should_save_docid_tombstone(ut, fvp, fcnp)) {
save_tombstone(hfsmp, fdvp, fvp, fcnp, 0);
} else {
}
} else if ( ut->t_lastop_document_id != 0
&& tdvp == ut->t_lastop_parent
&& vnode_vid(tdvp) == ut->t_lastop_parent_vid
&& strcmp((char *)ut->t_lastop_filename, (char *)tcnp->cn_nameptr) == 0) {
struct FndrExtendedFileInfo *fip = (struct FndrExtendedFileInfo *)((char *)&fcp->c_attr.ca_finderinfo + 16);
fcp->c_bsdflags |= UF_TRACKED;
fip->document_id = ut->t_lastop_document_id;
cat_update(hfsmp, &fcp->c_desc, &fcp->c_attr, NULL, NULL);
clear_tombstone_docid(ut, hfsmp, fcp);
} else if (ut->t_lastop_document_id && should_save_docid_tombstone(ut, fvp, fcnp) && should_save_docid_tombstone(ut, tvp, tcnp)) {
clear_tombstone_docid(ut, hfsmp, NULL);
}
}
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;
fcp->c_flag |= C_NEEDS_DATEADDED;
(void) hfs_update (fvp, 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++;
tdcp->c_flag |= C_MODIFIED;
hfs_incr_gencount(tdcp);
if (fdcp->c_entries > 0)
fdcp->c_entries--;
fdcp->c_dirchangecnt++;
fdcp->c_flag |= C_MODIFIED;
fdcp->c_touch_chgtime = TRUE;
fdcp->c_touch_modtime = TRUE;
if (ISSET(fcp->c_flag, C_HARDLINK)) {
hfs_relorigin(fcp, fdcp->c_fileid);
if (fdcp->c_fileid != fdcp->c_cnid)
hfs_relorigin(fcp, fdcp->c_cnid);
}
(void) hfs_update(fdvp, 0);
}
hfs_incr_gencount(fdcp);
tdcp->c_childhint = out_desc.cd_hint;
tdcp->c_touch_chgtime = TRUE;
tdcp->c_touch_modtime = TRUE;
(void) hfs_update(tdvp, 0);
vnode_update_identity(fvp, tdvp, tcnp->cn_nameptr, tcnp->cn_namelen,
tcnp->cn_hash, (VNODE_UPDATE_PARENT | VNODE_UPDATE_NAME));
if (fcp->c_rsrc_vp) {
char* rsrc_path = NULL;
int len;
MALLOC_ZONE (rsrc_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
len = snprintf (rsrc_path, MAXPATHLEN, "%s%s", tcnp->cn_nameptr, _PATH_RSRCFORKSPEC);
len = MIN(len, MAXPATHLEN);
vnode_update_identity (fcp->c_rsrc_vp, fvp, rsrc_path, len, 0, (VNODE_UPDATE_NAME | VNODE_UPDATE_CACHE));
FREE_ZONE (rsrc_path, MAXPATHLEN, M_NAMEI);
}
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);
}
hfs_unlockfour(fdcp, fcp, tdcp, tcp);
if (took_trunc_lock) {
hfs_unlock_truncate(VTOC(tvp), HFS_LOCK_DEFAULT);
}
if (tvp_rsrc) {
vnode_put(tvp_rsrc);
tvp_rsrc = NULL;
}
if (error && tvp_deleted)
error = EIO;
return (error);
}
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);
}
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, HFS_LOCK_DEFAULT))) {
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, 0, ap->a_context);
if (error) {
hfs_end_transaction(hfsmp);
hfs_unlock(cp);
hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
if (hfs_start_transaction(hfsmp) != 0) {
started_tr = 0;
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
goto out;
}
(void) hfs_removefile(dvp, vp, ap->a_cnp, 0, 0, 0, NULL, 0);
hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
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);
}
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;
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;
int bump_valence = 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 (VTOC(vp)->c_bsdflags & UF_COMPRESSED) {
int compressed = hfs_file_is_compressed(VTOC(vp), 0);
if (VTOCMP(vp) != NULL && !compressed) {
error = check_for_dataless_file(vp, NAMESPACE_HANDLER_READ_OP);
if (error) {
return error;
}
}
}
cp = VTOC(vp);
hfsmp = VTOHFS(vp);
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
return (error);
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 (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;
}
else {
if (dirhint->dh_desc.cd_flags & CD_EOF) {
error = 0;
eofflag = 1;
uio_setoffset(uio, startoffset);
hfs_systemfile_unlock (hfsmp, lockflags);
goto seekoffcalc;
}
}
error = cat_getdirentries(hfsmp, cp->c_entries, dirhint, uio, ap->a_flags, &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;
}
if ((cp->c_entries == 0) && (items > 0)) {
cp->c_entries++;
cp->c_flag |= C_MODIFIED;
printf("hfs_vnop_readdir: repairing valence to non-zero! \n");
bump_valence++;
}
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 (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);
}
if (bump_valence) {
hfs_update(vp, 0);
}
hfs_unlock(cp);
return (error);
}
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, HFS_LOCK_DEFAULT)))
return (error);
cp = VTOC(vp);
fp = VTOF(vp);
if (fp->ff_size == 0 || fp->ff_size > MAXPATHLEN) {
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);
}
int
hfs_vnop_pathconf(ap)
struct vnop_pathconf_args *ap;
{
int std_hfs = (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD);
switch (ap->a_name) {
case _PC_LINK_MAX:
if (std_hfs == 0){
*ap->a_retval = HFS_LINK_MAX;
}
#if CONFIG_HFS_STD
else {
*ap->a_retval = 1;
}
#endif
break;
case _PC_NAME_MAX:
if (std_hfs == 0) {
*ap->a_retval = kHFSPlusMaxFileNameChars;
}
#if CONFIG_HFS_STD
else {
*ap->a_retval = kHFSMaxFileNameChars;
}
#endif
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:
if (std_hfs == 0) {
*ap->a_retval = kHFSPlusMaxFileNameChars;
}
#if CONFIG_HFS_STD
else {
*ap->a_retval = kHFSMaxFileNameChars;
}
#endif
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 (std_hfs == 0) {
*ap->a_retval = 64;
}
#if CONFIG_HFS_STD
else {
*ap->a_retval = 32;
}
#endif
break;
case _PC_XATTR_SIZE_BITS:
*ap->a_retval = HFS_XATTR_SIZE_BITS;
break;
default:
return (EINVAL);
}
return (0);
}
const struct cat_fork *
hfs_prepare_fork_for_update(filefork_t *ff,
const struct cat_fork *cf,
struct cat_fork *cf_buf,
uint32_t block_size)
{
if (!ff)
return NULL;
if (!cf)
cf = &ff->ff_data;
if (!cf_buf)
cf_buf = &ff->ff_data;
off_t max_size = ff->ff_size;
if (!TAILQ_EMPTY(&ff->ff_invalidranges))
max_size = TAILQ_FIRST(&ff->ff_invalidranges)->rl_start;
if (!ff->ff_unallocblocks && ff->ff_size <= max_size)
return cf;
if (ff->ff_blocks < ff->ff_unallocblocks) {
panic("hfs: ff_blocks %d is less than unalloc blocks %d\n",
ff->ff_blocks, ff->ff_unallocblocks);
}
struct cat_fork *out = cf_buf;
if (out != cf)
bcopy(cf, out, sizeof(*cf));
out->cf_blocks -= out->cf_vblocks;
off_t alloc_bytes = hfs_blk_to_bytes(out->cf_blocks, block_size);
if (out->cf_size > alloc_bytes)
out->cf_size = alloc_bytes;
if (out->cf_size > max_size)
out->cf_size = max_size;
return out;
}
int
hfs_update(struct vnode *vp, int options)
{
struct cnode *cp = VTOC(vp);
struct proc *p;
const struct cat_fork *dataforkp = NULL;
const struct cat_fork *rsrcforkp = NULL;
struct cat_fork datafork;
struct cat_fork rsrcfork;
struct hfsmount *hfsmp;
int lockflags;
int error;
uint32_t tstate = 0;
if (ISSET(cp->c_flag, C_NOEXISTS))
return 0;
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)) {
CLR(cp->c_flag, C_MODIFIED | C_MINOR_MOD | C_NEEDS_DATEADDED);
cp->c_touch_acctime = 0;
cp->c_touch_chgtime = 0;
cp->c_touch_modtime = 0;
return (0);
}
if (kdebug_enable) {
if (cp->c_touch_acctime || cp->c_atime != cp->c_attr.ca_atimeondisk)
tstate |= DBG_HFS_UPDATE_ACCTIME;
if (cp->c_touch_modtime)
tstate |= DBG_HFS_UPDATE_MODTIME;
if (cp->c_touch_chgtime)
tstate |= DBG_HFS_UPDATE_CHGTIME;
if (cp->c_flag & C_MODIFIED)
tstate |= DBG_HFS_UPDATE_MODIFIED;
if (ISSET(options, HFS_UPDATE_FORCE))
tstate |= DBG_HFS_UPDATE_FORCE;
if (cp->c_flag & C_NEEDS_DATEADDED)
tstate |= DBG_HFS_UPDATE_DATEADDED;
if (cp->c_flag & C_MINOR_MOD)
tstate |= DBG_HFS_UPDATE_MINOR;
}
hfs_touchtimes(hfsmp, cp);
if (!ISSET(cp->c_flag, C_MODIFIED | C_MINOR_MOD)
&& !hfs_should_save_atime(cp)) {
return 0;
}
KDBG(HFSDBG_UPDATE | DBG_FUNC_START, VM_KERNEL_ADDRPERM(vp), tstate);
bool check_txn = false;
if (!ISSET(options, HFS_UPDATE_FORCE) && !ISSET(cp->c_flag, C_MODIFIED)) {
if (hfsmp->jnl
&& journal_current_txn(hfsmp->jnl) == cp->c_update_txn) {
check_txn = true;
} else {
tstate |= DBG_HFS_UPDATE_SKIPPED;
error = 0;
goto exit;
}
}
if ((error = hfs_start_transaction(hfsmp)) != 0)
goto exit;
if (check_txn
&& journal_current_txn(hfsmp->jnl) != cp->c_update_txn) {
hfs_end_transaction(hfsmp);
tstate |= DBG_HFS_UPDATE_SKIPPED;
error = 0;
goto exit;
}
if (cp->c_datafork)
dataforkp = &cp->c_datafork->ff_data;
if (cp->c_rsrcfork)
rsrcforkp = &cp->c_rsrcfork->ff_data;
dataforkp = hfs_prepare_fork_for_update(cp->c_datafork, NULL, &datafork, hfsmp->blockSize);
rsrcforkp = hfs_prepare_fork_for_update(cp->c_rsrcfork, NULL, &rsrcfork, hfsmp->blockSize);
if (__improbable(kdebug_enable & KDEBUG_TRACE)) {
long dbg_parms[NUMPARMS];
int dbg_namelen;
dbg_namelen = NUMPARMS * sizeof(long);
vn_getpath(vp, (char *)dbg_parms, &dbg_namelen);
if (dbg_namelen < (int)sizeof(dbg_parms))
memset((char *)dbg_parms + dbg_namelen, 0, sizeof(dbg_parms) - dbg_namelen);
kdebug_lookup_gen_events(dbg_parms, dbg_namelen, (void *)vp, TRUE);
}
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
error = cat_update(hfsmp, &cp->c_desc, &cp->c_attr, dataforkp, rsrcforkp);
if (hfsmp->jnl)
cp->c_update_txn = journal_current_txn(hfsmp->jnl);
hfs_systemfile_unlock(hfsmp, lockflags);
CLR(cp->c_flag, C_MODIFIED | C_MINOR_MOD);
hfs_end_transaction(hfsmp);
exit:
KDBG(HFSDBG_UPDATE | DBG_FUNC_END, VM_KERNEL_ADDRPERM(vp), tstate, error);
return error;
}
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;
int newvnode_flags = 0;
u_int32_t gnv_flags = 0;
int protectable_target = 0;
int nocache = 0;
#if CONFIG_PROTECT
struct cprotect *entry = NULL;
int32_t cp_class = -1;
uint32_t keywrap_flags = CP_KEYWRAP_DIFFCLASS;
if (VATTR_IS_ACTIVE(vap, va_dataprotect_class)) {
cp_class = (int32_t)vap->va_dataprotect_class;
keywrap_flags &= ~CP_KEYWRAP_DIFFCLASS;
}
int protected_mount = 0;
#endif
if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
return (error);
dcp = VTOC(dvp);
if ((error = hfs_checkdeleted(dcp))) {
hfs_unlock(dcp);
return error;
}
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 (S_ISDIR (mode) || S_ISREG (mode)) {
protectable_target = 1;
}
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 CONFIG_HFS_STD
if ((hfsmp->hfs_flags & HFS_STANDARD) && gTimeZone.tz_dsttime) {
attr.ca_itime += 3600;
}
#endif
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;
}
}
#if CONFIG_PROTECT
if (cp_fs_protected(hfsmp->hfs_mp)) {
protected_mount = 1;
}
if ((protected_mount) && (protectable_target)) {
attr.ca_recflags |= kHFSHasAttributesMask;
nocache = 1;
}
#endif
hfs_write_dateadded (&attr, attr.ca_atime);
hfs_write_gencount(&attr, (uint32_t)1);
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);
}
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 CONFIG_PROTECT
if ((protected_mount) && (protectable_target)) {
error = cp_setup_newentry(hfsmp, dcp, cp_class, attr.ca_mode, &entry);
if (error) {
goto exit;
}
}
#endif
if ((error = hfs_start_transaction(hfsmp)) != 0) {
goto exit;
}
started_tr = 1;
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
cnid_t new_id;
if ((error = cat_preflight(hfsmp, CAT_CREATE, NULL, 0))) {
hfs_systemfile_unlock(hfsmp, lockflags);
goto exit;
}
if ((error = cat_acquire_cnid(hfsmp, &new_id))) {
hfs_systemfile_unlock (hfsmp, lockflags);
goto exit;
}
error = cat_create(hfsmp, new_id, &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++;
hfs_incr_gencount(dcp);
dcp->c_touch_chgtime = dcp->c_touch_modtime = true;
dcp->c_flag |= C_MODIFIED;
hfs_update(dcp->c_vp, 0);
#if CONFIG_PROTECT
if ((attr.ca_fileid != 0) && (protected_mount) && (protectable_target)) {
error = cp_setxattr (NULL, entry, hfsmp, attr.ca_fileid, XATTR_CREATE);
if (error) {
int delete_err;
delete_err = cat_delete (hfsmp, &out_desc, &attr);
if (delete_err == 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);
}
error = EINVAL;
}
}
#endif
}
hfs_systemfile_unlock(hfsmp, lockflags);
if (error)
goto exit;
uint32_t txn = hfsmp->jnl ? journal_current_txn(hfsmp->jnl) : 0;
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 CONFIG_PROTECT
if ((attr.ca_fileid != 0) && (protected_mount) && (protectable_target)) {
cp_entry_destroy (hfsmp, entry);
entry = NULL;
}
#endif
gnv_flags |= GNV_CREATE;
if (nocache) {
gnv_flags |= GNV_NOCACHE;
}
error = hfs_getnewvnode(hfsmp, dvp, cnp, &out_desc, gnv_flags, &attr,
NULL, &tvp, &newvnode_flags);
if (error)
goto exit;
cp = VTOC(tvp);
cp->c_update_txn = txn;
struct doc_tombstone *ut;
ut = get_uthread_doc_tombstone();
if ( ut->t_lastop_document_id != 0
&& ut->t_lastop_parent == dvp
&& ut->t_lastop_parent_vid == vnode_vid(dvp)
&& strcmp((char *)ut->t_lastop_filename, (const char *)cp->c_desc.cd_nameptr) == 0) {
struct FndrExtendedDirInfo *fip = (struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16);
fip->document_id = (uint32_t)(ut->t_lastop_document_id & 0xffffffff);
cp->c_bsdflags |= UF_TRACKED;
cp->c_flag |= C_MODIFIED;
if ((error = hfs_start_transaction(hfsmp)) == 0) {
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
(void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
hfs_systemfile_unlock (hfsmp, lockflags);
(void) hfs_end_transaction(hfsmp);
}
clear_tombstone_docid(ut, hfsmp, cp); } else if (ut->t_lastop_document_id != 0) {
int len = cnp->cn_namelen;
if (len == 0) {
len = strlen(cnp->cn_nameptr);
}
if (is_ignorable_temp_name(cnp->cn_nameptr, cnp->cn_namelen)) {
} else {
clear_tombstone_docid(ut, hfsmp, NULL);
}
}
if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && (vnode_isfastdevicecandidate(dvp) && !vnode_isautocandidate(dvp))) {
cp->c_attr.ca_recflags |= kHFSFastDevCandidateMask;
vnode_setfastdevicecandidate(tvp);
if (vnode_isautocandidate(dvp)) {
cp->c_attr.ca_recflags |= kHFSAutoCandidateMask;
vnode_setautocandidate(tvp);
}
(void) hfs_addhotfile(tvp);
}
*vpp = tvp;
#if CONFIG_PROTECT
if ((protectable_target) && (protected_mount)) {
struct cprotect *keyed_entry = NULL;
if (cp->c_cpentry == NULL) {
panic ("hfs_makenode: no cpentry for cnode (%p)", cp);
}
error = cp_generate_keys (hfsmp, cp, CP_CLASS(cp->c_cpentry->cp_pclass), keywrap_flags, &keyed_entry);
if (error == 0) {
cp_replace_entry (hfsmp, cp, keyed_entry);
if (nocache) {
cache_enter (dvp, tvp, cnp);
}
}
else {
error = EPERM;
int err = hfs_removefile (dvp, tvp, cnp, 0, 0, 1, NULL, 0);
if (err) {
printf("hfs_makenode: removefile failed (%d) for CP entry %p\n", err, tvp);
}
hfs_unlock (cp);
err = vnode_recycle (tvp);
if (err) {
printf("hfs_makenode: vnode_recycle failed (%d) for CP entry %p\n", err, tvp);
}
vnode_put (tvp);
cp = NULL;
*vpp = NULL;
}
}
#endif
#if QUOTA
if (hfsmp->hfs_flags & HFS_QUOTAS) {
if (cp) {
(void) hfs_getinoquota(cp);
}
}
#endif
exit:
cat_releasedesc(&out_desc);
#if CONFIG_PROTECT
if (entry) {
cp_entry_destroy (hfsmp, entry);
entry = NULL;
}
#endif
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);
}
int
hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp, struct vnode **rvpp)
{
struct vnode *rvp = NULLVP;
struct vnode *empty_rvp = NULLVP;
struct vnode *dvp = NULLVP;
struct cnode *cp = VTOC(vp);
int error;
int vid;
if (vnode_vtype(vp) == VDIR) {
return EINVAL;
}
restart:
if ((rvp = cp->c_rsrc_vp)) {
vid = vnode_vid(rvp);
hfs_unlock(cp);
error = vnode_getwithvid(rvp, vid);
hfs_lock_always(cp, HFS_EXCLUSIVE_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, vol=%s, err=%d\n", name, hfsmp->vcbVN, 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;
int newvnode_flags = 0;
hfs_unlock (cp);
error = vnode_create_empty (&empty_rvp);
hfs_lock_always (cp, HFS_EXCLUSIVE_LOCK);
if (error) {
return error;
}
if (cp->c_rsrc_vp) {
vnode_put (empty_rvp);
empty_rvp = NULL;
rvp = NULL;
goto restart;
}
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);
if ((hfsmp->hfs_flags & HFS_STANDARD) == 0) {
error = cat_idlookup (hfsmp, cp->c_fileid, 0, 1, NULL, NULL, &rsrcfork);
}
#if CONFIG_HFS_STD
else {
error = cat_lookup (hfsmp, descptr, 1, 0, (struct cat_desc*)NULL,
(struct cat_attr*)NULL, &rsrcfork, NULL);
}
#endif
hfs_systemfile_unlock(hfsmp, lockflags);
if (error) {
vnode_put (empty_rvp);
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);
if (cn.cn_namelen >= MAXPATHLEN) {
FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
vnode_put (empty_rvp);
return ENAMETOOLONG;
}
}
dvp = vnode_getparent(vp);
rvp = empty_rvp;
error = hfs_getnewvnode(hfsmp, dvp, cn.cn_pnbuf ? &cn : NULL,
descptr, (GNV_WANTRSRC | GNV_SKIPLOCK | GNV_USE_VP),
&cp->c_attr, &rsrcfork, &rvp, &newvnode_flags);
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);
}
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));
}
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));
}
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, HFS_LOCK_DEFAULT) == 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, HFS_LOCK_DEFAULT) == 0) {
cp = VTOC(vp);
hfs_touchtimes(VTOHFS(vp), cp);
hfs_unlock(cp);
}
}
return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_close), ap));
}
#endif
static u_int32_t
hfs_get_document_id_internal(const uint8_t *finderinfo, mode_t mode)
{
const uint8_t *finfo = NULL;
u_int32_t doc_id = 0;
finfo = finderinfo + 16;
if (S_ISDIR(mode) || S_ISREG(mode)) {
const struct FndrExtendedFileInfo *extinfo = (const struct FndrExtendedFileInfo *)finfo;
doc_id = extinfo->document_id;
} else if (S_ISDIR(mode)) {
const struct FndrExtendedDirInfo *extinfo = (const struct FndrExtendedDirInfo *)finfo;
doc_id = extinfo->document_id;
}
return doc_id;
}
u_int32_t
hfs_get_document_id(struct cnode *cp)
{
return (hfs_get_document_id_internal((u_int8_t*)cp->c_finderinfo,
cp->c_attr.ca_mode));
}
u_int32_t
hfs_get_document_id_from_blob(const uint8_t *finderinfo, mode_t mode)
{
return (hfs_get_document_id_internal(finderinfo, mode));
}
int
hfs_vnop_fsync(ap)
struct vnop_fsync_args *ap;
{
struct vnode* vp = ap->a_vp;
int error;
if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) {
return 0;
}
error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
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_vnodeop_p)(void *);
#define VOPFUNC int (*)(void *)
#if CONFIG_HFS_STD
int (**hfs_std_vnodeop_p) (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 },
#if CONFIG_SEARCHFS
{ &vnop_searchfs_desc, (VOPFUNC)hfs_vnop_search },
#else
{ &vnop_searchfs_desc, (VOPFUNC)err_searchfs },
#endif
{ &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},
#if NAMEDSTREAMS
{ &vnop_getnamedstream_desc, (VOPFUNC)hfs_vnop_getnamedstream },
{ &vnop_makenamedstream_desc, (VOPFUNC)hfs_readonly_op },
{ &vnop_removenamedstream_desc, (VOPFUNC)hfs_readonly_op },
#endif
{ &vnop_getattrlistbulk_desc, (VOPFUNC)hfs_vnop_getattrlistbulk },
{ NULL, (VOPFUNC)NULL }
};
struct vnodeopv_desc hfs_std_vnodeop_opv_desc =
{ &hfs_std_vnodeop_p, hfs_standard_vnodeop_entries };
#endif
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)hfs_vnop_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 },
#if CONFIG_SEARCHFS
{ &vnop_searchfs_desc, (VOPFUNC)hfs_vnop_search },
#else
{ &vnop_searchfs_desc, (VOPFUNC)err_searchfs },
#endif
{ &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},
#if NAMEDSTREAMS
{ &vnop_getnamedstream_desc, (VOPFUNC)hfs_vnop_getnamedstream },
{ &vnop_makenamedstream_desc, (VOPFUNC)hfs_vnop_makenamedstream },
{ &vnop_removenamedstream_desc, (VOPFUNC)hfs_vnop_removenamedstream },
#endif
{ &vnop_getattrlistbulk_desc, (VOPFUNC)hfs_vnop_getattrlistbulk },
{ &vnop_mnomap_desc, (VOPFUNC)hfs_vnop_mnomap },
{ 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 },
{ &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},
{ (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_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},
{ (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
};
struct vnodeopv_desc hfs_fifoop_opv_desc =
{ &hfs_fifoop_p, hfs_fifoop_entries };
#endif