X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6d2010ae8f7a6078e10b361c6962983bab233e0f..bb59bff194111743b33cc36712410b5656329d3c:/bsd/hfs/hfs_catalog.c diff --git a/bsd/hfs/hfs_catalog.c b/bsd/hfs/hfs_catalog.c index 0ab6f4585..ef0b4a61e 100644 --- a/bsd/hfs/hfs_catalog.c +++ b/bsd/hfs/hfs_catalog.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Apple Inc. All rights reserved. + * Copyright (c) 2000-2014 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -67,8 +67,8 @@ struct btobj { struct update_state { struct cat_desc * s_desc; struct cat_attr * s_attr; - struct cat_fork * s_datafork; - struct cat_fork * s_rsrcfork; + const struct cat_fork * s_datafork; + const struct cat_fork * s_rsrcfork; struct hfsmount * s_hfsmp; }; @@ -92,6 +92,7 @@ u_char modetodirtype[16] = { #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 */ +#define HFS_LOOKUP_CASESENSITIVE 0x4 /* If set, verify results of a file/directory record match input case */ 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); @@ -121,9 +122,11 @@ static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_int32_t hint, static void getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp); +#if CONFIG_HFS_STD 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); +#endif static cnid_t getcnid(const CatalogRecord *crp); static u_int32_t getencoding(const CatalogRecord *crp); @@ -136,7 +139,200 @@ 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); + const struct cat_fork *dataforkp, const struct cat_fork *rsrcforkp); + + + +/* HFS ID Hashtable Functions */ +#define IDHASH(hfsmp, inum) (&hfsmp->hfs_idhashtbl[(inum) & hfsmp->hfs_idhash]) + +/* Initialize the HFS ID hash table */ +void +hfs_idhash_init (struct hfsmount *hfsmp) { + /* secured by catalog lock so no lock init needed */ + hfsmp->hfs_idhashtbl = hashinit(HFS_IDHASH_DEFAULT, M_HFSMNT, &hfsmp->hfs_idhash); +} + +/* Free the HFS ID hash table */ +void +hfs_idhash_destroy (struct hfsmount *hfsmp) { + /* during failed mounts & unmounts */ + FREE(hfsmp->hfs_idhashtbl, M_HFSMNT); +} + +/* +from hfs_catalog.h: +typedef struct cat_preflightid { + cnid_t fileid; + LIST_ENTRY(cat_preflightid) id_hash; +} cat_preflightid_t; + +from hfs.h: + u_long hfs_idhash; / size of cnid/fileid hash table -1 / + LIST_HEAD(idhashhead, cat_preflightid) *hfs_idhashtbl; / base of ID hash / +*/ + +/* + * Check the run-time ID hashtable. + * + * The catalog lock must be held (like other functions in this file). + * + * Returns: + * 1 if the ID is in the hash table. + * 0 if the ID is not in the hash table + */ +int cat_check_idhash (struct hfsmount *hfsmp, cnid_t test_fileid) { + + cat_preflightid_t *preflight; + int found = 0; + + for (preflight = IDHASH(hfsmp, test_fileid)->lh_first; preflight ; preflight = preflight->id_hash.le_next) { + if (preflight->fileid == test_fileid) { + found = 1; + break; + } + } + + return found; +} + +/* Insert the supplied preflight into the ID hash table */ +int cat_insert_idhash (struct hfsmount *hfsmp, cat_preflightid_t *preflight) { + + if (preflight) { + LIST_INSERT_HEAD(IDHASH(hfsmp, (preflight->fileid)), preflight, id_hash); + return 0; + } + return -1; +} + + +/* Remove the data structure with the specified ID from the hashtable */ +int cat_remove_idhash (cat_preflightid_t *preflight) { + + if ((preflight) && ((preflight->id_hash.le_next || preflight->id_hash.le_prev))) { + LIST_REMOVE (preflight, id_hash); + preflight->id_hash.le_next = NULL; + preflight->id_hash.le_prev = NULL; + + return 0; + } + + return -1; +} + +/* + * Acquire a new CNID for use. + * + * This is slightly more complicated than just pulling the value from the + * hfsmount data structure. We need to validate that the ID is not in-use + * even if we've not wrapped around and that there are not any lingering + * or orphaned fileIDs for this ID. + * + * Also validate that there are not any pending insertions into the + * catalog by checking the ID hash table. + */ +int +cat_acquire_cnid (struct hfsmount *hfsmp, cnid_t *new_cnid) +{ + uint32_t nextCNID; + struct BTreeIterator *iterator; + FSBufferDescriptor btdata; + uint16_t datasize; + CatalogRecord *recp; + int result = 0; + int std_hfs; + int wrapped = 0; + + std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord); + /* + * Get the next CNID. We can change it since we hold the catalog lock. + */ +nextid: + nextCNID = hfsmp->vcbNxtCNID; + if (nextCNID == 0xFFFFFFFF) { + if (std_hfs) { + return (ENOSPC); + } else { + wrapped++; + if (wrapped > 1) { + /* don't allow more than one wrap-around */ + return ENOSPC; + } + hfs_lock_mount (hfsmp); + hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID; + hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask; + hfs_unlock_mount (hfsmp); + } + } else { + hfsmp->vcbNxtCNID++; + } + MarkVCBDirty(hfsmp); + + /* First check that there are not any entries pending in the hash table with this ID */ + if (cat_check_idhash (hfsmp, nextCNID)) { + /* Someone wants to insert this into the catalog but hasn't done so yet. Skip it */ + goto nextid; + } + + /* Check to see if a thread record exists for the target ID we just got */ + MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); + bzero(iterator, sizeof(*iterator)); + buildthreadkey(nextCNID, std_hfs, (CatalogKey *)&iterator->key); + + MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK); + BDINIT(btdata, recp); + + result = BTSearchRecord(hfsmp->hfs_catalog_cp->c_datafork, iterator, &btdata, &datasize, iterator); + FREE (recp, M_TEMP); + FREE (iterator, M_TEMP); + + if (result == btNotFound) { + /* Good. File ID was not in use. Move on to checking EA B-Tree */ + result = file_attribute_exist (hfsmp, nextCNID); + if (result == EEXIST) { + /* This CNID has orphaned EAs. Skip it and move on to the next one */ + result = 0; + goto nextid; + } + if (result) { + /* For any other error, return the result */ + return result; + } + + /* + * Now validate that there are no lingering cnodes with this ID. If a cnode + * has been removed on-disk (marked C_NOEXISTS), but has not yet been reclaimed, + * then it will still have an entry in the cnode hash table. This means that + * a subsequent lookup will find THAT entry and believe this one has been deleted + * prematurely. If there is a lingering cnode, then just skip this entry and move on. + * + * Note that we pass (existence_only == 1) argument to hfs_chash_snoop. + */ + if (!std_hfs && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) { + if (hfs_chash_snoop (hfsmp, nextCNID, 1, NULL, NULL) == 0) { + goto nextid; + } + } + + /* + * If we get here, then we didn't see any thread records, orphaned EAs, + * or stale cnodes. This ID is safe to vend out. + */ + *new_cnid = nextCNID; + } + else if (result == noErr) { + /* move on to the next ID */ + goto nextid; + } + else { + /* For any other situation, just bail out */ + return EIO; + } + + return 0; + +} int cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, __unused proc_t p) @@ -180,21 +376,28 @@ cat_convertattr( { int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord; - if (std_hfs) { + if (std_hfs == 0) { + getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp); + } +#if CONFIG_HFS_STD + else { struct HFSPlusCatalogFile cnoderec; promoteattr(hfsmp, recp, &cnoderec); getbsdattr(hfsmp, &cnoderec, attrp); - } else { - getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp); - } + } +#endif - if (isadir(recp)) + if (isadir(recp)) { bzero(datafp, sizeof(*datafp)); + } +#if CONFIG_HFS_STD else if (std_hfs) { promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 0, datafp); promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 1, rsrcfp); - } else { + } +#endif + else { /* Convert the data fork. */ datafp->cf_size = recp->hfsPlusFile.dataFork.logicalSize; datafp->cf_new_size = 0; @@ -245,21 +448,38 @@ cat_convertkey( int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord; HFSPlusCatalogKey * pluskey = NULL; u_int32_t encoding; + cnid_t cnid = 0; + int err = 0; - if (std_hfs) { - MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK); - promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding); - - } else { + if (std_hfs == 0) { pluskey = (HFSPlusCatalogKey *)key; encoding = getencoding(recp); } +#if CONFIG_HFS_STD + else { + MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK); + promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding); + } +#endif + + /* Get the CNID before calling builddesc. Need to error check it. */ + cnid = getcnid(recp); + if (cnid == 0) { + /* If ths CNID == 0, it's invalid. Mark as corrupt */ + hfs_mark_inconsistent (hfsmp, HFS_INCONSISTENCY_DETECTED); + err = EINVAL; + } + else { + builddesc(pluskey, cnid, 0, encoding, isadir(recp), descp); + } - builddesc(pluskey, getcnid(recp), 0, encoding, isadir(recp), descp); +#if CONFIG_HFS_STD if (std_hfs) { FREE(pluskey, M_TEMP); } - return (0); +#endif + + return err; } @@ -299,15 +519,17 @@ cat_releasedesc(struct cat_desc *descp) * catalog descriptor (when supplied outdescp is non-null). */ int -cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc, +cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc, int force_casesensitive_lookup, struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid) { CatalogKey * keyp; int std_hfs; int result; + int flags; std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord); + flags = force_casesensitive_lookup ? HFS_LOOKUP_CASESENSITIVE : 0; MALLOC(keyp, CatalogKey *, sizeof(CatalogKey), M_TEMP, M_WAITOK); @@ -315,7 +537,7 @@ cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc, if (result) goto exit; - result = cat_lookupbykey(hfsmp, keyp, 0, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid); + result = cat_lookupbykey(hfsmp, keyp, flags, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid); if (result == ENOENT) { if (!std_hfs) { @@ -435,27 +657,35 @@ cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp) /* Turn thread record into a cnode key (in place). */ switch (recp->recordType) { - case kHFSFolderThreadRecord: - isdir = 1; - /* fall through */ - case kHFSFileThreadRecord: - keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6); - keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0]; - break; - case kHFSPlusFolderThreadRecord: - isdir = 1; - /* fall through */ - case kHFSPlusFileThreadRecord: - keyp = (CatalogKey *)&recp->hfsPlusThread.reserved; - keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength + - (keyp->hfsPlus.nodeName.length * 2); - break; - default: - result = ENOENT; - goto exit; +#if CONFIG_HFS_STD + case kHFSFolderThreadRecord: + isdir = 1; + /* fall through */ + case kHFSFileThreadRecord: + keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6); + keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0]; + break; +#endif + + case kHFSPlusFolderThreadRecord: + isdir = 1; + /* fall through */ + case kHFSPlusFileThreadRecord: + keyp = (CatalogKey *)&recp->hfsPlusThread.reserved; + keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength + + (keyp->hfsPlus.nodeName.length * 2); + break; + default: + result = ENOENT; + goto exit; } - if (std_hfs) { + + if (std_hfs == 0) { + builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp); + } +#if CONFIG_HFS_STD + else { HFSPlusCatalogKey * pluskey = NULL; u_int32_t encoding; @@ -463,10 +693,9 @@ cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp) promotekey(hfsmp, &keyp->hfs, pluskey, &encoding); builddesc(pluskey, cnid, 0, encoding, isdir, outdescp); FREE(pluskey, M_TEMP); - - } else { - builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp); } +#endif + exit: FREE(recp, M_TEMP); FREE(iterator, M_TEMP); @@ -481,7 +710,7 @@ exit: * catalog descriptor (when supplied outdescp is non-null). */ int -cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files, +cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files, int wantrsrc, struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp) { struct BTreeIterator * iterator; @@ -508,41 +737,44 @@ cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files, /* Turn thread record into a cnode key (in place) */ switch (recp->recordType) { - 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; - } +#if CONFIG_HFS_STD + case kHFSFileThreadRecord: + case kHFSFolderThreadRecord: + keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6); - keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0]; - break; + /* check for NULL name */ + if (keyp->hfs.nodeName[0] == 0) { + result = ENOENT; + goto exit; + } - case kHFSPlusFileThreadRecord: - case kHFSPlusFolderThreadRecord: - keyp = (CatalogKey *)&recp->hfsPlusThread.reserved; + keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0]; + break; +#endif - /* check for NULL name */ - if (keyp->hfsPlus.nodeName.length == 0) { - result = ENOENT; - goto exit; - } + case kHFSPlusFileThreadRecord: + case kHFSPlusFolderThreadRecord: + keyp = (CatalogKey *)&recp->hfsPlusThread.reserved; - keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength + - (keyp->hfsPlus.nodeName.length * 2); - break; + /* check for NULL name */ + if (keyp->hfsPlus.nodeName.length == 0) { + result = ENOENT; + goto exit; + } - default: - result = ENOENT; - goto exit; + keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength + + (keyp->hfsPlus.nodeName.length * 2); + break; + + default: + result = ENOENT; + goto exit; } result = cat_lookupbykey(hfsmp, keyp, ((allow_system_files != 0) ? HFS_LOOKUP_SYSFILE : 0), - 0, 0, outdescp, attrp, forkp, NULL); + 0, wantrsrc, outdescp, attrp, forkp, NULL); /* No corresponding file/folder record found for a thread record, * mark the volume inconsistent. */ @@ -575,7 +807,10 @@ cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc, cnid_t fileID; u_int32_t prefixlen; int result; - int extlen1, extlen2; + u_int8_t utf8[NAME_MAX + 1]; + u_int32_t utf8len; + u_int16_t unicode[kHFSPlusMaxFileNameChars + 1]; + size_t unicodelen; if (wantrsrc) return (ENOENT); @@ -591,26 +826,33 @@ cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc, return (ENOENT); } - result = cat_idlookup(hfsmp, fileID, 0, outdescp, attrp, forkp); + result = cat_idlookup(hfsmp, fileID, 0, 0, outdescp, attrp, forkp); if (result) return (ENOENT); /* It must be in the correct directory */ if (descp->cd_parentcnid != outdescp->cd_parentcnid) goto falsematch; - if (((u_int16_t)outdescp->cd_namelen < prefixlen) || - 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) + /* + * Compare the mangled version of file name looked up from the + * disk with the mangled name provided by the user. Note that + * this comparison is case-sensitive, which should be fine + * since we're trying to prevent user space from constructing + * a mangled name that differs from the one they'd get from the + * file system. + */ + result = utf8_decodestr(outdescp->cd_nameptr, outdescp->cd_namelen, + unicode, &unicodelen, sizeof(unicode), ':', 0); + if (result) { goto falsematch; - - if (bcmp(outdescp->cd_nameptr + (outdescp->cd_namelen - extlen2), - descp->cd_nameptr + (descp->cd_namelen - extlen1), - extlen1) != 0) + } + result = ConvertUnicodeToUTF8Mangled(unicodelen, unicode, + sizeof(utf8), &utf8len, utf8, fileID); + if ((result != 0) || + ((u_int16_t)descp->cd_namelen != utf8len) || + (bcmp(descp->cd_nameptr, utf8, utf8len) != 0)) { goto falsematch; + } return (0); @@ -654,9 +896,17 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t h /* Save the cnid, parentid, and encoding now in case there's a hard link or inode */ cnid = getcnid(recp); - if (!std_hfs) { + if (cnid == 0) { + /* CNID of 0 is invalid. Mark as corrupt */ + hfs_mark_inconsistent (hfsmp, HFS_INCONSISTENCY_DETECTED); + result = EINVAL; + goto exit; + } + + if (std_hfs == 0) { parentid = keyp->hfsPlus.parentID; } + encoding = getencoding(recp); hint = iterator->hint.nodeNum; @@ -664,9 +914,19 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t h if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) && ((cnid == hfsmp->hfs_jnlfileid) || (cnid == hfsmp->hfs_jnlinfoblkid)) && !(flags & HFS_LOOKUP_SYSFILE)) { - result = ENOENT; + result = ERESERVEDNAME; goto exit; } + + if (!std_hfs && !(hfsmp->hfs_flags & HFS_CASE_SENSITIVE)) { + /* Make sure the case of the file was correct if requested */ + if (flags & HFS_LOOKUP_CASESENSITIVE) { + if (0 != cat_binarykeycompare(&keyp->hfsPlus, (HFSPlusCatalogKey *)&iterator->key)) { + result = ERESERVEDNAME; + goto exit; + } + } + } /* * When a hardlink link is encountered, auto resolve it. @@ -695,19 +955,14 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t h } } - if (attrp != NULL) { - if (std_hfs) { - struct HFSPlusCatalogFile cnoderec; - - promoteattr(hfsmp, recp, &cnoderec); - getbsdattr(hfsmp, &cnoderec, attrp); - } else { + if (attrp != NULL) { + if (std_hfs == 0) { getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp); 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. @@ -728,12 +983,10 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t h } } 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. + * an inode record that was looked up and we do not end up + * reseting the hard link bit on it. */ - if (!(flags & HFS_LOOKUP_HARDLINK) && - (parentid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) && + if ((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) { @@ -748,13 +1001,25 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t h } } } +#if CONFIG_HFS_STD + else { + struct HFSPlusCatalogFile cnoderec; + + promoteattr(hfsmp, recp, &cnoderec); + getbsdattr(hfsmp, &cnoderec, attrp); + } +#endif } if (forkp != NULL) { if (isadir(recp)) { bzero(forkp, sizeof(*forkp)); - } else if (std_hfs) { + } +#if CONFIG_HFS_STD + else if (std_hfs) { promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, wantrsrc, forkp); - } else if (wantrsrc) { + } +#endif + else if (wantrsrc) { /* Convert the resource fork. */ forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize; forkp->cf_new_size = 0; @@ -823,7 +1088,7 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t h * than that which is in its extent records. */ - (void) hfs_mark_volume_inconsistent (hfsmp); + (void) hfs_mark_inconsistent (hfsmp, HFS_INCONSISTENCY_DETECTED); forkp->cf_blocks = validblks; if (attrp != NULL) { @@ -840,17 +1105,25 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t h if (descp != NULL) { HFSPlusCatalogKey * pluskey = NULL; - if (std_hfs) { + if (std_hfs == 0) { + pluskey = (HFSPlusCatalogKey *)&iterator->key; + } +#if CONFIG_HFS_STD + else { MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK); promotekey(hfsmp, (HFSCatalogKey *)&iterator->key, pluskey, &encoding); - } else { - pluskey = (HFSPlusCatalogKey *)&iterator->key; } +#endif + builddesc(pluskey, cnid, hint, encoding, isadir(recp), descp); + +#if CONFIG_HFS_STD if (std_hfs) { FREE(pluskey, M_TEMP); } +#endif + } if (desc_cnid != NULL) { @@ -874,13 +1147,12 @@ exit: * catalog descriptor (when supplied outdescp is non-null). */ int -cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp, +cat_create(struct hfsmount *hfsmp, cnid_t new_fileid, struct cat_desc *descp, struct cat_attr *attrp, struct cat_desc *out_descp) { FCB * fcb; struct btobj * bto; FSBufferDescriptor btdata; - u_int32_t nextCNID; u_int32_t datalen; int std_hfs; int result = 0; @@ -892,24 +1164,8 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr fcb = hfsmp->hfs_catalog_cp->c_datafork; std_hfs = (hfsmp->hfs_flags & HFS_STANDARD); - /* - * Get the next CNID. We can change it since we hold the catalog lock. - */ - nextCNID = hfsmp->vcbNxtCNID; - if (nextCNID == 0xFFFFFFFF) { - if (std_hfs) { - return (ENOSPC); - } else { - HFS_MOUNT_LOCK(hfsmp, TRUE) - hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID; - hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask; - HFS_MOUNT_UNLOCK(hfsmp, TRUE); - } - } else { - hfsmp->vcbNxtCNID++; - } - MarkVCBDirty(hfsmp); - + /* The caller is expected to reserve a CNID before calling this function! */ + /* Get space for iterator, key and data */ MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK); bto->iterator.hint.nodeNum = 0; @@ -934,50 +1190,22 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr btdata.itemSize = datalen; btdata.itemCount = 1; - for (;;) { - // this call requires the attribute file lock to be held - result = file_attribute_exist(hfsmp, nextCNID); - if (result == EEXIST) { - // that cnid has orphaned attributes so just skip it. - if (++nextCNID < kHFSFirstUserCatalogNodeID) { - nextCNID = kHFSFirstUserCatalogNodeID; - } - continue; - } - if (result) goto exit; - - buildthreadkey(nextCNID, std_hfs, (CatalogKey *) &bto->iterator.key); - - result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen); - if ((result == btExists) && !std_hfs && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) { - /* - * Allow CNIDs on HFS Plus volumes to wrap around - */ - if (++nextCNID < kHFSFirstUserCatalogNodeID) { - nextCNID = kHFSFirstUserCatalogNodeID; - } - continue; - } - 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; + /* Caller asserts the following: + * 1) this CNID is not in use by any orphaned EAs + * 2) There are no lingering cnodes (removed on-disk but still in-core) with this CNID + * 3) There are no thread or catalog records for this ID + */ + buildthreadkey(new_fileid, std_hfs, (CatalogKey *) &bto->iterator.key); + result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen); + if (result) { + goto exit; } } /* * Now insert the file/directory record */ - buildrecord(attrp, nextCNID, std_hfs, encoding, &bto->data, &datalen); + buildrecord(attrp, new_fileid, std_hfs, encoding, &bto->data, &datalen); btdata.bufferAddress = &bto->data; btdata.itemSize = datalen; btdata.itemCount = 1; @@ -991,13 +1219,13 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr /* Back out the thread record */ if (!std_hfs || S_ISDIR(attrp->ca_mode)) { - buildthreadkey(nextCNID, std_hfs, (CatalogKey *)&bto->iterator.key); + buildthreadkey(new_fileid, std_hfs, (CatalogKey *)&bto->iterator.key); if (BTDeleteRecord(fcb, &bto->iterator)) { /* Error on deleting extra thread record, mark * volume inconsistent */ - printf ("hfs: cat_create() failed to delete thread record on volume %s\n", hfsmp->vcbVN); - hfs_mark_volume_inconsistent(hfsmp); + printf ("hfs: cat_create() failed to delete thread record id=%u on vol=%s\n", new_fileid, hfsmp->vcbVN); + hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED); } } goto exit; @@ -1009,20 +1237,26 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr if (out_descp != NULL) { HFSPlusCatalogKey * pluskey = NULL; - if (std_hfs) { + if (std_hfs == 0) { + pluskey = (HFSPlusCatalogKey *)&bto->iterator.key; + } +#if CONFIG_HFS_STD + else { MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK); promotekey(hfsmp, (HFSCatalogKey *)&bto->iterator.key, pluskey, &encoding); - - } else - pluskey = (HFSPlusCatalogKey *)&bto->iterator.key; + } +#endif - builddesc(pluskey, nextCNID, bto->iterator.hint.nodeNum, + builddesc(pluskey, new_fileid, bto->iterator.hint.nodeNum, encoding, S_ISDIR(attrp->ca_mode), out_descp); +#if CONFIG_HFS_STD if (std_hfs) { FREE(pluskey, M_TEMP); } +#endif + } - attrp->ca_fileid = nextCNID; + attrp->ca_fileid = new_fileid; exit: (void) BTFlushPath(fcb); @@ -1096,10 +1330,11 @@ cat_rename ( * When moving a directory, make sure its a valid move. */ if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)) { - struct BTreeIterator iterator; + struct BTreeIterator *dir_iterator = NULL; + cnid_t cnid = from_cdp->cd_cnid; cnid_t pathcnid = todir_cdp->cd_parentcnid; - + /* First check the obvious ones */ if (cnid == fsRtDirID || cnid == to_cdp->cd_parentcnid || @@ -1107,25 +1342,33 @@ cat_rename ( result = EINVAL; goto exit; } - bzero(&iterator, sizeof(iterator)); + /* now allocate the dir_iterator */ + MALLOC (dir_iterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK); + if (dir_iterator == NULL) { + return ENOMEM; + } + bzero(dir_iterator, sizeof(*dir_iterator)); + /* * Traverse destination path all the way back to the root * making sure that source directory is not encountered. * */ while (pathcnid > fsRtDirID) { - buildthreadkey(pathcnid, std_hfs, - (CatalogKey *)&iterator.key); - result = BTSearchRecord(fcb, &iterator, &btdata, - &datasize, NULL); - if (result) goto exit; - + buildthreadkey(pathcnid, std_hfs, (CatalogKey *)&dir_iterator->key); + result = BTSearchRecord(fcb, dir_iterator, &btdata, &datasize, NULL); + if (result) { + FREE(dir_iterator, M_TEMP); + goto exit; + } pathcnid = getparentcnid(recp); if (pathcnid == cnid || pathcnid == 0) { result = EINVAL; + FREE(dir_iterator, M_TEMP); goto exit; } } + FREE(dir_iterator, M_TEMP); } /* @@ -1186,9 +1429,13 @@ cat_rename ( out_cdp->cd_encoding = encoding; } +#if CONFIG_HFS_STD if (std_hfs && !directory && - !(recp->hfsFile.flags & kHFSThreadExistsMask)) + !(recp->hfsFile.flags & kHFSThreadExistsMask)) { skipthread = 1; + } +#endif + #if 0 /* * If the keys are identical then there's nothing left to do! @@ -1206,6 +1453,7 @@ cat_rename ( result = BTInsertRecord(fcb, to_iterator, &btdata, datasize); if (result == btExists) { int fromtype = recp->recordType; + cnid_t cnid = 0; if (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid) goto exit; /* EEXIST */ @@ -1215,8 +1463,16 @@ cat_rename ( if (result) goto exit; + /* Get the CNID after calling searchrecord */ + cnid = getcnid (recp); + if (cnid == 0) { + hfs_mark_inconsistent(hfsmp, HFS_INCONSISTENCY_DETECTED); + result = EINVAL; + goto exit; + } + if ((fromtype != recp->recordType) || - (from_cdp->cd_cnid != getcnid(recp))) { + (from_cdp->cd_cnid != cnid)) { result = EEXIST; goto exit; /* EEXIST */ } @@ -1235,8 +1491,8 @@ cat_rename ( int err; err = BTInsertRecord(fcb, from_iterator, &btdata, datasize); if (err) { - printf("hfs: cat_create: could not undo (BTInsert = %d)", err); - hfs_mark_volume_inconsistent(hfsmp); + printf("hfs: cat_create: could not undo (BTInsert = %d)\n", err); + hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED); result = err; goto exit; } @@ -1262,8 +1518,8 @@ cat_rename ( int err; err = BTDeleteRecord(fcb, to_iterator); if (err) { - printf("hfs: cat_create: could not undo (BTDelete = %d)", err); - hfs_mark_volume_inconsistent(hfsmp); + printf("hfs: cat_create: could not undo (BTDelete = %d)\n", err); + hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED); result = err; goto exit; } @@ -1304,7 +1560,11 @@ cat_rename ( if (out_cdp) { HFSPlusCatalogKey * pluskey = NULL; - if (std_hfs) { + if (std_hfs == 0) { + pluskey = (HFSPlusCatalogKey *)&to_iterator->key; + } +#if CONFIG_HFS_STD + else { MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK); promotekey(hfsmp, (HFSCatalogKey *)&to_iterator->key, pluskey, &encoding); @@ -1315,15 +1575,17 @@ cat_rename ( realhint = hfs_pickencoding(pluskey->nodeName.unicode, pluskey->nodeName.length); vcb->vcbFndrInfo[4] = SET_HFS_TEXT_ENCODING(realhint); } - - } else - pluskey = (HFSPlusCatalogKey *)&to_iterator->key; + } +#endif builddesc(pluskey, from_cdp->cd_cnid, to_iterator->hint.nodeNum, encoding, directory, out_cdp); +#if CONFIG_HFS_STD if (std_hfs) { FREE(pluskey, M_TEMP); } +#endif + } exit: (void) BTFlushPath(fcb); @@ -1422,8 +1684,8 @@ cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key); if (BTDeleteRecord(fcb, iterator)) { if (!std_hfs) { - printf ("hfs: cat_delete() failed to delete thread record on volume %s\n", hfsmp->vcbVN); - hfs_mark_volume_inconsistent(hfsmp); + printf ("hfs: cat_delete() failed to delete thread record id=%u on vol=%s\n", cnid, hfsmp->vcbVN); + hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE); } } @@ -1442,16 +1704,14 @@ exit: */ 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) + const struct cat_fork *dataforkp, const struct cat_fork *rsrcforkp) { FCB * fcb; BTreeIterator * iterator; struct update_state state; - int std_hfs; int result; fcb = hfsmp->hfs_catalog_cp->c_datafork; - std_hfs = (hfsmp->hfs_flags & HFS_STANDARD); state.s_desc = descp; state.s_attr = attrp; @@ -1504,7 +1764,7 @@ exit: */ int cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp, - struct cat_fork *dataforkp, struct cat_fork *rsrcforkp) + const struct cat_fork *dataforkp, const struct cat_fork *rsrcforkp) { return cat_update_internal(hfsmp, false, descp, attrp, dataforkp, rsrcforkp); } @@ -1518,10 +1778,9 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st { struct cat_desc *descp; struct cat_attr *attrp; - struct cat_fork *forkp; + const struct cat_fork *forkp; struct hfsmount *hfsmp; long blksize; - int i; descp = state->s_desc; attrp = state->s_attr; @@ -1529,6 +1788,8 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st blksize = HFSTOVCB(hfsmp)->blockSize; switch (crp->recordType) { + +#if CONFIG_HFS_STD case kHFSFolderRecord: { HFSCatalogFolder *dir; @@ -1547,7 +1808,8 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st } case kHFSFileRecord: { HFSCatalogFile *file; - + int i; + file = (struct HFSCatalogFile *)crp; /* Do a quick sanity check */ if ((ckp->hfs.parentID != descp->cd_parentcnid) || @@ -1588,13 +1850,15 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st file->flags &= ~kHFSFileLockedMask; break; } +#endif + case kHFSPlusFolderRecord: { HFSPlusCatalogFolder *dir; dir = (struct HFSPlusCatalogFolder *)crp; /* Do a quick sanity check */ if (dir->folderID != attrp->ca_fileid) { - printf("hfs: catrec_update: id %d != %d\n", dir->folderID, attrp->ca_fileid); + printf("hfs: catrec_update: id %d != %d, vol=%s\n", dir->folderID, attrp->ca_fileid, hfsmp->vcbVN); return (btNotFound); } dir->flags = attrp->ca_recflags; @@ -1768,7 +2032,7 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st * 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 > 1)) { file->hl_linkCount = attrp->ca_linkcount; } } @@ -1817,7 +2081,7 @@ cat_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid) lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); /* Look up our catalog folder record */ - retval = cat_idlookup(hfsmp, cnid, 0, &desc, &attr, NULL); + retval = cat_idlookup(hfsmp, cnid, 0, 0, &desc, &attr, NULL); if (retval) { hfs_systemfile_unlock(hfsmp, lockflags); hfs_end_transaction(hfsmp); @@ -1877,12 +2141,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("hfs: cat_check_link_ancestry: getkey for %u failed\n", cnid); + printf("hfs: cat_check_link_ancestry: getkey failed id=%u, vol=%s\n", cnid, hfsmp->vcbVN); 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); + printf("hfs: cat_check_link_ancestry: cannot find id=%u, vol=%s\n", cnid, hfsmp->vcbVN); invalid = 1; /* On errors, assume an invalid parent */ break; } @@ -1951,15 +2215,15 @@ cat_update_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevli /* 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); + printf("hfs: cat_update_siblinglinks: couldn't resolve cnid=%d, vol=%s\n", linkfileid, hfsmp->vcbVN); } - + FREE (iterator, M_TEMP); return MacToVFSError(result); } @@ -2026,39 +2290,139 @@ cat_lookup_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevl /* 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; + 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_lookup_lastlink - find the last sibling link in the chain (no "next" ptr) + */ +int +cat_lookup_lastlink(struct hfsmount *hfsmp, cnid_t linkfileid, + cnid_t *lastlink, struct cat_desc *cdesc) +{ + FCB * fcb; + BTreeIterator * iterator; + struct FSBufferDescriptor btdata; + struct HFSPlusCatalogFile file; + int result; + int itercount = 0; + int foundlast = 0; + cnid_t currentlink = linkfileid; + + fcb = hfsmp->hfs_catalog_cp->c_datafork; + + /* Create an iterator for use by us temporarily */ + MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); + + while ((foundlast == 0) && (itercount < HFS_LINK_MAX )) { + itercount++; + bzero(iterator, sizeof(*iterator)); + + if ((result = getkey(hfsmp, currentlink, (CatalogKey *)&iterator->key))) { + goto exit; + } + BDINIT(btdata, &file); + + if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) { + goto exit; + } + + /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */ + if (file.flags & kHFSHasLinkChainMask) { + cnid_t parent; - /* 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; + parent = ((HFSPlusCatalogKey *)&iterator->key)->parentID; + /* + * The raw inode for a directory hardlink doesn't have a chain. + * Its link information lives in an EA. + */ + if (parent == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) { + /* We don't iterate to find the oldest directory hardlink. */ + result = ENOLINK; + goto exit; + } + else if (parent == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) { + /* Raw inode for file hardlink (the base inode) */ + currentlink = file.hl_firstLinkID; + + /* + * One minor special-casing here is necessary. + * If our ID brought us to the raw hardlink inode, and it does + * not have any siblings, then it's an open-unlinked file, and we + * should not proceed any further. + */ + if (currentlink == 0) { + result = ENOLINK; + goto exit; + } + } + else { + /* Otherwise, this item's parent is a legitimate directory in the namespace */ + if (file.hl_nextLinkID == 0) { + /* If nextLinkID is 0, then we found the end; no more hardlinks */ + foundlast = 1; + *lastlink = currentlink; + /* + * Since we had to construct a catalog key to do this lookup + * we still hold it in-hand. We might as well use it to build + * the descriptor that the caller asked for. + */ + builddesc ((HFSPlusCatalogKey*)&iterator->key, currentlink, 0, 0, 0, cdesc); + break; + } + + currentlink = file.hl_nextLinkID; + } + } + else { + /* Sorry, can't help you without a link chain */ + result = ENOLINK; + goto exit; } - } else { - *prevlinkid = 0; - *nextlinkid = 0; } exit: + /* If we didn't find what we were looking for, zero out the args */ + if (foundlast == 0) { + if (cdesc) { + bzero (cdesc, sizeof(struct cat_desc)); + } + if (lastlink) { + *lastlink = 0; + } + } + FREE(iterator, M_TEMP); return MacToVFSError(result); } @@ -2089,22 +2453,19 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr * int thread_inserted = 0; int alias_allocated = 0; int result = 0; + int std_hfs; + + std_hfs = (hfsmp->hfs_flags & HFS_STANDARD); fcb = hfsmp->hfs_catalog_cp->c_datafork; /* - * Get the next CNID. We can change it since we hold the catalog lock. + * Get the next CNID. Note that we are currently holding 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++; + result = cat_acquire_cnid(hfsmp, &nextCNID); + if (result) { + return result; } - MarkVCBDirty(hfsmp); /* Get space for iterator, key and data */ MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK); @@ -2120,43 +2481,20 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr * /* 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. */ + /* + * 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) + buildthreadkey(nextCNID, 0, (CatalogKey *) &bto->iterator.key); + result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen); + 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; - } } + thread_inserted = 1; /* * Now insert the link record. @@ -2192,12 +2530,12 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr * exit: if (result) { if (thread_inserted) { - printf("hfs: cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result)); + printf("hfs: cat_createlink: BTInsertRecord err=%d, vol=%s\n", MacToVFSError(result), hfsmp->vcbVN); 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); + printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp->vcbVN); + hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED); } } if (alias_allocated && rsrcforkp->extents[0].startBlock != 0) { @@ -2305,6 +2643,13 @@ cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalog HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE, &rsrcforkp->extents[0].startBlock, &rsrcforkp->extents[0].blockCount); + /* Did it fail with an out of space error? If so, re-try and allow journal flushing. */ + if (result == dskFulErr ) { + result = BlockAllocate(hfsmp, 0, blkcount, blkcount, + HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE | HFS_ALLOC_FLUSHTXN, + &rsrcforkp->extents[0].startBlock, + &rsrcforkp->extents[0].blockCount); + } if (result) { rsrcforkp->extents[0].startBlock = 0; goto exit; @@ -2442,6 +2787,7 @@ struct readattr_state { cnid_t dir_cnid; int stdhfs; int error; + int reached_eof; }; static int @@ -2461,10 +2807,13 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec, switch(rec->recordType) { case kHFSPlusFolderRecord: case kHFSPlusFileRecord: +#if CONFIG_HFS_STD case kHFSFolderRecord: case kHFSFileRecord: +#endif if (parentcnid != state->dir_cnid) { state->error = ENOENT; + state->reached_eof = 1; return (0); /* stop */ } break; @@ -2492,8 +2841,31 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec, } cep = &list->entry[list->realentries++]; + + if (state->stdhfs == 0) { + getbsdattr(hfsmp, (const struct HFSPlusCatalogFile *)rec, &cep->ce_attr); + builddesc((const HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec), + isadir(rec), &cep->ce_desc); + + if (rec->recordType == kHFSPlusFileRecord) { + cep->ce_datasize = rec->hfsPlusFile.dataFork.logicalSize; + cep->ce_datablks = rec->hfsPlusFile.dataFork.totalBlocks; + cep->ce_rsrcsize = rec->hfsPlusFile.resourceFork.logicalSize; + cep->ce_rsrcblks = rec->hfsPlusFile.resourceFork.totalBlocks; - if (state->stdhfs) { + /* Save link reference for later processing. */ + if ((SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) && + (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) { + cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum; + } else if ((rec->hfsPlusFile.flags & kHFSHasLinkChainMask) && + (SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHFSAliasType) && + (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) { + cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum; + } + } + } +#if CONFIG_HFS_STD + else { struct HFSPlusCatalogFile cnoderec; HFSPlusCatalogKey * pluskey; u_int32_t encoding; @@ -2514,28 +2886,8 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec, cep->ce_rsrcsize = rec->hfsFile.rsrcLogicalSize; cep->ce_rsrcblks = rec->hfsFile.rsrcPhysicalSize / blksize; } - } else { - getbsdattr(hfsmp, (const struct HFSPlusCatalogFile *)rec, &cep->ce_attr); - builddesc((const HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec), - isadir(rec), &cep->ce_desc); - - if (rec->recordType == kHFSPlusFileRecord) { - cep->ce_datasize = rec->hfsPlusFile.dataFork.logicalSize; - cep->ce_datablks = rec->hfsPlusFile.dataFork.totalBlocks; - cep->ce_rsrcsize = rec->hfsPlusFile.resourceFork.logicalSize; - cep->ce_rsrcblks = rec->hfsPlusFile.resourceFork.totalBlocks; - - /* Save link reference for later processing. */ - if ((SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) && - (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) { - cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum; - } else if ((rec->hfsPlusFile.flags & kHFSHasLinkChainMask) && - (SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHFSAliasType) && - (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) { - cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum; - } - } } +#endif return (list->realentries < list->maxentries); } @@ -2546,7 +2898,7 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec, * Note: index is zero relative */ int -cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list) +cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list, int *reachedeof) { FCB* fcb; CatalogKey * key; @@ -2558,6 +2910,7 @@ cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_ int index; int have_key; int result = 0; + int reached_eof = 0; ce_list->realentries = 0; @@ -2565,6 +2918,8 @@ cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_ std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord); parentcnid = dirhint->dh_desc.cd_parentcnid; + bzero (&state, sizeof(struct readattr_state)); + state.hfsmp = hfsmp; state.list = ce_list; state.dir_cnid = parentcnid; @@ -2621,7 +2976,15 @@ cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_ result = ps.error; else result = MacToVFSError(result); + if (result) { + /* + * Note: the index may now point to EOF if the directory + * was modified in between system calls. We will return + * ENOENT from cat_findposition if this is the case, and + * when we bail out with an error, our caller (hfs_readdirattr_internal) + * will suppress the error and indicate EOF to its caller. + */ result = MacToVFSError(result); goto exit; } @@ -2632,12 +2995,17 @@ cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_ result = BTIterateRecords(fcb, kBTreeNextRecord, iterator, (IterateCallBackProcPtr)getentriesattr_callback, &state); - if (state.error) + if (state.error) { result = state.error; - else if (ce_list->realentries == 0) + reached_eof = state.reached_eof; + } + else if (ce_list->realentries == 0) { result = ENOENT; - else + reached_eof = 1; + } + else { result = MacToVFSError(result); + } if (std_hfs) goto exit; @@ -2681,9 +3049,10 @@ cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_ cep->ce_rsrcblks = filerec.resourceFork.totalBlocks; } } + exit: FREE(iterator, M_TEMP); - + *reachedeof = reached_eof; return MacToVFSError(result); } @@ -2703,7 +3072,7 @@ typedef struct linkinfo linkinfo_t; /* State information for the getdirentries_callback function. */ struct packdirentry_state { - int cbs_extended; + int cbs_flags; /* VNODE_READDIR_* flags */ u_int32_t cbs_parentID; u_int32_t cbs_index; uio_t cbs_uio; @@ -2780,7 +3149,7 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp, * especially since it's closer to the return of this function. */ - if (state->cbs_extended) { + if (state->cbs_flags & VNODE_READDIR_EXTENDED) { /* The last record has not been returned yet, so we * want to stop after packing the last item */ @@ -2798,16 +3167,26 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp, } } - if (state->cbs_extended) { + if (state->cbs_flags & VNODE_READDIR_EXTENDED) { entry = state->cbs_direntry; nameptr = (u_int8_t *)&entry->d_name[0]; - maxnamelen = NAME_MAX; + if (state->cbs_flags & VNODE_READDIR_NAMEMAX) { + /* + * The NFS server sometimes needs to make filenames fit in + * NAME_MAX bytes (since its client may not be able to + * handle a longer name). In that case, NFS will ask us + * to mangle the name to keep it short enough. + */ + maxnamelen = NAME_MAX + 1; + } else { + maxnamelen = sizeof(entry->d_name); + } } else { nameptr = (u_int8_t *)&catent.d_name[0]; - maxnamelen = NAME_MAX; + maxnamelen = sizeof(catent.d_name); } - if (state->cbs_extended && stop_after_pack) { + if ((state->cbs_flags & VNODE_READDIR_EXTENDED) && stop_after_pack) { /* The last item returns a non-zero invalid cookie */ cnid = INT_MAX; } else { @@ -2892,7 +3271,7 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp, } else { encodestr: result = utf8_encodestr(cnp->ustr.unicode, namelen * sizeof(UniChar), - nameptr, &namelen, maxnamelen + 1, ':', 0); + nameptr, &namelen, maxnamelen, ':', 0); } /* Check result returned from encoding the filename to utf8 */ @@ -2911,13 +3290,13 @@ encodestr: } result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar), - cnp->ustr.unicode, maxnamelen + 1, + cnp->ustr.unicode, maxnamelen, (ByteCount*)&namelen, nameptr, linkid); is_mangled = 1; } } - if (state->cbs_extended) { + if (state->cbs_flags & VNODE_READDIR_EXTENDED) { /* * The index is 1 relative and includes "." and ".." * @@ -2949,7 +3328,7 @@ encodestr: return (0); /* stop */ } - if (!state->cbs_extended || state->cbs_hasprevdirentry) { + if (!(state->cbs_flags & VNODE_READDIR_EXTENDED) || state->cbs_hasprevdirentry) { state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio); if (state->cbs_result == 0) { ++state->cbs_index; @@ -3013,7 +3392,7 @@ encodestr: } /* Fill the direntry to be used the next time */ - if (state->cbs_extended) { + if (state->cbs_flags & VNODE_READDIR_EXTENDED) { if (stop_after_pack) { state->cbs_eof = true; return (0); /* stop */ @@ -3041,6 +3420,7 @@ encodestr: uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE); } +#if CONFIG_HFS_STD /* * getdirentries callback for standard HFS (non HFS+) directories. */ @@ -3072,7 +3452,7 @@ getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp, } nameptr = (u_int8_t *)&catent.d_name[0]; - maxnamelen = NAME_MAX; + maxnamelen = sizeof(catent.d_name); switch(crp->recordType) { case kHFSFolderRecord: @@ -3088,13 +3468,13 @@ getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp, }; cnp = (const CatalogName*) ckp->hfs.nodeName; - result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen + 1, (ByteCount *)&namelen, nameptr); + result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen, (ByteCount *)&namelen, nameptr); /* * When an HFS name cannot be encoded with the current * volume encoding we use MacRoman as a fallback. */ if (result) { - result = mac_roman_to_utf8(cnp->pstr, maxnamelen + 1, (ByteCount *)&namelen, nameptr); + result = mac_roman_to_utf8(cnp->pstr, maxnamelen, (ByteCount *)&namelen, nameptr); } catent.d_type = type; catent.d_namlen = namelen; @@ -3128,13 +3508,14 @@ getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp, /* Continue iteration if there's room */ return (state->cbs_result == 0 && uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE); } +#endif /* * Pack a uio buffer with directory entries from the catalog */ int -cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint, - uio_t uio, int extended, int * items, int * eofflag) +cat_getdirentries(struct hfsmount *hfsmp, u_int32_t entrycnt, directoryhint_t *dirhint, + uio_t uio, int flags, int * items, int * eofflag) { FCB* fcb; BTreeIterator * iterator; @@ -3146,7 +3527,10 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint int result; int index; int have_key; - + int extended; + + extended = flags & VNODE_READDIR_EXTENDED; + if (extended && (hfsmp->hfs_flags & HFS_STANDARD)) { return (ENOTSUP); } @@ -3155,7 +3539,7 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint /* * Get a buffer for link info array, btree iterator and a direntry: */ - maxlinks = MIN(entrycnt, uio_resid(uio) / SMALL_DIRENTRY_SIZE); + maxlinks = MIN(entrycnt, (u_int32_t)(uio_resid(uio) / SMALL_DIRENTRY_SIZE)); bufsize = MAXPATHLEN + (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator); if (extended) { bufsize += 2*sizeof(struct direntry); @@ -3163,7 +3547,7 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint MALLOC(buffer, void *, bufsize, M_TEMP, M_WAITOK); bzero(buffer, bufsize); - state.cbs_extended = extended; + state.cbs_flags = flags; state.cbs_hasprevdirentry = false; state.cbs_previlinkref = 0; state.cbs_nlinks = 0; @@ -3243,6 +3627,15 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint result = MacToVFSError(result); if (result) { result = MacToVFSError(result); + if (result == ENOENT) { + /* + * ENOENT means we've hit the EOF. + * suppress the error, and set the eof flag. + */ + result = 0; + dirhint->dh_desc.cd_flags |= CD_EOF; + *eofflag = 1; + } goto cleanup; } } @@ -3275,10 +3668,8 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint /* * Process as many entries as possible starting at iterator->key. */ - if (hfsmp->hfs_flags & HFS_STANDARD) - result = BTIterateRecords(fcb, op, iterator, - (IterateCallBackProcPtr)getdirentries_std_callback, &state); - else { + if ((hfsmp->hfs_flags & HFS_STANDARD) == 0) { + /* HFS+ */ result = BTIterateRecords(fcb, op, iterator, (IterateCallBackProcPtr)getdirentries_callback, &state); @@ -3289,7 +3680,7 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint * dummy values to copy the last directory entry stored in * packdirentry_state */ - if (state.cbs_extended && (result == fsBTRecordNotFoundErr)) { + if (extended && (result == fsBTRecordNotFoundErr)) { CatalogKey ckp; CatalogRecord crp; @@ -3299,6 +3690,13 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint result = getdirentries_callback(&ckp, &crp, &state); } } +#if CONFIG_HFS_STD + else { + /* HFS (standard) */ + result = BTIterateRecords(fcb, op, iterator, + (IterateCallBackProcPtr)getdirentries_std_callback, &state); + } +#endif /* Note that state.cbs_index is still valid on errors */ *items = state.cbs_index - index; @@ -3386,16 +3784,24 @@ static int cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp, struct position_state *state) { - cnid_t curID; + cnid_t curID = 0; - if (state->hfsmp->hfs_flags & HFS_STANDARD) - curID = ckp->hfs.parentID; - else + if ((state->hfsmp->hfs_flags & HFS_STANDARD) == 0) { curID = ckp->hfsPlus.parentID; + } +#if CONFIG_HFS_STD + else { + curID = ckp->hfs.parentID; + } +#endif /* Make sure parent directory didn't change */ if (state->parentID != curID) { - state->error = EINVAL; + /* + * The parent ID is different from curID this means we've hit + * the EOF for the directory. + */ + state->error = ENOENT; return (0); /* stop */ } @@ -3403,8 +3809,10 @@ cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp, switch(crp->recordType) { case kHFSPlusFolderRecord: case kHFSPlusFileRecord: +#if CONFIG_HFS_STD case kHFSFolderRecord: case kHFSFileRecord: +#endif ++state->count; break; default: @@ -3443,38 +3851,15 @@ cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey) u_int16_t * str2 = &trialKey->nodeName.unicode[0]; int length1 = searchKey->nodeName.length; int length2 = trialKey->nodeName.length; - u_int16_t c1, c2; - int length; - - if (length1 < length2) { - length = length1; - --result; - } else if (length1 > length2) { - length = length2; - ++result; - } else { - length = length1; - } - - while (length--) { - c1 = *(str1++); - c2 = *(str2++); - - if (c1 > c2) { - result = 1; - break; - } - if (c1 < c2) { - result = -1; - break; - } - } + + result = UnicodeBinaryCompare (str1, length1, str2, length2); } return result; } +#if CONFIG_HFS_STD /* * Compare two standard HFS catalog keys * @@ -3500,6 +3885,7 @@ CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey) return result; } +#endif /* @@ -3545,9 +3931,14 @@ static int buildkey(struct hfsmount *hfsmp, struct cat_desc *descp, HFSPlusCatalogKey *key, int retry) { + int std_hfs = (hfsmp->hfs_flags & HFS_STANDARD); int utf8_flags = UTF_ESCAPE_ILLEGAL; int result = 0; size_t unicodeBytes = 0; + + if (std_hfs == 0) { + retry = 0; + } if (descp->cd_namelen == 0 || descp->cd_nameptr[0] == '\0') return (EINVAL); /* invalid name */ @@ -3571,12 +3962,13 @@ buildkey(struct hfsmount *hfsmp, struct cat_desc *descp, return (result); } +#if CONFIG_HFS_STD /* * For HFS volumes convert to an HFS compatible key * * XXX need to save the encoding that succeeded */ - if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) { + if (std_hfs) { HFSCatalogKey hfskey; bzero(&hfskey, sizeof(hfskey)); @@ -3598,6 +3990,8 @@ buildkey(struct hfsmount *hfsmp, struct cat_desc *descp, } bcopy(&hfskey, key, sizeof(hfskey)); } +#endif + return (0); } @@ -3646,7 +4040,7 @@ cat_resolvelink(struct hfsmount *hfsmp, u_int32_t linkref, int isdirlink, struct if (recp->hl_linkCount == 0) recp->hl_linkCount = 2; } else { - printf("hfs: cat_resolvelink: can't find %s\n", inodename); + printf("hfs: cat_resolvelink: can't find inode=%s on vol=%s\n", inodename, hfsmp->vcbVN); } FREE(iterator, M_TEMP); @@ -3709,12 +4103,15 @@ getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key) /* Turn thread record into a cnode key (in place) */ switch (recp->recordType) { + +#if CONFIG_HFS_STD case kHFSFileThreadRecord: case kHFSFolderThreadRecord: keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6); keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0]; bcopy(keyp, key, keyp->hfs.keyLength + 1); break; +#endif case kHFSPlusFileThreadRecord: case kHFSPlusFolderThreadRecord: @@ -3792,27 +4189,7 @@ buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding int type = attrp->ca_mode & S_IFMT; u_int32_t createtime = to_hfs_time(attrp->ca_itime); - if (std_hfs) { - createtime = UTCToLocal(createtime); - if (type == S_IFDIR) { - bzero(crp, sizeof(HFSCatalogFolder)); - crp->recordType = kHFSFolderRecord; - crp->hfsFolder.folderID = cnid; - crp->hfsFolder.createDate = createtime; - crp->hfsFolder.modifyDate = createtime; - bcopy(attrp->ca_finderinfo, &crp->hfsFolder.userInfo, 32); - *recordSize = sizeof(HFSCatalogFolder); - } else { - bzero(crp, sizeof(HFSCatalogFile)); - crp->recordType = kHFSFileRecord; - crp->hfsFile.fileID = cnid; - crp->hfsFile.createDate = createtime; - crp->hfsFile.modifyDate = createtime; - bcopy(attrp->ca_finderinfo, &crp->hfsFile.userInfo, 16); - bcopy(&attrp->ca_finderinfo[16], &crp->hfsFile.finderInfo, 16); - *recordSize = sizeof(HFSCatalogFile); - } - } else { + if (std_hfs == 0) { struct HFSPlusBSDInfo * bsdp = NULL; if (type == S_IFDIR) { @@ -3860,6 +4237,30 @@ buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding bsdp->adminFlags = attrp->ca_flags >> 16; bsdp->ownerFlags = attrp->ca_flags & 0x000000FF; } +#if CONFIG_HFS_STD + else { + createtime = UTCToLocal(createtime); + if (type == S_IFDIR) { + bzero(crp, sizeof(HFSCatalogFolder)); + crp->recordType = kHFSFolderRecord; + crp->hfsFolder.folderID = cnid; + crp->hfsFolder.createDate = createtime; + crp->hfsFolder.modifyDate = createtime; + bcopy(attrp->ca_finderinfo, &crp->hfsFolder.userInfo, 32); + *recordSize = sizeof(HFSCatalogFolder); + } else { + bzero(crp, sizeof(HFSCatalogFile)); + crp->recordType = kHFSFileRecord; + crp->hfsFile.fileID = cnid; + crp->hfsFile.createDate = createtime; + crp->hfsFile.modifyDate = createtime; + bcopy(attrp->ca_finderinfo, &crp->hfsFile.userInfo, 16); + bcopy(&attrp->ca_finderinfo[16], &crp->hfsFile.finderInfo, 16); + *recordSize = sizeof(HFSCatalogFile); + } + } +#endif + } @@ -4029,6 +4430,7 @@ getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct bcopy(&crp->userInfo, attrp->ca_finderinfo, 32); } +#if CONFIG_HFS_STD /* * promotekey - promote hfs key to hfs plus key * @@ -4143,6 +4545,7 @@ promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlus crp->accessDate = crp->contentModDate; bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo)); } +#endif /* * Build a catalog node thread record from a catalog key @@ -4153,20 +4556,7 @@ buildthread(void *keyp, void *recp, int std_hfs, int directory) { int size = 0; - if (std_hfs) { - HFSCatalogKey *key = (HFSCatalogKey *)keyp; - HFSCatalogThread *rec = (HFSCatalogThread *)recp; - - size = sizeof(HFSCatalogThread); - bzero(rec, size); - if (directory) - rec->recordType = kHFSFolderThreadRecord; - else - rec->recordType = kHFSFileThreadRecord; - rec->parentID = key->parentID; - bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1); - - } else /* HFS+ */ { + if (std_hfs == 0) { HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)keyp; HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)recp; @@ -4180,11 +4570,28 @@ buildthread(void *keyp, void *recp, int std_hfs, int directory) bcopy(&key->nodeName, &rec->nodeName, sizeof(UniChar) * (key->nodeName.length + 1)); - /* HFS Plus has varaible sized thread records */ + /* HFS Plus has variable sized thread records */ size -= (sizeof(rec->nodeName.unicode) - (rec->nodeName.length * sizeof(UniChar))); + } - +#if CONFIG_HFS_STD + else { + HFSCatalogKey *key = (HFSCatalogKey *)keyp; + HFSCatalogThread *rec = (HFSCatalogThread *)recp; + + size = sizeof(HFSCatalogThread); + bzero(rec, size); + if (directory) + rec->recordType = kHFSFolderThreadRecord; + else + rec->recordType = kHFSFileThreadRecord; + rec->parentID = key->parentID; + bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1); + + } +#endif + return (size); } @@ -4194,16 +4601,20 @@ buildthread(void *keyp, void *recp, int std_hfs, int directory) static void buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key) { - if (std_hfs) { + if (std_hfs == 0) { + key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; + key->hfsPlus.parentID = parentID; + key->hfsPlus.nodeName.length = 0; + } +#if CONFIG_HFS_STD + else { key->hfs.keyLength = kHFSCatalogKeyMinimumLength; key->hfs.reserved = 0; key->hfs.parentID = parentID; key->hfs.nodeName[0] = 0; - } else { - key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; - key->hfsPlus.parentID = parentID; - key->hfsPlus.nodeName.length = 0; } +#endif + } /* @@ -4233,12 +4644,16 @@ getcnid(const CatalogRecord *crp) cnid_t cnid = 0; switch (crp->recordType) { + +#if CONFIG_HFS_STD case kHFSFolderRecord: cnid = crp->hfsFolder.folderID; break; case kHFSFileRecord: cnid = crp->hfsFile.fileID; break; +#endif + case kHFSPlusFolderRecord: cnid = crp->hfsPlusFolder.folderID; break; @@ -4246,7 +4661,7 @@ getcnid(const CatalogRecord *crp) cnid = crp->hfsPlusFile.fileID; break; default: - panic("hfs: getcnid: unknown recordType (crp @ %p)\n", crp); + printf("hfs: getcnid: unknown recordType=%d\n", crp->recordType); break; } @@ -4262,10 +4677,13 @@ getparentcnid(const CatalogRecord *recp) cnid_t cnid = 0; switch (recp->recordType) { + +#if CONFIG_HFS_STD case kHFSFileThreadRecord: case kHFSFolderThreadRecord: cnid = recp->hfsThread.parentID; break; +#endif case kHFSPlusFileThreadRecord: case kHFSPlusFolderThreadRecord: @@ -4285,8 +4703,16 @@ getparentcnid(const CatalogRecord *recp) static int isadir(const CatalogRecord *crp) { - return (crp->recordType == kHFSFolderRecord || - crp->recordType == kHFSPlusFolderRecord); + if (crp->recordType == kHFSPlusFolderRecord) { + return 1; + } +#if CONFIG_HFS_STD + if (crp->recordType == kHFSFolderRecord) { + return 1; + } +#endif + + return 0; } /* @@ -4352,13 +4778,13 @@ cat_lookup_dirlink(struct hfsmount *hfsmp, cnid_t dirlink_id, } 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); + hfs_mark_inconsistent(hfsmp, HFS_INCONSISTENCY_DETECTED); 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); + hfs_mark_inconsistent(hfsmp, HFS_INCONSISTENCY_DETECTED); error = ENOENT; } @@ -4385,3 +4811,4 @@ cat_update_dirlink(struct hfsmount *hfsmp, u_int8_t forktype, return cat_update_internal(hfsmp, true, descp, attrp, forkp, NULL); } } +