/*
- * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
-static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files, u_int32_t 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 */
+#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);
-static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
+int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp);
/* Internal catalog support routines */
static 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);
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);
+
+
+
+/* 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;
+
+}
-__private_extern__
int
cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, __unused proc_t p)
{
return MacToVFSError(result);
}
-__private_extern__
void
cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, __unused proc_t p)
{
hfs_systemfile_unlock(hfsmp, lockflags);
}
-
-__private_extern__
+__private_extern__
void
cat_convertattr(
struct hfsmount *hfsmp,
{
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;
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_volume_inconsistent (hfsmp);
+ 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;
}
* 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,
+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);
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) {
return (result);
}
-__private_extern__
int
cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
{
* catalog descriptor (when supplied outdescp is non-null).
*/
-__private_extern__
int
cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
{
/* 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;
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);
* 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,
+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;
/* 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, 0, outdescp, attrp, forkp, NULL);
+ result = cat_lookupbykey(hfsmp, keyp,
+ ((allow_system_files != 0) ? HFS_LOOKUP_SYSFILE : 0),
+ 0, wantrsrc, outdescp, attrp, forkp, NULL);
/* No corresponding file/folder record found for a thread record,
* mark the volume inconsistent.
*/
/*
* 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;
+ u_int8_t utf8[NAME_MAX + 1];
+ u_int32_t utf8len;
+ u_int16_t unicode[kHFSPlusMaxFileNameChars + 1];
+ size_t unicodelen;
if (wantrsrc)
return (ENOENT);
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);
* cat_lookupbykey - lookup a catalog node using a cnode key
*/
static int
-cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files, u_int32_t hint, int wantrsrc,
+cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t hint, int wantrsrc,
struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid)
{
struct BTreeIterator * iterator;
u_int32_t ilink = 0;
cnid_t cnid = 0;
u_int32_t encoding = 0;
+ cnid_t parentid = 0;
std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
if (result)
goto exit;
- /* Save the cnid and encoding now in case there's a hard link */
+ /* Save the cnid, parentid, and encoding now in case there's a hard link or inode */
cnid = getcnid(recp);
+ if (cnid == 0) {
+ /* CNID of 0 is invalid. Mark as corrupt */
+ hfs_mark_volume_inconsistent (hfsmp);
+ result = EINVAL;
+ goto exit;
+ }
+
+ if (std_hfs == 0) {
+ 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) {
-
- result = ENOENT;
+ !(flags & HFS_LOOKUP_SYSFILE)) {
+ 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.
if (!std_hfs
&& (attrp || forkp)
&& (recp->recordType == kHFSPlusFileRecord)
- && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->vcbCrDate) ||
+ && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_itime) ||
(to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_metadata_createdate))) {
int isdirlink = 0;
int isfilelink = 0;
(SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
isdirlink = 1;
}
- if (isfilelink || isdirlink) {
+ if ((isfilelink || isdirlink) && !(flags & HFS_LOOKUP_HARDLINK)) {
ilink = recp->hfsPlusFile.hl_linkReference;
(void) cat_resolvelink(hfsmp, ilink, isdirlink, (struct HFSPlusCatalogFile *)recp);
}
}
- if (attrp != NULL) {
- if (std_hfs) {
+ 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.
+ *
+ * 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 that was looked up and we do not end up
+ * reseting the hard link bit on it.
+ */
+ 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) {
+ 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 CONFIG_HFS_STD
+ else {
struct HFSPlusCatalogFile cnoderec;
promoteattr(hfsmp, recp, &cnoderec);
- getbsdattr(hfsmp, &cnoderec, attrp);
- } else {
- getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
- if (ilink)
- attrp->ca_linkref = ilink;
+ 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;
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;
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) {
* 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,
+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;
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;
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;
/* 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);
+ printf ("hfs: cat_create() failed to delete thread record id=%u on vol=%s\n", new_fileid, hfsmp->vcbVN);
hfs_mark_volume_inconsistent(hfsmp);
}
}
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);
* 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,
* 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 ||
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);
}
/*
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!
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 */
if (result)
goto exit;
+ /* Get the CNID after calling searchrecord */
+ cnid = getcnid (recp);
+ if (cnid == 0) {
+ hfs_mark_volume_inconsistent(hfsmp);
+ result = EINVAL;
+ goto exit;
+ }
+
if ((fromtype != recp->recordType) ||
- (from_cdp->cd_cnid != getcnid(recp))) {
+ (from_cdp->cd_cnid != cnid)) {
result = EEXIST;
goto exit; /* EEXIST */
}
int err;
err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
if (err) {
- printf("hfs: cat_create: could not undo (BTInsert = %d)", err);
+ printf("hfs: cat_create: could not undo (BTInsert = %d)\n", err);
hfs_mark_volume_inconsistent(hfsmp);
result = err;
goto exit;
int err;
err = BTDeleteRecord(fcb, to_iterator);
if (err) {
- printf("hfs: cat_create: could not undo (BTDelete = %d)", err);
+ printf("hfs: cat_create: could not undo (BTDelete = %d)\n", err);
hfs_mark_volume_inconsistent(hfsmp);
result = err;
goto exit;
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);
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);
* 2. BTDeleteRecord(thread);
* 3. BTUpdateRecord(parent);
*/
-__private_extern__
int
cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
{
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);
+ printf ("hfs: cat_delete() failed to delete thread record id=%u on vol=%s\n", cnid, hfsmp->vcbVN);
hfs_mark_volume_inconsistent(hfsmp);
}
}
/*
- * 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;
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;
* For open-deleted files we need to do a lookup by cnid
* (using thread rec).
*
- * For hard links, the target of the update is the inode
- * itself (not the link record) so a lookup by fileid
- * (i.e. thread rec) is needed.
+ * For hard links and if not requested by caller, the target
+ * of the update is the inode itself (not the link record)
+ * so a lookup by fileid (i.e. thread rec) is needed.
*/
- if ((descp->cd_cnid != attrp->ca_fileid) ||
- (descp->cd_namelen == 0) ||
- (attrp->ca_recflags & kHFSHasLinkChainMask)) {
+ if ((update_hardlink == false) &&
+ ((descp->cd_cnid != attrp->ca_fileid) ||
+ (descp->cd_namelen == 0) ||
+ (attrp->ca_recflags & kHFSHasLinkChainMask))) {
result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
} else {
result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
return MacToVFSError(result);
}
+/*
+ * cat_update - update the catalog node described by descp
+ * using the data from attrp and forkp.
+ */
+int
+cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
+ struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
+{
+ return cat_update_internal(hfsmp, false, descp, attrp, dataforkp, rsrcforkp);
+}
+
/*
* catrec_update - Update the fields of a catalog record
* This is called from within BTUpdateRecord.
struct cat_fork *forkp;
struct hfsmount *hfsmp;
long blksize;
- int i;
descp = state->s_desc;
attrp = state->s_attr;
blksize = HFSTOVCB(hfsmp)->blockSize;
switch (crp->recordType) {
+
+#if CONFIG_HFS_STD
case kHFSFolderRecord: {
HFSCatalogFolder *dir;
}
case kHFSFileRecord: {
HFSCatalogFile *file;
-
+ int i;
+
file = (struct HFSCatalogFile *)crp;
/* Do a quick sanity check */
if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
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;
}
case kHFSPlusFileRecord: {
HFSPlusCatalogFile *file;
+ int is_dirlink;
file = (struct HFSPlusCatalogFile *)crp;
/* Do a quick sanity check */
* supplied values (which will be default), which has the
* same effect as creating a new file while
* MNT_UNKNOWNPERMISSIONS is set.
+ *
+ * Do not modify bsdInfo for directory hard link records.
+ * They are set during creation and are not modifiable, so just
+ * leave them alone.
*/
- if ((file->bsdInfo.fileMode != 0) ||
- (attrp->ca_flags != 0) ||
- (attrp->ca_uid != hfsmp->hfs_uid) ||
- (attrp->ca_gid != hfsmp->hfs_gid) ||
- ((attrp->ca_mode & ALLPERMS) !=
- (hfsmp->hfs_file_mask & ACCESSPERMS))) {
+ is_dirlink = (file->flags & kHFSHasLinkChainMask) &&
+ (SWAP_BE32(file->userInfo.fdType) == kHFSAliasType) &&
+ (SWAP_BE32(file->userInfo.fdCreator) == kHFSAliasCreator);
+
+ if (!is_dirlink &&
+ ((file->bsdInfo.fileMode != 0) ||
+ (attrp->ca_flags != 0) ||
+ (attrp->ca_uid != hfsmp->hfs_uid) ||
+ (attrp->ca_gid != hfsmp->hfs_gid) ||
+ ((attrp->ca_mode & ALLPERMS) !=
+ (hfsmp->hfs_file_mask & ACCESSPERMS)))) {
if ((file->bsdInfo.fileMode == 0) ||
(((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
file->bsdInfo.ownerID = attrp->ca_uid;
/* 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;
}
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);
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;
}
/*
- * updatelink_callback - update a link's chain
+ * update_siblinglinks_callback - update a link's chain
*/
struct linkupdate_state {
};
static int
-updatelink_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state)
+update_siblinglinks_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state)
{
HFSPlusCatalogFile *file;
if (crp->recordType != kHFSPlusFileRecord) {
- printf("hfs: updatelink_callback: unexpected rec type %d\n", crp->recordType);
+ printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp->recordType);
return (btNotFound);
}
file->hl_nextLinkID = state->nextlinkid;
}
} else {
- printf("hfs: updatelink_callback: file %d isn't a chain\n", file->fileID);
+ printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file->fileID);
}
return (0);
}
/*
- * cat_updatelink - update a link's chain
+ * cat_update_siblinglinks - update a link's chain
*/
-__private_extern__
int
-cat_updatelink(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cnid_t nextlinkid)
+cat_update_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cnid_t nextlinkid)
{
FCB * fcb;
BTreeIterator * iterator;
state.prevlinkid = prevlinkid;
state.nextlinkid = nextlinkid;
- /* Borrow the btcb iterator since we have an exclusive catalog lock. */
- iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
- iterator->hint.nodeNum = 0;
-
+ /* Create an iterator for use by us temporarily */
+ MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ bzero(iterator, sizeof(*iterator));
+
result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key);
if (result == 0) {
- result = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr)updatelink_callback, &state);
+ result = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr)update_siblinglinks_callback, &state);
(void) BTFlushPath(fcb);
} else {
- printf("hfs: cat_updatelink: 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);
}
/*
* cat_lookuplink - lookup a link by it's name
*/
-__private_extern__
int
cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
{
fcb = hfsmp->hfs_catalog_cp->c_datafork;
- /* Borrow the btcb iterator since we have an exclusive catalog lock. */
- iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
- iterator->hint.nodeNum = 0;
+ /* Create an iterator for use by us temporarily */
+ MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ bzero(iterator, sizeof(*iterator));
if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
goto exit;
*nextlinkid = 0;
}
exit:
+ FREE(iterator, M_TEMP);
return MacToVFSError(result);
}
/*
- * cat_lookuplink - lookup a link by its cnid
+ * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
*/
-__private_extern__
int
-cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
+cat_lookup_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
{
FCB * fcb;
BTreeIterator * iterator;
fcb = hfsmp->hfs_catalog_cp->c_datafork;
- /* Borrow the btcb iterator since we have an exclusive catalog lock. */
- iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
- iterator->hint.nodeNum = 0;
-
+ /* Create an iterator for use by us temporarily */
+ MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ bzero(iterator, sizeof(*iterator));
+
if ((result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key))) {
- printf("hfs: cat_lookuplinkbyid: getkey for %d failed %d\n", linkfileid, result);
goto exit;
}
BDINIT(btdata, &file);
if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
- printf("hfs: cat_lookuplinkbyid: cannot find %d\n", linkfileid);
goto exit;
}
/* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
parent = ((HFSPlusCatalogKey *)&iterator->key)->parentID;
- /* ADL inodes don't have a chain (its in an EA) */
+ /* directory inodes don't have a chain (its in an EA) */
if (parent == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
result = ENOLINK; /* signal to caller to get head of list */
} else if (parent == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
*prevlinkid = file.hl_prevLinkID;
*nextlinkid = file.hl_nextLinkID;
}
- } else {
- *prevlinkid = 0;
- *nextlinkid = 0;
+ } 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;
+
+ 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;
+ }
}
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);
}
* 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)
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;
*/
nextCNID = hfsmp->vcbNxtCNID;
if (nextCNID == 0xFFFFFFFF) {
- HFS_MOUNT_LOCK(hfsmp, TRUE)
+ hfs_lock_mount (hfsmp);
hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
- HFS_MOUNT_UNLOCK(hfsmp, TRUE);
+ hfs_unlock_mount(hfsmp);
} else {
hfsmp->vcbNxtCNID++;
}
for (;;) {
buildthreadkey(nextCNID, 0, (CatalogKey *) &bto->iterator.key);
+
+ /*
+ * If the CNID wraparound bit is set, then we need to validate if there
+ * is a cnode in the hash already with this ID (even if it no longer exists
+ * on disk). If so, then just skip this ID and move on to the next one.
+ */
+ if (!std_hfs && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
+ /* Verify that the CNID does not already exist in the cnode hash... */
+ if (hfs_chash_snoop (hfsmp, nextCNID, 1, NULL, NULL) == 0) {
+ /* It was found in the cnode hash!*/
+ result = btExists;
+ }
+ }
+
+ if (result == 0) {
+ result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
+ }
- result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
if ((result == btExists) && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
/*
* Allow CNIDs on HFS Plus volumes to wrap around
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)) {
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;
/*
* cat_deletelink - delete a link from the catalog
*/
-__private_extern__
int
cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp)
{
switch(rec->recordType) {
case kHFSPlusFolderRecord:
case kHFSPlusFileRecord:
+#if CONFIG_HFS_STD
case kHFSFolderRecord:
case kHFSFileRecord:
+#endif
if (parentcnid != state->dir_cnid) {
state->error = ENOENT;
return (0); /* stop */
}
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;
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);
}
*
* Note: index is zero relative
*/
-__private_extern__
int
cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list)
{
/* 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;
* 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) {
+
+ 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
*/
}
}
- 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 {
} 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 */
}
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 ".."
*
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;
}
/* 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 */
uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
}
+#if CONFIG_HFS_STD
/*
* getdirentries callback for standard HFS (non HFS+) directories.
*/
}
nameptr = (u_int8_t *)&catent.d_name[0];
- maxnamelen = NAME_MAX;
+ maxnamelen = sizeof(catent.d_name);
switch(crp->recordType) {
case kHFSFolderRecord:
};
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;
/* 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
*/
-__private_extern__
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;
int result;
int index;
int have_key;
-
+ int extended;
+
+ extended = flags & VNODE_READDIR_EXTENDED;
+
if (extended && (hfsmp->hfs_flags & HFS_STANDARD)) {
return (ENOTSUP);
}
/*
* 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);
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;
* 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;
/*
* 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);
* 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;
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;
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;
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) {
switch(crp->recordType) {
case kHFSPlusFolderRecord:
case kHFSPlusFileRecord:
+#if CONFIG_HFS_STD
case kHFSFolderRecord:
case kHFSFileRecord:
+#endif
++state->count;
break;
default:
* 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)
{
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
*
return result;
}
+#endif
/*
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 */
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));
}
bcopy(&hfskey, key, sizeof(hfskey));
}
+#endif
+
return (0);
}
/*
* Resolve hard link reference to obtain the inode record.
*/
-__private_extern__
int
cat_resolvelink(struct hfsmount *hfsmp, u_int32_t linkref, int isdirlink, struct HFSPlusCatalogFile *recp)
{
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);
/* 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:
* The key's parent id is the only part of the key expected to be used by the caller.
* The name portion of the key may not always be valid (ie in the case of a hard link).
*/
-__private_extern__
int
cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp)
{
* Pick up the first link in the chain and get a descriptor for it.
* This allows blind bulk access checks to work for hardlinks.
*/
- if ((cat_lookuplinkbyid(hfsmp, cnid, &prevlinkid, &nextlinkid) == 0) &&
+ if ((cat_lookup_siblinglinks(hfsmp, cnid, &prevlinkid, &nextlinkid) == 0) &&
(nextlinkid != 0)) {
if (cat_findname(hfsmp, nextlinkid, &linkdesc) == 0) {
key->hfsPlus.parentID = linkdesc.cd_parentcnid;
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) {
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
+
}
bcopy(&crp->userInfo, attrp->ca_finderinfo, 32);
}
+#if CONFIG_HFS_STD
/*
* promotekey - promote hfs key to hfs plus key
*
crp->accessDate = crp->contentModDate;
bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo));
}
+#endif
/*
* Build a catalog node thread record from a catalog key
{
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;
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);
}
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
+
}
/*
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;
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;
}
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:
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;
+}
+
+/*
+ * 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);
+ }
}