xref: /openbmc/linux/fs/hfs/dir.c (revision 68198dca)
1  /*
2   *  linux/fs/hfs/dir.c
3   *
4   * Copyright (C) 1995-1997  Paul H. Hargrove
5   * (C) 2003 Ardis Technologies <roman@ardistech.com>
6   * This file may be distributed under the terms of the GNU General Public License.
7   *
8   * This file contains directory-related functions independent of which
9   * scheme is being used to represent forks.
10   *
11   * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
12   */
13  
14  #include "hfs_fs.h"
15  #include "btree.h"
16  
17  /*
18   * hfs_lookup()
19   */
20  static struct dentry *hfs_lookup(struct inode *dir, struct dentry *dentry,
21  				 unsigned int flags)
22  {
23  	hfs_cat_rec rec;
24  	struct hfs_find_data fd;
25  	struct inode *inode = NULL;
26  	int res;
27  
28  	res = hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd);
29  	if (res)
30  		return ERR_PTR(res);
31  	hfs_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name);
32  	res = hfs_brec_read(&fd, &rec, sizeof(rec));
33  	if (res) {
34  		hfs_find_exit(&fd);
35  		if (res == -ENOENT) {
36  			/* No such entry */
37  			inode = NULL;
38  			goto done;
39  		}
40  		return ERR_PTR(res);
41  	}
42  	inode = hfs_iget(dir->i_sb, &fd.search_key->cat, &rec);
43  	hfs_find_exit(&fd);
44  	if (!inode)
45  		return ERR_PTR(-EACCES);
46  done:
47  	d_add(dentry, inode);
48  	return NULL;
49  }
50  
51  /*
52   * hfs_readdir
53   */
54  static int hfs_readdir(struct file *file, struct dir_context *ctx)
55  {
56  	struct inode *inode = file_inode(file);
57  	struct super_block *sb = inode->i_sb;
58  	int len, err;
59  	char strbuf[HFS_MAX_NAMELEN];
60  	union hfs_cat_rec entry;
61  	struct hfs_find_data fd;
62  	struct hfs_readdir_data *rd;
63  	u16 type;
64  
65  	if (ctx->pos >= inode->i_size)
66  		return 0;
67  
68  	err = hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
69  	if (err)
70  		return err;
71  	hfs_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
72  	err = hfs_brec_find(&fd);
73  	if (err)
74  		goto out;
75  
76  	if (ctx->pos == 0) {
77  		/* This is completely artificial... */
78  		if (!dir_emit_dot(file, ctx))
79  			goto out;
80  		ctx->pos = 1;
81  	}
82  	if (ctx->pos == 1) {
83  		if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
84  			err = -EIO;
85  			goto out;
86  		}
87  
88  		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
89  		if (entry.type != HFS_CDR_THD) {
90  			pr_err("bad catalog folder thread\n");
91  			err = -EIO;
92  			goto out;
93  		}
94  		//if (fd.entrylength < HFS_MIN_THREAD_SZ) {
95  		//	pr_err("truncated catalog thread\n");
96  		//	err = -EIO;
97  		//	goto out;
98  		//}
99  		if (!dir_emit(ctx, "..", 2,
100  			    be32_to_cpu(entry.thread.ParID), DT_DIR))
101  			goto out;
102  		ctx->pos = 2;
103  	}
104  	if (ctx->pos >= inode->i_size)
105  		goto out;
106  	err = hfs_brec_goto(&fd, ctx->pos - 1);
107  	if (err)
108  		goto out;
109  
110  	for (;;) {
111  		if (be32_to_cpu(fd.key->cat.ParID) != inode->i_ino) {
112  			pr_err("walked past end of dir\n");
113  			err = -EIO;
114  			goto out;
115  		}
116  
117  		if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
118  			err = -EIO;
119  			goto out;
120  		}
121  
122  		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
123  		type = entry.type;
124  		len = hfs_mac2asc(sb, strbuf, &fd.key->cat.CName);
125  		if (type == HFS_CDR_DIR) {
126  			if (fd.entrylength < sizeof(struct hfs_cat_dir)) {
127  				pr_err("small dir entry\n");
128  				err = -EIO;
129  				goto out;
130  			}
131  			if (!dir_emit(ctx, strbuf, len,
132  				    be32_to_cpu(entry.dir.DirID), DT_DIR))
133  				break;
134  		} else if (type == HFS_CDR_FIL) {
135  			if (fd.entrylength < sizeof(struct hfs_cat_file)) {
136  				pr_err("small file entry\n");
137  				err = -EIO;
138  				goto out;
139  			}
140  			if (!dir_emit(ctx, strbuf, len,
141  				    be32_to_cpu(entry.file.FlNum), DT_REG))
142  				break;
143  		} else {
144  			pr_err("bad catalog entry type %d\n", type);
145  			err = -EIO;
146  			goto out;
147  		}
148  		ctx->pos++;
149  		if (ctx->pos >= inode->i_size)
150  			goto out;
151  		err = hfs_brec_goto(&fd, 1);
152  		if (err)
153  			goto out;
154  	}
155  	rd = file->private_data;
156  	if (!rd) {
157  		rd = kmalloc(sizeof(struct hfs_readdir_data), GFP_KERNEL);
158  		if (!rd) {
159  			err = -ENOMEM;
160  			goto out;
161  		}
162  		file->private_data = rd;
163  		rd->file = file;
164  		spin_lock(&HFS_I(inode)->open_dir_lock);
165  		list_add(&rd->list, &HFS_I(inode)->open_dir_list);
166  		spin_unlock(&HFS_I(inode)->open_dir_lock);
167  	}
168  	/*
169  	 * Can be done after the list insertion; exclusion with
170  	 * hfs_delete_cat() is provided by directory lock.
171  	 */
172  	memcpy(&rd->key, &fd.key->cat, sizeof(struct hfs_cat_key));
173  out:
174  	hfs_find_exit(&fd);
175  	return err;
176  }
177  
178  static int hfs_dir_release(struct inode *inode, struct file *file)
179  {
180  	struct hfs_readdir_data *rd = file->private_data;
181  	if (rd) {
182  		spin_lock(&HFS_I(inode)->open_dir_lock);
183  		list_del(&rd->list);
184  		spin_unlock(&HFS_I(inode)->open_dir_lock);
185  		kfree(rd);
186  	}
187  	return 0;
188  }
189  
190  /*
191   * hfs_create()
192   *
193   * This is the create() entry in the inode_operations structure for
194   * regular HFS directories.  The purpose is to create a new file in
195   * a directory and return a corresponding inode, given the inode for
196   * the directory and the name (and its length) of the new file.
197   */
198  static int hfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
199  		      bool excl)
200  {
201  	struct inode *inode;
202  	int res;
203  
204  	inode = hfs_new_inode(dir, &dentry->d_name, mode);
205  	if (!inode)
206  		return -ENOMEM;
207  
208  	res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode);
209  	if (res) {
210  		clear_nlink(inode);
211  		hfs_delete_inode(inode);
212  		iput(inode);
213  		return res;
214  	}
215  	d_instantiate(dentry, inode);
216  	mark_inode_dirty(inode);
217  	return 0;
218  }
219  
220  /*
221   * hfs_mkdir()
222   *
223   * This is the mkdir() entry in the inode_operations structure for
224   * regular HFS directories.  The purpose is to create a new directory
225   * in a directory, given the inode for the parent directory and the
226   * name (and its length) of the new directory.
227   */
228  static int hfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
229  {
230  	struct inode *inode;
231  	int res;
232  
233  	inode = hfs_new_inode(dir, &dentry->d_name, S_IFDIR | mode);
234  	if (!inode)
235  		return -ENOMEM;
236  
237  	res = hfs_cat_create(inode->i_ino, dir, &dentry->d_name, inode);
238  	if (res) {
239  		clear_nlink(inode);
240  		hfs_delete_inode(inode);
241  		iput(inode);
242  		return res;
243  	}
244  	d_instantiate(dentry, inode);
245  	mark_inode_dirty(inode);
246  	return 0;
247  }
248  
249  /*
250   * hfs_remove()
251   *
252   * This serves as both unlink() and rmdir() in the inode_operations
253   * structure for regular HFS directories.  The purpose is to delete
254   * an existing child, given the inode for the parent directory and
255   * the name (and its length) of the existing directory.
256   *
257   * HFS does not have hardlinks, so both rmdir and unlink set the
258   * link count to 0.  The only difference is the emptiness check.
259   */
260  static int hfs_remove(struct inode *dir, struct dentry *dentry)
261  {
262  	struct inode *inode = d_inode(dentry);
263  	int res;
264  
265  	if (S_ISDIR(inode->i_mode) && inode->i_size != 2)
266  		return -ENOTEMPTY;
267  	res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name);
268  	if (res)
269  		return res;
270  	clear_nlink(inode);
271  	inode->i_ctime = current_time(inode);
272  	hfs_delete_inode(inode);
273  	mark_inode_dirty(inode);
274  	return 0;
275  }
276  
277  /*
278   * hfs_rename()
279   *
280   * This is the rename() entry in the inode_operations structure for
281   * regular HFS directories.  The purpose is to rename an existing
282   * file or directory, given the inode for the current directory and
283   * the name (and its length) of the existing file/directory and the
284   * inode for the new directory and the name (and its length) of the
285   * new file/directory.
286   * XXX: how do you handle must_be dir?
287   */
288  static int hfs_rename(struct inode *old_dir, struct dentry *old_dentry,
289  		      struct inode *new_dir, struct dentry *new_dentry,
290  		      unsigned int flags)
291  {
292  	int res;
293  
294  	if (flags & ~RENAME_NOREPLACE)
295  		return -EINVAL;
296  
297  	/* Unlink destination if it already exists */
298  	if (d_really_is_positive(new_dentry)) {
299  		res = hfs_remove(new_dir, new_dentry);
300  		if (res)
301  			return res;
302  	}
303  
304  	res = hfs_cat_move(d_inode(old_dentry)->i_ino,
305  			   old_dir, &old_dentry->d_name,
306  			   new_dir, &new_dentry->d_name);
307  	if (!res)
308  		hfs_cat_build_key(old_dir->i_sb,
309  				  (btree_key *)&HFS_I(d_inode(old_dentry))->cat_key,
310  				  new_dir->i_ino, &new_dentry->d_name);
311  	return res;
312  }
313  
314  const struct file_operations hfs_dir_operations = {
315  	.read		= generic_read_dir,
316  	.iterate_shared	= hfs_readdir,
317  	.llseek		= generic_file_llseek,
318  	.release	= hfs_dir_release,
319  };
320  
321  const struct inode_operations hfs_dir_inode_operations = {
322  	.create		= hfs_create,
323  	.lookup		= hfs_lookup,
324  	.unlink		= hfs_remove,
325  	.mkdir		= hfs_mkdir,
326  	.rmdir		= hfs_remove,
327  	.rename		= hfs_rename,
328  	.setattr	= hfs_inode_setattr,
329  };
330