📄 catalog.c
字号:
} /* 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=%p\n", 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=%p\n", 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=%d\n", 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);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -