+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;
+
+}