Blame view

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