/*
- * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
*
- * @APPLE_LICENSE_HEADER_START@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License"). You may not use this file except in compliance with the
- * License. Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
*
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
*
- * @APPLE_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#include <sys/systm.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/vnode.h>
-#include <sys/namei.h>
#include <sys/dirent.h>
#include <vfs/vfs_support.h>
#include <libkern/libkern.h>
#include "hfs_endian.h"
#include "hfscommon/headers/BTreesInternal.h"
-#include "hfscommon/headers/CatalogPrivate.h"
+#include "hfscommon/headers/BTreesPrivate.h"
#include "hfscommon/headers/HFSUnicodeWrappers.h"
-extern OSErr PositionIterator(CatalogIterator *cip, UInt32 offset, BTreeIterator *bip, UInt16 *op);
/*
* Initialization of an FSBufferDescriptor structure.
struct hfsmount * s_hfsmp;
};
+struct position_state {
+ int error;
+ u_int32_t count;
+ u_int32_t index;
+ u_int32_t parentID;
+ struct hfsmount *hfsmp;
+};
-static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantrsrc,
- struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp);
-
-static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
- struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp);
+/* Map file mode type to directory entry types */
+u_char modetodirtype[16] = {
+ DT_REG, DT_FIFO, DT_CHR, DT_UNKNOWN,
+ DT_DIR, DT_UNKNOWN, DT_BLK, DT_UNKNOWN,
+ DT_REG, DT_UNKNOWN, DT_LNK, DT_UNKNOWN,
+ DT_SOCK, DT_UNKNOWN, DT_WHT, DT_UNKNOWN
+};
+#define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
-extern int mac_roman_to_unicode(const Str31 hfs_str, UniChar *uni_str,
- UInt32 maxCharLen, UInt32 *unicodeChars);
-extern int unicode_to_hfs(ExtendedVCB *vcb, ByteCount srcLen,
- const u_int16_t* srcStr, Str31 dstStr, int retry);
+#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);
+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 */
-int resolvelink(struct hfsmount *hfsmp, u_long linkref, struct HFSPlusCatalogFile *recp);
+static int cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
+ struct position_state *state);
+
+static int resolvelinkid(struct hfsmount *hfsmp, u_int32_t linkref, ino_t *ino);
static int getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key);
static void buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key);
-static void buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding, CatalogRecord *crp, int *recordSize);
+static void buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding, CatalogRecord *crp, u_int32_t *recordSize);
-static int catrec_update(const CatalogKey *ckp, CatalogRecord *crp, u_int16_t reclen, struct update_state *state);
+static int catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state);
-static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
+static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_int32_t hint, u_int32_t encoding,
int isdir, struct cat_desc *descp);
static void getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp);
-static void promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey, HFSPlusCatalogKey *keyp, u_long *encoding);
+#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_long getencoding(const CatalogRecord *crp);
+static u_int32_t getencoding(const CatalogRecord *crp);
static cnid_t getparentcnid(const CatalogRecord *recp);
static int isadir(const CatalogRecord *crp);
static int 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);
+
+
+
+/* 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)
+{
+ int lockflags = 0;
+ int result;
+
+ if (hfsmp->hfs_catalog_cp->c_lockowner != current_thread())
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+
+ result = BTReserveSpace(hfsmp->hfs_catalog_cp->c_datafork, ops, (void*)cookie);
+
+ if (lockflags)
+ hfs_systemfile_unlock(hfsmp, lockflags);
+
+ return MacToVFSError(result);
+}
+
+void
+cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, __unused proc_t p)
+{
+ int lockflags = 0;
+
+ if (hfsmp->hfs_catalog_cp->c_lockowner != current_thread())
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+
+ (void) BTReleaseReserve(hfsmp->hfs_catalog_cp->c_datafork, (void*)cookie);
+
+ if (lockflags)
+ hfs_systemfile_unlock(hfsmp, lockflags);
+}
+
+__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 {
- bcopy(&recp->hfsPlusFile.dataFork, datafp, sizeof(*datafp));
- bcopy(&recp->hfsPlusFile.resourceFork, rsrcfp, sizeof(*rsrcfp));
+ }
+#endif
+ else {
+ /* Convert the data fork. */
+ datafp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
+ datafp->cf_new_size = 0;
+ datafp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
+ if ((hfsmp->hfc_stage == HFC_RECORDING) &&
+ (attrp->ca_atime >= hfsmp->hfc_timebase)) {
+ datafp->cf_bytesread =
+ recp->hfsPlusFile.dataFork.clumpSize *
+ HFSTOVCB(hfsmp)->blockSize;
+ } else {
+ datafp->cf_bytesread = 0;
+ }
+ datafp->cf_vblocks = 0;
+ bcopy(&recp->hfsPlusFile.dataFork.extents[0],
+ &datafp->cf_extents[0], sizeof(HFSPlusExtentRecord));
+
+ /* Convert the resource fork. */
+ rsrcfp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
+ rsrcfp->cf_new_size = 0;
+ rsrcfp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
+ if ((hfsmp->hfc_stage == HFC_RECORDING) &&
+ (attrp->ca_atime >= hfsmp->hfc_timebase)) {
+ datafp->cf_bytesread =
+ recp->hfsPlusFile.resourceFork.clumpSize *
+ HFSTOVCB(hfsmp)->blockSize;
+ } else {
+ datafp->cf_bytesread = 0;
+ }
+ rsrcfp->cf_vblocks = 0;
+ bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
+ &rsrcfp->cf_extents[0], sizeof(HFSPlusExtentRecord));
}
}
+/*
+ * Convert a raw catalog key and record into an in-core catalog descriptor.
+ *
+ * Note: The caller is responsible for releasing the catalog descriptor.
+ */
+__private_extern__
int
cat_convertkey(
struct hfsmount *hfsmp,
{
int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
HFSPlusCatalogKey * pluskey = NULL;
- u_long encoding;
-
- if (std_hfs) {
- MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
- promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
+ u_int32_t encoding;
+ cnid_t cnid = 0;
+ int err = 0;
- } 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;
}
/*
* cat_releasedesc
*/
+__private_extern__
void
cat_releasedesc(struct cat_desc *descp)
{
- char * name;
+ const u_int8_t * name;
if (descp == NULL)
return;
name = descp->cd_nameptr;
descp->cd_nameptr = NULL;
descp->cd_namelen = 0;
- descp->cd_flags &= ~CD_HASBUF;
- FREE(name, M_TEMP);
+ vfs_removename((const char *)name);
}
descp->cd_nameptr = NULL;
descp->cd_namelen = 0;
+ descp->cd_flags &= ~CD_HASBUF;
}
/*
* These Catalog functions allow access to the HFS Catalog (database).
- * The catalog b-tree lock must be aquired before calling any of these routines.
+ * The catalog b-tree lock must be acquired before calling any of these routines.
*/
/*
- * cat_lookup - lookup a catalog node using a cnode decriptor
+ * cat_lookup - lookup a catalog node using a cnode descriptor
+ *
+ * Note: The caller is responsible for releasing the output
+ * 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)
+ 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, descp->cd_hint, wantrsrc, outdescp, attrp, forkp);
+ result = cat_lookupbykey(hfsmp, keyp, flags, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid);
if (result == ENOENT) {
if (!std_hfs) {
+ struct cat_desc temp_desc;
+ if (outdescp == NULL) {
+ bzero(&temp_desc, sizeof(temp_desc));
+ outdescp = &temp_desc;
+ }
result = cat_lookupmangled(hfsmp, descp, wantrsrc, outdescp, attrp, forkp);
+ if (desc_cnid) {
+ *desc_cnid = outdescp->cd_cnid;
+ }
+ if (outdescp == &temp_desc) {
+ /* Release the local copy of desc */
+ cat_releasedesc(outdescp);
+ }
} else if (hfsmp->hfs_encoding != kTextEncodingMacRoman) {
// make MacRoman key from utf-8
// result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
struct BTreeIterator *iterator;
struct FSBufferDescriptor file_data;
struct HFSCatalogFile file_rec;
- UInt16 datasize;
+ u_int16_t datasize;
FCB *fcb;
int result;
if (result)
goto exit;
- // XXXdbg - preflight all btree operations to make sure there's enough space
- result = BTCheckFreeSpace(fcb);
- if (result)
- goto exit;
-
BDINIT(file_data, &file_rec);
result = BTSearchRecord(fcb, &iterator[0], &file_data, &datasize, &iterator[0]);
if (result)
}
+/*
+ * cat_findname - obtain a descriptor from cnid
+ *
+ * Only a thread lookup is performed.
+ *
+ * Note: The caller is responsible for releasing the output
+ * catalog descriptor (when supplied outdescp is non-null).
+
+ */
+int
+cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
+{
+ struct BTreeIterator * iterator;
+ FSBufferDescriptor btdata;
+ CatalogKey * keyp;
+ CatalogRecord * recp;
+ int isdir;
+ int result;
+ int std_hfs;
+
+ isdir = 0;
+ std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
+
+ MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
+ iterator->hint.nodeNum = 0;
+
+ MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
+ BDINIT(btdata, recp);
+
+ result = BTSearchRecord(VTOF(hfsmp->hfs_catalog_vp), iterator, &btdata, NULL, NULL);
+ if (result)
+ goto exit;
+
+ /* Turn thread record into a cnode key (in place). */
+ switch (recp->recordType) {
+
+#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 == 0) {
+ builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp);
+ }
+#if CONFIG_HFS_STD
+ else {
+ HFSPlusCatalogKey * pluskey = NULL;
+ u_int32_t encoding;
+
+ MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
+ promotekey(hfsmp, &keyp->hfs, pluskey, &encoding);
+ builddesc(pluskey, cnid, 0, encoding, isdir, outdescp);
+ FREE(pluskey, M_TEMP);
+ }
+#endif
+
+exit:
+ FREE(recp, M_TEMP);
+ FREE(iterator, M_TEMP);
+
+ return MacToVFSError(result);
+}
+
/*
* cat_idlookup - lookup a catalog node using a cnode id
+ *
+ * Note: The caller is responsible for releasing the output
+ * catalog descriptor (when supplied outdescp is non-null).
*/
int
-cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp,
- struct cat_attr *attrp, struct cat_fork *forkp)
+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;
FSBufferDescriptor btdata;
- UInt16 datasize;
+ u_int16_t datasize;
CatalogKey * keyp;
CatalogRecord * recp;
int result;
/* Turn thread record into a cnode key (in place) */
switch (recp->recordType) {
- case kHFSFileThreadRecord:
- case kHFSFolderThreadRecord:
- keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
- keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
- break;
- case kHFSPlusFileThreadRecord:
- case kHFSPlusFolderThreadRecord:
- keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
- keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
- (keyp->hfsPlus.nodeName.length * 2);
- break;
+#if CONFIG_HFS_STD
+ case kHFSFileThreadRecord:
+ case kHFSFolderThreadRecord:
+ keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
- default:
- result = ENOENT;
- goto exit;
+ /* check for NULL name */
+ if (keyp->hfs.nodeName[0] == 0) {
+ result = ENOENT;
+ goto exit;
+ }
+
+ keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
+ break;
+#endif
+
+ case kHFSPlusFileThreadRecord:
+ case kHFSPlusFolderThreadRecord:
+ keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
+
+ /* check for NULL name */
+ if (keyp->hfsPlus.nodeName.length == 0) {
+ result = ENOENT;
+ goto exit;
+ }
+
+ keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
+ (keyp->hfsPlus.nodeName.length * 2);
+ break;
+
+ default:
+ result = ENOENT;
+ goto exit;
}
- result = cat_lookupbykey(hfsmp, keyp, 0, 0, outdescp, attrp, forkp);
+ 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.
+ */
+ if (result == 0 && outdescp) {
+ cnid_t dcnid = outdescp->cd_cnid;
+ /*
+ * Just for sanity's case, let's make sure that
+ * the key in the thread matches the key in the record.
+ */
+ if (cnid != dcnid) {
+ printf("hfs: cat_idlookup: Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid, cnid, dcnid, dcnid);
+ result = ENOENT;
+ }
+ }
exit:
FREE(recp, M_TEMP);
FREE(iterator, M_TEMP);
/*
* 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;
- int prefixlen;
+ u_int32_t prefixlen;
int result;
+ u_int8_t utf8[NAME_MAX + 1];
+ u_int32_t utf8len;
+ u_int16_t unicode[kHFSPlusMaxFileNameChars + 1];
+ size_t unicodelen;
if (wantrsrc)
return (ENOENT);
fileID = GetEmbeddedFileID(descp->cd_nameptr, descp->cd_namelen, &prefixlen);
- if (fileID < kHFSFirstUserCatalogNodeID)
+ if (fileID < (cnid_t)kHFSFirstUserCatalogNodeID)
return (ENOENT);
- result = cat_idlookup(hfsmp, fileID, outdescp, attrp, forkp);
- if (result)
+ if (fileID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
+ fileID == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid ||
+ fileID == hfsmp->hfs_jnlfileid ||
+ fileID == hfsmp->hfs_jnlinfoblkid) {
return (ENOENT);
+ }
+ 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 ((outdescp->cd_namelen < prefixlen) ||
- bcmp(outdescp->cd_nameptr, descp->cd_nameptr, prefixlen-6) != 0)
+ /*
+ * 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;
+ }
+ 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, u_long hint, int wantrsrc,
- struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp)
+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;
FSBufferDescriptor btdata;
CatalogRecord * recp;
- UInt16 datasize;
+ u_int16_t datasize;
int result;
int std_hfs;
- u_long ilink = 0;
+ u_int32_t ilink = 0;
cnid_t cnid = 0;
- u_long encoding = 0;
+ u_int32_t encoding = 0;
+ cnid_t parentid = 0;
std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
if (result)
goto exit;
- /* Save the cnid 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 &&
- ((cnid == hfsmp->hfs_jnlfileid) ||
- (cnid == hfsmp->hfs_jnlinfoblkid))) {
-
- result = ENOENT;
+ 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 = 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
+ * When a hardlink link is encountered, auto resolve it.
+ *
+ * The catalog record will change, and possibly its type.
*/
if (!std_hfs
&& (attrp || forkp)
&& (recp->recordType == kHFSPlusFileRecord)
- && (SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType)
- && (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)
- && ((to_bsd_time(recp->hfsPlusFile.createDate) == HFSTOVCB(hfsmp)->vcbCrDate) ||
- (to_bsd_time(recp->hfsPlusFile.createDate) == hfsmp->hfs_metadata_createdate))) {
-
- ilink = recp->hfsPlusFile.bsdInfo.special.iNodeNum;
-
- (void) resolvelink(hfsmp, ilink, (struct HFSPlusCatalogFile *)recp);
+ && ((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;
+
+ if ((SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
+ (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) {
+ isfilelink = 1;
+ } else if ((recp->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
+ (SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
+ (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
+ isdirlink = 1;
+ }
+ 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_rdev = ilink;
+ getbsdattr(hfsmp, &cnoderec, attrp);
}
+#endif
}
if (forkp != NULL) {
- if (isadir(recp))
+ 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)
- bcopy(&recp->hfsPlusFile.resourceFork, forkp, sizeof(*forkp));
- else
- bcopy(&recp->hfsPlusFile.dataFork, forkp, sizeof(*forkp));
+ }
+#endif
+ else if (wantrsrc) {
+ /* Convert the resource fork. */
+ forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
+ forkp->cf_new_size = 0;
+ forkp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
+ if ((hfsmp->hfc_stage == HFC_RECORDING) &&
+ (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
+ forkp->cf_bytesread =
+ recp->hfsPlusFile.resourceFork.clumpSize *
+ HFSTOVCB(hfsmp)->blockSize;
+ } else {
+ forkp->cf_bytesread = 0;
+ }
+ forkp->cf_vblocks = 0;
+ bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
+ &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
+ } else {
+ int i;
+ u_int32_t validblks;
+
+ /* Convert the data fork. */
+ forkp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
+ forkp->cf_new_size = 0;
+ forkp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
+ if ((hfsmp->hfc_stage == HFC_RECORDING) &&
+ (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
+ forkp->cf_bytesread =
+ recp->hfsPlusFile.dataFork.clumpSize *
+ HFSTOVCB(hfsmp)->blockSize;
+ } else {
+ forkp->cf_bytesread = 0;
+ }
+ forkp->cf_vblocks = 0;
+ bcopy(&recp->hfsPlusFile.dataFork.extents[0],
+ &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
+
+ /* Validate the fork's resident extents. */
+ validblks = 0;
+ for (i = 0; i < kHFSPlusExtentDensity; ++i) {
+ if (forkp->cf_extents[i].startBlock + forkp->cf_extents[i].blockCount >= hfsmp->totalBlocks) {
+ /* Suppress any bad extents so a remove can succeed. */
+ forkp->cf_extents[i].startBlock = 0;
+ forkp->cf_extents[i].blockCount = 0;
+ /* Disable writes */
+ if (attrp != NULL) {
+ attrp->ca_mode &= S_IFMT | S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ } else {
+ validblks += forkp->cf_extents[i].blockCount;
+ }
+ }
+ /* Adjust for any missing blocks. */
+ 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;
+ }
+ psize = (off_t)validblks * (off_t)hfsmp->blockSize;
+ if (psize < forkp->cf_size) {
+ forkp->cf_size = psize;
+ }
+
+ }
+ }
}
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) {
+ *desc_cnid = cnid;
}
exit:
FREE(iterator, M_TEMP);
/*
* cat_create - create a node in the catalog
+ *
+ * NOTE: both the catalog file and attribute file locks must
+ * be held before calling this function.
+ *
+ * The caller is responsible for releasing the output
+ * 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)
{
- ExtendedVCB * vcb;
FCB * fcb;
struct btobj * bto;
FSBufferDescriptor btdata;
- u_int32_t nextCNID;
u_int32_t datalen;
int std_hfs;
- int result;
- u_long encoding;
+ int result = 0;
+ u_int32_t encoding = kTextEncodingMacRoman;
int modeformat;
modeformat = attrp->ca_mode & S_IFMT;
- vcb = HFSTOVCB(hfsmp);
- fcb = GetFileControlBlock(vcb->catalogRefNum);
- nextCNID = vcb->vcbNxtCNID;
- std_hfs = (vcb->vcbSigWord == kHFSSigWord);
-
- if (std_hfs && nextCNID == 0xFFFFFFFF)
- return (ENOSPC);
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+ std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
+ /* 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);
- bzero(bto, sizeof(struct btobj));
+ bto->iterator.hint.nodeNum = 0;
result = buildkey(hfsmp, descp, &bto->key, 0);
if (result)
hfs_setencodingbits(hfsmp, encoding);
}
- // XXXdbg - preflight all btree operations to make sure there's enough space
- result = BTCheckFreeSpace(fcb);
- if (result)
- goto exit;
-
/*
* Insert the thread record first
*/
btdata.itemSize = datalen;
btdata.itemCount = 1;
- for (;;) {
- buildthreadkey(nextCNID, std_hfs, (CatalogKey *) &bto->iterator.key);
-
- result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
- if (result == btExists && !std_hfs) {
- /*
- * Allow CNIDs on HFS Plus volumes to wrap around
- */
- ++nextCNID;
- if (nextCNID < kHFSFirstUserCatalogNodeID) {
- vcb->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
- vcb->vcbFlags |= 0xFF00;
- nextCNID = kHFSFirstUserCatalogNodeID;
- }
- continue;
- }
- break;
+ /* 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;
}
- 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);
- (void) BTDeleteRecord(fcb, &bto->iterator);
+ 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 id=%u on vol=%s\n", new_fileid, hfsmp->vcbVN);
+ hfs_mark_volume_inconsistent(hfsmp);
+ }
}
goto exit;
}
/*
- * Insert was Successfull, update name, parent and volume
+ * Insert was successful, update name, parent and volume
*/
-
-
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);
}
- }
- attrp->ca_fileid = nextCNID;
+#endif
- /* Update parent stats */
- TrashCatalogIterator(vcb, descp->cd_parentcnid);
-
- /* Update volume stats */
- if (++nextCNID < kHFSFirstUserCatalogNodeID) {
- vcb->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
- nextCNID = kHFSFirstUserCatalogNodeID;
}
- vcb->vcbNxtCNID = nextCNID;
- vcb->vcbFlags |= 0xFF00;
+ attrp->ca_fileid = new_fileid;
exit:
(void) BTFlushPath(fcb);
* 3. BTDeleteRecord(from_cnode);
* 4. BTDeleteRecord(from_thread);
* 5. BTInsertRecord(to_thread);
+ *
+ * Note: The caller is responsible for releasing the output
+ * catalog descriptor (when supplied out_cdp is non-null).
*/
int
cat_rename (
HFSPlusCatalogKey * to_key;
ExtendedVCB * vcb;
FCB * fcb;
- UInt16 datasize;
+ u_int16_t datasize;
int result = 0;
int sourcegone = 0;
int skipthread = 0;
int directory = from_cdp->cd_flags & CD_ISDIR;
+ int is_dirlink = 0;
int std_hfs;
- u_long encoding = 0;
+ u_int32_t encoding = 0;
vcb = HFSTOVCB(hfsmp);
fcb = GetFileControlBlock(vcb->catalogRefNum);
if ((result = buildkey(hfsmp, to_cdp, (HFSPlusCatalogKey *)&to_iterator->key, 0)))
goto exit;
- // XXXdbg - preflight all btree operations to make sure there's enough space
- result = BTCheckFreeSpace(fcb);
- if (result)
- goto exit;
-
to_key = (HFSPlusCatalogKey *)&to_iterator->key;
MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
BDINIT(btdata, recp);
* When moving a directory, make sure its a valid move.
*/
if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)) {
- struct BTreeIterator iterator = {0};
+ 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;
}
-
+ /* 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));
+
/*
- * Traverese destination path all the way back to the root
+ * 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) {
+ if (pathcnid == cnid || pathcnid == 0) {
result = EINVAL;
+ FREE(dir_iterator, M_TEMP);
goto exit;
}
}
+ FREE(dir_iterator, M_TEMP);
}
/*
*/
result = BTSearchRecord(fcb, from_iterator, &btdata,
&datasize, from_iterator);
- if (result)
- goto exit;
+ if (result) {
+ if (std_hfs || (result != btNotFound))
+ goto exit;
- /* Update the text encoding (on disk and in descriptor */
- if (!std_hfs) {
- encoding = hfs_pickencoding(to_key->nodeName.unicode,
- to_key->nodeName.length);
+ struct cat_desc temp_desc;
+
+ /* Probably the node has mangled name */
+ result = cat_lookupmangled(hfsmp, from_cdp, 0, &temp_desc, NULL, NULL);
+ if (result)
+ goto exit;
+
+ /* The file has mangled name. Search the cnode data using full name */
+ bzero(from_iterator, sizeof(*from_iterator));
+ result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&from_iterator->key, 0);
+ if (result) {
+ cat_releasedesc(&temp_desc);
+ goto exit;
+ }
+
+ result = BTSearchRecord(fcb, from_iterator, &btdata, &datasize, from_iterator);
+ if (result) {
+ cat_releasedesc(&temp_desc);
+ goto exit;
+ }
+
+ cat_releasedesc(&temp_desc);
+ }
+
+ /* Check if the source is directory hard link. We do not change
+ * directory flag because it is later used to initialize result descp
+ */
+ if ((!std_hfs) &&
+ (directory) &&
+ (recp->recordType == kHFSPlusFileRecord) &&
+ (recp->hfsPlusFile.flags & kHFSHasLinkChainMask)) {
+ is_dirlink = 1;
+ }
+
+ /*
+ * Update the text encoding (on disk and in descriptor).
+ *
+ * Note that hardlink inodes don't require a text encoding hint.
+ */
+ if (!std_hfs &&
+ todir_cdp->cd_parentcnid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid &&
+ todir_cdp->cd_parentcnid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
+ encoding = hfs_pickencoding(to_key->nodeName.unicode, to_key->nodeName.length);
hfs_setencodingbits(hfsmp, encoding);
recp->hfsPlusFile.textEncoding = encoding;
if (out_cdp)
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!
goto exit;
#endif
- /* Trash the iterator caches */
- TrashCatalogIterator(vcb, from_cdp->cd_parentcnid);
- if (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)
- TrashCatalogIterator(vcb, to_cdp->cd_parentcnid);
-
/* Step 2: Insert cnode at new location */
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 */
/* Find cnode data at new location */
result = BTSearchRecord(fcb, to_iterator, &btdata, &datasize, NULL);
+ 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 */
-
+ }
/* The old name is a case variant and must be removed */
result = BTDeleteRecord(fcb, from_iterator);
if (result)
{
int err;
err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
- if (err)
- panic("cat_create: could not undo (BTInsert = %d)", err);
+ if (err) {
+ printf("hfs: cat_create: could not undo (BTInsert = %d)\n", err);
+ hfs_mark_volume_inconsistent(hfsmp);
+ result = err;
+ goto exit;
+ }
}
#else
(void) BTInsertRecord(fcb, from_iterator, &btdata, datasize);
{
int err;
err = BTDeleteRecord(fcb, to_iterator);
- if (err)
- panic("cat_create: could not undo (BTDelete = %d)", err);
+ if (err) {
+ printf("hfs: cat_create: could not undo (BTDelete = %d)\n", err);
+ hfs_mark_volume_inconsistent(hfsmp);
+ result = err;
+ goto exit;
+ }
}
#else
(void) BTDeleteRecord(fcb, to_iterator);
* (optional for HFS files)
*/
if (!skipthread) {
- datasize = buildthread(&to_iterator->key, recp, std_hfs, directory);
+ /* For directory hard links, always create a file thread
+ * record. For everything else, use the directory flag.
+ */
+ if (is_dirlink) {
+ datasize = buildthread(&to_iterator->key, recp, std_hfs, false);
+ } else {
+ datasize = buildthread(&to_iterator->key, recp, std_hfs, directory);
+ }
btdata.itemSize = datasize;
buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
result = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
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);
-
- } else
- pluskey = (HFSPlusCatalogKey *)&to_iterator->key;
+
+ /* Save the real encoding hint in the Finder Info (field 4). */
+ if (directory && from_cdp->cd_cnid == kHFSRootFolderID) {
+ u_int32_t realhint;
+
+ realhint = hfs_pickencoding(pluskey->nodeName.unicode, pluskey->nodeName.length);
+ vcb->vcbFndrInfo[4] = SET_HFS_TEXT_ENCODING(realhint);
+ }
+ }
+#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);
int
cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
{
- ExtendedVCB * vcb;
FCB * fcb;
BTreeIterator *iterator;
cnid_t cnid;
int std_hfs;
int result;
- vcb = HFSTOVCB(hfsmp);
- fcb = GetFileControlBlock(vcb->catalogRefNum);
- std_hfs = (vcb->vcbSigWord == kHFSSigWord);
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+ std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
/* Preflight check:
*
* A file must be zero length (no blocks)
*/
if (descp->cd_cnid < kHFSFirstUserCatalogNodeID ||
- descp->cd_parentcnid == kRootParID)
+ descp->cd_parentcnid == kHFSRootParentID)
return (EINVAL);
/* XXX Preflight Missing */
- /* Get space for iterator */
- MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
- bzero(iterator, sizeof(*iterator));
+ /* Borrow the btcb iterator since we have an exclusive catalog lock. */
+ iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
+ iterator->hint.nodeNum = 0;
/*
* Derive a key from either the file ID (for a virtual inode)
* or the descriptor.
*/
if (descp->cd_namelen == 0) {
- result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
- cnid = attrp->ca_fileid;
+ result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
+ cnid = attrp->ca_fileid;
} else {
result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
cnid = descp->cd_cnid;
if (result)
goto exit;
- // XXXdbg - preflight all btree operations to make sure there's enough space
- result = BTCheckFreeSpace(fcb);
- if (result)
- goto exit;
-
/* Delete record */
result = BTDeleteRecord(fcb, iterator);
- if (result)
- goto exit;
+ if (result) {
+ if (std_hfs || (result != btNotFound))
+ goto exit;
- /* Delete thread record, ignore errors */
- buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
- (void) BTDeleteRecord(fcb, iterator);
+ struct cat_desc temp_desc;
+
+ /* Probably the node has mangled name */
+ result = cat_lookupmangled(hfsmp, descp, 0, &temp_desc, attrp, NULL);
+ if (result)
+ goto exit;
+
+ /* The file has mangled name. Delete the file using full name */
+ bzero(iterator, sizeof(*iterator));
+ result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&iterator->key, 0);
+ cnid = temp_desc.cd_cnid;
+ if (result) {
+ cat_releasedesc(&temp_desc);
+ goto exit;
+ }
+
+ result = BTDeleteRecord(fcb, iterator);
+ if (result) {
+ cat_releasedesc(&temp_desc);
+ goto exit;
+ }
- TrashCatalogIterator(vcb, descp->cd_parentcnid);
+ cat_releasedesc(&temp_desc);
+ }
+
+ /* Delete thread record. On error, mark volume inconsistent */
+ buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
+ if (BTDeleteRecord(fcb, iterator)) {
+ if (!std_hfs) {
+ printf ("hfs: cat_delete() failed to delete thread record id=%u on vol=%s\n", cnid, hfsmp->vcbVN);
+ hfs_mark_volume_inconsistent(hfsmp);
+ }
+ }
exit:
(void) BTFlushPath(fcb);
- FREE(iterator, M_TEMP);
return MacToVFSError(result);
}
/*
- * 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.
*/
-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)
{
- ExtendedVCB * vcb;
FCB * fcb;
BTreeIterator * iterator;
struct update_state state;
- int std_hfs;
int result;
- vcb = HFSTOVCB(hfsmp);
- fcb = GetFileControlBlock(vcb->catalogRefNum);
- std_hfs = (vcb->vcbSigWord == kHFSSigWord);
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
state.s_desc = descp;
state.s_attr = attrp;
state.s_rsrcfork = rsrcforkp;
state.s_hfsmp = hfsmp;
- /* Get space for iterator */
- MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
- bzero(iterator, sizeof(*iterator));
+ /* Borrow the btcb iterator since we have an exclusive catalog lock. */
+ iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
/*
* 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))
+ 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
+ } else {
result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
+ }
if (result)
goto exit;
exit:
(void) BTFlushPath(fcb);
- FREE(iterator, M_TEMP);
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.
*/
static int
-catrec_update(const CatalogKey *ckp, CatalogRecord *crp, u_int16_t reclen,
- struct update_state *state)
+catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state)
{
struct cat_desc *descp;
struct cat_attr *attrp;
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) ||
(u_int16_t)forkp->cf_extents[i].blockCount;
}
}
+
+ /* Synchronize the lock state */
+ if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE))
+ file->flags |= kHFSFileLockedMask;
+ else
+ file->flags &= ~kHFSFileLockedMask;
break;
}
+#endif
+
case kHFSPlusFolderRecord: {
HFSPlusCatalogFolder *dir;
dir = (struct HFSPlusCatalogFolder *)crp;
/* Do a quick sanity check */
- if ((ckp->hfsPlus.parentID != descp->cd_parentcnid) ||
- (dir->folderID != descp->cd_cnid))
+ if (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;
dir->valence = attrp->ca_entries;
dir->createDate = to_hfs_time(attrp->ca_itime);
dir->contentModDate = to_hfs_time(attrp->ca_mtime);
dir->backupDate = to_hfs_time(attrp->ca_btime);
dir->accessDate = to_hfs_time(attrp->ca_atime);
+ attrp->ca_atimeondisk = attrp->ca_atime;
dir->attributeModDate = to_hfs_time(attrp->ca_ctime);
- dir->textEncoding = descp->cd_encoding;
+ /* Note: directory hardlink inodes don't require a text encoding hint. */
+ if (ckp->hfsPlus.parentID != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
+ dir->textEncoding = descp->cd_encoding;
+ }
+ dir->folderCount = attrp->ca_dircount;
bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 32);
/*
* Update the BSD Info if it was already initialized on
((attrp->ca_mode & ALLPERMS) !=
(hfsmp->hfs_dir_mask & ACCESSPERMS))) {
if ((dir->bsdInfo.fileMode == 0) ||
- (HFSTOVFS(hfsmp)->mnt_flag &
- MNT_UNKNOWNPERMISSIONS) == 0) {
+ (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
dir->bsdInfo.ownerID = attrp->ca_uid;
dir->bsdInfo.groupID = attrp->ca_gid;
}
dir->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
dir->bsdInfo.adminFlags = attrp->ca_flags >> 16;
dir->bsdInfo.fileMode = attrp->ca_mode;
+ /* A directory hardlink has a link count. */
+ if (attrp->ca_linkcount > 1 || dir->hl_linkCount > 1) {
+ dir->hl_linkCount = attrp->ca_linkcount;
+ }
}
break;
}
case kHFSPlusFileRecord: {
HFSPlusCatalogFile *file;
+ int is_dirlink;
file = (struct HFSPlusCatalogFile *)crp;
/* Do a quick sanity check */
if (file->fileID != attrp->ca_fileid)
return (btNotFound);
+ file->flags = attrp->ca_recflags;
file->createDate = to_hfs_time(attrp->ca_itime);
file->contentModDate = to_hfs_time(attrp->ca_mtime);
file->backupDate = to_hfs_time(attrp->ca_btime);
file->accessDate = to_hfs_time(attrp->ca_atime);
+ attrp->ca_atimeondisk = attrp->ca_atime;
file->attributeModDate = to_hfs_time(attrp->ca_ctime);
- file->textEncoding = descp->cd_encoding;
+ /*
+ * Note: file hardlink inodes don't require a text encoding
+ * hint, but they do have a first link value.
+ */
+ if (ckp->hfsPlus.parentID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
+ file->hl_firstLinkID = attrp->ca_firstlink;
+ } else {
+ file->textEncoding = descp->cd_encoding;
+ }
bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 32);
/*
* Update the BSD Info if it was already initialized on
* 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) ||
- (HFSTOVFS(hfsmp)->mnt_flag &
- MNT_UNKNOWNPERMISSIONS) == 0) {
+ (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
file->bsdInfo.ownerID = attrp->ca_uid;
file->bsdInfo.groupID = attrp->ca_gid;
}
file->resourceFork.totalBlocks = forkp->cf_blocks;
bcopy(&forkp->cf_extents[0], &file->resourceFork.extents,
sizeof(HFSPlusExtentRecord));
+ /* Push blocks read to disk */
+ file->resourceFork.clumpSize =
+ howmany(forkp->cf_bytesread, blksize);
}
if (state->s_datafork) {
forkp = state->s_datafork;
file->dataFork.totalBlocks = forkp->cf_blocks;
bcopy(&forkp->cf_extents[0], &file->dataFork.extents,
sizeof(HFSPlusExtentRecord));
+ /* Push blocks read to disk */
+ file->dataFork.clumpSize =
+ howmany(forkp->cf_bytesread, blksize);
}
if ((file->resourceFork.extents[0].startBlock != 0) &&
(file->resourceFork.extents[0].startBlock ==
- file->dataFork.extents[0].startBlock))
- panic("catrec_update: rsrc fork == data fork");
+ file->dataFork.extents[0].startBlock)) {
+ panic("hfs: catrec_update: rsrc fork == data fork");
+ }
/* Synchronize the lock state */
if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE))
file->flags &= ~kHFSFileLockedMask;
/* Push out special field if necessary */
- if (S_ISBLK(attrp->ca_mode) || S_ISCHR(attrp->ca_mode))
+ 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_nlink == 2)
- file->bsdInfo.special.linkCount = attrp->ca_nlink;
+ }
+ 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;
}
default:
return (0);
}
-/*
- * catrec_readattr -
- * This is called from within BTIterateRecords.
+/* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
+ * catalog btree of given cnid by walking up the parent chain till it reaches
+ * either the root folder, or the private metadata directory for storing
+ * directory hard links. This function updates the corresponding in-core
+ * cnode, if any, and the directory record in the catalog btree.
+ * On success, returns zero. On failure, returns non-zero value.
*/
-struct readattr_state {
- struct hfsmount *hfsmp;
- struct cat_entrylist *list;
- cnid_t dir_cnid;
- int stdhfs;
- int error;
-};
-
-static int
-catrec_readattr(const CatalogKey *key, const CatalogRecord *rec,
- u_long node, struct readattr_state *state)
+__private_extern__
+int
+cat_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid)
{
- struct cat_entrylist *list = state->list;
- struct hfsmount *hfsmp = state->hfsmp;
- struct cat_entry *cep;
- cnid_t parentcnid;
-
- if (list->realentries >= list->maxentries)
- return (0); /* stop */
-
- parentcnid = state->stdhfs ? key->hfs.parentID : key->hfsPlus.parentID;
+ int retval = 0;
+ int lockflags = 0;
+ struct cat_desc desc;
+ struct cat_attr attr;
+
+ while ((cnid != kHFSRootFolderID) && (cnid != kHFSRootParentID) &&
+ (cnid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid)) {
+ /* Update the bit in corresponding cnode, if any, in the hash.
+ * If the cnode has the bit already set, stop the traversal.
+ */
+ retval = hfs_chash_set_childlinkbit(hfsmp, cnid);
+ if (retval == 0) {
+ break;
+ }
- switch(rec->recordType) {
+ /* Update the catalog record on disk if either cnode was not
+ * found in the hash, or if a cnode was found and the cnode
+ * did not have the bit set previously.
+ */
+ retval = hfs_start_transaction(hfsmp);
+ if (retval) {
+ break;
+ }
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+
+ /* Look up our catalog folder record */
+ retval = cat_idlookup(hfsmp, cnid, 0, 0, &desc, &attr, NULL);
+ if (retval) {
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ hfs_end_transaction(hfsmp);
+ break;
+ }
+
+ /* Update the bit in the catalog record */
+ attr.ca_recflags |= kHFSHasChildLinkMask;
+ retval = cat_update(hfsmp, &desc, &attr, NULL, NULL);
+ if (retval) {
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ hfs_end_transaction(hfsmp);
+ cat_releasedesc(&desc);
+ break;
+ }
+
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ hfs_end_transaction(hfsmp);
+
+ cnid = desc.cd_parentcnid;
+ cat_releasedesc(&desc);
+ }
+
+ return retval;
+}
+
+/* This function traverses the parent directory hierarchy from the given
+ * directory to one level below root directory and checks if any of its
+ * ancestors is -
+ * 1. A directory hard link.
+ * 2. The 'pointed at' directory.
+ * If any of these conditions fail or an internal error is encountered
+ * during look up of the catalog record, this function returns non-zero value.
+ */
+__private_extern__
+int
+cat_check_link_ancestry(struct hfsmount *hfsmp, cnid_t cnid, cnid_t pointed_at_cnid)
+{
+ HFSPlusCatalogKey *keyp;
+ BTreeIterator *ip;
+ FSBufferDescriptor btdata;
+ HFSPlusCatalogFolder folder;
+ FCB *fcb;
+ int invalid;
+ int result;
+
+ invalid = 0;
+ BDINIT(btdata, &folder);
+ MALLOC(ip, BTreeIterator *, sizeof(*ip), M_TEMP, M_WAITOK);
+ keyp = (HFSPlusCatalogKey *)&ip->key;
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+
+ while (cnid != kHFSRootParentID) {
+ /* Check if the 'pointed at' directory is an ancestor */
+ if (pointed_at_cnid == cnid) {
+ invalid = 1;
+ break;
+ }
+ if ((result = getkey(hfsmp, cnid, (CatalogKey *)keyp))) {
+ printf("hfs: cat_check_link_ancestry: getkey 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 id=%u, vol=%s\n", cnid, hfsmp->vcbVN);
+ invalid = 1; /* On errors, assume an invalid parent */
+ break;
+ }
+ /* Check if this ancestor is a directory hard link */
+ if (folder.flags & kHFSHasLinkChainMask) {
+ invalid = 1;
+ break;
+ }
+ cnid = keyp->parentID;
+ }
+ FREE(ip, M_TEMP);
+ return (invalid);
+}
+
+
+/*
+ * update_siblinglinks_callback - update a link's chain
+ */
+
+struct linkupdate_state {
+ cnid_t filelinkid;
+ cnid_t prevlinkid;
+ cnid_t nextlinkid;
+};
+
+static int
+update_siblinglinks_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state)
+{
+ HFSPlusCatalogFile *file;
+
+ if (crp->recordType != kHFSPlusFileRecord) {
+ printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp->recordType);
+ return (btNotFound);
+ }
+
+ file = (struct HFSPlusCatalogFile *)crp;
+ if (file->flags & kHFSHasLinkChainMask) {
+ if (state->prevlinkid != HFS_IGNORABLE_LINK) {
+ file->hl_prevLinkID = state->prevlinkid;
+ }
+ if (state->nextlinkid != HFS_IGNORABLE_LINK) {
+ file->hl_nextLinkID = state->nextlinkid;
+ }
+ } else {
+ printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file->fileID);
+ }
+ return (0);
+}
+
+/*
+ * cat_update_siblinglinks - update a link's chain
+ */
+int
+cat_update_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cnid_t nextlinkid)
+{
+ FCB * fcb;
+ BTreeIterator * iterator;
+ struct linkupdate_state state;
+ int result;
+
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+ state.filelinkid = linkfileid;
+ state.prevlinkid = prevlinkid;
+ state.nextlinkid = nextlinkid;
+
+ /* Create an iterator for use by us temporarily */
+ MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ bzero(iterator, sizeof(*iterator));
+
+ result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key);
+ if (result == 0) {
+ result = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr)update_siblinglinks_callback, &state);
+ (void) BTFlushPath(fcb);
+ } else {
+ printf("hfs: cat_update_siblinglinks: couldn't resolve cnid=%d, vol=%s\n", linkfileid, hfsmp->vcbVN);
+ }
+
+ FREE (iterator, M_TEMP);
+ return MacToVFSError(result);
+}
+
+/*
+ * cat_lookuplink - lookup a link by it's name
+ */
+int
+cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
+{
+ FCB * fcb;
+ BTreeIterator * iterator;
+ struct FSBufferDescriptor btdata;
+ struct HFSPlusCatalogFile file;
+ int result;
+
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+
+ /* Create an iterator for use by us temporarily */
+ MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ bzero(iterator, sizeof(*iterator));
+
+ if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
+ goto exit;
+ }
+ BDINIT(btdata, &file);
+
+ if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
+ goto exit;
+ }
+ if (file.recordType != kHFSPlusFileRecord) {
+ result = ENOENT;
+ goto exit;
+ }
+ *linkfileid = file.fileID;
+
+ if (file.flags & kHFSHasLinkChainMask) {
+ *prevlinkid = file.hl_prevLinkID;
+ *nextlinkid = file.hl_nextLinkID;
+ } else {
+ *prevlinkid = 0;
+ *nextlinkid = 0;
+ }
+exit:
+ FREE(iterator, M_TEMP);
+ return MacToVFSError(result);
+}
+
+
+/*
+ * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
+ */
+int
+cat_lookup_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
+{
+ FCB * fcb;
+ BTreeIterator * iterator;
+ struct FSBufferDescriptor btdata;
+ struct HFSPlusCatalogFile file;
+ int result;
+
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+
+ /* Create an iterator for use by us temporarily */
+ MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ bzero(iterator, sizeof(*iterator));
+
+ if ((result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key))) {
+ 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;
+
+ /* 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;
+
+ 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);
+}
+
+
+/*
+ * cat_createlink - create a link in the catalog
+ *
+ * The following cat_attr fields are expected to be set:
+ * ca_linkref
+ * ca_itime
+ * ca_mode (S_IFREG)
+ * ca_recflags
+ * ca_flags
+ * ca_finderinfo (type and creator)
+ */
+int
+cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
+ cnid_t nextlinkid, cnid_t *linkfileid)
+{
+ FCB * fcb;
+ struct btobj * bto;
+ FSBufferDescriptor btdata;
+ HFSPlusForkData *rsrcforkp;
+ u_int32_t nextCNID;
+ u_int32_t datalen;
+ u_int32_t encoding;
+ int thread_inserted = 0;
+ int alias_allocated = 0;
+ int result = 0;
+ 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.
+ */
+ nextCNID = hfsmp->vcbNxtCNID;
+ if (nextCNID == 0xFFFFFFFF) {
+ hfs_lock_mount (hfsmp);
+ hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
+ hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
+ hfs_unlock_mount(hfsmp);
+ } else {
+ hfsmp->vcbNxtCNID++;
+ }
+ MarkVCBDirty(hfsmp);
+
+ /* Get space for iterator, key and data */
+ MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
+ bto->iterator.hint.nodeNum = 0;
+ rsrcforkp = &bto->data.hfsPlusFile.resourceFork;
+
+ result = buildkey(hfsmp, descp, &bto->key, 0);
+ if (result) {
+ printf("hfs: cat_createlink: err %d from buildkey\n", result);
+ goto exit;
+ }
+
+ /* This is our only chance to set the encoding (other than a rename). */
+ encoding = hfs_pickencoding(bto->key.nodeName.unicode, bto->key.nodeName.length);
+
+ /* Insert the thread record first. */
+ datalen = buildthread((void*)&bto->key, &bto->data, 0, 0);
+ btdata.bufferAddress = &bto->data;
+ btdata.itemSize = datalen;
+ btdata.itemCount = 1;
+
+ for (;;) {
+ buildthreadkey(nextCNID, 0, (CatalogKey *) &bto->iterator.key);
+
+ /*
+ * 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);
+ }
+
+ if ((result == btExists) && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
+ /*
+ * Allow CNIDs on HFS Plus volumes to wrap around
+ */
+ if (++nextCNID < kHFSFirstUserCatalogNodeID) {
+ nextCNID = kHFSFirstUserCatalogNodeID;
+ }
+ continue;
+ }
+ if (result == 0) {
+ thread_inserted = 1;
+ }
+ break;
+ }
+ if (result)
+ goto exit;
+
+ /*
+ * CNID is now established. If we have wrapped then
+ * update the vcbNxtCNID.
+ */
+ if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
+ hfsmp->vcbNxtCNID = nextCNID + 1;
+ if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
+ hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
+ }
+ }
+
+ /*
+ * Now insert the link record.
+ */
+ buildrecord(attrp, nextCNID, 0, encoding, &bto->data, &datalen);
+
+ bto->data.hfsPlusFile.hl_prevLinkID = 0;
+ bto->data.hfsPlusFile.hl_nextLinkID = nextlinkid;
+ bto->data.hfsPlusFile.hl_linkReference = attrp->ca_linkref;
+
+ /* For directory hard links, create alias in resource fork */
+ if (descp->cd_flags & CD_ISDIR) {
+ if ((result = cat_makealias(hfsmp, attrp->ca_linkref, &bto->data.hfsPlusFile))) {
+ goto exit;
+ }
+ alias_allocated = 1;
+ }
+ btdata.bufferAddress = &bto->data;
+ btdata.itemSize = datalen;
+ btdata.itemCount = 1;
+
+ bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key));
+
+ result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
+ if (result) {
+ if (result == btExists)
+ result = EEXIST;
+ goto exit;
+ }
+ if (linkfileid != NULL) {
+ *linkfileid = nextCNID;
+ }
+exit:
+ if (result) {
+ if (thread_inserted) {
+ printf("hfs: cat_createlink: 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);
+ }
+ }
+ if (alias_allocated && rsrcforkp->extents[0].startBlock != 0) {
+ (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock,
+ rsrcforkp->extents[0].blockCount, 0);
+ rsrcforkp->extents[0].startBlock = 0;
+ rsrcforkp->extents[0].blockCount = 0;
+ }
+ }
+ (void) BTFlushPath(fcb);
+ FREE(bto, M_TEMP);
+
+ return MacToVFSError(result);
+}
+
+/* Directory hard links are visible as aliases on pre-Leopard systems and
+ * as normal directories on Leopard or later. All directory hard link aliases
+ * have the same resource fork content except for the three uniquely
+ * identifying values that are updated in the resource fork data when the alias
+ * is created. The following array is the constant resource fork data used
+ * only for creating directory hard link aliases.
+ */
+static const char hfs_dirlink_alias_rsrc[] = {
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
+ 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* Constants for directory hard link alias */
+enum {
+ /* Size of resource fork data array for directory hard link alias */
+ kHFSAliasSize = 0x1d0,
+
+ /* Volume type for ejectable devices like disk image */
+ kHFSAliasVolTypeEjectable = 0x5,
+
+ /* Offset for volume create date, in Mac OS local time */
+ kHFSAliasVolCreateDateOffset = 0x12a,
+
+ /* Offset for the type of volume */
+ kHFSAliasVolTypeOffset = 0x130,
+
+ /* Offset for folder ID of the parent directory of the directory inode */
+ kHFSAliasParentIDOffset = 0x132,
+
+ /* Offset for folder ID of the directory inode */
+ kHFSAliasTargetIDOffset = 0x176,
+};
+
+/* Create and write an alias that points at the directory represented by given
+ * inode number on the same volume. Directory hard links are visible as
+ * aliases in pre-Leopard systems and this function creates these aliases.
+ *
+ * Note: This code is very specific to creating alias for the purpose
+ * of directory hard links only, and should not be generalized.
+ */
+static int
+cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp)
+{
+ struct buf *bp;
+ daddr64_t blkno;
+ u_int32_t blkcount;
+ int blksize;
+ int sectorsize;
+ int result;
+ HFSPlusForkData *rsrcforkp;
+ char *alias;
+ uint32_t *valptr;
+
+ rsrcforkp = &(crp->resourceFork);
+
+ blksize = hfsmp->blockSize;
+ blkcount = howmany(kHFSAliasSize, blksize);
+ sectorsize = hfsmp->hfs_logical_block_size;
+ bzero(rsrcforkp, sizeof(HFSPlusForkData));
+
+ /* Allocate some disk space for the alias content. */
+ result = BlockAllocate(hfsmp, 0, blkcount, blkcount,
+ HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE,
+ &rsrcforkp->extents[0].startBlock,
+ &rsrcforkp->extents[0].blockCount);
+ /* 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;
+ }
+
+ /* Acquire a buffer cache block for our block. */
+ blkno = ((u_int64_t)rsrcforkp->extents[0].startBlock * (u_int64_t)blksize) / sectorsize;
+ blkno += hfsmp->hfsPlusIOPosOffset / sectorsize;
+
+ bp = buf_getblk(hfsmp->hfs_devvp, blkno, roundup(kHFSAliasSize, hfsmp->hfs_logical_block_size), 0, 0, BLK_META);
+ if (hfsmp->jnl) {
+ journal_modify_block_start(hfsmp->jnl, bp);
+ }
+
+ /* Generate alias content */
+ alias = (char *)buf_dataptr(bp);
+ bzero(alias, buf_size(bp));
+ bcopy(hfs_dirlink_alias_rsrc, alias, kHFSAliasSize);
+
+ /* Set the volume create date, local time in Mac OS format */
+ valptr = (uint32_t *)(alias + kHFSAliasVolCreateDateOffset);
+ *valptr = OSSwapHostToBigInt32(hfsmp->localCreateDate);
+
+ /* If the file system is on a virtual device like disk image,
+ * update the volume type to be ejectable device.
+ */
+ if (hfsmp->hfs_flags & HFS_VIRTUAL_DEVICE) {
+ *(uint16_t *)(alias + kHFSAliasVolTypeOffset) =
+ OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable);
+ }
+
+ /* Set id of the parent of the target directory */
+ valptr = (uint32_t *)(alias + kHFSAliasParentIDOffset);
+ *valptr = OSSwapHostToBigInt32(hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid);
+
+ /* Set id of the target directory */
+ valptr = (uint32_t *)(alias + kHFSAliasTargetIDOffset);
+ *valptr = OSSwapHostToBigInt32(inode_num);
+
+ /* Write alias content to disk. */
+ if (hfsmp->jnl) {
+ journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL);
+ } else if ((result = buf_bwrite(bp))) {
+ goto exit;
+ }
+
+ /* Finish initializing the fork data. */
+ rsrcforkp->logicalSize = kHFSAliasSize;
+ rsrcforkp->totalBlocks = rsrcforkp->extents[0].blockCount;
+
+exit:
+ if (result && rsrcforkp->extents[0].startBlock != 0) {
+ (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock, rsrcforkp->extents[0].blockCount, 0);
+ rsrcforkp->extents[0].startBlock = 0;
+ rsrcforkp->extents[0].blockCount = 0;
+ rsrcforkp->logicalSize = 0;
+ rsrcforkp->totalBlocks = 0;
+ }
+ return (result);
+}
+
+/*
+ * cat_deletelink - delete a link from the catalog
+ */
+int
+cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp)
+{
+ struct HFSPlusCatalogFile file;
+ struct cat_attr cattr;
+ uint32_t totalBlocks;
+ int i;
+ int result;
+
+ bzero(&file, sizeof (file));
+ bzero(&cattr, sizeof (cattr));
+ cattr.ca_fileid = descp->cd_cnid;
+
+ /* Directory links have alias content to remove. */
+ if (descp->cd_flags & CD_ISDIR) {
+ FCB * fcb;
+ BTreeIterator * iterator;
+ struct FSBufferDescriptor btdata;
+
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
+
+ /* Borrow the btcb iterator since we have an exclusive catalog lock. */
+ iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
+ iterator->hint.nodeNum = 0;
+
+ if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
+ goto exit;
+ }
+ BDINIT(btdata, &file);
+
+ if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
+ goto exit;
+ }
+ }
+
+ result = cat_delete(hfsmp, descp, &cattr);
+
+ if ((result == 0) &&
+ (descp->cd_flags & CD_ISDIR) &&
+ (file.recordType == kHFSPlusFileRecord)) {
+
+ totalBlocks = file.resourceFork.totalBlocks;
+
+ for (i = 0; (i < 8) && (totalBlocks > 0); i++) {
+ if ((file.resourceFork.extents[i].blockCount == 0) &&
+ (file.resourceFork.extents[i].startBlock == 0)) {
+ break;
+ }
+
+ (void) BlockDeallocate(hfsmp,
+ file.resourceFork.extents[i].startBlock,
+ file.resourceFork.extents[i].blockCount, 0);
+
+ totalBlocks -= file.resourceFork.extents[i].blockCount;
+ file.resourceFork.extents[i].startBlock = 0;
+ file.resourceFork.extents[i].blockCount = 0;
+ }
+ }
+exit:
+ return (result);
+}
+
+
+/*
+ * Callback to collect directory entries.
+ * Called with readattr_state for each item in a directory.
+ */
+struct readattr_state {
+ struct hfsmount *hfsmp;
+ struct cat_entrylist *list;
+ cnid_t dir_cnid;
+ int stdhfs;
+ int error;
+};
+
+static int
+getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec,
+ struct readattr_state *state)
+{
+ struct cat_entrylist *list = state->list;
+ struct hfsmount *hfsmp = state->hfsmp;
+ struct cat_entry *cep;
+ cnid_t parentcnid;
+
+ if (list->realentries >= list->maxentries)
+ return (0); /* stop */
+
+ parentcnid = state->stdhfs ? key->hfs.parentID : key->hfsPlus.parentID;
+
+ switch(rec->recordType) {
case kHFSPlusFolderRecord:
case kHFSPlusFileRecord:
+#if CONFIG_HFS_STD
case kHFSFolderRecord:
case kHFSFileRecord:
+#endif
if (parentcnid != state->dir_cnid) {
state->error = ENOENT;
return (0); /* stop */
return (0); /* stop */
}
- /* Hide the private meta data directory and journal files */
- if (parentcnid == kRootDirID) {
- if ((rec->recordType == kHFSPlusFolderRecord) &&
- (rec->hfsPlusFolder.folderID == hfsmp->hfs_private_metadata_dir)) {
- return (1); /* continue */
+ /* Hide the private system directories and journal files */
+ if (parentcnid == kHFSRootFolderID) {
+ if (rec->recordType == kHFSPlusFolderRecord) {
+ if (rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
+ rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
+ list->skipentries++;
+ return (1); /* continue */
+ }
+ }
+ if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
+ (rec->recordType == kHFSPlusFileRecord) &&
+ ((rec->hfsPlusFile.fileID == hfsmp->hfs_jnlfileid) ||
+ (rec->hfsPlusFile.fileID == hfsmp->hfs_jnlinfoblkid))) {
+ list->skipentries++;
+ return (1); /* continue */
+ }
+ }
+
+ cep = &list->entry[list->realentries++];
+
+ if (state->stdhfs == 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;
+
+ /* 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;
+
+ promoteattr(hfsmp, rec, &cnoderec);
+ getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
+
+ MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
+ promotekey(hfsmp, (const HFSCatalogKey *)key, pluskey, &encoding);
+ builddesc(pluskey, getcnid(rec), 0, encoding, isadir(rec), &cep->ce_desc);
+ FREE(pluskey, M_TEMP);
+
+ if (rec->recordType == kHFSFileRecord) {
+ int blksize = HFSTOVCB(hfsmp)->blockSize;
+
+ cep->ce_datasize = rec->hfsFile.dataLogicalSize;
+ cep->ce_datablks = rec->hfsFile.dataPhysicalSize / blksize;
+ cep->ce_rsrcsize = rec->hfsFile.rsrcLogicalSize;
+ cep->ce_rsrcblks = rec->hfsFile.rsrcPhysicalSize / blksize;
+ }
+ }
+#endif
+
+ return (list->realentries < list->maxentries);
+}
+
+/*
+ * Pack a cat_entrylist buffer with attributes from the catalog
+ *
+ * Note: index is zero relative
+ */
+int
+cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list)
+{
+ FCB* fcb;
+ CatalogKey * key;
+ BTreeIterator * iterator;
+ struct readattr_state state;
+ cnid_t parentcnid;
+ int i;
+ int std_hfs;
+ int index;
+ int have_key;
+ int result = 0;
+
+ ce_list->realentries = 0;
+
+ fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
+ std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
+ parentcnid = dirhint->dh_desc.cd_parentcnid;
+
+ state.hfsmp = hfsmp;
+ state.list = ce_list;
+ state.dir_cnid = parentcnid;
+ state.stdhfs = std_hfs;
+ state.error = 0;
+
+ MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+ bzero(iterator, sizeof(*iterator));
+ key = (CatalogKey *)&iterator->key;
+ have_key = 0;
+ iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
+ index = dirhint->dh_index + 1;
+
+ /*
+ * Attempt to build a key from cached filename
+ */
+ if (dirhint->dh_desc.cd_namelen != 0) {
+ if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
+ have_key = 1;
+ }
+ }
+
+ /*
+ * If the last entry wasn't cached then position the btree iterator
+ */
+ if ((index == 0) || !have_key) {
+ /*
+ * Position the iterator at the directory's thread record.
+ * (i.e. just before the first entry)
+ */
+ buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
+ result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
+ if (result) {
+ result = MacToVFSError(result);
+ goto exit;
+ }
+
+ /*
+ * Iterate until we reach the entry just
+ * before the one we want to start with.
+ */
+ if (index > 0) {
+ struct position_state ps;
+
+ ps.error = 0;
+ ps.count = 0;
+ ps.index = index;
+ ps.parentID = dirhint->dh_desc.cd_parentcnid;
+ ps.hfsmp = hfsmp;
+
+ result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
+ (IterateCallBackProcPtr)cat_findposition, &ps);
+ if (ps.error)
+ result = ps.error;
+ else
+ result = MacToVFSError(result);
+ if (result) {
+ result = MacToVFSError(result);
+ goto exit;
+ }
+ }
+ }
+
+ /* Fill list with entries starting at iterator->key. */
+ result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
+ (IterateCallBackProcPtr)getentriesattr_callback, &state);
+
+ if (state.error)
+ result = state.error;
+ else if (ce_list->realentries == 0)
+ result = ENOENT;
+ else
+ result = MacToVFSError(result);
+
+ if (std_hfs)
+ goto exit;
+
+ /*
+ * Resolve any hard links.
+ */
+ for (i = 0; i < (int)ce_list->realentries; ++i) {
+ struct FndrFileInfo *fip;
+ struct cat_entry *cep;
+ struct HFSPlusCatalogFile filerec;
+ int isdirlink = 0;
+ int isfilelink = 0;
+
+ cep = &ce_list->entry[i];
+ if (cep->ce_attr.ca_linkref == 0)
+ continue;
+
+ /* Note: Finder info is still in Big Endian */
+ fip = (struct FndrFileInfo *)&cep->ce_attr.ca_finderinfo;
+
+ if (S_ISREG(cep->ce_attr.ca_mode) &&
+ (SWAP_BE32(fip->fdType) == kHardLinkFileType) &&
+ (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)) {
+ isfilelink = 1;
+ }
+ if (S_ISREG(cep->ce_attr.ca_mode) &&
+ (SWAP_BE32(fip->fdType) == kHFSAliasType) &&
+ (SWAP_BE32(fip->fdCreator) == kHFSAliasCreator) &&
+ (cep->ce_attr.ca_recflags & kHFSHasLinkChainMask)) {
+ isdirlink = 1;
+ }
+ if (isfilelink || isdirlink) {
+ if (cat_resolvelink(hfsmp, cep->ce_attr.ca_linkref, isdirlink, &filerec) != 0)
+ continue;
+ /* Repack entry from inode record. */
+ getbsdattr(hfsmp, &filerec, &cep->ce_attr);
+ cep->ce_datasize = filerec.dataFork.logicalSize;
+ cep->ce_datablks = filerec.dataFork.totalBlocks;
+ cep->ce_rsrcsize = filerec.resourceFork.logicalSize;
+ cep->ce_rsrcblks = filerec.resourceFork.totalBlocks;
+ }
+ }
+exit:
+ FREE(iterator, M_TEMP);
+
+ return MacToVFSError(result);
+}
+
+#define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
+
+/*
+ * Callback to pack directory entries.
+ * Called with packdirentry_state for each item in a directory.
+ */
+
+/* Hard link information collected during cat_getdirentries. */
+struct linkinfo {
+ u_int32_t link_ref;
+ user_addr_t dirent_addr;
+};
+typedef struct linkinfo linkinfo_t;
+
+/* State information for the getdirentries_callback function. */
+struct packdirentry_state {
+ int cbs_flags; /* VNODE_READDIR_* flags */
+ u_int32_t cbs_parentID;
+ u_int32_t cbs_index;
+ uio_t cbs_uio;
+ ExtendedVCB * cbs_hfsmp;
+ int cbs_result;
+ int32_t cbs_nlinks;
+ int32_t cbs_maxlinks;
+ linkinfo_t * cbs_linkinfo;
+ struct cat_desc * cbs_desc;
+ u_int8_t * cbs_namebuf;
+ /*
+ * The following fields are only used for NFS readdir, which
+ * uses the next file id as the seek offset of each entry.
+ */
+ struct direntry * cbs_direntry;
+ struct direntry * cbs_prevdirentry;
+ u_int32_t cbs_previlinkref;
+ Boolean cbs_hasprevdirentry;
+ Boolean cbs_eof;
+};
+
+/*
+ * getdirentries callback for HFS Plus directories.
+ */
+static int
+getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp,
+ struct packdirentry_state *state)
+{
+ struct hfsmount *hfsmp;
+ const CatalogName *cnp;
+ cnid_t curID;
+ OSErr result;
+ struct dirent catent;
+ struct direntry * entry = NULL;
+ time_t itime;
+ u_int32_t ilinkref = 0;
+ u_int32_t curlinkref = 0;
+ cnid_t cnid;
+ int hide = 0;
+ u_int8_t type = DT_UNKNOWN;
+ u_int8_t is_mangled = 0;
+ u_int8_t is_link = 0;
+ u_int8_t *nameptr;
+ user_addr_t uiobase = USER_ADDR_NULL;
+ size_t namelen = 0;
+ size_t maxnamelen;
+ size_t uiosize = 0;
+ caddr_t uioaddr;
+ Boolean stop_after_pack = false;
+
+ hfsmp = state->cbs_hfsmp;
+ curID = ckp->hfsPlus.parentID;
+
+ /* We're done when parent directory changes */
+ if (state->cbs_parentID != curID) {
+ /*
+ * If the parent ID is different from curID this means we've hit
+ * the EOF for the directory. To help future callers, we mark
+ * the cbs_eof boolean. However, we should only mark the EOF
+ * boolean if we're about to return from this function.
+ *
+ * This is because this callback function does its own uiomove
+ * to get the data to userspace. If we set the boolean before determining
+ * whether or not the current entry has enough room to write its
+ * data to userland, we could fool the callers of this catalog function
+ * into thinking they've hit EOF earlier than they really would have.
+ * In that case, we'd know that we have more entries to process and
+ * send to userland, but we didn't have enough room.
+ *
+ * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
+ * about to return and won't write any new data back
+ * to userland. In the stop_after_pack case, we'll set this boolean
+ * regardless, so it's slightly safer to let that logic mark the boolean,
+ * especially since it's closer to the return of this function.
+ */
+
+ if (state->cbs_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_hasprevdirentry) {
+ stop_after_pack = true;
+ } else {
+ state->cbs_eof = true;
+ state->cbs_result = ENOENT;
+ return (0); /* stop */
+ }
+ } else {
+ state->cbs_eof = true;
+ state->cbs_result = ENOENT;
+ return (0); /* stop */
+ }
+ }
+
+ if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
+ entry = state->cbs_direntry;
+ nameptr = (u_int8_t *)&entry->d_name[0];
+ 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 = sizeof(catent.d_name);
+ }
+
+ if ((state->cbs_flags & VNODE_READDIR_EXTENDED) && stop_after_pack) {
+ /* The last item returns a non-zero invalid cookie */
+ cnid = INT_MAX;
+ } else {
+ switch(crp->recordType) {
+ case kHFSPlusFolderRecord:
+ type = DT_DIR;
+ cnid = crp->hfsPlusFolder.folderID;
+ /* Hide our private system directories. */
+ if (curID == kHFSRootFolderID) {
+ if (cnid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
+ cnid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
+ hide = 1;
+ }
+ }
+ break;
+ case kHFSPlusFileRecord:
+ itime = to_bsd_time(crp->hfsPlusFile.createDate);
+ type = MODE_TO_DT(crp->hfsPlusFile.bsdInfo.fileMode);
+ cnid = crp->hfsPlusFile.fileID;
+ /*
+ * When a hardlink link is encountered save its link ref.
+ */
+ if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
+ (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator) &&
+ ((itime == (time_t)hfsmp->hfs_itime) ||
+ (itime == (time_t)hfsmp->hfs_metadata_createdate))) {
+ /* If link ref is inode's file id then use it directly. */
+ if (crp->hfsPlusFile.flags & kHFSHasLinkChainMask) {
+ cnid = crp->hfsPlusFile.hl_linkReference;
+ } else {
+ ilinkref = crp->hfsPlusFile.hl_linkReference;
+ }
+ is_link =1;
+ } else if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
+ (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator) &&
+ (crp->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
+ (crp->hfsPlusFile.hl_linkReference >= kHFSFirstUserCatalogNodeID) &&
+ ((itime == (time_t)hfsmp->hfs_itime) ||
+ (itime == (time_t)hfsmp->hfs_metadata_createdate))) {
+ /* A directory's link resolves to a directory. */
+ type = DT_DIR;
+ /* A directory's link ref is always inode's file id. */
+ cnid = crp->hfsPlusFile.hl_linkReference;
+ is_link = 1;
+ }
+ /* Hide the journal files */
+ if ((curID == kHFSRootFolderID) &&
+ ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))) &&
+ ((cnid == hfsmp->hfs_jnlfileid) ||
+ (cnid == hfsmp->hfs_jnlinfoblkid))) {
+ hide = 1;
+ }
+ break;
+ default:
+ return (0); /* stop */
+ };
+
+ cnp = (const CatalogName*) &ckp->hfsPlus.nodeName;
+
+ namelen = cnp->ustr.length;
+ /*
+ * For MacRoman encoded names, assume that its ascii and
+ * convert it directly in an attempt to avoid the more
+ * expensive utf8_encodestr conversion.
+ */
+ if ((namelen < maxnamelen) && (crp->hfsPlusFile.textEncoding == 0)) {
+ int i;
+ u_int16_t ch;
+ const u_int16_t *chp;
+
+ chp = &cnp->ustr.unicode[0];
+ for (i = 0; i < (int)namelen; ++i) {
+ ch = *chp++;
+ if (ch > 0x007f || ch == 0x0000) {
+ /* Perform expensive utf8_encodestr conversion */
+ goto encodestr;
+ }
+ nameptr[i] = (ch == '/') ? ':' : (u_int8_t)ch;
+ }
+ nameptr[namelen] = '\0';
+ result = 0;
+ } else {
+encodestr:
+ result = utf8_encodestr(cnp->ustr.unicode, namelen * sizeof(UniChar),
+ nameptr, &namelen, maxnamelen, ':', 0);
+ }
+
+ /* Check result returned from encoding the filename to utf8 */
+ if (result == ENAMETOOLONG) {
+ /*
+ * If we were looking at a catalog record for a hardlink (not the inode),
+ * then we want to use its link ID as opposed to the inode ID for
+ * a mangled name. For all other cases, they are the same. Note that
+ * due to the way directory hardlinks are implemented, the actual link
+ * is going to be counted as a file record, so we can catch both
+ * with is_link.
+ */
+ cnid_t linkid = cnid;
+ if (is_link) {
+ linkid = crp->hfsPlusFile.fileID;
+ }
+
+ result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
+ cnp->ustr.unicode, maxnamelen,
+ (ByteCount*)&namelen, nameptr, linkid);
+ is_mangled = 1;
+ }
+ }
+
+ if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
+ /*
+ * The index is 1 relative and includes "." and ".."
+ *
+ * Also stuff the cnid in the upper 32 bits of the cookie.
+ * The cookie is stored to the previous entry, which will
+ * be packed and copied this time
+ */
+ state->cbs_prevdirentry->d_seekoff = (state->cbs_index + 3) | ((u_int64_t)cnid << 32);
+ uiosize = state->cbs_prevdirentry->d_reclen;
+ uioaddr = (caddr_t) state->cbs_prevdirentry;
+ } else {
+ catent.d_type = type;
+ catent.d_namlen = namelen;
+ catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
+ if (hide)
+ catent.d_fileno = 0; /* file number = 0 means skip entry */
+ else
+ catent.d_fileno = cnid;
+ uioaddr = (caddr_t) &catent;
+ }
+
+ /* Save current base address for post processing of hard-links. */
+ if (ilinkref || state->cbs_previlinkref) {
+ uiobase = uio_curriovbase(state->cbs_uio);
+ }
+ /* If this entry won't fit then we're done */
+ if ((uiosize > (user_size_t)uio_resid(state->cbs_uio)) ||
+ (ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks)) {
+ return (0); /* stop */
+ }
+
+ 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;
+
+ /* Remember previous entry */
+ state->cbs_desc->cd_cnid = cnid;
+ if (type == DT_DIR) {
+ state->cbs_desc->cd_flags |= CD_ISDIR;
+ } else {
+ state->cbs_desc->cd_flags &= ~CD_ISDIR;
+ }
+ if (state->cbs_desc->cd_nameptr != NULL) {
+ state->cbs_desc->cd_namelen = 0;
+ }
+#if 0
+ state->cbs_desc->cd_encoding = xxxx;
+#endif
+ if (!is_mangled) {
+ state->cbs_desc->cd_namelen = namelen;
+ bcopy(nameptr, state->cbs_namebuf, namelen + 1);
+ } else {
+ /* Store unmangled name for the directory hint else it will
+ * restart readdir at the last location again
+ */
+ u_int8_t *new_nameptr;
+ size_t bufsize;
+ size_t tmp_namelen = 0;
+
+ cnp = (const CatalogName *)&ckp->hfsPlus.nodeName;
+ bufsize = 1 + utf8_encodelen(cnp->ustr.unicode,
+ cnp->ustr.length * sizeof(UniChar),
+ ':', 0);
+ MALLOC(new_nameptr, u_int8_t *, bufsize, M_TEMP, M_WAITOK);
+ result = utf8_encodestr(cnp->ustr.unicode,
+ cnp->ustr.length * sizeof(UniChar),
+ new_nameptr, &tmp_namelen, bufsize, ':', 0);
+
+ state->cbs_desc->cd_namelen = tmp_namelen;
+ bcopy(new_nameptr, state->cbs_namebuf, tmp_namelen + 1);
+
+ FREE(new_nameptr, M_TEMP);
+ }
}
- if (hfsmp->jnl &&
- (rec->recordType == kHFSPlusFileRecord) &&
- ((rec->hfsPlusFile.fileID == hfsmp->hfs_jnlfileid) ||
- (rec->hfsPlusFile.fileID == hfsmp->hfs_jnlinfoblkid))) {
+ if (state->cbs_hasprevdirentry) {
+ curlinkref = ilinkref; /* save current */
+ ilinkref = state->cbs_previlinkref; /* use previous */
+ }
+ /*
+ * Record any hard links for post processing.
+ */
+ if ((ilinkref != 0) &&
+ (state->cbs_result == 0) &&
+ (state->cbs_nlinks < state->cbs_maxlinks)) {
+ state->cbs_linkinfo[state->cbs_nlinks].dirent_addr = uiobase;
+ state->cbs_linkinfo[state->cbs_nlinks].link_ref = ilinkref;
+ state->cbs_nlinks++;
+ }
+ if (state->cbs_hasprevdirentry) {
+ ilinkref = curlinkref; /* restore current */
+ }
+ }
- return (1); /* continue */
+ /* Fill the direntry to be used the next time */
+ if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
+ if (stop_after_pack) {
+ state->cbs_eof = true;
+ return (0); /* stop */
}
+ entry->d_type = type;
+ entry->d_namlen = namelen;
+ entry->d_reclen = EXT_DIRENT_LEN(namelen);
+ if (hide) {
+ /* File number = 0 means skip entry */
+ entry->d_fileno = 0;
+ } else {
+ entry->d_fileno = cnid;
+ }
+ /* swap the current and previous entry */
+ struct direntry * tmp;
+ tmp = state->cbs_direntry;
+ state->cbs_direntry = state->cbs_prevdirentry;
+ state->cbs_prevdirentry = tmp;
+ state->cbs_hasprevdirentry = true;
+ state->cbs_previlinkref = ilinkref;
}
+ /* Continue iteration if there's room */
+ return (state->cbs_result == 0 &&
+ uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
+}
- cep = &list->entry[list->realentries++];
+#if CONFIG_HFS_STD
+/*
+ * getdirentries callback for standard HFS (non HFS+) directories.
+ */
+static int
+getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp,
+ struct packdirentry_state *state)
+{
+ struct hfsmount *hfsmp;
+ const CatalogName *cnp;
+ cnid_t curID;
+ OSErr result;
+ struct dirent catent;
+ cnid_t cnid;
+ u_int8_t type = DT_UNKNOWN;
+ u_int8_t *nameptr;
+ size_t namelen = 0;
+ size_t maxnamelen;
+ size_t uiosize = 0;
+ caddr_t uioaddr;
+
+ hfsmp = state->cbs_hfsmp;
- if (state->stdhfs) {
- struct HFSPlusCatalogFile cnoderec;
- HFSPlusCatalogKey * pluskey;
- long encoding;
+ curID = ckp->hfs.parentID;
- promoteattr(hfsmp, rec, &cnoderec);
- getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
+ /* We're done when parent directory changes */
+ if (state->cbs_parentID != curID) {
+ state->cbs_result = ENOENT;
+ return (0); /* stop */
+ }
- MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
- promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
- builddesc(pluskey, getcnid(rec), node, encoding, isadir(rec), &cep->ce_desc);
- FREE(pluskey, M_TEMP);
+ nameptr = (u_int8_t *)&catent.d_name[0];
+ maxnamelen = sizeof(catent.d_name);
- if (rec->recordType == kHFSFileRecord) {
- int blksize = HFSTOVCB(hfsmp)->blockSize;
+ switch(crp->recordType) {
+ case kHFSFolderRecord:
+ type = DT_DIR;
+ cnid = crp->hfsFolder.folderID;
+ break;
+ case kHFSFileRecord:
+ type = DT_REG;
+ cnid = crp->hfsFile.fileID;
+ break;
+ default:
+ return (0); /* stop */
+ };
- cep->ce_datasize = rec->hfsFile.dataLogicalSize;
- cep->ce_datablks = rec->hfsFile.dataPhysicalSize / blksize;
- cep->ce_rsrcsize = rec->hfsFile.rsrcLogicalSize;
- cep->ce_rsrcblks = rec->hfsFile.rsrcPhysicalSize / blksize;
+ cnp = (const CatalogName*) ckp->hfs.nodeName;
+ 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, (ByteCount *)&namelen, nameptr);
+ }
+ catent.d_type = type;
+ catent.d_namlen = namelen;
+ catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
+ catent.d_fileno = cnid;
+ uioaddr = (caddr_t) &catent;
+
+ /* If this entry won't fit then we're done */
+ if (uiosize > (user_size_t)uio_resid(state->cbs_uio)) {
+ return (0); /* stop */
+ }
+
+ state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
+ if (state->cbs_result == 0) {
+ ++state->cbs_index;
+
+ /* Remember previous entry */
+ state->cbs_desc->cd_cnid = cnid;
+ if (type == DT_DIR) {
+ state->cbs_desc->cd_flags |= CD_ISDIR;
+ } else {
+ state->cbs_desc->cd_flags &= ~CD_ISDIR;
}
- } else {
- getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
- builddesc((HFSPlusCatalogKey *)key, getcnid(rec), node, 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_rdev = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
+ if (state->cbs_desc->cd_nameptr != NULL) {
+ state->cbs_desc->cd_namelen = 0;
}
+ state->cbs_desc->cd_namelen = namelen;
+ bcopy(nameptr, state->cbs_namebuf, namelen + 1);
}
- return (list->realentries < list->maxentries);
+ /* Continue iteration if there's room */
+ return (state->cbs_result == 0 && uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
}
+#endif
/*
- * Note: index is zero relative
+ * Pack a uio buffer with directory entries from the catalog
*/
int
-cat_getentriesattr(struct hfsmount *hfsmp, struct cat_desc *prevdesc, int index,
- struct cat_entrylist *ce_list)
+cat_getdirentries(struct hfsmount *hfsmp, u_int32_t entrycnt, directoryhint_t *dirhint,
+ uio_t uio, int flags, int * items, int * eofflag)
{
FCB* fcb;
- CatalogKey * key;
BTreeIterator * iterator;
- struct readattr_state state;
- cnid_t parentcnid;
- int i;
- int std_hfs;
- int result = 0;
-
- ce_list->realentries = 0;
-
- fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
- std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
- parentcnid = prevdesc->cd_parentcnid;
-
- state.hfsmp = hfsmp;
- state.list = ce_list;
- state.dir_cnid = parentcnid;
- state.stdhfs = std_hfs;
- state.error = 0;
+ CatalogKey * key;
+ struct packdirentry_state state;
+ void * buffer;
+ int bufsize;
+ int maxlinks;
+ int result;
+ int index;
+ int have_key;
+ int extended;
+
+ extended = flags & VNODE_READDIR_EXTENDED;
+
+ if (extended && (hfsmp->hfs_flags & HFS_STANDARD)) {
+ return (ENOTSUP);
+ }
+ fcb = hfsmp->hfs_catalog_cp->c_datafork;
- MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
- bzero(iterator, sizeof(*iterator));
+ /*
+ * Get a buffer for link info array, btree iterator and a direntry:
+ */
+ 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_flags = flags;
+ state.cbs_hasprevdirentry = false;
+ state.cbs_previlinkref = 0;
+ state.cbs_nlinks = 0;
+ state.cbs_maxlinks = maxlinks;
+ state.cbs_linkinfo = (linkinfo_t *)((char *)buffer + MAXPATHLEN);
+ /*
+ * We need to set cbs_eof to false regardless of whether or not the
+ * control flow is actually in the extended case, since we use this
+ * field to track whether or not we've returned EOF from the iterator function.
+ */
+ state.cbs_eof = false;
+
+ iterator = (BTreeIterator *) ((char *)state.cbs_linkinfo + (maxlinks * sizeof(linkinfo_t)));
key = (CatalogKey *)&iterator->key;
- iterator->hint.nodeNum = prevdesc->cd_hint;
+ have_key = 0;
+ index = dirhint->dh_index + 1;
+ if (extended) {
+ state.cbs_direntry = (struct direntry *)((char *)iterator + sizeof(BTreeIterator));
+ state.cbs_prevdirentry = state.cbs_direntry + 1;
+ }
+ /*
+ * Attempt to build a key from cached filename
+ */
+ if (dirhint->dh_desc.cd_namelen != 0) {
+ if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
+ iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
+ have_key = 1;
+ }
+ }
+
+ if (index == 0 && dirhint->dh_threadhint != 0) {
+ /*
+ * Position the iterator at the directory's thread record.
+ * (i.e. just before the first entry)
+ */
+ buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
+ iterator->hint.nodeNum = dirhint->dh_threadhint;
+ iterator->hint.index = 0;
+ have_key = 1;
+ }
/*
- * If the last entry wasn't cached then establish the iterator
+ * If the last entry wasn't cached then position the btree iterator
*/
- if ((index == 0) ||
- (prevdesc->cd_namelen == 0) ||
- (buildkey(hfsmp, prevdesc, (HFSPlusCatalogKey *)key, 0) != 0)) {
- int i;
+ if (!have_key) {
/*
- * Position the iterator at the directory thread.
- * (ie just before the first entry)
+ * Position the iterator at the directory's thread record.
+ * (i.e. just before the first entry)
*/
- buildthreadkey(parentcnid, std_hfs, key);
+ buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
- if (result)
- goto exit; /* bad news */
+ if (result) {
+ result = MacToVFSError(result);
+ goto cleanup;
+ }
+ if (index == 0) {
+ dirhint->dh_threadhint = iterator->hint.nodeNum;
+ }
/*
* Iterate until we reach the entry just
* before the one we want to start with.
*/
- for (i = 0; i < index; ++i) {
- result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, NULL, NULL);
- if (result)
- goto exit; /* bad news */
+ if (index > 0) {
+ struct position_state ps;
+
+ ps.error = 0;
+ ps.count = 0;
+ ps.index = index;
+ ps.parentID = dirhint->dh_desc.cd_parentcnid;
+ ps.hfsmp = hfsmp;
+
+ result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
+ (IterateCallBackProcPtr)cat_findposition, &ps);
+ if (ps.error)
+ result = ps.error;
+ else
+ result = MacToVFSError(result);
+ if (result) {
+ result = MacToVFSError(result);
+ goto cleanup;
+ }
}
}
- /* Fill list with entries. */
- result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
- (IterateCallBackProcPtr)catrec_readattr, &state);
+ state.cbs_index = index;
+ state.cbs_hfsmp = hfsmp;
+ state.cbs_uio = uio;
+ state.cbs_desc = &dirhint->dh_desc;
+ state.cbs_namebuf = (u_int8_t *)buffer;
+ state.cbs_result = 0;
+ state.cbs_parentID = dirhint->dh_desc.cd_parentcnid;
+
+ /* Use a temporary buffer to hold intermediate descriptor names. */
+ if (dirhint->dh_desc.cd_namelen > 0 && dirhint->dh_desc.cd_nameptr != NULL) {
+ bcopy(dirhint->dh_desc.cd_nameptr, buffer, dirhint->dh_desc.cd_namelen+1);
+ if (dirhint->dh_desc.cd_flags & CD_HASBUF) {
+ dirhint->dh_desc.cd_flags &= ~CD_HASBUF;
+ vfs_removename((const char *)dirhint->dh_desc.cd_nameptr);
+ }
+ }
+ dirhint->dh_desc.cd_nameptr = (u_int8_t *)buffer;
- if (state.error)
- result = state.error;
- else if (ce_list->realentries == 0)
- result = ENOENT;
+ enum BTreeIterationOperations op;
+ if (extended && index != 0 && have_key)
+ op = kBTreeCurrentRecord;
else
- result = MacToVFSError(result);
-
- if (std_hfs)
- goto exit;
+ op = kBTreeNextRecord;
/*
- * Resolve any hard links.
+ * Process as many entries as possible starting at iterator->key.
*/
- for (i = 0; i < ce_list->realentries; ++i) {
- struct FndrFileInfo *fip;
- struct cat_entry *cep;
- struct HFSPlusCatalogFile filerec;
-
- cep = &ce_list->entry[i];
- if (!S_ISREG(cep->ce_attr.ca_mode))
- continue;
+ if ((hfsmp->hfs_flags & HFS_STANDARD) == 0) {
+ /* HFS+ */
+ result = BTIterateRecords(fcb, op, iterator,
+ (IterateCallBackProcPtr)getdirentries_callback, &state);
- /* Note: Finder info is still in Big Endian */
- fip = (struct FndrFileInfo *)&cep->ce_attr.ca_finderinfo;
+ /* For extended calls, every call to getdirentries_callback()
+ * transfers the previous directory entry found to the user
+ * buffer. Therefore when BTIterateRecords reaches the end of
+ * Catalog BTree, call getdirentries_callback() again with
+ * dummy values to copy the last directory entry stored in
+ * packdirentry_state
+ */
+ if (extended && (result == fsBTRecordNotFoundErr)) {
+ CatalogKey ckp;
+ CatalogRecord crp;
- /* Check for hard link signature. */
- if ((cep->ce_attr.ca_rdev != 0)
- && (SWAP_BE32(fip->fdType) == kHardLinkFileType)
- && (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)
- && ((cep->ce_attr.ca_itime == HFSTOVCB(hfsmp)->vcbCrDate) ||
- (cep->ce_attr.ca_itime == hfsmp->hfs_metadata_createdate))) {
+ bzero(&ckp, sizeof(ckp));
+ bzero(&crp, sizeof(crp));
- if (resolvelink(hfsmp, cep->ce_attr.ca_rdev, &filerec) != 0)
- continue;
- /* Repack entry from inode record. */
- getbsdattr(hfsmp, &filerec, &cep->ce_attr);
- cep->ce_datasize = filerec.dataFork.logicalSize;
- cep->ce_datablks = filerec.dataFork.totalBlocks;
- cep->ce_rsrcsize = filerec.resourceFork.logicalSize;
- cep->ce_rsrcblks = filerec.resourceFork.totalBlocks;
+ result = getdirentries_callback(&ckp, &crp, &state);
}
}
-exit:
- FREE(iterator, M_TEMP);
+#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;
+ }
- return MacToVFSError(result);
-}
+ /* Finish updating the catalog iterator. */
+ dirhint->dh_desc.cd_hint = iterator->hint.nodeNum;
+ dirhint->dh_desc.cd_flags |= CD_DECOMPOSED;
+ dirhint->dh_index = index - 1;
+
+ /* Fix up the name. */
+ if (dirhint->dh_desc.cd_namelen > 0) {
+ dirhint->dh_desc.cd_nameptr = (const u_int8_t *)vfs_addname((char *)buffer, dirhint->dh_desc.cd_namelen, 0, 0);
+ dirhint->dh_desc.cd_flags |= CD_HASBUF;
+ } else {
+ dirhint->dh_desc.cd_nameptr = NULL;
+ dirhint->dh_desc.cd_namelen = 0;
+ }
+
+ /*
+ * Post process any hard links to get the real file id.
+ */
+ if (state.cbs_nlinks > 0) {
+ ino_t fileid = 0;
+ user_addr_t address;
+ int i;
+ for (i = 0; i < state.cbs_nlinks; ++i) {
+ if (resolvelinkid(hfsmp, state.cbs_linkinfo[i].link_ref, &fileid) != 0)
+ continue;
+ /* This assumes that d_ino is always first field. */
+ address = state.cbs_linkinfo[i].dirent_addr;
+ if (address == (user_addr_t)0)
+ continue;
+ if (uio_isuserspace(uio)) {
+ if (extended) {
+ ino64_t fileid_64 = (ino64_t)fileid;
+ (void) copyout(&fileid_64, address, sizeof(fileid_64));
+ } else {
+ (void) copyout(&fileid, address, sizeof(fileid));
+ }
+ } else /* system space */ {
+ if (extended) {
+ ino64_t fileid_64 = (ino64_t)fileid;
+ bcopy(&fileid_64, (void*) CAST_DOWN(caddr_t, address), sizeof(fileid_64));
+ } else {
+ bcopy(&fileid, (void*) CAST_DOWN(caddr_t, address), sizeof(fileid));
+ }
+ }
+ }
+ }
-struct read_state {
- u_int32_t cbs_parentID;
- u_int32_t cbs_hiddenDirID;
- u_int32_t cbs_hiddenJournalID;
- u_int32_t cbs_hiddenInfoBlkID;
- off_t cbs_lastoffset;
- struct uio * cbs_uio;
- ExtendedVCB * cbs_vcb;
- int16_t cbs_hfsPlus;
- int16_t cbs_result;
-};
+ if (state.cbs_result)
+ result = state.cbs_result;
+ else
+ result = MacToVFSError(result);
+ if (result == ENOENT) {
+ result = 0;
+ }
-static int
-catrec_read(const CatalogKey *ckp, const CatalogRecord *crp,
- u_int16_t recordLen, struct read_state *state)
-{
- CatalogName *cnp;
- size_t utf8chars;
- u_int32_t curID;
- OSErr result;
- struct dirent catent;
+cleanup:
+ FREE(buffer, M_TEMP);
- if (state->cbs_hfsPlus)
- curID = ckp->hfsPlus.parentID;
- else
- curID = ckp->hfs.parentID;
+ return (result);
+}
+
- /* We're done when parent directory changes */
- if (state->cbs_parentID != curID) {
-lastitem:
/*
- * The NSDirectoryList class chokes on empty records (it doesnt check d_reclen!)
- * so remove padding for now...
+ * Callback to establish directory position.
+ * Called with position_state for each item in a directory.
*/
-#if 0
- /*
- * Pad the end of list with an empty record.
- * This eliminates an extra call by readdir(3c).
- */
- catent.d_fileno = 0;
- catent.d_reclen = 0;
- catent.d_type = 0;
- catent.d_namlen = 0;
- *(int32_t*)&catent.d_name[0] = 0;
-
- state->cbs_lastoffset = state->cbs_uio->uio_offset;
+static int
+cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
+ struct position_state *state)
+{
+ cnid_t curID = 0;
- state->cbs_result = uiomove((caddr_t) &catent, 12, state->cbs_uio);
- if (state->cbs_result == 0)
- state->cbs_result = ENOENT;
-#else
- state->cbs_lastoffset = state->cbs_uio->uio_offset;
- state->cbs_result = ENOENT;
-#endif
- return (0); /* stop */
+ if ((state->hfsmp->hfs_flags & HFS_STANDARD) == 0) {
+ curID = ckp->hfsPlus.parentID;
}
+#if CONFIG_HFS_STD
+ else {
+ curID = ckp->hfs.parentID;
+ }
+#endif
- if (state->cbs_hfsPlus) {
- switch(crp->recordType) {
- case kHFSPlusFolderRecord:
- catent.d_type = DT_DIR;
- catent.d_fileno = crp->hfsPlusFolder.folderID;
- break;
- case kHFSPlusFileRecord:
- catent.d_type = DT_REG;
- catent.d_fileno = crp->hfsPlusFile.fileID;
- break;
- default:
- return (0); /* stop */
- };
-
- cnp = (CatalogName*) &ckp->hfsPlus.nodeName;
- result = utf8_encodestr(cnp->ustr.unicode, cnp->ustr.length * sizeof(UniChar),
- catent.d_name, &utf8chars, kdirentMaxNameBytes + 1, ':', 0);
- if (result == ENAMETOOLONG) {
- result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
- cnp->ustr.unicode, kdirentMaxNameBytes + 1, (ByteCount*)&utf8chars, catent.d_name, catent.d_fileno);
- }
- } else { /* hfs */
- switch(crp->recordType) {
- case kHFSFolderRecord:
- catent.d_type = DT_DIR;
- catent.d_fileno = crp->hfsFolder.folderID;
- break;
- case kHFSFileRecord:
- catent.d_type = DT_REG;
- catent.d_fileno = crp->hfsFile.fileID;
- break;
- default:
- return (0); /* stop */
- };
-
- cnp = (CatalogName*) ckp->hfs.nodeName;
- result = hfs_to_utf8(state->cbs_vcb, cnp->pstr, kdirentMaxNameBytes + 1,
- (ByteCount *)&utf8chars, catent.d_name);
- /*
- * 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, kdirentMaxNameBytes + 1,
- (ByteCount *)&utf8chars, catent.d_name);
+ /* Make sure parent directory didn't change */
+ if (state->parentID != curID) {
+ state->error = EINVAL;
+ return (0); /* stop */
}
- catent.d_namlen = utf8chars;
- catent.d_reclen = DIRENTRY_SIZE(utf8chars);
-
- /* hide our private meta data directory */
- if (curID == kRootDirID &&
- catent.d_fileno == state->cbs_hiddenDirID &&
- catent.d_type == DT_DIR)
- goto lastitem;
+ /* Count this entry */
+ switch(crp->recordType) {
+ case kHFSPlusFolderRecord:
+ case kHFSPlusFileRecord:
+#if CONFIG_HFS_STD
+ case kHFSFolderRecord:
+ case kHFSFileRecord:
+#endif
+ ++state->count;
+ break;
+ default:
+ printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
+ crp->recordType, curID);
+ state->error = EINVAL;
+ return (0); /* stop */
+ };
+
+ return (state->count < state->index);
+}
- /* Hide the journal files */
- if ((curID == kRootDirID) &&
- (catent.d_type == DT_REG) &&
- ((catent.d_fileno == state->cbs_hiddenJournalID) ||
- (catent.d_fileno == state->cbs_hiddenInfoBlkID))) {
- return (1); /* skip and continue */
- }
+/*
+ * cat_binarykeycompare - compare two HFS Plus catalog keys.
- state->cbs_lastoffset = state->cbs_uio->uio_offset;
+ * The name portion of the key is compared using a 16-bit binary comparison.
+ * This is called from the b-tree code.
+ */
+int
+cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
+{
+ u_int32_t searchParentID, trialParentID;
+ int result;
- /* if this entry won't fit then we're done */
- if (catent.d_reclen > state->cbs_uio->uio_resid)
- return (0); /* stop */
+ searchParentID = searchKey->parentID;
+ trialParentID = trialKey->parentID;
+ result = 0;
+
+ if (searchParentID > trialParentID) {
+ ++result;
+ } else if (searchParentID < trialParentID) {
+ --result;
+ } else {
+ u_int16_t * str1 = &searchKey->nodeName.unicode[0];
+ u_int16_t * str2 = &trialKey->nodeName.unicode[0];
+ int length1 = searchKey->nodeName.length;
+ int length2 = trialKey->nodeName.length;
- state->cbs_result = uiomove((caddr_t) &catent, catent.d_reclen, state->cbs_uio);
+ result = UnicodeBinaryCompare (str1, length1, str2, length2);
+ }
- /* continue iteration if there's room */
- return (state->cbs_result == 0 &&
- state->cbs_uio->uio_resid >= AVERAGE_HFSDIRENTRY_SIZE);
+ return result;
}
+
+#if CONFIG_HFS_STD
/*
+ * Compare two standard HFS catalog keys
*
+ * Result: +n search key > trial key
+ * 0 search key = trial key
+ * -n search key < trial key
*/
int
-cat_getdirentries(struct hfsmount *hfsmp, struct cat_desc *descp,
- struct uio *uio, int *eofflag)
+CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
{
- ExtendedVCB *vcb = HFSTOVCB(hfsmp);
- BTreeIterator * iterator;
- CatalogIterator *cip;
- u_int32_t diroffset;
- u_int16_t op;
- struct read_state state;
- u_int32_t dirID = descp->cd_cnid;
+ cnid_t searchParentID, trialParentID;
int result;
- diroffset = uio->uio_offset;
- *eofflag = 0;
-
- MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
- bzero(iterator, sizeof(*iterator));
-
- /* get an iterator and position it */
- cip = GetCatalogIterator(vcb, dirID, diroffset);
-
- result = PositionIterator(cip, diroffset, iterator, &op);
- if (result == cmNotFound) {
- *eofflag = 1;
- result = 0;
- AgeCatalogIterator(cip);
- goto cleanup;
- } else if ((result = MacToVFSError(result)))
- goto cleanup;
-
- state.cbs_hiddenDirID = hfsmp->hfs_private_metadata_dir;
- if (hfsmp->jnl) {
- state.cbs_hiddenJournalID = hfsmp->hfs_jnlfileid;
- state.cbs_hiddenInfoBlkID = hfsmp->hfs_jnlinfoblkid;
- }
-
- state.cbs_lastoffset = cip->currentOffset;
- state.cbs_vcb = vcb;
- state.cbs_uio = uio;
- state.cbs_result = 0;
- state.cbs_parentID = dirID;
+ searchParentID = searchKey->parentID;
+ trialParentID = trialKey->parentID;
- if (vcb->vcbSigWord == kHFSPlusSigWord)
- state.cbs_hfsPlus = 1;
- else
- state.cbs_hfsPlus = 0;
+ if (searchParentID > trialParentID)
+ result = 1;
+ else if (searchParentID < trialParentID)
+ result = -1;
+ else /* parent dirID's are equal, compare names */
+ result = FastRelString(searchKey->nodeName, trialKey->nodeName);
- /* process as many entries as possible... */
- result = BTIterateRecords(GetFileControlBlock(vcb->catalogRefNum), op,
- iterator, (IterateCallBackProcPtr)catrec_read, &state);
+ return result;
+}
+#endif
- if (state.cbs_result)
- result = state.cbs_result;
- else
- result = MacToVFSError(result);
- if (result == ENOENT) {
- *eofflag = 1;
- result = 0;
- }
+/*
+ * Compare two HFS+ catalog keys
+ *
+ * Result: +n search key > trial key
+ * 0 search key = trial key
+ * -n search key < trial key
+ */
+int
+CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
+{
+ cnid_t searchParentID, trialParentID;
+ int result;
- if (result == 0) {
- cip->currentOffset = state.cbs_lastoffset;
- cip->nextOffset = uio->uio_offset;
- UpdateCatalogIterator(iterator, cip);
+ searchParentID = searchKey->parentID;
+ trialParentID = trialKey->parentID;
+
+ if (searchParentID > trialParentID) {
+ result = 1;
}
-
-cleanup:
- if (result) {
- cip->volume = 0;
- cip->folderID = 0;
- AgeCatalogIterator(cip);
+ else if (searchParentID < trialParentID) {
+ result = -1;
+ } else {
+ /* parent node ID's are equal, compare names */
+ if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
+ result = searchKey->nodeName.length - trialKey->nodeName.length;
+ else
+ result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
+ searchKey->nodeName.length,
+ &trialKey->nodeName.unicode[0],
+ trialKey->nodeName.length);
}
- (void) ReleaseCatalogIterator(cip);
- FREE(iterator, M_TEMP);
-
- return (result);
+ return result;
}
buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
HFSPlusCatalogKey *key, int retry)
{
- int utf8_flags = 0;
+ 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));
hfskey.parentID = key->parentID;
hfskey.nodeName[0] = 0;
if (key->nodeName.length > 0) {
- if (unicode_to_hfs(HFSTOVCB(hfsmp),
+ int res;
+ if ((res = unicode_to_hfs(HFSTOVCB(hfsmp),
key->nodeName.length * 2,
key->nodeName.unicode,
- &hfskey.nodeName[0], retry) != 0) {
- return (EINVAL);
+ &hfskey.nodeName[0], retry)) != 0) {
+ if (res != ENAMETOOLONG)
+ res = EINVAL;
+
+ return res;
}
hfskey.keyLength += hfskey.nodeName[0];
}
bcopy(&hfskey, key, sizeof(hfskey));
}
+#endif
+
return (0);
}
/*
* Resolve hard link reference to obtain the inode record.
*/
-__private_extern__
int
-resolvelink(struct hfsmount *hfsmp, u_long linkref, struct HFSPlusCatalogFile *recp)
+cat_resolvelink(struct hfsmount *hfsmp, u_int32_t linkref, int isdirlink, struct HFSPlusCatalogFile *recp)
{
FSBufferDescriptor btdata;
struct BTreeIterator *iterator;
struct cat_desc idesc;
char inodename[32];
+ cnid_t parentcnid;
int result = 0;
BDINIT(btdata, recp);
- MAKE_INODE_NAME(inodename, linkref);
+
+ if (isdirlink) {
+ MAKE_DIRINODE_NAME(inodename, sizeof(inodename), (unsigned int)linkref);
+ parentcnid = hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid;
+ } else {
+ MAKE_INODE_NAME(inodename, sizeof(inodename), (unsigned int)linkref);
+ parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
+ }
/* Get space for iterator */
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
bzero(iterator, sizeof(*iterator));
/* Build a descriptor for private dir. */
- idesc.cd_parentcnid = hfsmp->hfs_private_metadata_dir;
- idesc.cd_nameptr = inodename;
+ idesc.cd_parentcnid = parentcnid;
+ idesc.cd_nameptr = (const u_int8_t *)inodename;
idesc.cd_namelen = strlen(inodename);
idesc.cd_flags = 0;
idesc.cd_hint = 0;
if (result == 0) {
/* Make sure there's a reference */
- if (recp->bsdInfo.special.linkCount == 0)
- recp->bsdInfo.special.linkCount = 2;
+ if (recp->hl_linkCount == 0)
+ recp->hl_linkCount = 2;
} else {
- printf("HFS resolvelink: can't find %s\n", inodename);
+ printf("hfs: cat_resolvelink: can't find inode=%s on vol=%s\n", inodename, hfsmp->vcbVN);
}
FREE(iterator, M_TEMP);
return (result ? ENOENT : 0);
}
+/*
+ * Resolve hard link reference to obtain the inode number.
+ */
+static int
+resolvelinkid(struct hfsmount *hfsmp, u_int32_t linkref, ino_t *ino)
+{
+ struct HFSPlusCatalogFile record;
+ int error;
+
+ /*
+ * Since we know resolvelinkid is only called from
+ * cat_getdirentries, we can assume that only file
+ * hardlinks need to be resolved (cat_getdirentries
+ * can resolve directory hardlinks in place).
+ */
+ error = cat_resolvelink(hfsmp, linkref, 0, &record);
+ if (error == 0) {
+ if (record.fileID == 0)
+ error = ENOENT;
+ else
+ *ino = record.fileID;
+ }
+ return (error);
+}
+
/*
* getkey - get a key from id by doing a thread lookup
*/
{
struct BTreeIterator * iterator;
FSBufferDescriptor btdata;
- UInt16 datasize;
+ u_int16_t datasize;
CatalogKey * keyp;
CatalogRecord * recp;
int result;
/* 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:
return MacToVFSError(result);
}
+/*
+ * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
+ * null arguments to cat_idlookup instead, but we save around 10% by not building the
+ * cat_desc here). Both key and attrp must point to real structures.
+ *
+ * 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).
+ */
+int
+cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp)
+{
+ int result;
+
+ result = getkey(hfsmp, cnid, key);
+
+ if (result == 0) {
+ result = cat_lookupbykey(hfsmp, key, 0, 0, 0, NULL, attrp, NULL, NULL);
+ }
+ /*
+ * Check for a raw file hardlink inode.
+ * Fix up the parent id in the key if necessary.
+ * Only hard links created by Mac OS X 10.5 or later can be resolved here.
+ */
+ if ((result == 0) &&
+ (key->hfsPlus.parentID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
+ (attrp->ca_recflags & kHFSHasLinkChainMask)) {
+ cnid_t nextlinkid = 0;
+ cnid_t prevlinkid = 0;
+ struct cat_desc linkdesc;
+
+ /*
+ * 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_lookup_siblinglinks(hfsmp, cnid, &prevlinkid, &nextlinkid) == 0) &&
+ (nextlinkid != 0)) {
+ if (cat_findname(hfsmp, nextlinkid, &linkdesc) == 0) {
+ key->hfsPlus.parentID = linkdesc.cd_parentcnid;
+ cat_releasedesc(&linkdesc);
+ }
+ }
+ }
+ return MacToVFSError(result);
+}
+
/*
* buildrecord - build a default catalog directory or file record
*/
static void
buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding,
- CatalogRecord *crp, int *recordSize)
+ CatalogRecord *crp, u_int32_t *recordSize)
{
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;
- struct FndrFileInfo * fip = NULL;
if (type == S_IFDIR) {
- bzero(crp, sizeof(HFSPlusCatalogFolder));
crp->recordType = kHFSPlusFolderRecord;
+ crp->hfsPlusFolder.flags = attrp->ca_recflags;
+ crp->hfsPlusFolder.valence = 0;
crp->hfsPlusFolder.folderID = cnid;
crp->hfsPlusFolder.createDate = createtime;
crp->hfsPlusFolder.contentModDate = createtime;
- crp->hfsPlusFolder.accessDate = createtime;
crp->hfsPlusFolder.attributeModDate = createtime;
+ crp->hfsPlusFolder.accessDate = createtime;
+ crp->hfsPlusFolder.backupDate = 0;
crp->hfsPlusFolder.textEncoding = encoding;
+ crp->hfsPlusFolder.folderCount = 0;
bcopy(attrp->ca_finderinfo, &crp->hfsPlusFolder.userInfo, 32);
bsdp = &crp->hfsPlusFolder.bsdInfo;
+ bsdp->special.linkCount = 1;
*recordSize = sizeof(HFSPlusCatalogFolder);
} else {
- bzero(crp, sizeof(HFSPlusCatalogFile));
crp->recordType = kHFSPlusFileRecord;
+ crp->hfsPlusFile.flags = attrp->ca_recflags;
+ crp->hfsPlusFile.reserved1 = 0;
crp->hfsPlusFile.fileID = cnid;
crp->hfsPlusFile.createDate = createtime;
crp->hfsPlusFile.contentModDate = createtime;
crp->hfsPlusFile.accessDate = createtime;
crp->hfsPlusFile.attributeModDate = createtime;
- crp->hfsPlusFile.flags |= kHFSThreadExistsMask;
+ crp->hfsPlusFile.backupDate = 0;
crp->hfsPlusFile.textEncoding = encoding;
+ crp->hfsPlusFile.reserved2 = 0;
+ bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
bsdp = &crp->hfsPlusFile.bsdInfo;
- switch(type) {
- case S_IFBLK:
- case S_IFCHR:
- /* BLK/CHR need to save the device info */
+ /* BLK/CHR need to save the device info */
+ if (type == S_IFBLK || type == S_IFCHR) {
bsdp->special.rawDevice = attrp->ca_rdev;
- break;
- case S_IFREG:
- /* Hardlink links need to save the linkref */
- fip = (FndrFileInfo *)&attrp->ca_finderinfo;
- if ((SWAP_BE32(fip->fdType) == kHardLinkFileType) &&
- (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)) {
- bsdp->special.iNodeNum = attrp->ca_rdev;
- }
- bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
- break;
- case S_IFLNK:
- /* Symlinks also have a type and creator */
- bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
- break;
+ } else {
+ bsdp->special.linkCount = 1;
}
+ bzero(&crp->hfsPlusFile.dataFork, 2*sizeof(HFSPlusForkData));
*recordSize = sizeof(HFSPlusCatalogFile);
}
bsdp->ownerID = attrp->ca_uid;
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
+
}
* builddesc - build a cnode descriptor from an HFS+ key
*/
static int
-builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
+builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_int32_t hint, u_int32_t encoding,
int isdir, struct cat_desc *descp)
{
int result = 0;
- char * nameptr;
- long bufsize;
+ unsigned char * nameptr;
+ size_t bufsize;
size_t utf8len;
+ unsigned char tmpbuff[128];
/* guess a size... */
bufsize = (3 * key->nodeName.length) + 1;
- MALLOC(nameptr, char *, bufsize, M_TEMP, M_WAITOK);
+ if (bufsize >= sizeof(tmpbuff) - 1) {
+ MALLOC(nameptr, unsigned char *, bufsize, M_TEMP, M_WAITOK);
+ } else {
+ nameptr = &tmpbuff[0];
+ }
result = utf8_encodestr(key->nodeName.unicode,
key->nodeName.length * sizeof(UniChar),
key->nodeName.length * sizeof(UniChar),
':', 0);
FREE(nameptr, M_TEMP);
- MALLOC(nameptr, char *, bufsize, M_TEMP, M_WAITOK);
+ MALLOC(nameptr, unsigned char *, bufsize, M_TEMP, M_WAITOK);
result = utf8_encodestr(key->nodeName.unicode,
key->nodeName.length * sizeof(UniChar),
bufsize, ':', 0);
}
descp->cd_parentcnid = key->parentID;
- descp->cd_nameptr = nameptr;
+ descp->cd_nameptr = (const u_int8_t *)vfs_addname((char *)nameptr, utf8len, 0, 0);
descp->cd_namelen = utf8len;
descp->cd_cnid = cnid;
descp->cd_hint = hint;
descp->cd_flags = CD_DECOMPOSED | CD_HASBUF;
if (isdir)
- descp->cd_flags |= CD_ISDIR;
+ descp->cd_flags |= CD_ISDIR;
descp->cd_encoding = encoding;
+ if (nameptr != &tmpbuff[0]) {
+ FREE(nameptr, M_TEMP);
+ }
return result;
}
int isDirectory = (crp->recordType == kHFSPlusFolderRecord);
const struct HFSPlusBSDInfo *bsd = &crp->bsdInfo;
- attrp->ca_nlink = 1;
+ attrp->ca_recflags = crp->flags;
attrp->ca_atime = to_bsd_time(crp->accessDate);
+ attrp->ca_atimeondisk = attrp->ca_atime;
attrp->ca_mtime = to_bsd_time(crp->contentModDate);
- attrp->ca_mtime_nsec = 0;
attrp->ca_ctime = to_bsd_time(crp->attributeModDate);
attrp->ca_itime = to_bsd_time(crp->createDate);
attrp->ca_btime = to_bsd_time(crp->backupDate);
attrp->ca_flags = 0;
attrp->ca_uid = hfsmp->hfs_uid;
attrp->ca_gid = hfsmp->hfs_gid;
- if (isDirectory)
+ if (isDirectory) {
attrp->ca_mode = S_IFDIR | (hfsmp->hfs_dir_mask & ACCESSPERMS);
- else
+ } else {
attrp->ca_mode = S_IFREG | (hfsmp->hfs_file_mask & ACCESSPERMS);
+ }
+ attrp->ca_linkcount = 1;
attrp->ca_rdev = 0;
} else {
+ attrp->ca_linkcount = 1; /* may be overridden below */
attrp->ca_rdev = 0;
attrp->ca_uid = bsd->ownerID;
attrp->ca_gid = bsd->groupID;
case S_IFBLK:
attrp->ca_rdev = bsd->special.rawDevice;
break;
+
+ case S_IFDIR: /* fall through */
case S_IFREG:
/* Pick up the hard link count */
if (bsd->special.linkCount > 0)
- attrp->ca_nlink = bsd->special.linkCount;
+ attrp->ca_linkcount = bsd->special.linkCount;
break;
}
- if (HFSTOVFS(hfsmp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
- /*
- * Override the permissions as determined by the mount auguments
- * in ALMOST the same way unset permissions are treated but keep
- * track of whether or not the file or folder is hfs locked
- * by leaving the h_pflags field unchanged from what was unpacked
- * out of the catalog.
- */
- attrp->ca_uid = hfsmp->hfs_uid;
- attrp->ca_gid = hfsmp->hfs_gid;
- }
+ /*
+ * Override the permissions as determined by the mount auguments
+ * in ALMOST the same way unset permissions are treated but keep
+ * track of whether or not the file or folder is hfs locked
+ * by leaving the h_pflags field unchanged from what was unpacked
+ * out of the catalog.
+ */
+ /*
+ * This code was used to do UID translation with MNT_IGNORE_OWNERS
+ * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
+ * at the VFS layer, so there is no need to do it here now; this also
+ * allows VFS to let root see the real UIDs.
+ *
+ * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
+ * attrp->ca_uid = hfsmp->hfs_uid;
+ * attrp->ca_gid = hfsmp->hfs_gid;
+ * }
+ */
}
if (isDirectory) {
attrp->ca_mode &= ~S_IFMT;
attrp->ca_mode |= S_IFDIR;
}
- attrp->ca_nlink = 2 + ((HFSPlusCatalogFolder *)crp)->valence;
- attrp->ca_entries = ((HFSPlusCatalogFolder *)crp)->valence;
+ attrp->ca_entries = ((const HFSPlusCatalogFolder *)crp)->valence;
+ attrp->ca_dircount = ((hfsmp->hfs_flags & HFS_FOLDERCOUNT) && (attrp->ca_recflags & kHFSHasFolderCountMask)) ?
+ ((const HFSPlusCatalogFolder *)crp)->folderCount : 0;
+
+ /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
+ if (((const HFSPlusCatalogFolder *)crp)->userInfo.frFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
+ attrp->ca_flags |= UF_HIDDEN;
} else {
/* Keep IMMUTABLE bits in sync with HFS locked flag */
if (crp->flags & kHFSFileLockedMask) {
/* The file's supposed to be unlocked: */
attrp->ca_flags &= ~(SF_IMMUTABLE | UF_IMMUTABLE);
}
+ /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
+ if (crp->userInfo.fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
+ attrp->ca_flags |= UF_HIDDEN;
/* get total blocks (both forks) */
attrp->ca_blocks = crp->dataFork.totalBlocks + crp->resourceFork.totalBlocks;
+
+ /* On HFS+ the ThreadExists flag must always be set. */
+ if ((hfsmp->hfs_flags & HFS_STANDARD) == 0)
+ attrp->ca_recflags |= kHFSThreadExistsMask;
+
+ /* Pick up the hardlink first link, if any. */
+ attrp->ca_firstlink = (attrp->ca_recflags & kHFSHasLinkChainMask) ? crp->hl_firstLinkID : 0;
}
attrp->ca_fileid = crp->fileID;
bcopy(&crp->userInfo, attrp->ca_finderinfo, 32);
}
+#if CONFIG_HFS_STD
/*
* promotekey - promote hfs key to hfs plus key
*
*/
static void
promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey,
- HFSPlusCatalogKey *keyp, u_long *encoding)
+ HFSPlusCatalogKey *keyp, u_int32_t *encoding)
{
hfs_to_unicode_func_t hfs_get_unicode = hfsmp->hfs_get_unicode;
- UInt32 uniCount;
+ u_int32_t uniCount;
int error;
*encoding = hfsmp->hfs_encoding;
int resource, struct cat_fork * forkp)
{
struct HFSPlusExtentDescriptor *xp;
- u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
+ u_int32_t blocksize = HFSTOVCB(hfsmp)->blockSize;
bzero(forkp, sizeof(*forkp));
xp = &forkp->cf_extents[0];
if (resource) {
forkp->cf_size = filep->rsrcLogicalSize;
forkp->cf_blocks = filep->rsrcPhysicalSize / blocksize;
+ forkp->cf_bytesread = 0;
+ forkp->cf_vblocks = 0;
xp[0].startBlock = (u_int32_t)filep->rsrcExtents[0].startBlock;
xp[0].blockCount = (u_int32_t)filep->rsrcExtents[0].blockCount;
xp[1].startBlock = (u_int32_t)filep->rsrcExtents[1].startBlock;
} else {
forkp->cf_size = filep->dataLogicalSize;
forkp->cf_blocks = filep->dataPhysicalSize / blocksize;
+ forkp->cf_bytesread = 0;
+ forkp->cf_vblocks = 0;
xp[0].startBlock = (u_int32_t)filep->dataExtents[0].startBlock;
xp[0].blockCount = (u_int32_t)filep->dataExtents[0].blockCount;
xp[1].startBlock = (u_int32_t)filep->dataExtents[1].startBlock;
}
/*
- * promoteattr - promote hfs catalog attributes to hfs plus
+ * promoteattr - promote standard hfs catalog attributes to hfs plus
*
*/
static void
promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp)
{
- u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
+ u_int32_t blocksize = HFSTOVCB(hfsmp)->blockSize;
if (dataPtr->recordType == kHFSFolderRecord) {
- struct HFSCatalogFolder * folder;
+ const struct HFSCatalogFolder * folder;
- folder = (struct HFSCatalogFolder *) dataPtr;
+ folder = (const struct HFSCatalogFolder *) dataPtr;
crp->recordType = kHFSPlusFolderRecord;
crp->flags = folder->flags;
crp->fileID = folder->folderID;
crp->contentModDate = LocalToUTC(folder->modifyDate);
crp->backupDate = LocalToUTC(folder->backupDate);
crp->reserved1 = folder->valence;
+ crp->reserved2 = 0;
bcopy(&folder->userInfo, &crp->userInfo, 32);
} else /* file */ {
- struct HFSCatalogFile * file;
+ const struct HFSCatalogFile * file;
- file = (struct HFSCatalogFile *) dataPtr;
+ file = (const struct HFSCatalogFile *) dataPtr;
crp->recordType = kHFSPlusFileRecord;
crp->flags = file->flags;
crp->fileID = file->fileID;
crp->contentModDate = LocalToUTC(file->modifyDate);
crp->backupDate = LocalToUTC(file->backupDate);
crp->reserved1 = 0;
+ crp->reserved2 = 0;
bcopy(&file->userInfo, &crp->userInfo, 16);
bcopy(&file->finderInfo, &crp->finderInfo, 16);
crp->dataFork.totalBlocks = file->dataPhysicalSize / blocksize;
crp->attributeModDate = crp->contentModDate;
crp->accessDate = crp->contentModDate;
bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo));
- crp->reserved2 = 0;
}
+#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
+
}
/*
* Extract the text encoding from a catalog node record.
*/
-static u_long
+static u_int32_t
getencoding(const CatalogRecord *crp)
{
- u_long encoding;
+ u_int32_t encoding;
if (crp->recordType == kHFSPlusFolderRecord)
encoding = crp->hfsPlusFolder.textEncoding;
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 @ 0x%x)\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:
cnid = recp->hfsPlusThread.parentID;
break;
default:
- panic("hfs: getparentcnid: unknown recordType (crp @ 0x%x)\n", recp);
+ panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp);
break;
}
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);
+ }
+}