11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds * linux/fs/hfs/dir.c
31da177e4SLinus Torvalds *
41da177e4SLinus Torvalds * Copyright (C) 1995-1997 Paul H. Hargrove
51da177e4SLinus Torvalds * (C) 2003 Ardis Technologies <roman@ardistech.com>
61da177e4SLinus Torvalds * This file may be distributed under the terms of the GNU General Public License.
71da177e4SLinus Torvalds *
81da177e4SLinus Torvalds * This file contains directory-related functions independent of which
91da177e4SLinus Torvalds * scheme is being used to represent forks.
101da177e4SLinus Torvalds *
111da177e4SLinus Torvalds * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
121da177e4SLinus Torvalds */
131da177e4SLinus Torvalds
141da177e4SLinus Torvalds #include "hfs_fs.h"
151da177e4SLinus Torvalds #include "btree.h"
161da177e4SLinus Torvalds
171da177e4SLinus Torvalds /*
181da177e4SLinus Torvalds * hfs_lookup()
191da177e4SLinus Torvalds */
hfs_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)201da177e4SLinus Torvalds static struct dentry *hfs_lookup(struct inode *dir, struct dentry *dentry,
2100cd8dd3SAl Viro unsigned int flags)
221da177e4SLinus Torvalds {
231da177e4SLinus Torvalds hfs_cat_rec rec;
241da177e4SLinus Torvalds struct hfs_find_data fd;
251da177e4SLinus Torvalds struct inode *inode = NULL;
261da177e4SLinus Torvalds int res;
271da177e4SLinus Torvalds
289509f178SAlexey Khoroshilov res = hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd);
299509f178SAlexey Khoroshilov if (res)
309509f178SAlexey Khoroshilov return ERR_PTR(res);
31328b9227SRoman Zippel hfs_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name);
321da177e4SLinus Torvalds res = hfs_brec_read(&fd, &rec, sizeof(rec));
331da177e4SLinus Torvalds if (res) {
346b9cceeaSAl Viro if (res != -ENOENT)
356b9cceeaSAl Viro inode = ERR_PTR(res);
366b9cceeaSAl Viro } else {
371da177e4SLinus Torvalds inode = hfs_iget(dir->i_sb, &fd.search_key->cat, &rec);
381da177e4SLinus Torvalds if (!inode)
396b9cceeaSAl Viro inode = ERR_PTR(-EACCES);
406b9cceeaSAl Viro }
416b9cceeaSAl Viro hfs_find_exit(&fd);
426b9cceeaSAl Viro return d_splice_alias(inode, dentry);
431da177e4SLinus Torvalds }
441da177e4SLinus Torvalds
451da177e4SLinus Torvalds /*
461da177e4SLinus Torvalds * hfs_readdir
471da177e4SLinus Torvalds */
hfs_readdir(struct file * file,struct dir_context * ctx)48002f8becSAl Viro static int hfs_readdir(struct file *file, struct dir_context *ctx)
491da177e4SLinus Torvalds {
50002f8becSAl Viro struct inode *inode = file_inode(file);
511da177e4SLinus Torvalds struct super_block *sb = inode->i_sb;
521da177e4SLinus Torvalds int len, err;
53328b9227SRoman Zippel char strbuf[HFS_MAX_NAMELEN];
541da177e4SLinus Torvalds union hfs_cat_rec entry;
551da177e4SLinus Torvalds struct hfs_find_data fd;
561da177e4SLinus Torvalds struct hfs_readdir_data *rd;
571da177e4SLinus Torvalds u16 type;
581da177e4SLinus Torvalds
59002f8becSAl Viro if (ctx->pos >= inode->i_size)
601da177e4SLinus Torvalds return 0;
611da177e4SLinus Torvalds
629509f178SAlexey Khoroshilov err = hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
639509f178SAlexey Khoroshilov if (err)
649509f178SAlexey Khoroshilov return err;
65328b9227SRoman Zippel hfs_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
661da177e4SLinus Torvalds err = hfs_brec_find(&fd);
671da177e4SLinus Torvalds if (err)
681da177e4SLinus Torvalds goto out;
691da177e4SLinus Torvalds
70002f8becSAl Viro if (ctx->pos == 0) {
711da177e4SLinus Torvalds /* This is completely artificial... */
72002f8becSAl Viro if (!dir_emit_dot(file, ctx))
731da177e4SLinus Torvalds goto out;
74002f8becSAl Viro ctx->pos = 1;
75002f8becSAl Viro }
76002f8becSAl Viro if (ctx->pos == 1) {
77ec81aecbSAmerigo Wang if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
78ec81aecbSAmerigo Wang err = -EIO;
79ec81aecbSAmerigo Wang goto out;
80ec81aecbSAmerigo Wang }
81ec81aecbSAmerigo Wang
821da177e4SLinus Torvalds hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
831da177e4SLinus Torvalds if (entry.type != HFS_CDR_THD) {
84d6142673SJoe Perches pr_err("bad catalog folder thread\n");
851da177e4SLinus Torvalds err = -EIO;
861da177e4SLinus Torvalds goto out;
871da177e4SLinus Torvalds }
881da177e4SLinus Torvalds //if (fd.entrylength < HFS_MIN_THREAD_SZ) {
89d6142673SJoe Perches // pr_err("truncated catalog thread\n");
901da177e4SLinus Torvalds // err = -EIO;
911da177e4SLinus Torvalds // goto out;
921da177e4SLinus Torvalds //}
93002f8becSAl Viro if (!dir_emit(ctx, "..", 2,
941da177e4SLinus Torvalds be32_to_cpu(entry.thread.ParID), DT_DIR))
951da177e4SLinus Torvalds goto out;
96002f8becSAl Viro ctx->pos = 2;
97002f8becSAl Viro }
98002f8becSAl Viro if (ctx->pos >= inode->i_size)
991da177e4SLinus Torvalds goto out;
100002f8becSAl Viro err = hfs_brec_goto(&fd, ctx->pos - 1);
1011da177e4SLinus Torvalds if (err)
1021da177e4SLinus Torvalds goto out;
1031da177e4SLinus Torvalds
1041da177e4SLinus Torvalds for (;;) {
1051da177e4SLinus Torvalds if (be32_to_cpu(fd.key->cat.ParID) != inode->i_ino) {
106d6142673SJoe Perches pr_err("walked past end of dir\n");
1071da177e4SLinus Torvalds err = -EIO;
1081da177e4SLinus Torvalds goto out;
1091da177e4SLinus Torvalds }
110ec81aecbSAmerigo Wang
111ec81aecbSAmerigo Wang if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
112ec81aecbSAmerigo Wang err = -EIO;
113ec81aecbSAmerigo Wang goto out;
114ec81aecbSAmerigo Wang }
115ec81aecbSAmerigo Wang
1161da177e4SLinus Torvalds hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
1171da177e4SLinus Torvalds type = entry.type;
118328b9227SRoman Zippel len = hfs_mac2asc(sb, strbuf, &fd.key->cat.CName);
1191da177e4SLinus Torvalds if (type == HFS_CDR_DIR) {
1201da177e4SLinus Torvalds if (fd.entrylength < sizeof(struct hfs_cat_dir)) {
121d6142673SJoe Perches pr_err("small dir entry\n");
1221da177e4SLinus Torvalds err = -EIO;
1231da177e4SLinus Torvalds goto out;
1241da177e4SLinus Torvalds }
125002f8becSAl Viro if (!dir_emit(ctx, strbuf, len,
1261da177e4SLinus Torvalds be32_to_cpu(entry.dir.DirID), DT_DIR))
1271da177e4SLinus Torvalds break;
1281da177e4SLinus Torvalds } else if (type == HFS_CDR_FIL) {
1291da177e4SLinus Torvalds if (fd.entrylength < sizeof(struct hfs_cat_file)) {
130d6142673SJoe Perches pr_err("small file entry\n");
1311da177e4SLinus Torvalds err = -EIO;
1321da177e4SLinus Torvalds goto out;
1331da177e4SLinus Torvalds }
134002f8becSAl Viro if (!dir_emit(ctx, strbuf, len,
1351da177e4SLinus Torvalds be32_to_cpu(entry.file.FlNum), DT_REG))
1361da177e4SLinus Torvalds break;
1371da177e4SLinus Torvalds } else {
138d6142673SJoe Perches pr_err("bad catalog entry type %d\n", type);
1391da177e4SLinus Torvalds err = -EIO;
1401da177e4SLinus Torvalds goto out;
1411da177e4SLinus Torvalds }
142002f8becSAl Viro ctx->pos++;
143002f8becSAl Viro if (ctx->pos >= inode->i_size)
1441da177e4SLinus Torvalds goto out;
1451da177e4SLinus Torvalds err = hfs_brec_goto(&fd, 1);
1461da177e4SLinus Torvalds if (err)
1471da177e4SLinus Torvalds goto out;
1481da177e4SLinus Torvalds }
149002f8becSAl Viro rd = file->private_data;
1501da177e4SLinus Torvalds if (!rd) {
1511da177e4SLinus Torvalds rd = kmalloc(sizeof(struct hfs_readdir_data), GFP_KERNEL);
1521da177e4SLinus Torvalds if (!rd) {
1531da177e4SLinus Torvalds err = -ENOMEM;
1541da177e4SLinus Torvalds goto out;
1551da177e4SLinus Torvalds }
156002f8becSAl Viro file->private_data = rd;
157002f8becSAl Viro rd->file = file;
1589717a91bSAl Viro spin_lock(&HFS_I(inode)->open_dir_lock);
1591da177e4SLinus Torvalds list_add(&rd->list, &HFS_I(inode)->open_dir_list);
1609717a91bSAl Viro spin_unlock(&HFS_I(inode)->open_dir_lock);
1611da177e4SLinus Torvalds }
1629717a91bSAl Viro /*
1639717a91bSAl Viro * Can be done after the list insertion; exclusion with
1649717a91bSAl Viro * hfs_delete_cat() is provided by directory lock.
1659717a91bSAl Viro */
166eec11535SDan Carpenter memcpy(&rd->key, &fd.key->cat, sizeof(struct hfs_cat_key));
1671da177e4SLinus Torvalds out:
1681da177e4SLinus Torvalds hfs_find_exit(&fd);
1691da177e4SLinus Torvalds return err;
1701da177e4SLinus Torvalds }
1711da177e4SLinus Torvalds
hfs_dir_release(struct inode * inode,struct file * file)1721da177e4SLinus Torvalds static int hfs_dir_release(struct inode *inode, struct file *file)
1731da177e4SLinus Torvalds {
1741da177e4SLinus Torvalds struct hfs_readdir_data *rd = file->private_data;
1751da177e4SLinus Torvalds if (rd) {
1769717a91bSAl Viro spin_lock(&HFS_I(inode)->open_dir_lock);
1771da177e4SLinus Torvalds list_del(&rd->list);
1789717a91bSAl Viro spin_unlock(&HFS_I(inode)->open_dir_lock);
1791da177e4SLinus Torvalds kfree(rd);
1801da177e4SLinus Torvalds }
1811da177e4SLinus Torvalds return 0;
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds
1841da177e4SLinus Torvalds /*
1851da177e4SLinus Torvalds * hfs_create()
1861da177e4SLinus Torvalds *
1871da177e4SLinus Torvalds * This is the create() entry in the inode_operations structure for
1881da177e4SLinus Torvalds * regular HFS directories. The purpose is to create a new file in
1891da177e4SLinus Torvalds * a directory and return a corresponding inode, given the inode for
1901da177e4SLinus Torvalds * the directory and the name (and its length) of the new file.
1911da177e4SLinus Torvalds */
hfs_create(struct mnt_idmap * idmap,struct inode * dir,struct dentry * dentry,umode_t mode,bool excl)1926c960e68SChristian Brauner static int hfs_create(struct mnt_idmap *idmap, struct inode *dir,
193549c7297SChristian Brauner struct dentry *dentry, umode_t mode, bool excl)
1941da177e4SLinus Torvalds {
1951da177e4SLinus Torvalds struct inode *inode;
1961da177e4SLinus Torvalds int res;
1971da177e4SLinus Torvalds
1981da177e4SLinus Torvalds inode = hfs_new_inode(dir, &dentry->d_name, mode);
1991da177e4SLinus Torvalds if (!inode)
20013f24485SChengyu Song return -ENOMEM;
2011da177e4SLinus Torvalds
2021da177e4SLinus Torvalds res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode);
2031da177e4SLinus Torvalds if (res) {
2046d6b77f1SMiklos Szeredi clear_nlink(inode);
2051da177e4SLinus Torvalds hfs_delete_inode(inode);
2061da177e4SLinus Torvalds iput(inode);
2071da177e4SLinus Torvalds return res;
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds d_instantiate(dentry, inode);
2101da177e4SLinus Torvalds mark_inode_dirty(inode);
2111da177e4SLinus Torvalds return 0;
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds
2141da177e4SLinus Torvalds /*
2151da177e4SLinus Torvalds * hfs_mkdir()
2161da177e4SLinus Torvalds *
2171da177e4SLinus Torvalds * This is the mkdir() entry in the inode_operations structure for
2181da177e4SLinus Torvalds * regular HFS directories. The purpose is to create a new directory
2191da177e4SLinus Torvalds * in a directory, given the inode for the parent directory and the
2201da177e4SLinus Torvalds * name (and its length) of the new directory.
2211da177e4SLinus Torvalds */
hfs_mkdir(struct mnt_idmap * idmap,struct inode * dir,struct dentry * dentry,umode_t mode)222c54bd91eSChristian Brauner static int hfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
223549c7297SChristian Brauner struct dentry *dentry, umode_t mode)
2241da177e4SLinus Torvalds {
2251da177e4SLinus Torvalds struct inode *inode;
2261da177e4SLinus Torvalds int res;
2271da177e4SLinus Torvalds
2281da177e4SLinus Torvalds inode = hfs_new_inode(dir, &dentry->d_name, S_IFDIR | mode);
2291da177e4SLinus Torvalds if (!inode)
23013f24485SChengyu Song return -ENOMEM;
2311da177e4SLinus Torvalds
2321da177e4SLinus Torvalds res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode);
2331da177e4SLinus Torvalds if (res) {
2346d6b77f1SMiklos Szeredi clear_nlink(inode);
2351da177e4SLinus Torvalds hfs_delete_inode(inode);
2361da177e4SLinus Torvalds iput(inode);
2371da177e4SLinus Torvalds return res;
2381da177e4SLinus Torvalds }
2391da177e4SLinus Torvalds d_instantiate(dentry, inode);
2401da177e4SLinus Torvalds mark_inode_dirty(inode);
2411da177e4SLinus Torvalds return 0;
2421da177e4SLinus Torvalds }
2431da177e4SLinus Torvalds
2441da177e4SLinus Torvalds /*
24569102e9bSAl Viro * hfs_remove()
2461da177e4SLinus Torvalds *
24769102e9bSAl Viro * This serves as both unlink() and rmdir() in the inode_operations
24869102e9bSAl Viro * structure for regular HFS directories. The purpose is to delete
24969102e9bSAl Viro * an existing child, given the inode for the parent directory and
25069102e9bSAl Viro * the name (and its length) of the existing directory.
25169102e9bSAl Viro *
25269102e9bSAl Viro * HFS does not have hardlinks, so both rmdir and unlink set the
25369102e9bSAl Viro * link count to 0. The only difference is the emptiness check.
2541da177e4SLinus Torvalds */
hfs_remove(struct inode * dir,struct dentry * dentry)25569102e9bSAl Viro static int hfs_remove(struct inode *dir, struct dentry *dentry)
2561da177e4SLinus Torvalds {
2572b0143b5SDavid Howells struct inode *inode = d_inode(dentry);
2581da177e4SLinus Torvalds int res;
2591da177e4SLinus Torvalds
26069102e9bSAl Viro if (S_ISDIR(inode->i_mode) && inode->i_size != 2)
2611da177e4SLinus Torvalds return -ENOTEMPTY;
2621da177e4SLinus Torvalds res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name);
2631da177e4SLinus Torvalds if (res)
2641da177e4SLinus Torvalds return res;
265ce71ec36SDave Hansen clear_nlink(inode);
266*7305586aSJeff Layton inode_set_ctime_current(inode);
2671da177e4SLinus Torvalds hfs_delete_inode(inode);
2681da177e4SLinus Torvalds mark_inode_dirty(inode);
2691da177e4SLinus Torvalds return 0;
2701da177e4SLinus Torvalds }
2711da177e4SLinus Torvalds
2721da177e4SLinus Torvalds /*
2731da177e4SLinus Torvalds * hfs_rename()
2741da177e4SLinus Torvalds *
2751da177e4SLinus Torvalds * This is the rename() entry in the inode_operations structure for
2761da177e4SLinus Torvalds * regular HFS directories. The purpose is to rename an existing
2771da177e4SLinus Torvalds * file or directory, given the inode for the current directory and
2781da177e4SLinus Torvalds * the name (and its length) of the existing file/directory and the
2791da177e4SLinus Torvalds * inode for the new directory and the name (and its length) of the
2801da177e4SLinus Torvalds * new file/directory.
2811da177e4SLinus Torvalds * XXX: how do you handle must_be dir?
2821da177e4SLinus Torvalds */
hfs_rename(struct mnt_idmap * idmap,struct inode * old_dir,struct dentry * old_dentry,struct inode * new_dir,struct dentry * new_dentry,unsigned int flags)283e18275aeSChristian Brauner static int hfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
284549c7297SChristian Brauner struct dentry *old_dentry, struct inode *new_dir,
285549c7297SChristian Brauner struct dentry *new_dentry, unsigned int flags)
2861da177e4SLinus Torvalds {
2871da177e4SLinus Torvalds int res;
2881da177e4SLinus Torvalds
289f03b8ad8SMiklos Szeredi if (flags & ~RENAME_NOREPLACE)
290f03b8ad8SMiklos Szeredi return -EINVAL;
291f03b8ad8SMiklos Szeredi
2921da177e4SLinus Torvalds /* Unlink destination if it already exists */
2932b0143b5SDavid Howells if (d_really_is_positive(new_dentry)) {
29469102e9bSAl Viro res = hfs_remove(new_dir, new_dentry);
2951da177e4SLinus Torvalds if (res)
2961da177e4SLinus Torvalds return res;
2971da177e4SLinus Torvalds }
2981da177e4SLinus Torvalds
2992b0143b5SDavid Howells res = hfs_cat_move(d_inode(old_dentry)->i_ino,
3001da177e4SLinus Torvalds old_dir, &old_dentry->d_name,
3011da177e4SLinus Torvalds new_dir, &new_dentry->d_name);
3021da177e4SLinus Torvalds if (!res)
303328b9227SRoman Zippel hfs_cat_build_key(old_dir->i_sb,
3042b0143b5SDavid Howells (btree_key *)&HFS_I(d_inode(old_dentry))->cat_key,
3051da177e4SLinus Torvalds new_dir->i_ino, &new_dentry->d_name);
3061da177e4SLinus Torvalds return res;
3071da177e4SLinus Torvalds }
3081da177e4SLinus Torvalds
3094b6f5d20SArjan van de Ven const struct file_operations hfs_dir_operations = {
3101da177e4SLinus Torvalds .read = generic_read_dir,
3119717a91bSAl Viro .iterate_shared = hfs_readdir,
3121da177e4SLinus Torvalds .llseek = generic_file_llseek,
3131da177e4SLinus Torvalds .release = hfs_dir_release,
3141da177e4SLinus Torvalds };
3151da177e4SLinus Torvalds
31692e1d5beSArjan van de Ven const struct inode_operations hfs_dir_inode_operations = {
3171da177e4SLinus Torvalds .create = hfs_create,
3181da177e4SLinus Torvalds .lookup = hfs_lookup,
31969102e9bSAl Viro .unlink = hfs_remove,
3201da177e4SLinus Torvalds .mkdir = hfs_mkdir,
32169102e9bSAl Viro .rmdir = hfs_remove,
3221da177e4SLinus Torvalds .rename = hfs_rename,
3231da177e4SLinus Torvalds .setattr = hfs_inode_setattr,
3241da177e4SLinus Torvalds };
325