more changes on original files
[linux-2.4.git] / fs / hfs / dir_dbl.c
1 /*
2  * linux/fs/hfs/dir_dbl.c
3  *
4  * Copyright (C) 1995-1997  Paul H. Hargrove
5  * This file may be distributed under the terms of the GNU General Public License.
6  *
7  * This file contains the inode_operations and file_operations
8  * structures for HFS directories.
9  *
10  * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
11  *
12  * "XXX" in a comment is a note to myself to consider changing something.
13  *
14  * In function preconditions the term "valid" applied to a pointer to
15  * a structure means that the pointer is non-NULL and the structure it
16  * points to has all fields initialized to consistent values.
17  */
18
19 #include "hfs.h"
20 #include <linux/hfs_fs_sb.h>
21 #include <linux/hfs_fs_i.h>
22 #include <linux/hfs_fs.h>
23
24 /*================ Forward declarations ================*/
25
26 static struct dentry *dbl_lookup(struct inode *, struct dentry *);
27 static int dbl_readdir(struct file *, void *, filldir_t);
28 static int dbl_create(struct inode *, struct dentry *, int);
29 static int dbl_mkdir(struct inode *, struct dentry *, int);
30 static int dbl_unlink(struct inode *, struct dentry *);
31 static int dbl_rmdir(struct inode *, struct dentry *);
32 static int dbl_rename(struct inode *, struct dentry *,
33                       struct inode *, struct dentry *);
34
35 /*================ Global variables ================*/
36
37 #define DOT_LEN                 1
38 #define DOT_DOT_LEN             2
39 #define ROOTINFO_LEN            8
40 #define PCNT_ROOTINFO_LEN       9
41
42 const struct hfs_name hfs_dbl_reserved1[] = {
43         {DOT_LEN,               "."},
44         {DOT_DOT_LEN,           ".."},
45         {0,                     ""},
46 };
47
48 const struct hfs_name hfs_dbl_reserved2[] = {
49         {ROOTINFO_LEN,          "RootInfo"},
50         {PCNT_ROOTINFO_LEN,     "%RootInfo"},
51         {0,                     ""},
52 };
53
54 #define DOT             (&hfs_dbl_reserved1[0])
55 #define DOT_DOT         (&hfs_dbl_reserved1[1])
56 #define ROOTINFO        (&hfs_dbl_reserved2[0])
57 #define PCNT_ROOTINFO   (&hfs_dbl_reserved2[1])
58
59 struct file_operations hfs_dbl_dir_operations = {
60         read:           generic_read_dir,
61         readdir:        dbl_readdir,
62         fsync:          file_fsync,
63 };
64
65 struct inode_operations hfs_dbl_dir_inode_operations = {
66         create:         dbl_create,
67         lookup:         dbl_lookup,
68         unlink:         dbl_unlink,
69         mkdir:          dbl_mkdir,
70         rmdir:          dbl_rmdir,
71         rename:         dbl_rename,
72         setattr:        hfs_notify_change,
73 };
74
75
76 /*================ File-local functions ================*/
77
78 /*
79  * is_hdr()
80  */
81 static int is_hdr(struct inode *dir, const char *name, int len)
82 {
83         int retval = 0;
84
85         if (name[0] == '%') {
86                 struct hfs_cat_entry *entry = HFS_I(dir)->entry;
87                 struct hfs_cat_entry *victim;
88                 struct hfs_name cname;
89                 struct hfs_cat_key key;
90
91                 hfs_nameout(dir, &cname, name+1, len-1);
92                 hfs_cat_build_key(entry->cnid, &cname, &key);
93                 if ((victim = hfs_cat_get(entry->mdb, &key))) {
94                         hfs_cat_put(victim);
95                         retval = 1;
96                 }
97         }
98         return retval;
99 }
100
101 /*
102  * dbl_lookup()
103  *
104  * This is the lookup() entry in the inode_operations structure for
105  * HFS directories in the AppleDouble scheme.  The purpose is to
106  * generate the inode corresponding to an entry in a directory, given
107  * the inode for the directory and the name (and its length) of the
108  * entry.
109  */
110 static struct dentry *dbl_lookup(struct inode * dir, struct dentry *dentry)
111 {
112         struct hfs_name cname;
113         struct hfs_cat_entry *entry;
114         struct hfs_cat_key key;
115         struct inode *inode = NULL;
116
117         dentry->d_op = &hfs_dentry_operations;
118         entry = HFS_I(dir)->entry;
119         
120         /* Perform name-mangling */
121         hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len);
122  
123         /* no need to check for "."  or ".." */
124
125         /* Check for "%RootInfo" if in the root directory. */
126         if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
127             hfs_streq(cname.Name, cname.Len, 
128                       PCNT_ROOTINFO->Name, PCNT_ROOTINFO_LEN)) {
129                 ++entry->count; /* __hfs_iget() eats one */
130                 inode = hfs_iget(entry, HFS_DBL_HDR, dentry);
131                 goto done;
132         }
133
134         /* Do an hfs_iget() on the mangled name. */
135         hfs_cat_build_key(entry->cnid, &cname, &key);
136         inode = hfs_iget(hfs_cat_get(entry->mdb, &key), HFS_DBL_NORM, dentry);
137
138         /* Try as a header if not found and first character is '%' */
139         if (!inode && (dentry->d_name.name[0] == '%')) {
140                 hfs_nameout(dir, &cname, dentry->d_name.name+1,
141                             dentry->d_name.len-1);
142                 hfs_cat_build_key(entry->cnid, &cname, &key);
143                 inode = hfs_iget(hfs_cat_get(entry->mdb, &key),
144                                  HFS_DBL_HDR, dentry);
145         }
146         
147 done:
148         d_add(dentry, inode);
149         return NULL;
150 }
151
152 /*
153  * dbl_readdir()
154  *
155  * This is the readdir() entry in the file_operations structure for
156  * HFS directories in the AppleDouble scheme.  The purpose is to
157  * enumerate the entries in a directory, given the inode of the
158  * directory and a (struct file *), the 'f_pos' field of which
159  * indicates the location in the directory.  The (struct file *) is
160  * updated so that the next call with the same 'dir' and 'filp'
161  * arguments will produce the next directory entry.  The entries are
162  * returned in 'dirent', which is "filled-in" by calling filldir().
163  * This allows the same readdir() function be used for different
164  * formats.  We try to read in as many entries as we can before
165  * filldir() refuses to take any more.
166  *
167  * XXX: In the future it may be a good idea to consider not generating
168  * metadata files for covered directories since the data doesn't
169  * correspond to the mounted directory.  However this requires an
170  * iget() for every directory which could be considered an excessive
171  * amount of overhead.  Since the inode for a mount point is always
172  * in-core this is another argument for a call to get an inode if it
173  * is in-core or NULL if it is not.
174  */
175 static int dbl_readdir(struct file * filp,
176                        void * dirent, filldir_t filldir)
177 {
178         struct hfs_brec brec;
179         struct hfs_cat_entry *entry;
180         struct inode *dir = filp->f_dentry->d_inode;
181
182         entry = HFS_I(dir)->entry;
183
184         if (filp->f_pos == 0) {
185                 /* Entry 0 is for "." */
186                 if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino,
187                             DT_DIR)) {
188                         return 0;
189                 }
190                 filp->f_pos = 1;
191         }
192
193         if (filp->f_pos == 1) {
194                 /* Entry 1 is for ".." */
195                 if (filldir(dirent, DOT_DOT->Name, DOT_DOT_LEN, 1,
196                             hfs_get_hl(entry->key.ParID), DT_DIR)) {
197                         return 0;
198                 }
199                 filp->f_pos = 2;
200         }
201
202         if (filp->f_pos < (dir->i_size - 1)) {
203                 hfs_u32 cnid;
204                 hfs_u8 type;
205
206                 if (hfs_cat_open(entry, &brec) ||
207                     hfs_cat_next(entry, &brec, (filp->f_pos - 1) >> 1,
208                                  &cnid, &type)) {
209                         return 0;
210                 }
211
212                 while (filp->f_pos < (dir->i_size - 1)) {
213                         unsigned char tmp_name[HFS_NAMEMAX + 1];
214                         ino_t ino;
215                         int is_hdr = (filp->f_pos & 1);
216                         unsigned int len;
217
218                         if (is_hdr) {
219                                 ino = ntohl(cnid) | HFS_DBL_HDR;
220                                 tmp_name[0] = '%';
221                                 len = 1 + hfs_namein(dir, tmp_name + 1,
222                                     &((struct hfs_cat_key *)brec.key)->CName);
223                         } else {
224                                 if (hfs_cat_next(entry, &brec, 1,
225                                                         &cnid, &type)) {
226                                         return 0;
227                                 }
228                                 ino = ntohl(cnid);
229                                 len = hfs_namein(dir, tmp_name,
230                                     &((struct hfs_cat_key *)brec.key)->CName);
231                         }
232
233                         if (filldir(dirent, tmp_name, len, filp->f_pos, ino,
234                                     DT_UNKNOWN)) {
235                                 hfs_cat_close(entry, &brec);
236                                 return 0;
237                         }
238                         ++filp->f_pos;
239                 }
240                 hfs_cat_close(entry, &brec);
241         }
242
243         if (filp->f_pos == (dir->i_size - 1)) {
244                 if (entry->cnid == htonl(HFS_ROOT_CNID)) {
245                         /* In root dir last entry is for "%RootInfo" */
246                         if (filldir(dirent, PCNT_ROOTINFO->Name,
247                                     PCNT_ROOTINFO_LEN, filp->f_pos,
248                                     ntohl(entry->cnid) | HFS_DBL_HDR,
249                                     DT_UNKNOWN)) {
250                                 return 0;
251                         }
252                 }
253                 ++filp->f_pos;
254         }
255
256         return 0;
257 }
258
259 /*
260  * dbl_create()
261  *
262  * This is the create() entry in the inode_operations structure for
263  * AppleDouble directories.  The purpose is to create a new file in
264  * a directory and return a corresponding inode, given the inode for
265  * the directory and the name (and its length) of the new file.
266  */
267 static int dbl_create(struct inode * dir, struct dentry *dentry,
268                       int mode)
269 {
270         int error;
271
272         if (is_hdr(dir, dentry->d_name.name, dentry->d_name.len)) {
273                 error = -EEXIST;
274         } else {
275                 error = hfs_create(dir, dentry, mode);
276         }
277         return error;
278 }
279
280 /*
281  * dbl_mkdir()
282  *
283  * This is the mkdir() entry in the inode_operations structure for
284  * AppleDouble directories.  The purpose is to create a new directory
285  * in a directory, given the inode for the parent directory and the
286  * name (and its length) of the new directory.
287  */
288 static int dbl_mkdir(struct inode * parent, struct dentry *dentry,
289                      int mode)
290 {
291         int error;
292
293         if (is_hdr(parent, dentry->d_name.name, dentry->d_name.len)) {
294                 error = -EEXIST;
295         } else {
296                 error = hfs_mkdir(parent, dentry, mode);
297         }
298         return error;
299 }
300
301 /*
302  * dbl_unlink()
303  *
304  * This is the unlink() entry in the inode_operations structure for
305  * AppleDouble directories.  The purpose is to delete an existing
306  * file, given the inode for the parent directory and the name
307  * (and its length) of the existing file.
308  */
309 static int dbl_unlink(struct inode * dir, struct dentry *dentry)
310 {
311         int error;
312
313         error = hfs_unlink(dir, dentry);
314         if ((error == -ENOENT) && is_hdr(dir, dentry->d_name.name,
315                                          dentry->d_name.len)) {
316                 error = -EPERM;
317         }
318         return error;
319 }
320
321 /*
322  * dbl_rmdir()
323  *
324  * This is the rmdir() entry in the inode_operations structure for
325  * AppleDouble directories.  The purpose is to delete an existing
326  * directory, given the inode for the parent directory and the name
327  * (and its length) of the existing directory.
328  */
329 static int dbl_rmdir(struct inode * parent, struct dentry *dentry)
330 {
331         int error;
332
333         error = hfs_rmdir(parent, dentry);
334         if ((error == -ENOENT) && is_hdr(parent, dentry->d_name.name,
335                                          dentry->d_name.len)) {
336                 error = -ENOTDIR;
337         }
338         return error;
339 }
340
341 /*
342  * dbl_rename()
343  *
344  * This is the rename() entry in the inode_operations structure for
345  * AppleDouble directories.  The purpose is to rename an existing
346  * file or directory, given the inode for the current directory and
347  * the name (and its length) of the existing file/directory and the
348  * inode for the new directory and the name (and its length) of the
349  * new file/directory.
350  * 
351  * XXX: how do we handle must_be_dir?
352  */
353 static int dbl_rename(struct inode *old_dir, struct dentry *old_dentry,
354                       struct inode *new_dir, struct dentry *new_dentry)
355 {
356         int error;
357
358         if (is_hdr(new_dir, new_dentry->d_name.name,
359                    new_dentry->d_name.len)) {
360                 error = -EPERM;
361         } else {
362                 error = hfs_rename(old_dir, old_dentry,
363                                    new_dir, new_dentry);
364                 if ((error == -ENOENT) /*&& !must_be_dir*/ &&
365                     is_hdr(old_dir, old_dentry->d_name.name,
366                            old_dentry->d_name.len)) {
367                         error = -EPERM;
368                 }
369         }
370         return error;
371 }
372
373
374 /* due to the dcache caching negative dentries for non-existent files,
375  * we need to drop those entries when a file silently gets created.
376  * as far as i can tell, the calls that need to do this are the file
377  * related calls (create, rename, and mknod). the directory calls
378  * should be immune. the relevant calls in dir.c call drop_dentry 
379  * upon successful completion. */
380 void hfs_dbl_drop_dentry(struct dentry *dentry, const ino_t type)
381 {
382   unsigned char tmp_name[HFS_NAMEMAX + 1];
383   struct dentry *de = NULL;
384
385   switch (type) {
386   case HFS_DBL_HDR:
387    /* given %name, look for name. i don't think this happens. */
388    de = hfs_lookup_dentry(dentry->d_parent,
389                           dentry->d_name.name + 1, dentry->d_name.len - 1);
390     break;
391   case HFS_DBL_DATA:
392     /* given name, look for %name */
393     tmp_name[0] = '%';
394     strncpy(tmp_name + 1, dentry->d_name.name, HFS_NAMELEN - 1);
395     de = hfs_lookup_dentry(dentry->d_parent, 
396                            tmp_name, dentry->d_name.len + 1);
397   }
398
399   if (de) {
400     if (!de->d_inode)
401       d_drop(de);
402     dput(de);
403   }
404 }