/*
- * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
-static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files, u_long hint, int wantrsrc,
+#define HFS_LOOKUP_SYSFILE 0x1 /* If set, allow lookup of system files */
+#define HFS_LOOKUP_HARDLINK 0x2 /* If set, allow lookup of hard link records and not resolve the hard links */
+static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t hint, int wantrsrc,
struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid);
-static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
+int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp);
/* Internal catalog support routines */
static int cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
struct position_state *state);
-static int resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino);
+static int resolvelinkid(struct hfsmount *hfsmp, u_int32_t linkref, ino_t *ino);
static int getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key);
static int catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state);
-static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
+static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_int32_t hint, u_int32_t encoding,
int isdir, struct cat_desc *descp);
static void getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp);
-static void promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey, HFSPlusCatalogKey *keyp, u_long *encoding);
+static void promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey, HFSPlusCatalogKey *keyp, u_int32_t *encoding);
static void promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *file, int resource, struct cat_fork * forkp);
static void promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp);
static cnid_t getcnid(const CatalogRecord *crp);
-static u_long getencoding(const CatalogRecord *crp);
+static u_int32_t getencoding(const CatalogRecord *crp);
static cnid_t getparentcnid(const CatalogRecord *recp);
static int isadir(const CatalogRecord *crp);
static int cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp);
+static int cat_update_internal(struct hfsmount *hfsmp, int update_hardlink, struct cat_desc *descp, struct cat_attr *attrp,
+ struct cat_fork *dataforkp, struct cat_fork *rsrcforkp);
-__private_extern__
int
cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, __unused proc_t p)
{
return MacToVFSError(result);
}
-__private_extern__
void
cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, __unused proc_t p)
{
hfs_systemfile_unlock(hfsmp, lockflags);
}
-
-__private_extern__
+__private_extern__
void
cat_convertattr(
struct hfsmount *hfsmp,
} else {
/* Convert the data fork. */
datafp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
+ datafp->cf_new_size = 0;
datafp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
if ((hfsmp->hfc_stage == HFC_RECORDING) &&
(attrp->ca_atime >= hfsmp->hfc_timebase)) {
/* Convert the resource fork. */
rsrcfp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
+ rsrcfp->cf_new_size = 0;
rsrcfp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
if ((hfsmp->hfc_stage == HFC_RECORDING) &&
(attrp->ca_atime >= hfsmp->hfc_timebase)) {
{
int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
HFSPlusCatalogKey * pluskey = NULL;
- u_long encoding;
+ u_int32_t encoding;
if (std_hfs) {
MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
* Note: The caller is responsible for releasing the output
* catalog descriptor (when supplied outdescp is non-null).
*/
-__private_extern__
int
cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
struct cat_desc *outdescp, struct cat_attr *attrp,
return (result);
}
-__private_extern__
int
cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
{
* catalog descriptor (when supplied outdescp is non-null).
*/
-__private_extern__
int
cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
{
}
if (std_hfs) {
HFSPlusCatalogKey * pluskey = NULL;
- u_long encoding;
+ u_int32_t encoding;
MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
promotekey(hfsmp, &keyp->hfs, pluskey, &encoding);
* Note: The caller is responsible for releasing the output
* catalog descriptor (when supplied outdescp is non-null).
*/
-__private_extern__
int
cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files,
struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
case kHFSFileThreadRecord:
case kHFSFolderThreadRecord:
keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
+
+ /* check for NULL name */
+ if (keyp->hfs.nodeName[0] == 0) {
+ result = ENOENT;
+ goto exit;
+ }
+
keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
break;
case kHFSPlusFileThreadRecord:
case kHFSPlusFolderThreadRecord:
keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
+
+ /* check for NULL name */
+ if (keyp->hfsPlus.nodeName.length == 0) {
+ result = ENOENT;
+ goto exit;
+ }
+
keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
(keyp->hfsPlus.nodeName.length * 2);
break;
goto exit;
}
- result = cat_lookupbykey(hfsmp, keyp, allow_system_files, 0, 0, outdescp, attrp, forkp, NULL);
+ result = cat_lookupbykey(hfsmp, keyp,
+ ((allow_system_files != 0) ? HFS_LOOKUP_SYSFILE : 0),
+ 0, 0, outdescp, attrp, forkp, NULL);
/* No corresponding file/folder record found for a thread record,
* mark the volume inconsistent.
*/
* the key in the thread matches the key in the record.
*/
if (cnid != dcnid) {
- printf("Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid, cnid, dcnid, dcnid);
+ printf("hfs: cat_idlookup: Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid, cnid, dcnid, dcnid);
result = ENOENT;
}
}
/*
* cat_lookupmangled - lookup a catalog node using a mangled name
*/
-static int
+int
cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
{
cnid_t fileID;
u_int32_t prefixlen;
int result;
+ int extlen1, extlen2;
if (wantrsrc)
return (ENOENT);
bcmp(outdescp->cd_nameptr, descp->cd_nameptr, prefixlen-6) != 0)
goto falsematch;
+ extlen1 = CountFilenameExtensionChars(descp->cd_nameptr, descp->cd_namelen);
+ extlen2 = CountFilenameExtensionChars(outdescp->cd_nameptr, outdescp->cd_namelen);
+ if (extlen1 != extlen2)
+ goto falsematch;
+
+ if (bcmp(outdescp->cd_nameptr + (outdescp->cd_namelen - extlen2),
+ descp->cd_nameptr + (descp->cd_namelen - extlen1),
+ extlen1) != 0)
+ goto falsematch;
+
return (0);
falsematch:
* cat_lookupbykey - lookup a catalog node using a cnode key
*/
static int
-cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files, u_long hint, int wantrsrc,
+cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t hint, int wantrsrc,
struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid)
{
struct BTreeIterator * iterator;
u_int16_t datasize;
int result;
int std_hfs;
- u_long ilink = 0;
+ u_int32_t ilink = 0;
cnid_t cnid = 0;
- u_long encoding = 0;
+ u_int32_t encoding = 0;
+ cnid_t parentid = 0;
std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
if (result)
goto exit;
- /* Save the cnid and encoding now in case there's a hard link */
+ /* Save the cnid, parentid, and encoding now in case there's a hard link or inode */
cnid = getcnid(recp);
+ if (!std_hfs) {
+ parentid = keyp->hfsPlus.parentID;
+ }
encoding = getencoding(recp);
hint = iterator->hint.nodeNum;
/* Hide the journal files (if any) */
if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
((cnid == hfsmp->hfs_jnlfileid) || (cnid == hfsmp->hfs_jnlinfoblkid)) &&
- !allow_system_files) {
-
+ !(flags & HFS_LOOKUP_SYSFILE)) {
result = ENOENT;
goto exit;
}
if (!std_hfs
&& (attrp || forkp)
&& (recp->recordType == kHFSPlusFileRecord)
- && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->vcbCrDate) ||
+ && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_itime) ||
(to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_metadata_createdate))) {
int isdirlink = 0;
int isfilelink = 0;
(SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
isdirlink = 1;
}
- if (isfilelink || isdirlink) {
+ if ((isfilelink || isdirlink) && !(flags & HFS_LOOKUP_HARDLINK)) {
ilink = recp->hfsPlusFile.hl_linkReference;
(void) cat_resolvelink(hfsmp, ilink, isdirlink, (struct HFSPlusCatalogFile *)recp);
}
getbsdattr(hfsmp, &cnoderec, attrp);
} else {
getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
- if (ilink)
+ if (ilink) {
+ /* Update the inode number for this hard link */
attrp->ca_linkref = ilink;
+ }
+
+ /*
+ * Set kHFSHasLinkChainBit for hard links, and reset it for all
+ * other items. Also set linkCount to 1 for regular files.
+ *
+ * Due to some bug (rdar://8505977), some regular files can have
+ * kHFSHasLinkChainBit set and linkCount more than 1 even if they
+ * are not really hard links. The runtime code should not consider
+ * these files has hard links. Therefore we reset the kHFSHasLinkChainBit
+ * and linkCount for regular file before we vend it out. This might
+ * also result in repairing the bad files on disk, if the corresponding
+ * file is modified and updated on disk.
+ */
+ if (ilink) {
+ /* This is a hard link and the link count bit was not set */
+ if (!(attrp->ca_recflags & kHFSHasLinkChainMask)) {
+ printf ("hfs: set hardlink bit on vol=%s cnid=%u inoid=%u\n", hfsmp->vcbVN, cnid, ilink);
+ attrp->ca_recflags |= kHFSHasLinkChainMask;
+ }
+ } else {
+ /* Make sure that this non-hard link (regular) record is not
+ * an inode record or a valid hard link being that is not
+ * resolved for volume resize purposes. We do not want to
+ * reset the hard link bit or reset link count on these records.
+ */
+ if (!(flags & HFS_LOOKUP_HARDLINK) &&
+ (parentid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
+ (parentid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid)) {
+ /* This is not a hard link or inode and the link count bit was set */
+ if (attrp->ca_recflags & kHFSHasLinkChainMask) {
+ printf ("hfs: clear hardlink bit on vol=%s cnid=%u\n", hfsmp->vcbVN, cnid);
+ attrp->ca_recflags &= ~kHFSHasLinkChainMask;
+ }
+ /* This is a regular file and the link count was more than 1 */
+ if (S_ISREG(attrp->ca_mode) && (attrp->ca_linkcount > 1)) {
+ printf ("hfs: set linkcount=1 on vol=%s cnid=%u old=%u\n", hfsmp->vcbVN, cnid, attrp->ca_linkcount);
+ attrp->ca_linkcount = 1;
+ }
+ }
+ }
}
}
if (forkp != NULL) {
} else if (wantrsrc) {
/* Convert the resource fork. */
forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
+ forkp->cf_new_size = 0;
forkp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
if ((hfsmp->hfc_stage == HFC_RECORDING) &&
(to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
/* Convert the data fork. */
forkp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
+ forkp->cf_new_size = 0;
forkp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
if ((hfsmp->hfc_stage == HFC_RECORDING) &&
(to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
if ((validblks < forkp->cf_blocks) && (forkp->cf_extents[7].blockCount == 0)) {
off_t psize;
+ /*
+ * This is technically a volume corruption.
+ * If the total number of blocks calculated by iterating + summing
+ * the extents in the resident extent records, is less than that
+ * which is reported in the catalog entry, we should force a fsck.
+ * Only modifying ca_blocks here is not guaranteed to make it out
+ * to disk; it is a runtime-only field.
+ *
+ * Note that we could have gotten into this state if we had invalid ranges
+ * that existed in borrowed blocks that somehow made it out to disk.
+ * The cnode's on disk block count should never be greater
+ * than that which is in its extent records.
+ */
+
+ (void) hfs_mark_volume_inconsistent (hfsmp);
+
forkp->cf_blocks = validblks;
if (attrp != NULL) {
attrp->ca_blocks = validblks + recp->hfsPlusFile.resourceFork.totalBlocks;
* The caller is responsible for releasing the output
* catalog descriptor (when supplied outdescp is non-null).
*/
-__private_extern__
int
cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
struct cat_desc *out_descp)
u_int32_t datalen;
int std_hfs;
int result = 0;
- u_long encoding = kTextEncodingMacRoman;
+ u_int32_t encoding = kTextEncodingMacRoman;
int modeformat;
modeformat = attrp->ca_mode & S_IFMT;
* Note: The caller is responsible for releasing the output
* catalog descriptor (when supplied out_cdp is non-null).
*/
-__private_extern__
int
cat_rename (
struct hfsmount * hfsmp,
int directory = from_cdp->cd_flags & CD_ISDIR;
int is_dirlink = 0;
int std_hfs;
- u_long encoding = 0;
+ u_int32_t encoding = 0;
vcb = HFSTOVCB(hfsmp);
fcb = GetFileControlBlock(vcb->catalogRefNum);
int err;
err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
if (err) {
- printf("cat_create: could not undo (BTInsert = %d)", err);
+ printf("hfs: cat_create: could not undo (BTInsert = %d)", err);
hfs_mark_volume_inconsistent(hfsmp);
result = err;
goto exit;
int err;
err = BTDeleteRecord(fcb, to_iterator);
if (err) {
- printf("cat_create: could not undo (BTDelete = %d)", err);
+ printf("hfs: cat_create: could not undo (BTDelete = %d)", err);
hfs_mark_volume_inconsistent(hfsmp);
result = err;
goto exit;
/* Save the real encoding hint in the Finder Info (field 4). */
if (directory && from_cdp->cd_cnid == kHFSRootFolderID) {
- u_long realhint;
+ u_int32_t realhint;
realhint = hfs_pickencoding(pluskey->nodeName.unicode, pluskey->nodeName.length);
vcb->vcbFndrInfo[4] = SET_HFS_TEXT_ENCODING(realhint);
* 2. BTDeleteRecord(thread);
* 3. BTUpdateRecord(parent);
*/
-__private_extern__
int
cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
{
/*
- * cnode_update - update the catalog node described by descp
- * using the data from attrp and forkp.
+ * cat_update_internal - update the catalog node described by descp
+ * using the data from attrp and forkp.
+ * If update_hardlink is true, the hard link catalog record is updated
+ * and not the inode catalog record.
*/
-__private_extern__
-int
-cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
+static int
+cat_update_internal(struct hfsmount *hfsmp, int update_hardlink, struct cat_desc *descp, struct cat_attr *attrp,
struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
{
FCB * fcb;
* For open-deleted files we need to do a lookup by cnid
* (using thread rec).
*
- * For hard links, the target of the update is the inode
- * itself (not the link record) so a lookup by fileid
- * (i.e. thread rec) is needed.
+ * For hard links and if not requested by caller, the target
+ * of the update is the inode itself (not the link record)
+ * so a lookup by fileid (i.e. thread rec) is needed.
*/
- if ((descp->cd_cnid != attrp->ca_fileid) ||
- (descp->cd_namelen == 0) ||
- (attrp->ca_recflags & kHFSHasLinkChainMask)) {
+ if ((update_hardlink == false) &&
+ ((descp->cd_cnid != attrp->ca_fileid) ||
+ (descp->cd_namelen == 0) ||
+ (attrp->ca_recflags & kHFSHasLinkChainMask))) {
result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
} else {
result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
return MacToVFSError(result);
}
+/*
+ * cat_update - update the catalog node described by descp
+ * using the data from attrp and forkp.
+ */
+int
+cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
+ struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
+{
+ return cat_update_internal(hfsmp, false, descp, attrp, dataforkp, rsrcforkp);
+}
+
/*
* catrec_update - Update the fields of a catalog record
* This is called from within BTUpdateRecord.
dir = (struct HFSPlusCatalogFolder *)crp;
/* Do a quick sanity check */
if (dir->folderID != attrp->ca_fileid) {
- printf("catrec_update: id %d != %d\n", dir->folderID, attrp->ca_fileid);
+ printf("hfs: catrec_update: id %d != %d\n", dir->folderID, attrp->ca_fileid);
return (btNotFound);
}
dir->flags = attrp->ca_recflags;
}
case kHFSPlusFileRecord: {
HFSPlusCatalogFile *file;
+ int is_dirlink;
file = (struct HFSPlusCatalogFile *)crp;
/* Do a quick sanity check */
* supplied values (which will be default), which has the
* same effect as creating a new file while
* MNT_UNKNOWNPERMISSIONS is set.
+ *
+ * Do not modify bsdInfo for directory hard link records.
+ * They are set during creation and are not modifiable, so just
+ * leave them alone.
*/
- if ((file->bsdInfo.fileMode != 0) ||
- (attrp->ca_flags != 0) ||
- (attrp->ca_uid != hfsmp->hfs_uid) ||
- (attrp->ca_gid != hfsmp->hfs_gid) ||
- ((attrp->ca_mode & ALLPERMS) !=
- (hfsmp->hfs_file_mask & ACCESSPERMS))) {
+ is_dirlink = (file->flags & kHFSHasLinkChainMask) &&
+ (SWAP_BE32(file->userInfo.fdType) == kHFSAliasType) &&
+ (SWAP_BE32(file->userInfo.fdCreator) == kHFSAliasCreator);
+
+ if (!is_dirlink &&
+ ((file->bsdInfo.fileMode != 0) ||
+ (attrp->ca_flags != 0) ||
+ (attrp->ca_uid != hfsmp->hfs_uid) ||
+ (attrp->ca_gid != hfsmp->hfs_gid) ||
+ ((attrp->ca_mode & ALLPERMS) !=
+ (hfsmp->hfs_file_mask & ACCESSPERMS)))) {
if ((file->bsdInfo.fileMode == 0) ||
(((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
file->bsdInfo.ownerID = attrp->ca_uid;
if ((file->resourceFork.extents[0].startBlock != 0) &&
(file->resourceFork.extents[0].startBlock ==
file->dataFork.extents[0].startBlock)) {
- panic("catrec_update: rsrc fork == data fork");
+ panic("hfs: catrec_update: rsrc fork == data fork");
}
/* Synchronize the lock state */
/* Push out special field if necessary */
if (S_ISBLK(attrp->ca_mode) || S_ISCHR(attrp->ca_mode)) {
file->bsdInfo.special.rawDevice = attrp->ca_rdev;
- } else if (descp->cd_cnid != attrp->ca_fileid || attrp->ca_linkcount == 2) {
- file->hl_linkCount = attrp->ca_linkcount;
+ }
+ else {
+ /*
+ * Protect against the degenerate case where the descriptor contains the
+ * raw inode ID in its CNID field. If the HFSPlusCatalogFile record indicates
+ * the linkcount was greater than 1 (the default value), then it must have become
+ * a hardlink. In this case, update the linkcount from the cat_attr passed in.
+ */
+ if ((descp->cd_cnid != attrp->ca_fileid) || (attrp->ca_linkcount > 1 ) ||
+ (file->hl_linkCount > 1)) {
+ file->hl_linkCount = attrp->ca_linkcount;
+ }
}
break;
}
/* Update the bit in corresponding cnode, if any, in the hash.
* If the cnode has the bit already set, stop the traversal.
*/
- retval = hfs_chash_set_childlinkbit(hfsmp->hfs_raw_dev, cnid);
+ retval = hfs_chash_set_childlinkbit(hfsmp, cnid);
if (retval == 0) {
break;
}
break;
}
if ((result = getkey(hfsmp, cnid, (CatalogKey *)keyp))) {
- printf("cat_check_link_ancestry: getkey for %u failed\n", cnid);
+ 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("cat_check_link_ancestry: cannot find %u\n", cnid);
+ printf("hfs: cat_check_link_ancestry: cannot find %u\n", cnid);
invalid = 1; /* On errors, assume an invalid parent */
break;
}
/*
- * updatelink_callback - update a link's chain
+ * update_siblinglinks_callback - update a link's chain
*/
struct linkupdate_state {
};
static int
-updatelink_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state)
+update_siblinglinks_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state)
{
HFSPlusCatalogFile *file;
if (crp->recordType != kHFSPlusFileRecord) {
- printf("updatelink_callback: unexpected rec type %d\n", crp->recordType);
+ printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp->recordType);
return (btNotFound);
}
file->hl_nextLinkID = state->nextlinkid;
}
} else {
- printf("updatelink_callback: file %d isn't a chain\n", file->fileID);
+ printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file->fileID);
}
return (0);
}
/*
- * cat_updatelink - update a link's chain
+ * cat_update_siblinglinks - update a link's chain
*/
-__private_extern__
int
-cat_updatelink(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cnid_t nextlinkid)
+cat_update_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cnid_t nextlinkid)
{
FCB * fcb;
BTreeIterator * iterator;
state.prevlinkid = prevlinkid;
state.nextlinkid = nextlinkid;
- /* Borrow the btcb iterator since we have an exclusive catalog lock. */
- iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
- iterator->hint.nodeNum = 0;
+ /* 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)updatelink_callback, &state);
+ result = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr)update_siblinglinks_callback, &state);
(void) BTFlushPath(fcb);
} else {
- printf("cat_updatelink: couldn't resolve cnid %d\n", linkfileid);
+ 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
*/
-__private_extern__
int
cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
{
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;
+ /* 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;
*nextlinkid = 0;
}
exit:
+ FREE(iterator, M_TEMP);
return MacToVFSError(result);
}
/*
- * cat_lookuplink - lookup a link by its cnid
+ * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
*/
-__private_extern__
int
-cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
+cat_lookup_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
{
FCB * fcb;
BTreeIterator * iterator;
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;
+ /* 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("cat_lookuplinkbyid: getkey for %d failed %d\n", linkfileid, result);
+ 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("cat_lookuplinkbyid: cannot find %d\n", linkfileid);
+ printf("hfs: cat_lookup_siblinglinks: cannot find %d\n", linkfileid);
goto exit;
}
/* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
parent = ((HFSPlusCatalogKey *)&iterator->key)->parentID;
- /* ADL inodes don't have a chain (its in an EA) */
+ /* 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) {
*nextlinkid = 0;
}
exit:
+ FREE(iterator, M_TEMP);
return MacToVFSError(result);
}
* ca_flags
* ca_finderinfo (type and creator)
*/
-__private_extern__
int
cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
cnid_t nextlinkid, cnid_t *linkfileid)
HFSPlusForkData *rsrcforkp;
u_int32_t nextCNID;
u_int32_t datalen;
- u_long encoding;
+ u_int32_t encoding;
int thread_inserted = 0;
int alias_allocated = 0;
int result = 0;
result = buildkey(hfsmp, descp, &bto->key, 0);
if (result) {
- printf("cat_createlink: err %d from buildkey\n", result);
+ printf("hfs: cat_createlink: err %d from buildkey\n", result);
goto exit;
}
exit:
if (result) {
if (thread_inserted) {
- printf("cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result));
+ printf("hfs: cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result));
buildthreadkey(nextCNID, 0, (CatalogKey *)&bto->iterator.key);
if (BTDeleteRecord(fcb, &bto->iterator)) {
}
if (alias_allocated && rsrcforkp->extents[0].startBlock != 0) {
(void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock,
- rsrcforkp->extents[0].blockCount);
+ rsrcforkp->extents[0].blockCount, 0);
rsrcforkp->extents[0].startBlock = 0;
rsrcforkp->extents[0].blockCount = 0;
}
blksize = hfsmp->blockSize;
blkcount = howmany(kHFSAliasSize, blksize);
- sectorsize = hfsmp->hfs_phys_block_size;
+ sectorsize = hfsmp->hfs_logical_block_size;
bzero(rsrcforkp, sizeof(HFSPlusForkData));
/* Allocate some disk space for the alias content. */
- result = BlockAllocate(hfsmp, 0, blkcount, blkcount, 1, 1,
+ result = BlockAllocate(hfsmp, 0, blkcount, blkcount,
+ HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE,
&rsrcforkp->extents[0].startBlock,
&rsrcforkp->extents[0].blockCount);
if (result) {
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_phys_block_size), 0, 0, BLK_META);
+ 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);
}
exit:
if (result && rsrcforkp->extents[0].startBlock != 0) {
- (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock, rsrcforkp->extents[0].blockCount);
+ (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;
/*
* cat_deletelink - delete a link from the catalog
*/
-__private_extern__
int
cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp)
{
(void) BlockDeallocate(hfsmp,
file.resourceFork.extents[i].startBlock,
- file.resourceFork.extents[i].blockCount);
+ file.resourceFork.extents[i].blockCount, 0);
totalBlocks -= file.resourceFork.extents[i].blockCount;
file.resourceFork.extents[i].startBlock = 0;
if (state->stdhfs) {
struct HFSPlusCatalogFile cnoderec;
HFSPlusCatalogKey * pluskey;
- u_long encoding;
+ u_int32_t encoding;
promoteattr(hfsmp, rec, &cnoderec);
getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
*
* Note: index is zero relative
*/
-__private_extern__
int
cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list)
{
/* Hard link information collected during cat_getdirentries. */
struct linkinfo {
- u_long link_ref;
+ u_int32_t link_ref;
user_addr_t dirent_addr;
};
typedef struct linkinfo linkinfo_t;
int hide = 0;
u_int8_t type = DT_UNKNOWN;
u_int8_t is_mangled = 0;
+ u_int8_t is_link = 0;
u_int8_t *nameptr;
user_addr_t uiobase = USER_ADDR_NULL;
size_t namelen = 0;
/* We're done when parent directory changes */
if (state->cbs_parentID != curID) {
+ /*
+ * If the parent ID is different from curID this means we've hit
+ * the EOF for the directory. To help future callers, we mark
+ * the cbs_eof boolean. However, we should only mark the EOF
+ * boolean if we're about to return from this function.
+ *
+ * This is because this callback function does its own uiomove
+ * to get the data to userspace. If we set the boolean before determining
+ * whether or not the current entry has enough room to write its
+ * data to userland, we could fool the callers of this catalog function
+ * into thinking they've hit EOF earlier than they really would have.
+ * In that case, we'd know that we have more entries to process and
+ * send to userland, but we didn't have enough room.
+ *
+ * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
+ * about to return and won't write any new data back
+ * to userland. In the stop_after_pack case, we'll set this boolean
+ * regardless, so it's slightly safer to let that logic mark the boolean,
+ * especially since it's closer to the return of this function.
+ */
+
if (state->cbs_extended) {
/* The last record has not been returned yet, so we
* want to stop after packing the last item
if (state->cbs_hasprevdirentry) {
stop_after_pack = true;
} else {
+ state->cbs_eof = true;
state->cbs_result = ENOENT;
return (0); /* stop */
}
} else {
+ state->cbs_eof = true;
state->cbs_result = ENOENT;
return (0); /* stop */
}
} else {
ilinkref = crp->hfsPlusFile.hl_linkReference;
}
+ is_link =1;
} else if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
(SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator) &&
(crp->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
type = DT_DIR;
/* A directory's link ref is always inode's file id. */
cnid = crp->hfsPlusFile.hl_linkReference;
+ is_link = 1;
}
/* Hide the journal files */
if ((curID == kHFSRootFolderID) &&
/* Check result returned from encoding the filename to utf8 */
if (result == ENAMETOOLONG) {
+ /*
+ * If we were looking at a catalog record for a hardlink (not the inode),
+ * then we want to use its link ID as opposed to the inode ID for
+ * a mangled name. For all other cases, they are the same. Note that
+ * due to the way directory hardlinks are implemented, the actual link
+ * is going to be counted as a file record, so we can catch both
+ * with is_link.
+ */
+ cnid_t linkid = cnid;
+ if (is_link) {
+ linkid = crp->hfsPlusFile.fileID;
+ }
+
result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
cnp->ustr.unicode, maxnamelen + 1,
- (ByteCount*)&namelen, nameptr, cnid);
+ (ByteCount*)&namelen, nameptr, linkid);
is_mangled = 1;
}
}
uiobase = uio_curriovbase(state->cbs_uio);
}
/* If this entry won't fit then we're done */
- if ((uiosize > uio_resid(state->cbs_uio)) ||
+ if ((uiosize > (user_size_t)uio_resid(state->cbs_uio)) ||
(ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks)) {
return (0); /* stop */
}
uioaddr = (caddr_t) &catent;
/* If this entry won't fit then we're done */
- if (uiosize > uio_resid(state->cbs_uio)) {
+ if (uiosize > (user_size_t)uio_resid(state->cbs_uio)) {
return (0); /* stop */
}
/*
* Pack a uio buffer with directory entries from the catalog
*/
-__private_extern__
int
cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint,
uio_t uio, int extended, int * items, int * eofflag)
state.cbs_nlinks = 0;
state.cbs_maxlinks = maxlinks;
state.cbs_linkinfo = (linkinfo_t *)((char *)buffer + MAXPATHLEN);
-
+ /*
+ * We need to set cbs_eof to false regardless of whether or not the
+ * control flow is actually in the extended case, since we use this
+ * field to track whether or not we've returned EOF from the iterator function.
+ */
+ state.cbs_eof = false;
+
iterator = (BTreeIterator *) ((char *)state.cbs_linkinfo + (maxlinks * sizeof(linkinfo_t)));
key = (CatalogKey *)&iterator->key;
have_key = 0;
if (extended) {
state.cbs_direntry = (struct direntry *)((char *)iterator + sizeof(BTreeIterator));
state.cbs_prevdirentry = state.cbs_direntry + 1;
- state.cbs_eof = false;
}
/*
* Attempt to build a key from cached filename
*items = state.cbs_index - index;
index = state.cbs_index;
+ /*
+ * Also note that cbs_eof is set in all cases if we ever hit EOF
+ * during the enumeration by the catalog callback. Mark the directory's hint
+ * descriptor as having hit EOF.
+ */
+
if (state.cbs_eof) {
+ dirhint->dh_desc.cd_flags |= CD_EOF;
*eofflag = 1;
}
* Post process any hard links to get the real file id.
*/
if (state.cbs_nlinks > 0) {
- u_int32_t fileid = 0;
+ ino_t fileid = 0;
user_addr_t address;
int i;
++state->count;
break;
default:
- printf("cat_findposition: invalid record type %d in dir %d\n",
+ printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
crp->recordType, curID);
state->error = EINVAL;
return (0); /* stop */
* The name portion of the key is compared using a 16-bit binary comparison.
* This is called from the b-tree code.
*/
-__private_extern__
int
cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
{
hfskey.parentID = key->parentID;
hfskey.nodeName[0] = 0;
if (key->nodeName.length > 0) {
- if (unicode_to_hfs(HFSTOVCB(hfsmp),
+ int res;
+ if ((res = unicode_to_hfs(HFSTOVCB(hfsmp),
key->nodeName.length * 2,
key->nodeName.unicode,
- &hfskey.nodeName[0], retry) != 0) {
- return (EINVAL);
+ &hfskey.nodeName[0], retry)) != 0) {
+ if (res != ENAMETOOLONG)
+ res = EINVAL;
+
+ return res;
}
hfskey.keyLength += hfskey.nodeName[0];
}
/*
* Resolve hard link reference to obtain the inode record.
*/
-__private_extern__
int
-cat_resolvelink(struct hfsmount *hfsmp, u_long linkref, int isdirlink, struct HFSPlusCatalogFile *recp)
+cat_resolvelink(struct hfsmount *hfsmp, u_int32_t linkref, int isdirlink, struct HFSPlusCatalogFile *recp)
{
FSBufferDescriptor btdata;
struct BTreeIterator *iterator;
if (recp->hl_linkCount == 0)
recp->hl_linkCount = 2;
} else {
- printf("HFS resolvelink: can't find %s\n", inodename);
+ printf("hfs: cat_resolvelink: can't find %s\n", inodename);
}
FREE(iterator, M_TEMP);
* Resolve hard link reference to obtain the inode number.
*/
static int
-resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino)
+resolvelinkid(struct hfsmount *hfsmp, u_int32_t linkref, ino_t *ino)
{
struct HFSPlusCatalogFile record;
int error;
* The key's parent id is the only part of the key expected to be used by the caller.
* The name portion of the key may not always be valid (ie in the case of a hard link).
*/
-__private_extern__
int
cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp)
{
* Pick up the first link in the chain and get a descriptor for it.
* This allows blind bulk access checks to work for hardlinks.
*/
- if ((cat_lookuplinkbyid(hfsmp, cnid, &prevlinkid, &nextlinkid) == 0) &&
+ if ((cat_lookup_siblinglinks(hfsmp, cnid, &prevlinkid, &nextlinkid) == 0) &&
(nextlinkid != 0)) {
if (cat_findname(hfsmp, nextlinkid, &linkdesc) == 0) {
key->hfsPlus.parentID = linkdesc.cd_parentcnid;
* builddesc - build a cnode descriptor from an HFS+ key
*/
static int
-builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
+builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_int32_t hint, u_int32_t encoding,
int isdir, struct cat_desc *descp)
{
int result = 0;
*/
static void
promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey,
- HFSPlusCatalogKey *keyp, u_long *encoding)
+ HFSPlusCatalogKey *keyp, u_int32_t *encoding)
{
hfs_to_unicode_func_t hfs_get_unicode = hfsmp->hfs_get_unicode;
u_int32_t uniCount;
int resource, struct cat_fork * forkp)
{
struct HFSPlusExtentDescriptor *xp;
- u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
+ u_int32_t blocksize = HFSTOVCB(hfsmp)->blockSize;
bzero(forkp, sizeof(*forkp));
xp = &forkp->cf_extents[0];
static void
promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp)
{
- u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
+ u_int32_t blocksize = HFSTOVCB(hfsmp)->blockSize;
if (dataPtr->recordType == kHFSFolderRecord) {
const struct HFSCatalogFolder * folder;
/*
* Extract the text encoding from a catalog node record.
*/
-static u_long
+static u_int32_t
getencoding(const CatalogRecord *crp)
{
- u_long encoding;
+ u_int32_t encoding;
if (crp->recordType == kHFSPlusFolderRecord)
encoding = crp->hfsPlusFolder.textEncoding;
crp->recordType == kHFSPlusFolderRecord);
}
+/*
+ * cat_lookup_dirlink - lookup a catalog record for directory hard link
+ * (not inode) using catalog record id. Note that this function does
+ * NOT resolve directory hard link to its directory inode and return
+ * the link record.
+ *
+ * Note: The caller is responsible for releasing the output catalog
+ * descriptor (when supplied outdescp is non-null).
+ */
+int
+cat_lookup_dirlink(struct hfsmount *hfsmp, cnid_t dirlink_id,
+ u_int8_t forktype, struct cat_desc *outdescp,
+ struct cat_attr *attrp, struct cat_fork *forkp)
+{
+ struct BTreeIterator *iterator = NULL;
+ FSBufferDescriptor btdata;
+ u_int16_t datasize;
+ CatalogKey *keyp;
+ CatalogRecord *recp = NULL;
+ int error;
+
+ /* No directory hard links on standard HFS */
+ if (hfsmp->vcbSigWord == kHFSSigWord) {
+ return ENOTSUP;
+ }
+
+ MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ if (iterator == NULL) {
+ return ENOMEM;
+ }
+ bzero(iterator, sizeof(*iterator));
+ buildthreadkey(dirlink_id, 1, (CatalogKey *)&iterator->key);
+
+ MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
+ if (recp == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ BDINIT(btdata, recp);
+
+ error = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
+ &btdata, &datasize, iterator);
+ if (error) {
+ goto out;
+ }
+ /* Directory hard links are catalog file record */
+ if (recp->recordType != kHFSPlusFileThreadRecord) {
+ error = ENOENT;
+ goto out;
+ }
+
+ keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
+ keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
+ (keyp->hfsPlus.nodeName.length * 2);
+ if (forktype == kHFSResourceForkType) {
+ /* Lookup resource fork for directory hard link */
+ error = cat_lookupbykey(hfsmp, keyp, HFS_LOOKUP_HARDLINK, 0, true, outdescp, attrp, forkp, NULL);
+ } else {
+ /* Lookup data fork, if any, for directory hard link */
+ error = cat_lookupbykey(hfsmp, keyp, HFS_LOOKUP_HARDLINK, 0, false, outdescp, attrp, forkp, NULL);
+ }
+ if (error) {
+ printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id, error);
+ hfs_mark_volume_inconsistent(hfsmp);
+ goto out;
+ }
+ /* Just for sanity, make sure that id in catalog record and thread record match */
+ if ((outdescp != NULL) && (dirlink_id != outdescp->cd_cnid)) {
+ printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id, outdescp->cd_cnid);
+ hfs_mark_volume_inconsistent(hfsmp);
+ error = ENOENT;
+ }
+
+out:
+ if (recp) {
+ FREE(recp, M_TEMP);
+ }
+ FREE(iterator, M_TEMP);
+
+ return MacToVFSError(error);
+}
+
+/*
+ * cnode_update_dirlink - update the catalog node for directory hard link
+ * described by descp using the data from attrp and forkp.
+ */
+int
+cat_update_dirlink(struct hfsmount *hfsmp, u_int8_t forktype,
+ struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp)
+{
+ if (forktype == kHFSResourceForkType) {
+ return cat_update_internal(hfsmp, true, descp, attrp, NULL, forkp);
+ } else {
+ return cat_update_internal(hfsmp, true, descp, attrp, forkp, NULL);
+ }
+}