xref: /openbmc/linux/fs/hfs/dir.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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