Blame view

fs/hfs/dir.c 8.21 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
27
28
29
30
  /*
   *  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;
  
  	dentry->d_op = &hfs_dentry_operations;
  
  	hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd);
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
31
  	hfs_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  	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...
56
  	struct inode *inode = filp->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
  	struct super_block *sb = inode->i_sb;
  	int len, err;
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
59
  	char strbuf[HFS_MAX_NAMELEN];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
63
64
65
66
67
68
  	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 ...
69
  	hfs_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
75
76
77
78
79
80
81
  	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...
82
83
84
85
  		if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
  			err = -EIO;
  			goto out;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
87
  		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
  		if (entry.type != HFS_CDR_THD) {
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
88
89
  			printk(KERN_ERR "hfs: bad catalog folder thread
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
92
93
  			err = -EIO;
  			goto out;
  		}
  		//if (fd.entrylength < HFS_MIN_THREAD_SZ) {
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
94
95
  		//	printk(KERN_ERR "hfs: truncated catalog thread
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  		//	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...
114
115
  			printk(KERN_ERR "hfs: walked past end of dir
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
  			err = -EIO;
  			goto out;
  		}
ec81aecb2   Amerigo Wang   hfs: fix a potent...
119
120
121
122
123
  
  		if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
  			err = -EIO;
  			goto out;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
  		hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
  		type = entry.type;
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
126
  		len = hfs_mac2asc(sb, strbuf, &fd.key->cat.CName);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
  		if (type == HFS_CDR_DIR) {
  			if (fd.entrylength < sizeof(struct hfs_cat_dir)) {
7cf3cc303   Roman Zippel   [PATCH] hfs: clea...
129
130
  				printk(KERN_ERR "hfs: small dir entry
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
132
133
134
135
136
137
138
  				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...
139
140
  				printk(KERN_ERR "hfs: small file entry
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
145
146
147
  				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...
148
149
  			printk(KERN_ERR "hfs: bad catalog entry type %d
  ", type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
204
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
  			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) {
  		inode->i_nlink = 0;
  		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) {
  		inode->i_nlink = 0;
  		hfs_delete_inode(inode);
  		iput(inode);
  		return res;
  	}
  	d_instantiate(dentry, inode);
  	mark_inode_dirty(inode);
  	return 0;
  }
  
  /*
   * hfs_unlink()
   *
   * This is the unlink() entry in the inode_operations structure for
   * regular HFS directories.  The purpose is to delete an existing
   * file, given the inode for the parent directory and the name
   * (and its length) of the existing file.
   */
  static int hfs_unlink(struct inode *dir, struct dentry *dentry)
  {
  	struct inode *inode;
  	int res;
  
  	inode = dentry->d_inode;
  	res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name);
  	if (res)
  		return res;
9a53c3a78   Dave Hansen   [PATCH] r/o bind ...
263
  	drop_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
288
289
  	hfs_delete_inode(inode);
  	inode->i_ctime = CURRENT_TIME_SEC;
  	mark_inode_dirty(inode);
  
  	return res;
  }
  
  /*
   * hfs_rmdir()
   *
   * This is the rmdir() entry in the inode_operations structure for
   * regular HFS directories.  The purpose is to delete an existing
   * directory, given the inode for the parent directory and the name
   * (and its length) of the existing directory.
   */
  static int hfs_rmdir(struct inode *dir, struct dentry *dentry)
  {
  	struct inode *inode;
  	int res;
  
  	inode = dentry->d_inode;
  	if (inode->i_size != 2)
  		return -ENOTEMPTY;
  	res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name);
  	if (res)
  		return res;
ce71ec368   Dave Hansen   [PATCH] r/o bind ...
290
  	clear_nlink(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  	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) {
  		res = hfs_unlink(new_dir, new_dentry);
  		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 ...
324
325
  		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
326
327
328
  				  new_dir->i_ino, &new_dentry->d_name);
  	return res;
  }
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
329
  const struct file_operations hfs_dir_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
332
333
334
  	.read		= generic_read_dir,
  	.readdir	= hfs_readdir,
  	.llseek		= generic_file_llseek,
  	.release	= hfs_dir_release,
  };
92e1d5be9   Arjan van de Ven   [PATCH] mark stru...
335
  const struct inode_operations hfs_dir_inode_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
340
341
342
343
  	.create		= hfs_create,
  	.lookup		= hfs_lookup,
  	.unlink		= hfs_unlink,
  	.mkdir		= hfs_mkdir,
  	.rmdir		= hfs_rmdir,
  	.rename		= hfs_rename,
  	.setattr	= hfs_inode_setattr,
  };