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, 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);
+static int hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk);
/*
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) && (retval != ENXIO)) {
- printf("hfs_sync_metadata: can't read volume header at %d! (retval 0x%x)\n",
- priIDSector, retval);
+ if (retval != 0) {
+ panic("hfs: sync_metadata: can't read super-block?! (retval 0x%x, priIDSector)\n",
+ retval, priIDSector);
}
if (retval == 0 && ((buf_flags(bp) & (B_DELWRI | B_LOCKED)) == B_DELWRI)) {
if (fhlen < sizeof(struct hfsfid))
return (EINVAL);
- result = hfs_vget(VFSTOHFS(mp), ntohl(hfsfhp->hfsfid_cnid), &nvp, 0);
+ result = hfs_vget(VFSTOHFS(mp), hfsfhp->hfsfid_cnid, &nvp, 0);
if (result) {
if (result == ENOENT)
result = ESTALE;
* 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 ((ntohl(hfsfhp->hfsfid_gen) < VTOC(nvp)->c_itime)) {
+ if ((hfsfhp->hfsfid_gen < VTOC(nvp)->c_itime)) {
hfs_unlock(VTOC(nvp));
vnode_put(nvp);
return (ESTALE);
cp = VTOC(vp);
hfsfhp = (struct hfsfid *)fhp;
- hfsfhp->hfsfid_cnid = htonl(cp->c_fileid);
- hfsfhp->hfsfid_gen = htonl(cp->c_itime);
+ hfsfhp->hfsfid_cnid = cp->c_fileid;
+ hfsfhp->hfsfid_gen = cp->c_itime;
*fhlenp = sizeof(struct hfsfid);
return (0);
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);
}
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;
(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;
+ }
}
}
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;
int transaction_begun = 0;
int error;
-
- lck_mtx_lock(&hfsmp->hfs_mutex);
- if (hfsmp->hfs_flags & HFS_RESIZE_IN_PROGRESS) {
- lck_mtx_unlock(&hfsmp->hfs_mutex);
- return (EALREADY);
+ /*
+ * Grab the root vnode to serialize with another hfs_truncatefs call.
+ */
+ error = hfs_vget(hfsmp, kHFSRootFolderID, &rvp, 0);
+ if (error) {
+ return (error);
}
- hfsmp->hfs_flags |= HFS_RESIZE_IN_PROGRESS;
- hfsmp->hfs_resize_filesmoved = 0;
- hfsmp->hfs_resize_totalfiles = 0;
- lck_mtx_unlock(&hfsmp->hfs_mutex);
-
/*
- * - Journaled HFS Plus volumes only.
+ * - HFS Plus file systems only.
+ * - Journaling must be enabled.
* - No embedded volumes.
*/
- if ((hfsmp->jnl == NULL) ||
+ if ((hfsmp->hfs_flags & HFS_STANDARD) ||
+ (hfsmp->jnl == NULL) ||
(hfsmp->hfsPlusIOPosOffset != 0)) {
error = EPERM;
goto out;
goto out;
}
/* Make sure there's enough space to work with. */
- if (reclaimblks >= hfs_freeblks(hfsmp, 1)) {
+ if (reclaimblks > (hfsmp->freeBlocks / 4)) {
error = ENOSPC;
goto out;
}
- /* Start with a clean journal. */
- journal_flush(hfsmp->jnl);
+
+ printf("hfs_truncatefs: shrinking %s by %d blocks out of %d\n",
+ hfsmp->vcbVN, reclaimblks, hfsmp->totalBlocks);
if (hfs_start_transaction(hfsmp) != 0) {
error = EINVAL;
transaction_begun = 0;
/* Attempt to reclaim some space. */
- if (hfs_reclaimspace(hfsmp, newblkcnt, reclaimblks) != 0) {
+ if (hfs_reclaimspace(hfsmp, newblkcnt) != 0) {
printf("hfs_truncatefs: couldn't reclaim space on %s\n", hfsmp->vcbVN);
error = ENOSPC;
goto out;
/* 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 = EAGAIN; /* tell client to try again */
+ error = ENOSPC;
goto out;
}
}
/*
* 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) {
+ journal_modify_block_start(hfsmp->jnl, bp);
bzero((void*)((char *)buf_dataptr(bp) + HFS_ALT_OFFSET(hfsmp->hfs_phys_block_size)), kMDBSize);
- (void) VNOP_BWRITE(bp);
+
+ journal_modify_block_end(hfsmp->jnl, bp);
} 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.
*/
}
if (transaction_begun) {
hfs_end_transaction(hfsmp);
- journal_flush(hfsmp->jnl);
}
-
- lck_mtx_lock(&hfsmp->hfs_mutex);
- hfsmp->hfs_flags &= ~HFS_RESIZE_IN_PROGRESS;
- lck_mtx_unlock(&hfsmp->hfs_mutex);
-
+ if (rvp) {
+ hfs_unlock(VTOC(rvp));
+ vnode_put(rvp);
+ }
return (error);
}
-
/*
* Reclaim space at the end of a file system.
*/
static int
-hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk, u_long reclaimblks)
+hfs_reclaimspace(struct hfsmount *hfsmp, u_long startblk)
{
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;
- int filecnt = 0;
+ int filecnt;
int maxfilecnt;
u_long block;
- u_long datablks;
- u_long rsrcblks;
- u_long blkstomove = 0;
int lockflags;
int i;
int error;
- int lastprogress = 0;
-
- /* Check if Attributes file overlaps reclaim area. */
+ /*
+ * Check if Attributes file overlaps.
+ */
if (hfsmp->hfs_attribute_vp) {
+ struct filefork *fp;
+
fp = VTOF(hfsmp->hfs_attribute_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;
+ 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 ((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 move a maximum of 250,000 files. */
- maxfilecnt = MIN(hfsmp->hfs_filecount, 250000);
- maxfilecnt = MIN((u_long)maxfilecnt, reclaimblks);
+ /* For now we'll move a maximum of 16,384 files. */
+ maxfilecnt = MIN(hfsmp->hfs_filecount, 16384);
cnidbufsize = maxfilecnt * sizeof(cnid_t);
if (kmem_alloc(kernel_map, (vm_offset_t *)&cnidbufp, cnidbufsize)) {
return (ENOMEM);
btdata.itemSize = sizeof(filerec);
btdata.itemCount = 1;
- /* Keep the Catalog and extents files locked during iteration. */
- lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS, HFS_SHARED_LOCK);
-
+ /* Keep the Catalog file locked during iteration. */
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
error = BTIterateRecord(fcb, kBTreeFirstRecord, iterator, NULL, NULL);
if (error) {
- goto end_iteration;
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ goto out;
}
+
/*
* Iterate over all the catalog records looking for files
* that overlap into the space we're trying to free up.
for (filecnt = 0; filecnt < maxfilecnt; ) {
error = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
if (error) {
- if (error == fsBTRecordNotFoundErr || error == fsBTEndOfIterationErr) {
- error = 0;
- }
+ if (error == btNotFound)
+ error = 0;
break;
}
- if (filerec.recordType != kHFSPlusFileRecord) {
+ if (filerec.recordType != kHFSPlusFileRecord ||
+ filerec.fileID == hfsmp->hfs_jnlfileid)
continue;
- }
- datablks = rsrcblks = 0;
/*
* Check if either fork overlaps target space.
*/
for (i = 0; i < kHFSPlusExtentDensity; ++i) {
- 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;
- }
- }
- 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;
+ 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;
break;
}
+ cnidbufp[filecnt++] = filerec.fileID;
+ 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;
- }
+ block = filerec.resourceFork.extents[i].startBlock +
+ filerec.resourceFork.extents[i].blockCount;
+ if (block >= startblk) {
+ cnidbufp[filecnt++] = filerec.fileID;
+ break;
}
}
}
-
-end_iteration:
- if (filecnt == 0) {
- error = ENOSPC;
- }
/* 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;
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 (hfsmp->hfs_resize_filesmoved != 0) {
- printf("hfs_reclaimspace: relocated %d files on \"%s\"\n",
- (int)hfsmp->hfs_resize_filesmoved, hfsmp->vcbVN);
- }
+
+ /*
+ * Note: this implementation doesn't handle overflow extents.
+ */
out:
kmem_free(kernel_map, (vm_offset_t)iterator, sizeof(*iterator));
kmem_free(kernel_map, (vm_offset_t)cnidbufp, cnidbufsize);
- /*
- * Restore the roving allocation pointer on errors.
- * (but only if we didn't move any files)
- */
- if (error && hfsmp->hfs_resize_filesmoved == 0) {
+ /* On errors restore the roving allocation pointer. */
+ if (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.
*/