initial commit with v2.6.9
[linux-2.6.9-moxart.git] / fs / hfsplus / dir.c
blob456e0a3b0381a7f53adb8bf7f7b3076c202ef906
1 /*
2 * linux/fs/hfsplus/dir.c
4 * Copyright (C) 2001
5 * Brad Boyer (flar@allandria.com)
6 * (C) 2003 Ardis Technologies <roman@ardistech.com>
8 * Handling of directories
9 */
11 #include <linux/errno.h>
12 #include <linux/fs.h>
13 #include <linux/sched.h>
14 #include <linux/slab.h>
15 #include <linux/random.h>
16 #include <linux/version.h>
18 #include "hfsplus_fs.h"
19 #include "hfsplus_raw.h"
21 static inline void hfsplus_instantiate(struct dentry *dentry,
22 struct inode *inode, u32 cnid)
24 dentry->d_fsdata = (void *)(unsigned long)cnid;
25 d_instantiate(dentry, inode);
28 /* Find the entry inside dir named dentry->d_name */
29 static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
30 struct nameidata *nd)
32 struct inode *inode = NULL;
33 struct hfs_find_data fd;
34 struct super_block *sb;
35 hfsplus_cat_entry entry;
36 int err;
37 u32 cnid, linkid = 0;
38 u16 type;
40 sb = dir->i_sb;
41 dentry->d_fsdata = NULL;
42 hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
43 hfsplus_cat_build_key(fd.search_key, dir->i_ino, &dentry->d_name);
44 again:
45 err = hfs_brec_read(&fd, &entry, sizeof(entry));
46 if (err) {
47 if (err == -ENOENT) {
48 hfs_find_exit(&fd);
49 /* No such entry */
50 inode = NULL;
51 goto out;
53 goto fail;
55 type = be16_to_cpu(entry.type);
56 if (type == HFSPLUS_FOLDER) {
57 if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
58 err = -EIO;
59 goto fail;
61 cnid = be32_to_cpu(entry.folder.id);
62 dentry->d_fsdata = (void *)(unsigned long)cnid;
63 } else if (type == HFSPLUS_FILE) {
64 if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
65 err = -EIO;
66 goto fail;
68 cnid = be32_to_cpu(entry.file.id);
69 if (entry.file.user_info.fdType == cpu_to_be32(HFSP_HARDLINK_TYPE) &&
70 entry.file.user_info.fdCreator == cpu_to_be32(HFSP_HFSPLUS_CREATOR)) {
71 struct qstr str;
72 char name[32];
74 if (dentry->d_fsdata) {
75 err = -ENOENT;
76 inode = NULL;
77 goto out;
79 dentry->d_fsdata = (void *)(unsigned long)cnid;
80 linkid = be32_to_cpu(entry.file.permissions.dev);
81 str.len = sprintf(name, "iNode%d", linkid);
82 str.name = name;
83 hfsplus_cat_build_key(fd.search_key, HFSPLUS_SB(sb).hidden_dir->i_ino, &str);
84 goto again;
85 } else if (!dentry->d_fsdata)
86 dentry->d_fsdata = (void *)(unsigned long)cnid;
87 } else {
88 printk("HFS+-fs: Illegal catalog entry type in lookup\n");
89 err = -EIO;
90 goto fail;
92 hfs_find_exit(&fd);
93 inode = iget(dir->i_sb, cnid);
94 if (!inode)
95 return ERR_PTR(-EACCES);
96 if (S_ISREG(inode->i_mode))
97 HFSPLUS_I(inode).dev = linkid;
98 out:
99 d_add(dentry, inode);
100 return NULL;
101 fail:
102 hfs_find_exit(&fd);
103 return ERR_PTR(err);
106 static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
108 struct inode *inode = filp->f_dentry->d_inode;
109 struct super_block *sb = inode->i_sb;
110 int len, err;
111 char strbuf[HFSPLUS_MAX_STRLEN + 1];
112 hfsplus_cat_entry entry;
113 struct hfs_find_data fd;
114 struct hfsplus_readdir_data *rd;
115 u16 type;
117 if (filp->f_pos >= inode->i_size)
118 return 0;
120 hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
121 hfsplus_cat_build_key(fd.search_key, inode->i_ino, NULL);
122 err = hfs_brec_find(&fd);
123 if (err)
124 goto out;
126 switch ((u32)filp->f_pos) {
127 case 0:
128 /* This is completely artificial... */
129 if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
130 goto out;
131 filp->f_pos++;
132 /* fall through */
133 case 1:
134 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
135 if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
136 printk("HFS+-fs: bad catalog folder thread\n");
137 err = -EIO;
138 goto out;
140 if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
141 printk("HFS+-fs: truncated catalog thread\n");
142 err = -EIO;
143 goto out;
145 if (filldir(dirent, "..", 2, 1,
146 be32_to_cpu(entry.thread.parentID), DT_DIR))
147 goto out;
148 filp->f_pos++;
149 /* fall through */
150 default:
151 if (filp->f_pos >= inode->i_size)
152 goto out;
153 err = hfs_brec_goto(&fd, filp->f_pos - 1);
154 if (err)
155 goto out;
158 for (;;) {
159 if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
160 printk("HFS+-fs: walked past end of dir\n");
161 err = -EIO;
162 goto out;
164 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
165 type = be16_to_cpu(entry.type);
166 len = HFSPLUS_MAX_STRLEN;
167 err = hfsplus_uni2asc(&fd.key->cat.name, strbuf, &len);
168 if (err)
169 goto out;
170 if (type == HFSPLUS_FOLDER) {
171 if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
172 printk("HFS+-fs: small dir entry\n");
173 err = -EIO;
174 goto out;
176 if (HFSPLUS_SB(sb).hidden_dir &&
177 HFSPLUS_SB(sb).hidden_dir->i_ino == be32_to_cpu(entry.folder.id))
178 goto next;
179 if (filldir(dirent, strbuf, len, filp->f_pos,
180 be32_to_cpu(entry.folder.id), DT_DIR))
181 break;
182 } else if (type == HFSPLUS_FILE) {
183 if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
184 printk("HFS+-fs: small file entry\n");
185 err = -EIO;
186 goto out;
188 if (filldir(dirent, strbuf, len, filp->f_pos,
189 be32_to_cpu(entry.file.id), DT_REG))
190 break;
191 } else {
192 printk("HFS+-fs: bad catalog entry type\n");
193 err = -EIO;
194 goto out;
196 next:
197 filp->f_pos++;
198 if (filp->f_pos >= inode->i_size)
199 goto out;
200 err = hfs_brec_goto(&fd, 1);
201 if (err)
202 goto out;
204 rd = filp->private_data;
205 if (!rd) {
206 rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
207 if (!rd) {
208 err = -ENOMEM;
209 goto out;
211 filp->private_data = rd;
212 rd->file = filp;
213 list_add(&rd->list, &HFSPLUS_I(inode).open_dir_list);
215 memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
216 out:
217 hfs_find_exit(&fd);
218 return err;
221 static int hfsplus_dir_release(struct inode *inode, struct file *file)
223 struct hfsplus_readdir_data *rd = file->private_data;
224 if (rd) {
225 list_del(&rd->list);
226 kfree(rd);
228 return 0;
231 int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode,
232 struct nameidata *nd)
234 struct inode *inode;
235 int res;
237 inode = hfsplus_new_inode(dir->i_sb, mode);
238 if (!inode)
239 return -ENOSPC;
241 res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
242 if (res) {
243 inode->i_nlink = 0;
244 hfsplus_delete_inode(inode);
245 iput(inode);
246 return res;
248 hfsplus_instantiate(dentry, inode, inode->i_ino);
249 mark_inode_dirty(inode);
250 return 0;
253 int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, struct dentry *dst_dentry)
255 struct super_block *sb = dst_dir->i_sb;
256 struct inode *inode = src_dentry->d_inode;
257 struct inode *src_dir = src_dentry->d_parent->d_inode;
258 struct qstr str;
259 char name[32];
260 u32 cnid, id;
261 int res;
263 if (HFSPLUS_IS_RSRC(inode))
264 return -EPERM;
266 if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
267 for (;;) {
268 get_random_bytes(&id, sizeof(cnid));
269 id &= 0x3fffffff;
270 str.name = name;
271 str.len = sprintf(name, "iNode%d", id);
272 res = hfsplus_rename_cat(inode->i_ino,
273 src_dir, &src_dentry->d_name,
274 HFSPLUS_SB(sb).hidden_dir, &str);
275 if (!res)
276 break;
277 if (res != -EEXIST)
278 return res;
280 HFSPLUS_I(inode).dev = id;
281 cnid = HFSPLUS_SB(sb).next_cnid++;
282 src_dentry->d_fsdata = (void *)(unsigned long)cnid;
283 res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode);
284 if (res)
285 /* panic? */
286 return res;
287 HFSPLUS_SB(sb).file_count++;
289 cnid = HFSPLUS_SB(sb).next_cnid++;
290 res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
291 if (res)
292 return res;
294 inode->i_nlink++;
295 hfsplus_instantiate(dst_dentry, inode, cnid);
296 atomic_inc(&inode->i_count);
297 inode->i_ctime = CURRENT_TIME;
298 mark_inode_dirty(inode);
299 HFSPLUS_SB(sb).file_count++;
300 sb->s_dirt = 1;
302 return 0;
305 int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
307 struct super_block *sb = dir->i_sb;
308 struct inode *inode = dentry->d_inode;
309 struct qstr str;
310 char name[32];
311 u32 cnid;
312 int res;
314 if (HFSPLUS_IS_RSRC(inode))
315 return -EPERM;
317 cnid = (u32)(unsigned long)dentry->d_fsdata;
318 if (inode->i_ino == cnid &&
319 atomic_read(&HFSPLUS_I(inode).opencnt)) {
320 str.name = name;
321 str.len = sprintf(name, "temp%lu", inode->i_ino);
322 res = hfsplus_rename_cat(inode->i_ino,
323 dir, &dentry->d_name,
324 HFSPLUS_SB(sb).hidden_dir, &str);
325 if (!res)
326 inode->i_flags |= S_DEAD;
327 return res;
329 res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
330 if (res)
331 return res;
333 inode->i_nlink--;
334 hfsplus_delete_inode(inode);
335 if (inode->i_ino != cnid && !inode->i_nlink) {
336 if (!atomic_read(&HFSPLUS_I(inode).opencnt)) {
337 res = hfsplus_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL);
338 if (!res)
339 hfsplus_delete_inode(inode);
340 } else
341 inode->i_flags |= S_DEAD;
343 inode->i_ctime = CURRENT_TIME;
344 mark_inode_dirty(inode);
346 return res;
349 int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode)
351 struct inode *inode;
352 int res;
354 inode = hfsplus_new_inode(dir->i_sb, S_IFDIR | mode);
355 if (!inode)
356 return -ENOSPC;
358 res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
359 if (res) {
360 inode->i_nlink = 0;
361 hfsplus_delete_inode(inode);
362 iput(inode);
363 return res;
365 hfsplus_instantiate(dentry, inode, inode->i_ino);
366 mark_inode_dirty(inode);
367 return 0;
370 int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
372 struct inode *inode;
373 int res;
375 inode = dentry->d_inode;
376 if (inode->i_size != 2)
377 return -ENOTEMPTY;
378 res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
379 if (res)
380 return res;
381 inode->i_nlink = 0;
382 inode->i_ctime = CURRENT_TIME;
383 hfsplus_delete_inode(inode);
384 mark_inode_dirty(inode);
385 return 0;
388 int hfsplus_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
390 struct super_block *sb;
391 struct inode *inode;
392 int res;
394 sb = dir->i_sb;
395 inode = hfsplus_new_inode(sb, S_IFLNK | S_IRWXUGO);
396 if (!inode)
397 return -ENOSPC;
399 res = page_symlink(inode, symname, strlen(symname) + 1);
400 if (res) {
401 inode->i_nlink = 0;
402 hfsplus_delete_inode(inode);
403 iput(inode);
404 return res;
407 mark_inode_dirty(inode);
408 res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
410 if (!res) {
411 hfsplus_instantiate(dentry, inode, inode->i_ino);
412 mark_inode_dirty(inode);
415 return res;
418 int hfsplus_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
420 struct super_block *sb;
421 struct inode *inode;
422 int res;
424 sb = dir->i_sb;
425 inode = hfsplus_new_inode(sb, mode);
426 if (!inode)
427 return -ENOSPC;
429 res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
430 if (res) {
431 inode->i_nlink = 0;
432 hfsplus_delete_inode(inode);
433 iput(inode);
434 return res;
436 init_special_inode(inode, mode, rdev);
437 hfsplus_instantiate(dentry, inode, inode->i_ino);
438 mark_inode_dirty(inode);
440 return 0;
443 int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
444 struct inode *new_dir, struct dentry *new_dentry)
446 int res;
448 /* Unlink destination if it already exists */
449 if (new_dentry->d_inode) {
450 res = hfsplus_unlink(new_dir, new_dentry);
451 if (res)
452 return res;
455 res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
456 old_dir, &old_dentry->d_name,
457 new_dir, &new_dentry->d_name);
458 if (!res)
459 new_dentry->d_fsdata = old_dentry->d_fsdata;
460 return res;
463 struct inode_operations hfsplus_dir_inode_operations = {
464 .lookup = hfsplus_lookup,
465 .create = hfsplus_create,
466 .link = hfsplus_link,
467 .unlink = hfsplus_unlink,
468 .mkdir = hfsplus_mkdir,
469 .rmdir = hfsplus_rmdir,
470 .symlink = hfsplus_symlink,
471 .mknod = hfsplus_mknod,
472 .rename = hfsplus_rename,
475 struct file_operations hfsplus_dir_operations = {
476 .read = generic_read_dir,
477 .readdir = hfsplus_readdir,
478 .ioctl = hfsplus_ioctl,
479 .llseek = generic_file_llseek,
480 .release = hfsplus_dir_release,