Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  *  linux/fs/hfs/catalog.c
0003  *
0004  * Copyright (C) 1995-1997  Paul H. Hargrove
0005  * (C) 2003 Ardis Technologies <roman@ardistech.com>
0006  * This file may be distributed under the terms of the GNU General Public License.
0007  *
0008  * This file contains the functions related to the catalog B-tree.
0009  *
0010  * Cache code shamelessly stolen from
0011  *     linux/fs/inode.c Copyright (C) 1991, 1992  Linus Torvalds
0012  *     re-shamelessly stolen Copyright (C) 1997 Linus Torvalds
0013  */
0014 
0015 #include "hfs_fs.h"
0016 #include "btree.h"
0017 
0018 /*
0019  * hfs_cat_build_key()
0020  *
0021  * Given the ID of the parent and the name build a search key.
0022  */
0023 void hfs_cat_build_key(struct super_block *sb, btree_key *key, u32 parent, const struct qstr *name)
0024 {
0025     key->cat.reserved = 0;
0026     key->cat.ParID = cpu_to_be32(parent);
0027     if (name) {
0028         hfs_asc2mac(sb, &key->cat.CName, name);
0029         key->key_len = 6 + key->cat.CName.len;
0030     } else {
0031         memset(&key->cat.CName, 0, sizeof(struct hfs_name));
0032         key->key_len = 6;
0033     }
0034 }
0035 
0036 static int hfs_cat_build_record(hfs_cat_rec *rec, u32 cnid, struct inode *inode)
0037 {
0038     __be32 mtime = hfs_mtime();
0039 
0040     memset(rec, 0, sizeof(*rec));
0041     if (S_ISDIR(inode->i_mode)) {
0042         rec->type = HFS_CDR_DIR;
0043         rec->dir.DirID = cpu_to_be32(cnid);
0044         rec->dir.CrDat = mtime;
0045         rec->dir.MdDat = mtime;
0046         rec->dir.BkDat = 0;
0047         rec->dir.UsrInfo.frView = cpu_to_be16(0xff);
0048         return sizeof(struct hfs_cat_dir);
0049     } else {
0050         /* init some fields for the file record */
0051         rec->type = HFS_CDR_FIL;
0052         rec->file.Flags = HFS_FIL_USED | HFS_FIL_THD;
0053         if (!(inode->i_mode & S_IWUSR))
0054             rec->file.Flags |= HFS_FIL_LOCK;
0055         rec->file.FlNum = cpu_to_be32(cnid);
0056         rec->file.CrDat = mtime;
0057         rec->file.MdDat = mtime;
0058         rec->file.BkDat = 0;
0059         rec->file.UsrWds.fdType = HFS_SB(inode->i_sb)->s_type;
0060         rec->file.UsrWds.fdCreator = HFS_SB(inode->i_sb)->s_creator;
0061         return sizeof(struct hfs_cat_file);
0062     }
0063 }
0064 
0065 static int hfs_cat_build_thread(struct super_block *sb,
0066                 hfs_cat_rec *rec, int type,
0067                 u32 parentid, const struct qstr *name)
0068 {
0069     rec->type = type;
0070     memset(rec->thread.reserved, 0, sizeof(rec->thread.reserved));
0071     rec->thread.ParID = cpu_to_be32(parentid);
0072     hfs_asc2mac(sb, &rec->thread.CName, name);
0073     return sizeof(struct hfs_cat_thread);
0074 }
0075 
0076 /*
0077  * create_entry()
0078  *
0079  * Add a new file or directory to the catalog B-tree and
0080  * return a (struct hfs_cat_entry) for it in '*result'.
0081  */
0082 int hfs_cat_create(u32 cnid, struct inode *dir, const struct qstr *str, struct inode *inode)
0083 {
0084     struct hfs_find_data fd;
0085     struct super_block *sb;
0086     union hfs_cat_rec entry;
0087     int entry_size;
0088     int err;
0089 
0090     hfs_dbg(CAT_MOD, "create_cat: %s,%u(%d)\n",
0091         str->name, cnid, inode->i_nlink);
0092     if (dir->i_size >= HFS_MAX_VALENCE)
0093         return -ENOSPC;
0094 
0095     sb = dir->i_sb;
0096     err = hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
0097     if (err)
0098         return err;
0099 
0100     /*
0101      * Fail early and avoid ENOSPC during the btree operations. We may
0102      * have to split the root node at most once.
0103      */
0104     err = hfs_bmap_reserve(fd.tree, 2 * fd.tree->depth);
0105     if (err)
0106         goto err2;
0107 
0108     hfs_cat_build_key(sb, fd.search_key, cnid, NULL);
0109     entry_size = hfs_cat_build_thread(sb, &entry, S_ISDIR(inode->i_mode) ?
0110             HFS_CDR_THD : HFS_CDR_FTH,
0111             dir->i_ino, str);
0112     err = hfs_brec_find(&fd);
0113     if (err != -ENOENT) {
0114         if (!err)
0115             err = -EEXIST;
0116         goto err2;
0117     }
0118     err = hfs_brec_insert(&fd, &entry, entry_size);
0119     if (err)
0120         goto err2;
0121 
0122     hfs_cat_build_key(sb, fd.search_key, dir->i_ino, str);
0123     entry_size = hfs_cat_build_record(&entry, cnid, inode);
0124     err = hfs_brec_find(&fd);
0125     if (err != -ENOENT) {
0126         /* panic? */
0127         if (!err)
0128             err = -EEXIST;
0129         goto err1;
0130     }
0131     err = hfs_brec_insert(&fd, &entry, entry_size);
0132     if (err)
0133         goto err1;
0134 
0135     dir->i_size++;
0136     dir->i_mtime = dir->i_ctime = current_time(dir);
0137     mark_inode_dirty(dir);
0138     hfs_find_exit(&fd);
0139     return 0;
0140 
0141 err1:
0142     hfs_cat_build_key(sb, fd.search_key, cnid, NULL);
0143     if (!hfs_brec_find(&fd))
0144         hfs_brec_remove(&fd);
0145 err2:
0146     hfs_find_exit(&fd);
0147     return err;
0148 }
0149 
0150 /*
0151  * hfs_cat_compare()
0152  *
0153  * Description:
0154  *   This is the comparison function used for the catalog B-tree.  In
0155  *   comparing catalog B-tree entries, the parent id is the most
0156  *   significant field (compared as unsigned ints).  The name field is
0157  *   the least significant (compared in "Macintosh lexical order",
0158  *   see hfs_strcmp() in string.c)
0159  * Input Variable(s):
0160  *   struct hfs_cat_key *key1: pointer to the first key to compare
0161  *   struct hfs_cat_key *key2: pointer to the second key to compare
0162  * Output Variable(s):
0163  *   NONE
0164  * Returns:
0165  *   int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
0166  * Preconditions:
0167  *   key1 and key2 point to "valid" (struct hfs_cat_key)s.
0168  * Postconditions:
0169  *   This function has no side-effects
0170  */
0171 int hfs_cat_keycmp(const btree_key *key1, const btree_key *key2)
0172 {
0173     __be32 k1p, k2p;
0174 
0175     k1p = key1->cat.ParID;
0176     k2p = key2->cat.ParID;
0177 
0178     if (k1p != k2p)
0179         return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
0180 
0181     return hfs_strcmp(key1->cat.CName.name, key1->cat.CName.len,
0182               key2->cat.CName.name, key2->cat.CName.len);
0183 }
0184 
0185 /* Try to get a catalog entry for given catalog id */
0186 // move to read_super???
0187 int hfs_cat_find_brec(struct super_block *sb, u32 cnid,
0188               struct hfs_find_data *fd)
0189 {
0190     hfs_cat_rec rec;
0191     int res, len, type;
0192 
0193     hfs_cat_build_key(sb, fd->search_key, cnid, NULL);
0194     res = hfs_brec_read(fd, &rec, sizeof(rec));
0195     if (res)
0196         return res;
0197 
0198     type = rec.type;
0199     if (type != HFS_CDR_THD && type != HFS_CDR_FTH) {
0200         pr_err("found bad thread record in catalog\n");
0201         return -EIO;
0202     }
0203 
0204     fd->search_key->cat.ParID = rec.thread.ParID;
0205     len = fd->search_key->cat.CName.len = rec.thread.CName.len;
0206     if (len > HFS_NAMELEN) {
0207         pr_err("bad catalog namelength\n");
0208         return -EIO;
0209     }
0210     memcpy(fd->search_key->cat.CName.name, rec.thread.CName.name, len);
0211     return hfs_brec_find(fd);
0212 }
0213 
0214 
0215 /*
0216  * hfs_cat_delete()
0217  *
0218  * Delete the indicated file or directory.
0219  * The associated thread is also removed unless ('with_thread'==0).
0220  */
0221 int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str)
0222 {
0223     struct super_block *sb;
0224     struct hfs_find_data fd;
0225     struct hfs_readdir_data *rd;
0226     int res, type;
0227 
0228     hfs_dbg(CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid);
0229     sb = dir->i_sb;
0230     res = hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
0231     if (res)
0232         return res;
0233 
0234     hfs_cat_build_key(sb, fd.search_key, dir->i_ino, str);
0235     res = hfs_brec_find(&fd);
0236     if (res)
0237         goto out;
0238 
0239     type = hfs_bnode_read_u8(fd.bnode, fd.entryoffset);
0240     if (type == HFS_CDR_FIL) {
0241         struct hfs_cat_file file;
0242         hfs_bnode_read(fd.bnode, &file, fd.entryoffset, sizeof(file));
0243         if (be32_to_cpu(file.FlNum) == cnid) {
0244 #if 0
0245             hfs_free_fork(sb, &file, HFS_FK_DATA);
0246 #endif
0247             hfs_free_fork(sb, &file, HFS_FK_RSRC);
0248         }
0249     }
0250 
0251     /* we only need to take spinlock for exclusion with ->release() */
0252     spin_lock(&HFS_I(dir)->open_dir_lock);
0253     list_for_each_entry(rd, &HFS_I(dir)->open_dir_list, list) {
0254         if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
0255             rd->file->f_pos--;
0256     }
0257     spin_unlock(&HFS_I(dir)->open_dir_lock);
0258 
0259     res = hfs_brec_remove(&fd);
0260     if (res)
0261         goto out;
0262 
0263     hfs_cat_build_key(sb, fd.search_key, cnid, NULL);
0264     res = hfs_brec_find(&fd);
0265     if (!res) {
0266         res = hfs_brec_remove(&fd);
0267         if (res)
0268             goto out;
0269     }
0270 
0271     dir->i_size--;
0272     dir->i_mtime = dir->i_ctime = current_time(dir);
0273     mark_inode_dirty(dir);
0274     res = 0;
0275 out:
0276     hfs_find_exit(&fd);
0277 
0278     return res;
0279 }
0280 
0281 /*
0282  * hfs_cat_move()
0283  *
0284  * Rename a file or directory, possibly to a new directory.
0285  * If the destination exists it is removed and a
0286  * (struct hfs_cat_entry) for it is returned in '*result'.
0287  */
0288 int hfs_cat_move(u32 cnid, struct inode *src_dir, const struct qstr *src_name,
0289          struct inode *dst_dir, const struct qstr *dst_name)
0290 {
0291     struct super_block *sb;
0292     struct hfs_find_data src_fd, dst_fd;
0293     union hfs_cat_rec entry;
0294     int entry_size, type;
0295     int err;
0296 
0297     hfs_dbg(CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n",
0298         cnid, src_dir->i_ino, src_name->name,
0299         dst_dir->i_ino, dst_name->name);
0300     sb = src_dir->i_sb;
0301     err = hfs_find_init(HFS_SB(sb)->cat_tree, &src_fd);
0302     if (err)
0303         return err;
0304     dst_fd = src_fd;
0305 
0306     /*
0307      * Fail early and avoid ENOSPC during the btree operations. We may
0308      * have to split the root node at most once.
0309      */
0310     err = hfs_bmap_reserve(src_fd.tree, 2 * src_fd.tree->depth);
0311     if (err)
0312         goto out;
0313 
0314     /* find the old dir entry and read the data */
0315     hfs_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
0316     err = hfs_brec_find(&src_fd);
0317     if (err)
0318         goto out;
0319     if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) {
0320         err = -EIO;
0321         goto out;
0322     }
0323 
0324     hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
0325                 src_fd.entrylength);
0326 
0327     /* create new dir entry with the data from the old entry */
0328     hfs_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name);
0329     err = hfs_brec_find(&dst_fd);
0330     if (err != -ENOENT) {
0331         if (!err)
0332             err = -EEXIST;
0333         goto out;
0334     }
0335 
0336     err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength);
0337     if (err)
0338         goto out;
0339     dst_dir->i_size++;
0340     dst_dir->i_mtime = dst_dir->i_ctime = current_time(dst_dir);
0341     mark_inode_dirty(dst_dir);
0342 
0343     /* finally remove the old entry */
0344     hfs_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
0345     err = hfs_brec_find(&src_fd);
0346     if (err)
0347         goto out;
0348     err = hfs_brec_remove(&src_fd);
0349     if (err)
0350         goto out;
0351     src_dir->i_size--;
0352     src_dir->i_mtime = src_dir->i_ctime = current_time(src_dir);
0353     mark_inode_dirty(src_dir);
0354 
0355     type = entry.type;
0356     if (type == HFS_CDR_FIL && !(entry.file.Flags & HFS_FIL_THD))
0357         goto out;
0358 
0359     /* remove old thread entry */
0360     hfs_cat_build_key(sb, src_fd.search_key, cnid, NULL);
0361     err = hfs_brec_find(&src_fd);
0362     if (err)
0363         goto out;
0364     err = hfs_brec_remove(&src_fd);
0365     if (err)
0366         goto out;
0367 
0368     /* create new thread entry */
0369     hfs_cat_build_key(sb, dst_fd.search_key, cnid, NULL);
0370     entry_size = hfs_cat_build_thread(sb, &entry, type == HFS_CDR_FIL ? HFS_CDR_FTH : HFS_CDR_THD,
0371                     dst_dir->i_ino, dst_name);
0372     err = hfs_brec_find(&dst_fd);
0373     if (err != -ENOENT) {
0374         if (!err)
0375             err = -EEXIST;
0376         goto out;
0377     }
0378     err = hfs_brec_insert(&dst_fd, &entry, entry_size);
0379 out:
0380     hfs_bnode_put(dst_fd.bnode);
0381     hfs_find_exit(&src_fd);
0382     return err;
0383 }