X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/935ed37a5c468c8a1c07408573c08b8b7ef80e8b..7ee9d059c4eecf68ae4f8b0fb99ae2471eda79af:/bsd/hfs/hfs_catalog.c?ds=sidebyside diff --git a/bsd/hfs/hfs_catalog.c b/bsd/hfs/hfs_catalog.c index 04db5d290..0ab6f4585 100644 --- a/bsd/hfs/hfs_catalog.c +++ b/bsd/hfs/hfs_catalog.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2008 Apple Inc. All rights reserved. + * Copyright (c) 2000-2010 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -90,10 +90,12 @@ u_char modetodirtype[16] = { #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 */ @@ -101,7 +103,7 @@ static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int 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); @@ -114,17 +116,17 @@ static void buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int3 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); @@ -133,8 +135,9 @@ static int buildthread(void *keyp, void *recp, int std_hfs, int directory); 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) { @@ -152,7 +155,6 @@ cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, __unus return MacToVFSError(result); } -__private_extern__ void cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, __unused proc_t p) { @@ -167,8 +169,7 @@ 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, @@ -196,6 +197,7 @@ cat_convertattr( } 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)) { @@ -211,6 +213,7 @@ cat_convertattr( /* 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)) { @@ -241,7 +244,7 @@ cat_convertkey( { 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); @@ -295,7 +298,6 @@ cat_releasedesc(struct cat_desc *descp) * 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, @@ -342,7 +344,6 @@ exit: return (result); } -__private_extern__ int cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp) { @@ -407,7 +408,6 @@ exit: * catalog descriptor (when supplied outdescp is non-null). */ -__private_extern__ int cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp) { @@ -457,7 +457,7 @@ 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); @@ -480,7 +480,6 @@ exit: * 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) @@ -512,12 +511,26 @@ cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files, 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; @@ -527,7 +540,9 @@ cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files, 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. */ @@ -538,7 +553,7 @@ cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files, * 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; } } @@ -553,13 +568,14 @@ exit: /* * 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); @@ -586,6 +602,16 @@ cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc, 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: @@ -598,7 +624,7 @@ 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; @@ -607,9 +633,10 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files 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); @@ -625,16 +652,18 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files 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; } @@ -647,7 +676,7 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files 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; @@ -660,7 +689,7 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files (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); } @@ -674,8 +703,50 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files 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) { @@ -686,6 +757,7 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files } 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)) { @@ -704,6 +776,7 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files /* 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)) { @@ -736,6 +809,22 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files 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; @@ -784,7 +873,6 @@ exit: * 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) @@ -796,7 +884,7 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr 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; @@ -959,7 +1047,6 @@ exit: * 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, @@ -982,7 +1069,7 @@ cat_rename ( 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); @@ -1148,7 +1235,7 @@ cat_rename ( 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; @@ -1175,7 +1262,7 @@ cat_rename ( 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; @@ -1223,7 +1310,7 @@ cat_rename ( /* 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); @@ -1258,7 +1345,6 @@ exit: * 2. BTDeleteRecord(thread); * 3. BTUpdateRecord(parent); */ -__private_extern__ int cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp) { @@ -1349,12 +1435,13 @@ exit: /* - * 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; @@ -1379,13 +1466,14 @@ cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr * 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); @@ -1410,6 +1498,17 @@ exit: 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. @@ -1495,7 +1594,7 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st 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; @@ -1556,6 +1655,7 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st } case kHFSPlusFileRecord: { HFSPlusCatalogFile *file; + int is_dirlink; file = (struct HFSPlusCatalogFile *)crp; /* Do a quick sanity check */ @@ -1598,13 +1698,22 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st * 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; @@ -1638,7 +1747,7 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st 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 */ @@ -1650,8 +1759,18 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st /* 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; } @@ -1682,7 +1801,7 @@ cat_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid) /* 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; } @@ -1758,12 +1877,12 @@ cat_check_link_ancestry(struct hfsmount *hfsmp, cnid_t cnid, cnid_t pointed_at_c 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; } @@ -1780,7 +1899,7 @@ cat_check_link_ancestry(struct hfsmount *hfsmp, cnid_t cnid, cnid_t pointed_at_c /* - * updatelink_callback - update a link's chain + * update_siblinglinks_callback - update a link's chain */ struct linkupdate_state { @@ -1790,12 +1909,12 @@ 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); } @@ -1808,17 +1927,16 @@ updatelink_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct l 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; @@ -1830,24 +1948,25 @@ cat_updatelink(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cni 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) { @@ -1859,9 +1978,9 @@ cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfilei 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; @@ -1885,16 +2004,16 @@ cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfilei *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; @@ -1904,18 +2023,19 @@ cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid 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. */ @@ -1924,7 +2044,7 @@ cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid 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) { @@ -1939,6 +2059,7 @@ cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid *nextlinkid = 0; } exit: + FREE(iterator, M_TEMP); return MacToVFSError(result); } @@ -1954,7 +2075,6 @@ exit: * 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) @@ -1965,7 +2085,7 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr * 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; @@ -1993,7 +2113,7 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr * 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; } @@ -2072,7 +2192,7 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr * 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)) { @@ -2082,7 +2202,7 @@ exit: } 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; } @@ -2177,11 +2297,12 @@ cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalog 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) { @@ -2193,7 +2314,7 @@ cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalog 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); } @@ -2236,7 +2357,7 @@ cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalog 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; @@ -2248,7 +2369,6 @@ exit: /* * cat_deletelink - delete a link from the catalog */ -__private_extern__ int cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp) { @@ -2300,7 +2420,7 @@ 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; @@ -2376,7 +2496,7 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec, 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); @@ -2425,7 +2545,6 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec, * * Note: index is zero relative */ -__private_extern__ int cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list) { @@ -2577,7 +2696,7 @@ exit: /* 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; @@ -2626,6 +2745,7 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp, 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; @@ -2639,6 +2759,27 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp, /* 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 @@ -2646,10 +2787,12 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp, 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 */ } @@ -2697,6 +2840,7 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp, } 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) && @@ -2707,6 +2851,7 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp, 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) && @@ -2752,9 +2897,22 @@ encodestr: /* 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; } } @@ -2786,7 +2944,7 @@ encodestr: 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 */ } @@ -2945,7 +3103,7 @@ getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp, 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 */ } @@ -2974,7 +3132,6 @@ getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp, /* * 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) @@ -3012,7 +3169,13 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint 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; @@ -3020,7 +3183,6 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint 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 @@ -3142,7 +3304,14 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint *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; } @@ -3164,7 +3333,7 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint * 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; @@ -3239,7 +3408,7 @@ cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp, ++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 */ @@ -3255,7 +3424,6 @@ cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp, * 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) { @@ -3416,11 +3584,15 @@ buildkey(struct hfsmount *hfsmp, struct cat_desc *descp, 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]; } @@ -3433,9 +3605,8 @@ buildkey(struct hfsmount *hfsmp, struct cat_desc *descp, /* * 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; @@ -3475,7 +3646,7 @@ cat_resolvelink(struct hfsmount *hfsmp, u_long linkref, int isdirlink, struct HF 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); @@ -3487,7 +3658,7 @@ cat_resolvelink(struct hfsmount *hfsmp, u_long linkref, int isdirlink, struct HF * 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; @@ -3573,7 +3744,6 @@ exit: * 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) { @@ -3600,7 +3770,7 @@ cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct * 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; @@ -3697,7 +3867,7 @@ buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding * 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; @@ -3865,7 +4035,7 @@ getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct */ 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; @@ -3900,7 +4070,7 @@ promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep, 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]; @@ -3936,7 +4106,7 @@ promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep, 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; @@ -4039,10 +4209,10 @@ buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key) /* * 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; @@ -4119,3 +4289,99 @@ isadir(const CatalogRecord *crp) 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); + } +}