]> git.saurik.com Git - apple/hfs.git/commitdiff
hfs-407.1.3.tar.gz macos-1013 macos-10131 v407.1.3
authorApple <opensource@apple.com>
Tue, 26 Sep 2017 16:55:08 +0000 (16:55 +0000)
committerApple <opensource@apple.com>
Tue, 26 Sep 2017 16:55:08 +0000 (16:55 +0000)
38 files changed:
core/VolumeAllocation.c
core/hfs.h
core/hfs_attrlist.c
core/hfs_btreeio.c
core/hfs_fsctl.h
core/hfs_journal.c
core/hfs_notification.c
core/hfs_readwrite.c
core/hfs_vfsops.c
core/hfs_vnops.c
core/hfs_xattr.c
fs/Info.plist
fsck_hfs/dfalib/SVerify1.c
fsck_hfs/fsck_hfs.8
hfs.xcodeproj/project.pbxproj
tests/cases/test-access.c
tests/cases/test-dateadded.c
tests/cases/test-doc-tombstone.c
tests/cases/test-fsinfo.c
tests/cases/test-fsync.c
tests/cases/test-invalid-ranges.m
tests/cases/test-mmap-mod-time.c
tests/cases/test-mod-time.c
tests/cases/test-move-data-extents.c
tests/cases/test-msync-16k.c
tests/cases/test-renamex.c
tests/cases/test-set-create-time.c
tests/cases/test-set-protection-class.c
tests/cases/test-sparse-dev.c
tests/cases/test-throttled-io.c
tests/cases/test-transcode.m
tests/cases/test-uncached-io.c
tests/cases/test-unicode-file-names.c
tests/cases/test_disklevel.c [new file with mode: 0644]
tests/disk-image.h
tests/disk-image.m
tests/gen-dmg.sh
tests/hfs-tests.mm

index 3ecccbe5c486545bc85f968feb66c8f605a3f561..f26811cd783c18cdb5f0acb80c0299ac5573cef2 100644 (file)
@@ -794,13 +794,13 @@ CheckUnmappedBytes (struct hfsmount *hfsmp, uint64_t blockno, uint64_t numblocks
  ;                             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.
index f726cd31e1220ae448ed097921c7d6be200aece6..5dce5dae4288b3626c10de928a4e1b1329fbd609 100644 (file)
@@ -143,15 +143,19 @@ extern struct timezone gTimeZone;
 #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*/
 
@@ -305,6 +309,7 @@ typedef struct hfsmount {
        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" */
@@ -320,7 +325,7 @@ typedef struct hfsmount {
        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;
@@ -418,7 +423,8 @@ typedef struct hfsmount {
        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 {
@@ -1091,6 +1097,9 @@ int hfs_setxattr_internal(struct cnode *, const void *, size_t,
                           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);
 
 
index 40dced03fd217b586c0644f2ef4ab3044230ac2b..1fa4268b19d222c739419107ba7fe11df1606309 100644 (file)
@@ -211,7 +211,18 @@ hfs_readdirattr_internal(struct vnode *dvp, struct attrlist *alist,
        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;
 
@@ -1128,9 +1139,10 @@ hfs_attrblksize(struct attrlist *attrlist)
 
        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;
        
@@ -1272,9 +1284,10 @@ get_vattr_data_for_attrs(struct attrlist *alp, struct vnode_attr *vap,
     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);
@@ -1386,9 +1399,11 @@ vattr_data_for_common_attrs( struct attrlist *alp, struct vnode_attr *vap,
         * 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);
        }
index 75fda11851cf1723e3cbfddcd9220f953fd9a863..ec2072e4075f5e90d354b64a8d7cf5f4da1702de 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -722,6 +722,12 @@ again:
        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;
 
        /*
@@ -903,6 +909,18 @@ again:
        }
 
 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));
        }
index a1ba59798e9e5fca192bfe5f26ff185f9864f2a5..6fa854c712a302c4f9b765112824c0f65ad212a1 100644 (file)
@@ -252,26 +252,20 @@ enum {
 /* 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
 
@@ -279,13 +273,10 @@ enum {
 #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
@@ -295,49 +286,44 @@ enum {
 
 /* 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 */
 
@@ -345,7 +331,6 @@ enum {
 
 /* 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)
 
 
 /* 
@@ -357,27 +342,22 @@ enum {
  * 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
@@ -385,11 +365,17 @@ enum {
 #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 */
 
index bfcc6400b9fdf836c60e5076baf42d04d03468c8..8556c1c165d868de2cc3bb607ae4ed66e54b4edd 100644 (file)
@@ -1923,6 +1923,15 @@ journal_open(struct vnode *jvp,
                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;
 
@@ -2016,6 +2025,13 @@ journal_open(struct vnode *jvp,
                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",
@@ -4771,6 +4787,11 @@ int journal_relocate(journal *jnl, off_t offset, off_t journal_size, int32_t tbu
        /*
         * 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);
index a2c361ec01efa96ea5c745eab1c79dc28944f253..614c32d3840fff2757531451fb701c997848fa1d 100644 (file)
@@ -67,16 +67,49 @@ void hfs_generate_volume_notifications(struct hfsmount *hfsmp)
        
        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);
 
@@ -96,13 +129,13 @@ void hfs_generate_volume_notifications(struct hfsmount *hfsmp)
                }
 #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 */
@@ -110,18 +143,53 @@ void hfs_generate_volume_notifications(struct hfsmount *hfsmp)
                        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);
                        }
index 879fe0a0c6bf161b19bd9bf1000f90c12364f114..9b3f586601bf0c2251a179f046129a8a233d90cd 100644 (file)
@@ -1539,8 +1539,8 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
 
 #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;
@@ -1552,7 +1552,7 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
 
        switch (ap->a_command) {
 
-       case HFS_GETPATH:
+       case HFSIOC_GETPATH:
        {
                struct vnode *file_vp;
                cnid_t  cnid;
@@ -1592,7 +1592,7 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
                return (error);
        }
        
-       case HFS_SET_MAX_DEFRAG_SIZE: 
+       case HFSIOC_SET_MAX_DEFRAG_SIZE:
        {
                int error = 0;          /* Assume success */
                u_int32_t maxsize = 0;
@@ -1619,7 +1619,7 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
                return (error);
        }
 
-       case HFS_FORCE_ENABLE_DEFRAG:
+       case HFSIOC_FORCE_ENABLE_DEFRAG:
        {
                int error = 0;          /* Assume success */
                u_int32_t do_enable = 0;
@@ -1648,7 +1648,7 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
        }
 
 
-       case HFS_TRANSFER_DOCUMENT_ID:
+       case HFSIOC_TRANSFER_DOCUMENT_ID:
        {
                struct cnode *cp = NULL;
                int error;
@@ -1770,8 +1770,8 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
 
 
 
-       case HFS_PREV_LINK:
-       case HFS_NEXT_LINK:
+       case HFSIOC_PREV_LINK:
+       case HFSIOC_NEXT_LINK:
        {
                cnid_t linkfileid;
                cnid_t nextlinkid;
@@ -1795,7 +1795,7 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
                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;
@@ -1803,7 +1803,7 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
                return (0);
        }
 
-       case HFS_RESIZE_PROGRESS: {
+       case HFSIOC_RESIZE_PROGRESS: {
 
                vfsp = vfs_statfs(HFSTOVFS(hfsmp));
                if (suser(cred, NULL) &&
@@ -1821,7 +1821,7 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
                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;
@@ -1854,7 +1854,7 @@ hfs_vnop_ioctl( struct vnop_ioctl_args /* {
                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;
 
@@ -1898,7 +1898,7 @@ fail_change_next_allocation:
        }
 
 #if HFS_SPARSE_DEV
-       case HFS_SETBACKINGSTOREINFO: {
+       case HFSIOC_SETBACKINGSTOREINFO: {
                struct vnode * di_vp;
                struct hfs_backingstoreinfo *bsdata;
                int error = 0;
@@ -1975,7 +1975,8 @@ fail_change_next_allocation:
                file_drop(bsdata->backingfd);
                return (0);
        }
-       case HFS_CLRBACKINGSTOREINFO: {
+
+       case HFSIOC_CLRBACKINGSTOREINFO: {
                struct vnode * tmpvp;
 
                vfsp = vfs_statfs(HFSTOVFS(hfsmp));
@@ -2004,7 +2005,7 @@ fail_change_next_allocation:
 #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;
@@ -2074,7 +2075,8 @@ fail_change_next_allocation:
                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) {
@@ -2091,7 +2093,7 @@ fail_change_next_allocation:
            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) {
@@ -2114,7 +2116,7 @@ fail_change_next_allocation:
                        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);        
        }
@@ -2471,19 +2473,19 @@ fail_change_next_allocation:
                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;
            }
@@ -2491,11 +2493,11 @@ fail_change_next_allocation:
            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) {
 
@@ -2505,11 +2507,25 @@ fail_change_next_allocation:
            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;
            }
@@ -2517,7 +2533,7 @@ fail_change_next_allocation:
            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;
 
@@ -2545,7 +2561,8 @@ fail_change_next_allocation:
                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.
@@ -2566,7 +2583,7 @@ fail_change_next_allocation:
                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)
@@ -2584,7 +2601,7 @@ fail_change_next_allocation:
                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) {
@@ -2595,7 +2612,8 @@ fail_change_next_allocation:
            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;
@@ -2611,7 +2629,7 @@ fail_change_next_allocation:
        }
 
 
-       case HFS_FSINFO_METADATA_BLOCKS: {
+       case HFSIOC_FSINFO_METADATA_BLOCKS: {
                int error;
                struct hfsinfo_metadata *hinfo;
 
@@ -2626,7 +2644,7 @@ fail_change_next_allocation:
                break;
        }
 
-       case HFS_GET_FSINFO: {
+       case HFSIOC_GET_FSINFO: {
                hfs_fsinfo *fsinfo = (hfs_fsinfo *)ap->a_data;
 
                /* Only root is allowed to get fsinfo */
@@ -2657,7 +2675,7 @@ fail_change_next_allocation:
                return hfs_get_fsinfo(hfsmp, ap->a_data);
        }
 
-       case HFS_CS_FREESPACE_TRIM: {
+       case HFSIOC_CS_FREESPACE_TRIM: {
                int error = 0;
                int lockflags = 0;
 
@@ -2747,7 +2765,7 @@ fail_change_next_allocation:
                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);
@@ -2793,7 +2811,7 @@ fail_change_next_allocation:
                return error;
        }
 
-       case HFS_REPIN_HOTFILE_STATE: {
+       case HFSIOC_REPIN_HOTFILE_STATE: {
                int error=0;
                uint32_t repin_what = *((uint32_t*)ap->a_data);
 
@@ -2831,7 +2849,7 @@ fail_change_next_allocation:
 
 #if HFS_CONFIG_KEY_ROLL
 
-       case HFS_KEY_ROLL: {
+       case HFSIOC_KEY_ROLL: {
                if (!kauth_cred_issuser(kauth_cred_get()))
                        return EACCES;
 
@@ -2840,7 +2858,7 @@ fail_change_next_allocation:
                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;
 
@@ -2854,7 +2872,7 @@ fail_change_next_allocation:
                break;
        }
 
-       case HFS_SET_KEY_AUTO_ROLL: {
+       case HFSIOC_SET_KEY_AUTO_ROLL: {
                if (!kauth_cred_issuser(kauth_cred_get()))
                        return EACCES;
 
index 10e5f533aa154aa1434378fe81c14e74ec87d29c..6d0748817a663f2ebb426a6c3a2b424f77d67591 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 1999-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -1133,11 +1133,6 @@ void hfs_scan_blocks (struct hfsmount *hfsmp) {
 
 }
 
-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
  */
@@ -1632,8 +1627,8 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct hfs_mount_args *args,
                                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();
                }
 
                /*
@@ -1890,10 +1885,11 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct hfs_mount_args *args,
                /*
                 * 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,
@@ -1901,6 +1897,9 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct hfs_mount_args *args,
                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);
@@ -1908,10 +1907,11 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct hfs_mount_args *args,
                /*
                 * 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:
@@ -1925,6 +1925,9 @@ hfs_mountfs(struct vnode *devvp, struct mount *mp, struct hfs_mount_args *args,
                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);
@@ -4227,8 +4230,8 @@ hfs_getvoluuid(struct hfsmount *hfsmp, uuid_t result_uuid)
 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);
@@ -4292,6 +4295,12 @@ hfs_vfs_getattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t
 #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
@@ -4358,6 +4367,12 @@ hfs_vfs_getattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t
                        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;
 
                /*
@@ -4398,13 +4413,21 @@ hfs_vfs_getattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t
                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;
@@ -4416,10 +4439,20 @@ hfs_vfs_getattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t
        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;
 
index 316a657d88bb2793b19e7935a43bafad4c72bd91..16d1d1d5b5f6943d9301765530873d827e051130 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -55,7 +55,6 @@
 #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>
@@ -114,6 +113,22 @@ static int hfs_move_data(cnode_t *from_cp, cnode_t *to_cp,
 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
@@ -1876,6 +1891,156 @@ static int hfs_flush_rsrc(vnode_t vp, vfs_context_t ctx)
 }
 #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:
  * 
@@ -1912,6 +2077,10 @@ hfs_vnop_exchange(struct vnop_exchange_args *ap)
        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;
@@ -1919,6 +2088,13 @@ hfs_vnop_exchange(struct vnop_exchange_args *ap)
        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:
@@ -2053,8 +2229,7 @@ hfs_vnop_exchange(struct vnop_exchange_args *ap)
        }
 
        /* 
-        * 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
@@ -2068,6 +2243,38 @@ hfs_vnop_exchange(struct vnop_exchange_args *ap)
                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;
        }
@@ -2138,18 +2345,89 @@ hfs_vnop_exchange(struct vnop_exchange_args *ap)
         */
        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);
@@ -2168,11 +2446,16 @@ hfs_vnop_exchange(struct vnop_exchange_args *ap)
        
        /* 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.
@@ -2240,6 +2523,9 @@ hfs_vnop_exchange(struct vnop_exchange_args *ap)
         * 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.
         * 
@@ -2250,6 +2536,20 @@ hfs_vnop_exchange(struct vnop_exchange_args *ap)
         */      
        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);
 
 
@@ -2278,8 +2578,22 @@ hfs_vnop_exchange(struct vnop_exchange_args *ap)
         * 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);
 
 
@@ -2318,6 +2632,17 @@ exit:
        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);
 }
 
@@ -5355,6 +5680,7 @@ hfs_vnop_readdir(struct vnop_readdir_args *ap)
        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;
@@ -5409,10 +5735,15 @@ hfs_vnop_readdir(struct vnop_readdir_args *ap)
                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;
                }
        }
@@ -5420,6 +5751,10 @@ hfs_vnop_readdir(struct vnop_readdir_args *ap)
        /* 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;
@@ -5569,6 +5904,10 @@ hfs_vnop_readdir(struct vnop_readdir_args *ap)
                        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;
@@ -5578,6 +5917,12 @@ hfs_vnop_readdir(struct vnop_readdir_args *ap)
        /* 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;
        }
@@ -5637,6 +5982,10 @@ seekoffcalc:
 
 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. */
index c379f9fcfa934fed81fe8ed289e8b187ffd75546..0d3dcd357bf86bf0c698e542df8f4d831a795626 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -1507,6 +1507,62 @@ exit_nolock:
        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.
@@ -2016,7 +2072,7 @@ hfs_set_volxattr(struct hfsmount *hfsmp, unsigned int xattrtype, int state)
                return (ENOTSUP);
        }
 #endif
-       if (xattrtype != HFS_SET_XATTREXTENTS_STATE) {
+       if (xattrtype != HFSIOC_SET_XATTREXTENTS_STATE) {
                return EINVAL;
        }
 
index c8a77b15be570d44e26b5450bb27995195d6c48a..c3dfd93730ba1c7ebc00f6500a4ccfe9e5f95a4c 100644 (file)
                        <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>
index adcfeaee6bf85ab69676c8fece0b4a13c9c341ac..7ccbafdf90ccb7188fc75bae1d78fda77257490e 100644 (file)
@@ -2446,6 +2446,7 @@ CheckAttributeRecord(SGlobPtr GPtr, const HFSPlusAttrKey *key, const HFSPlusAttr
                         * 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) {
@@ -2454,9 +2455,11 @@ CheckAttributeRecord(SGlobPtr GPtr, const HFSPlusAttrKey *key, const HFSPlusAttr
                                        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;
                                }
index 159a53289687a5963e9c3d3ccbd4e9d0c50cefe9..df3f11f95a2a04fc3004ee925e59aff340c2b6cf 100644 (file)
@@ -229,7 +229,9 @@ No errors found, or successfully repaired.
 .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.
index ef8da41d2f2c87b7e09475650edb9093b96a9a93..9ec964f182fbbf923b19f3e6e33130b9799f7824 100644 (file)
@@ -7,6 +7,17 @@
        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" */;
@@ -15,7 +26,6 @@
                        dependencies = (
                                FBA540011B7BF2DF00899E5B /* PBXTargetDependency */,
                                FBC234C41B4EC6AE0002D849 /* PBXTargetDependency */,
-                               8657285C18319A93007F580F /* PBXTargetDependency */,
                                4DBD523F1548A499007AA736 /* PBXTargetDependency */,
                                4DBD52411548A49A007AA736 /* PBXTargetDependency */,
                                4DBD52431548A49D007AA736 /* PBXTargetDependency */,
@@ -23,7 +33,6 @@
                                4DBD52471548A4A2007AA736 /* PBXTargetDependency */,
                                4DBD52491548A4A4007AA736 /* PBXTargetDependency */,
                                4DBD524B1548A4A7007AA736 /* PBXTargetDependency */,
-                               4DBD524D1548A4AA007AA736 /* PBXTargetDependency */,
                        );
                        name = Common;
                        productName = Common;
@@ -34,6 +43,7 @@
                        buildPhases = (
                        );
                        dependencies = (
+                               07CA47231DA859CA00138D78 /* PBXTargetDependency */,
                                FBE3A5A51BBEE34400CB9A33 /* PBXTargetDependency */,
                                FB48E4BC1BB30CC400523121 /* PBXTargetDependency */,
                                FBC234C61B4EC6B90002D849 /* PBXTargetDependency */,
@@ -49,6 +59,7 @@
                        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 = (
index de1b2b7e60efcf486a8690082c9ec73dd3966fbe..f5e28c6658c9ccb745b12a7eb6947e7a1e265ccf 100644 (file)
@@ -59,6 +59,7 @@ int run_access(__unused test_ctx_t *ctx)
                assert_fail("access_vector[0] != ENOENT (== %u)!", access_vector[0]);
        
        free(path);
+       assert_no_err (close(fd));
 
        return 0;
 }
index 1e58d1af07a742dfd499e8b02b9de3ea60a65cc6..6693c49de590b95e8828937b5dd0aa02e7bf4cae 100644 (file)
@@ -85,6 +85,7 @@ int run_dateadded(__unused test_ctx_t *ctx)
 
        assert_no_err(unlink(file3));
        assert_no_err(rmdir(dir));
+       assert_no_err(close(fd));
 
        return 0;
 }
index 6b1cc6b9eccff3045767fdc9acfda5a4df759926..acb42a6ef63fefdd65d51e1462a447750407e7a8 100644 (file)
@@ -280,7 +280,6 @@ int run_doc_tombstone(__unused test_ctx_t *ctx)
        assert_equal(orig_doc_id, attrs.doc_id, "%u");
        
        assert_no_err(rmdir(file));
-       
+
        return 0;
-       
 }
index 7d1c7dba355bd665fab0b4f44cee0c18b34cb50c..56d413d44c3587d6da29a9094ffae18c871e5d22 100644 (file)
@@ -249,7 +249,7 @@ static void setup_testvolume()
 {
        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));
@@ -261,10 +261,12 @@ static void setup_testvolume()
                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);
        }
 }
 
index 144b7f1b4d6561e6d44d55b7d227f1c20a3f9bf5..b8e944008d02e09e78881fbc2b56d104936f9216 100644 (file)
@@ -109,6 +109,7 @@ int run_fsync(__unused test_ctx_t *ctx)
        free(file);
        free(sock);
        free(fifo);
+       free(buf);
 
        return 0;
 }
index 409a9610b9c4475c6fe2f09950b0d9af525ec9e2..8e85f5b6a797f9b1891f00a0679834e99b4dfa0c 100644 (file)
@@ -133,6 +133,8 @@ int run_invalid_ranges(__unused test_ctx_t *ctx)
                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);
@@ -147,6 +149,8 @@ int run_invalid_ranges(__unused test_ctx_t *ctx)
 
        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
@@ -295,6 +299,7 @@ int run_invalid_ranges(__unused test_ctx_t *ctx)
        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);
@@ -315,13 +320,14 @@ int run_invalid_ranges(__unused test_ctx_t *ctx)
        // 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));
 
@@ -346,6 +352,10 @@ int run_invalid_ranges(__unused test_ctx_t *ctx)
        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));
 
index bc940a072d23995c131d5c241aeaa52732b72ba2..9859780f0b26495b14d25f1c45d7de94bd9a533e 100644 (file)
@@ -77,6 +77,7 @@ int run_mmap_mod_time(__unused test_ctx_t *ctx)
 
        assert_no_err(unlink(file));
 
+       assert_no_err(close(fd));
        free(file);
        
        return 0;
index 2f5566291f6aa025d4413c6ab6d1e50f90ca0895..d306c703bcb40fd4be5f0c360f92ea27d1e550f1 100644 (file)
@@ -58,7 +58,9 @@ static void run_test(void)
 
        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)
index 003bcba36f494edc2d3ea189abcf4746d393f2a6..8e4750f7bdb6d249c5129f782dea6ad2c8de7063 100644 (file)
@@ -60,7 +60,6 @@ static int make_frag_file(int blocks_per_extent)
                                .l2p_contigbytes = 1024 * 1024,
                                .l2p_devoffset   = len - 1,
                        };
-
                        if (fcntl(fd, F_LOG2PHYS_EXT, &l2p)) {
                                if (errno == ERANGE)
                                        break;
@@ -87,12 +86,13 @@ int run_move_data_extents(__unused test_ctx_t *ctx)
 {
        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,
@@ -100,7 +100,7 @@ int run_move_data_extents(__unused test_ctx_t *ctx)
 
 #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);
@@ -130,6 +130,7 @@ int run_move_data_extents(__unused test_ctx_t *ctx)
        pthread_join(thread, NULL);
 
        close(fd);
+       close(frag_fd);
 
        /*
         * Make sure that the extents from move_data_extents.1 got deleted
@@ -194,6 +195,10 @@ int run_move_data_extents(__unused test_ctx_t *ctx)
 
        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);
index 974fd52546dca5edf713e958d992888ae5a3665b..555746ecb941fd7259fcca761b48aadb1e572182 100644 (file)
@@ -145,6 +145,8 @@ int run_msync_16k(__unused test_ctx_t *ctx)
 
        assert_no_err(unlink(file));
        
+       free(cmp_buf);
+       free(buf);
        free(file);
 
        return 0;
index 1af045e636cf5ace545db06d43f49888d660c0a6..4858a7d721fe3028caedb3f763c3a7c72f0a23da 100644 (file)
@@ -56,6 +56,9 @@ int run_renamex_test (__unused test_ctx_t *ctx) {
        error = unlink(dst_file);
        assert (error == 0);
 
+       assert_no_err(close(src_file_fd));
+       assert_no_err(close(dst_file_fd));
+
        return 0;
 }      
 
index 4498cd3d8f269c77c84829813254f97aa96d3229..041b5aea93157083cb4cf0a0b94efc36bcbc2152 100644 (file)
@@ -41,6 +41,7 @@ int run_set_create_time(__unused test_ctx_t *ctx)
 
        assert(attrs.cr_time.tv_sec == 2000 && attrs.mod_time.tv_sec == 1000);
 
+       assert_no_err (close(fd));
        unlink(file);
        free(file);
 
index 164de50260370a6b4adbfce4be7881762a914508..88984938836f7136c49ce7317b637ef6c23e4f36 100644 (file)
@@ -158,9 +158,13 @@ int run_set_protection_class(__unused test_ctx_t *ctx)
                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;
 }
index 4168330507191a63b346fefb56450c5c0c0d7abd..c33a53f3bbbc4ff7325bf44287752ee7c2e827ec 100644 (file)
@@ -49,6 +49,9 @@ int run_sparse_dev(__unused test_ctx_t *ctx)
 
        assert(sfs.f_bfree * sfs.f_bsize < 64 * 1024 * 1024);
 
+
+       assert_no_err (close(fd));
+
        return 0;
 }
 
index 2d38181896dc4b0e3be9542b945c7a84e1623c5b..2bf0c14cd74f4590b93524603ea1ad10e625c516 100644 (file)
@@ -135,7 +135,6 @@ static void test2_thread(void)
        } while (!done);
        
        assert_no_err (close(fd));
-       
 }
 
 static int run_test2(void)
@@ -194,6 +193,7 @@ static bool clean_up(void)
 
 int run_throttled_io(__unused test_ctx_t *ctx)
 {
+       
        gDI = disk_image_get();
        
        asprintf(&gFile1, "%s/throttled_io.1", gDI->mount_point);
@@ -203,11 +203,11 @@ int run_throttled_io(__unused test_ctx_t *ctx)
        test_cleanup(^ bool {
                return clean_up();
        });
-       
+
        int res = run_test1();
        if (res)
                return res;
-       
+
        res = run_test2();
        if (res)
                return res;
index 5dba386e7ad1a0e1ad77448fac784713c05d6811..70ca87a70701cf6473db6e713411607522b29288 100644 (file)
@@ -60,6 +60,9 @@ int run_transcode(__unused test_ctx_t *ctx)
        assert_no_err(MKBKeyBagRegisterOTABackup(NULL, NULL));
        assert_no_err(MKBKeyBagRelease(handle));
 
+       close(fd);
+       free(key);
+
        return 0;
 }
 
index 5193693d0e5e18ad9098ffbad10d3501368c207a..62792270c197556c5d36a52388de69df1c5e32f1 100644 (file)
@@ -186,7 +186,9 @@ int run_uncached_io(__unused test_ctx_t *ctx)
        } // for (pass...)
 
        assert_no_err(unlink(file1));
-       
+
+       assert_no_err (close(fd));
+       assert_no_err (close(fd2));
        free(file1);
        free(file2);
        
index 9d3e6e3eb863e6043695f8958c62dfe3e45683e4..016fb9f69ca17f75d25158d1596bd14d1f0c2fa4 100644 (file)
@@ -68,7 +68,8 @@ int run_unicode_file_names(__unused test_ctx_t *ctx)
        assert((fd = open(path3, O_RDONLY)) >= 0);
 
        assert_no_err(unlink(path3));
-       
+       assert_no_err (close(fd));
+
        free(dir);
        free(path);
        free(path2);
diff --git a/tests/cases/test_disklevel.c b/tests/cases/test_disklevel.c
new file mode 100644 (file)
index 0000000..eaf7f6d
--- /dev/null
@@ -0,0 +1,68 @@
+#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;
+}
index cd9d9ae657d05f5527e1ac8bbee365f45d7bcc1b..43a486855d3298d0d4bf7a7557eb1bebf6c8f458 100644 (file)
@@ -16,6 +16,9 @@
  * 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
index e7bb56c1847dd1be4c884114d37ca5976e1daa5f..4940be412f0daae2c1b7e88e65c5d0ddaa441b1f 100644 (file)
@@ -30,6 +30,7 @@
 bool disk_image_cleanup(disk_image_t *di)
 {
        pid_t pid;
+       bool result = false;
        
        // We need to be root
        assert(seteuid(0) == 0);
@@ -61,10 +62,15 @@ bool disk_image_cleanup(disk_image_t *di)
        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)
@@ -196,12 +202,6 @@ disk_image_t *disk_image_create(const char *path, disk_image_opts_t *opts)
        
        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);
@@ -223,11 +223,21 @@ disk_image_t *disk_image_get(void)
                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;
@@ -241,6 +251,7 @@ bool disk_image_cleanup(disk_image_t *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);
@@ -260,10 +271,16 @@ bool disk_image_cleanup(disk_image_t *di)
                && 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)
@@ -454,15 +471,25 @@ disk_image_t *disk_image_get(void)
                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
index 0c5c1c972182d1823bcad77d5ccabe902db19da2..92d1122c1f88fbcf8d7ce013d7ff1b86decdeb6c 100755 (executable)
@@ -11,6 +11,6 @@ set -e
 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"
index 7ece91acff8fd821436a04d1f6aa02231d30a639..eeb2f82a629eec6e7829cb0d2df416a610d47c44 100644 (file)
@@ -228,6 +228,7 @@ int main(int argc, char *argv[])
                        }
                        
                        if (di) {
+                               // disk_image_cleanup(di) will free di.
                                disk_image_cleanup(di);
                                systemx("/bin/rm", SYSTEMX_QUIET, "-rf", "/tmp/mnt", NULL);
                        }