]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfs_vfsops.c
xnu-792.10.96.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vfsops.c
index ba4a5983d9b836a1e2324d605de5f3a69e737770..a1053d3c40850edd4d1aff4e79101c09ecbae3fb 100644 (file)
@@ -140,7 +140,9 @@ static int hfs_unmount(struct mount *mp, int mntflags, vfs_context_t context);
 static int hfs_vfs_vget(struct mount *mp, ino64_t ino, struct vnode **vpp, vfs_context_t context);
 static int hfs_vptofh(struct vnode *vp, int *fhlenp, unsigned char *fhp, vfs_context_t context);
 
 static int hfs_vfs_vget(struct mount *mp, ino64_t ino, struct vnode **vpp, vfs_context_t context);
 static int hfs_vptofh(struct vnode *vp, int *fhlenp, unsigned char *fhp, vfs_context_t context);
 
-static int hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk);
+static int hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk, u_long reclaimblks);
+static int hfs_overlapped_overflow_extents(struct hfsmount *hfsmp, u_int32_t startblk,
+                                           u_int32_t catblks, u_int32_t fileID, int rsrcfork);
 
 
 /*
 
 
 /*
@@ -1561,9 +1563,9 @@ hfs_sync_metadata(void *arg)
        priIDSector = (daddr64_t)((vcb->hfsPlusIOPosOffset / sectorsize) +
                                  HFS_PRI_SECTOR(sectorsize));
        retval = (int)buf_meta_bread(hfsmp->hfs_devvp, priIDSector, sectorsize, NOCRED, &bp);
        priIDSector = (daddr64_t)((vcb->hfsPlusIOPosOffset / sectorsize) +
                                  HFS_PRI_SECTOR(sectorsize));
        retval = (int)buf_meta_bread(hfsmp->hfs_devvp, priIDSector, sectorsize, NOCRED, &bp);
-       if (retval != 0) {
-               panic("hfs: sync_metadata: can't read super-block?! (retval 0x%x, priIDSector)\n",
-                         retval, priIDSector);
+       if ((retval != 0) && (retval != ENXIO)) {
+               printf("hfs_sync_metadata: can't read volume header at %d! (retval 0x%x)\n",
+                       priIDSector, retval);
        }
 
        if (retval == 0 && ((buf_flags(bp) & (B_DELWRI | B_LOCKED)) == B_DELWRI)) {
        }
 
        if (retval == 0 && ((buf_flags(bp) & (B_DELWRI | B_LOCKED)) == B_DELWRI)) {
@@ -1765,7 +1767,7 @@ hfs_fhtovp(struct mount *mp, int fhlen, unsigned char *fhp, struct vnode **vpp,
        if (fhlen < sizeof(struct hfsfid))
                return (EINVAL);
 
        if (fhlen < sizeof(struct hfsfid))
                return (EINVAL);
 
-       result = hfs_vget(VFSTOHFS(mp), hfsfhp->hfsfid_cnid, &nvp, 0);
+       result = hfs_vget(VFSTOHFS(mp), ntohl(hfsfhp->hfsfid_cnid), &nvp, 0);
        if (result) {
                if (result == ENOENT)
                        result = ESTALE;
        if (result) {
                if (result == ENOENT)
                        result = ESTALE;
@@ -1783,7 +1785,7 @@ hfs_fhtovp(struct mount *mp, int fhlen, unsigned char *fhp, struct vnode **vpp,
         * error prone. Future, would be change the "wrap bit" to a unique
         * wrap number and use that for generation number. For now do this.
         */  
         * error prone. Future, would be change the "wrap bit" to a unique
         * wrap number and use that for generation number. For now do this.
         */  
-       if ((hfsfhp->hfsfid_gen < VTOC(nvp)->c_itime)) {
+       if ((ntohl(hfsfhp->hfsfid_gen) < VTOC(nvp)->c_itime)) {
                hfs_unlock(VTOC(nvp));
                vnode_put(nvp);
                return (ESTALE);
                hfs_unlock(VTOC(nvp));
                vnode_put(nvp);
                return (ESTALE);
@@ -1813,8 +1815,8 @@ hfs_vptofh(struct vnode *vp, int *fhlenp, unsigned char *fhp, vfs_context_t cont
 
        cp = VTOC(vp);
        hfsfhp = (struct hfsfid *)fhp;
 
        cp = VTOC(vp);
        hfsfhp = (struct hfsfid *)fhp;
-       hfsfhp->hfsfid_cnid = cp->c_fileid;
-       hfsfhp->hfsfid_gen = cp->c_itime;
+       hfsfhp->hfsfid_cnid = htonl(cp->c_fileid);
+       hfsfhp->hfsfid_gen = htonl(cp->c_itime);
        *fhlenp = sizeof(struct hfsfid);
        
        return (0);
        *fhlenp = sizeof(struct hfsfid);
        
        return (0);
@@ -1845,10 +1847,7 @@ hfs_init(__unused struct vfsconf *vfsp)
        hfs_group_attr   = lck_grp_attr_alloc_init();
        hfs_mutex_group  = lck_grp_alloc_init("hfs-mutex", hfs_group_attr);
        hfs_rwlock_group = lck_grp_alloc_init("hfs-rwlock", hfs_group_attr);
        hfs_group_attr   = lck_grp_attr_alloc_init();
        hfs_mutex_group  = lck_grp_alloc_init("hfs-mutex", hfs_group_attr);
        hfs_rwlock_group = lck_grp_alloc_init("hfs-rwlock", hfs_group_attr);
-
-       /* Turn on lock debugging */
-       //lck_attr_setdebug(hfs_lock_attr);
-
+       
 
        return (0);
 }
 
        return (0);
 }
@@ -2021,6 +2020,16 @@ hfs_sysctl(int *name, __unused u_int namelen, user_addr_t oldp, size_t *oldlenp,
 
                hfs_global_exclusive_lock_acquire(hfsmp);
                
 
                hfs_global_exclusive_lock_acquire(hfsmp);
                
+               /*
+                * Flush all dirty metadata buffers.
+                */
+               buf_flushdirtyblks(hfsmp->hfs_devvp, MNT_WAIT, 0, "hfs_sysctl");
+               buf_flushdirtyblks(hfsmp->hfs_extents_vp, MNT_WAIT, 0, "hfs_sysctl");
+               buf_flushdirtyblks(hfsmp->hfs_catalog_vp, MNT_WAIT, 0, "hfs_sysctl");
+               buf_flushdirtyblks(hfsmp->hfs_allocation_vp, MNT_WAIT, 0, "hfs_sysctl");
+               if (hfsmp->hfs_attribute_vp)
+                       buf_flushdirtyblks(hfsmp->hfs_attribute_vp, MNT_WAIT, 0, "hfs_sysctl");
+
                HFSTOVCB(hfsmp)->vcbJinfoBlock = name[1];
                HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeJournaledMask;
                hfsmp->jvp = jvp;
                HFSTOVCB(hfsmp)->vcbJinfoBlock = name[1];
                HFSTOVCB(hfsmp)->vcbAtrb |= kHFSVolumeJournaledMask;
                hfsmp->jvp = jvp;
@@ -2221,11 +2230,6 @@ hfs_vget(struct hfsmount *hfsmp, cnid_t cnid, struct vnode **vpp, int skiplock)
                    (bcmp(cndesc.cd_nameptr, HFS_INODE_PREFIX, HFS_INODE_PREFIX_LEN) == 0)) {
                        linkref = strtoul((const char*)&cndesc.cd_nameptr[HFS_INODE_PREFIX_LEN], NULL, 10);
                        cnattr.ca_rdev = linkref;
                    (bcmp(cndesc.cd_nameptr, HFS_INODE_PREFIX, HFS_INODE_PREFIX_LEN) == 0)) {
                        linkref = strtoul((const char*)&cndesc.cd_nameptr[HFS_INODE_PREFIX_LEN], NULL, 10);
                        cnattr.ca_rdev = linkref;
-
-                       // patch up the parentcnid
-                       if (cnattr.ca_attrblks != 0) {
-                           cndesc.cd_parentcnid = cnattr.ca_attrblks;
-                       }
                }
        }
 
                }
        }
 
@@ -3012,7 +3016,6 @@ __private_extern__
 int
 hfs_truncatefs(struct hfsmount *hfsmp, u_int64_t newsize, __unused vfs_context_t context)
 {
 int
 hfs_truncatefs(struct hfsmount *hfsmp, u_int64_t newsize, __unused vfs_context_t context)
 {
-       struct vnode* rvp = NULL;
        struct  buf *bp = NULL;
        u_int64_t oldsize;
        u_int32_t newblkcnt;
        struct  buf *bp = NULL;
        u_int64_t oldsize;
        u_int32_t newblkcnt;
@@ -3021,20 +3024,22 @@ hfs_truncatefs(struct hfsmount *hfsmp, u_int64_t newsize, __unused vfs_context_t
        int transaction_begun = 0;
        int error;
 
        int transaction_begun = 0;
        int error;
 
-       /*
-        * Grab the root vnode to serialize with another hfs_truncatefs call.
-        */
-       error = hfs_vget(hfsmp, kHFSRootFolderID, &rvp, 0);
-       if (error) {
-               return (error);
+       
+       lck_mtx_lock(&hfsmp->hfs_mutex);
+       if (hfsmp->hfs_flags & HFS_RESIZE_IN_PROGRESS) {
+               lck_mtx_unlock(&hfsmp->hfs_mutex);
+               return (EALREADY);
        }
        }
+       hfsmp->hfs_flags |= HFS_RESIZE_IN_PROGRESS;
+       hfsmp->hfs_resize_filesmoved = 0;
+       hfsmp->hfs_resize_totalfiles = 0;
+       lck_mtx_unlock(&hfsmp->hfs_mutex);
+
        /*
        /*
-        * - HFS Plus file systems only. 
-        * - Journaling must be enabled.
+        * - Journaled HFS Plus volumes only.
         * - No embedded volumes.
         */
         * - No embedded volumes.
         */
-       if ((hfsmp->hfs_flags & HFS_STANDARD) ||
-           (hfsmp->jnl == NULL) ||
+       if ((hfsmp->jnl == NULL) ||
            (hfsmp->hfsPlusIOPosOffset != 0)) {
                error = EPERM;
                goto out;
            (hfsmp->hfsPlusIOPosOffset != 0)) {
                error = EPERM;
                goto out;
@@ -3051,13 +3056,12 @@ hfs_truncatefs(struct hfsmount *hfsmp, u_int64_t newsize, __unused vfs_context_t
                goto out;
        }
        /* Make sure there's enough space to work with. */
                goto out;
        }
        /* Make sure there's enough space to work with. */
-       if (reclaimblks > (hfsmp->freeBlocks / 4)) {
+       if (reclaimblks >= hfs_freeblks(hfsmp, 1)) {
                error = ENOSPC;
                goto out;
        }
                error = ENOSPC;
                goto out;
        }
-
-       printf("hfs_truncatefs: shrinking %s by %d blocks out of %d\n",
-              hfsmp->vcbVN, reclaimblks, hfsmp->totalBlocks);
+       /* Start with a clean journal. */
+       journal_flush(hfsmp->jnl);
 
        if (hfs_start_transaction(hfsmp) != 0) {
                error = EINVAL;
 
        if (hfs_start_transaction(hfsmp) != 0) {
                error = EINVAL;
@@ -3077,7 +3081,7 @@ hfs_truncatefs(struct hfsmount *hfsmp, u_int64_t newsize, __unused vfs_context_t
                transaction_begun = 0;
 
                /* Attempt to reclaim some space. */ 
                transaction_begun = 0;
 
                /* Attempt to reclaim some space. */ 
-               if (hfs_reclaimspace(hfsmp, newblkcnt) != 0) {
+               if (hfs_reclaimspace(hfsmp, newblkcnt, reclaimblks) != 0) {
                        printf("hfs_truncatefs: couldn't reclaim space on %s\n", hfsmp->vcbVN);
                        error = ENOSPC;
                        goto out;
                        printf("hfs_truncatefs: couldn't reclaim space on %s\n", hfsmp->vcbVN);
                        error = ENOSPC;
                        goto out;
@@ -3091,7 +3095,7 @@ hfs_truncatefs(struct hfsmount *hfsmp, u_int64_t newsize, __unused vfs_context_t
                /* Check if we're clear now. */
                if (hfs_isallocated(hfsmp, newblkcnt, reclaimblks - 1)) {
                        printf("hfs_truncatefs: didn't reclaim enough space on %s\n", hfsmp->vcbVN);
                /* Check if we're clear now. */
                if (hfs_isallocated(hfsmp, newblkcnt, reclaimblks - 1)) {
                        printf("hfs_truncatefs: didn't reclaim enough space on %s\n", hfsmp->vcbVN);
-                       error = ENOSPC;
+                       error = EAGAIN;  /* tell client to try again */
                        goto out;
                }
        }
                        goto out;
                }
        }
@@ -3120,21 +3124,26 @@ hfs_truncatefs(struct hfsmount *hfsmp, u_int64_t newsize, __unused vfs_context_t
 
        /*
         * Invalidate the existing alternate volume header.
 
        /*
         * Invalidate the existing alternate volume header.
+        *
+        * Don't do this as a transaction (don't call journal_modify_block)
+        * since this block will be outside of the truncated file system!
         */
        if (hfsmp->hfs_alt_id_sector) {
                if (buf_meta_bread(hfsmp->hfs_devvp, hfsmp->hfs_alt_id_sector,
                                   hfsmp->hfs_phys_block_size, NOCRED, &bp) == 0) {
         */
        if (hfsmp->hfs_alt_id_sector) {
                if (buf_meta_bread(hfsmp->hfs_devvp, hfsmp->hfs_alt_id_sector,
                                   hfsmp->hfs_phys_block_size, NOCRED, &bp) == 0) {
-                       journal_modify_block_start(hfsmp->jnl, bp);
        
                        bzero((void*)((char *)buf_dataptr(bp) + HFS_ALT_OFFSET(hfsmp->hfs_phys_block_size)), kMDBSize);
        
                        bzero((void*)((char *)buf_dataptr(bp) + HFS_ALT_OFFSET(hfsmp->hfs_phys_block_size)), kMDBSize);
-       
-                       journal_modify_block_end(hfsmp->jnl, bp);
+                       (void) VNOP_BWRITE(bp);
                } else if (bp) {
                        buf_brelse(bp);
                }
                bp = NULL;
        }
 
                } else if (bp) {
                        buf_brelse(bp);
                }
                bp = NULL;
        }
 
+       /* Log successful shrinking. */
+       printf("hfs_truncatefs: shrank \"%s\" to %d blocks (was %d blocks)\n",
+              hfsmp->vcbVN, newblkcnt, hfsmp->totalBlocks);
+
        /*
         * Adjust file system variables and flush them to disk.
         */
        /*
         * Adjust file system variables and flush them to disk.
         */
@@ -3152,54 +3161,90 @@ out:
        }
        if (transaction_begun) {
                hfs_end_transaction(hfsmp);
        }
        if (transaction_begun) {
                hfs_end_transaction(hfsmp);
+               journal_flush(hfsmp->jnl);
        }
        }
-       if (rvp) {
-               hfs_unlock(VTOC(rvp));
-               vnode_put(rvp);
-       }
+
+       lck_mtx_lock(&hfsmp->hfs_mutex);
+       hfsmp->hfs_flags &= ~HFS_RESIZE_IN_PROGRESS;
+       lck_mtx_unlock(&hfsmp->hfs_mutex);
+
        return (error);
 }
 
        return (error);
 }
 
+
 /*
  * Reclaim space at the end of a file system.
  */
 static int
 /*
  * Reclaim space at the end of a file system.
  */
 static int
-hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk)
+hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk, u_long reclaimblks)
 {
        struct vnode *vp = NULL;
        FCB *fcb;
        struct BTreeIterator * iterator = NULL;
        struct FSBufferDescriptor btdata;
        struct HFSPlusCatalogFile filerec;
 {
        struct vnode *vp = NULL;
        FCB *fcb;
        struct BTreeIterator * iterator = NULL;
        struct FSBufferDescriptor btdata;
        struct HFSPlusCatalogFile filerec;
+       struct filefork *fp;
        u_int32_t  saved_next_allocation;
        cnid_t * cnidbufp;
        size_t cnidbufsize;
        u_int32_t  saved_next_allocation;
        cnid_t * cnidbufp;
        size_t cnidbufsize;
-       int filecnt;
+       int filecnt = 0;
        int maxfilecnt;
        u_long block;
        int maxfilecnt;
        u_long block;
+       u_long datablks;
+       u_long rsrcblks;
+       u_long blkstomove = 0;
        int lockflags;
        int i;
        int error;
        int lockflags;
        int i;
        int error;
+       int lastprogress = 0;
 
 
-       /* 
-        * Check if Attributes file overlaps.
-        */
+
+       /* Check if Attributes file overlaps reclaim area. */
        if (hfsmp->hfs_attribute_vp) {
        if (hfsmp->hfs_attribute_vp) {
-               struct filefork *fp;
-       
                fp = VTOF(hfsmp->hfs_attribute_vp);
                fp = VTOF(hfsmp->hfs_attribute_vp);
+               datablks = 0;
                for (i = 0; i < kHFSPlusExtentDensity; ++i) {
                for (i = 0; i < kHFSPlusExtentDensity; ++i) {
-                       block = fp->ff_extents[i].startBlock +
-                               fp->ff_extents[i].blockCount;
+                       if (fp->ff_extents[i].blockCount == 0) {
+                               break;
+                       }
+                       datablks += fp->ff_extents[i].blockCount;
+                       block = fp->ff_extents[i].startBlock + fp->ff_extents[i].blockCount;
                        if (block >= startblk) {
                                printf("hfs_reclaimspace: Attributes file can't move\n");
                                return (EPERM);
                        }
                }
                        if (block >= startblk) {
                                printf("hfs_reclaimspace: Attributes file can't move\n");
                                return (EPERM);
                        }
                }
+               if ((i == kHFSPlusExtentDensity) && (fp->ff_blocks > datablks)) {
+                       if (hfs_overlapped_overflow_extents(hfsmp, startblk, datablks, kHFSAttributesFileID, 0)) {
+                               printf("hfs_reclaimspace: Attributes file can't move\n");
+                               return (EPERM);
+                       }
+               }
+       }
+       /* Check if Catalog file overlaps reclaim area. */
+       fp = VTOF(hfsmp->hfs_catalog_vp);
+       datablks = 0;
+       for (i = 0; i < kHFSPlusExtentDensity; ++i) {
+               if (fp->ff_extents[i].blockCount == 0) {
+                       break;
+               }
+               datablks += fp->ff_extents[i].blockCount;
+               block = fp->ff_extents[i].startBlock + fp->ff_extents[i].blockCount;
+               if (block >= startblk) {
+                       printf("hfs_reclaimspace: Catalog file can't move\n");
+                       return (EPERM);
+               }
+       }
+       if ((i == kHFSPlusExtentDensity) && (fp->ff_blocks > datablks)) {
+               if (hfs_overlapped_overflow_extents(hfsmp, startblk, datablks, kHFSCatalogFileID, 0)) {
+                       printf("hfs_reclaimspace: Catalog file can't move\n");
+                       return (EPERM);
+               }
        }
 
        }
 
-       /* For now we'll move a maximum of 16,384 files. */
-       maxfilecnt = MIN(hfsmp->hfs_filecount, 16384);
+       /* For now move a maximum of 250,000 files. */
+       maxfilecnt = MIN(hfsmp->hfs_filecount, 250000);
+       maxfilecnt = MIN((u_long)maxfilecnt, reclaimblks);
        cnidbufsize = maxfilecnt * sizeof(cnid_t);
        if (kmem_alloc(kernel_map, (vm_offset_t *)&cnidbufp, cnidbufsize)) {
                return (ENOMEM);
        cnidbufsize = maxfilecnt * sizeof(cnid_t);
        if (kmem_alloc(kernel_map, (vm_offset_t *)&cnidbufp, cnidbufsize)) {
                return (ENOMEM);
@@ -3219,14 +3264,13 @@ hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk)
        btdata.itemSize = sizeof(filerec);
        btdata.itemCount = 1;
 
        btdata.itemSize = sizeof(filerec);
        btdata.itemCount = 1;
 
-       /* Keep the Catalog file locked during iteration. */
-       lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
+       /* Keep the Catalog and extents files locked during iteration. */
+       lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS, HFS_SHARED_LOCK);
+
        error = BTIterateRecord(fcb, kBTreeFirstRecord, iterator, NULL, NULL);
        if (error) {
        error = BTIterateRecord(fcb, kBTreeFirstRecord, iterator, NULL, NULL);
        if (error) {
-               hfs_systemfile_unlock(hfsmp, lockflags);
-               goto out;
+               goto end_iteration;
        }
        }
-
        /*
         * Iterate over all the catalog records looking for files
         * that overlap into the space we're trying to free up.
        /*
         * Iterate over all the catalog records looking for files
         * that overlap into the space we're trying to free up.
@@ -3234,41 +3278,99 @@ hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk)
        for (filecnt = 0; filecnt < maxfilecnt; ) {
                error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
                if (error) {
        for (filecnt = 0; filecnt < maxfilecnt; ) {
                error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
                if (error) {
-                       if (error == btNotFound)
-                               error = 0;
+                       if (error == fsBTRecordNotFoundErr || error == fsBTEndOfIterationErr) {
+                               error = 0;                              
+                       }
                        break;
                }
                        break;
                }
-               if (filerec.recordType != kHFSPlusFileRecord ||
-                   filerec.fileID == hfsmp->hfs_jnlfileid)
+               if (filerec.recordType != kHFSPlusFileRecord) {
                        continue;
                        continue;
+               }
+               datablks = rsrcblks = 0;
                /* 
                 * Check if either fork overlaps target space.
                 */
                for (i = 0; i < kHFSPlusExtentDensity; ++i) {
                /* 
                 * Check if either fork overlaps target space.
                 */
                for (i = 0; i < kHFSPlusExtentDensity; ++i) {
-                       block = filerec.dataFork.extents[i].startBlock +
-                               filerec.dataFork.extents[i].blockCount;
-                       if (block >= startblk) {
-                               if (filerec.fileID == hfsmp->hfs_jnlfileid) {
-                                       printf("hfs_reclaimspace: cannot move active journal\n");
-                                       error = EPERM;
+                       if (filerec.dataFork.extents[i].blockCount != 0) {
+                               datablks += filerec.dataFork.extents[i].blockCount;
+                               block = filerec.dataFork.extents[i].startBlock +
+                                               filerec.dataFork.extents[i].blockCount;
+                               if (block >= startblk) {
+                                       if ((filerec.fileID == hfsmp->hfs_jnlfileid) ||
+                                               (filerec.fileID == hfsmp->hfs_jnlinfoblkid)) {
+                                               printf("hfs_reclaimspace: cannot move active journal\n");
+                                               error = EPERM;
+                                               goto end_iteration;
+                                       }
+                                       cnidbufp[filecnt++] = filerec.fileID;
+                                       blkstomove += filerec.dataFork.totalBlocks;
                                        break;
                                }
                                        break;
                                }
-                               cnidbufp[filecnt++] = filerec.fileID;
-                               break;
                        }
                        }
-                       block = filerec.resourceFork.extents[i].startBlock +
-                               filerec.resourceFork.extents[i].blockCount;
-                       if (block >= startblk) {
-                               cnidbufp[filecnt++] = filerec.fileID;
-                               break;
+                       if (filerec.resourceFork.extents[i].blockCount != 0) {
+                               rsrcblks += filerec.resourceFork.extents[i].blockCount;
+                               block = filerec.resourceFork.extents[i].startBlock +
+                                               filerec.resourceFork.extents[i].blockCount;
+                               if (block >= startblk) {
+                                       cnidbufp[filecnt++] = filerec.fileID;
+                                       blkstomove += filerec.resourceFork.totalBlocks;
+                                       break;
+                               }
+                       }
+               }
+               /*
+                * Check for any overflow extents that overlap.
+                */
+               if (i == kHFSPlusExtentDensity) {
+                       if (filerec.dataFork.totalBlocks > datablks) {
+                               if (hfs_overlapped_overflow_extents(hfsmp, startblk, datablks, filerec.fileID, 0)) {
+                                       cnidbufp[filecnt++] = filerec.fileID;
+                                       blkstomove += filerec.dataFork.totalBlocks;
+                               }
+                       } else if (filerec.resourceFork.totalBlocks > rsrcblks) {
+                               if (hfs_overlapped_overflow_extents(hfsmp, startblk, rsrcblks, filerec.fileID, 1)) {
+                                       cnidbufp[filecnt++] = filerec.fileID;
+                                       blkstomove += filerec.resourceFork.totalBlocks;
+                               }
                        }
                }
        }
                        }
                }
        }
+
+end_iteration:
+       if (filecnt == 0) {
+               error = ENOSPC;
+       }
        /* All done with catalog. */
        hfs_systemfile_unlock(hfsmp, lockflags);
        if (error)
                goto out;
 
        /* All done with catalog. */
        hfs_systemfile_unlock(hfsmp, lockflags);
        if (error)
                goto out;
 
+       /*
+        * Double check space requirements to make sure
+        * there is enough space to relocate any files
+        * that reside in the reclaim area.
+        *
+        *                                          Blocks To Move --------------
+        *                                                            |    |    |
+        *                                                            V    V    V
+        * ------------------------------------------------------------------------
+        * |                                                        | /   ///  // |
+        * |                                                        | /   ///  // |
+        * |                                                        | /   ///  // |
+        * ------------------------------------------------------------------------
+        *
+        * <------------------- New Total Blocks ------------------><-- Reclaim -->
+        *
+        * <------------------------ Original Total Blocks ----------------------->
+        *
+        */
+       if ((reclaimblks + blkstomove) >= hfs_freeblks(hfsmp, 1)) {
+               error = ENOSPC;
+               goto out;
+       }
+       hfsmp->hfs_resize_filesmoved = 0;
+       hfsmp->hfs_resize_totalfiles = filecnt;
+       
        /* Now move any files that are in the way. */
        for (i = 0; i < filecnt; ++i) {
                struct vnode * rvp;
        /* Now move any files that are in the way. */
        for (i = 0; i < filecnt; ++i) {
                struct vnode * rvp;
@@ -3296,28 +3398,127 @@ hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk)
                hfs_unlock(VTOC(vp));
                vnode_put(vp);
                vp = NULL;
                hfs_unlock(VTOC(vp));
                vnode_put(vp);
                vp = NULL;
+
+               ++hfsmp->hfs_resize_filesmoved;
+
+               /* Report intermediate progress. */
+               if (filecnt > 100) {
+                       int progress;
+
+                       progress = (i * 100) / filecnt;
+                       if (progress > (lastprogress + 9)) {
+                               printf("hfs_reclaimspace: %d%% done...\n", progress);
+                               lastprogress = progress;
+                       }
+               }
        }
        if (vp) {
                hfs_unlock(VTOC(vp));
                vnode_put(vp);
                vp = NULL;
        }
        }
        if (vp) {
                hfs_unlock(VTOC(vp));
                vnode_put(vp);
                vp = NULL;
        }
-
-       /*
-        * Note: this implementation doesn't handle overflow extents.
-        */
+       if (hfsmp->hfs_resize_filesmoved != 0) {
+               printf("hfs_reclaimspace: relocated %d files on \"%s\"\n",
+                      (int)hfsmp->hfs_resize_filesmoved, hfsmp->vcbVN);
+       }
 out:
        kmem_free(kernel_map, (vm_offset_t)iterator, sizeof(*iterator));
        kmem_free(kernel_map, (vm_offset_t)cnidbufp, cnidbufsize);
 
 out:
        kmem_free(kernel_map, (vm_offset_t)iterator, sizeof(*iterator));
        kmem_free(kernel_map, (vm_offset_t)cnidbufp, cnidbufsize);
 
-       /* On errors restore the roving allocation pointer. */
-       if (error) {
+       /*
+        * Restore the roving allocation pointer on errors.
+        * (but only if we didn't move any files)
+        */
+       if (error && hfsmp->hfs_resize_filesmoved == 0) {
                hfsmp->nextAllocation = saved_next_allocation;
        }
        return (error);
 }
 
 
                hfsmp->nextAllocation = saved_next_allocation;
        }
        return (error);
 }
 
 
+/*
+ * Check if there are any overflow extents that overlap.
+ */
+static int
+hfs_overlapped_overflow_extents(struct hfsmount *hfsmp, u_int32_t startblk, u_int32_t catblks, u_int32_t fileID, int rsrcfork)
+{
+       struct BTreeIterator * iterator = NULL;
+       struct FSBufferDescriptor btdata;
+       HFSPlusExtentRecord extrec;
+       HFSPlusExtentKey *extkeyptr;
+       FCB *fcb;
+       u_int32_t block;
+       u_int8_t forktype;
+       int overlapped = 0;
+       int i;
+       int error;
+
+       forktype = rsrcfork ? 0xFF : 0;
+       if (kmem_alloc(kernel_map, (vm_offset_t *)&iterator, sizeof(*iterator))) {
+               return (0);
+       }       
+       bzero(iterator, sizeof(*iterator));
+       extkeyptr = (HFSPlusExtentKey *)&iterator->key;
+       extkeyptr->keyLength = kHFSPlusExtentKeyMaximumLength;
+       extkeyptr->forkType = forktype;
+       extkeyptr->fileID = fileID;
+       extkeyptr->startBlock = catblks;
+
+       btdata.bufferAddress = &extrec;
+       btdata.itemSize = sizeof(extrec);
+       btdata.itemCount = 1;
+       
+       fcb = VTOF(hfsmp->hfs_extents_vp);
+
+       error = BTSearchRecord(fcb, iterator, &btdata, NULL, iterator);
+       while (error == 0) {
+               /* Stop when we encounter a different file. */
+               if ((extkeyptr->fileID != fileID) ||
+                   (extkeyptr->forkType != forktype)) {
+                       break;
+               }
+               /* 
+                * Check if the file overlaps target space.
+                */
+               for (i = 0; i < kHFSPlusExtentDensity; ++i) {
+                       if (extrec[i].blockCount == 0) {
+                               break;
+                       }
+                       block = extrec[i].startBlock + extrec[i].blockCount;
+                       if (block >= startblk) {
+                               overlapped = 1;
+                               break;
+                       }
+               }
+               /* Look for more records. */
+               error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
+       }
+
+       kmem_free(kernel_map, (vm_offset_t)iterator, sizeof(*iterator));
+       return (overlapped);
+}
+
+
+/*
+ * Calculate the progress of a file system resize operation.
+ */
+__private_extern__
+int
+hfs_resize_progress(struct hfsmount *hfsmp, u_int32_t *progress)
+{
+       if ((hfsmp->hfs_flags & HFS_RESIZE_IN_PROGRESS) == 0) {
+               return (ENXIO);
+       }
+
+       if (hfsmp->hfs_resize_totalfiles > 0)
+               *progress = (hfsmp->hfs_resize_filesmoved * 100) / hfsmp->hfs_resize_totalfiles;
+       else
+               *progress = 0;
+
+       return (0);
+}
+
+
 /*
  * Get file system attributes.
  */
 /*
  * Get file system attributes.
  */