catalog.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:39k
- /*
- * linux/fs/hfs/catalog.c
- *
- * Copyright (C) 1995-1997 Paul H. Hargrove
- * This file may be distributed under the terms of the GNU General Public License.
- *
- * This file contains the functions related to the catalog B-tree.
- *
- * "XXX" in a comment is a note to myself to consider changing something.
- *
- * Cache code shamelessly stolen from
- * linux/fs/inode.c Copyright (C) 1991, 1992 Linus Torvalds
- * re-shamelessly stolen Copyright (C) 1997 Linus Torvalds
- *
- * In function preconditions the term "valid" applied to a pointer to
- * a structure means that the pointer is non-NULL and the structure it
- * points to has all fields initialized to consistent values.
- *
- * The code in this file initializes some structures by calling
- * memset(&foo, 0, sizeof(foo)). This produces the desired behavior
- * only due to the non-ANSI assumption that the machine representation
- */
- #include "hfs.h"
- /*================ Variable-like macros ================*/
- /* Number of hash table slots */
- #define C_HASHBITS 10
- #define C_HASHSIZE (1UL << C_HASHBITS)
- #define C_HASHMASK (C_HASHSIZE - 1)
- /* Number of entries to fit in a single page on an i386.
- * Actually, now it's used to increment the free entry pool. */
- #define CCACHE_INC (PAGE_SIZE/sizeof(struct hfs_cat_entry))
- #define CCACHE_MAX (CCACHE_INC * 8)
- /*================ File-local data types ================*/
- /* The catalog record for a file */
- typedef struct {
- hfs_byte_t Flags; /* Flags such as read-only */
- hfs_byte_t Typ; /* file version number = 0 */
- hfs_finfo_t UsrWds; /* data used by the Finder */
- hfs_lword_t FlNum; /* The CNID */
- hfs_word_t StBlk; /* obsolete */
- hfs_lword_t LgLen; /* The logical EOF of the data fork*/
- hfs_lword_t PyLen; /* The physical EOF of the data fork */
- hfs_word_t RStBlk; /* obsolete */
- hfs_lword_t RLgLen; /* The logical EOF of the rsrc fork */
- hfs_lword_t RPyLen; /* The physical EOF of the rsrc fork */
- hfs_lword_t CrDat; /* The creation date */
- hfs_lword_t MdDat; /* The modified date */
- hfs_lword_t BkDat; /* The last backup date */
- hfs_fxinfo_t FndrInfo; /* more data for the Finder */
- hfs_word_t ClpSize; /* number of bytes to allocate
- when extending files */
- hfs_byte_t ExtRec[12]; /* first extent record
- for the data fork */
- hfs_byte_t RExtRec[12]; /* first extent record
- for the resource fork */
- hfs_lword_t Resrv; /* reserved by Apple */
- } __attribute__((packed)) FIL_REC;
- /* the catalog record for a directory */
- typedef struct {
- hfs_word_t Flags; /* flags */
- hfs_word_t Val; /* Valence: number of files and
- dirs in the directory */
- hfs_lword_t DirID; /* The CNID */
- hfs_lword_t CrDat; /* The creation date */
- hfs_lword_t MdDat; /* The modification date */
- hfs_lword_t BkDat; /* The last backup date */
- hfs_dinfo_t UsrInfo; /* data used by the Finder */
- hfs_dxinfo_t FndrInfo; /* more data used by Finder */
- hfs_byte_t Resrv[16]; /* reserved by Apple */
- } __attribute__((packed)) DIR_REC;
- /* the catalog record for a thread */
- typedef struct {
- hfs_byte_t Reserv[8]; /* reserved by Apple */
- hfs_lword_t ParID; /* CNID of parent directory */
- struct hfs_name CName; /* The name of this entry */
- } __attribute__((packed)) THD_REC;
- /* A catalog tree record */
- struct hfs_cat_rec {
- hfs_byte_t cdrType; /* The type of entry */
- hfs_byte_t cdrResrv2; /* padding */
- union {
- FIL_REC fil;
- DIR_REC dir;
- THD_REC thd;
- } u;
- } __attribute__((packed));
- /*================ File-local variables ================*/
-
- static LIST_HEAD(entry_in_use);
- static LIST_HEAD(entry_unused);
- static struct list_head hash_table[C_HASHSIZE];
- static spinlock_t entry_lock = SPIN_LOCK_UNLOCKED;
- static struct {
- int nr_entries;
- int nr_free_entries;
- } entries_stat;
- /*================ File-local functions ================*/
- /*
- * brec_to_id
- *
- * Get the CNID from a brec
- */
- static inline hfs_u32 brec_to_id(struct hfs_brec *brec)
- {
- struct hfs_cat_rec *rec = brec->data;
- return hfs_get_nl((rec->cdrType==HFS_CDR_FIL) ?
- rec->u.fil.FlNum : rec->u.dir.DirID);
- }
- /*
- * hashfn()
- *
- * hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer.
- */
- static inline unsigned int hashfn(const struct hfs_mdb *mdb,
- const struct hfs_cat_key *key)
- {
- unsigned int hash;
-
- hash = (unsigned long) mdb | (unsigned long) key->ParID[3] |
- hfs_strhash(key->CName.Name, key->CName.Len);
- hash = hash ^ (hash >> C_HASHBITS) ^ (hash >> C_HASHBITS*2);
- return hash & C_HASHMASK;
- }
- /*
- * hash()
- *
- * hash an (struct mdb *) and a (struct hfs_cat_key *)
- * to a pointer to a slot in the hash table.
- */
- static inline struct list_head *hash(struct hfs_mdb *mdb,
- const struct hfs_cat_key *key)
- {
- return hash_table + hashfn(mdb, key);
- }
- static inline void insert_hash(struct hfs_cat_entry *entry)
- {
- struct list_head *head = hash(entry->mdb, &entry->key);
- list_add(&entry->hash, head);
- }
- static inline void remove_hash(struct hfs_cat_entry *entry)
- {
- list_del(&entry->hash);
- INIT_LIST_HEAD(&entry->hash);
- }
- /*
- * wait_on_entry()
- *
- * Sleep until a locked entry is unlocked.
- */
- static inline void wait_on_entry(struct hfs_cat_entry * entry)
- {
- while ((entry->state & HFS_LOCK)) {
- hfs_sleep_on(&entry->wait);
- }
- }
- /*
- * lock_entry()
- *
- * Obtain an exclusive lock on an entry.
- */
- static void lock_entry(struct hfs_cat_entry * entry)
- {
- wait_on_entry(entry);
- spin_lock(&entry_lock);
- entry->state |= HFS_LOCK;
- spin_unlock(&entry_lock);
- }
- /*
- * lock_entry()
- *
- * Relinquish an exclusive lock on an entry.
- */
- static void unlock_entry(struct hfs_cat_entry * entry)
- {
- spin_lock(&entry_lock);
- entry->state &= ~HFS_LOCK;
- spin_unlock(&entry_lock);
- hfs_wake_up(&entry->wait);
- }
- /* put entry on mdb dirty list. */
- void hfs_cat_mark_dirty(struct hfs_cat_entry *entry)
- {
- struct hfs_mdb *mdb = entry->mdb;
- spin_lock(&entry_lock);
- if (!(entry->state & HFS_DIRTY)) {
- entry->state |= HFS_DIRTY;
- /* Only add valid (ie hashed) entries to the dirty list. */
- if (!list_empty(&entry->hash)) {
- list_del(&entry->list);
- list_add(&entry->list, &mdb->entry_dirty);
- }
- }
- spin_unlock(&entry_lock);
- }
- /* delete an entry and remove it from the hash table. */
- static void delete_entry(struct hfs_cat_entry *entry)
- {
- if (!(entry->state & HFS_DELETED)) {
- entry->state |= HFS_DELETED;
- list_del(&entry->hash);
- INIT_LIST_HEAD(&entry->hash);
- if (entry->type == HFS_CDR_FIL) {
- /* free all extents */
- entry->u.file.data_fork.lsize = 0;
- hfs_extent_adj(&entry->u.file.data_fork);
- entry->u.file.rsrc_fork.lsize = 0;
- hfs_extent_adj(&entry->u.file.rsrc_fork);
- }
- }
- }
- static inline void init_entry(struct hfs_cat_entry *entry)
- {
- memset(entry, 0, sizeof(*entry));
- hfs_init_waitqueue(&entry->wait);
- INIT_LIST_HEAD(&entry->hash);
- INIT_LIST_HEAD(&entry->list);
- }
- /*
- * hfs_cat_alloc()
- *
- * Try to allocate another entry.
- */
- static inline struct hfs_cat_entry *hfs_cat_alloc(void)
- {
- struct hfs_cat_entry *entry;
- if (!HFS_NEW(entry))
- return NULL;
- init_entry(entry);
- return entry;
- }
- /* this gets called with the spinlock held. */
- static int grow_entries(void)
- {
- struct hfs_cat_entry *entry;
- int i;
-
- for (i = 0; i < CCACHE_INC; i++) {
- if (!(entry = hfs_cat_alloc()))
- break;
- list_add(&entry->list, &entry_unused);
- }
- entries_stat.nr_entries += i;
- entries_stat.nr_free_entries += i;
-
- return i;
- }
- /*
- * __read_entry()
- *
- * Convert a (struct hfs_cat_rec) to a (struct hfs_cat_entry).
- */
- static void __read_entry(struct hfs_cat_entry *entry,
- const struct hfs_cat_rec *cat)
- {
- entry->type = cat->cdrType;
- if (cat->cdrType == HFS_CDR_DIR) {
- struct hfs_dir *dir = &entry->u.dir;
- entry->cnid = hfs_get_nl(cat->u.dir.DirID);
- dir->magic = HFS_DIR_MAGIC;
- dir->flags = hfs_get_ns(cat->u.dir.Flags);
- memcpy(&entry->info.dir.dinfo, &cat->u.dir.UsrInfo, 16);
- memcpy(&entry->info.dir.dxinfo, &cat->u.dir.FndrInfo, 16);
- entry->create_date = hfs_get_nl(cat->u.dir.CrDat);
- entry->modify_date = hfs_get_nl(cat->u.dir.MdDat);
- entry->backup_date = hfs_get_nl(cat->u.dir.BkDat);
- dir->dirs = dir->files = 0;
- hfs_init_waitqueue(&dir->read_wait);
- hfs_init_waitqueue(&dir->write_wait);
- } else if (cat->cdrType == HFS_CDR_FIL) {
- struct hfs_file *fil = &entry->u.file;
- entry->cnid = hfs_get_nl(cat->u.fil.FlNum);
- fil->magic = HFS_FILE_MAGIC;
- fil->data_fork.fork = HFS_FK_DATA;
- fil->data_fork.entry = entry;
- fil->data_fork.lsize = hfs_get_hl(cat->u.fil.LgLen);
- fil->data_fork.psize = hfs_get_hl(cat->u.fil.PyLen) >>
- HFS_SECTOR_SIZE_BITS;
- hfs_extent_in(&fil->data_fork, cat->u.fil.ExtRec);
- fil->rsrc_fork.fork = HFS_FK_RSRC;
- fil->rsrc_fork.entry = entry;
- fil->rsrc_fork.lsize = hfs_get_hl(cat->u.fil.RLgLen);
- fil->rsrc_fork.psize = hfs_get_hl(cat->u.fil.RPyLen) >>
- HFS_SECTOR_SIZE_BITS;
- hfs_extent_in(&fil->rsrc_fork, cat->u.fil.RExtRec);
- memcpy(&entry->info.file.finfo, &cat->u.fil.UsrWds, 16);
- memcpy(&entry->info.file.fxinfo, &cat->u.fil.FndrInfo, 16);
- entry->create_date = hfs_get_nl(cat->u.fil.CrDat);
- entry->modify_date = hfs_get_nl(cat->u.fil.MdDat);
- entry->backup_date = hfs_get_nl(cat->u.fil.BkDat);
- fil->clumpablks = (hfs_get_hs(cat->u.fil.ClpSize)
- / entry->mdb->alloc_blksz)
- >> HFS_SECTOR_SIZE_BITS;
- fil->flags = cat->u.fil.Flags;
- } else {
- hfs_warn("hfs_fs: entry is neither file nor directory!n");
- }
- }
- /*
- * count_dir_entries()
- *
- * Count the number of files and directories in a given directory.
- */
- static inline void count_dir_entries(struct hfs_cat_entry *entry,
- struct hfs_brec *brec)
- {
- int error = 0;
- hfs_u32 cnid;
- hfs_u8 type;
- if (!hfs_cat_open(entry, brec)) {
- while (!(error = hfs_cat_next(entry, brec, 1, &cnid, &type))) {
- if (type == HFS_CDR_FIL) {
- ++entry->u.dir.files;
- } else if (type == HFS_CDR_DIR) {
- ++entry->u.dir.dirs;
- }
- } /* -ENOENT is normal termination */
- }
- if (error != -ENOENT) {
- entry->cnid = 0;
- }
- }
- /*
- * read_entry()
- *
- * Convert a (struct hfs_brec) to a (struct hfs_cat_entry).
- */
- static inline void read_entry(struct hfs_cat_entry *entry,
- struct hfs_brec *brec)
- {
- int need_count;
- struct hfs_cat_rec *rec = brec->data;
- __read_entry(entry, rec);
- need_count = (rec->cdrType == HFS_CDR_DIR) && rec->u.dir.Val;
- hfs_brec_relse(brec, NULL);
- if (need_count) {
- count_dir_entries(entry, brec);
- }
- }
- /*
- * __write_entry()
- *
- * Convert a (struct hfs_cat_entry) to a (struct hfs_cat_rec).
- */
- static void __write_entry(const struct hfs_cat_entry *entry,
- struct hfs_cat_rec *cat)
- {
- if (entry->type == HFS_CDR_DIR) {
- const struct hfs_dir *dir = &entry->u.dir;
- hfs_put_ns(dir->flags, cat->u.dir.Flags);
- hfs_put_hs(dir->dirs + dir->files, cat->u.dir.Val);
- hfs_put_nl(entry->cnid, cat->u.dir.DirID);
- hfs_put_nl(entry->create_date, cat->u.dir.CrDat);
- hfs_put_nl(entry->modify_date, cat->u.dir.MdDat);
- hfs_put_nl(entry->backup_date, cat->u.dir.BkDat);
- memcpy(&cat->u.dir.UsrInfo, &entry->info.dir.dinfo, 16);
- memcpy(&cat->u.dir.FndrInfo, &entry->info.dir.dxinfo, 16);
- } else if (entry->type == HFS_CDR_FIL) {
- const struct hfs_file *fil = &entry->u.file;
- cat->u.fil.Flags = fil->flags;
- hfs_put_nl(entry->cnid, cat->u.fil.FlNum);
- memcpy(&cat->u.fil.UsrWds, &entry->info.file.finfo, 16);
- hfs_put_hl(fil->data_fork.lsize, cat->u.fil.LgLen);
- hfs_put_hl(fil->data_fork.psize << HFS_SECTOR_SIZE_BITS,
- cat->u.fil.PyLen);
- hfs_put_hl(fil->rsrc_fork.lsize, cat->u.fil.RLgLen);
- hfs_put_hl(fil->rsrc_fork.psize << HFS_SECTOR_SIZE_BITS,
- cat->u.fil.RPyLen);
- hfs_put_nl(entry->create_date, cat->u.fil.CrDat);
- hfs_put_nl(entry->modify_date, cat->u.fil.MdDat);
- hfs_put_nl(entry->backup_date, cat->u.fil.BkDat);
- memcpy(&cat->u.fil.FndrInfo, &entry->info.file.fxinfo, 16);
- hfs_put_hs((fil->clumpablks * entry->mdb->alloc_blksz)
- << HFS_SECTOR_SIZE_BITS, cat->u.fil.ClpSize);
- hfs_extent_out(&fil->data_fork, cat->u.fil.ExtRec);
- hfs_extent_out(&fil->rsrc_fork, cat->u.fil.RExtRec);
- } else {
- hfs_warn("__write_entry: invalid entryn");
- }
- }
- /*
- * write_entry()
- *
- * Write a modified entry back to the catalog B-tree. this gets called
- * with the entry locked.
- */
- static void write_entry(struct hfs_cat_entry * entry)
- {
- struct hfs_brec brec;
- int error;
- if (!(entry->state & HFS_DELETED)) {
- error = hfs_bfind(&brec, entry->mdb->cat_tree,
- HFS_BKEY(&entry->key), HFS_BFIND_WRITE);
- if (!error) {
- if ((entry->state & HFS_KEYDIRTY)) {
- /* key may have changed case due to a rename */
- entry->state &= ~HFS_KEYDIRTY;
- if (brec.key->KeyLen != entry->key.KeyLen) {
- hfs_warn("hfs_write_entry: key length "
- "changed!n");
- error = 1;
- } else {
- memcpy(brec.key, &entry->key,
- entry->key.KeyLen);
- }
- } else if (entry->cnid != brec_to_id(&brec)) {
- hfs_warn("hfs_write_entry: CNID "
- "changed unexpectedly!n");
- error = 1;
- }
- if (!error) {
- __write_entry(entry, brec.data);
- }
- hfs_brec_relse(&brec, NULL);
- }
- if (error) {
- hfs_warn("hfs_write_entry: unable to write "
- "entry %08xn", entry->cnid);
- }
- }
- }
- /* this gets called with the spinlock held. */
- static struct hfs_cat_entry *find_entry(struct hfs_mdb *mdb,
- const struct hfs_cat_key *key)
- {
- struct list_head *tmp, *head = hash(mdb, key);
- struct hfs_cat_entry * entry;
- tmp = head;
- for (;;) {
- tmp = tmp->next;
- entry = NULL;
- if (tmp == head)
- break;
- entry = list_entry(tmp, struct hfs_cat_entry, hash);
- if (entry->mdb != mdb)
- continue;
- if (hfs_cat_compare(&entry->key, key)) {
- continue;
- }
- entry->count++;
- break;
- }
- return entry;
- }
- /* be careful. this gets called with the spinlock held. */
- static struct hfs_cat_entry *get_new_entry(struct hfs_mdb *mdb,
- const struct hfs_cat_key *key,
- const int read)
- {
- struct hfs_cat_entry *entry;
- struct list_head *head = hash(mdb, key);
- struct list_head *tmp;
- add_new_entry:
- tmp = entry_unused.next;
- if ((tmp != &entry_unused) ) {
- list_del(tmp);
- entries_stat.nr_free_entries--;
- entry = list_entry(tmp, struct hfs_cat_entry, list);
- list_add(&entry->list, &entry_in_use);
- list_add(&entry->hash, head);
- entry->mdb = mdb;
- entry->count = 1;
- memcpy(&entry->key, key, sizeof(*key));
- entry->state = HFS_LOCK;
- spin_unlock(&entry_lock);
- if (read) {
- struct hfs_brec brec;
- 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=%dn",
- 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: %pn",
- entry);
- return;
- }
- #if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
- hfs_warn("hfs_cat_put: %p(%u) type=%d state=%lun",
- 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=%dn",
- 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;
- }
-
- /* Block writers */
- start_read(dir);
- /* Find the directory */
- hfs_cat_build_key(dir->cnid, NULL, &key);
- error = hfs_bfind(brec, dir->mdb->cat_tree,
- HFS_BKEY(&key), HFS_BFIND_READ_EQ);
- if (error) {
- end_read(dir);
- }
- return error;
- }
- /*
- * hfs_cat_next()
- *
- * Given a catalog brec structure, replace it with the count'th next brec
- * in the same directory.
- * Return an error code if there is a problem, 0 if OK.
- * Note that an error code of -ENOENT means there are no more entries
- * in this directory.
- * The directory is "closed" on an error.
- */
- int hfs_cat_next(struct hfs_cat_entry *dir, struct hfs_brec *brec,
- hfs_u16 count, hfs_u32 *cnid, hfs_u8 *type)
- {
- int error;
- if (!dir || !brec) {
- return -EINVAL;
- }
- /* Get the count'th next catalog tree entry */
- error = hfs_bsucc(brec, count);
- if (!error) {
- struct hfs_cat_key *key = (struct hfs_cat_key *)brec->key;
- if (hfs_get_nl(key->ParID) != dir->cnid) {
- hfs_brec_relse(brec, NULL);
- error = -ENOENT;
- }
- }
- if (!error) {
- *type = ((struct hfs_cat_rec *)brec->data)->cdrType;
- *cnid = brec_to_id(brec);
- } else {
- end_read(dir);
- }
- return error;
- }
- /*
- * hfs_cat_close()
- *
- * Given a catalog brec structure, replace it with the count'th next brec
- * in the same directory.
- * Return an error code if there is a problem, 0 if OK.
- * Note that an error code of -ENOENT means there are no more entries
- * in this directory.
- */
- void hfs_cat_close(struct hfs_cat_entry *dir, struct hfs_brec *brec)
- {
- if (dir && brec) {
- hfs_brec_relse(brec, NULL);
- end_read(dir);
- }
- }
- /*
- * hfs_cat_parent()
- *
- * Given a catalog entry, return the entry for its parent.
- * Uses catalog key for the entry to get its parent's ID
- * and then uses the parent's thread record to locate the
- * parent's actual catalog entry.
- */
- struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *entry)
- {
- struct hfs_cat_entry *retval = NULL;
- struct hfs_mdb *mdb = entry->mdb;
- struct hfs_brec brec;
- struct hfs_cat_key key;
- int error;
- lock_entry(entry);
- if (!(entry->state & HFS_DELETED)) {
- hfs_cat_build_key(hfs_get_nl(entry->key.ParID), NULL, &key);
- error = hfs_bfind(&brec, mdb->cat_tree,
- HFS_BKEY(&key), HFS_BFIND_READ_EQ);
- if (!error) {
- /* convert thread record to key */
- struct hfs_cat_rec *rec = brec.data;
- key.KeyLen = 6 + rec->u.thd.CName.Len;
- memcpy(&key.ParID, &rec->u.thd.ParID,
- sizeof(hfs_u32) + sizeof(struct hfs_name));
- hfs_brec_relse(&brec, NULL);
- retval = hfs_cat_get(mdb, &key);
- }
- }
- unlock_entry(entry);
- return retval;
- }
-
- /*
- * hfs_cat_create()
- *
- * Create a new file with the indicated name in the indicated directory.
- * The file will have the indicated flags, type and creator.
- * If successful an (struct hfs_cat_entry) is returned in '*result'.
- */
- int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
- hfs_u8 flags, hfs_u32 type, hfs_u32 creator,
- struct hfs_cat_entry **result)
- {
- struct hfs_cat_rec record;
- hfs_u32 id = new_cnid(parent->mdb);
- hfs_u32 mtime = hfs_time();
- #if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
- hfs_warn("hfs_cat_create: %p/%s flags=%d res=%pn",
- parent, key->CName.Name, flags, result);
- #endif
- /* init some fields for the file record */
- memset(&record, 0, sizeof(record));
- record.cdrType = HFS_CDR_FIL;
- record.u.fil.Flags = flags | HFS_FIL_USED;
- hfs_put_nl(id, record.u.fil.FlNum);
- hfs_put_nl(mtime, record.u.fil.CrDat);
- hfs_put_nl(mtime, record.u.fil.MdDat);
- hfs_put_nl(0, record.u.fil.BkDat);
- hfs_put_nl(type, record.u.fil.UsrWds.fdType);
- hfs_put_nl(creator, record.u.fil.UsrWds.fdCreator);
- return create_entry(parent, key, &record, 0, id, result);
- }
- /*
- * hfs_cat_mkdir()
- *
- * Create a new directory with the indicated name in the indicated directory.
- * If successful an (struct hfs_cat_entry) is returned in '*result'.
- */
- int hfs_cat_mkdir(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
- struct hfs_cat_entry **result)
- {
- struct hfs_cat_rec record;
- hfs_u32 id = new_cnid(parent->mdb);
- hfs_u32 mtime = hfs_time();
- #if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
- hfs_warn("hfs_cat_mkdir: %p/%s res=%pn", parent, key->CName.Name,
- result);
- #endif
- /* init some fields for the directory record */
- memset(&record, 0, sizeof(record));
- record.cdrType = HFS_CDR_DIR;
- hfs_put_nl(id, record.u.dir.DirID);
- hfs_put_nl(mtime, record.u.dir.CrDat);
- hfs_put_nl(mtime, record.u.dir.MdDat);
- hfs_put_nl(0, record.u.dir.BkDat);
- hfs_put_hs(0xff, record.u.dir.UsrInfo.frView);
- return create_entry(parent, key, &record, 1, id, result);
- }
- /*
- * hfs_cat_delete()
- *
- * Delete the indicated file or directory.
- * The associated thread is also removed unless ('with_thread'==0).
- */
- int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry,
- int with_thread)
- {
- struct hfs_cat_key key;
- struct hfs_mdb *mdb = parent->mdb;
- int is_dir, error = 0;
- #if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
- hfs_warn("hfs_cat_delete: %p/%p type=%d state=%lu, thread=%dn",
- parent, entry, entry->type, entry->state, with_thread);
- #endif
- if (parent->mdb != entry->mdb) {
- return -EINVAL;
- }
- if (entry->type == HFS_CDR_FIL) {
- with_thread = (entry->u.file.flags&HFS_FIL_THD) && with_thread;
- is_dir = 0;
- } else {
- is_dir = 1;
- }
- /* keep readers from getting confused by changing dir size */
- start_write(parent);
- /* don't delete a busy directory */
- if (entry->type == HFS_CDR_DIR) {
- start_read(entry);
- error = -ENOTEMPTY;
- if (entry->u.dir.files || entry->u.dir.dirs)
- goto hfs_delete_end;
- }
- /* try to delete the file or directory */
- lock_entry(entry);
- error = -ENOENT;
- if ((entry->state & HFS_DELETED)) {
- /* somebody beat us to it. */
- goto hfs_delete_unlock;
- }
-
- /* delete the catalog record */
- if ((error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key)))) {
- goto hfs_delete_unlock;
- }
- /* Mark the entry deleted and remove it from the cache */
- delete_entry(entry);
- /* try to delete the thread entry if it exists */
- if (with_thread) {
- hfs_cat_build_key(entry->cnid, NULL, &key);
- (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key));
- }
-
- update_dir(mdb, parent, is_dir, -1);
- hfs_delete_unlock:
- unlock_entry(entry);
- hfs_delete_end:
- if (entry->type == HFS_CDR_DIR) {
- end_read(entry);
- }
- end_write(parent);
- return error;
- }
- /*
- * hfs_cat_move()
- *
- * Rename a file or directory, possibly to a new directory.
- * If the destination exists it is removed and a
- * (struct hfs_cat_entry) for it is returned in '*result'.
- */
- int hfs_cat_move(struct hfs_cat_entry *old_dir, struct hfs_cat_entry *new_dir,
- struct hfs_cat_entry *entry, struct hfs_cat_key *new_key,
- struct hfs_cat_entry **removed)
- {
- struct hfs_cat_entry *dest;
- struct hfs_mdb *mdb;
- int error = 0;
- int is_dir, has_thread;
- if (removed) {
- *removed = NULL;
- }
- /* sanity checks */
- if (!old_dir || !new_dir) {
- return -EINVAL;
- }
- mdb = old_dir->mdb;
- if (mdb != new_dir->mdb) {
- return -EXDEV;
- }
- /* precompute a few things */
- if (entry->type == HFS_CDR_DIR) {
- is_dir = 1;
- has_thread = 1;
- } else if (entry->type == HFS_CDR_FIL) {
- is_dir = 0;
- has_thread = entry->u.file.flags & HFS_FIL_THD;
- } else {
- return -EINVAL;
- }
- while (mdb->rename_lock) {
- hfs_sleep_on(&mdb->rename_wait);
- }
- spin_lock(&entry_lock);
- mdb->rename_lock = 1; /* XXX: should be atomic_inc */
- spin_unlock(&entry_lock);
- /* keep readers from getting confused by changing dir size */
- start_write(new_dir);
- if (old_dir != new_dir) {
- start_write(old_dir);
- }
- /* Don't move a directory inside itself */
- if (is_dir) {
- struct hfs_cat_key thd_key;
- struct hfs_brec brec;
- hfs_u32 id = new_dir->cnid;
- while (id != htonl(HFS_ROOT_CNID)) {
- if (id == entry->cnid) {
- error = -EINVAL;
- } else {
- hfs_cat_build_key(id, NULL, &thd_key);
- error = hfs_bfind(&brec, mdb->cat_tree,
- HFS_BKEY(&thd_key),
- HFS_BFIND_READ_EQ);
- }
- if (error) {
- goto done;
- } else {
- struct hfs_cat_rec *rec = brec.data;
- id = hfs_get_nl(rec->u.thd.ParID);
- hfs_brec_relse(&brec, NULL);
- }
- }
- }
- restart:
- /* see if the destination exists, getting it if it does */
- dest = hfs_cat_get(mdb, new_key);
- if (!dest) {
- /* destination doesn't exist, so create it */
- struct hfs_cat_rec new_record;
- /* create a locked entry in the cache */
- dest = get_entry(mdb, new_key, 0);
- if (!dest) {
- error = -EIO;
- goto done;
- }
- if (dest->cnid) {
- /* The (unlocked) entry exists in the cache */
- goto have_distinct;
- }
- /* limit directory valence to signed 16-bit integer */
- if ((new_dir->u.dir.dirs + new_dir->u.dir.files) >=
- HFS_MAX_VALENCE) {
- error = -ENOSPC;
- goto bail3;
- }
- /* build the new record. make sure to zero out the
- record. */
- memset(&new_record, 0, sizeof(new_record));
- new_record.cdrType = entry->type;
- __write_entry(entry, &new_record);
- /* insert the new record */
- error = hfs_binsert(mdb->cat_tree, HFS_BKEY(new_key),
- &new_record, is_dir ? 2 + sizeof(DIR_REC) :
- 2 + sizeof(FIL_REC));
- if (error == -EEXIST) {
- delete_entry(dest);
- unlock_entry(dest);
- hfs_cat_put(dest);
- goto restart;
- } else if (error) {
- goto bail3;
- }
- /* update the destination directory */
- update_dir(mdb, new_dir, is_dir, 1);
- } else if (entry != dest) {
- have_distinct:
- /* The destination exists and is not same as source */
- lock_entry(dest);
- if ((dest->state & HFS_DELETED)) {
- unlock_entry(dest);
- hfs_cat_put(dest);
- goto restart;
- }
- if (dest->type != entry->type) {
- /* can't move a file on top
- of a dir nor vice versa. */
- error = is_dir ? -ENOTDIR : -EISDIR;
- } else if (is_dir && (dest->u.dir.dirs || dest->u.dir.files)) {
- /* directory to replace is not empty */
- error = -ENOTEMPTY;
- }
- if (error) {
- goto bail2;
- }
- } else {
- /* The destination exists but is same as source */
- --entry->count;
- dest = NULL;
- }
- /* lock the entry */
- lock_entry(entry);
- if ((entry->state & HFS_DELETED)) {
- error = -ENOENT;
- goto bail1;
- }
- if (dest) {
- /* remove the old entry */
- error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key));
- if (error) {
- /* We couldn't remove the entry for the
- original file, so nothing has changed. */
- goto bail1;
- }
- update_dir(mdb, old_dir, is_dir, -1);
- }
- /* update the thread of the dir/file we're moving */
- if (has_thread) {
- struct hfs_cat_key thd_key;
- struct hfs_brec brec;
- hfs_cat_build_key(entry->cnid, NULL, &thd_key);
- error = hfs_bfind(&brec, mdb->cat_tree,
- HFS_BKEY(&thd_key), HFS_BFIND_WRITE);
- if (error == -ENOENT) {
- if (is_dir) {
- /* directory w/o a thread! */
- error = -EIO;
- } else {
- /* We were lied to! */
- entry->u.file.flags &= ~HFS_FIL_THD;
- hfs_cat_mark_dirty(entry);
- }
- }
- if (!error) {
- struct hfs_cat_rec *rec = brec.data;
- memcpy(&rec->u.thd.ParID, &new_key->ParID,
- sizeof(hfs_u32) + sizeof(struct hfs_name));
- hfs_brec_relse(&brec, NULL);
- } else if (error == -ENOENT) {
- error = 0;
- } else if (!dest) {
- /* Nothing was changed */
- unlock_entry(entry);
- goto done;
- } else {
- /* Something went seriously wrong.
- The dir/file has been deleted. */
- /* XXX try some recovery? */
- delete_entry(entry);
- goto bail1;
- }
- }
- /* TRY to remove the thread for the pre-existing entry */
- if (dest && dest->cnid &&
- (is_dir || (dest->u.file.flags & HFS_FIL_THD))) {
- struct hfs_cat_key thd_key;
- hfs_cat_build_key(dest->cnid, NULL, &thd_key);
- (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key));
- }
- /* update directories */
- new_dir->modify_date = hfs_time();
- hfs_cat_mark_dirty(new_dir);
- /* update key */
- remove_hash(entry);
- memcpy(&entry->key, new_key, sizeof(*new_key));
- /* KEYDIRTY as case might differ */
- entry->state |= HFS_KEYDIRTY;
- insert_hash(entry);
- hfs_cat_mark_dirty(entry);
- unlock_entry(entry);
- /* delete any pre-existing or place-holder entry */
- if (dest) {
- delete_entry(dest);
- unlock_entry(dest);
- if (removed && dest->cnid) {
- *removed = dest;
- } else {
- hfs_cat_put(dest);
- }
- }
- goto done;
- bail1:
- unlock_entry(entry);
- bail2:
- if (dest) {
- if (!dest->cnid) {
- /* TRY to remove the new entry */
- (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(new_key));
- update_dir(mdb, new_dir, is_dir, -1);
- bail3:
- delete_entry(dest);
- }
- unlock_entry(dest);
- hfs_cat_put(dest);
- }
- done:
- if (new_dir != old_dir) {
- end_write(old_dir);
- }
- end_write(new_dir);
- spin_lock(&entry_lock);
- mdb->rename_lock = 0; /* XXX: should use atomic_dec */
- hfs_wake_up(&mdb->rename_wait);
- spin_unlock(&entry_lock);
- return error;
- }
- /*
- * Initialize the hash tables
- */
- void hfs_cat_init(void)
- {
- int i;
- struct list_head *head = hash_table;
- i = C_HASHSIZE;
- do {
- INIT_LIST_HEAD(head);
- head++;
- i--;
- } while (i);
- }