+/*
+ * HFS Plus only
+ *
+ */
+__private_extern__
+OSErr HeadTruncateFile (
+ ExtendedVCB *vcb,
+ FCB *fcb,
+ UInt32 headblks)
+{
+ HFSPlusExtentRecord extents;
+ HFSPlusExtentRecord tailExtents;
+ HFSCatalogNodeID fileID;
+ UInt8 forkType;
+ UInt32 blkcnt;
+ UInt32 startblk;
+ UInt32 blksfreed;
+ int i, j;
+ int error;
+
+
+ if (vcb->vcbSigWord != kHFSPlusSigWord)
+ return (-1);
+
+ forkType = FORK_IS_RSRC(fcb) ? kResourceForkType : kDataForkType;
+ fileID = FTOC(fcb)->c_fileid;
+ bzero(tailExtents, sizeof(tailExtents));
+
+ blksfreed = 0;
+ startblk = 0;
+
+ /*
+ * Process catalog resident extents
+ */
+ for (i = 0, j = 0; i < kHFSPlusExtentDensity; ++i) {
+ blkcnt = fcb->fcbExtents[i].blockCount;
+ if (blkcnt == 0)
+ break; /* end of extents */
+
+ if (blksfreed < headblks) {
+ error = BlockDeallocate(vcb, fcb->fcbExtents[i].startBlock, blkcnt);
+ /*
+ * Any errors after the first BlockDeallocate
+ * must be ignored so we can put the file in
+ * a known state.
+ */
+ if (error ) {
+ if (i == 0)
+ goto ErrorExit; /* uh oh */
+ else {
+ error = 0;
+ printf("HeadTruncateFile: problems deallocating %s (%d)\n",
+ FTOC(fcb)->c_desc.cd_nameptr ? FTOC(fcb)->c_desc.cd_nameptr : "", error);
+ }
+ }
+
+ blksfreed += blkcnt;
+ fcb->fcbExtents[i].startBlock = 0;
+ fcb->fcbExtents[i].blockCount = 0;
+ } else {
+ tailExtents[j].startBlock = fcb->fcbExtents[i].startBlock;
+ tailExtents[j].blockCount = blkcnt;
+ ++j;
+ }
+ startblk += blkcnt;
+ }
+
+ if (blkcnt == 0)
+ goto CopyExtents;
+
+ /*
+ * Process overflow extents
+ */
+ for (;;) {
+ UInt32 extblks;
+
+ error = FindExtentRecord(vcb, forkType, fileID, startblk, false, NULL, extents, NULL);
+ if (error) {
+ /*
+ * Any errors after the first BlockDeallocate
+ * must be ignored so we can put the file in
+ * a known state.
+ */
+ if (error != btNotFound)
+ printf("HeadTruncateFile: problems finding extents %s (%d)\n",
+ FTOC(fcb)->c_desc.cd_nameptr ? FTOC(fcb)->c_desc.cd_nameptr : "", error);
+ error = 0;
+ break;
+ }
+
+ for(i = 0, extblks = 0; i < kHFSPlusExtentDensity; ++i) {
+ blkcnt = extents[i].blockCount;
+ if (blkcnt == 0)
+ break; /* end of extents */
+
+ if (blksfreed < headblks) {
+ error = BlockDeallocate(vcb, extents[i].startBlock, blkcnt);
+ if (error) {
+ printf("HeadTruncateFile: problems deallocating %s (%d)\n",
+ FTOC(fcb)->c_desc.cd_nameptr ? FTOC(fcb)->c_desc.cd_nameptr : "", error);
+ error = 0;
+ }
+ blksfreed += blkcnt;
+ } else {
+ tailExtents[j].startBlock = extents[i].startBlock;
+ tailExtents[j].blockCount = blkcnt;
+ ++j;
+ }
+ extblks += blkcnt;
+ }
+
+ error = DeleteExtentRecord(vcb, forkType, fileID, startblk);
+ if (error) {
+ printf("HeadTruncateFile: problems deallocating %s (%d)\n",
+ FTOC(fcb)->c_desc.cd_nameptr ? FTOC(fcb)->c_desc.cd_nameptr : "", error);
+ error = 0;
+ }
+
+ if (blkcnt == 0)
+ break; /* all done */
+
+ startblk += extblks;
+ }
+
+CopyExtents:
+ if (blksfreed) {
+ bcopy(tailExtents, fcb->fcbExtents, sizeof(tailExtents));
+ blkcnt = fcb->ff_blocks - headblks;
+ FTOC(fcb)->c_blocks -= blkcnt;
+ fcb->ff_blocks = blkcnt;
+
+ FTOC(fcb)->c_flag |= C_CHANGE | C_FORCEUPDATE;
+
+ (void) FlushExtentFile(vcb);
+ }
+
+ErrorExit:
+ return MacToVFSError(error);
+}
+
+