; Additionally build up the summary table as needed.
;
; This function reads the bitmap in large block size
- ; (up to 1MB) unlink the runtime which reads the bitmap
+ ; (up to 1MB) unlike the runtime which reads the bitmap
; in 4K block size. So if this function is being called
; after the volume is mounted and actively modified, the
; caller needs to invalidate all of the existing buffers
; associated with the bitmap vnode before calling this
; function. If the buffers are not invalidated, it can
- ; cause but_t collision and potential data corruption.
+ ; cause buf_t collision and potential data corruption.
;
; Input Arguments:
; hfsmp - The volume containing the allocation blocks.
#define HFS_ROOTVERYLOWDISKTRIGGERLEVEL ((u_int64_t)(512*1024*1024))
#define HFS_ROOTLOWDISKTRIGGERFRACTION 10
#define HFS_ROOTLOWDISKTRIGGERLEVEL ((u_int64_t)(1024*1024*1024))
+#define HFS_ROOTNEARLOWDISKTRIGGERFRACTION 10.5
+#define HFS_ROOTNEARLOWDISKTRIGGERLEVEL ((u_int64_t)(1024*1024*1024 + 100*1024*1024))
#define HFS_ROOTLOWDISKSHUTOFFFRACTION 11
#define HFS_ROOTLOWDISKSHUTOFFLEVEL ((u_int64_t)(1024*1024*1024 + 250*1024*1024))
#define HFS_VERYLOWDISKTRIGGERFRACTION 1
-#define HFS_VERYLOWDISKTRIGGERLEVEL ((u_int64_t)(100*1024*1024))
+#define HFS_VERYLOWDISKTRIGGERLEVEL ((u_int64_t)(150*1024*1024))
#define HFS_LOWDISKTRIGGERFRACTION 2
-#define HFS_LOWDISKTRIGGERLEVEL ((u_int64_t)(150*1024*1024))
-#define HFS_LOWDISKSHUTOFFFRACTION 3
-#define HFS_LOWDISKSHUTOFFLEVEL ((u_int64_t)(200*1024*1024))
+#define HFS_LOWDISKTRIGGERLEVEL ((u_int64_t)(500*1024*1024))
+#define HFS_NEARLOWDISKTRIGGERFRACTION 10
+#define HFS_NEARLOWDISKTRIGGERLEVEL ((uint64_t)(1024*1024*1024))
+#define HFS_LOWDISKSHUTOFFFRACTION 12
+#define HFS_LOWDISKSHUTOFFLEVEL ((u_int64_t)(1024*1024*1024 + 200*1024*1024))
/* Internal Data structures*/
u_int32_t hfs_notification_conditions;
u_int32_t hfs_freespace_notify_dangerlimit;
u_int32_t hfs_freespace_notify_warninglimit;
+ u_int32_t hfs_freespace_notify_nearwarninglimit;
u_int32_t hfs_freespace_notify_desiredlevel;
/* time mounted and last mounted mod time "snapshot" */
u_int32_t hfs_freed_block_count;
u_int64_t hfs_cs_hotfile_size; // in bytes
int hfs_hotfile_freeblks;
- int hfs_hotfile_blk_adjust;
+ int hfs_hotfile_blk_adjust; // since we pass this to OSAddAtomic, this needs to be 4-byte aligned
int hfs_hotfile_maxblks;
int hfs_overflow_maxblks;
int hfs_catalog_maxblks;
queing more syncs. */
thread_t hfs_syncer_thread;
- // Not currently used except for debugging purposes
+ // Not currently used except for debugging purposes
+ // Since we pass this to OSAddAtomic, this needs to be 4-byte aligned.
uint32_t hfs_active_threads;
enum {
struct vnop_setxattr_args *, struct hfsmount *, u_int32_t);
extern int hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid,
bool *open_transaction);
+
+int hfs_removexattr_by_id (struct hfsmount *hfsmp, uint32_t fileid, const char *xattr_name );
+
extern int hfs_set_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype, int state);
int lockflags;
u_int32_t dirchg = 0;
int reachedeof = 0;
-
+ int internal_actualcount;
+ int internal_eofflag;
+
+ /* Lets makse sure we have something assign to actualcount always, min change required */
+ if (actualcount == NULL) {
+ actualcount = &internal_actualcount;
+ }
+ /* Lets makse sure we have something assign to eofflag always, min change required */
+ if (eofflag == NULL) {
+ eofflag = &internal_eofflag;
+ }
+
*(actualcount) = 0;
*(eofflag) = 0;
hfs_assert((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0);
- // disable this for build machines
- // it's not useful and will break every time we add new _CMNEXT_ bits
- // hfs_assert((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0);
+ // disable this because it will break the simulator/build machines each
+ // time a new _CMNEXT_ bit is added
+ // hfs_assert(((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0) ||
+ // ((attrlist->forkattr & ~ATTR_CMNEXT_VALIDMASK) == 0));
size = 0;
struct cat_attr *atrp, struct cat_fork *datafork, struct cat_fork *rsrcfork,
vfs_context_t ctx)
{
- if (alp->commonattr)
+ if (alp->commonattr || alp->forkattr) {
vattr_data_for_common_attrs(alp, vap, hfsmp, vp, descp, atrp,
- ctx);
+ ctx);
+ }
if (alp->dirattr && S_ISDIR(atrp->ca_mode))
vattr_data_for_dir_attrs(alp, vap, hfsmp, vp, descp, atrp);
* The stat call (getattr) will always return the c_fileid
* and Carbon APIs, which are hardlink-ignorant, will always
* receive the c_cnid (from getattrlist).
+ *
+ * Forkattrs are now repurposed for Common Extended Attributes.
*/
- if ((ATTR_CMN_OBJID & attr) ||
- (ATTR_CMN_OBJPERMANENTID & attr)) {
+ if ((ATTR_CMN_OBJID & attr) || (ATTR_CMN_OBJPERMANENTID & attr) ||
+ alp->forkattr & ATTR_CMNEXT_LINKID) {
vap->va_linkid = cdp->cd_cnid;
VATTR_SET_SUPPORTED(vap, va_linkid);
}
/*
- * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
btcb->releaseBlockProc = ReleaseBTreeBlock;
btcb->setEndOfForkProc = ExtendBTreeFile;
btcb->keyCompareProc = (KeyCompareProcPtr)hfs_attrkeycompare;
+
+ /*
+ * NOTE: We must make sure to zero out this pointer if we error out in this function!
+ * If we don't, then unmount will treat it as a valid pointer which can lead to a
+ * use-after-free
+ */
VTOF(vp)->fcbBTCBPtr = btcb;
/*
}
exit:
+
+ if (vp && result) {
+ /*
+ * If we're about to error out, then make sure to zero out the B-Tree control block pointer
+ * from the filefork of the EA B-Tree cnode/vnode. Failing to do this will lead to a use
+ * after free at unmount or BTFlushPath. Since we're about to error out anyway, this memory
+ * will be freed.
+ */
+ VTOF(vp)->fcbBTCBPtr = NULL;
+ }
+
+
if (vp) {
hfs_unlock(VTOC(vp));
}
/* HFS FS CONTROL COMMANDS */
#define HFSIOC_RESIZE_PROGRESS _IOR('h', 1, u_int32_t)
-#define HFS_RESIZE_PROGRESS IOCBASECMD(HFSIOC_RESIZE_PROGRESS)
#define HFSIOC_RESIZE_VOLUME _IOW('h', 2, u_int64_t)
-#define HFS_RESIZE_VOLUME IOCBASECMD(HFSIOC_RESIZE_VOLUME)
#define HFSIOC_CHANGE_NEXT_ALLOCATION _IOWR('h', 3, u_int32_t)
-#define HFS_CHANGE_NEXT_ALLOCATION IOCBASECMD(HFSIOC_CHANGE_NEXT_ALLOCATION)
/* Magic value for next allocation to use with fcntl to set next allocation
* to zero and never update it again on new block allocation.
*/
#define HFS_NO_UPDATE_NEXT_ALLOCATION 0xffffFFFF
#define HFSIOC_GETCREATETIME _IOR('h', 4, time_t)
-#define HFS_GETCREATETIME IOCBASECMD(HFSIOC_GETCREATETIME)
#define HFSIOC_SETBACKINGSTOREINFO _IOW('h', 7, struct hfs_backingstoreinfo)
-#define HFS_SETBACKINGSTOREINFO IOCBASECMD(HFSIOC_SETBACKINGSTOREINFO)
#define HFSIOC_CLRBACKINGSTOREINFO _IO('h', 8)
-#define HFS_CLRBACKINGSTOREINFO IOCBASECMD(HFSIOC_CLRBACKINGSTOREINFO)
// 'h', 9 used to be HFSIOC_BULKACCESS which is now deprecated
#define HFSIOC_UNSUPPORTED _IOW('h', 10, int32_t)
#define HFSIOC_PREV_LINK _IOWR('h', 11, u_int32_t)
-#define HFS_PREV_LINK IOCBASECMD(HFSIOC_PREV_LINK)
#define HFSIOC_NEXT_LINK _IOWR('h', 12, u_int32_t)
-#define HFS_NEXT_LINK IOCBASECMD(HFSIOC_NEXT_LINK)
#define HFSIOC_GETPATH _IOWR('h', 13, pathname_t)
-#define HFS_GETPATH IOCBASECMD(HFSIOC_GETPATH)
/* By default, the path returned by HFS_GETPATH is an absolute path,
* i.e. it also contains the mount point of the volume on which the
* fileID exists. If the following bit is set, the path returned is
/* Enable/disable extent-based extended attributes */
#define HFSIOC_SET_XATTREXTENTS_STATE _IOW('h', 14, u_int32_t)
-#define HFS_SET_XATTREXTENTS_STATE IOCBASECMD(HFSIOC_SET_XATTREXTENTS_STATE)
-#define HFSIOC_EXT_BULKACCESS _IOW('h', 15, struct user32_ext_access_t)
-#define HFS_EXT_BULKACCESS_FSCTL IOCBASECMD(HFSIOC_EXT_BULKACCESS)
+#if defined(KERNEL)
+#define HFSIOC_EXT_BULKACCESS32 _IOW('h', 15, struct user32_ext_access_t)
+#define HFSIOC_EXT_BULKACCESS64 _IOW('h', 15, struct user64_ext_access_t)
+#else
+#define HFSIOC_EXT_BULKACCESS _IOW('h', 15, struct ext_access_t)
+#endif /* KERNEL */
#define HFSIOC_MARK_BOOT_CORRUPT _IO('h', 16)
-#define HFS_MARK_BOOT_CORRUPT IOCBASECMD(HFSIOC_MARK_BOOT_CORRUPT)
#define HFSIOC_GET_JOURNAL_INFO _IOR('h', 17, struct hfs_journal_info)
-#define HFS_FSCTL_GET_JOURNAL_INFO IOCBASECMD(HFSIOC_GET_JOURNAL_INFO)
#define HFSIOC_SET_VERY_LOW_DISK _IOW('h', 20, u_int32_t)
-#define HFS_FSCTL_SET_VERY_LOW_DISK IOCBASECMD(HFSIOC_SET_VERY_LOW_DISK)
#define HFSIOC_SET_LOW_DISK _IOW('h', 21, u_int32_t)
-#define HFS_FSCTL_SET_LOW_DISK IOCBASECMD(HFSIOC_SET_LOW_DISK)
#define HFSIOC_SET_DESIRED_DISK _IOW('h', 22, u_int32_t)
-#define HFS_FSCTL_SET_DESIRED_DISK IOCBASECMD(HFSIOC_SET_DESIRED_DISK)
#define HFSIOC_SET_ALWAYS_ZEROFILL _IOW('h', 23, int32_t)
+ /* XXXJRT Keep until 31866920 is resolved. */
#define HFS_SET_ALWAYS_ZEROFILL IOCBASECMD(HFSIOC_SET_ALWAYS_ZEROFILL)
#define HFSIOC_VOLUME_STATUS _IOR('h', 24, u_int32_t)
-#define HFS_VOLUME_STATUS IOCBASECMD(HFSIOC_VOLUME_STATUS)
/* Disable metadata zone for given volume */
#define HFSIOC_DISABLE_METAZONE _IO('h', 25)
-#define HFS_DISABLE_METAZONE IOCBASECMD(HFSIOC_DISABLE_METAZONE)
/* Change the next CNID value */
#define HFSIOC_CHANGE_NEXTCNID _IOWR('h', 26, u_int32_t)
+ /* XXXJRT Keep until 31866920 is resolved. */
#define HFS_CHANGE_NEXTCNID IOCBASECMD(HFSIOC_CHANGE_NEXTCNID)
/* Get the low disk space values */
#define HFSIOC_GET_VERY_LOW_DISK _IOR('h', 27, u_int32_t)
-#define HFS_FSCTL_GET_VERY_LOW_DISK IOCBASECMD(HFSIOC_GET_VERY_LOW_DISK)
#define HFSIOC_GET_LOW_DISK _IOR('h', 28, u_int32_t)
-#define HFS_FSCTL_GET_LOW_DISK IOCBASECMD(HFSIOC_GET_LOW_DISK)
#define HFSIOC_GET_DESIRED_DISK _IOR('h', 29, u_int32_t)
-#define HFS_FSCTL_GET_DESIRED_DISK IOCBASECMD(HFSIOC_GET_DESIRED_DISK)
/* 30 was HFSIOC_GET_WRITE_GEN_COUNTER and is now deprecated */
/* revisiond only uses this when something transforms in a way the kernel can't track such as "foo.rtf" -> "foo.rtfd" */
#define HFSIOC_TRANSFER_DOCUMENT_ID _IOW('h', 32, u_int32_t)
-#define HFS_TRANSFER_DOCUMENT_ID IOCBASECMD(HFSIOC_TRANSFER_DOCUMENT_ID)
/*
* f_bsize by statfs(2).
*/
#define HFSIOC_FSINFO_METADATA_BLOCKS _IOWR('h', 38, struct hfsinfo_metadata)
-#define HFS_FSINFO_METADATA_BLOCKS IOCBASECMD(HFSIOC_FSINFO_METADATA_BLOCKS)
/* Send TRIMs for all free blocks to the underlying device */
#define HFSIOC_CS_FREESPACE_TRIM _IOWR('h', 39, u_int32_t)
-#define HFS_CS_FREESPACE_TRIM IOCBASECMD(HFSIOC_CS_FREESPACE_TRIM)
/* Get file system information for the given volume */
#define HFSIOC_GET_FSINFO _IOWR('h', 45, hfs_fsinfo)
-#define HFS_GET_FSINFO IOCBASECMD(HFSIOC_GET_FSINFO)
/* Re-pin hotfile data; argument controls what state gets repinned */
#define HFSIOC_REPIN_HOTFILE_STATE _IOWR('h', 46, u_int32_t)
-#define HFS_REPIN_HOTFILE_STATE IOCBASECMD(HFSIOC_REPIN_HOTFILE_STATE)
#define HFS_REPIN_METADATA 0x0001
#define HFS_REPIN_USERDATA 0x0002
/* Mark a directory or file as worth caching on any underlying "fast" device */
#define HFSIOC_SET_HOTFILE_STATE _IOWR('h', 47, u_int32_t)
-#define HFS_SET_HOTFILE_STATE IOCBASECMD(HFSIOC_SET_HOTFILE_STATE)
/* flags to pass to SET_HOTFILE_STATE */
#define HFS_MARK_FASTDEVCANDIDATE 0x0001
#define HFS_NEVER_FASTDEVCANDIDATE 0x0004
#define HFSIOC_SET_MAX_DEFRAG_SIZE _IOWR('h', 48, u_int32_t)
-#define HFS_SET_MAX_DEFRAG_SIZE IOCBASECMD(HFSIOC_SET_MAX_DEFRAG_SIZE)
#define HFSIOC_FORCE_ENABLE_DEFRAG _IOWR('h', 49, u_int32_t)
-#define HFS_FORCE_ENABLE_DEFRAG IOCBASECMD(HFSIOC_FORCE_ENABLE_DEFRAG)
+/* These fsctls are ported from apfs. */
+#ifndef APFSIOC_SET_NEAR_LOW_DISK
+#define APFSIOC_SET_NEAR_LOW_DISK _IOW('J', 17, u_int32_t)
+#endif /* APFSIOC_SET_NEAR_LOW_DISK */
+
+#ifndef APFSIOC_GET_NEAR_LOW_DISK
+#define APFSIOC_GET_NEAR_LOW_DISK _IOR('J', 18, u_int32_t)
+#endif /* APFSIOC_GET_NEAR_LOW_DISK */
#endif /* __APPLE_API_UNSTABLE */
goto bad_journal;
}
+ /*
+ * Check for a bad jhdr size after reading in the journal header.
+ * The journal header length cannot be zero
+ */
+ if (jnl->jhdr->jhdr_size == 0) {
+ printf("jnl: %s: open: bad jhdr size (%d) \n", jdev_name, jnl->jhdr->jhdr_size);
+ goto bad_journal;
+ }
+
orig_checksum = jnl->jhdr->checksum;
jnl->jhdr->checksum = 0;
goto bad_journal;
}
+ if (jnl->jhdr->blhdr_size < 0) {
+ //throw out invalid sizes
+ printf("jnl %s: open: blhdr size looks bogus! (%d) \n",
+ jdev_name, jnl->jhdr->blhdr_size);
+ goto bad_journal;
+ }
+
// take care of replaying the journal if necessary
if (flags & JOURNAL_RESET) {
printf("jnl: %s: journal start/end pointers reset! (s 0x%llx e 0x%llx)\n",
/*
* Sanity check inputs, and adjust the size of the transaction buffer.
*/
+ if (jnl->jhdr->jhdr_size == 0) {
+ printf("jnl: %s: relocate: bad jhdr size (%d)\n", jnl->jdev_name, jnl->jhdr->jhdr_size);
+ return EINVAL;
+ }
+
if ((offset % jnl->jhdr->jhdr_size) != 0) {
printf("jnl: %s: relocate: offset 0x%llx is not an even multiple of block size 0x%x\n",
jnl->jdev_name, offset, jnl->jhdr->jhdr_size);
freeblks = hfs_freeblks(hfsmp, 1);
+ /*
+ * Find the theshold the number of free blocks fits into.
+ * We fire upon reaching a level below desired only once,
+ * except for when we reach the low disk or near low disk levels
+ * from below, in which case we do not fire unless we have also
+ * reached the desired disk level (hysteresis).
+ * This is illustrated in the following diagram:
+ *
+ * fire ^
+ * --------- desired level
+ * |
+ *
+ *
+ * |
+ * --------- near low disk level
+ * fire v
+ *
+ *
+ * |
+ * --------- low disk level
+ * fire v
+ *
+ *
+ * | ^ fire
+ * --------- very low disk level
+ * fire v |
+ *
+ */
if (freeblks < hfsmp->hfs_freespace_notify_dangerlimit) {
- state = 2;
+ state = 4;
} else if (freeblks < hfsmp->hfs_freespace_notify_warninglimit) {
+ state = 3;
+ } else if (freeblks < hfsmp->hfs_freespace_notify_nearwarninglimit) {
+ state = 2;
+ } else if (freeblks < hfsmp->hfs_freespace_notify_desiredlevel) {
+ /* We are between the near low disk and desired levels */
state = 1;
} else if (freeblks >= hfsmp->hfs_freespace_notify_desiredlevel) {
state = 0;
}
/* Free blocks are less than dangerlimit for the first time */
- if (state == 2 && !(hfsmp->hfs_notification_conditions & VQ_VERYLOWDISK)) {
+ if (state == 4 && !(hfsmp->hfs_notification_conditions & VQ_VERYLOWDISK)) {
/* Dump some logging to track down intermittent issues */
printf("hfs: set VeryLowDisk: vol:%s, freeblks:%d, dangerlimit:%d\n", hfsmp->vcbVN, freeblks, hfsmp->hfs_freespace_notify_dangerlimit);
}
#endif
- hfsmp->hfs_notification_conditions |= (VQ_VERYLOWDISK|VQ_LOWDISK);
+ hfsmp->hfs_notification_conditions |= (VQ_VERYLOWDISK|VQ_LOWDISK|VQ_NEARLOWDISK);
vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
- } else if (state == 1) {
+ } else if (state == 3) {
/* Free blocks are less than warning limit for the first time */
if (!(hfsmp->hfs_notification_conditions & VQ_LOWDISK)) {
printf("hfs: set LowDisk: vol:%s, freeblks:%d, warninglimit:%d\n", hfsmp->vcbVN, freeblks, hfsmp->hfs_freespace_notify_warninglimit);
- hfsmp->hfs_notification_conditions |= VQ_LOWDISK;
+ hfsmp->hfs_notification_conditions |= (VQ_LOWDISK|VQ_NEARLOWDISK);
vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
} else if (hfsmp->hfs_notification_conditions & VQ_VERYLOWDISK) {
/* Free blocks count has increased from danger limit to warning limit, so just clear VERYLOWDISK warning */
hfsmp->hfs_notification_conditions &= ~VQ_VERYLOWDISK;
vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
}
+ } else if (state == 2) {
+ /* Free blocks are less than the near warning limit for the first time */
+ if (!(hfsmp->hfs_notification_conditions & VQ_NEARLOWDISK)) {
+ printf("hfs: set NearLowDisk: vol:%s, freeblks:%d, nearwarninglimit:%d\n", hfsmp->vcbVN, freeblks,
+ hfsmp->hfs_freespace_notify_nearwarninglimit);
+
+ hfsmp->hfs_notification_conditions |= VQ_NEARLOWDISK;
+ vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
+ } else {
+ /* Free blocks count has increased from warning/danger limit to near warning limit,
+ * so clear VERYLOWDISK / LOWDISK warnings, and signal if we clear VERYLOWDISK */
+ hfsmp->hfs_notification_conditions &= ~VQ_LOWDISK;
+ if (hfsmp->hfs_notification_conditions & VQ_VERYLOWDISK) {
+ printf("hfs: clear VeryLowDisk: vol:%s, freeblks:%d, dangerlimit:%d\n", hfsmp->vcbVN, freeblks,
+ hfsmp->hfs_freespace_notify_dangerlimit);
+
+ hfsmp->hfs_notification_conditions &= ~VQ_VERYLOWDISK;
+ vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
+ }
+ }
+ } else if (state == 1) {
+ /* Free blocks are less than the desireable level, but more than the near warning level
+ * In this case, we may have to notify if we were previously underneath the danger limit */
+ if (hfsmp->hfs_notification_conditions & VQ_VERYLOWDISK) {
+ printf("hfs: clear VeryLowDisk: vol:%s, freeblks:%d, dangerlimit:%d\n", hfsmp->vcbVN, freeblks,
+ hfsmp->hfs_freespace_notify_dangerlimit);
+
+ hfsmp->hfs_notification_conditions &= ~VQ_VERYLOWDISK;
+ vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
+ }
} else if (state == 0) {
/* Free blocks count has increased to desirable level, so clear all conditions */
- if (hfsmp->hfs_notification_conditions & (VQ_LOWDISK|VQ_VERYLOWDISK)) {
+ if (hfsmp->hfs_notification_conditions & (VQ_NEARLOWDISK|VQ_LOWDISK|VQ_VERYLOWDISK)) {
+ if (hfsmp->hfs_notification_conditions & VQ_NEARLOWDISK) {
+ printf("hfs: clear NearLowDisk: vol:%s, freeblks:%d, nearwarninglimit:%d, desiredlevel:%d\n", hfsmp->vcbVN,
+ freeblks, hfsmp->hfs_freespace_notify_nearwarninglimit, hfsmp->hfs_freespace_notify_desiredlevel);
+ }
if (hfsmp->hfs_notification_conditions & VQ_LOWDISK) {
- printf("hfs: clear LowDisk: vol:%s, freeblks:%d, warninglimit:%d, desiredlevel:%d\n", hfsmp->vcbVN, freeblks, hfsmp->hfs_freespace_notify_warninglimit, hfsmp->hfs_freespace_notify_desiredlevel);
+ printf("hfs: clear LowDisk: vol:%s, freeblks:%d, warninglimit:%d, desiredlevel:%d\n", hfsmp->vcbVN, freeblks,
+ hfsmp->hfs_freespace_notify_warninglimit, hfsmp->hfs_freespace_notify_desiredlevel);
}
if (hfsmp->hfs_notification_conditions & VQ_VERYLOWDISK) {
printf("hfs: clear VeryLowDisk: vol:%s, freeblks:%d, dangerlimit:%d\n", hfsmp->vcbVN, freeblks, hfsmp->hfs_freespace_notify_warninglimit);
}
- hfsmp->hfs_notification_conditions &= ~(VQ_VERYLOWDISK|VQ_LOWDISK);
+ hfsmp->hfs_notification_conditions &= ~(VQ_VERYLOWDISK|VQ_LOWDISK|VQ_NEARLOWDISK);
if (hfsmp->hfs_notification_conditions == 0) {
- vfs_event_signal(&fsid, VQ_UPDATE, (intptr_t)NULL);
+ vfs_event_signal(&fsid, VQ_UPDATE|VQ_DESIRED_DISK, (intptr_t)NULL);
} else {
vfs_event_signal(&fsid, hfsmp->hfs_notification_conditions, (intptr_t)NULL);
}
#if CONFIG_PROTECT
#if HFS_CONFIG_KEY_ROLL
- // The HFS_KEY_ROLL fsctl does its own access checks
- if (ap->a_command != HFS_KEY_ROLL)
+ // The HFSIOC_KEY_ROLL fsctl does its own access checks
+ if (ap->a_command != HFSIOC_KEY_ROLL)
#endif
{
int error = 0;
switch (ap->a_command) {
- case HFS_GETPATH:
+ case HFSIOC_GETPATH:
{
struct vnode *file_vp;
cnid_t cnid;
return (error);
}
- case HFS_SET_MAX_DEFRAG_SIZE:
+ case HFSIOC_SET_MAX_DEFRAG_SIZE:
{
int error = 0; /* Assume success */
u_int32_t maxsize = 0;
return (error);
}
- case HFS_FORCE_ENABLE_DEFRAG:
+ case HFSIOC_FORCE_ENABLE_DEFRAG:
{
int error = 0; /* Assume success */
u_int32_t do_enable = 0;
}
- case HFS_TRANSFER_DOCUMENT_ID:
+ case HFSIOC_TRANSFER_DOCUMENT_ID:
{
struct cnode *cp = NULL;
int error;
- case HFS_PREV_LINK:
- case HFS_NEXT_LINK:
+ case HFSIOC_PREV_LINK:
+ case HFSIOC_NEXT_LINK:
{
cnid_t linkfileid;
cnid_t nextlinkid;
if ((error = hfs_lookup_siblinglinks(hfsmp, linkfileid, &prevlinkid, &nextlinkid))) {
return (error);
}
- if (ap->a_command == HFS_NEXT_LINK) {
+ if (ap->a_command == HFSIOC_NEXT_LINK) {
*(cnid_t *)ap->a_data = nextlinkid;
} else {
*(cnid_t *)ap->a_data = prevlinkid;
return (0);
}
- case HFS_RESIZE_PROGRESS: {
+ case HFSIOC_RESIZE_PROGRESS: {
vfsp = vfs_statfs(HFSTOVFS(hfsmp));
if (suser(cred, NULL) &&
return hfs_resize_progress(hfsmp, (u_int32_t *)ap->a_data);
}
- case HFS_RESIZE_VOLUME: {
+ case HFSIOC_RESIZE_VOLUME: {
u_int64_t newsize;
u_int64_t cursize;
int ret;
IOBSDMountChange(hfsmp->hfs_mp, kIOMountChangeDidResize);
return (ret);
}
- case HFS_CHANGE_NEXT_ALLOCATION: {
+ case HFSIOC_CHANGE_NEXT_ALLOCATION: {
int error = 0; /* Assume success */
u_int32_t location;
}
#if HFS_SPARSE_DEV
- case HFS_SETBACKINGSTOREINFO: {
+ case HFSIOC_SETBACKINGSTOREINFO: {
struct vnode * di_vp;
struct hfs_backingstoreinfo *bsdata;
int error = 0;
file_drop(bsdata->backingfd);
return (0);
}
- case HFS_CLRBACKINGSTOREINFO: {
+
+ case HFSIOC_CLRBACKINGSTOREINFO: {
struct vnode * tmpvp;
vfsp = vfs_statfs(HFSTOVFS(hfsmp));
#endif /* HFS_SPARSE_DEV */
/* Change the next CNID stored in the VH */
- case HFS_CHANGE_NEXTCNID: {
+ case HFSIOC_CHANGE_NEXTCNID: {
int error = 0; /* Assume success */
u_int32_t fileid;
int wraparound = 0;
return hfs_thaw(hfsmp, current_proc());
}
- case HFS_EXT_BULKACCESS_FSCTL: {
+ case HFSIOC_EXT_BULKACCESS32:
+ case HFSIOC_EXT_BULKACCESS64: {
int size;
#if CONFIG_HFS_STD
if (hfsmp->hfs_flags & HFS_STANDARD) {
return do_bulk_access_check(hfsmp, vp, ap, size, context);
}
- case HFS_SET_XATTREXTENTS_STATE: {
+ case HFSIOC_SET_XATTREXTENTS_STATE: {
int state;
if (ap->a_data == NULL) {
return (EPERM);
}
if (state == 0 || state == 1)
- return hfs_set_volxattr(hfsmp, HFS_SET_XATTREXTENTS_STATE, state);
+ return hfs_set_volxattr(hfsmp, HFSIOC_SET_XATTREXTENTS_STATE, state);
else
return (EINVAL);
}
return 0;
}
- case SPOTLIGHT_FSCTL_GET_MOUNT_TIME:
+ case SPOTLIGHT_IOC_GET_MOUNT_TIME:
*(uint32_t *)ap->a_data = hfsmp->hfs_mount_time;
break;
- case SPOTLIGHT_FSCTL_GET_LAST_MTIME:
+ case SPOTLIGHT_IOC_GET_LAST_MTIME:
*(uint32_t *)ap->a_data = hfsmp->hfs_last_mounted_mtime;
break;
- case HFS_FSCTL_GET_VERY_LOW_DISK:
+ case HFSIOC_GET_VERY_LOW_DISK:
*(uint32_t*)ap->a_data = hfsmp->hfs_freespace_notify_dangerlimit;
break;
- case HFS_FSCTL_SET_VERY_LOW_DISK:
+ case HFSIOC_SET_VERY_LOW_DISK:
if (*(uint32_t *)ap->a_data >= hfsmp->hfs_freespace_notify_warninglimit) {
return EINVAL;
}
hfsmp->hfs_freespace_notify_dangerlimit = *(uint32_t *)ap->a_data;
break;
- case HFS_FSCTL_GET_LOW_DISK:
+ case HFSIOC_GET_LOW_DISK:
*(uint32_t*)ap->a_data = hfsmp->hfs_freespace_notify_warninglimit;
break;
- case HFS_FSCTL_SET_LOW_DISK:
+ case HFSIOC_SET_LOW_DISK:
if ( *(uint32_t *)ap->a_data >= hfsmp->hfs_freespace_notify_desiredlevel
|| *(uint32_t *)ap->a_data <= hfsmp->hfs_freespace_notify_dangerlimit) {
hfsmp->hfs_freespace_notify_warninglimit = *(uint32_t *)ap->a_data;
break;
- case HFS_FSCTL_GET_DESIRED_DISK:
+ /* The following two fsctls were ported from apfs. */
+ case APFSIOC_GET_NEAR_LOW_DISK:
+ *(uint32_t*)ap->a_data = hfsmp->hfs_freespace_notify_nearwarninglimit;
+ break;
+
+ case APFSIOC_SET_NEAR_LOW_DISK:
+ if ( *(uint32_t *)ap->a_data >= hfsmp->hfs_freespace_notify_desiredlevel
+ || *(uint32_t *)ap->a_data <= hfsmp->hfs_freespace_notify_warninglimit) {
+ return EINVAL;
+ }
+
+ hfsmp->hfs_freespace_notify_nearwarninglimit = *(uint32_t *)ap->a_data;
+ break;
+
+ case HFSIOC_GET_DESIRED_DISK:
*(uint32_t*)ap->a_data = hfsmp->hfs_freespace_notify_desiredlevel;
break;
- case HFS_FSCTL_SET_DESIRED_DISK:
+ case HFSIOC_SET_DESIRED_DISK:
if (*(uint32_t *)ap->a_data <= hfsmp->hfs_freespace_notify_warninglimit) {
return EINVAL;
}
hfsmp->hfs_freespace_notify_desiredlevel = *(uint32_t *)ap->a_data;
break;
- case HFS_VOLUME_STATUS:
+ case HFSIOC_VOLUME_STATUS:
*(uint32_t *)ap->a_data = hfsmp->hfs_notification_conditions;
break;
hfs_unlock_mount(hfsmp);
break;
- case HFS_MARK_BOOT_CORRUPT:
+ /* case HFS_MARK_BOOT_CORRUPT: _IO are the same */
+ case HFSIOC_MARK_BOOT_CORRUPT:
/* Mark the boot volume corrupt by setting
* kHFSVolumeInconsistentBit in the volume header. This will
* force fsck_hfs on next mount.
hfs_mark_inconsistent(hfsmp, HFS_FSCK_FORCED);
break;
- case HFS_FSCTL_GET_JOURNAL_INFO:
+ case HFSIOC_GET_JOURNAL_INFO:
jip = (struct hfs_journal_info*)ap->a_data;
if (vp == NULLVP)
jip->jsize = jnl_size;
break;
- case HFS_SET_ALWAYS_ZEROFILL: {
+ case HFSIOC_SET_ALWAYS_ZEROFILL: {
struct cnode *cp = VTOC(vp);
if (*(int *)ap->a_data) {
break;
}
- case HFS_DISABLE_METAZONE: {
+ /* case HFS_DISABLE_METAZONE: _IO are the same */
+ case HFSIOC_DISABLE_METAZONE: {
/* Only root can disable metadata zone */
if (!kauth_cred_issuser(kauth_cred_get())) {
return EACCES;
}
- case HFS_FSINFO_METADATA_BLOCKS: {
+ case HFSIOC_FSINFO_METADATA_BLOCKS: {
int error;
struct hfsinfo_metadata *hinfo;
break;
}
- case HFS_GET_FSINFO: {
+ case HFSIOC_GET_FSINFO: {
hfs_fsinfo *fsinfo = (hfs_fsinfo *)ap->a_data;
/* Only root is allowed to get fsinfo */
return hfs_get_fsinfo(hfsmp, ap->a_data);
}
- case HFS_CS_FREESPACE_TRIM: {
+ case HFSIOC_CS_FREESPACE_TRIM: {
int error = 0;
int lockflags = 0;
break;
}
- case HFS_SET_HOTFILE_STATE: {
+ case HFSIOC_SET_HOTFILE_STATE: {
int error;
struct cnode *cp = VTOC(vp);
uint32_t hf_state = *((uint32_t*)ap->a_data);
return error;
}
- case HFS_REPIN_HOTFILE_STATE: {
+ case HFSIOC_REPIN_HOTFILE_STATE: {
int error=0;
uint32_t repin_what = *((uint32_t*)ap->a_data);
#if HFS_CONFIG_KEY_ROLL
- case HFS_KEY_ROLL: {
+ case HFSIOC_KEY_ROLL: {
if (!kauth_cred_issuser(kauth_cred_get()))
return EACCES;
return hfs_key_roll_op(ap->a_context, ap->a_vp, args);
}
- case HFS_GET_KEY_AUTO_ROLL: {
+ case HFSIOC_GET_KEY_AUTO_ROLL: {
if (!kauth_cred_issuser(kauth_cred_get()))
return EACCES;
break;
}
- case HFS_SET_KEY_AUTO_ROLL: {
+ case HFSIOC_SET_KEY_AUTO_ROLL: {
if (!kauth_cred_issuser(kauth_cred_get()))
return EACCES;
/*
- * Copyright (c) 1999-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 1999-2017 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
}
-static int hfs_root_unmounted_cleanly = 0;
-
-SYSCTL_DECL(_vfs_generic);
-HFS_SYSCTL(INT, _vfs_generic, OID_AUTO, root_unmounted_cleanly, CTLFLAG_RD, &hfs_root_unmounted_cleanly, 0, "Root filesystem was unmounted cleanly");
-
/*
* Common code for mount and mountroot
*/
goto error_exit;
}
- if (isroot) {
- hfs_root_unmounted_cleanly = ((SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) != 0);
+ if (isroot && ((SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) != 0)) {
+ vfs_set_root_unmounted_cleanly();
}
/*
/*
* Set the free space warning levels for a non-root volume:
*
- * Set the "danger" limit to 1% of the volume size or 100MB, whichever
- * is less. Set the "warning" limit to 2% of the volume size or 150MB,
- * whichever is less. And last, set the "desired" freespace level to
- * to 3% of the volume size or 200MB, whichever is less.
+ * Set the "danger" limit to 1% of the volume size or 150MB, whichever is less.
+ * Set the "warning" limit to 2% of the volume size or 500MB, whichever is less.
+ * Set the "near warning" limit to 10% of the volume size or 1GB, whichever is less.
+ * And last, set the "desired" freespace level to to 12% of the volume size or 1.2GB,
+ * whichever is less.
*/
hfsmp->hfs_freespace_notify_dangerlimit =
MIN(HFS_VERYLOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize,
hfsmp->hfs_freespace_notify_warninglimit =
MIN(HFS_LOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize,
(HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_LOWDISKTRIGGERFRACTION);
+ hfsmp->hfs_freespace_notify_nearwarninglimit =
+ MIN(HFS_NEARLOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize,
+ (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_NEARLOWDISKTRIGGERFRACTION);
hfsmp->hfs_freespace_notify_desiredlevel =
MIN(HFS_LOWDISKSHUTOFFLEVEL / HFSTOVCB(hfsmp)->blockSize,
(HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_LOWDISKSHUTOFFFRACTION);
/*
* Set the free space warning levels for the root volume:
*
- * Set the "danger" limit to 5% of the volume size or 512MB, whichever
- * is less. Set the "warning" limit to 10% of the volume size or 1GB,
- * whichever is less. And last, set the "desired" freespace level to
- * to 11% of the volume size or 1.25GB, whichever is less.
+ * Set the "danger" limit to 5% of the volume size or 512MB, whichever is less.
+ * Set the "warning" limit to 10% of the volume size or 1GB, whichever is less.
+ * Set the "near warning" limit to 10.5% of the volume size or 1.1GB, whichever is less.
+ * And last, set the "desired" freespace level to to 11% of the volume size or 1.25GB,
+ * whichever is less.
*
* NOTE: While those are the default limits, KernelEventAgent (as of 3/2016)
* will unilaterally override these to the following on OSX only:
hfsmp->hfs_freespace_notify_warninglimit =
MIN(HFS_ROOTLOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize,
(HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_ROOTLOWDISKTRIGGERFRACTION);
+ hfsmp->hfs_freespace_notify_nearwarninglimit =
+ MIN(HFS_ROOTNEARLOWDISKTRIGGERLEVEL / HFSTOVCB(hfsmp)->blockSize,
+ (HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_ROOTNEARLOWDISKTRIGGERFRACTION);
hfsmp->hfs_freespace_notify_desiredlevel =
MIN(HFS_ROOTLOWDISKSHUTOFFLEVEL / HFSTOVCB(hfsmp)->blockSize,
(HFSTOVCB(hfsmp)->totalBlocks / 100) * HFS_ROOTLOWDISKSHUTOFFFRACTION);
static int
hfs_vfs_getattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t context)
{
-#define HFS_ATTR_FILE_VALIDMASK (ATTR_FILE_VALIDMASK & ~(ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST))
-#define HFS_ATTR_CMN_VOL_VALIDMASK (ATTR_CMN_VALIDMASK & ~(ATTR_CMN_ACCTIME))
+#define HFS_ATTR_FILE_VALIDMASK (ATTR_FILE_VALIDMASK & ~(ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST | ATTR_FILE_CLUMPSIZE))
+#define HFS_ATTR_CMN_VOL_VALIDMASK (ATTR_CMN_VALIDMASK & ~(ATTR_CMN_DATA_PROTECT_FLAGS))
ExtendedVCB *vcb = VFSTOVCB(mp);
struct hfsmount *hfsmp = VFSTOHFS(mp);
#if CONFIG_HFS_DIRLINK
VOL_CAP_FMT_DIR_HARDLINKS |
#endif
+#ifdef VOL_CAP_FMT_DOCUMENT_ID
+ VOL_CAP_FMT_DOCUMENT_ID |
+#endif /* VOL_CAP_FMT_DOCUMENT_ID */
+#ifdef VOL_CAP_FMT_WRITE_GENERATION_COUNT
+ VOL_CAP_FMT_WRITE_GENERATION_COUNT |
+#endif /* VOL_CAP_FMT_WRITE_GENERATION_COUNT */
VOL_CAP_FMT_PATH_FROM_ID;
}
#if CONFIG_HFS_STD
VOL_CAP_FMT_HIDDEN_FILES |
VOL_CAP_FMT_PATH_FROM_ID |
VOL_CAP_FMT_DECMPFS_COMPRESSION |
+#ifdef VOL_CAP_FMT_DOCUMENT_ID
+ VOL_CAP_FMT_DOCUMENT_ID |
+#endif /* VOL_CAP_FMT_DOCUMENT_ID */
+#ifdef VOL_CAP_FMT_WRITE_GENERATION_COUNT
+ VOL_CAP_FMT_WRITE_GENERATION_COUNT |
+#endif /* VOL_CAP_FMT_WRITE_GENERATION_COUNT */
VOL_CAP_FMT_DIR_HARDLINKS;
/*
vol_attributes_attr_t *attrp = &fsap->f_attributes;
attrp->validattr.commonattr = HFS_ATTR_CMN_VOL_VALIDMASK;
+#if CONFIG_PROTECT
+ attrp->validattr.commonattr |= ATTR_CMN_DATA_PROTECT_FLAGS;
+#endif // CONFIG_PROTECT
+
attrp->validattr.volattr = ATTR_VOL_VALIDMASK & ~ATTR_VOL_INFO;
attrp->validattr.dirattr = ATTR_DIR_VALIDMASK;
attrp->validattr.fileattr = HFS_ATTR_FILE_VALIDMASK;
attrp->validattr.forkattr = 0;
- attrp->nativeattr.commonattr = HFS_ATTR_CMN_VOL_VALIDMASK;
- attrp->nativeattr.volattr = ATTR_VOL_VALIDMASK & ~ATTR_VOL_INFO;
+ attrp->nativeattr.commonattr = HFS_ATTR_CMN_VOL_VALIDMASK;
+#if CONFIG_PROTECT
+ attrp->nativeattr.commonattr |= ATTR_CMN_DATA_PROTECT_FLAGS;
+#endif // CONFIG_PROTECT
+
+ attrp->nativeattr.volattr = ATTR_VOL_VALIDMASK & ~ATTR_VOL_INFO;
attrp->nativeattr.dirattr = ATTR_DIR_VALIDMASK;
attrp->nativeattr.fileattr = HFS_ATTR_FILE_VALIDMASK;
attrp->nativeattr.forkattr = 0;
fsap->f_modify_time.tv_sec = hfsmp->vcbLsMod;
fsap->f_modify_time.tv_nsec = 0;
VFSATTR_SET_SUPPORTED(fsap, f_modify_time);
+ // We really don't have volume access time, they should check the root node, fake it up
+ if (VFSATTR_IS_ACTIVE(fsap, f_access_time)) {
+ struct timeval tv;
+
+ microtime(&tv);
+ fsap->f_access_time.tv_sec = tv.tv_sec;
+ fsap->f_access_time.tv_nsec = 0;
+ VFSATTR_SET_SUPPORTED(fsap, f_access_time);
+ }
fsap->f_backup_time.tv_sec = hfsmp->vcbVolBkUp;
fsap->f_backup_time.tv_nsec = 0;
VFSATTR_SET_SUPPORTED(fsap, f_backup_time);
+
if (VFSATTR_IS_ACTIVE(fsap, f_fssubtype)) {
u_int16_t subtype = 0;
/*
- * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <miscfs/specfs/specdev.h>
#include <miscfs/fifofs/fifo.h>
#include <vfs/vfs_support.h>
-#include <machine/spl.h>
#include <sys/kdebug.h>
#include <sys/sysctl.h>
static int hfs_move_fork(filefork_t *srcfork, cnode_t *src,
filefork_t *dstfork, cnode_t *dst);
+
+static int hfs_exchangedata_getxattr (struct vnode *vp, uint32_t name_selector, void **buffer, size_t *xattr_size);
+static int hfs_exchangedata_setxattr (struct hfsmount *hfsmp, uint32_t fileid,
+ uint32_t name_selector, void *buffer, size_t xattr_size);
+
+enum XATTR_NAME_ENTRIES {
+ quarantine = 0,
+ MAX_NUM_XATTR_NAMES //must be last
+};
+
+
+/* These are special EAs that follow the content in exchangedata(2). */
+const char *XATTR_NAMES [MAX_NUM_XATTR_NAMES] = { "com.apple.quarantine" };
+
+#define MAX_EXCHANGE_EA_SIZE 4096
+
#if HFS_COMPRESSION
static int hfs_move_compressed(cnode_t *from_vp, cnode_t *to_vp);
#endif
}
#endif // HFS_COMPRESSION
+
+/* Helper Functions for exchangedata(2) */
+
+/*
+ * hfs_exchangedata_getxattr
+ * arguments:
+ * vp: vnode to extract the EA for
+ * name_selector: the index into the array of EA name entries.
+ * buffer: address for output buffer to store the output EA
+ * NOTE: This function will allocate the buffer, it is the caller's responsibility to free it.
+ * xattr_size: output argument; will return the size of the EA, to correspond with the buffer.
+ *
+ * Return: 0 on success.
+ * errno on error. If we return any error, the buffer is guaranteed to be NULL.
+ *
+ * Assumes CNODE lock held on cnode for 'vp'
+ */
+static
+int hfs_exchangedata_getxattr (struct vnode *vp, uint32_t name_selector, void **buffer, size_t *xattr_size) {
+ void *xattr_rawdata = NULL;
+ void *extracted_xattr = NULL;
+ uio_t uio;
+ size_t memsize = MAX_EXCHANGE_EA_SIZE;
+ size_t attrsize;
+ int error = 0;
+ struct hfsmount *hfsmp = NULL;
+
+ /* Sanity check inputs */
+ if (name_selector > MAX_NUM_XATTR_NAMES) {
+ return EINVAL;
+ }
+
+ if (buffer == NULL || xattr_size == NULL) {
+ return EINVAL;
+ }
+
+ hfsmp = VTOHFS(vp);
+
+ //allocate 4k memory to hold the EA. We don't use this for "large" EAs, and the default
+ //EA B-tree size should produce inline attributes of size < 4K
+ xattr_rawdata = hfs_malloc (MAX_EXCHANGE_EA_SIZE);
+ if (!xattr_rawdata) {
+ return ENOMEM;
+ }
+
+ //now create the UIO
+ uio = uio_create (1, 0, UIO_SYSSPACE, UIO_READ);
+ if (!uio) {
+ hfs_free (xattr_rawdata, memsize);
+ return ENOMEM;
+ }
+ uio_addiov(uio, CAST_USER_ADDR_T(xattr_rawdata), memsize);
+ attrsize = memsize;
+
+ struct vnop_getxattr_args vga = {
+ .a_uio = uio,
+ .a_name = XATTR_NAMES[name_selector],
+ .a_size = &attrsize
+ };
+
+ // this takes care of grabbing the systemfile locks for us.
+ error = hfs_getxattr_internal (VTOC(vp), &vga, hfsmp, 0);
+
+ if (error) {
+ /*
+ * We could have gotten a variety of errors back from the XATTR tree:
+ * is it too big? (bigger than 4k?) == ERANGE
+ * was the EA not found? == ENOATTR
+ */
+ uio_free(uio);
+ hfs_free (xattr_rawdata, memsize);
+ return error;
+ }
+
+ //free the UIO
+ uio_free(uio);
+
+ //upon success, a_size/attrsize now contains the actua/exported EA size
+ extracted_xattr = hfs_malloc (attrsize);
+ memcpy (extracted_xattr, xattr_rawdata, attrsize);
+ hfs_free (xattr_rawdata, memsize);
+
+ *xattr_size = attrsize;
+ *buffer = extracted_xattr;
+
+ return error;
+}
+
+
+/*
+ * hfs_exchangedata_setxattr
+ *
+ * Note: This function takes fileIDs in as inputs, because exchangedata does
+ * swizzly things with the two cnodes (See big block comment in hfs_vnop_exchange)
+ * so we operate with FileIDs more or less directly on the XATTR b-tree.
+ *
+ * arguments:
+ * hfsmp: the mount we're working on
+ * fileid: the fileID of the EA to store into the tree.
+ * name_selector: selector into the EA name array.
+ * buffer: pointer to the memory of the EA to write.
+ * xattr_size: size of the EA to write.
+ *
+ * Returns 0 on success
+ * errno on failure
+ *
+ * Assumes that a transaction has already begun when this is called
+ */
+
+static
+int hfs_exchangedata_setxattr (struct hfsmount *hfsmp, uint32_t fileid,
+ uint32_t name_selector, void *buffer, size_t xattr_size) {
+
+ int error = 0;
+
+
+ /* Sanity check arguments */
+ if (name_selector > MAX_NUM_XATTR_NAMES) {
+ return EINVAL;
+ }
+
+ if (buffer == NULL || xattr_size == 0 || fileid < kHFSFirstUserCatalogNodeID ) {
+ return EINVAL;
+ }
+
+ // is the size too big?
+ if (xattr_size > hfsmp->hfs_max_inline_attrsize) {
+ return EINVAL;
+ }
+
+ /* setup the arguments to setxattr*/
+ struct vnop_setxattr_args vsa = {
+ .a_desc = NULL,
+ .a_vp = NULL,
+ .a_name = XATTR_NAMES[name_selector],
+ .a_uio = NULL, // we use the data_ptr argument to setxattr_internal instead
+ .a_options = 0,
+ .a_context = NULL // no context needed, only done from within exchangedata
+ };
+
+ /*
+ * Since we must be in a transaction to guard the exchangedata operation, this will start
+ * a nested transaction within the exchangedata one.
+ */
+ error = hfs_setxattr_internal (NULL, (caddr_t) buffer, xattr_size, &vsa, hfsmp, fileid);
+
+ return error;
+
+}
+
/*
* hfs_vnop_exchange:
*
char to_iname[32];
uint32_t to_flag_special;
uint32_t from_flag_special;
+
+ uint16_t to_recflags_special;
+ uint16_t from_recflags_special;
+
cnid_t from_parid;
cnid_t to_parid;
int lockflags;
cat_cookie_t cookie;
time_t orig_from_ctime, orig_to_ctime;
bool have_cnode_locks = false, have_from_trunc_lock = false, have_to_trunc_lock = false;
+
+ /* For the quarantine EA */
+ void *from_xattr = NULL;
+ void *to_xattr = NULL;
+ size_t from_attrsize = 0;
+ size_t to_attrsize = 0;
+
/*
* VFS does the following checks:
}
/*
- * Ok, now that all of the pre-flighting is done, call the underlying
- * function if needed.
+ * If doing a data move, then call the underlying function.
*/
if (ISSET(ap->a_options, FSOPT_EXCHANGE_DATA_ONLY)) {
#if HFS_COMPRESSION
goto exit;
}
+ /*
+ * If we're doing a normal exchangedata, then get the source/dst quarantine
+ * EAs as needed. We do it here before we start the transaction.
+ */
+
+ //get the EA for the 'from' vnode if it exists.
+ error = hfs_exchangedata_getxattr (from_vp, quarantine, &from_xattr, &from_attrsize);
+ if (error) {
+ if (error == ENOATTR) {
+ //it's OK for the quarantine EA to not exist
+ error = 0;
+ }
+ else {
+ goto exit;
+ }
+ }
+
+
+ //get the EA from the 'to' vnode if it exists
+ error = hfs_exchangedata_getxattr (to_vp, quarantine, &to_xattr, &to_attrsize);
+ if (error) {
+ if (error == ENOATTR) {
+ //it's OK for the quarantine EA to not exist
+ error = 0;
+ }
+ else {
+ goto exit;
+ }
+ }
+
+
+ /* Start a transaction; we have to do all of this atomically */
if ((error = hfs_start_transaction(hfsmp)) != 0) {
goto exit;
}
*/
error = ExchangeFileIDs(hfsmp, from_nameptr, to_nameptr, from_parid,
to_parid, from_cp->c_hint, to_cp->c_hint);
- hfs_systemfile_unlock(hfsmp, lockflags);
-
- /*
- * Note that we don't need to exchange any extended attributes
- * since the attributes are keyed by file ID.
- */
+ hfs_systemfile_unlock(hfsmp, lockflags);
if (error != E_NONE) {
error = MacToVFSError(error);
goto exit;
}
+ /*
+ * Now, we have to swap the quarantine EA.
+ *
+ * Ordinarily, we would not have to swap/exchange any extended attributes,
+ * since they are keyed by the file ID, and this function is supposed
+ * to manipulate the main data stream/fork only.
+ *
+ * However, we want the quarantine EA to follow the file content.
+ */
+
+ int from_xattr_status = 0;
+ if (from_xattr) {
+ /*
+ * Caution!
+ * We've crossed a point of no return here, because if we
+ * have successfully swapped the file content above, we need to continue here
+ * to swap the rest of the cnode content, which is not subject to failure.
+ * Failing the whole function because the xattr swap will result in perceived
+ * data loss to the caller, so we swallow the error case here.
+ */
+ from_xattr_status = hfs_removexattr_by_id (hfsmp, from_cp->c_fileid, XATTR_NAMES[quarantine]);
+ if (from_xattr_status == 0) {
+ int xattr_lockflags;
+ int remaining_eas;
+ /*
+ * Check to see if we need to remove the xattr bit from the catalog record flags while
+ * 'from_cp' still tracks with its original file ID. Once the cnodes' contents are swapped
+ * and they are ready to be re-hashed, we will OR in the bit if we know that we moved the
+ * EA to the counterpart.
+ */
+ xattr_lockflags = hfs_systemfile_lock (hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
+ remaining_eas = file_attribute_exist (hfsmp, from_cp->c_fileid);
+ if (remaining_eas == 0) {
+ from_cp->c_attr.ca_recflags &= ~kHFSHasAttributesMask;
+ //the cnode will be pushed out to disk LATER on.
+ }
+ hfs_systemfile_unlock (hfsmp, xattr_lockflags);
+
+ }
+ }
+
+ //and the same for to_xattr
+ if (to_xattr) {
+ int xattr_status = hfs_removexattr_by_id (hfsmp, to_cp->c_fileid, XATTR_NAMES[quarantine]);
+
+ if (xattr_status == 0) {
+ int xattr_lockflags;
+ int remaining_eas;
+ /*
+ * Check to see if we need to remove the xattr bit from the catalog record flags while
+ * 'to_cp' still tracks with its original file ID. Once the cnodes' contents are swapped
+ * and they are ready to be re-hashed, we will OR in the bit if we know that we moved the
+ * EA to the counterpart.
+ */
+ xattr_lockflags = hfs_systemfile_lock (hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
+ remaining_eas = file_attribute_exist (hfsmp, from_cp->c_fileid);
+ if (remaining_eas == 0) {
+ to_cp->c_attr.ca_recflags &= ~kHFSHasAttributesMask;
+ //the cnode will be pushed out to disk LATER on.
+ }
+ hfs_systemfile_unlock (hfsmp, xattr_lockflags);
+
+ /* Now move the EA to the counterparty fileID. We piggyback on the larger transaction here */
+ hfs_exchangedata_setxattr (hfsmp, from_cp->c_fileid, quarantine, to_xattr, to_attrsize);
+ }
+ }
+
+ if (from_xattr && from_xattr_status == 0) {
+ /*
+ * if the from EA got removed properly, then attach it to the 'to' file. We do it at this point
+ * to ensure that it got removed properly above before re-setting it again.
+ */
+ hfs_exchangedata_setxattr (hfsmp, to_cp->c_fileid, quarantine, from_xattr, from_attrsize);
+ }
+
+
/* Purge the vnodes from the name cache */
if (from_vp)
cache_purge(from_vp);
/* Save whether or not each cnode is a hardlink or has EAs */
from_flag_special = from_cp->c_flag & (C_HARDLINK | C_HASXATTRS);
+ from_recflags_special = (from_cp->c_attr.ca_recflags & kHFSHasAttributesMask);
+
to_flag_special = to_cp->c_flag & (C_HARDLINK | C_HASXATTRS);
+ to_recflags_special = (to_cp->c_attr.ca_recflags & kHFSHasAttributesMask);
/* Drop the special bits from each cnode */
from_cp->c_flag &= ~(C_HARDLINK | C_HASXATTRS);
to_cp->c_flag &= ~(C_HARDLINK | C_HASXATTRS);
+ from_cp->c_attr.ca_recflags &= ~(kHFSHasAttributesMask);
+ to_cp->c_attr.ca_recflags &= ~(kHFSHasAttributesMask);
/*
* Now complete the in-memory portion of the copy.
* The flags that are special are:
* C_HARDLINK, C_HASXATTRS
*
+ * and the c_attr recflag:
+ * kHFSHasAttributesMask
+ *
* These flags move with the item and file ID in the namespace since their
* state is tied to that of the file ID.
*
*/
from_cp->c_flag |= to_flag_special | C_MODIFIED;
from_cp->c_attr.ca_recflags = to_cp->c_attr.ca_recflags;
+ from_cp->c_attr.ca_recflags |= to_recflags_special;
+ if (from_xattr) {
+ /*
+ * NOTE:
+ * This is counter-intuitive and part of the complexity of exchangedata.
+ * if 'from_cp' originally had a quarantine EA, then ensure that the cnode
+ * pointed to by 'from_cp' CONTINUES to keep the "has EAs" bit. This is because
+ * the cnode is about to be re-hashed with a new ID, but the file CONTENT
+ * (i.e. the file fork) stayed put. And we want the quarantine EA to follow
+ * the content. The check above is correct.
+ */
+ from_cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
+ }
+
bcopy(to_cp->c_finderinfo, from_cp->c_finderinfo, 32);
* Leave the rest of the flags alone.
*/
to_cp->c_flag |= from_flag_special | C_MODIFIED;
-
to_cp->c_attr.ca_recflags = tempattr.ca_recflags;
+ to_cp->c_attr.ca_recflags |= from_recflags_special;
+
+ if (to_xattr) {
+ /*
+ * NOTE:
+ * This is counter-intuitive and part of the complexity of exchangedata.
+ * if 'to_cp' originally had a quarantine EA, then ensure that the cnode
+ * pointed to by 'to_cp' CONTINUES to keep the "has EAs" bit. This is because
+ * the cnode is about to be re-hashed with a new ID, but the file CONTENT
+ * (i.e. the file fork) stayed put. And we want the quarantine EA to follow
+ * the content. The check above is correct.
+ */
+ to_cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
+ }
+
bcopy(tempattr.ca_finderinfo, to_cp->c_finderinfo, 32);
if (have_to_trunc_lock)
hfs_unlock_truncate(to_cp, 0);
+ /* Free the memory used by the EAs */
+ if (from_xattr) {
+ hfs_free (from_xattr, from_attrsize);
+ from_xattr = NULL;
+ }
+
+ if (to_xattr) {
+ hfs_free (to_xattr, to_attrsize);
+ to_xattr = NULL;
+ }
+
return (error);
}
int eofflag = 0;
user_addr_t user_start = 0;
user_size_t user_len = 0;
+ user_size_t user_original_resid = 0;
int index;
unsigned int tag;
int items;
if (user_len > (256 * 1024)) {
/* only allow the user to wire down at most 256k */
user_len = (256 * 1024);
+ user_original_resid = uio_resid(uio);
uio_setresid (uio, (user_ssize_t)(256 * 1024));
}
if ((error = vslock(user_start, user_len)) != 0) {
+ if (user_original_resid > 0) {
+ uio_setresid(uio, user_original_resid);
+ user_original_resid = 0;
+ }
return error;
}
}
/* Note that the dirhint calls require an exclusive lock. */
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
if (user_start) {
+ if (user_original_resid > 0) {
+ uio_setresid(uio, user_original_resid);
+ user_original_resid = 0;
+ }
vsunlock(user_start, user_len, TRUE);
}
return error;
error = 0;
eofflag = 1;
uio_setoffset(uio, startoffset);
+ if (user_original_resid > 0) {
+ uio_setresid(uio, user_original_resid);
+ user_original_resid = 0;
+ }
hfs_systemfile_unlock (hfsmp, lockflags);
goto seekoffcalc;
/* Pack the buffer with dirent entries. */
error = cat_getdirentries(hfsmp, cp->c_entries, dirhint, uio, ap->a_flags, &items, &eofflag);
+ if (user_original_resid > 0) {
+ user_original_resid = user_original_resid - ((user_ssize_t)256*1024 - uio_resid(uio));
+ uio_setresid(uio, user_original_resid);
+ user_original_resid = 0;
+ }
+
if (index == 0 && error == 0) {
cp->c_dirthreadhint = dirhint->dh_threadhint;
}
out:
if (user_start) {
+ if (user_original_resid > 0) {
+ uio_setresid(uio, user_original_resid);
+ user_original_resid = 0;
+ }
vsunlock(user_start, user_len, TRUE);
}
/* If we didn't do anything then go ahead and dump the hint. */
/*
- * Copyright (c) 2004-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2017 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
return MacToVFSError(result);
}
+/*
+ * Removes a non rsrc-fork, non-finderinfo EA from the specified file ID.
+ * Note that this results in a bit of code duplication for the xattr removal
+ * path. This is done because it's a bit messy to deal with things without the
+ * cnode. This function is used by exchangedata to port XATTRS to alternate
+ * fileIDs while everything is locked, and the cnodes are in a transitional state.
+ *
+ * Assumes that the cnode backing the fileid specified is LOCKED.
+ */
+
+int
+hfs_removexattr_by_id (struct hfsmount *hfsmp, uint32_t fileid, const char *xattr_name ) {
+ struct BTreeIterator iter; // allocated on the stack to avoid heap allocation mid-txn
+ int ret = 0;
+ int started_txn = 0;
+ int lockflags;
+
+ memset (&iter, 0, sizeof(iter));
+
+ //position the B-Tree iter key before grabbing locks and starting a txn
+ ret = hfs_buildattrkey (fileid, xattr_name, (HFSPlusAttrKey*)&iter.key);
+ if (ret) {
+ goto xattr_out;
+ }
+
+ //note: this is likely a nested transaction since there is a global transaction cover
+ if (hfs_start_transaction (hfsmp) != 0) {
+ ret = EINVAL;
+ goto xattr_out;
+ }
+ started_txn = 1;
+
+
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
+
+ //actually remove the EA from the tree
+ ret = remove_attribute_records(hfsmp, &iter);
+
+ hfs_systemfile_unlock(hfsmp, lockflags);
+
+ /*
+ * NOTE: Responsibility of the caller to remove the "has XATTRs" bit in the catalog record
+ * if this was the last EA.
+ */
+
+
+xattr_out:
+ if (started_txn) {
+ hfs_end_transaction(hfsmp);
+ }
+
+ return MacToVFSError(ret);
+
+}
+
+
/* Check if any attribute record exist for given fileID. This function
* is called by hfs_vnop_removexattr to determine if it should clear the
* attribute bit in the catalog record or not.
return (ENOTSUP);
}
#endif
- if (xattrtype != HFS_SET_XATTREXTENTS_STATE) {
+ if (xattrtype != HFSIOC_SET_XATTREXTENTS_STATE) {
return EINVAL;
}
<string>fsck_hfs</string>
<key>FSLiveVerificationArguments</key>
<string>-l</string>
+ <key>FSEncryptionName</key>
+ <string>Mac OS Extended (Journaled, Encrypted)</string>
<key>FSCoreStorageEncryptionName</key>
<string>Mac OS Extended (Journaled, Encrypted)</string>
<key>FSfileObjectsAreCasePreserving</key>
<string>fsck_hfs</string>
<key>FSLiveVerificationArguments</key>
<string>-l</string>
+ <key>FSEncryptionName</key>
+ <string>Mac OS Extended (Case-sensitive, Journaled, Encrypted)</string>
<key>FSCoreStorageEncryptionName</key>
<string>Mac OS Extended (Case-sensitive, Journaled, Encrypted)</string>
<key>FSfileObjectsAreCasePreserving</key>
* currently. Also check extents only in verify stage to avoid
* false overlap extents error.
*/
+
if (dfaStage == kVerifyStage) {
/* Start block in the key should be zero */
if (key->startBlock != 0) {
goto err_out;
}
+ HFSPlusForkData forkData;
+ memcpy((void*)(&forkData), (void*)(&rec->forkData.theFork), sizeof(HFSPlusForkData));
/* Check the extent information and record overlapping extents, if any */
- result = CheckFileExtents (GPtr, fileID, kEAData, attrname,
- rec->forkData.theFork.extents, &blocks);
+ result = CheckFileExtents (GPtr, fileID, kEAData, attrname,
+ &forkData.extents, &blocks);
if (result) {
goto update_out;
}
.It 3
A quick-check (the
.Fl n
-option) found a dirty filesystem; no repairs were made.
+option) found a dirty filesystem; no repairs were made. There is a potential
+corruption in the filesystem, and either the journal could not be read, or a
+runtime corruption was present so the HFS Volume Inconsistent bit was set.
.It 4
During boot, the root filesystem was found to be dirty; repairs were
made, and the filesystem was remounted. The system should be rebooted.
objects = {
/* Begin PBXAggregateTarget section */
+ 07828B591E3FDD25009D2106 /* hfs_libraries */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 07828B5D1E3FDD25009D2106 /* Build configuration list for PBXAggregateTarget "hfs_libraries" */;
+ buildPhases = (
+ );
+ dependencies = (
+ 07828B5F1E3FDD2E009D2106 /* PBXTargetDependency */,
+ );
+ name = hfs_libraries;
+ productName = hfs_libraries;
+ };
4DBD523B1548A488007AA736 /* Common */ = {
isa = PBXAggregateTarget;
buildConfigurationList = 4DBD523C1548A488007AA736 /* Build configuration list for PBXAggregateTarget "Common" */;
dependencies = (
FBA540011B7BF2DF00899E5B /* PBXTargetDependency */,
FBC234C41B4EC6AE0002D849 /* PBXTargetDependency */,
- 8657285C18319A93007F580F /* PBXTargetDependency */,
4DBD523F1548A499007AA736 /* PBXTargetDependency */,
4DBD52411548A49A007AA736 /* PBXTargetDependency */,
4DBD52431548A49D007AA736 /* PBXTargetDependency */,
4DBD52471548A4A2007AA736 /* PBXTargetDependency */,
4DBD52491548A4A4007AA736 /* PBXTargetDependency */,
4DBD524B1548A4A7007AA736 /* PBXTargetDependency */,
- 4DBD524D1548A4AA007AA736 /* PBXTargetDependency */,
);
name = Common;
productName = Common;
buildPhases = (
);
dependencies = (
+ 07CA47231DA859CA00138D78 /* PBXTargetDependency */,
FBE3A5A51BBEE34400CB9A33 /* PBXTargetDependency */,
FB48E4BC1BB30CC400523121 /* PBXTargetDependency */,
FBC234C61B4EC6B90002D849 /* PBXTargetDependency */,
buildPhases = (
);
dependencies = (
+ 07828B611E3FDD3B009D2106 /* PBXTargetDependency */,
FBE3A5A31BBEE33D00CB9A33 /* PBXTargetDependency */,
4DBD524F1548A4C8007AA736 /* PBXTargetDependency */,
);
/* Begin PBXBuildFile section */
0703A0541CD826160035BCFD /* test-defrag.c in Sources */ = {isa = PBXBuildFile; fileRef = 0703A0531CD826160035BCFD /* test-defrag.c */; };
07C2BF891CB43F5E00D8327D /* test-renamex.c in Sources */ = {isa = PBXBuildFile; fileRef = 07C2BF881CB43F5E00D8327D /* test-renamex.c */; };
+ 09D6B7D71E317ED2003C20DC /* test_disklevel.c in Sources */ = {isa = PBXBuildFile; fileRef = 09D6B7D61E317ED2003C20DC /* test_disklevel.c */; };
2A386A3B1C22209C007FEDAC /* test-list-ids.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A386A3A1C221E67007FEDAC /* test-list-ids.c */; };
2A84DBD41D9E15F2007964B8 /* test-raw-dev-unaligned.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A84DBD31D9E1179007964B8 /* test-raw-dev-unaligned.c */; };
2A9399951BDFEB5200FB075B /* test-access.c in Sources */ = {isa = PBXBuildFile; fileRef = 2A9399941BDFEA6E00FB075B /* test-access.c */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
+ 07828B5E1E3FDD2E009D2106 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 86CBF37E183186C300A64A93;
+ remoteInfo = hfs_metadata;
+ };
+ 07828B601E3FDD3B009D2106 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 07828B591E3FDD25009D2106;
+ remoteInfo = hfs_libraries;
+ };
+ 07CA47221DA859CA00138D78 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 4D07DCB71538EF3A002B57CB;
+ remoteInfo = fstyp_hfs;
+ };
4DBD523E1548A499007AA736 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
remoteGlobalIDString = 4DFD93F31535FF510039B6BA;
remoteInfo = fsck_hfs;
};
- 4DBD524C1548A4AA007AA736 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = 4D07DCB71538EF3A002B57CB;
- remoteInfo = fstyp_hfs;
- };
4DBD524E1548A4C8007AA736 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
remoteGlobalIDString = 4DFD94BC15373C2C0039B6BA;
remoteInfo = fsck_makestrings;
};
- 8657285B18319A93007F580F /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = 86CBF37E183186C300A64A93;
- remoteInfo = hfs_metadata;
- };
FB48E4BB1BB30CC400523121 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
/* Begin PBXFileReference section */
0703A0531CD826160035BCFD /* test-defrag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-defrag.c"; sourceTree = "<group>"; };
07C2BF881CB43F5E00D8327D /* test-renamex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-renamex.c"; sourceTree = "<group>"; };
+ 09D6B7D61E317ED2003C20DC /* test_disklevel.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = test_disklevel.c; sourceTree = "<group>"; };
2A386A3A1C221E67007FEDAC /* test-list-ids.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-list-ids.c"; sourceTree = "<group>"; };
2A84DBD31D9E1179007964B8 /* test-raw-dev-unaligned.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-raw-dev-unaligned.c"; sourceTree = "<group>"; };
2A9399941BDFEA6E00FB075B /* test-access.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "test-access.c"; sourceTree = "<group>"; };
FB285C281B7E81180099B2ED /* test-sparse-dev.c */,
FB2B5C551B87656900ACEDD9 /* test-transcode.m */,
FBD69AF81B91309C0022ECAD /* test-dateadded.c */,
+ 09D6B7D61E317ED2003C20DC /* test_disklevel.c */,
);
path = cases;
sourceTree = "<group>";
attributes = {
LastUpgradeCheck = 0710;
TargetAttributes = {
+ 07828B591E3FDD25009D2106 = {
+ CreatedOnToolsVersion = 8.3;
+ ProvisioningStyle = Automatic;
+ };
FB20E0DF1AE950C200CEBE7B = {
CreatedOnToolsVersion = 6.3;
};
FBCC52FD1B852758008B752C /* hfs-alloc-trace */,
FB48E5031BB3798500523121 /* Sim_Headers */,
FB7C140C1C2368E6004F8B2C /* kext-version */,
+ 07828B591E3FDD25009D2106 /* hfs_libraries */,
);
};
/* End PBXProject section */
2A9399D31BE2C06800FB075B /* test-uncached-io.c in Sources */,
2A9399D11BE2BFFD00FB075B /* test-throttled-io.c in Sources */,
2A9399CF1BE2BCEA00FB075B /* test-mod-time.c in Sources */,
+ 09D6B7D71E317ED2003C20DC /* test_disklevel.c in Sources */,
2A9399CD1BE2BC6900FB075B /* test-mmap-mod-time.c in Sources */,
2A9399CA1BE18A5000FB075B /* test-invalid-ranges.m in Sources */,
2A9399C91BE1747900FB075B /* test-map-private.m in Sources */,
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ 07828B5F1E3FDD2E009D2106 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 86CBF37E183186C300A64A93 /* hfs_metadata */;
+ targetProxy = 07828B5E1E3FDD2E009D2106 /* PBXContainerItemProxy */;
+ };
+ 07828B611E3FDD3B009D2106 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 07828B591E3FDD25009D2106 /* hfs_libraries */;
+ targetProxy = 07828B601E3FDD3B009D2106 /* PBXContainerItemProxy */;
+ };
+ 07CA47231DA859CA00138D78 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 4D07DCB71538EF3A002B57CB /* fstyp_hfs */;
+ targetProxy = 07CA47221DA859CA00138D78 /* PBXContainerItemProxy */;
+ };
4DBD523F1548A499007AA736 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4DFD95111537402A0039B6BA /* hfs.fs */;
target = 4DFD93F31535FF510039B6BA /* fsck_hfs */;
targetProxy = 4DBD524A1548A4A7007AA736 /* PBXContainerItemProxy */;
};
- 4DBD524D1548A4AA007AA736 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = 4D07DCB71538EF3A002B57CB /* fstyp_hfs */;
- targetProxy = 4DBD524C1548A4AA007AA736 /* PBXContainerItemProxy */;
- };
4DBD524F1548A4C8007AA736 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 4DBD523B1548A488007AA736 /* Common */;
target = 4DFD94BC15373C2C0039B6BA /* fsck_makestrings */;
targetProxy = 4DBD52521548A4D4007AA736 /* PBXContainerItemProxy */;
};
- 8657285C18319A93007F580F /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = 86CBF37E183186C300A64A93 /* hfs_metadata */;
- targetProxy = 8657285B18319A93007F580F /* PBXContainerItemProxy */;
- };
FB48E4BC1BB30CC400523121 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = FB48E49B1BB3070400523121 /* OSX Kernel Framework Headers */;
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
+ 07828B5A1E3FDD25009D2106 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+ 07828B5B1E3FDD25009D2106 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 07828B5C1E3FDD25009D2106 /* Coverage */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Coverage;
+ };
1DEB928708733DD80010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 07828B5D1E3FDD25009D2106 /* Build configuration list for PBXAggregateTarget "hfs_libraries" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 07828B5A1E3FDD25009D2106 /* Release */,
+ 07828B5B1E3FDD25009D2106 /* Debug */,
+ 07828B5C1E3FDD25009D2106 /* Coverage */,
+ );
+ defaultConfigurationIsVisible = 0;
+ };
1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "hfs.util" */ = {
isa = XCConfigurationList;
buildConfigurations = (
assert_fail("access_vector[0] != ENOENT (== %u)!", access_vector[0]);
free(path);
+ assert_no_err (close(fd));
return 0;
}
assert_no_err(unlink(file3));
assert_no_err(rmdir(dir));
+ assert_no_err(close(fd));
return 0;
}
assert_equal(orig_doc_id, attrs.doc_id, "%u");
assert_no_err(rmdir(file));
-
+
return 0;
-
}
{
char *path;
int fd;
- void *buf = malloc(1 KB);
+ void *buf;
// Create a test folder with MAX_FILES files
assert_no_err(systemx("/bin/rm", "-rf", srcdir, NULL));
assert_with_errno((fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
free(path);
- unsigned buf_size = (1 KB) * i;
+ unsigned buf_size = (1 KB) * (i + 1);
buf = malloc(buf_size);
memset(buf, 0x25, buf_size);
check_io(write(fd, buf, buf_size), buf_size);
+ free(buf);
+ close(fd);
}
}
free(file);
free(sock);
free(fifo);
+ free(buf);
return 0;
}
pwrite(fd, "hello", 5, i * ps * 2);
}
+ assert_no_err(munmap(p, o + 2 * ps));
+
// OK, that should have created 1024 invalid ranges. Sync the data.
p = mmap(NULL, 1024 * ps * 2, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, 0);
assert_no_err(unlink(file));
+ assert_no_err(munmap(p, 1024 * ps * 2));
+
#if !TARGET_OS_EMBEDDED
disk_image_t *di2 = disk_image_create(DISK_IMAGE, &(disk_image_opts_t){
.size = 100 * 1024 * 1024
char *fsync_path;
asprintf(&fsync_path, "%s/fsync", di->mount_point);
+ assert_no_err(close(fd));
fd = open(fsync_path, O_CREAT | O_RDWR | O_TRUNC, 0666);
assert_with_errno(fd >= 0);
// Attach to the disk image again
assert(!systemx("/usr/bin/hdiutil", SYSTEMX_QUIET, "attach", DISK_IMAGE, NULL));
+ assert_no_err(close(fd));
assert_with_errno((fd = open(path, O_RDWR)) >= 0);
// Either the file should be short, or there should be zeroes
ssize_t amount = pread(fd, buf, block_size, block_size);
assert_with_errno(amount >= 0);
- assert(!memcmp(buf, zero, amount));
+ assert(!memcmp(buf, zero, (amount > 1000) ? 1000 : amount));
assert_no_err(close(fd));
check_io(pread(fd, buf3, 0x100000, 0x100000), 0x100000);
assert(!memcmp(buf2, buf3, 0x100000));
+ free(buf3);
+ free(buf2);
+ free(buf);
+ free(zero);
assert_no_err(close(fd));
assert_no_err(unlink(file));
assert_no_err(unlink(file));
+ assert_no_err(close(fd));
free(file);
return 0;
assert_no_err(close(fd));
assert_no_err(close(fd2));
+
assert_no_err(unlink(file));
+ assert_no_err(munmap(p, NBPG));
}
int run_mod_time(__unused test_ctx_t *ctx)
.l2p_contigbytes = 1024 * 1024,
.l2p_devoffset = len - 1,
};
-
if (fcntl(fd, F_LOG2PHYS_EXT, &l2p)) {
if (errno == ERANGE)
break;
{
di = disk_image_get();
- int fd = make_frag_file(1);
+ int frag_fd = make_frag_file(1);
+ int fd;
+ int fd2;
struct stat sb;
- fstat(fd, &sb);
+ fstat(frag_fd, &sb);
- int fd2;
asprintf(&file2, "%s/move_data_extents.2", di->mount_point);
assert_with_errno((fd2 = open(file2,
#define F_MOVEDATAEXTENTS 69
- assert_no_err(fcntl(fd, F_MOVEDATAEXTENTS, fd2));
+ assert_no_err(fcntl(frag_fd, F_MOVEDATAEXTENTS, fd2));
char buf[4096];
check_io(pread(fd2, buf, 4096, sb.st_size - 4096), 4096);
pthread_join(thread, NULL);
close(fd);
+ close(frag_fd);
/*
* Make sure that the extents from move_data_extents.1 got deleted
assert_no_err(unlink(file2));
+ assert_no_err (close(fd));
+ assert_no_err (close(fd2));
+ assert_no_err (close(fd3));
+
free(file1);
free(file2);
free(file3);
assert_no_err(unlink(file));
+ free(cmp_buf);
+ free(buf);
free(file);
return 0;
error = unlink(dst_file);
assert (error == 0);
+ assert_no_err(close(src_file_fd));
+ assert_no_err(close(dst_file_fd));
+
return 0;
}
assert(attrs.cr_time.tv_sec == 2000 && attrs.mod_time.tv_sec == 1000);
+ assert_no_err (close(fd));
unlink(file);
free(file);
assert_no_err(close(fd));
if (fd2 != -1)
assert_no_err(close(fd2));
+
+ assert_no_err(munmap(p, size));
}
unlink(path);
+ free(buf);
+ free(buf2);
return 0;
}
assert(sfs.f_bfree * sfs.f_bsize < 64 * 1024 * 1024);
+
+ assert_no_err (close(fd));
+
return 0;
}
} while (!done);
assert_no_err (close(fd));
-
}
static int run_test2(void)
int run_throttled_io(__unused test_ctx_t *ctx)
{
+
gDI = disk_image_get();
asprintf(&gFile1, "%s/throttled_io.1", gDI->mount_point);
test_cleanup(^ bool {
return clean_up();
});
-
+
int res = run_test1();
if (res)
return res;
-
+
res = run_test2();
if (res)
return res;
assert_no_err(MKBKeyBagRegisterOTABackup(NULL, NULL));
assert_no_err(MKBKeyBagRelease(handle));
+ close(fd);
+ free(key);
+
return 0;
}
} // for (pass...)
assert_no_err(unlink(file1));
-
+
+ assert_no_err (close(fd));
+ assert_no_err (close(fd2));
free(file1);
free(file2);
assert((fd = open(path3, O_RDONLY)) >= 0);
assert_no_err(unlink(path3));
-
+ assert_no_err (close(fd));
+
free(dir);
free(path);
free(path2);
--- /dev/null
+#include <fcntl.h>
+#include <sys/attr.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "hfs-tests.h"
+#include "test-utils.h"
+#include "../core/hfs_fsctl.h"
+#include "disk-image.h"
+
+TEST(disklevel)
+
+int run_disklevel(__unused test_ctx_t *ctx)
+{
+ disk_image_t *di = disk_image_get();
+ const char *test_hfs_volume = di->mount_point;
+ uint32_t very_low_disk = 0, low_disk = 0, near_low_disk = 0, desired_disk = 0;
+
+ // Make sure initial values are sane.
+ assert_no_err(fsctl(test_hfs_volume, HFSIOC_GET_VERY_LOW_DISK, &very_low_disk, 0));
+ assert_no_err(fsctl(test_hfs_volume, HFSIOC_GET_LOW_DISK, &low_disk, 0));
+ assert_no_err(fsctl(test_hfs_volume, APFSIOC_GET_NEAR_LOW_DISK, &near_low_disk, 0));
+ assert_no_err(fsctl(test_hfs_volume, HFSIOC_GET_DESIRED_DISK, &desired_disk, 0));
+ assert(very_low_disk > 0);
+ assert(very_low_disk < low_disk);
+ assert(low_disk < near_low_disk);
+ assert(near_low_disk < desired_disk);
+
+ very_low_disk = 1;
+ low_disk = 2;
+ near_low_disk = 3;
+ desired_disk = 4;
+ // Re-assign the values to new legal values and make sure they are preserved.
+ assert_no_err(fsctl(test_hfs_volume, HFSIOC_SET_VERY_LOW_DISK, &very_low_disk, 0));
+ assert_no_err(fsctl(test_hfs_volume, HFSIOC_SET_LOW_DISK, &low_disk, 0));
+ assert_no_err(fsctl(test_hfs_volume, APFSIOC_SET_NEAR_LOW_DISK, &near_low_disk, 0));
+ assert_no_err(fsctl(test_hfs_volume, HFSIOC_SET_DESIRED_DISK, &desired_disk, 0));
+
+ assert_no_err(fsctl(test_hfs_volume, HFSIOC_GET_VERY_LOW_DISK, &very_low_disk, 0));
+ assert_no_err(fsctl(test_hfs_volume, HFSIOC_GET_LOW_DISK, &low_disk, 0));
+ assert_no_err(fsctl(test_hfs_volume, APFSIOC_GET_NEAR_LOW_DISK, &near_low_disk, 0));
+ assert_no_err(fsctl(test_hfs_volume, HFSIOC_GET_DESIRED_DISK, &desired_disk, 0));
+ assert_equal(very_low_disk, 1, "%d");
+ assert_equal(low_disk, 2, "%d");
+ assert_equal(near_low_disk, 3, "%d");
+ assert_equal(desired_disk, 4, "%d");
+
+ // Now, attempt to reassign the levels to illegal values and make sure they don't lose their previous value.
+ very_low_disk = 4;
+ low_disk = 1;
+ near_low_disk = 2;
+ desired_disk = 0;
+ assert(fsctl(test_hfs_volume, HFSIOC_SET_VERY_LOW_DISK, &very_low_disk, 0) < 0);
+ assert(fsctl(test_hfs_volume, HFSIOC_SET_LOW_DISK, &low_disk, 0) < 0);
+ assert(fsctl(test_hfs_volume, APFSIOC_SET_NEAR_LOW_DISK, &near_low_disk, 0) < 0);
+ assert(fsctl(test_hfs_volume, HFSIOC_SET_DESIRED_DISK, &desired_disk, 0) < 0);
+
+ assert_no_err(fsctl(test_hfs_volume, HFSIOC_GET_VERY_LOW_DISK, &very_low_disk, 0));
+ assert_no_err(fsctl(test_hfs_volume, HFSIOC_GET_LOW_DISK, &low_disk, 0));
+ assert_no_err(fsctl(test_hfs_volume, APFSIOC_GET_NEAR_LOW_DISK, &near_low_disk, 0));
+ assert_no_err(fsctl(test_hfs_volume, HFSIOC_GET_DESIRED_DISK, &desired_disk, 0));
+ assert_equal(very_low_disk, 1, "%d");
+ assert_equal(low_disk, 2, "%d");
+ assert_equal(near_low_disk, 3, "%d");
+ assert_equal(desired_disk, 4, "%d");
+
+ return 0;
+}
* One 'shared' disk image is created for any test to use, if it wants.
* To use this disk image, call disk_image_get(). To create a 'not-shared'
* disk image for use just within your test, call disk_image_create().
+ *
+ * Callers of disk_image_create() and disk_image_get() should not free the pointer they receive,
+ * as it is freed automatically.
*/
__BEGIN_DECLS
bool disk_image_cleanup(disk_image_t *di)
{
pid_t pid;
+ bool result = false;
// We need to be root
assert(seteuid(0) == 0);
if (WIFEXITED(status) && !WEXITSTATUS(status)
&& stat(di->disk, &sb) == -1 && errno == ENOENT) {
unlink(di->path);
- return true;
+ result = true;
+ // We are the last user of di, so free it.
+ free(di->mount_point);
+ free(di->disk);
+ free(di->path);
+ free(di);
}
-
- return false;
+
+ return result;
}
void *zalloc(__unused void *opaque, uInt items, uInt size)
di->mount_point = strdup(opts->mount_point);
- char *chown_args[5] = { "chown", "-R", "mobile:mobile", (char *)di->mount_point, NULL };
- assert_no_err(posix_spawn(&pid, "/usr/sbin/chown", NULL, NULL, chown_args, NULL));
-
- assert_with_errno(ignore_eintr(waitpid(pid, &status, 0), -1) == pid);
- assert(WIFEXITED(status) && !WEXITSTATUS(status));
-
if (strcmp(path, SHARED_PATH)) { // Don't register a cleanup for the shared image
test_cleanup(^ bool {
return disk_image_cleanup(di);
di->mount_point = SHARED_MOUNT;
di->disk = strdup(sfs.f_mntfromname);
di->path = SHARED_PATH;
+
+ // Make sure the di struct is freed when tests are complete.
+ test_cleanup(^ bool {
+ free(di->disk);
+ free(di);
+ return true;
+ });
} else {
disk_image_opts_t opts = {
.mount_point = SHARED_MOUNT
};
di = disk_image_create(SHARED_PATH, &opts);
+ // Per the contract of disk_image_create(),
+ // di will be freed when disk_image_cleanup() is called,
+ // so don't free it here.
}
return di;
= { "hdiutil", "detach", (char *)di->disk, "-force", NULL };
pid_t pid;
+ bool result = false;
posix_spawn_file_actions_t facts;
posix_spawn_file_actions_init(&facts);
&& stat(di->disk, &sb) == -1 && errno == ENOENT) {
if (unlink(di->path) && errno == EACCES && !seteuid(0))
unlink(di->path);
- return true;
+ result = true;
+
+ // We are the last user of di, so free it.
+ free(di->mount_point);
+ free(di->disk);
+ free(di->path);
+ free(di);
}
-
- return false;
+
+ return result;
}
disk_image_t *disk_image_create(const char *path, disk_image_opts_t *opts)
di->mount_point = SHARED_MOUNT;
di->disk = strdup(sfs.f_mntfromname);
di->path = SHARED_PATH;
+
+ // Make sure the di struct is freed when tests are complete.
+ test_cleanup(^ bool {
+ free(di->disk);
+ free(di);
+ return true;
+ });
} else {
disk_image_opts_t opts = {
.size = 4 GB,
.mount_point = SHARED_MOUNT
};
di = disk_image_create(SHARED_PATH, &opts);
+ // Per the contract of disk_image_create(),
+ // di will be freed when disk_image_cleanup() is called,
+ // so don't free it here.
}
return di;
}
-#endif // TARGET_OS_EMBEDDED
\ No newline at end of file
+#endif // TARGET_OS_EMBEDDED
mkdir -p "$DERIVED_FILE_DIR"
env -i xcrun clang "$SRCROOT"/tests/img-to-c.c -lz -o "$DERIVED_FILE_DIR"/img-to-c
-"$DERIVED_FILE_DIR"/img-to-c -size 2g -type SPARSE -fs JHFS+ >"$1"
+"$DERIVED_FILE_DIR"/img-to-c -size 2g -type SPARSE -fs JHFS+ -uid 501 -gid 501 >"$1"
echo "Created $1"
}
if (di) {
+ // disk_image_cleanup(di) will free di.
disk_image_cleanup(di);
systemx("/bin/rm", SYSTEMX_QUIET, "-rf", "/tmp/mnt", NULL);
}