+ if (create_record == true) {
+ /* Initialize new record content with only one extent entry */
+ bzero(extents, sizeof(HFSPlusExtentRecord));
+ /* The new record will contain only one extent entry */
+ extents[0] = shift_extent;
+ /* There are no more overflow extents to be shifted */
+ shift_extent.startBlock = shift_extent.blockCount = 0;
+
+ if (is_xattr) {
+ /* BTSearchRecord above returned btNotFound,
+ * but since the attribute btree is never empty
+ * if we are trying to insert new overflow
+ * record for the xattrs, the extents_key will
+ * contain correct data. So we don't need to
+ * re-initialize it again like below.
+ */
+
+ /* Initialize the new xattr record */
+ xattr_rec->recordType = kHFSPlusAttrExtents;
+ xattr_rec->overflowExtents.reserved = 0;
+ reclen = sizeof(HFSPlusAttrExtents);
+ } else {
+ /* BTSearchRecord above returned btNotFound,
+ * which means that extents_key content might
+ * not correspond to the record that we are
+ * trying to create, especially when the extents
+ * overflow btree is empty. So we reinitialize
+ * the extents_key again always.
+ */
+ extents_key->keyLength = kHFSPlusExtentKeyMaximumLength;
+ extents_key->forkType = extent_info->forkType;
+ extents_key->fileID = extent_info->fileID;
+
+ /* Initialize the new extent record */
+ reclen = sizeof(HFSPlusExtentRecord);
+ }
+ } else {
+ /* The overflow extent entry from previous record will be
+ * the first entry in this extent record. If the last
+ * extent entry in this record is valid, it will be shifted
+ * into the following extent record as its first entry. So
+ * save the last entry before shifting entries in current
+ * record.
+ */
+ last_extent = extents[kHFSPlusExtentDensity-1];
+
+ /* Shift all entries by one index towards the end */
+ for (i = kHFSPlusExtentDensity-2; i >= 0; i--) {
+ extents[i+1] = extents[i];
+ }
+
+ /* Overflow extent entry saved from previous record
+ * is now the first entry in the current record.
+ */
+ extents[0] = shift_extent;
+
+ if (hfs_resize_debug) {
+ printf ("hfs_split_extent: Shift overflow=(%u,%u) to record with updated startBlock=%u\n", shift_extent.startBlock, shift_extent.blockCount, write_recStartBlock);
+ }
+
+ /* The last entry from current record will be the
+ * overflow entry which will be the first entry for
+ * the following extent record.
+ */
+ shift_extent = last_extent;
+
+ /* Since the key->startBlock is being changed for this record,
+ * it should be deleted and inserted with the new key.
+ */
+ error = BTDeleteRecord(extent_info->fcb, &iterator);
+ if (error) {
+ printf ("hfs_split_extent: fileID=%u startBlock=%u BTDeleteRecord error=%d\n", extent_info->fileID, read_recStartBlock, error);
+ goto out;
+ }
+ if (hfs_resize_debug) {
+ printf ("hfs_split_extent: Deleted record with startBlock=%u\n", (is_xattr ? xattr_key->startBlock : extents_key->startBlock));
+ }
+ }
+
+ /* Insert the newly created or modified extent record */
+ bzero(&iterator.hint, sizeof(iterator.hint));
+ if (is_xattr) {
+ xattr_key->startBlock = write_recStartBlock;
+ } else {
+ extents_key->startBlock = write_recStartBlock;
+ }
+ error = BTInsertRecord(extent_info->fcb, &iterator, &btdata, reclen);
+ if (error) {
+ printf ("hfs_split_extent: fileID=%u, startBlock=%u BTInsertRecord error=%d\n", extent_info->fileID, write_recStartBlock, error);
+ goto out;
+ }
+ if (hfs_resize_debug) {
+ printf ("hfs_split_extent: Inserted extent record with startBlock=%u\n", write_recStartBlock);
+ }
+ }
+ BTFlushPath(extent_info->fcb);
+out:
+ if (extents_rec) {
+ FREE (extents_rec, M_TEMP);
+ }
+ if (xattr_rec) {
+ FREE (xattr_rec, M_TEMP);
+ }
+ return error;
+}
+
+
+/*
+ * Relocate an extent if it lies beyond the expected end of volume.
+ *
+ * This function is called for every extent of the file being relocated.
+ * It allocates space for relocation, copies the data, deallocates
+ * the old extent, and update corresponding on-disk extent. If the function
+ * does not find contiguous space to relocate an extent, it splits the
+ * extent in smaller size to be able to relocate it out of the area of
+ * disk being reclaimed. As an optimization, if an extent lies partially
+ * in the area of the disk being reclaimed, it is split so that we only
+ * have to relocate the area that was overlapping with the area of disk
+ * being reclaimed.
+ *
+ * Note that every extent is relocated in its own transaction so that
+ * they do not overwhelm the journal. This function handles the extent
+ * record that exists in the catalog record, extent record from overflow
+ * extents btree, and extents for large EAs.
+ *
+ * Inputs:
+ * extent_info - This is the structure that contains state about
+ * the current file, extent, and extent record that
+ * is being relocated. This structure is shared
+ * among code that traverses through all the extents
+ * of the file, code that relocates extents, and
+ * code that splits the extent.
+ */
+static int
+hfs_reclaim_extent(struct hfsmount *hfsmp, const u_long allocLimit, struct hfs_reclaim_extent_info *extent_info, vfs_context_t context)
+{
+ int error = 0;
+ int index;
+ struct cnode *cp;
+ u_int32_t oldStartBlock;
+ u_int32_t oldBlockCount;
+ u_int32_t newStartBlock;
+ u_int32_t newBlockCount;
+ u_int32_t roundedBlockCount;
+ uint16_t node_size;
+ uint32_t remainder_blocks;
+ u_int32_t alloc_flags;
+ int blocks_allocated = false;
+
+ index = extent_info->extent_index;
+ cp = VTOC(extent_info->vp);
+
+ oldStartBlock = extent_info->extents[index].startBlock;
+ oldBlockCount = extent_info->extents[index].blockCount;
+
+ if (0 && hfs_resize_debug) {
+ printf ("hfs_reclaim_extent: Examine record:%u recStartBlock=%u, %u:(%u,%u)\n", extent_info->overflow_count, extent_info->recStartBlock, index, oldStartBlock, oldBlockCount);
+ }
+
+ /* If the current extent lies completely within allocLimit,
+ * it does not require any relocation.
+ */
+ if ((oldStartBlock + oldBlockCount) <= allocLimit) {
+ extent_info->cur_blockCount += oldBlockCount;
+ return error;
+ }
+
+ /* Every extent should be relocated in its own transaction
+ * to make sure that we don't overflow the journal buffer.
+ */
+ error = hfs_start_transaction(hfsmp);
+ if (error) {
+ return error;
+ }
+ extent_info->lockflags = hfs_systemfile_lock(hfsmp, extent_info->lockflags, HFS_EXCLUSIVE_LOCK);
+
+ /* Check if the extent lies partially in the area to reclaim,
+ * i.e. it starts before allocLimit and ends beyond allocLimit.
+ * We have already skipped extents that lie completely within
+ * allocLimit in the check above, so we only check for the
+ * startBlock. If it lies partially, split it so that we
+ * only relocate part of the extent.
+ */
+ if (oldStartBlock < allocLimit) {
+ newBlockCount = allocLimit - oldStartBlock;
+
+ /* If the extent belongs to a btree, check and trim
+ * it to be multiple of the node size.
+ */
+ if (extent_info->is_sysfile) {
+ node_size = get_btree_nodesize(extent_info->vp);
+ /* If the btree node size is less than the block size,
+ * splitting this extent will not split a node across
+ * different extents. So we only check and trim if
+ * node size is more than the allocation block size.
+ */
+ if (node_size > hfsmp->blockSize) {
+ remainder_blocks = newBlockCount % (node_size / hfsmp->blockSize);
+ if (remainder_blocks) {
+ newBlockCount -= remainder_blocks;
+ if (hfs_resize_debug) {
+ printf ("hfs_reclaim_extent: Fixing extent block count, node_blks=%u, old=%u, new=%u\n", node_size/hfsmp->blockSize, newBlockCount + remainder_blocks, newBlockCount);
+ }
+ }
+ }
+ }
+
+ if (hfs_resize_debug) {
+ int idx = extent_info->extent_index;
+ printf ("hfs_reclaim_extent: Split straddling extent %u:(%u,%u) for %u blocks\n", idx, extent_info->extents[idx].startBlock, extent_info->extents[idx].blockCount, newBlockCount);
+ }
+
+ /* Split the extents into two parts --- the first extent lies
+ * completely within allocLimit and therefore does not require
+ * relocation. The second extent will require relocation which
+ * will be handled when the caller calls this function again
+ * for the next extent.
+ */
+ error = hfs_split_extent(extent_info, newBlockCount);
+ if (error == 0) {
+ /* Split success, no relocation required */
+ goto out;
+ }
+ /* Split failed, so try to relocate entire extent */
+ if (hfs_resize_debug) {
+ printf ("hfs_reclaim_extent: Split straddling extent failed, reclocate full extent\n");
+ }
+ }
+
+ /* At this point, the current extent requires relocation.
+ * We will try to allocate space equal to the size of the extent
+ * being relocated first to try to relocate it without splitting.
+ * If the allocation fails, we will try to allocate contiguous
+ * blocks out of metadata zone. If that allocation also fails,
+ * then we will take a whatever contiguous block run is returned
+ * by the allocation, split the extent into two parts, and then
+ * relocate the first splitted extent.
+ */
+ alloc_flags = HFS_ALLOC_FORCECONTIG | HFS_ALLOC_SKIPFREEBLKS;
+ if (extent_info->is_sysfile) {
+ alloc_flags |= HFS_ALLOC_METAZONE;
+ }
+
+ error = BlockAllocate(hfsmp, 1, oldBlockCount, oldBlockCount, alloc_flags,
+ &newStartBlock, &newBlockCount);
+ if ((extent_info->is_sysfile == false) &&
+ ((error == dskFulErr) || (error == ENOSPC))) {
+ /* For non-system files, try reallocating space in metadata zone */
+ alloc_flags |= HFS_ALLOC_METAZONE;
+ error = BlockAllocate(hfsmp, 1, oldBlockCount, oldBlockCount,
+ alloc_flags, &newStartBlock, &newBlockCount);
+ }
+ if ((error == dskFulErr) || (error == ENOSPC)) {
+ /* We did not find desired contiguous space for this extent.
+ * So try to allocate the maximum contiguous space available.
+ */
+ alloc_flags &= ~HFS_ALLOC_FORCECONTIG;
+
+ error = BlockAllocate(hfsmp, 1, oldBlockCount, oldBlockCount,
+ alloc_flags, &newStartBlock, &newBlockCount);
+ if (error) {
+ printf ("hfs_reclaim_extent: fileID=%u start=%u, %u:(%u,%u) BlockAllocate error=%d\n", extent_info->fileID, extent_info->recStartBlock, index, oldStartBlock, oldBlockCount, error);
+ goto out;
+ }
+ blocks_allocated = true;
+
+ /* The number of blocks allocated is less than the requested
+ * number of blocks. For btree extents, check and trim the
+ * extent to be multiple of the node size.
+ */
+ if (extent_info->is_sysfile) {
+ node_size = get_btree_nodesize(extent_info->vp);
+ if (node_size > hfsmp->blockSize) {
+ remainder_blocks = newBlockCount % (node_size / hfsmp->blockSize);
+ if (remainder_blocks) {
+ roundedBlockCount = newBlockCount - remainder_blocks;
+ /* Free tail-end blocks of the newly allocated extent */
+ BlockDeallocate(hfsmp, newStartBlock + roundedBlockCount,
+ newBlockCount - roundedBlockCount,
+ HFS_ALLOC_SKIPFREEBLKS);
+ newBlockCount = roundedBlockCount;
+ if (hfs_resize_debug) {
+ printf ("hfs_reclaim_extent: Fixing extent block count, node_blks=%u, old=%u, new=%u\n", node_size/hfsmp->blockSize, newBlockCount + remainder_blocks, newBlockCount);
+ }
+ if (newBlockCount == 0) {
+ printf ("hfs_reclaim_extent: Not enough contiguous blocks available to relocate fileID=%d\n", extent_info->fileID);
+ error = ENOSPC;
+ goto out;
+ }
+ }
+ }
+ }
+
+ /* The number of blocks allocated is less than the number of
+ * blocks requested, so split this extent --- the first extent
+ * will be relocated as part of this function call and the caller
+ * will handle relocating the second extent by calling this
+ * function again for the second extent.
+ */
+ error = hfs_split_extent(extent_info, newBlockCount);
+ if (error) {
+ printf ("hfs_reclaim_extent: fileID=%u start=%u, %u:(%u,%u) split error=%d\n", extent_info->fileID, extent_info->recStartBlock, index, oldStartBlock, oldBlockCount, error);
+ goto out;
+ }
+ oldBlockCount = newBlockCount;
+ }
+ if (error) {
+ printf ("hfs_reclaim_extent: fileID=%u start=%u, %u:(%u,%u) contig BlockAllocate error=%d\n", extent_info->fileID, extent_info->recStartBlock, index, oldStartBlock, oldBlockCount, error);
+ goto out;
+ }
+ blocks_allocated = true;
+
+ /* Copy data from old location to new location */
+ error = hfs_copy_extent(hfsmp, extent_info->vp, oldStartBlock,
+ newStartBlock, newBlockCount, context);
+ if (error) {
+ printf ("hfs_reclaim_extent: fileID=%u start=%u, %u:(%u,%u)=>(%u,%u) hfs_copy_extent error=%d\n", extent_info->fileID, extent_info->recStartBlock, index, oldStartBlock, oldBlockCount, newStartBlock, newBlockCount, error);
+ goto out;
+ }
+
+ /* Update the extent record with the new start block information */
+ extent_info->extents[index].startBlock = newStartBlock;
+
+ /* Sync the content back to the disk */
+ if (extent_info->catalog_fp) {
+ /* Update the extents in catalog record */
+ if (extent_info->is_dirlink) {
+ error = cat_update_dirlink(hfsmp, extent_info->forkType,
+ extent_info->dirlink_desc, extent_info->dirlink_attr,
+ &(extent_info->dirlink_fork->ff_data));
+ } else {
+ cp->c_flag |= C_MODIFIED;
+ /* If this is a system file, sync volume headers on disk */
+ if (extent_info->is_sysfile) {
+ error = hfs_flushvolumeheader(hfsmp, MNT_WAIT, HFS_ALTFLUSH);
+ }
+ }
+ } else {
+ /* Replace record for extents overflow or extents-based xattrs */
+ error = BTReplaceRecord(extent_info->fcb, extent_info->iterator,
+ &(extent_info->btdata), extent_info->recordlen);
+ }
+ if (error) {
+ printf ("hfs_reclaim_extent: fileID=%u, update record error=%u\n", extent_info->fileID, error);
+ goto out;
+ }
+
+ /* Deallocate the old extent */
+ error = BlockDeallocate(hfsmp, oldStartBlock, oldBlockCount, HFS_ALLOC_SKIPFREEBLKS);
+ if (error) {
+ printf ("hfs_reclaim_extent: fileID=%u start=%u, %u:(%u,%u) BlockDeallocate error=%d\n", extent_info->fileID, extent_info->recStartBlock, index, oldStartBlock, oldBlockCount, error);
+ goto out;
+ }
+ extent_info->blocks_relocated += newBlockCount;
+
+ if (hfs_resize_debug) {
+ printf ("hfs_reclaim_extent: Relocated record:%u %u:(%u,%u) to (%u,%u)\n", extent_info->overflow_count, index, oldStartBlock, oldBlockCount, newStartBlock, newBlockCount);
+ }
+
+out:
+ if (error != 0) {
+ if (blocks_allocated == true) {
+ BlockDeallocate(hfsmp, newStartBlock, newBlockCount, HFS_ALLOC_SKIPFREEBLKS);
+ }
+ } else {
+ /* On success, increment the total allocation blocks processed */
+ extent_info->cur_blockCount += newBlockCount;
+ }
+
+ hfs_systemfile_unlock(hfsmp, extent_info->lockflags);
+
+ /* For a non-system file, if an extent entry from catalog record
+ * was modified, sync the in-memory changes to the catalog record
+ * on disk before ending the transaction.
+ */
+ if ((extent_info->catalog_fp) &&
+ (extent_info->is_sysfile == false)) {
+ (void) hfs_update(extent_info->vp, MNT_WAIT);
+ }
+
+ hfs_end_transaction(hfsmp);
+
+ return error;
+}
+
+/* Report intermediate progress during volume resize */
+static void
+hfs_truncatefs_progress(struct hfsmount *hfsmp)
+{
+ u_int32_t cur_progress;
+
+ hfs_resize_progress(hfsmp, &cur_progress);
+ if (cur_progress > (hfsmp->hfs_resize_progress + 9)) {
+ printf("hfs_truncatefs: %d%% done...\n", cur_progress);
+ hfsmp->hfs_resize_progress = cur_progress;
+ }
+ return;
+}
+
+/*
+ * Reclaim space at the end of a volume for given file and forktype.
+ *
+ * This routine attempts to move any extent which contains allocation blocks
+ * at or after "allocLimit." A separate transaction is used for every extent
+ * that needs to be moved. If there is not contiguous space available for
+ * moving an extent, it can be split into smaller extents. The contents of
+ * any moved extents are read and written via the volume's device vnode --
+ * NOT via "vp." During the move, moved blocks which are part of a transaction
+ * have their physical block numbers invalidated so they will eventually be
+ * written to their new locations.
+ *
+ * This function is also called for directory hard links. Directory hard links
+ * are regular files with no data fork and resource fork that contains alias
+ * information for backward compatibility with pre-Leopard systems. However
+ * non-Mac OS X implementation can add/modify data fork or resource fork
+ * information to directory hard links, so we check, and if required, relocate
+ * both data fork and resource fork.
+ *
+ * Inputs:
+ * hfsmp The volume being resized.
+ * vp The vnode for the system file.
+ * fileID ID of the catalog record that needs to be relocated
+ * forktype The type of fork that needs relocated,
+ * kHFSResourceForkType for resource fork,
+ * kHFSDataForkType for data fork
+ * allocLimit Allocation limit for the new volume size,
+ * do not use this block or beyond. All extents
+ * that use this block or any blocks beyond this limit
+ * will be relocated.
+ *
+ * Side Effects:
+ * hfsmp->hfs_resize_blocksmoved is incremented by the number of allocation
+ * blocks that were relocated.
+ */
+static int
+hfs_reclaim_file(struct hfsmount *hfsmp, struct vnode *vp, u_int32_t fileID,
+ u_int8_t forktype, u_long allocLimit, vfs_context_t context)
+{
+ int error = 0;
+ struct hfs_reclaim_extent_info *extent_info;
+ int i;
+ int lockflags = 0;
+ struct cnode *cp;
+ struct filefork *fp;
+ int took_truncate_lock = false;
+ int release_desc = false;
+ HFSPlusExtentKey *key;
+
+ /* If there is no vnode for this file, then there's nothing to do. */
+ if (vp == NULL) {
+ return 0;
+ }
+
+ cp = VTOC(vp);
+
+ MALLOC(extent_info, struct hfs_reclaim_extent_info *,
+ sizeof(struct hfs_reclaim_extent_info), M_TEMP, M_WAITOK);
+ if (extent_info == NULL) {
+ return ENOMEM;
+ }
+ bzero(extent_info, sizeof(struct hfs_reclaim_extent_info));
+ extent_info->vp = vp;
+ extent_info->fileID = fileID;
+ extent_info->forkType = forktype;
+ extent_info->is_sysfile = vnode_issystem(vp);
+ if (vnode_isdir(vp) && (cp->c_flag & C_HARDLINK)) {
+ extent_info->is_dirlink = true;
+ }
+ /* We always need allocation bitmap and extent btree lock */
+ lockflags = SFL_BITMAP | SFL_EXTENTS;
+ if ((fileID == kHFSCatalogFileID) || (extent_info->is_dirlink == true)) {
+ lockflags |= SFL_CATALOG;
+ } else if (fileID == kHFSAttributesFileID) {
+ lockflags |= SFL_ATTRIBUTE;
+ } else if (fileID == kHFSStartupFileID) {
+ lockflags |= SFL_STARTUP;
+ }
+ extent_info->lockflags = lockflags;
+ extent_info->fcb = VTOF(hfsmp->hfs_extents_vp);
+
+ /* Flush data associated with current file on disk.
+ *
+ * If the current vnode is directory hard link, no flushing of
+ * journal or vnode is required. The current kernel does not
+ * modify data/resource fork of directory hard links, so nothing
+ * will be in the cache. If a directory hard link is newly created,
+ * the resource fork data is written directly using devvp and
+ * the code that actually relocates data (hfs_copy_extent()) also
+ * uses devvp for its I/O --- so they will see a consistent copy.
+ */
+ if (extent_info->is_sysfile) {
+ /* If the current vnode is system vnode, flush journal
+ * to make sure that all data is written to the disk.
+ */
+ error = hfs_journal_flush(hfsmp, TRUE);
+ if (error) {
+ printf ("hfs_reclaim_file: journal_flush returned %d\n", error);
+ goto out;
+ }
+ } else if (extent_info->is_dirlink == false) {
+ /* Flush all blocks associated with this regular file vnode.
+ * Normally there should not be buffer cache blocks for regular
+ * files, but for objects like symlinks, we can have buffer cache
+ * blocks associated with the vnode. Therefore we call
+ * buf_flushdirtyblks() also.
+ */
+ buf_flushdirtyblks(vp, 0, BUF_SKIP_LOCKED, "hfs_reclaim_file");
+
+ hfs_unlock(cp);
+ hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK);
+ took_truncate_lock = true;
+ (void) cluster_push(vp, 0);
+ error = hfs_lock(cp, HFS_FORCE_LOCK);
+ if (error) {
+ goto out;
+ }
+
+ /* If the file no longer exists, nothing left to do */
+ if (cp->c_flag & C_NOEXISTS) {
+ error = 0;
+ goto out;
+ }
+
+ /* Wait for any in-progress writes to this vnode to complete, so that we'll
+ * be copying consistent bits. (Otherwise, it's possible that an async
+ * write will complete to the old extent after we read from it. That
+ * could lead to corruption.)
+ */
+ error = vnode_waitforwrites(vp, 0, 0, 0, "hfs_reclaim_file");
+ if (error) {
+ goto out;
+ }
+ }
+
+ if (hfs_resize_debug) {
+ printf("hfs_reclaim_file: === Start reclaiming %sfork for %sid=%u ===\n", (forktype ? "rsrc" : "data"), (extent_info->is_dirlink ? "dirlink" : "file"), fileID);
+ }
+
+ if (extent_info->is_dirlink) {
+ MALLOC(extent_info->dirlink_desc, struct cat_desc *,
+ sizeof(struct cat_desc), M_TEMP, M_WAITOK);
+ MALLOC(extent_info->dirlink_attr, struct cat_attr *,
+ sizeof(struct cat_attr), M_TEMP, M_WAITOK);
+ MALLOC(extent_info->dirlink_fork, struct filefork *,
+ sizeof(struct filefork), M_TEMP, M_WAITOK);
+ if ((extent_info->dirlink_desc == NULL) ||
+ (extent_info->dirlink_attr == NULL) ||
+ (extent_info->dirlink_fork == NULL)) {
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* Lookup catalog record for directory hard link and
+ * create a fake filefork for the value looked up from
+ * the disk.
+ */
+ fp = extent_info->dirlink_fork;
+ bzero(extent_info->dirlink_fork, sizeof(struct filefork));
+ extent_info->dirlink_fork->ff_cp = cp;
+ lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_EXCLUSIVE_LOCK);
+ error = cat_lookup_dirlink(hfsmp, fileID, forktype,
+ extent_info->dirlink_desc, extent_info->dirlink_attr,
+ &(extent_info->dirlink_fork->ff_data));
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ if (error) {
+ printf ("hfs_reclaim_file: cat_lookup_dirlink for fileID=%u returned error=%u\n", fileID, error);
+ goto out;
+ }
+ release_desc = true;
+ } else {
+ fp = VTOF(vp);
+ }
+
+ extent_info->catalog_fp = fp;
+ extent_info->recStartBlock = 0;
+ extent_info->extents = extent_info->catalog_fp->ff_extents;
+ /* Relocate extents from the catalog record */
+ for (i = 0; i < kHFSPlusExtentDensity; ++i) {
+ if (fp->ff_extents[i].blockCount == 0) {
+ break;
+ }
+ extent_info->extent_index = i;
+ error = hfs_reclaim_extent(hfsmp, allocLimit, extent_info, context);
+ if (error) {
+ printf ("hfs_reclaim_file: fileID=%u #%d %u:(%u,%u) hfs_reclaim_extent error=%d\n", fileID, extent_info->overflow_count, i, fp->ff_extents[i].startBlock, fp->ff_extents[i].blockCount, error);
+ goto out;
+ }
+ }
+
+ /* If the number of allocation blocks processed for reclaiming
+ * are less than total number of blocks for the file, continuing
+ * working on overflow extents record.
+ */
+ if (fp->ff_blocks <= extent_info->cur_blockCount) {
+ if (0 && hfs_resize_debug) {
+ printf ("hfs_reclaim_file: Nothing more to relocate, offset=%d, ff_blocks=%u, cur_blockCount=%u\n", i, fp->ff_blocks, extent_info->cur_blockCount);
+ }
+ goto out;
+ }
+
+ if (hfs_resize_debug) {
+ printf ("hfs_reclaim_file: Will check overflow records, offset=%d, ff_blocks=%u, cur_blockCount=%u\n", i, fp->ff_blocks, extent_info->cur_blockCount);
+ }
+
+ MALLOC(extent_info->iterator, struct BTreeIterator *, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
+ if (extent_info->iterator == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ bzero(extent_info->iterator, sizeof(struct BTreeIterator));
+ key = (HFSPlusExtentKey *) &(extent_info->iterator->key);
+ key->keyLength = kHFSPlusExtentKeyMaximumLength;
+ key->forkType = forktype;
+ key->fileID = fileID;
+ key->startBlock = extent_info->cur_blockCount;
+
+ extent_info->btdata.bufferAddress = extent_info->record.overflow;
+ extent_info->btdata.itemSize = sizeof(HFSPlusExtentRecord);
+ extent_info->btdata.itemCount = 1;
+
+ extent_info->catalog_fp = NULL;
+
+ /* Search the first overflow extent with expected startBlock as 'cur_blockCount' */
+ lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_EXCLUSIVE_LOCK);
+ error = BTSearchRecord(extent_info->fcb, extent_info->iterator,
+ &(extent_info->btdata), &(extent_info->recordlen),
+ extent_info->iterator);
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ while (error == 0) {
+ extent_info->overflow_count++;
+ extent_info->recStartBlock = key->startBlock;
+ extent_info->extents = extent_info->record.overflow;
+ for (i = 0; i < kHFSPlusExtentDensity; i++) {
+ if (extent_info->record.overflow[i].blockCount == 0) {
+ goto out;
+ }
+ extent_info->extent_index = i;
+ error = hfs_reclaim_extent(hfsmp, allocLimit, extent_info, context);
+ if (error) {
+ printf ("hfs_reclaim_file: fileID=%u #%d %u:(%u,%u) hfs_reclaim_extent error=%d\n", fileID, extent_info->overflow_count, i, extent_info->record.overflow[i].startBlock, extent_info->record.overflow[i].blockCount, error);
+ goto out;
+ }
+ }
+
+ /* Look for more overflow records */
+ lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_EXCLUSIVE_LOCK);
+ error = BTIterateRecord(extent_info->fcb, kBTreeNextRecord,
+ extent_info->iterator, &(extent_info->btdata),
+ &(extent_info->recordlen));
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ if (error) {
+ break;
+ }
+ /* Stop when we encounter a different file or fork. */
+ if ((key->fileID != fileID) || (key->forkType != forktype)) {
+ break;
+ }
+ }
+ if (error == fsBTRecordNotFoundErr || error == fsBTEndOfIterationErr) {
+ error = 0;
+ }
+
+out:
+ /* If any blocks were relocated, account them and report progress */
+ if (extent_info->blocks_relocated) {
+ hfsmp->hfs_resize_blocksmoved += extent_info->blocks_relocated;
+ hfs_truncatefs_progress(hfsmp);
+ if (fileID < kHFSFirstUserCatalogNodeID) {
+ printf ("hfs_reclaim_file: Relocated %u blocks from fileID=%u on \"%s\"\n",
+ extent_info->blocks_relocated, fileID, hfsmp->vcbVN);
+ }
+ }
+ if (extent_info->iterator) {
+ FREE(extent_info->iterator, M_TEMP);
+ }
+ if (release_desc == true) {
+ cat_releasedesc(extent_info->dirlink_desc);
+ }
+ if (extent_info->dirlink_desc) {
+ FREE(extent_info->dirlink_desc, M_TEMP);
+ }
+ if (extent_info->dirlink_attr) {
+ FREE(extent_info->dirlink_attr, M_TEMP);
+ }
+ if (extent_info->dirlink_fork) {
+ FREE(extent_info->dirlink_fork, M_TEMP);
+ }
+ if ((extent_info->blocks_relocated != 0) && (extent_info->is_sysfile == false)) {
+ (void) hfs_update(vp, MNT_WAIT);
+ }
+ if (took_truncate_lock) {
+ hfs_unlock_truncate(cp, 0);
+ }
+ if (extent_info) {
+ FREE(extent_info, M_TEMP);
+ }
+ if (hfs_resize_debug) {
+ printf("hfs_reclaim_file: === Finished relocating %sfork for fileid=%u (error=%d) ===\n", (forktype ? "rsrc" : "data"), fileID, error);
+ }