/*
- * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2014 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <sys/vnode.h>
#include <sys/xattr.h>
#include <sys/fcntl.h>
+#include <sys/fsctl.h>
#include <sys/vnode_internal.h>
#include <sys/kauth.h>
+#include <sys/uio_internal.h>
#include "hfs.h"
#include "hfs_cnode.h"
#include "hfs_endian.h"
#include "hfs_btreeio.h"
#include "hfs_fsctl.h"
+#include "hfs_cprotect.h"
#include "hfscommon/headers/BTreesInternal.h"
int result;
uio_t uio;
size_t size;
+#if HFS_COMPRESSION
+ int showcompressed;
+ vfs_context_t ctx;
+ vnode_t vp;
+#endif /* HFS_COMPRESSION */
};
-#define HFS_MAXATTRIBUTESIZE (128 * 1024)
-#define HFS_MAXATTRBLKS (32 * 1024)
-
/* HFS Internal Names */
#define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity"
#define XATTR_XATTREXTENTS_NAME "system.xattrextents"
-/* Faster version if we already know this is the data fork. */
-#define RSRC_FORK_EXISTS(CP) \
- (((CP)->c_attr.ca_blocks - (CP)->c_datafork->ff_data.cf_blocks) > 0)
-
static u_int32_t emptyfinfo[8] = {0};
+static int hfs_zero_hidden_fields (struct cnode *cp, u_int8_t *finderinfo);
+
const char hfs_attrdatafilename[] = "Attribute Data";
static int listattr_callback(const HFSPlusAttrKey *key, const HFSPlusAttrData *data,
if ( !S_ISREG(cp->c_mode) ) {
return (EPERM);
}
- if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
+#if HFS_COMPRESSION
+ int hide_rsrc = hfs_hides_rsrc(ap->a_context, VTOC(vp), 1);
+#endif /* HFS_COMPRESSION */
+ if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
return (error);
}
- if (!RSRC_FORK_EXISTS(cp) && (ap->a_operation != NS_OPEN)) {
+ if ((!hfs_has_rsrc(cp)
+#if HFS_COMPRESSION
+ || hide_rsrc
+#endif /* HFS_COMPRESSION */
+ ) && (ap->a_operation != NS_OPEN)) {
hfs_unlock(cp);
return (ENOATTR);
}
- error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp, TRUE);
+ error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp);
hfs_unlock(cp);
return (error);
if ( !S_ISREG(cp->c_mode) ) {
return (EPERM);
}
- if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
+#if HFS_COMPRESSION
+ if (hfs_hides_rsrc(ap->a_context, VTOC(vp), 1)) {
+ if (VNODE_IS_RSRC(vp)) {
+ return EINVAL;
+ } else {
+ error = decmpfs_decompress_file(vp, VTOCMP(vp), -1, 1, 0);
+ if (error != 0)
+ return error;
+ }
+ }
+#endif /* HFS_COMPRESSION */
+ if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
return (error);
}
- error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp, TRUE);
+ error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp);
hfs_unlock(cp);
return (error);
hfs_vnop_removenamedstream(struct vnop_removenamedstream_args* ap)
{
vnode_t svp = ap->a_svp;
- struct cnode *scp;
+ cnode_t *scp = VTOC(svp);
int error = 0;
/*
if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) != 0) {
return (ENOATTR);
}
- scp = VTOC(svp);
-
- /* Take truncate lock before taking cnode lock. */
- hfs_lock_truncate(scp, TRUE);
- if ((error = hfs_lock(scp, HFS_EXCLUSIVE_LOCK))) {
- goto out;
+#if HFS_COMPRESSION
+ if (hfs_hides_rsrc(ap->a_context, scp, 1)) {
+ /* do nothing */
+ return 0;
}
- if (VTOF(svp)->ff_size != 0) {
+#endif /* HFS_COMPRESSION */
+
+ hfs_lock_truncate(scp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
+ if (VTOF(svp)->ff_size) {
+ // hfs_truncate will deal with the cnode lock
error = hfs_truncate(svp, 0, IO_NDELAY, 0, ap->a_context);
}
- hfs_unlock(scp);
-out:
- hfs_unlock_truncate(scp, TRUE);
- return (error);
+ hfs_unlock_truncate(scp, HFS_LOCK_DEFAULT);
+
+ return error;
}
#endif
+/* Zero out the date added field for the specified cnode */
+static int hfs_zero_hidden_fields (struct cnode *cp, u_int8_t *finderinfo)
+{
+ u_int8_t *finfo = finderinfo;
+
+ /* Advance finfo by 16 bytes to the 2nd half of the finderinfo */
+ finfo = finfo + 16;
+
+ if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
+ struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
+ extinfo->document_id = 0;
+ extinfo->date_added = 0;
+ extinfo->write_gen_counter = 0;
+ } else if (S_ISDIR(cp->c_attr.ca_mode)) {
+ struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
+ extinfo->document_id = 0;
+ extinfo->date_added = 0;
+ extinfo->write_gen_counter = 0;
+ } else {
+ /* Return an error */
+ return -1;
+ }
+ return 0;
+
+}
+
/*
* Retrieve the data of an extended attribute.
*/
-__private_extern__
int
hfs_vnop_getxattr(struct vnop_getxattr_args *ap)
/*
struct cnode *cp;
struct hfsmount *hfsmp;
uio_t uio = ap->a_uio;
- struct BTreeIterator * iterator = NULL;
- struct filefork *btfile;
- FSBufferDescriptor btdata;
- HFSPlusAttrRecord * recp = NULL;
size_t bufsize;
- u_int16_t datasize;
- int lockflags;
int result;
cp = VTOC(vp);
if (vp == cp->c_vp) {
+#if HFS_COMPRESSION
+ int decmpfs_hide = hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1); /* 1 == don't take the cnode lock */
+ if (decmpfs_hide && !(ap->a_options & XATTR_SHOWCOMPRESSION))
+ return ENOATTR;
+#endif /* HFS_COMPRESSION */
+
/* Get the Finder Info. */
if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
u_int8_t finderinfo[32];
bufsize = 32;
- if ((result = hfs_lock(cp, HFS_SHARED_LOCK))) {
+ if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
return (result);
}
/* Make a copy since we may not export all of it. */
bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
hfs_unlock(cp);
+
+ /* Zero out the date added field in the local copy */
+ hfs_zero_hidden_fields (cp, finderinfo);
/* Don't expose a symlink's private type/creator. */
if (vnode_islnk(vp)) {
*ap->a_size = bufsize;
return (0);
}
- if (uio_resid(uio) < bufsize)
+ if ((user_size_t)uio_resid(uio) < bufsize)
return (ERANGE);
result = uiomove((caddr_t)&finderinfo , bufsize, uio);
/* Read the Resource Fork. */
if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
struct vnode *rvp = NULL;
+ int openunlinked = 0;
+ int namelen = 0;
if ( !S_ISREG(cp->c_mode) ) {
return (EPERM);
}
- if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
+ if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
return (result);
}
- if ( !RSRC_FORK_EXISTS(cp)) {
+ namelen = cp->c_desc.cd_namelen;
+
+ if (!hfs_has_rsrc(cp)) {
hfs_unlock(cp);
return (ENOATTR);
}
hfsmp = VTOHFS(vp);
- result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE);
+ if ((cp->c_flag & C_DELETED) && (namelen == 0)) {
+ openunlinked = 1;
+ }
+
+ result = hfs_vgetrsrc(hfsmp, vp, &rvp);
hfs_unlock(cp);
if (result) {
return (result);
if (uio == NULL) {
*ap->a_size = (size_t)VTOF(rvp)->ff_size;
} else {
+#if HFS_COMPRESSION
+ user_ssize_t uio_size = 0;
+ if (decmpfs_hide)
+ uio_size = uio_resid(uio);
+#endif /* HFS_COMPRESSION */
result = VNOP_READ(rvp, uio, 0, ap->a_context);
+#if HFS_COMPRESSION
+ if (decmpfs_hide &&
+ (result == 0) &&
+ (uio_resid(uio) == uio_size)) {
+ /*
+ * We intentionally make the above call to VNOP_READ so that
+ * it can return an authorization/permission/etc. Error
+ * based on ap->a_context and thus deny this operation;
+ * in that case, result != 0 and we won't proceed.
+ *
+ * However, if result == 0, it will have returned no data
+ * because hfs_vnop_read hid the resource fork
+ * (hence uio_resid(uio) == uio_size, i.e. the uio is untouched)
+ *
+ * In that case, we try again with the decmpfs_ctx context
+ * to get the actual data
+ */
+ result = VNOP_READ(rvp, uio, 0, decmpfs_ctx);
+ }
+#endif /* HFS_COMPRESSION */
+ }
+ /* force the rsrc fork vnode to recycle right away */
+ if (openunlinked) {
+ int vref;
+ vref = vnode_ref (rvp);
+ if (vref == 0) {
+ vnode_rele (rvp);
+ }
+ vnode_recycle(rvp);
}
vnode_put(rvp);
return (result);
return (EPERM);
}
- if ((result = hfs_lock(cp, HFS_SHARED_LOCK))) {
+ if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
return (result);
}
- /* Bail if we don't have any extended attributes. */
+
+ /* Check for non-rsrc, non-finderinfo EAs */
+ result = hfs_getxattr_internal (cp, ap, VTOHFS(cp->c_vp), 0);
+
+ hfs_unlock(cp);
+
+ return MacToVFSError(result);
+}
+
+// Has same limitations as hfs_getxattr_internal below
+int hfs_xattr_read(vnode_t vp, const char *name, void *data, size_t *size)
+{
+ char uio_buf[UIO_SIZEOF(1)];
+ uio_t uio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, uio_buf,
+ sizeof(uio_buf));
+
+ uio_addiov(uio, CAST_USER_ADDR_T(data), *size);
+
+ struct vnop_getxattr_args args = {
+ .a_uio = uio,
+ .a_name = name,
+ .a_size = size
+ };
+
+ return hfs_getxattr_internal(VTOC(vp), &args, VTOHFS(vp), 0);
+}
+
+/*
+ * getxattr_internal
+ *
+ * We break out this internal function which searches the attributes B-Tree and the
+ * overflow extents file to find non-resource, non-finderinfo EAs. There may be cases
+ * where we need to get EAs in contexts where we are already holding the cnode lock,
+ * and to re-enter hfs_vnop_getxattr would cause us to double-lock the cnode. Instead,
+ * we can just directly call this function.
+ *
+ * We pass the hfsmp argument directly here because we may not necessarily have a cnode to
+ * operate on. Under normal conditions, we have a file or directory to query, but if we
+ * are operating on the root directory (id 1), then we may not have a cnode. In this case, if hte
+ * 'cp' argument is NULL, then we need to use the 'fileid' argument as the entry to manipulate
+ *
+ * NOTE: This function assumes the cnode lock for 'cp' is held exclusive or shared.
+ */
+int hfs_getxattr_internal (struct cnode *cp, struct vnop_getxattr_args *ap,
+ struct hfsmount *hfsmp, u_int32_t fileid)
+{
+
+ struct filefork *btfile;
+ struct BTreeIterator * iterator = NULL;
+ size_t bufsize = 0;
+ HFSPlusAttrRecord *recp = NULL;
+ FSBufferDescriptor btdata;
+ int lockflags = 0;
+ int result = 0;
+ u_int16_t datasize = 0;
+ uio_t uio = ap->a_uio;
+ u_int32_t target_id = 0;
+
+ if (cp) {
+ target_id = cp->c_fileid;
+ } else {
+ target_id = fileid;
+ }
+
+
+ /* Bail if we don't have an EA B-Tree. */
if ((hfsmp->hfs_attribute_vp == NULL) ||
- (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
- result = ENOATTR;
+ ((cp) && (cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0)) {
+ result = ENOATTR;
goto exit;
}
+
+ /* Initialize the B-Tree iterator for searching for the proper EA */
btfile = VTOF(hfsmp->hfs_attribute_vp);
-
+
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
if (iterator == NULL) {
result = ENOMEM;
goto exit;
}
bzero(iterator, sizeof(*iterator));
-
- bufsize = sizeof(HFSPlusAttrData) - 2;
- if (uio)
- bufsize += uio_resid(uio);
- bufsize = MAX(bufsize, sizeof(HFSPlusAttrRecord));
- MALLOC(recp, HFSPlusAttrRecord *, bufsize, M_TEMP, M_WAITOK);
+
+ /* Allocate memory for reading in the attribute record. This buffer is
+ * big enough to read in all types of attribute records. It is not big
+ * enough to read inline attribute data which is read in later.
+ */
+ MALLOC(recp, HFSPlusAttrRecord *, sizeof(HFSPlusAttrRecord), M_TEMP, M_WAITOK);
if (recp == NULL) {
result = ENOMEM;
goto exit;
}
btdata.bufferAddress = recp;
- btdata.itemSize = bufsize;
+ btdata.itemSize = sizeof(HFSPlusAttrRecord);
btdata.itemCount = 1;
- result = hfs_buildattrkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
- if (result)
- goto exit;
+ result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
+ if (result) {
+ goto exit;
+ }
- /* Lookup the attribute. */
+ /* Lookup the attribute in the Attribute B-Tree */
lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
hfs_systemfile_unlock(hfsmp, lockflags);
-
+
if (result) {
- if (result == btNotFound)
+ if (result == btNotFound) {
result = ENOATTR;
+ }
goto exit;
}
-
+
+ /*
+ * Operate differently if we have inline EAs that can fit in the attribute B-Tree or if
+ * we have extent based EAs.
+ */
switch (recp->recordType) {
- case kHFSPlusAttrInlineData:
- /*
- * Sanity check record size. It's not required to have any
- * user data, so the minimum size is 2 bytes less that the
- * size of HFSPlusAttrData (since HFSPlusAttrData struct
- * has 2 bytes set aside for attribute data).
- */
- if (datasize < (sizeof(HFSPlusAttrData) - 2)) {
- printf("hfs_getxattr: %d,%s invalid record size %d (expecting %lu)\n",
- VTOC(vp)->c_fileid, ap->a_name, datasize, sizeof(HFSPlusAttrData));
- result = ENOATTR;
- break;
- }
- *ap->a_size = recp->attrData.attrSize;
- if (uio && recp->attrData.attrSize != 0) {
- if (*ap->a_size > uio_resid(uio))
- result = ERANGE;
- else
- result = uiomove((caddr_t) &recp->attrData.attrData , recp->attrData.attrSize, uio);
- }
- break;
- case kHFSPlusAttrForkData:
- if (datasize < sizeof(HFSPlusAttrForkData)) {
- printf("hfs_getxattr: %d,%s invalid record size %d (expecting %lu)\n",
- VTOC(vp)->c_fileid, ap->a_name, datasize, sizeof(HFSPlusAttrForkData));
- result = ENOATTR;
- break;
- }
- *ap->a_size = recp->forkData.theFork.logicalSize;
- if (uio == NULL) {
- break;
- }
- if (*ap->a_size > uio_resid(uio)) {
- result = ERANGE;
+ /* Attribute fits in the Attribute B-Tree */
+ case kHFSPlusAttrInlineData: {
+ /*
+ * Sanity check record size. It's not required to have any
+ * user data, so the minimum size is 2 bytes less that the
+ * size of HFSPlusAttrData (since HFSPlusAttrData struct
+ * has 2 bytes set aside for attribute data).
+ */
+ if (datasize < (sizeof(HFSPlusAttrData) - 2)) {
+ printf("hfs_getxattr: vol=%s %d,%s invalid record size %d (expecting %lu)\n",
+ hfsmp->vcbVN, target_id, ap->a_name, datasize, sizeof(HFSPlusAttrData));
+ result = ENOATTR;
+ break;
+ }
+ *ap->a_size = recp->attrData.attrSize;
+ if (uio && recp->attrData.attrSize != 0) {
+ if (*ap->a_size > (user_size_t)uio_resid(uio)) {
+ /* User provided buffer is not large enough for the xattr data */
+ result = ERANGE;
+ } else {
+ /* Previous BTreeSearchRecord() read in only the attribute record,
+ * and not the attribute data. Now allocate enough memory for
+ * both attribute record and data, and read the attribute record again.
+ */
+ bufsize = sizeof(HFSPlusAttrData) - 2 + recp->attrData.attrSize;
+ FREE(recp, M_TEMP);
+ MALLOC(recp, HFSPlusAttrRecord *, bufsize, M_TEMP, M_WAITOK);
+ if (recp == NULL) {
+ result = ENOMEM;
+ goto exit;
+ }
+
+ btdata.bufferAddress = recp;
+ btdata.itemSize = bufsize;
+ btdata.itemCount = 1;
+
+ bzero(iterator, sizeof(*iterator));
+ result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
+ if (result) {
+ goto exit;
+ }
+
+ /* Lookup the attribute record and inline data */
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
+ result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ if (result) {
+ if (result == btNotFound) {
+ result = ENOATTR;
+ }
+ goto exit;
+ }
+
+ /* Copy-out the attribute data to the user buffer */
+ *ap->a_size = recp->attrData.attrSize;
+ result = uiomove((caddr_t) &recp->attrData.attrData , recp->attrData.attrSize, uio);
+ }
+ }
break;
}
- /* Process overflow extents if necessary. */
- if (has_overflow_extents(&recp->forkData.theFork)) {
- HFSPlusExtentDescriptor *extentbuf;
- HFSPlusExtentDescriptor *extentptr;
- size_t extentbufsize;
- u_int32_t totalblocks;
- u_int32_t blkcnt;
- u_int32_t attrlen;
-
- totalblocks = recp->forkData.theFork.totalBlocks;
- /* Ignore bogus block counts. */
- if (totalblocks > HFS_MAXATTRBLKS) {
- result = ERANGE;
+
+ /* Extent-Based EAs */
+ case kHFSPlusAttrForkData: {
+ if (datasize < sizeof(HFSPlusAttrForkData)) {
+ printf("hfs_getxattr: vol=%s %d,%s invalid record size %d (expecting %lu)\n",
+ hfsmp->vcbVN, target_id, ap->a_name, datasize, sizeof(HFSPlusAttrForkData));
+ result = ENOATTR;
break;
}
- attrlen = recp->forkData.theFork.logicalSize;
-
- /* Get a buffer to hold the worst case amount of extents. */
- extentbufsize = totalblocks * sizeof(HFSPlusExtentDescriptor);
- extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord));
- MALLOC(extentbuf, HFSPlusExtentDescriptor *, extentbufsize, M_TEMP, M_WAITOK);
- if (extentbuf == NULL) {
- result = ENOMEM;
+ *ap->a_size = recp->forkData.theFork.logicalSize;
+ if (uio == NULL) {
break;
}
- bzero(extentbuf, extentbufsize);
- extentptr = extentbuf;
-
- /* Grab the first 8 extents. */
- bcopy(&recp->forkData.theFork.extents[0], extentptr, sizeof(HFSPlusExtentRecord));
- extentptr += kHFSPlusExtentDensity;
- blkcnt = count_extent_blocks(totalblocks, recp->forkData.theFork.extents);
-
- /* Now lookup the overflow extents. */
- lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
- while (blkcnt < totalblocks) {
- ((HFSPlusAttrKey *)&iterator->key)->startBlock = blkcnt;
- result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
- if (result ||
- (recp->recordType != kHFSPlusAttrExtents) ||
- (datasize < sizeof(HFSPlusAttrExtents))) {
- printf("hfs_getxattr: %s missing extents, only %d blks of %d found\n",
- ap->a_name, blkcnt, totalblocks);
- result = ENOATTR;
- break; /* break from while */
+ if (*ap->a_size > (user_size_t)uio_resid(uio)) {
+ result = ERANGE;
+ break;
+ }
+ /* Process overflow extents if necessary. */
+ if (has_overflow_extents(&recp->forkData.theFork)) {
+ HFSPlusExtentDescriptor *extentbuf;
+ HFSPlusExtentDescriptor *extentptr;
+ size_t extentbufsize;
+ u_int32_t totalblocks;
+ u_int32_t blkcnt;
+ u_int32_t attrlen;
+
+ totalblocks = recp->forkData.theFork.totalBlocks;
+ /* Ignore bogus block counts. */
+ if (totalblocks > howmany(HFS_XATTR_MAXSIZE, hfsmp->blockSize)) {
+ result = ERANGE;
+ break;
+ }
+ attrlen = recp->forkData.theFork.logicalSize;
+
+ /* Get a buffer to hold the worst case amount of extents. */
+ extentbufsize = totalblocks * sizeof(HFSPlusExtentDescriptor);
+ extentbufsize = roundup(extentbufsize, sizeof(HFSPlusExtentRecord));
+ MALLOC(extentbuf, HFSPlusExtentDescriptor *, extentbufsize, M_TEMP, M_WAITOK);
+ if (extentbuf == NULL) {
+ result = ENOMEM;
+ break;
}
- /* Grab the next 8 extents. */
- bcopy(&recp->overflowExtents.extents[0], extentptr, sizeof(HFSPlusExtentRecord));
+ bzero(extentbuf, extentbufsize);
+ extentptr = extentbuf;
+
+ /* Grab the first 8 extents. */
+ bcopy(&recp->forkData.theFork.extents[0], extentptr, sizeof(HFSPlusExtentRecord));
extentptr += kHFSPlusExtentDensity;
- blkcnt += count_extent_blocks(totalblocks, recp->overflowExtents.extents);
- }
- hfs_systemfile_unlock(hfsmp, lockflags);
-
- if (blkcnt < totalblocks) {
- result = ENOATTR;
- } else {
- result = read_attr_data(hfsmp, uio, attrlen, extentbuf);
+ blkcnt = count_extent_blocks(totalblocks, recp->forkData.theFork.extents);
+
+ /* Now lookup the overflow extents. */
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
+ while (blkcnt < totalblocks) {
+ ((HFSPlusAttrKey *)&iterator->key)->startBlock = blkcnt;
+ result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
+ if (result ||
+ (recp->recordType != kHFSPlusAttrExtents) ||
+ (datasize < sizeof(HFSPlusAttrExtents))) {
+ printf("hfs_getxattr: %s missing extents, only %d blks of %d found\n",
+ ap->a_name, blkcnt, totalblocks);
+ result = ENOATTR;
+ break; /* break from while */
+ }
+ /* Grab the next 8 extents. */
+ bcopy(&recp->overflowExtents.extents[0], extentptr, sizeof(HFSPlusExtentRecord));
+ extentptr += kHFSPlusExtentDensity;
+ blkcnt += count_extent_blocks(totalblocks, recp->overflowExtents.extents);
+ }
+
+ /* Release Attr B-Tree lock */
+ hfs_systemfile_unlock(hfsmp, lockflags);
+
+ if (blkcnt < totalblocks) {
+ result = ENOATTR;
+ } else {
+ result = read_attr_data(hfsmp, uio, attrlen, extentbuf);
+ }
+ FREE(extentbuf, M_TEMP);
+
+ } else { /* No overflow extents. */
+ result = read_attr_data(hfsmp, uio, recp->forkData.theFork.logicalSize, recp->forkData.theFork.extents);
}
- FREE(extentbuf, M_TEMP);
-
- } else /* No overflow extents. */ {
- result = read_attr_data(hfsmp, uio, recp->forkData.theFork.logicalSize, recp->forkData.theFork.extents);
+ break;
}
- break;
-
- default:
- result = ENOATTR;
- break;
+
+ default:
+ /* We only support Extent or inline EAs. Default to ENOATTR for anything else */
+ result = ENOATTR;
+ break;
}
-exit:
- hfs_unlock(cp);
-
+
+exit:
if (iterator) {
FREE(iterator, M_TEMP);
}
FREE(recp, M_TEMP);
}
- return MacToVFSError(result);
+ return result;
+
}
+
/*
* Set the data of an extended attribute.
*/
-__private_extern__
int
hfs_vnop_setxattr(struct vnop_setxattr_args *ap)
/*
struct cnode *cp = NULL;
struct hfsmount *hfsmp;
uio_t uio = ap->a_uio;
- struct BTreeIterator * iterator = NULL;
- struct filefork *btfile = NULL;
size_t attrsize;
- FSBufferDescriptor btdata;
- HFSPlusAttrRecord *recp = NULL;
- HFSPlusExtentDescriptor *extentptr = NULL;
- HFSPlusAttrRecord attrdata; /* 90 bytes */
void * user_data_ptr = NULL;
- int started_transaction = 0;
- int lockflags = 0;
- int exists;
- int allocatedblks = 0;
int result;
+ time_t orig_ctime=VTOC(vp)->c_ctime;
if (ap->a_name == NULL || ap->a_name[0] == '\0') {
return (EINVAL); /* invalid name */
if (VNODE_IS_RSRC(vp)) {
return (EPERM);
}
+
+#if HFS_COMPRESSION
+ if (hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1) ) { /* 1 == don't take the cnode lock */
+ result = decmpfs_decompress_file(vp, VTOCMP(vp), -1, 1, 0);
+ if (result != 0)
+ return result;
+ }
+#endif /* HFS_COMPRESSION */
+
+ check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_WRITE_OP, NSPACE_REARM_NO_ARG);
+
/* Set the Finder Info. */
if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
u_int8_t finderinfo[32];
struct FndrFileInfo *fip;
void * finderinfo_start;
+ u_int8_t *finfo = NULL;
u_int16_t fdFlags;
+ u_int32_t dateadded = 0;
+ u_int32_t write_gen_counter = 0;
+ u_int32_t document_id = 0;
attrsize = sizeof(VTOC(vp)->c_finderinfo);
- if (uio_resid(uio) != attrsize) {
+ if ((user_size_t)uio_resid(uio) != attrsize) {
return (ERANGE);
}
/* Grab the new Finder Info data. */
}
fip = (struct FndrFileInfo *)&finderinfo;
- if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
+ if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
return (result);
}
cp = VTOC(vp);
}
}
+ /* Grab the current date added from the cnode */
+ dateadded = hfs_get_dateadded (cp);
+ if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
+ struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)((u_int8_t*)cp->c_finderinfo + 16);
+ /*
+ * Grab generation counter directly from the cnode
+ * instead of calling hfs_get_gencount(), because
+ * for zero generation count values hfs_get_gencount()
+ * lies and bumps it up to one.
+ */
+ write_gen_counter = extinfo->write_gen_counter;
+ document_id = extinfo->document_id;
+ } else if (S_ISDIR(cp->c_attr.ca_mode)) {
+ struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((u_int8_t*)cp->c_finderinfo + 16);
+ write_gen_counter = extinfo->write_gen_counter;
+ document_id = extinfo->document_id;
+ }
+
+ /*
+ * Zero out the finder info's reserved fields like date added,
+ * generation counter, and document id to ignore user's attempts
+ * to set it
+ */
+ hfs_zero_hidden_fields(cp, finderinfo);
+
if (bcmp(finderinfo_start, emptyfinfo, attrsize)) {
/* attr exists and "create" was specified. */
if (ap->a_options & XATTR_CREATE) {
hfs_unlock(cp);
return (EEXIST);
}
- } else /* empty */ {
+ } else { /* empty */
/* attr doesn't exists and "replace" was specified. */
if (ap->a_options & XATTR_REPLACE) {
hfs_unlock(cp);
return (ENOATTR);
}
}
+
+ /*
+ * Now restore the date added and other reserved fields to the finderinfo to
+ * be written out. Advance to the 2nd half of the finderinfo to write them
+ * out into the buffer.
+ *
+ * Make sure to endian swap the date added back into big endian. When we used
+ * hfs_get_dateadded above to retrieve it, it swapped into local endianness
+ * for us. But now that we're writing it out, put it back into big endian.
+ */
+ finfo = &finderinfo[16];
+ if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
+ struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
+ extinfo->date_added = OSSwapHostToBigInt32(dateadded);
+ extinfo->write_gen_counter = write_gen_counter;
+ extinfo->document_id = document_id;
+ } else if (S_ISDIR(cp->c_attr.ca_mode)) {
+ struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
+ extinfo->date_added = OSSwapHostToBigInt32(dateadded);
+ extinfo->write_gen_counter = write_gen_counter;
+ extinfo->document_id = document_id;
+ }
+
/* Set the cnode's Finder Info. */
- if (attrsize == sizeof(cp->c_finderinfo))
+ if (attrsize == sizeof(cp->c_finderinfo)) {
bcopy(&finderinfo[0], finderinfo_start, attrsize);
- else
+ } else {
bcopy(&finderinfo[8], finderinfo_start, attrsize);
-
+ }
+
/* Updating finderInfo updates change time and modified time */
cp->c_touch_chgtime = TRUE;
cp->c_flag |= C_MODIFIED;
* are both 16-bit fields.
*/
fdFlags = *((u_int16_t *) &cp->c_finderinfo[8]);
- if (fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
- cp->c_flags |= UF_HIDDEN;
- else
- cp->c_flags &= ~UF_HIDDEN;
+ if (fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask)) {
+ cp->c_bsdflags |= UF_HIDDEN;
+ } else {
+ cp->c_bsdflags &= ~UF_HIDDEN;
+ }
- result = hfs_update(vp, FALSE);
+ result = hfs_update(vp, 0);
hfs_unlock(cp);
return (result);
/* Write the Resource Fork. */
if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
struct vnode *rvp = NULL;
+ int namelen = 0;
+ int openunlinked = 0;
if (!vnode_isreg(vp)) {
return (EPERM);
}
- if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK))) {
+ if ((result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
return (result);
}
cp = VTOC(vp);
+ namelen = cp->c_desc.cd_namelen;
- if (RSRC_FORK_EXISTS(cp)) {
+ if (hfs_has_rsrc(cp)) {
/* attr exists and "create" was specified. */
if (ap->a_options & XATTR_CREATE) {
hfs_unlock(cp);
return (ENOATTR);
}
}
- result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE);
+
+ /*
+ * Note that we could be called on to grab the rsrc fork vnode
+ * for a file that has become open-unlinked.
+ */
+ if ((cp->c_flag & C_DELETED) && (namelen == 0)) {
+ openunlinked = 1;
+ }
+
+ result = hfs_vgetrsrc(hfsmp, vp, &rvp);
hfs_unlock(cp);
if (result) {
return (result);
}
- /* VNOP_WRITE will update timestamps accordingly */
+ /* VNOP_WRITE marks cnode as needing a modtime update */
result = VNOP_WRITE(rvp, uio, 0, ap->a_context);
+
+ /* if open unlinked, force it inactive */
+ if (openunlinked) {
+ int vref;
+ vref = vnode_ref (rvp);
+ if (vref == 0) {
+ vnode_rele(rvp);
+ }
+ vnode_recycle (rvp);
+ } else {
+ /* cnode is not open-unlinked, so re-lock cnode to sync */
+ if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
+ vnode_recycle (rvp);
+ vnode_put(rvp);
+ return result;
+ }
+
+ /* hfs fsync rsrc fork to force to disk and update modtime */
+ result = hfs_fsync (rvp, MNT_NOWAIT, 0, vfs_context_proc (ap->a_context));
+ hfs_unlock (cp);
+ }
+
vnode_put(rvp);
return (result);
}
attrsize = uio_resid(uio);
/* Enforce an upper limit. */
- if (attrsize > HFS_MAXATTRIBUTESIZE) {
- return (E2BIG);
+ if (attrsize > HFS_XATTR_MAXSIZE) {
+ result = E2BIG;
+ goto exit;
}
/*
- * Attempt to copy the users attr data before taking any locks.
+ * Attempt to copy the users attr data before taking any locks,
+ * only if it will be an inline attribute. For larger attributes,
+ * the data will be directly read from the uio.
*/
if (attrsize > 0 &&
hfsmp->hfs_max_inline_attrsize != 0 &&
attrsize < hfsmp->hfs_max_inline_attrsize) {
MALLOC(user_data_ptr, void *, attrsize, M_TEMP, M_WAITOK);
if (user_data_ptr == NULL) {
- return (ENOMEM);
+ result = ENOMEM;
+ goto exit;
}
result = uiomove((caddr_t)user_data_ptr, attrsize, uio);
}
}
- result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK);
+ result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
if (result) {
goto exit;
}
cp = VTOC(vp);
+
+ /*
+ * If we're trying to set a non-finderinfo, non-resourcefork EA, then
+ * call the breakout function.
+ */
+ result = hfs_setxattr_internal (cp, user_data_ptr, attrsize, ap, VTOHFS(vp), 0);
+
+ exit:
+ if (cp) {
+ hfs_unlock(cp);
+ }
+ if (user_data_ptr) {
+ FREE(user_data_ptr, M_TEMP);
+ }
+
+ return (result == btNotFound ? ENOATTR : MacToVFSError(result));
+}
+
+// Has same limitations as hfs_setxattr_internal below
+int hfs_xattr_write(vnode_t vp, const char *name, const void *data, size_t size)
+{
+ struct vnop_setxattr_args args = {
+ .a_vp = vp,
+ .a_name = name,
+ };
+
+ return hfs_setxattr_internal(VTOC(vp), data, size, &args, VTOHFS(vp), 0);
+}
+
+/*
+ * hfs_setxattr_internal
+ *
+ * Internal function to set non-rsrc, non-finderinfo EAs to either the attribute B-Tree or
+ * extent-based EAs.
+ *
+ * See comments from hfs_getxattr_internal on why we need to pass 'hfsmp' and fileid here.
+ * The gist is that we could end up writing to the root folder which may not have a cnode.
+ *
+ * Assumptions:
+ * 1. cnode 'cp' is locked EXCLUSIVE before calling this function.
+ * 2. data_ptr contains data to be written. If gathering data from userland, this must be
+ * done before calling this function.
+ * 3. If data originates entirely in-kernel, use a null UIO, and ensure the size is less than
+ * hfsmp->hfs_max_inline_attrsize bytes long.
+ */
+int hfs_setxattr_internal (struct cnode *cp, const void *data_ptr, size_t attrsize,
+ struct vnop_setxattr_args *ap, struct hfsmount *hfsmp,
+ u_int32_t fileid)
+{
+ uio_t uio = ap->a_uio;
+ struct vnode *vp = ap->a_vp;
+ int started_transaction = 0;
+ struct BTreeIterator * iterator = NULL;
+ struct filefork *btfile = NULL;
+ FSBufferDescriptor btdata;
+ HFSPlusAttrRecord attrdata; /* 90 bytes */
+ HFSPlusAttrRecord *recp = NULL;
+ HFSPlusExtentDescriptor *extentptr = NULL;
+ int result = 0;
+ int lockflags = 0;
+ int exists = 0;
+ int allocatedblks = 0;
+ u_int32_t target_id;
+ if (cp) {
+ target_id = cp->c_fileid;
+ } else {
+ target_id = fileid;
+ }
+
/* Start a transaction for our changes. */
if (hfs_start_transaction(hfsmp) != 0) {
result = EINVAL;
goto exit;
}
started_transaction = 1;
-
+
/*
* Once we started the transaction, nobody can compete
* with us, so make sure this file is still there.
*/
- if (cp->c_flag & C_NOEXISTS) {
+ if ((cp) && (cp->c_flag & C_NOEXISTS)) {
result = ENOENT;
goto exit;
}
-
+
/*
* If there isn't an attributes b-tree then create one.
*/
hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
}
- /* Take exclusive access to the attributes b-tree. */
lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
/* Build the b-tree key. */
goto exit;
}
bzero(iterator, sizeof(*iterator));
- result = hfs_buildattrkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
+ result = hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
if (result) {
goto exit;
}
-
+
/* Preflight for replace/create semantics. */
btfile = VTOF(hfsmp->hfs_attribute_vp);
btdata.bufferAddress = &attrdata;
btdata.itemSize = sizeof(attrdata);
btdata.itemCount = 1;
exists = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL) == 0;
-
+
/* Replace requires that the attribute already exists. */
if ((ap->a_options & XATTR_REPLACE) && !exists) {
result = ENOATTR;
result = EEXIST;
goto exit;
}
+
/* If it won't fit inline then use extent-based attributes. */
if (attrsize > hfsmp->hfs_max_inline_attrsize) {
size_t extentbufsize;
int extentblks;
u_int32_t *keystartblk;
int i;
-
- /* Check if volume supports extent-based attributes */
- if ((hfsmp->hfs_flags & HFS_XATTR_EXTENTS) == 0) {
- result = E2BIG;
+
+ if (uio == NULL) {
+ /*
+ * setxattrs originating from in-kernel are not supported if they are bigger
+ * than the inline max size. Just return ENOATTR and force them to do it with a
+ * smaller EA.
+ */
+ result = EPERM;
goto exit;
- }
-
+ }
+
/* Get some blocks. */
blkcnt = howmany(attrsize, hfsmp->blockSize);
extentbufsize = blkcnt * sizeof(HFSPlusExtentDescriptor);
/* Copy data into the blocks. */
result = write_attr_data(hfsmp, uio, attrsize, extentptr);
if (result) {
- printf("hfs_setxattr: write_attr_data err (%d) %s:%s\n",
- result, vnode_name(vp) ? vnode_name(vp) : "", ap->a_name);
+ if (vp) {
+ const char *name = vnode_getname(vp);
+ printf("hfs_setxattr: write_attr_data vol=%s err (%d) %s:%s\n",
+ hfsmp->vcbVN, result, name ? name : "", ap->a_name);
+ if (name)
+ vnode_putname(name);
+ }
goto exit;
}
if (exists) {
result = remove_attribute_records(hfsmp, iterator);
if (result) {
- printf("hfs_setxattr: remove_attribute_records err (%d) %s:%s\n",
- result, vnode_name(vp) ? vnode_name(vp) : "", ap->a_name);
- goto exit;
+ if (vp) {
+ const char *name = vnode_getname(vp);
+ printf("hfs_setxattr: remove_attribute_records vol=%s err (%d) %s:%s\n",
+ hfsmp->vcbVN, result, name ? name : "", ap->a_name);
+ if (name)
+ vnode_putname(name);
+ }
+ goto exit;
}
}
-
/* Create attribute fork data record. */
MALLOC(recp, HFSPlusAttrRecord *, sizeof(HFSPlusAttrRecord), M_TEMP, M_WAITOK);
if (recp == NULL) {
btdata.bufferAddress = recp;
btdata.itemCount = 1;
btdata.itemSize = sizeof(HFSPlusAttrForkData);
-
+
recp->recordType = kHFSPlusAttrForkData;
recp->forkData.reserved = 0;
recp->forkData.theFork.logicalSize = attrsize;
recp->forkData.theFork.clumpSize = 0;
recp->forkData.theFork.totalBlocks = blkcnt;
bcopy(extentptr, recp->forkData.theFork.extents, sizeof(HFSPlusExtentRecord));
-
- (void) hfs_buildattrkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
-
+
+ (void) hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
+
result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
if (result) {
-#if HFS_XATTR_VERBOSE
- printf("hfs_setxattr: BTInsertRecord err (%d) %s:%s\n",
- MacToVFSError(result), vnode_name(vp) ? vnode_name(vp) : "", ap->a_name);
-#endif
+ printf ("hfs_setxattr: BTInsertRecord(): vol=%s %d,%s err=%d\n",
+ hfsmp->vcbVN, target_id, ap->a_name, result);
goto exit;
}
extentblks = count_extent_blocks(blkcnt, recp->forkData.theFork.extents);
blkcnt -= extentblks;
keystartblk = &((HFSPlusAttrKey *)&iterator->key)->startBlock;
i = 0;
-
+
/* Create overflow extents as needed. */
while (blkcnt > 0) {
/* Initialize the key and record. */
btdata.itemSize = sizeof(HFSPlusAttrExtents);
recp->recordType = kHFSPlusAttrExtents;
recp->overflowExtents.reserved = 0;
-
+
/* Copy the next set of extents. */
i += kHFSPlusExtentDensity;
bcopy(&extentptr[i], recp->overflowExtents.extents, sizeof(HFSPlusExtentRecord));
-
+
result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
if (result) {
- printf("hfs_setxattr: BTInsertRecord err (%d) %s:%s\n",
- MacToVFSError(result), vnode_name(vp) ? vnode_name(vp) : "", ap->a_name);
+ printf ("hfs_setxattr: BTInsertRecord() overflow: vol=%s %d,%s err=%d\n",
+ hfsmp->vcbVN, target_id, ap->a_name, result);
goto exit;
}
extentblks = count_extent_blocks(blkcnt, recp->overflowExtents.extents);
blkcnt -= extentblks;
}
- } else /* Inline data */ {
+ } else { /* Inline data */
if (exists) {
result = remove_attribute_records(hfsmp, iterator);
if (result) {
goto exit;
}
}
-
+
/* Calculate size of record rounded up to multiple of 2 bytes. */
btdata.itemSize = sizeof(HFSPlusAttrData) - 2 + attrsize + ((attrsize & 1) ? 1 : 0);
MALLOC(recp, HFSPlusAttrRecord *, btdata.itemSize, M_TEMP, M_WAITOK);
recp->attrData.reserved[0] = 0;
recp->attrData.reserved[1] = 0;
recp->attrData.attrSize = attrsize;
-
+
/* Copy in the attribute data (if any). */
if (attrsize > 0) {
- if (user_data_ptr)
- bcopy(user_data_ptr, &recp->attrData.attrData, attrsize);
- else
+ if (data_ptr) {
+ bcopy(data_ptr, &recp->attrData.attrData, attrsize);
+ } else {
+ /*
+ * A null UIO meant it originated in-kernel. If they didn't supply data_ptr
+ * then deny the copy operation.
+ */
+ if (uio == NULL) {
+ result = EPERM;
+ goto exit;
+ }
result = uiomove((caddr_t)&recp->attrData.attrData, attrsize, uio);
+ }
+
if (result) {
goto exit;
}
}
-
- (void) hfs_buildattrkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
-
+
+ (void) hfs_buildattrkey(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
+
btdata.bufferAddress = recp;
btdata.itemCount = 1;
result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
}
+
exit:
if (btfile && started_transaction) {
(void) BTFlushPath(btfile);
}
- if (lockflags) {
- hfs_systemfile_unlock(hfsmp, lockflags);
- }
+ hfs_systemfile_unlock(hfsmp, lockflags);
if (result == 0) {
- cp = VTOC(vp);
- /* Setting an attribute only updates change time and not
- * modified time of the file.
- */
- cp->c_touch_chgtime = TRUE;
- cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
- if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) {
- cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
+ if (vp) {
+ cp = VTOC(vp);
+ /* Setting an attribute only updates change time and not
+ * modified time of the file.
+ */
+ cp->c_touch_chgtime = TRUE;
+ cp->c_flag |= C_MODIFIED;
+ cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
+ if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) {
+ cp->c_attr.ca_recflags |= kHFSHasSecurityMask;
+ }
+ (void) hfs_update(vp, 0);
}
- (void) hfs_update(vp, 0);
}
if (started_transaction) {
if (result && allocatedblks) {
}
hfs_end_transaction(hfsmp);
}
- if (cp) {
- hfs_unlock(cp);
- }
- if (result == 0) {
- HFS_KNOTE(vp, NOTE_ATTRIB);
- }
- if (user_data_ptr) {
- FREE(user_data_ptr, M_TEMP);
- }
+
if (recp) {
FREE(recp, M_TEMP);
}
if (iterator) {
FREE(iterator, M_TEMP);
}
- return (result == btNotFound ? ENOATTR : MacToVFSError(result));
+
+ return result;
}
+
+
+
/*
* Remove an extended attribute.
*/
-__private_extern__
int
hfs_vnop_removexattr(struct vnop_removexattr_args *ap)
/*
struct BTreeIterator * iterator = NULL;
int lockflags;
int result;
+ time_t orig_ctime=VTOC(vp)->c_ctime;
if (ap->a_name == NULL || ap->a_name[0] == '\0') {
return (EINVAL); /* invalid name */
return (EPERM);
}
+#if HFS_COMPRESSION
+ if (hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1) && !(ap->a_options & XATTR_SHOWCOMPRESSION)) {
+ return ENOATTR;
+ }
+#endif /* HFS_COMPRESSION */
+
+ check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_DELETE_OP, NSPACE_REARM_NO_ARG);
+
/* If Resource Fork is non-empty then truncate it. */
if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
struct vnode *rvp = NULL;
if ( !vnode_isreg(vp) ) {
return (EPERM);
}
- if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
+ if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
return (result);
}
- if ( !RSRC_FORK_EXISTS(cp)) {
+ if (!hfs_has_rsrc(cp)) {
hfs_unlock(cp);
return (ENOATTR);
}
- result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE);
+ result = hfs_vgetrsrc(hfsmp, vp, &rvp);
hfs_unlock(cp);
if (result) {
return (result);
}
- hfs_lock_truncate(VTOC(rvp), TRUE);
- if ((result = hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK))) {
- hfs_unlock_truncate(cp, TRUE);
+ hfs_lock_truncate(VTOC(rvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
+
+ // Tell UBC now before we take the cnode lock and start the transaction
+ hfs_ubc_setsize(rvp, 0, false);
+
+ if ((result = hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
+ hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
vnode_put(rvp);
return (result);
}
* hfs_truncate() and hfs_update()
*/
if ((result = hfs_start_transaction(hfsmp))) {
- hfs_unlock_truncate(cp, TRUE);
+ hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
hfs_unlock(cp);
vnode_put(rvp);
return (result);
if (result == 0) {
cp->c_touch_chgtime = TRUE;
cp->c_flag |= C_MODIFIED;
- result = hfs_update(vp, FALSE);
+ result = hfs_update(vp, 0);
}
hfs_end_transaction(hfsmp);
- hfs_unlock_truncate(VTOC(rvp), TRUE);
+ hfs_unlock_truncate(VTOC(rvp), HFS_LOCK_DEFAULT);
hfs_unlock(VTOC(rvp));
vnode_put(rvp);
if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
void * finderinfo_start;
int finderinfo_size;
-
- if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
+ u_int8_t finderinfo[32];
+ u_int32_t date_added, write_gen_counter, document_id;
+ u_int8_t *finfo = NULL;
+
+ if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
return (result);
}
-
- /* Symlink's don't have an external type/creator. */
+
+ /* Use the local copy to store our temporary changes. */
+ bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
+
+
+ /* Zero out the date added field in the local copy */
+ hfs_zero_hidden_fields (cp, finderinfo);
+
+ /* Don't expose a symlink's private type/creator. */
if (vnode_islnk(vp)) {
- /* Skip over type/creator fields. */
+ struct FndrFileInfo *fip;
+
+ fip = (struct FndrFileInfo *)&finderinfo;
+ fip->fdType = 0;
+ fip->fdCreator = 0;
+ }
+
+ /* Do the byte compare against the local copy */
+ if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) {
+ hfs_unlock(cp);
+ return (ENOATTR);
+ }
+
+ /*
+ * If there was other content, zero out everything except
+ * type/creator and date added. First, save the date added.
+ */
+ finfo = cp->c_finderinfo;
+ finfo = finfo + 16;
+ if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
+ struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
+ date_added = extinfo->date_added;
+ write_gen_counter = extinfo->write_gen_counter;
+ document_id = extinfo->document_id;
+ } else if (S_ISDIR(cp->c_attr.ca_mode)) {
+ struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
+ date_added = extinfo->date_added;
+ write_gen_counter = extinfo->write_gen_counter;
+ document_id = extinfo->document_id;
+ }
+
+ if (vnode_islnk(vp)) {
+ /* Ignore type/creator */
finderinfo_start = &cp->c_finderinfo[8];
finderinfo_size = sizeof(cp->c_finderinfo) - 8;
} else {
finderinfo_start = &cp->c_finderinfo[0];
finderinfo_size = sizeof(cp->c_finderinfo);
}
- if (bcmp(finderinfo_start, emptyfinfo, finderinfo_size) == 0) {
- hfs_unlock(cp);
- return (ENOATTR);
- }
-
bzero(finderinfo_start, finderinfo_size);
-
+
+
+ /* Now restore the date added */
+ if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
+ struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
+ extinfo->date_added = date_added;
+ extinfo->write_gen_counter = write_gen_counter;
+ extinfo->document_id = document_id;
+ } else if (S_ISDIR(cp->c_attr.ca_mode)) {
+ struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
+ extinfo->date_added = date_added;
+ extinfo->write_gen_counter = write_gen_counter;
+ extinfo->document_id = document_id;
+ }
+
/* Updating finderInfo updates change time and modified time */
cp->c_touch_chgtime = TRUE;
cp->c_flag |= C_MODIFIED;
- hfs_update(vp, FALSE);
-
+ hfs_update(vp, 0);
+
hfs_unlock(cp);
-
+
return (0);
}
/*
}
bzero(iterator, sizeof(*iterator));
- if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
- return (result);
+ if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
+ goto exit_nolock;
}
result = hfs_buildattrkey(cp->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
result = file_attribute_exist(hfsmp, cp->c_fileid);
if (result == 0) {
cp->c_attr.ca_recflags &= ~kHFSHasAttributesMask;
+ cp->c_flag |= C_MODIFIED;
}
if (result == EEXIST) {
result = 0;
/* If ACL was removed, clear security bit */
if ((bcmp(ap->a_name, KAUTH_FILESEC_XATTR, sizeof(KAUTH_FILESEC_XATTR)) == 0)) {
cp->c_attr.ca_recflags &= ~kHFSHasSecurityMask;
+ cp->c_flag |= C_MODIFIED;
}
(void) hfs_update(vp, 0);
}
hfs_end_transaction(hfsmp);
exit:
hfs_unlock(cp);
- if (result == 0) {
- HFS_KNOTE(vp, NOTE_ATTRIB);
- }
+exit_nolock:
FREE(iterator, M_TEMP);
return MacToVFSError(result);
}
* - The Allocation Bitmap file must be locked exclusive.
* - The iterator key must be initialized.
*/
-static int
+int
remove_attribute_records(struct hfsmount *hfsmp, BTreeIterator * iterator)
{
struct filefork *btfile;
int extentblks;
u_int32_t *keystartblk;
-#if HFS_XATTR_VERBOSE
if (datasize < sizeof(HFSPlusAttrForkData)) {
- printf("remove_attribute_records: bad record size %d (expecting %d)\n", datasize, sizeof(HFSPlusAttrForkData));
+ printf("hfs: remove_attribute_records: bad record size %d (expecting %lu)\n", datasize, sizeof(HFSPlusAttrForkData));
}
-#endif
totalblks = attrdata.forkData.theFork.totalBlocks;
/* Process the first 8 extents. */
extentblks = count_extent_blocks(totalblks, attrdata.forkData.theFork.extents);
if (extentblks > totalblks)
- panic("remove_attribute_records: corruption...");
+ panic("hfs: remove_attribute_records: corruption...");
if (BTDeleteRecord(btfile, iterator) == 0) {
free_attr_blks(hfsmp, extentblks, attrdata.forkData.theFork.extents);
}
if (result ||
(attrdata.recordType != kHFSPlusAttrExtents) ||
(datasize < sizeof(HFSPlusAttrExtents))) {
- printf("remove_attribute_records: BTSearchRecord %d (%d), totalblks %d\n",
- MacToVFSError(result), attrdata.recordType != kHFSPlusAttrExtents, totalblks);
+ printf("hfs: remove_attribute_records: BTSearchRecord: vol=%s, err=%d (%d), totalblks %d\n",
+ hfsmp->vcbVN, MacToVFSError(result), attrdata.recordType != kHFSPlusAttrExtents, totalblks);
result = ENOATTR;
break; /* break from while */
}
/* Process the next 8 extents. */
extentblks = count_extent_blocks(totalblks, attrdata.overflowExtents.extents);
if (extentblks > totalblks)
- panic("remove_attribute_records: corruption...");
+ panic("hfs: remove_attribute_records: corruption...");
if (BTDeleteRecord(btfile, iterator) == 0) {
free_attr_blks(hfsmp, extentblks, attrdata.overflowExtents.extents);
}
/*
* Retrieve the list of extended attribute names.
*/
-__private_extern__
int
hfs_vnop_listxattr(struct vnop_listxattr_args *ap)
/*
struct BTreeIterator * iterator = NULL;
struct filefork *btfile;
struct listattr_callback_state state;
- void * finderinfo_start;
- int finderinfo_size;
user_addr_t user_start = 0;
user_size_t user_len = 0;
int lockflags;
int result;
+ u_int8_t finderinfo[32];
+
if (VNODE_IS_RSRC(vp)) {
return (EPERM);
}
+
+#if HFS_COMPRESSION
+ int compressed = hfs_file_is_compressed(cp, 1); /* 1 == don't take the cnode lock */
+#endif /* HFS_COMPRESSION */
+
hfsmp = VTOHFS(vp);
*ap->a_size = 0;
+
+ /*
+ * Take the truncate lock; this serializes us against the ioctl
+ * to truncate data & reset the decmpfs state
+ * in the compressed file handler.
+ */
+ hfs_lock_truncate(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
- if ((result = hfs_lock(cp, HFS_SHARED_LOCK))) {
+ /* Now the regular cnode lock (shared) */
+ if ((result = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
+ hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
return (result);
}
+ /*
+ * Make a copy of the cnode's finderinfo to a local so we can
+ * zero out the date added field. Also zero out the private type/creator
+ * for symlinks.
+ */
+ bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
+ hfs_zero_hidden_fields (cp, finderinfo);
+
/* Don't expose a symlink's private type/creator. */
if (vnode_islnk(vp)) {
- /* Skip over type/creator fields. */
- finderinfo_start = &cp->c_finderinfo[8];
- finderinfo_size = sizeof(cp->c_finderinfo) - 8;
- } else {
- finderinfo_start = &cp->c_finderinfo[0];
- finderinfo_size = sizeof(cp->c_finderinfo);
- }
+ struct FndrFileInfo *fip;
+
+ fip = (struct FndrFileInfo *)&finderinfo;
+ fip->fdType = 0;
+ fip->fdCreator = 0;
+ }
+
+
/* If Finder Info is non-empty then export it's name. */
- if (bcmp(finderinfo_start, emptyfinfo, finderinfo_size) != 0) {
+ if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) != 0) {
if (uio == NULL) {
*ap->a_size += sizeof(XATTR_FINDERINFO_NAME);
- } else if (uio_resid(uio) < sizeof(XATTR_FINDERINFO_NAME)) {
+ } else if ((user_size_t)uio_resid(uio) < sizeof(XATTR_FINDERINFO_NAME)) {
result = ERANGE;
goto exit;
} else {
}
}
/* If Resource Fork is non-empty then export it's name. */
- if (S_ISREG(cp->c_mode) && RSRC_FORK_EXISTS(cp)) {
- if (uio == NULL) {
- *ap->a_size += sizeof(XATTR_RESOURCEFORK_NAME);
- } else if (uio_resid(uio) < sizeof(XATTR_RESOURCEFORK_NAME)) {
- result = ERANGE;
- goto exit;
- } else {
- result = uiomove(XATTR_RESOURCEFORK_NAME,
- sizeof(XATTR_RESOURCEFORK_NAME), uio);
- if (result)
+ if (S_ISREG(cp->c_mode) && hfs_has_rsrc(cp)) {
+#if HFS_COMPRESSION
+ if ((ap->a_options & XATTR_SHOWCOMPRESSION) ||
+ !compressed ||
+ !decmpfs_hides_rsrc(ap->a_context, VTOCMP(vp))
+ )
+#endif /* HFS_COMPRESSION */
+ {
+ if (uio == NULL) {
+ *ap->a_size += sizeof(XATTR_RESOURCEFORK_NAME);
+ } else if ((user_size_t)uio_resid(uio) < sizeof(XATTR_RESOURCEFORK_NAME)) {
+ result = ERANGE;
goto exit;
+ } else {
+ result = uiomove(XATTR_RESOURCEFORK_NAME,
+ sizeof(XATTR_RESOURCEFORK_NAME), uio);
+ if (result)
+ goto exit;
+ }
}
}
/*
state.result = 0;
state.uio = uio;
state.size = 0;
+#if HFS_COMPRESSION
+ state.showcompressed = !compressed || ap->a_options & XATTR_SHOWCOMPRESSION;
+ state.ctx = ap->a_context;
+ state.vp = vp;
+#endif /* HFS_COMPRESSION */
/*
* Process entries starting just after iterator->key.
if (user_start) {
vsunlock(user_start, user_len, TRUE);
}
- FREE(iterator, M_TEMP);
-
+ if (iterator) {
+ FREE(iterator, M_TEMP);
+ }
hfs_unlock(cp);
+ hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
return MacToVFSError(result);
}
/*
- * Callback - called for each attribute
+ * Callback - called for each attribute record
*/
static int
listattr_callback(const HFSPlusAttrKey *key, __unused const HFSPlusAttrData *data, struct listattr_callback_state *state)
{
char attrname[XATTR_MAXNAMELEN + 1];
- size_t bytecount;
+ ssize_t bytecount;
int result;
if (state->fileID != key->fileID) {
/* Convert the attribute name into UTF-8. */
result = utf8_encodestr(key->attrName, key->attrNameLen * sizeof(UniChar),
- (u_int8_t *)attrname, &bytecount, sizeof(attrname), '/', 0);
+ (u_int8_t *)attrname, (size_t *)&bytecount, sizeof(attrname), '/', 0);
if (result) {
state->result = result;
return (0); /* stop */
if (xattr_protected(attrname))
return (1); /* continue */
+#if HFS_COMPRESSION
+ if (!state->showcompressed && decmpfs_hides_xattr(state->ctx, VTOCMP(state->vp), attrname) )
+ return 1; /* continue */
+#endif /* HFS_COMPRESSION */
+
if (state->uio == NULL) {
state->size += bytecount;
} else {
*
* This function takes the necessary locks on the attribute
* b-tree file and the allocation (bitmap) file.
+ *
+ * NOTE: Upon sucecss, this function will return with an open
+ * transaction. The reason we do it this way is because when we
+ * delete the last attribute, we must make sure the flag in the
+ * catalog record that indicates there are no more records is cleared.
+ * The caller is responsible for doing this and *must* do it before
+ * ending the transaction.
*/
-__private_extern__
int
-hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid)
+hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid,
+ bool *open_transaction)
{
- BTreeIterator *iterator;
+ BTreeIterator *iterator = NULL;
HFSPlusAttrKey *key;
struct filefork *btfile;
- int result, lockflags;
+ int result, lockflags = 0;
+
+ *open_transaction = false;
+
+ if (hfsmp->hfs_attribute_vp == NULL)
+ return 0;
- if (hfsmp->hfs_attribute_vp == NULL) {
- return (0);
- }
btfile = VTOF(hfsmp->hfs_attribute_vp);
MALLOC(iterator, BTreeIterator *, sizeof(BTreeIterator), M_TEMP, M_WAITOK);
+ if (iterator == NULL) {
+ return (ENOMEM);
+ }
bzero(iterator, sizeof(BTreeIterator));
key = (HFSPlusAttrKey *)&iterator->key;
/* Loop until there are no more attributes for this file id */
- for(;;) {
+ do {
+ if (!*open_transaction)
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
+
+ (void) hfs_buildattrkey(fileid, NULL, key);
+ result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL);
+ if (result || key->fileID != fileid)
+ goto exit;
+
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ lockflags = 0;
+
+ if (*open_transaction) {
+ hfs_end_transaction(hfsmp);
+ *open_transaction = false;
+ }
+
if (hfs_start_transaction(hfsmp) != 0) {
result = EINVAL;
goto exit;
}
- /* Lock the attribute b-tree and the allocation (bitmap) files */
+ *open_transaction = true;
+
lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
- /*
- * Go to first possible attribute key/record pair
- */
- (void) hfs_buildattrkey(fileid, NULL, key);
- result = BTIterateRecord(btfile, kBTreeNextRecord, iterator, NULL, NULL);
- if (result || key->fileID != fileid) {
- hfs_systemfile_unlock(hfsmp, lockflags);
- hfs_end_transaction(hfsmp);
- goto exit;
- }
result = remove_attribute_records(hfsmp, iterator);
#if HFS_XATTR_VERBOSE
printf("hfs_removeallattr: unexpected err %d\n", result);
}
#endif
+ } while (!result);
+
+exit:
+ FREE(iterator, M_TEMP);
+
+ if (lockflags)
hfs_systemfile_unlock(hfsmp, lockflags);
+
+ result = result == btNotFound ? 0 : MacToVFSError(result);
+
+ if (result && *open_transaction) {
hfs_end_transaction(hfsmp);
+ *open_transaction = false;
}
-exit:
- FREE(iterator, M_TEMP);
- return (result == btNotFound ? 0: MacToVFSError(result));
+
+ return result;
}
__private_extern__
/*
* Enable/Disable volume attributes stored as EA for root file system.
* Supported attributes are -
- * 1. ACLs
- * 2. Extent-based Extended Attributes
+ * 1. Extent-based Extended Attributes
*/
-__private_extern__
int
hfs_set_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype, int state)
{
if (hfsmp->hfs_flags & HFS_STANDARD) {
return (ENOTSUP);
}
+ if (xattrtype != HFS_SET_XATTREXTENTS_STATE) {
+ return EINVAL;
+ }
/*
* If there isn't an attributes b-tree then create one.
* Build a b-tree key.
* We use the root's parent id (1) to hold this volume attribute.
*/
- if (xattrtype == HFS_SETACLSTATE) {
- /* ACL */
- (void) hfs_buildattrkey(kHFSRootParentID, XATTR_EXTENDEDSECURITY_NAME,
- (HFSPlusAttrKey *)&iterator->key);
- } else if (xattrtype == HFS_SET_XATTREXTENTS_STATE) {
- /* Extent-based extended attributes */
- (void) hfs_buildattrkey(kHFSRootParentID, XATTR_XATTREXTENTS_NAME,
- (HFSPlusAttrKey *)&iterator->key);
- } else {
- result = EINVAL;
- goto exit;
- }
+ (void) hfs_buildattrkey(kHFSRootParentID, XATTR_XATTREXTENTS_NAME,
+ (HFSPlusAttrKey *)&iterator->key);
/* Start a transaction for our changes. */
if (hfs_start_transaction(hfsmp) != 0) {
/* Finish the transaction of our changes. */
hfs_end_transaction(hfsmp);
-exit:
- if (iterator) {
- FREE(iterator, M_TEMP);
- }
- if (result == 0) {
- if (xattrtype == HFS_SETACLSTATE) {
- if (state == 0) {
- vfs_clearextendedsecurity(HFSTOVFS(hfsmp));
- } else {
- vfs_setextendedsecurity(HFSTOVFS(hfsmp));
- }
- } else {
- /* HFS_SET_XATTREXTENTS_STATE */
- HFS_MOUNT_LOCK(hfsmp, TRUE);
- if (state == 0) {
- hfsmp->hfs_flags &= ~HFS_XATTR_EXTENTS;
- } else {
- hfsmp->hfs_flags |= HFS_XATTR_EXTENTS;
- }
- HFS_MOUNT_UNLOCK(hfsmp, TRUE);
- }
- }
-
- return MacToVFSError(result);
-}
-
- /*
- * Check for volume attributes stored as EA for root file system.
- * Supported attributes are -
- * 1. ACLs
- * 2. Extent-based Extended Attributes
- */
-__private_extern__
-void
-hfs_check_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype)
-{
- struct BTreeIterator * iterator;
- struct filefork *btfile;
- int lockflags;
- int result;
-
- if (hfsmp->hfs_flags & HFS_STANDARD ||
- hfsmp->hfs_attribute_vp == NULL) {
- return;
- }
-
- MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
- if (iterator == NULL) {
- return;
- }
- bzero(iterator, sizeof(*iterator));
-
- /*
- * Build a b-tree key.
- * We use the root's parent id (1) to hold this volume attribute.
- */
- if (xattrtype == HFS_SETACLSTATE) {
- /* ACLs */
- (void) hfs_buildattrkey(kHFSRootParentID, XATTR_EXTENDEDSECURITY_NAME,
- (HFSPlusAttrKey *)&iterator->key);
+ /* Update the state in the mount point */
+ hfs_lock_mount (hfsmp);
+ if (state == 0) {
+ hfsmp->hfs_flags &= ~HFS_XATTR_EXTENTS;
} else {
- /* Extent-based extended attributes */
- (void) hfs_buildattrkey(kHFSRootParentID, XATTR_XATTREXTENTS_NAME,
- (HFSPlusAttrKey *)&iterator->key);
+ hfsmp->hfs_flags |= HFS_XATTR_EXTENTS;
}
- btfile = VTOF(hfsmp->hfs_attribute_vp);
-
- lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
+ hfs_unlock_mount (hfsmp);
- /* Check for our attribute. */
- result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
-
- hfs_systemfile_unlock(hfsmp, lockflags);
- FREE(iterator, M_TEMP);
-
- if (result == 0) {
- if (xattrtype == HFS_SETACLSTATE) {
- vfs_setextendedsecurity(HFSTOVFS(hfsmp));
- } else {
- HFS_MOUNT_LOCK(hfsmp, TRUE);
- hfsmp->hfs_flags |= HFS_XATTR_EXTENTS;
- HFS_MOUNT_UNLOCK(hfsmp, TRUE);
- }
+exit:
+ if (iterator) {
+ FREE(iterator, M_TEMP);
}
+ return MacToVFSError(result);
}
/*
* Names are equal; compare startBlock
*/
- if (searchKey->startBlock == trialKey->startBlock)
+ if (searchKey->startBlock == trialKey->startBlock) {
return (0);
- else
+ } else {
return (searchKey->startBlock < trialKey->startBlock ? -1 : 1);
}
+ }
return result;
}
size_t maxsize;
if (attrvp != NULL) {
- (void) hfs_lock(VTOC(attrvp), HFS_SHARED_LOCK);
+ (void) hfs_lock(VTOC(attrvp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
if (BTGetInformation(VTOF(attrvp), 0, &btinfo) == 0)
nodesize = btinfo.nodeSize;
hfs_unlock(VTOC(attrvp));
}
/*
- * Get a referenced vnode for attribute data I/O.
+ * Initialize vnode for attribute data I/O.
+ *
+ * On success,
+ * - returns zero
+ * - the attrdata vnode is initialized as hfsmp->hfs_attrdata_vp
+ * - an iocount is taken on the attrdata vnode which exists
+ * for the entire duration of the mount. It is only dropped
+ * during unmount
+ * - the attrdata cnode is not locked
+ *
+ * On failure,
+ * - returns non-zero value
+ * - the caller does not have to worry about any locks or references
*/
-static int
-get_attr_data_vnode(struct hfsmount *hfsmp, vnode_t *vpp)
+int init_attrdata_vnode(struct hfsmount *hfsmp)
{
vnode_t vp;
int result = 0;
+ struct cat_desc cat_desc;
+ struct cat_attr cat_attr;
+ struct cat_fork cat_fork;
+ int newvnode_flags = 0;
+
+ bzero(&cat_desc, sizeof(cat_desc));
+ cat_desc.cd_parentcnid = kHFSRootParentID;
+ cat_desc.cd_nameptr = (const u_int8_t *)hfs_attrdatafilename;
+ cat_desc.cd_namelen = strlen(hfs_attrdatafilename);
+ cat_desc.cd_cnid = kHFSAttributeDataFileID;
+ /* Tag vnode as system file, note that we can still use cluster I/O */
+ cat_desc.cd_flags |= CD_ISMETA;
+
+ bzero(&cat_attr, sizeof(cat_attr));
+ cat_attr.ca_linkcount = 1;
+ cat_attr.ca_mode = S_IFREG;
+ cat_attr.ca_fileid = cat_desc.cd_cnid;
+ cat_attr.ca_blocks = hfsmp->totalBlocks;
- vp = hfsmp->hfs_attrdata_vp;
- if (vp == NULLVP) {
- struct cat_desc cat_desc;
- struct cat_attr cat_attr;
- struct cat_fork cat_fork;
-
- /* We don't tag it as a system file since we intend to use cluster I/O. */
- bzero(&cat_desc, sizeof(cat_desc));
- cat_desc.cd_parentcnid = kHFSRootParentID;
- cat_desc.cd_nameptr = (const u_int8_t *)hfs_attrdatafilename;
- cat_desc.cd_namelen = strlen(hfs_attrdatafilename);
- cat_desc.cd_cnid = kHFSAttributeDataFileID;
-
- bzero(&cat_attr, sizeof(cat_attr));
- cat_attr.ca_linkcount = 1;
- cat_attr.ca_mode = S_IFREG;
- cat_attr.ca_fileid = cat_desc.cd_cnid;
- cat_attr.ca_blocks = hfsmp->totalBlocks;
-
- /*
- * The attribute data file is a virtual file that spans the
- * entire file system space.
- *
- * Each extent-based attribute occupies a unique portion of
- * in this virtual file. The cluster I/O is done using actual
- * allocation block offsets so no additional mapping is needed
- * for the VNOP_BLOCKMAP call.
- *
- * This approach allows the attribute data to be cached without
- * incurring the high cost of using a separate vnode per attribute.
- *
- * Since we need to acquire the attribute b-tree file lock anyways,
- * the virtual file doesn't introduce any additional serialization.
- */
- bzero(&cat_fork, sizeof(cat_fork));
- cat_fork.cf_size = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize;
- cat_fork.cf_blocks = hfsmp->totalBlocks;
- cat_fork.cf_extents[0].startBlock = 0;
- cat_fork.cf_extents[0].blockCount = cat_fork.cf_blocks;
-
- result = hfs_getnewvnode(hfsmp, NULL, NULL, &cat_desc, 0, &cat_attr, &cat_fork, &vp);
- if (result == 0) {
- HFS_MOUNT_LOCK(hfsmp, 1);
- /* Check if someone raced us for creating this vnode. */
- if (hfsmp->hfs_attrdata_vp != NULLVP) {
- HFS_MOUNT_UNLOCK(hfsmp, 1);
- vnode_put(vp);
- vnode_recycle(vp);
- vp = hfsmp->hfs_attrdata_vp;
- } else {
- hfsmp->hfs_attrdata_vp = vp;
- HFS_MOUNT_UNLOCK(hfsmp, 1);
- /* Keep a reference on this vnode until unmount */
- vnode_ref_ext(vp, O_EVTONLY);
- hfs_unlock(VTOC(vp));
- }
- }
- } else {
- if ((result = vnode_get(vp)))
- vp = NULLVP;
+ /*
+ * The attribute data file is a virtual file that spans the
+ * entire file system space.
+ *
+ * Each extent-based attribute occupies a unique portion of
+ * in this virtual file. The cluster I/O is done using actual
+ * allocation block offsets so no additional mapping is needed
+ * for the VNOP_BLOCKMAP call.
+ *
+ * This approach allows the attribute data to be cached without
+ * incurring the high cost of using a separate vnode per attribute.
+ *
+ * Since we need to acquire the attribute b-tree file lock anyways,
+ * the virtual file doesn't introduce any additional serialization.
+ */
+ bzero(&cat_fork, sizeof(cat_fork));
+ cat_fork.cf_size = (u_int64_t)hfsmp->totalBlocks * (u_int64_t)hfsmp->blockSize;
+ cat_fork.cf_blocks = hfsmp->totalBlocks;
+ cat_fork.cf_extents[0].startBlock = 0;
+ cat_fork.cf_extents[0].blockCount = cat_fork.cf_blocks;
+
+ result = hfs_getnewvnode(hfsmp, NULL, NULL, &cat_desc, 0, &cat_attr,
+ &cat_fork, &vp, &newvnode_flags);
+ if (result == 0) {
+ hfsmp->hfs_attrdata_vp = vp;
+ hfs_unlock(VTOC(vp));
}
- *vpp = vp;
return (result);
}
static int
read_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents)
{
- vnode_t evp = NULLVP;
+ vnode_t evp = hfsmp->hfs_attrdata_vp;
int bufsize;
- int iosize;
+ int64_t iosize;
int attrsize;
int blksize;
int i;
int result = 0;
- if ((result = get_attr_data_vnode(hfsmp, &evp))) {
- return (result);
- }
- hfs_lock_truncate(VTOC(evp), 0);
+ hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
bufsize = (int)uio_resid(uio);
attrsize = (int)datasize;
* For the typical case there is only one extent.
*/
for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) {
- iosize = (int)extents[i].blockCount * blksize;
+ iosize = extents[i].blockCount * blksize;
iosize = MIN(iosize, attrsize);
iosize = MIN(iosize, bufsize);
uio_setresid(uio, iosize);
result = cluster_read(evp, uio, VTOF(evp)->ff_size, IO_SYNC | IO_UNIT);
#if HFS_XATTR_VERBOSE
- printf("read_attr_data: cr iosize %d [%d, %d] (%d)\n",
+ printf("hfs: read_attr_data: cr iosize %lld [%d, %d] (%d)\n",
iosize, extents[i].startBlock, extents[i].blockCount, result);
#endif
if (result)
uio_setresid(uio, bufsize);
uio_setoffset(uio, datasize);
- hfs_unlock_truncate(VTOC(evp), 0);
- vnode_put(evp);
+ hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT);
return (result);
}
static int
write_attr_data(struct hfsmount *hfsmp, uio_t uio, size_t datasize, HFSPlusExtentDescriptor *extents)
{
- vnode_t evp = NULLVP;
+ vnode_t evp = hfsmp->hfs_attrdata_vp;
off_t filesize;
int bufsize;
int attrsize;
- int iosize;
+ int64_t iosize;
int blksize;
int i;
int result = 0;
- /* Get exclusive use of attribute data vnode. */
- if ((result = get_attr_data_vnode(hfsmp, &evp))) {
- return (result);
- }
- hfs_lock_truncate(VTOC(evp), 0);
+ hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
bufsize = uio_resid(uio);
attrsize = (int) datasize;
* Write the attribute data one extent at a time.
*/
for (i = 0; (attrsize > 0) && (bufsize > 0) && (extents[i].startBlock != 0); ++i) {
- iosize = (int)extents[i].blockCount * blksize;
+ iosize = extents[i].blockCount * blksize;
iosize = MIN(iosize, attrsize);
iosize = MIN(iosize, bufsize);
uio_setresid(uio, iosize);
result = cluster_write(evp, uio, filesize, filesize, filesize,
(off_t) 0, IO_SYNC | IO_UNIT);
#if HFS_XATTR_VERBOSE
- printf("write_attr_data: cw iosize %d [%d, %d] (%d)\n",
+ printf("hfs: write_attr_data: cw iosize %lld [%d, %d] (%d)\n",
iosize, extents[i].startBlock, extents[i].blockCount, result);
#endif
if (result)
uio_setresid(uio, bufsize);
uio_setoffset(uio, datasize);
- hfs_unlock_truncate(VTOC(evp), 0);
- vnode_put(evp);
+ hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT);
return (result);
}
lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
for (i = 0; (blkcnt > 0) && (i < maxextents); i++) {
- result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, 0, 0,
+ /* Try allocating and see if we find something decent */
+ result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, 0,
&extents[i].startBlock, &extents[i].blockCount);
+ /*
+ * If we couldn't find anything, then re-try the allocation but allow
+ * journal flushes.
+ */
+ if (result == dskFulErr) {
+ result = BlockAllocate(hfsmp, startblk, blkcnt, blkcnt, HFS_ALLOC_FLUSHTXN,
+ &extents[i].startBlock, &extents[i].blockCount);
+ }
+
+
#if HFS_XATTR_VERBOSE
- printf("alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
+ printf("hfs: alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
blkcnt, extents[i].startBlock, extents[i].blockCount, result);
#endif
if (result) {
result = ENOSPC;
#if HFS_XATTR_VERBOSE
- printf("alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt);
+ printf("hfs: alloc_attr_blks: unexpected failure, %d blocks unallocated\n", blkcnt);
#endif
- for (; i <= 0; i--) {
+ for (; i >= 0; i--) {
if ((blkcnt = extents[i].blockCount) != 0) {
- (void) BlockDeallocate(hfsmp, extents[i].startBlock, blkcnt);
+ (void) BlockDeallocate(hfsmp, extents[i].startBlock, blkcnt, 0);
extents[i].startBlock = 0;
extents[i].blockCount = 0;
}
static void
free_attr_blks(struct hfsmount *hfsmp, int blkcnt, HFSPlusExtentDescriptor *extents)
{
- vnode_t evp = NULLVP;
+ vnode_t evp = hfsmp->hfs_attrdata_vp;
int remblks = blkcnt;
int lockflags;
int i;
- if (get_attr_data_vnode(hfsmp, &evp) != 0) {
- evp = NULLVP;
- }
lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
for (i = 0; (remblks > 0) && (extents[i].blockCount != 0); i++) {
if (extents[i].blockCount > (u_int32_t)blkcnt) {
#if HFS_XATTR_VERBOSE
- printf("free_attr_blks: skipping bad extent [%d, %d]\n",
+ printf("hfs: free_attr_blks: skipping bad extent [%d, %d]\n",
extents[i].startBlock, extents[i].blockCount);
#endif
extents[i].blockCount = 0;
if (extents[i].startBlock == 0) {
break;
}
- (void)BlockDeallocate(hfsmp, extents[i].startBlock, extents[i].blockCount);
+ (void)BlockDeallocate(hfsmp, extents[i].startBlock, extents[i].blockCount, 0);
+ remblks -= extents[i].blockCount;
extents[i].startBlock = 0;
extents[i].blockCount = 0;
- remblks -= extents[i].blockCount;
#if HFS_XATTR_VERBOSE
- printf("free_attr_blks: BlockDeallocate [%d, %d]\n",
+ printf("hfs: free_attr_blks: BlockDeallocate [%d, %d]\n",
extents[i].startBlock, extents[i].blockCount);
#endif
/* Discard any resident pages for this block range. */
}
hfs_systemfile_unlock(hfsmp, lockflags);
- if (evp) {
- vnode_put(evp);
- }
}
static int
}
return (blocks);
}
+