more changes on original files
[linux-2.4.git] / fs / hfs / dir.c
1 /*
2  * linux/fs/hfs/dir.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 directory-related functions independent of which
8  * scheme is being used to represent forks.
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 /*================ File-local functions ================*/
25
26 /*
27  * build_key()
28  *
29  * Build a key for a file by the given name in the given directory.
30  * If the name matches one of the reserved names returns 1 otherwise 0.
31  */
32 static int build_key(struct hfs_cat_key *key, struct inode *dir,
33                      const char *name, int len)
34 {
35         struct hfs_name cname;
36         const struct hfs_name *reserved;
37
38         /* mangle the name */
39         hfs_nameout(dir, &cname, name, len);
40
41         /* check against reserved names */
42         reserved = HFS_SB(dir->i_sb)->s_reserved1;
43         while (reserved->Len) {
44                 if (hfs_streq(reserved->Name, reserved->Len, 
45                               cname.Name, cname.Len)) {
46                         return 1;
47                 }
48                 ++reserved;
49         }
50
51         /* check against the names reserved only in the root directory */
52         if (HFS_I(dir)->entry->cnid == htonl(HFS_ROOT_CNID)) {
53                 reserved = HFS_SB(dir->i_sb)->s_reserved2;
54                 while (reserved->Len) {
55                         if (hfs_streq(reserved->Name, reserved->Len,
56                                       cname.Name, cname.Len)) {
57                                 return 1;
58                         }
59                         ++reserved;
60                 }
61         }
62
63         /* build the key */
64         hfs_cat_build_key(HFS_I(dir)->entry->cnid, &cname, key);
65
66         return 0;
67 }
68
69 /*
70  * update_dirs_plus()
71  *
72  * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
73  * 'i_version' of the inodes associated with a directory that has
74  * had a file ('is_dir'==0) or directory ('is_dir'!=0) added to it.
75  */
76 static inline void update_dirs_plus(struct hfs_cat_entry *dir, int is_dir)
77 {
78         int i;
79
80         for (i = 0; i < 4; ++i) {
81                 struct dentry *de = dir->sys_entry[i];
82                 if (de) {
83                         struct inode *tmp = de->d_inode;
84                         if (S_ISDIR(tmp->i_mode)) {
85                                 if (is_dir &&
86                                     (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) {
87                                         /* In "normal" directory only */
88                                         ++(tmp->i_nlink);
89                                 }
90                                 tmp->i_size += HFS_I(tmp)->dir_size;
91                                 tmp->i_version = ++event;
92                         }
93                         tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
94                         mark_inode_dirty(tmp);
95                 }
96         }
97 }
98
99 /*
100  * update_dirs_minus()
101  *
102  * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
103  * 'i_version' of the inodes associated with a directory that has
104  * had a file ('is_dir'==0) or directory ('is_dir'!=0) removed.
105  */
106 static inline void update_dirs_minus(struct hfs_cat_entry *dir, int is_dir)
107 {
108         int i;
109
110         for (i = 0; i < 4; ++i) {
111                 struct dentry *de = dir->sys_entry[i];
112                 if (de) {
113                         struct inode *tmp = de->d_inode;
114                         if (S_ISDIR(tmp->i_mode)) {
115                                 if (is_dir &&
116                                     (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) {
117                                         /* In "normal" directory only */
118                                         --(tmp->i_nlink);
119                                 }
120                                 tmp->i_size -= HFS_I(tmp)->dir_size;
121                                 tmp->i_version = ++event;
122                         }
123                         tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
124                         mark_inode_dirty(tmp);
125                 }
126         }
127 }
128
129 /*
130  * mark_inodes_deleted()
131  *
132  * Update inodes associated with a deleted entry to reflect its deletion.
133  * Well, we really just drop the dentry.
134  *
135  * XXX: we should be using delete_inode for some of this stuff.
136  */
137 static inline void mark_inodes_deleted(struct hfs_cat_entry *entry, 
138                                        struct dentry *dentry)
139 {
140         struct dentry *de;
141         struct inode *tmp;
142         int i;
143
144         for (i = 0; i < 4; ++i) {
145                 if ((de = entry->sys_entry[i]) && (dentry != de)) {
146                       dget(de);
147                       tmp = de->d_inode;
148                       tmp->i_nlink = 0;
149                       tmp->i_ctime = CURRENT_TIME;
150                       mark_inode_dirty(tmp);
151                       d_delete(de);
152                       dput(de);
153                 }
154         }
155 }
156
157 /*================ Global functions ================*/
158
159 /*
160  * hfs_create()
161  *
162  * This is the create() entry in the inode_operations structure for
163  * regular HFS directories.  The purpose is to create a new file in
164  * a directory and return a corresponding inode, given the inode for
165  * the directory and the name (and its length) of the new file.
166  */
167 int hfs_create(struct inode * dir, struct dentry *dentry, int mode)
168 {
169         struct hfs_cat_entry *entry = HFS_I(dir)->entry;
170         struct hfs_cat_entry *new;
171         struct hfs_cat_key key;
172         struct inode *inode;
173         int error;
174
175         /* build the key, checking against reserved names */
176         if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len)) 
177                 return -EEXIST;
178
179         if ((error = hfs_cat_create(entry, &key, 
180                                (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK,
181                                HFS_SB(dir->i_sb)->s_type,
182                                HFS_SB(dir->i_sb)->s_creator, &new)))
183                 return error;
184
185         /* create an inode for the new file. back out if we run
186          * into trouble. */
187         new->count++; /* hfs_iget() eats one */
188         if (!(inode = hfs_iget(new, HFS_I(dir)->file_type, dentry))) {
189                 hfs_cat_delete(entry, new, 1);
190                 hfs_cat_put(new);
191                 return -EIO;
192         }
193
194         hfs_cat_put(new);
195         update_dirs_plus(entry, 0);
196         /* toss any relevant negative dentries */
197         if (HFS_I(dir)->d_drop_op)
198                 HFS_I(dir)->d_drop_op(dentry, HFS_I(dir)->file_type);
199         mark_inode_dirty(inode);
200         d_instantiate(dentry, inode);
201         return 0;
202 }
203
204 /*
205  * hfs_mkdir()
206  *
207  * This is the mkdir() entry in the inode_operations structure for
208  * regular HFS directories.  The purpose is to create a new directory
209  * in a directory, given the inode for the parent directory and the
210  * name (and its length) of the new directory.
211  */
212 int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode)
213 {
214         struct hfs_cat_entry *entry = HFS_I(parent)->entry;
215         struct hfs_cat_entry *new;
216         struct hfs_cat_key key;
217         struct inode *inode;
218         int error;
219
220         /* build the key, checking against reserved names */
221         if (build_key(&key, parent, dentry->d_name.name, 
222                       dentry->d_name.len)) 
223                 return -EEXIST;
224
225         /* try to create the directory */
226         if ((error = hfs_cat_mkdir(entry, &key, &new)))
227                 return error;
228
229         /* back out if we run into trouble */
230         new->count++; /* hfs_iget eats one */
231         if (!(inode = hfs_iget(new, HFS_I(parent)->file_type, dentry))) {
232                 hfs_cat_delete(entry, new, 1);
233                 hfs_cat_put(new);
234                 return -EIO;
235         }
236
237         hfs_cat_put(new);
238         update_dirs_plus(entry, 1);
239         mark_inode_dirty(inode);
240         d_instantiate(dentry, inode);
241         return 0;
242 }
243
244 /*
245  * hfs_unlink()
246  *
247  * This is the unlink() entry in the inode_operations structure for
248  * regular HFS directories.  The purpose is to delete an existing
249  * file, given the inode for the parent directory and the name
250  * (and its length) of the existing file.
251  */
252 int hfs_unlink(struct inode * dir, struct dentry *dentry)
253 {
254         struct hfs_cat_entry *entry = HFS_I(dir)->entry;
255         struct hfs_cat_entry *victim = NULL;
256         struct hfs_cat_key key;
257         int error;
258
259         if (build_key(&key, dir, dentry->d_name.name,
260                       dentry->d_name.len)) 
261                 return -EPERM;
262
263         if (!(victim = hfs_cat_get(entry->mdb, &key))) 
264                 return -ENOENT;
265
266         error = -EPERM;
267         if (victim->type != HFS_CDR_FIL)
268                 goto hfs_unlink_put;
269
270         if (!(error = hfs_cat_delete(entry, victim, 1))) {
271                 struct inode *inode = dentry->d_inode;
272
273                 mark_inodes_deleted(victim, dentry);
274                 inode->i_nlink--; 
275                 inode->i_ctime = CURRENT_TIME;
276                 mark_inode_dirty(inode);
277                 update_dirs_minus(entry, 0);
278         }
279
280 hfs_unlink_put:
281         hfs_cat_put(victim);    /* Note that hfs_cat_put(NULL) is safe. */
282         return error;
283 }
284
285 /*
286  * hfs_rmdir()
287  *
288  * This is the rmdir() entry in the inode_operations structure for
289  * regular HFS directories.  The purpose is to delete an existing
290  * directory, given the inode for the parent directory and the name
291  * (and its length) of the existing directory.
292  */
293 int hfs_rmdir(struct inode * parent, struct dentry *dentry)
294 {
295         struct hfs_cat_entry *entry = HFS_I(parent)->entry;
296         struct hfs_cat_entry *victim = NULL;
297         struct inode *inode = dentry->d_inode;
298         struct hfs_cat_key key;
299         int error;
300
301         if (build_key(&key, parent, dentry->d_name.name,
302                       dentry->d_name.len))
303                 return -EPERM;
304
305         if (!(victim = hfs_cat_get(entry->mdb, &key)))
306                 return -ENOENT;
307
308         error = -ENOTDIR;
309         if (victim->type != HFS_CDR_DIR) 
310                 goto hfs_rmdir_put;
311
312         error = -EBUSY;
313         if (!d_unhashed(dentry))
314                 goto hfs_rmdir_put;
315
316         /* we only have to worry about 2 and 3 for mount points */
317         if (victim->sys_entry[2] && d_mountpoint(victim->sys_entry[2]))
318                 goto hfs_rmdir_put;
319         if (victim->sys_entry[3] && d_mountpoint(victim->sys_entry[3])) 
320                 goto hfs_rmdir_put;
321
322         
323         if ((error = hfs_cat_delete(entry, victim, 1)))
324                 goto hfs_rmdir_put;
325
326         mark_inodes_deleted(victim, dentry);
327         inode->i_nlink = 0;
328         inode->i_ctime = CURRENT_TIME;
329         mark_inode_dirty(inode);
330         update_dirs_minus(entry, 1);
331          
332 hfs_rmdir_put:
333         hfs_cat_put(victim);    /* Note that hfs_cat_put(NULL) is safe. */
334         return error;
335 }
336
337 /*
338  * hfs_rename()
339  *
340  * This is the rename() entry in the inode_operations structure for
341  * regular HFS directories.  The purpose is to rename an existing
342  * file or directory, given the inode for the current directory and
343  * the name (and its length) of the existing file/directory and the
344  * inode for the new directory and the name (and its length) of the
345  * new file/directory.
346  * XXX: how do you handle must_be dir?
347  */
348 int hfs_rename(struct inode *old_dir, struct dentry *old_dentry,
349                struct inode *new_dir, struct dentry *new_dentry)
350 {
351         struct hfs_cat_entry *old_parent = HFS_I(old_dir)->entry;
352         struct hfs_cat_entry *new_parent = HFS_I(new_dir)->entry;
353         struct hfs_cat_entry *victim = NULL;
354         struct hfs_cat_entry *deleted;
355         struct hfs_cat_key key;
356         int error;
357
358         if (build_key(&key, old_dir, old_dentry->d_name.name,
359                       old_dentry->d_name.len) ||
360             (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino))) 
361                 return -EPERM;
362
363         if (!(victim = hfs_cat_get(old_parent->mdb, &key))) 
364                 return -ENOENT;
365
366         error = -EPERM;
367         if (build_key(&key, new_dir, new_dentry->d_name.name,
368                              new_dentry->d_name.len)) 
369                 goto hfs_rename_put;
370
371         if (!(error = hfs_cat_move(old_parent, new_parent,
372                                    victim, &key, &deleted))) {
373                 int is_dir = (victim->type == HFS_CDR_DIR);
374                 
375                 /* drop the old dentries */
376                 mark_inodes_deleted(victim, old_dentry);
377                 update_dirs_minus(old_parent, is_dir);
378                 if (deleted) {
379                         mark_inodes_deleted(deleted, new_dentry);
380                         hfs_cat_put(deleted);
381                 } else {
382                         /* no existing inodes. just drop negative dentries */
383                         if (HFS_I(new_dir)->d_drop_op) 
384                                 HFS_I(new_dir)->d_drop_op(new_dentry, 
385                                           HFS_I(new_dir)->file_type);
386                         update_dirs_plus(new_parent, is_dir);
387                 }
388         
389         }
390
391 hfs_rename_put:
392         hfs_cat_put(victim);    /* Note that hfs_cat_put(NULL) is safe. */
393         return error;
394 }