patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / fs / hfsplus / dir.c
1 /*
2  *  linux/fs/hfsplus/dir.c
3  *
4  * Copyright (C) 2001
5  * Brad Boyer (flar@allandria.com)
6  * (C) 2003 Ardis Technologies <roman@ardistech.com>
7  *
8  * Handling of directories
9  */
10
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>
17
18 #include "hfsplus_fs.h"
19 #include "hfsplus_raw.h"
20
21 static inline void hfsplus_instantiate(struct dentry *dentry,
22                                        struct inode *inode, u32 cnid)
23 {
24         dentry->d_fsdata = (void *)(unsigned long)cnid;
25         d_instantiate(dentry, inode);
26 }
27
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)
31 {
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;
39
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;
52                 }
53                 goto fail;
54         }
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;
60                 }
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;
67                 }
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];
73
74                         if (dentry->d_fsdata) {
75                                 err = -ENOENT;
76                                 inode = NULL;
77                                 goto out;
78                         }
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;
91         }
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);
104 }
105
106 static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
107 {
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;
116
117         if (filp->f_pos >= inode->i_size)
118                 return 0;
119
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;
125
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;
139                 }
140                 if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
141                         printk("HFS+-fs: truncated catalog thread\n");
142                         err = -EIO;
143                         goto out;
144                 }
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;
156         }
157
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;
163                 }
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;
175                         }
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;
187                         }
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;
195                 }
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;
203         }
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;
210                 }
211                 filp->private_data = rd;
212                 rd->file = filp;
213                 list_add(&rd->list, &HFSPLUS_I(inode).open_dir_list);
214         }
215         memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
216 out:
217         hfs_find_exit(&fd);
218         return err;
219 }
220
221 static int hfsplus_dir_release(struct inode *inode, struct file *file)
222 {
223         struct hfsplus_readdir_data *rd = file->private_data;
224         if (rd) {
225                 list_del(&rd->list);
226                 kfree(rd);
227         }
228         return 0;
229 }
230
231 int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode,
232                    struct nameidata *nd)
233 {
234         struct inode *inode;
235         int res;
236
237         inode = hfsplus_new_inode(dir->i_sb, mode);
238         if (!inode)
239                 return -ENOSPC;
240
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;
247         }
248         hfsplus_instantiate(dentry, inode, inode->i_ino);
249         mark_inode_dirty(inode);
250         return 0;
251 }
252
253 int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, struct dentry *dst_dentry)
254 {
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;
262
263         if (HFSPLUS_IS_RSRC(inode))
264                 return -EPERM;
265
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;
279                 }
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++;
288         }
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;
293
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;
301
302         return 0;
303 }
304
305 int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
306 {
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;
313
314         if (HFSPLUS_IS_RSRC(inode))
315                 return -EPERM;
316
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;
328         }
329         res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
330         if (res)
331                 return res;
332
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;
342         }
343         inode->i_ctime = CURRENT_TIME;
344         mark_inode_dirty(inode);
345
346         return res;
347 }
348
349 int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode)
350 {
351         struct inode *inode;
352         int res;
353
354         inode = hfsplus_new_inode(dir->i_sb, S_IFDIR | mode);
355         if (!inode)
356                 return -ENOSPC;
357
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;
364         }
365         hfsplus_instantiate(dentry, inode, inode->i_ino);
366         mark_inode_dirty(inode);
367         return 0;
368 }
369
370 int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
371 {
372         struct inode *inode;
373         int res;
374
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;
386 }
387
388 int hfsplus_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
389 {
390         struct super_block *sb;
391         struct inode *inode;
392         int res;
393
394         sb = dir->i_sb;
395         inode = hfsplus_new_inode(sb, S_IFLNK | S_IRWXUGO);
396         if (!inode)
397                 return -ENOSPC;
398
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;
405         }
406
407         mark_inode_dirty(inode);
408         res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
409
410         if (!res) {
411                 hfsplus_instantiate(dentry, inode, inode->i_ino);
412                 mark_inode_dirty(inode);
413         }
414
415         return res;
416 }
417
418 int hfsplus_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
419 {
420         struct super_block *sb;
421         struct inode *inode;
422         int res;
423
424         sb = dir->i_sb;
425         inode = hfsplus_new_inode(sb, mode);
426         if (!inode)
427                 return -ENOSPC;
428
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;
435         }
436         init_special_inode(inode, mode, rdev);
437         hfsplus_instantiate(dentry, inode, inode->i_ino);
438         mark_inode_dirty(inode);
439
440         return 0;
441 }
442
443 int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
444                    struct inode *new_dir, struct dentry *new_dentry)
445 {
446         int res;
447
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;
453         }
454
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;
461 }
462
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,
473 };
474
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,
481 };