+ invalid = 0;
+ BDINIT(btdata, &folder);
+ MALLOC(ip, BTreeIterator *, sizeof(*ip), M_TEMP, M_WAITOK);
+ keyp = (HFSPlusCatalogKey *)&ip->key;
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+
+ while (cnid != kHFSRootParentID) {
+ /* Check if the 'pointed at' directory is an ancestor */
+ if (pointed_at_cnid == cnid) {
+ invalid = 1;
+ break;
+ }
+ if ((result = getkey(hfsmp, cnid, (CatalogKey *)keyp))) {
+ printf("hfs: cat_check_link_ancestry: getkey for %u failed\n", cnid);
+ invalid = 1; /* On errors, assume an invalid parent */
+ break;
+ }
+ if ((result = BTSearchRecord(fcb, ip, &btdata, NULL, NULL))) {
+ printf("hfs: cat_check_link_ancestry: cannot find %u\n", cnid);
+ invalid = 1; /* On errors, assume an invalid parent */
+ break;
+ }
+ /* Check if this ancestor is a directory hard link */
+ if (folder.flags & kHFSHasLinkChainMask) {
+ invalid = 1;
+ break;
+ }
+ cnid = keyp->parentID;
+ }
+ FREE(ip, M_TEMP);
+ return (invalid);
+}
+
+
+/*
+ * update_siblinglinks_callback - update a link's chain
+ */
+
+struct linkupdate_state {
+ cnid_t filelinkid;
+ cnid_t prevlinkid;
+ cnid_t nextlinkid;
+};
+
+static int
+update_siblinglinks_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state)
+{
+ HFSPlusCatalogFile *file;
+
+ if (crp->recordType != kHFSPlusFileRecord) {
+ printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp->recordType);
+ return (btNotFound);
+ }
+
+ file = (struct HFSPlusCatalogFile *)crp;
+ if (file->flags & kHFSHasLinkChainMask) {
+ if (state->prevlinkid != HFS_IGNORABLE_LINK) {
+ file->hl_prevLinkID = state->prevlinkid;
+ }
+ if (state->nextlinkid != HFS_IGNORABLE_LINK) {
+ file->hl_nextLinkID = state->nextlinkid;
+ }
+ } else {
+ printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file->fileID);
+ }
+ return (0);
+}
+
+/*
+ * cat_update_siblinglinks - update a link's chain
+ */
+int
+cat_update_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cnid_t nextlinkid)
+{
+ FCB * fcb;
+ BTreeIterator * iterator;
+ struct linkupdate_state state;
+ int result;
+
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+ state.filelinkid = linkfileid;
+ state.prevlinkid = prevlinkid;
+ state.nextlinkid = nextlinkid;
+
+ /* Create an iterator for use by us temporarily */
+ MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ bzero(iterator, sizeof(*iterator));
+
+ result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key);
+ if (result == 0) {
+ result = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr)update_siblinglinks_callback, &state);
+ (void) BTFlushPath(fcb);
+ } else {
+ printf("hfs: cat_update_siblinglinks: couldn't resolve cnid %d\n", linkfileid);
+ }
+
+ FREE (iterator, M_TEMP);
+ return MacToVFSError(result);
+}
+
+/*
+ * cat_lookuplink - lookup a link by it's name
+ */
+int
+cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
+{
+ FCB * fcb;
+ BTreeIterator * iterator;
+ struct FSBufferDescriptor btdata;
+ struct HFSPlusCatalogFile file;
+ int result;
+
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+
+ /* Create an iterator for use by us temporarily */
+ MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ bzero(iterator, sizeof(*iterator));
+
+ if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
+ goto exit;
+ }
+ BDINIT(btdata, &file);
+
+ if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
+ goto exit;
+ }
+ if (file.recordType != kHFSPlusFileRecord) {
+ result = ENOENT;
+ goto exit;
+ }
+ *linkfileid = file.fileID;
+
+ if (file.flags & kHFSHasLinkChainMask) {
+ *prevlinkid = file.hl_prevLinkID;
+ *nextlinkid = file.hl_nextLinkID;
+ } else {
+ *prevlinkid = 0;
+ *nextlinkid = 0;
+ }
+exit:
+ FREE(iterator, M_TEMP);
+ return MacToVFSError(result);
+}
+
+
+/*
+ * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
+ */
+int
+cat_lookup_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
+{
+ FCB * fcb;
+ BTreeIterator * iterator;
+ struct FSBufferDescriptor btdata;
+ struct HFSPlusCatalogFile file;
+ int result;
+
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+
+ /* Create an iterator for use by us temporarily */
+ MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ bzero(iterator, sizeof(*iterator));
+
+
+ if ((result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key))) {
+ printf("hfs: cat_lookup_siblinglinks: getkey for %d failed %d\n", linkfileid, result);
+ goto exit;
+ }
+ BDINIT(btdata, &file);
+
+ if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
+ printf("hfs: cat_lookup_siblinglinks: cannot find %d\n", linkfileid);
+ goto exit;
+ }
+ /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
+ if (file.flags & kHFSHasLinkChainMask) {
+ cnid_t parent;
+
+ parent = ((HFSPlusCatalogKey *)&iterator->key)->parentID;
+
+ /* directory inodes don't have a chain (its in an EA) */
+ if (parent == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
+ result = ENOLINK; /* signal to caller to get head of list */
+ } else if (parent == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
+ *prevlinkid = 0;
+ *nextlinkid = file.hl_firstLinkID;
+ } else {
+ *prevlinkid = file.hl_prevLinkID;
+ *nextlinkid = file.hl_nextLinkID;
+ }
+ } else {
+ *prevlinkid = 0;
+ *nextlinkid = 0;
+ }
+exit:
+ FREE(iterator, M_TEMP);
+ return MacToVFSError(result);
+}
+
+
+/*
+ * cat_createlink - create a link in the catalog
+ *
+ * The following cat_attr fields are expected to be set:
+ * ca_linkref
+ * ca_itime
+ * ca_mode (S_IFREG)
+ * ca_recflags
+ * ca_flags
+ * ca_finderinfo (type and creator)
+ */
+int
+cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
+ cnid_t nextlinkid, cnid_t *linkfileid)
+{
+ FCB * fcb;
+ struct btobj * bto;
+ FSBufferDescriptor btdata;
+ HFSPlusForkData *rsrcforkp;
+ u_int32_t nextCNID;
+ u_int32_t datalen;
+ u_int32_t encoding;
+ int thread_inserted = 0;
+ int alias_allocated = 0;
+ int result = 0;
+
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+
+ /*
+ * Get the next CNID. We can change it since we hold the catalog lock.
+ */
+ nextCNID = hfsmp->vcbNxtCNID;
+ if (nextCNID == 0xFFFFFFFF) {
+ HFS_MOUNT_LOCK(hfsmp, TRUE)
+ hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
+ hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
+ HFS_MOUNT_UNLOCK(hfsmp, TRUE);
+ } else {
+ hfsmp->vcbNxtCNID++;
+ }
+ MarkVCBDirty(hfsmp);
+
+ /* Get space for iterator, key and data */
+ MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
+ bto->iterator.hint.nodeNum = 0;
+ rsrcforkp = &bto->data.hfsPlusFile.resourceFork;
+
+ result = buildkey(hfsmp, descp, &bto->key, 0);
+ if (result) {
+ printf("hfs: cat_createlink: err %d from buildkey\n", result);
+ goto exit;
+ }
+
+ /* This is our only chance to set the encoding (other than a rename). */
+ encoding = hfs_pickencoding(bto->key.nodeName.unicode, bto->key.nodeName.length);
+
+ /* Insert the thread record first. */
+ datalen = buildthread((void*)&bto->key, &bto->data, 0, 0);
+ btdata.bufferAddress = &bto->data;
+ btdata.itemSize = datalen;
+ btdata.itemCount = 1;
+
+ for (;;) {
+ buildthreadkey(nextCNID, 0, (CatalogKey *) &bto->iterator.key);
+
+ result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
+ if ((result == btExists) && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
+ /*
+ * Allow CNIDs on HFS Plus volumes to wrap around
+ */
+ if (++nextCNID < kHFSFirstUserCatalogNodeID) {
+ nextCNID = kHFSFirstUserCatalogNodeID;
+ }
+ continue;
+ }
+ if (result == 0) {
+ thread_inserted = 1;
+ }
+ break;
+ }
+ if (result)
+ goto exit;
+
+ /*
+ * CNID is now established. If we have wrapped then
+ * update the vcbNxtCNID.
+ */
+ if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
+ hfsmp->vcbNxtCNID = nextCNID + 1;
+ if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
+ hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
+ }
+ }
+
+ /*
+ * Now insert the link record.
+ */
+ buildrecord(attrp, nextCNID, 0, encoding, &bto->data, &datalen);
+
+ bto->data.hfsPlusFile.hl_prevLinkID = 0;
+ bto->data.hfsPlusFile.hl_nextLinkID = nextlinkid;
+ bto->data.hfsPlusFile.hl_linkReference = attrp->ca_linkref;
+
+ /* For directory hard links, create alias in resource fork */
+ if (descp->cd_flags & CD_ISDIR) {
+ if ((result = cat_makealias(hfsmp, attrp->ca_linkref, &bto->data.hfsPlusFile))) {
+ goto exit;
+ }
+ alias_allocated = 1;
+ }
+ btdata.bufferAddress = &bto->data;
+ btdata.itemSize = datalen;
+ btdata.itemCount = 1;
+
+ bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key));
+
+ result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
+ if (result) {
+ if (result == btExists)
+ result = EEXIST;
+ goto exit;
+ }
+ if (linkfileid != NULL) {
+ *linkfileid = nextCNID;
+ }
+exit:
+ if (result) {
+ if (thread_inserted) {
+ printf("hfs: cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result));
+
+ buildthreadkey(nextCNID, 0, (CatalogKey *)&bto->iterator.key);
+ if (BTDeleteRecord(fcb, &bto->iterator)) {
+ printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp->vcbVN);
+ hfs_mark_volume_inconsistent(hfsmp);
+ }
+ }
+ if (alias_allocated && rsrcforkp->extents[0].startBlock != 0) {
+ (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock,
+ rsrcforkp->extents[0].blockCount, 0);
+ rsrcforkp->extents[0].startBlock = 0;
+ rsrcforkp->extents[0].blockCount = 0;
+ }
+ }
+ (void) BTFlushPath(fcb);
+ FREE(bto, M_TEMP);
+
+ return MacToVFSError(result);
+}
+
+/* Directory hard links are visible as aliases on pre-Leopard systems and
+ * as normal directories on Leopard or later. All directory hard link aliases
+ * have the same resource fork content except for the three uniquely
+ * identifying values that are updated in the resource fork data when the alias
+ * is created. The following array is the constant resource fork data used
+ * only for creating directory hard link aliases.
+ */
+static const char hfs_dirlink_alias_rsrc[] = {
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
+ 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* Constants for directory hard link alias */
+enum {
+ /* Size of resource fork data array for directory hard link alias */
+ kHFSAliasSize = 0x1d0,
+
+ /* Volume type for ejectable devices like disk image */
+ kHFSAliasVolTypeEjectable = 0x5,
+
+ /* Offset for volume create date, in Mac OS local time */
+ kHFSAliasVolCreateDateOffset = 0x12a,
+
+ /* Offset for the type of volume */
+ kHFSAliasVolTypeOffset = 0x130,
+
+ /* Offset for folder ID of the parent directory of the directory inode */
+ kHFSAliasParentIDOffset = 0x132,
+
+ /* Offset for folder ID of the directory inode */
+ kHFSAliasTargetIDOffset = 0x176,
+};
+
+/* Create and write an alias that points at the directory represented by given
+ * inode number on the same volume. Directory hard links are visible as
+ * aliases in pre-Leopard systems and this function creates these aliases.
+ *
+ * Note: This code is very specific to creating alias for the purpose
+ * of directory hard links only, and should not be generalized.
+ */
+static int
+cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp)
+{
+ struct buf *bp;
+ daddr64_t blkno;
+ u_int32_t blkcount;
+ int blksize;
+ int sectorsize;
+ int result;
+ HFSPlusForkData *rsrcforkp;
+ char *alias;
+ uint32_t *valptr;
+
+ rsrcforkp = &(crp->resourceFork);
+
+ blksize = hfsmp->blockSize;
+ blkcount = howmany(kHFSAliasSize, blksize);
+ sectorsize = hfsmp->hfs_logical_block_size;
+ bzero(rsrcforkp, sizeof(HFSPlusForkData));
+
+ /* Allocate some disk space for the alias content. */
+ result = BlockAllocate(hfsmp, 0, blkcount, blkcount,
+ HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE,
+ &rsrcforkp->extents[0].startBlock,
+ &rsrcforkp->extents[0].blockCount);
+ if (result) {
+ rsrcforkp->extents[0].startBlock = 0;
+ goto exit;
+ }
+
+ /* Acquire a buffer cache block for our block. */
+ blkno = ((u_int64_t)rsrcforkp->extents[0].startBlock * (u_int64_t)blksize) / sectorsize;
+ blkno += hfsmp->hfsPlusIOPosOffset / sectorsize;
+
+ bp = buf_getblk(hfsmp->hfs_devvp, blkno, roundup(kHFSAliasSize, hfsmp->hfs_logical_block_size), 0, 0, BLK_META);
+ if (hfsmp->jnl) {
+ journal_modify_block_start(hfsmp->jnl, bp);
+ }
+
+ /* Generate alias content */
+ alias = (char *)buf_dataptr(bp);
+ bzero(alias, buf_size(bp));
+ bcopy(hfs_dirlink_alias_rsrc, alias, kHFSAliasSize);
+
+ /* Set the volume create date, local time in Mac OS format */
+ valptr = (uint32_t *)(alias + kHFSAliasVolCreateDateOffset);
+ *valptr = OSSwapHostToBigInt32(hfsmp->localCreateDate);
+
+ /* If the file system is on a virtual device like disk image,
+ * update the volume type to be ejectable device.
+ */
+ if (hfsmp->hfs_flags & HFS_VIRTUAL_DEVICE) {
+ *(uint16_t *)(alias + kHFSAliasVolTypeOffset) =
+ OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable);
+ }
+
+ /* Set id of the parent of the target directory */
+ valptr = (uint32_t *)(alias + kHFSAliasParentIDOffset);
+ *valptr = OSSwapHostToBigInt32(hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid);
+
+ /* Set id of the target directory */
+ valptr = (uint32_t *)(alias + kHFSAliasTargetIDOffset);
+ *valptr = OSSwapHostToBigInt32(inode_num);
+
+ /* Write alias content to disk. */
+ if (hfsmp->jnl) {
+ journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL);
+ } else if ((result = buf_bwrite(bp))) {
+ goto exit;
+ }
+
+ /* Finish initializing the fork data. */
+ rsrcforkp->logicalSize = kHFSAliasSize;
+ rsrcforkp->totalBlocks = rsrcforkp->extents[0].blockCount;
+
+exit:
+ if (result && rsrcforkp->extents[0].startBlock != 0) {
+ (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock, rsrcforkp->extents[0].blockCount, 0);
+ rsrcforkp->extents[0].startBlock = 0;
+ rsrcforkp->extents[0].blockCount = 0;
+ rsrcforkp->logicalSize = 0;
+ rsrcforkp->totalBlocks = 0;
+ }
+ return (result);
+}
+
+/*
+ * cat_deletelink - delete a link from the catalog
+ */
+int
+cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp)
+{
+ struct HFSPlusCatalogFile file;
+ struct cat_attr cattr;
+ uint32_t totalBlocks;
+ int i;
+ int result;
+
+ bzero(&file, sizeof (file));
+ bzero(&cattr, sizeof (cattr));
+ cattr.ca_fileid = descp->cd_cnid;
+
+ /* Directory links have alias content to remove. */
+ if (descp->cd_flags & CD_ISDIR) {
+ FCB * fcb;
+ BTreeIterator * iterator;
+ struct FSBufferDescriptor btdata;
+
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+
+ /* Borrow the btcb iterator since we have an exclusive catalog lock. */
+ iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
+ iterator->hint.nodeNum = 0;
+
+ if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
+ goto exit;
+ }
+ BDINIT(btdata, &file);
+
+ if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
+ goto exit;
+ }
+ }
+
+ result = cat_delete(hfsmp, descp, &cattr);
+
+ if ((result == 0) &&
+ (descp->cd_flags & CD_ISDIR) &&
+ (file.recordType == kHFSPlusFileRecord)) {
+
+ totalBlocks = file.resourceFork.totalBlocks;
+
+ for (i = 0; (i < 8) && (totalBlocks > 0); i++) {
+ if ((file.resourceFork.extents[i].blockCount == 0) &&
+ (file.resourceFork.extents[i].startBlock == 0)) {
+ break;
+ }
+
+ (void) BlockDeallocate(hfsmp,
+ file.resourceFork.extents[i].startBlock,
+ file.resourceFork.extents[i].blockCount, 0);
+
+ totalBlocks -= file.resourceFork.extents[i].blockCount;
+ file.resourceFork.extents[i].startBlock = 0;
+ file.resourceFork.extents[i].blockCount = 0;
+ }
+ }
+exit:
+ return (result);
+}
+
+
+/*
+ * Callback to collect directory entries.
+ * Called with readattr_state for each item in a directory.
+ */
+struct readattr_state {
+ struct hfsmount *hfsmp;
+ struct cat_entrylist *list;
+ cnid_t dir_cnid;
+ int stdhfs;
+ int error;
+};
+
+static int
+getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec,
+ struct readattr_state *state)
+{
+ struct cat_entrylist *list = state->list;
+ struct hfsmount *hfsmp = state->hfsmp;
+ struct cat_entry *cep;
+ cnid_t parentcnid;
+
+ if (list->realentries >= list->maxentries)
+ return (0); /* stop */
+
+ parentcnid = state->stdhfs ? key->hfs.parentID : key->hfsPlus.parentID;
+
+ switch(rec->recordType) {
+ case kHFSPlusFolderRecord:
+ case kHFSPlusFileRecord:
+ case kHFSFolderRecord:
+ case kHFSFileRecord:
+ if (parentcnid != state->dir_cnid) {
+ state->error = ENOENT;
+ return (0); /* stop */
+ }
+ break;
+ default:
+ state->error = ENOENT;
+ return (0); /* stop */
+ }
+
+ /* Hide the private system directories and journal files */
+ if (parentcnid == kHFSRootFolderID) {
+ if (rec->recordType == kHFSPlusFolderRecord) {
+ if (rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
+ rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
+ list->skipentries++;
+ return (1); /* continue */
+ }
+ }
+ if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
+ (rec->recordType == kHFSPlusFileRecord) &&
+ ((rec->hfsPlusFile.fileID == hfsmp->hfs_jnlfileid) ||
+ (rec->hfsPlusFile.fileID == hfsmp->hfs_jnlinfoblkid))) {
+ list->skipentries++;
+ return (1); /* continue */
+ }
+ }
+
+ cep = &list->entry[list->realentries++];
+
+ if (state->stdhfs) {
+ struct HFSPlusCatalogFile cnoderec;
+ HFSPlusCatalogKey * pluskey;
+ u_int32_t encoding;
+
+ promoteattr(hfsmp, rec, &cnoderec);
+ getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);