📄 catalog.c
字号:
if (hfs_bfind(&brec, mdb->cat_tree, HFS_BKEY(key), HFS_BFIND_READ_EQ)) { /* uh oh. we failed to read the record. * the entry doesn't actually exist. */ goto read_fail; } read_entry(entry, &brec); /* error */ if (!entry->cnid) { goto read_fail; } /* we don't have to acquire a spinlock here or * below for the unlocking bits as we're the first * user of this entry. */ entry->state &= ~HFS_LOCK; hfs_wake_up(&entry->wait); } return entry; } /* try to allocate more entries. grow_entries() doesn't release * the spinlock. */ if (grow_entries()) goto add_new_entry; spin_unlock(&entry_lock); return NULL;read_fail: /* short-cut hfs_cat_put by doing everything here. */ spin_lock(&entry_lock); list_del(&entry->hash); list_del(&entry->list); init_entry(entry); list_add(&entry->list, &entry_unused); entries_stat.nr_free_entries++; spin_unlock(&entry_lock); return NULL;}/* * get_entry() * * Try to return an entry for the indicated file or directory. * If ('read' == 0) then no attempt will be made to read it from disk * and a locked, but uninitialized, entry is returned. */static struct hfs_cat_entry *get_entry(struct hfs_mdb *mdb, const struct hfs_cat_key *key, const int read){ struct hfs_cat_entry * entry;#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) hfs_warn("hfs_get_entry: mdb=%p key=%s read=%d\n", mdb, key->CName.Name, read);#endif spin_lock(&entry_lock); entry = find_entry(mdb, key); if (!entry) { return get_new_entry(mdb, key, read); } spin_unlock(&entry_lock); wait_on_entry(entry); return entry;}/* * new_cnid() * * Allocate a CNID to use for a new file or directory. */static inline hfs_u32 new_cnid(struct hfs_mdb *mdb){ /* If the create succeeds then the mdb will get dirtied */ return htonl(mdb->next_id++);}/* * update_dir() * * Update counts, times and dirt on a changed directory */static void update_dir(struct hfs_mdb *mdb, struct hfs_cat_entry *dir, int is_dir, int count){ /* update counts */ if (is_dir) { mdb->dir_count += count; dir->u.dir.dirs += count; if (dir->cnid == htonl(HFS_ROOT_CNID)) { mdb->root_dirs += count; } } else { mdb->file_count += count; dir->u.dir.files += count; if (dir->cnid == htonl(HFS_ROOT_CNID)) { mdb->root_files += count; } } /* update times and dirt */ dir->modify_date = hfs_time(); hfs_cat_mark_dirty(dir);}/* * Add a writer to dir, excluding readers. * * XXX: this is wrong. it allows a move to occur when a directory * is being written to. */static inline void start_write(struct hfs_cat_entry *dir){ if (dir->u.dir.readers || waitqueue_active(&dir->u.dir.read_wait)) { hfs_sleep_on(&dir->u.dir.write_wait); } ++dir->u.dir.writers;}/* * Add a reader to dir, excluding writers. */static inline void start_read(struct hfs_cat_entry *dir){ if (dir->u.dir.writers || waitqueue_active(&dir->u.dir.write_wait)) { hfs_sleep_on(&dir->u.dir.read_wait); } ++dir->u.dir.readers;}/* * Remove a writer from dir, possibly admitting readers. */static inline void end_write(struct hfs_cat_entry *dir){ if (!(--dir->u.dir.writers)) { hfs_wake_up(&dir->u.dir.read_wait); }}/* * Remove a reader from dir, possibly admitting writers. */static inline void end_read(struct hfs_cat_entry *dir){ if (!(--dir->u.dir.readers)) { hfs_wake_up(&dir->u.dir.write_wait); }}/* * create_entry() * * Add a new file or directory to the catalog B-tree and * return a (struct hfs_cat_entry) for it in '*result'. */static int create_entry(struct hfs_cat_entry *parent, struct hfs_cat_key *key, const struct hfs_cat_rec *record, int is_dir, hfs_u32 cnid, struct hfs_cat_entry **result){ struct hfs_mdb *mdb = parent->mdb; struct hfs_cat_entry *entry; struct hfs_cat_key thd_key; struct hfs_cat_rec thd_rec; int error, has_thread; if (result) { *result = NULL; } /* keep readers from getting confused by changing dir size */ start_write(parent); /* create a locked entry in the cache */ entry = get_entry(mdb, key, 0); if (!entry) { /* The entry exists but can't be read */ error = -EIO; goto done; } if (entry->cnid) { /* The (unlocked) entry exists in the cache */ error = -EEXIST; goto bail2; } /* limit directory valence to signed 16-bit integer */ if ((parent->u.dir.dirs + parent->u.dir.files) >= HFS_MAX_VALENCE) { error = -ENOSPC; goto bail1; } has_thread = is_dir || (record->u.fil.Flags & HFS_FIL_THD); if (has_thread) { /* init some fields for the thread record */ memset(&thd_rec, 0, sizeof(thd_rec)); thd_rec.cdrType = is_dir ? HFS_CDR_THD : HFS_CDR_FTH; memcpy(&thd_rec.u.thd.ParID, &key->ParID, sizeof(hfs_u32) + sizeof(struct hfs_name)); /* insert the thread record */ hfs_cat_build_key(cnid, NULL, &thd_key); error = hfs_binsert(mdb->cat_tree, HFS_BKEY(&thd_key), &thd_rec, 2 + sizeof(THD_REC)); if (error) { goto bail1; } } /* insert the record */ error = hfs_binsert(mdb->cat_tree, HFS_BKEY(key), record, is_dir ? 2 + sizeof(DIR_REC) : 2 + sizeof(FIL_REC)); if (error) { if (has_thread && (error != -EIO)) { /* at least TRY to remove the thread record */ (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key)); } goto bail1; } /* update the parent directory */ update_dir(mdb, parent, is_dir, 1); /* complete the cache entry and return success */ __read_entry(entry, record); unlock_entry(entry); if (result) { *result = entry; } else { hfs_cat_put(entry); } goto done;bail1: /* entry really didn't exist, so we don't need to really delete it. * we do need to remove it from the hash, though. */ entry->state |= HFS_DELETED; remove_hash(entry); unlock_entry(entry);bail2: hfs_cat_put(entry);done: end_write(parent); return error;}/*================ Global functions ================*//* * hfs_cat_put() * * Release an entry we aren't using anymore. * * nothing in hfs_cat_put goes to sleep now except on the initial entry. */void hfs_cat_put(struct hfs_cat_entry * entry){ if (entry) { wait_on_entry(entry); /* just in case. this should never happen. */ if (!entry->count) { hfs_warn("hfs_cat_put: trying to free free entry: %p\n", entry); return; }#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) hfs_warn("hfs_cat_put: %p(%u) type=%d state=%lu\n", entry, entry->count, entry->type, entry->state);#endif spin_lock(&entry_lock); if (!--entry->count) { if ((entry->state & HFS_DELETED)) goto entry_deleted; if ((entry->type == HFS_CDR_FIL)) { /* clear out any cached extents */ if (entry->u.file.data_fork.first.next) { hfs_extent_free(&entry->u.file.data_fork); } if (entry->u.file.rsrc_fork.first.next) { hfs_extent_free(&entry->u.file.rsrc_fork); } } /* if we put a dirty entry, write it out. */ if ((entry->state & HFS_DIRTY)) { entry->state ^= HFS_DIRTY | HFS_LOCK; write_entry(entry); entry->state &= ~HFS_LOCK; } list_del(&entry->hash);entry_deleted: /* deleted entries have already been removed * from the hash list. */ list_del(&entry->list); if (entries_stat.nr_free_entries > CCACHE_MAX) { HFS_DELETE(entry); entries_stat.nr_entries--; } else { init_entry(entry); list_add(&entry->list, &entry_unused); entries_stat.nr_free_entries++; } } spin_unlock(&entry_lock); }}/* * hfs_cat_get() * * Wrapper for get_entry() which always calls with ('read'==1). * Used for access to get_entry() from outside this file. */struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *mdb, const struct hfs_cat_key *key){ return get_entry(mdb, key, 1);}/* invalidate all entries for a device */static void invalidate_list(struct list_head *head, struct hfs_mdb *mdb, struct list_head *dispose){ struct list_head *next; next = head->next; for (;;) { struct list_head *tmp = next; struct hfs_cat_entry * entry; next = next->next; if (tmp == head) break; entry = list_entry(tmp, struct hfs_cat_entry, list); if (entry->mdb != mdb) { continue; } if (!entry->count) { list_del(&entry->hash); INIT_LIST_HEAD(&entry->hash); list_del(&entry->list); list_add(&entry->list, dispose); continue; } hfs_warn("hfs_fs: entry %p(%u) busy on removed device %s.\n", entry, entry->count, hfs_mdb_name(entry->mdb->sys_mdb)); }}/* delete entries from a list */static void delete_list(struct list_head *head) { struct list_head *next = head->next; struct hfs_cat_entry *entry; for (;;) { struct list_head * tmp = next; next = next->next; if (tmp == head) { break; } entry = list_entry(tmp, struct hfs_cat_entry, list); HFS_DELETE(entry); }}/* * hfs_cat_invalidate() * * Called by hfs_mdb_put() to remove all the entries * in the cache that are associated with a given MDB. */void hfs_cat_invalidate(struct hfs_mdb *mdb){ LIST_HEAD(throw_away); spin_lock(&entry_lock); invalidate_list(&entry_in_use, mdb, &throw_away); invalidate_list(&mdb->entry_dirty, mdb, &throw_away); spin_unlock(&entry_lock); delete_list(&throw_away);#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) hfs_warn("hfs_cat_invalidate: free=%d total=%d\n", entries_stat.nr_free_entries, entries_stat.nr_entries);#endif}/* * hfs_cat_commit() * * Called by hfs_mdb_commit() to write dirty entries to the disk buffers. */void hfs_cat_commit(struct hfs_mdb *mdb){ struct list_head *tmp, *head = &mdb->entry_dirty; struct hfs_cat_entry *entry; spin_lock(&entry_lock); while ((tmp = head->prev) != head) { entry = list_entry(tmp, struct hfs_cat_entry, list); if ((entry->state & HFS_LOCK)) { spin_unlock(&entry_lock); wait_on_entry(entry); spin_lock(&entry_lock); } else { struct list_head *insert = &entry_in_use; if (!entry->count) insert = entry_in_use.prev; /* add to in_use list */ list_del(&entry->list); list_add(&entry->list, insert); /* reset DIRTY, set LOCK */ entry->state ^= HFS_DIRTY | HFS_LOCK; spin_unlock(&entry_lock); write_entry(entry); spin_lock(&entry_lock); entry->state &= ~HFS_LOCK; hfs_wake_up(&entry->wait); } } spin_unlock(&entry_lock);}/* * hfs_cat_free() * * Releases all the memory allocated in grow_entries(). * Must call hfs_cat_invalidate() on all MDBs before calling this. * This only gets rid of the unused pool of entries. all the other * entry references should have either been freed by cat_invalidate * or moved onto the unused list. */void hfs_cat_free(void){ delete_list(&entry_unused);}/* * hfs_cat_compare() * * Description: * This is the comparison function used for the catalog B-tree. In * comparing catalog B-tree entries, the parent id is the most * significant field (compared as unsigned ints). The name field is * the least significant (compared in "Macintosh lexical order", * see hfs_strcmp() in string.c) * Input Variable(s): * struct hfs_cat_key *key1: pointer to the first key to compare * struct hfs_cat_key *key2: pointer to the second key to compare * Output Variable(s): * NONE * Returns: * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2 * Preconditions: * key1 and key2 point to "valid" (struct hfs_cat_key)s. * Postconditions: * This function has no side-effects */int hfs_cat_compare(const struct hfs_cat_key *key1, const struct hfs_cat_key *key2){ unsigned int parents; int retval; parents = hfs_get_hl(key1->ParID) - hfs_get_hl(key2->ParID); if (parents != 0) { retval = (int)parents; } else { retval = hfs_strcmp(key1->CName.Name, key1->CName.Len, key2->CName.Name, key2->CName.Len); } return retval;}/* * hfs_cat_build_key() * * Given the ID of the parent and the name build a search key. */void hfs_cat_build_key(hfs_u32 parent, const struct hfs_name *cname, struct hfs_cat_key *key){ hfs_put_nl(parent, key->ParID); if (cname) { key->KeyLen = 6 + cname->Len; memcpy(&key->CName, cname, sizeof(*cname)); } else { key->KeyLen = 6; memset(&key->CName, 0, sizeof(*cname)); }}/* * hfs_cat_open() * * Given a directory on an HFS filesystem get its thread and * lock the directory against insertions and deletions. * Return 0 on success or an error code on failure. */int hfs_cat_open(struct hfs_cat_entry *dir, struct hfs_brec *brec){ struct hfs_cat_key key; int error; if (dir->type != HFS_CDR_DIR) { return -EINVAL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -