/*
- * Copyright (c) 2004-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2014 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#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"
#endif /* HFS_COMPRESSION */
};
-#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_dateadded (struct cnode *cp, u_int8_t *finderinfo);
+static int hfs_zero_hidden_fields (struct cnode *cp, u_int8_t *finderinfo);
const char hfs_attrdatafilename[] = "Attribute Data";
#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))) {
+ if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
return (error);
}
- if ((!RSRC_FORK_EXISTS(cp)
+ if ((!hfs_has_rsrc(cp)
#if HFS_COMPRESSION
|| hide_rsrc
#endif /* HFS_COMPRESSION */
hfs_unlock(cp);
return (ENOATTR);
}
- error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp, TRUE, FALSE);
+ error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp);
hfs_unlock(cp);
return (error);
}
}
#endif /* HFS_COMPRESSION */
- if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
+ if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
return (error);
}
- error = hfs_vgetrsrc(VTOHFS(vp), vp, svpp, TRUE, FALSE);
+ 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;
/*
return (ENOATTR);
}
#if HFS_COMPRESSION
- if (hfs_hides_rsrc(ap->a_context, VTOC(svp), 1)) {
+ if (hfs_hides_rsrc(ap->a_context, scp, 1)) {
/* do nothing */
return 0;
}
#endif /* HFS_COMPRESSION */
-
- scp = VTOC(svp);
- /* Take truncate lock before taking cnode lock. */
- hfs_lock_truncate(scp, HFS_EXCLUSIVE_LOCK);
- if ((error = hfs_lock(scp, HFS_EXCLUSIVE_LOCK))) {
- goto out;
- }
- if (VTOF(svp)->ff_size != 0) {
- error = hfs_truncate(svp, 0, IO_NDELAY, 0, 0, ap->a_context);
+ 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, 0);
- 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_dateadded (struct cnode *cp, u_int8_t *finderinfo) {
+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)) {
- struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
- extinfo->date_added = 0;
- }
- else if (S_ISDIR(cp->c_attr.ca_mode)) {
- struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)finfo;
- extinfo->date_added = 0;
- }
- else {
+ 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;
}
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_dateadded (cp, 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)) {
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);
}
namelen = cp->c_desc.cd_namelen;
- if ( !RSRC_FORK_EXISTS(cp)) {
+ if (!hfs_has_rsrc(cp)) {
hfs_unlock(cp);
return (ENOATTR);
}
openunlinked = 1;
}
- result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE, FALSE);
+ result = hfs_vgetrsrc(hfsmp, vp, &rvp);
hfs_unlock(cp);
if (result) {
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);
}
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
*
* 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 hfsmount *hfsmp, u_int32_t fileid)
+{
struct filefork *btfile;
struct BTreeIterator * iterator = NULL;
if (cp) {
target_id = cp->c_fileid;
- }
- else {
+ } else {
target_id = fileid;
}
}
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(target_id, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
if (result) {
goto exit;
* we have extent based EAs.
*/
switch (recp->recordType) {
+
/* Attribute fits in the Attribute B-Tree */
- case kHFSPlusAttrInlineData:
+ 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
* 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",
- target_id, ap->a_name, datasize, sizeof(HFSPlusAttrData));
+ 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 {
+ } 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;
+ }
+
/* Extent-Based EAs */
case kHFSPlusAttrForkData: {
if (datasize < sizeof(HFSPlusAttrForkData)) {
- printf("hfs_getxattr: %d,%s invalid record size %d (expecting %lu)\n",
- target_id, ap->a_name, 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;
}
totalblocks = recp->forkData.theFork.totalBlocks;
/* Ignore bogus block counts. */
- if (totalblocks > HFS_MAXATTRBLKS) {
+ if (totalblocks > howmany(HFS_XATTR_MAXSIZE, hfsmp->blockSize)) {
result = ERANGE;
break;
}
if (blkcnt < totalblocks) {
result = ENOATTR;
- }
- else {
+ } else {
result = read_attr_data(hfsmp, uio, attrlen, extentbuf);
}
FREE(extentbuf, M_TEMP);
- }
- else /* No overflow extents. */ {
+ } else { /* No overflow extents. */
result = read_attr_data(hfsmp, uio, recp->forkData.theFork.logicalSize, recp->forkData.theFork.extents);
}
break;
if (result != 0)
return result;
}
-
- check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_WRITE_OP, NULL);
#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 *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);
}
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);
-
- /* Zero out the date added field to ignore user's attempts to set it */
- hfs_zero_dateadded(cp, finderinfo);
+ 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. */
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);
}
/*
- * Now restore the date added to the finderinfo to be written out.
- * Advance to the 2nd half of the finderinfo to write out the date added
- * into the buffer.
+ * 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)) {
+ 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);
- }
- else if (S_ISDIR(cp->c_attr.ca_mode)) {
+ 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;
* are both 16-bit fields.
*/
fdFlags = *((u_int16_t *) &cp->c_finderinfo[8]);
- if (fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
+ if (fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask)) {
cp->c_bsdflags |= UF_HIDDEN;
- else
+ } else {
cp->c_bsdflags &= ~UF_HIDDEN;
+ }
- result = hfs_update(vp, FALSE);
+ result = hfs_update(vp, 0);
hfs_unlock(cp);
return (result);
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);
openunlinked = 1;
}
- result = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE, FALSE);
+ result = hfs_vgetrsrc(hfsmp, vp, &rvp);
hfs_unlock(cp);
if (result) {
return (result);
vnode_rele(rvp);
}
vnode_recycle (rvp);
- }
- else {
+ } else {
/* cnode is not open-unlinked, so re-lock cnode to sync */
- if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
+ if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
vnode_recycle (rvp);
vnode_put(rvp);
return result;
}
/*
- * 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 &&
}
}
- result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK);
+ result = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
if (result) {
goto exit;
}
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
* 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, caddr_t data_ptr, size_t attrsize,
+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) {
+ u_int32_t fileid)
+{
uio_t uio = ap->a_uio;
struct vnode *vp = ap->a_vp;
int started_transaction = 0;
int exists = 0;
int allocatedblks = 0;
u_int32_t target_id;
- int takelock = 1;
if (cp) {
target_id = cp->c_fileid;
- }
- else {
+ } else {
target_id = fileid;
- if (target_id != 1) {
- /*
- * If we are manipulating something other than
- * the root folder (id 1), and do not have a cnode-in-hand,
- * then we must already hold the requisite b-tree locks from
- * earlier up the call stack. (See hfs_makenode)
- */
- takelock = 0;
- }
}
/* Start a transaction for our changes. */
hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
}
- if (takelock) {
- /* Take exclusive access to the attributes b-tree. */
- lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
- }
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
/* Build the b-tree key. */
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
if (result) {
if (vp) {
const char *name = vnode_getname(vp);
- printf("hfs_setxattr: write_attr_data err (%d) %s:%s\n",
- result, name ? name : "", ap->a_name);
+ 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);
}
if (result) {
if (vp) {
const char *name = vnode_getname(vp);
- printf("hfs_setxattr: remove_attribute_records err (%d) %s:%s\n",
- result, name ? name : "", ap->a_name);
+ 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);
}
result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
if (result) {
- printf ("hfs_setxattr: BTInsertRecord() - %d,%s err=%d\n",
- target_id, ap->a_name, result);
+ 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);
result = BTInsertRecord(btfile, iterator, &btdata, btdata.itemSize);
if (result) {
- printf ("hfs_setxattr: BTInsertRecord() overflow - %d,%s err=%d\n",
- target_id, ap->a_name, result);
+ 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) {
if (attrsize > 0) {
if (data_ptr) {
bcopy(data_ptr, &recp->attrData.attrData, attrsize);
- }
- else {
+ } else {
/*
* A null UIO meant it originated in-kernel. If they didn't supply data_ptr
* then deny the copy operation.
if (btfile && started_transaction) {
(void) BTFlushPath(btfile);
}
- if (lockflags) {
- hfs_systemfile_unlock(hfsmp, lockflags);
- }
+ hfs_systemfile_unlock(hfsmp, lockflags);
if (result == 0) {
if (vp) {
cp = VTOC(vp);
* 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;
if (hfs_hides_xattr(ap->a_context, VTOC(vp), ap->a_name, 1) && !(ap->a_options & XATTR_SHOWCOMPRESSION)) {
return ENOATTR;
}
-
- check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_DELETE_OP, NULL);
#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) {
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, FALSE);
+ result = hfs_vgetrsrc(hfsmp, vp, &rvp);
hfs_unlock(cp);
if (result) {
return (result);
}
- hfs_lock_truncate(VTOC(rvp), HFS_EXCLUSIVE_LOCK);
- if ((result = hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK))) {
- hfs_unlock_truncate(cp, 0);
+ 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, 0);
+ hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
hfs_unlock(cp);
vnode_put(rvp);
return (result);
}
- result = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 0, 0, ap->a_context);
+ result = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 0, ap->a_context);
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), 0);
+ hfs_unlock_truncate(VTOC(rvp), HFS_LOCK_DEFAULT);
hfs_unlock(VTOC(rvp));
vnode_put(rvp);
void * finderinfo_start;
int finderinfo_size;
u_int8_t finderinfo[32];
- u_int32_t date_added;
+ u_int32_t date_added, write_gen_counter, document_id;
u_int8_t *finfo = NULL;
- if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
+ if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
return (result);
}
/* Zero out the date added field in the local copy */
- hfs_zero_dateadded (cp, finderinfo);
+ hfs_zero_hidden_fields (cp, finderinfo);
/* Don't expose a symlink's private type/creator. */
if (vnode_islnk(vp)) {
*/
finfo = cp->c_finderinfo;
finfo = finfo + 16;
- if (S_ISREG(cp->c_attr.ca_mode)) {
+ 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;
- }
- else if (S_ISDIR(cp->c_attr.ca_mode)) {
+ 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 {
+ } else {
finderinfo_start = &cp->c_finderinfo[0];
finderinfo_size = sizeof(cp->c_finderinfo);
}
/* Now restore the date added */
- if (S_ISREG(cp->c_attr.ca_mode)) {
+ 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;
- }
- else if (S_ISDIR(cp->c_attr.ca_mode)) {
+ 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);
}
bzero(iterator, sizeof(*iterator));
- if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK))) {
+ if ((result = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
goto exit_nolock;
}
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);
}
if (result ||
(attrdata.recordType != kHFSPlusAttrExtents) ||
(datasize < sizeof(HFSPlusAttrExtents))) {
- printf("hfs: 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 */
}
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);
}
* for symlinks.
*/
bcopy(cp->c_finderinfo, finderinfo, sizeof(finderinfo));
- hfs_zero_dateadded (cp, finderinfo);
+ hfs_zero_hidden_fields (cp, finderinfo);
/* Don't expose a symlink's private type/creator. */
if (vnode_islnk(vp)) {
}
- /* If Finder Info is non-empty then export it's name. */
+ /* If Finder Info is non-empty then export it's name. */
if (bcmp(finderinfo, emptyfinfo, sizeof(emptyfinfo)) != 0) {
if (uio == NULL) {
*ap->a_size += sizeof(XATTR_FINDERINFO_NAME);
}
}
/* If Resource Fork is non-empty then export it's name. */
- if (S_ISREG(cp->c_mode) && RSRC_FORK_EXISTS(cp)) {
+ if (S_ISREG(cp->c_mode) && hfs_has_rsrc(cp)) {
#if HFS_COMPRESSION
if ((ap->a_options & XATTR_SHOWCOMPRESSION) ||
!compressed ||
- !hfs_hides_rsrc(ap->a_context, VTOC(vp), 1) /* 1 == don't take the cnode lock */
+ !decmpfs_hides_rsrc(ap->a_context, VTOCMP(vp))
)
#endif /* HFS_COMPRESSION */
{
FREE(iterator, M_TEMP);
}
hfs_unlock(cp);
+ hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
return MacToVFSError(result);
}
return (1); /* continue */
#if HFS_COMPRESSION
- if (!state->showcompressed && hfs_hides_xattr(state->ctx, VTOC(state->vp), attrname, 1) ) /* 1 == don't take the cnode lock */
+ if (!state->showcompressed && decmpfs_hides_xattr(state->ctx, VTOCMP(state->vp), attrname) )
return 1; /* continue */
#endif /* HFS_COMPRESSION */
*
* 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.
*/
int
-hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid)
+hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid,
+ bool *open_transaction)
{
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);
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);
- if (result)
- break;
+ *open_transaction = false;
}
-exit:
- FREE(iterator, M_TEMP);
- return (result == btNotFound ? 0: MacToVFSError(result));
+
+ return result;
}
__private_extern__
hfs_end_transaction(hfsmp);
/* Update the state in the mount point */
- HFS_MOUNT_LOCK(hfsmp, TRUE);
+ hfs_lock_mount (hfsmp);
if (state == 0) {
hfsmp->hfs_flags &= ~HFS_XATTR_EXTENTS;
} else {
hfsmp->hfs_flags |= HFS_XATTR_EXTENTS;
}
- HFS_MOUNT_UNLOCK(hfsmp, TRUE);
+ hfs_unlock_mount (hfsmp);
exit:
if (iterator) {
/*
* 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));
{
vnode_t evp = hfsmp->hfs_attrdata_vp;
int bufsize;
- int iosize;
+ int64_t iosize;
int attrsize;
int blksize;
int i;
int result = 0;
- hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK);
+ 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("hfs: 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);
+ hfs_unlock_truncate(VTOC(evp), HFS_LOCK_DEFAULT);
return (result);
}
off_t filesize;
int bufsize;
int attrsize;
- int iosize;
+ int64_t iosize;
int blksize;
int i;
int result = 0;
- hfs_lock_truncate(VTOC(evp), HFS_SHARED_LOCK);
+ 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("hfs: 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);
+ 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++) {
+ /* 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("hfs: alloc_attr_blks: BA blkcnt %d [%d, %d] (%d)\n",
blkcnt, extents[i].startBlock, extents[i].blockCount, result);
}
return (blocks);
}
+