Import 2.3.1pre2
[davej-history.git] / fs / hfs / dir_nat.c
blobf9f131a17f2ffda99687458a32d712c757877439
1 /*
2 * linux/fs/hfs/dir_nat.c
4 * Copyright (C) 1995-1997 Paul H. Hargrove
5 * This file may be distributed under the terms of the GNU Public License.
7 * This file contains the inode_operations and file_operations
8 * structures for HFS directories.
10 * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
12 * The source code distributions of Netatalk, versions 1.3.3b2 and
13 * 1.4b2, were used as a specification of the location and format of
14 * files used by Netatalk's afpd. No code from Netatalk appears in
15 * hfs_fs. hfs_fs is not a work ``derived'' from Netatalk in the
16 * sense of intellectual property law.
18 * "XXX" in a comment is a note to myself to consider changing something.
20 * In function preconditions the term "valid" applied to a pointer to
21 * a structure means that the pointer is non-NULL and the structure it
22 * points to has all fields initialized to consistent values.
25 #include "hfs.h"
26 #include <linux/hfs_fs_sb.h>
27 #include <linux/hfs_fs_i.h>
28 #include <linux/hfs_fs.h>
30 /*================ Forward declarations ================*/
32 static struct dentry *nat_lookup(struct inode *, struct dentry *);
33 static int nat_readdir(struct file *, void *, filldir_t);
34 static int nat_rmdir(struct inode *, struct dentry *);
35 static int nat_hdr_unlink(struct inode *, struct dentry *);
36 static int nat_hdr_rename(struct inode *, struct dentry *,
37 struct inode *, struct dentry *);
39 /*================ Global variables ================*/
41 #define DOT_LEN 1
42 #define DOT_DOT_LEN 2
43 #define DOT_APPLEDOUBLE_LEN 12
44 #define DOT_PARENT_LEN 7
45 #define ROOTINFO_LEN 8
47 const struct hfs_name hfs_nat_reserved1[] = {
48 {DOT_LEN, "."},
49 {DOT_DOT_LEN, ".."},
50 {DOT_APPLEDOUBLE_LEN, ".AppleDouble"},
51 {DOT_PARENT_LEN, ".Parent"},
52 {0, ""},
55 const struct hfs_name hfs_nat_reserved2[] = {
56 {ROOTINFO_LEN, "RootInfo"},
59 #define DOT (&hfs_nat_reserved1[0])
60 #define DOT_DOT (&hfs_nat_reserved1[1])
61 #define DOT_APPLEDOUBLE (&hfs_nat_reserved1[2])
62 #define DOT_PARENT (&hfs_nat_reserved1[3])
63 #define ROOTINFO (&hfs_nat_reserved2[0])
65 static struct file_operations hfs_nat_dir_operations = {
66 NULL, /* lseek - default */
67 hfs_dir_read, /* read - invalid */
68 NULL, /* write - bad */
69 nat_readdir, /* readdir */
70 NULL, /* select - default */
71 NULL, /* ioctl - default */
72 NULL, /* mmap - none */
73 NULL, /* no special open code */
74 NULL, /* flush */
75 NULL, /* no special release code */
76 file_fsync, /* fsync - default */
77 NULL, /* fasync - default */
78 NULL, /* check_media_change - none */
79 NULL, /* revalidate - none */
80 NULL /* lock - none */
83 struct inode_operations hfs_nat_ndir_inode_operations = {
84 &hfs_nat_dir_operations,/* default directory file-ops */
85 hfs_create, /* create */
86 nat_lookup, /* lookup */
87 NULL, /* link */
88 hfs_unlink, /* unlink */
89 NULL, /* symlink */
90 hfs_mkdir, /* mkdir */
91 nat_rmdir, /* rmdir */
92 NULL, /* mknod */
93 hfs_rename, /* rename */
94 NULL, /* readlink */
95 NULL, /* follow_link */
96 NULL, /* readpage */
97 NULL, /* writepage */
98 NULL, /* bmap */
99 NULL, /* truncate */
100 NULL, /* permission */
101 NULL, /* smap */
102 NULL, /* updatepage */
103 NULL /* revalidate */
106 struct inode_operations hfs_nat_hdir_inode_operations = {
107 &hfs_nat_dir_operations,/* default directory file-ops */
108 hfs_create, /* create */
109 nat_lookup, /* lookup */
110 NULL, /* link */
111 nat_hdr_unlink, /* unlink */
112 NULL, /* symlink */
113 NULL, /* mkdir */
114 NULL, /* rmdir */
115 NULL, /* mknod */
116 nat_hdr_rename, /* rename */
117 NULL, /* readlink */
118 NULL, /* follow_link */
119 NULL, /* readpage */
120 NULL, /* writepage */
121 NULL, /* bmap */
122 NULL, /* truncate */
123 NULL, /* permission */
124 NULL, /* smap */
125 NULL, /* updatepage */
126 NULL /* revalidate */
129 /*================ File-local functions ================*/
132 * nat_lookup()
134 * This is the lookup() entry in the inode_operations structure for
135 * HFS directories in the Netatalk scheme. The purpose is to generate
136 * the inode corresponding to an entry in a directory, given the inode
137 * for the directory and the name (and its length) of the entry.
139 static struct dentry *nat_lookup(struct inode * dir, struct dentry *dentry)
141 ino_t dtype;
142 struct hfs_name cname;
143 struct hfs_cat_entry *entry;
144 struct hfs_cat_key key;
145 struct inode *inode = NULL;
147 dentry->d_op = &hfs_dentry_operations;
148 entry = HFS_I(dir)->entry;
149 dtype = HFS_ITYPE(dir->i_ino);
151 /* Perform name-mangling */
152 hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len);
154 /* no need to check for "." or ".." */
156 /* Check for ".AppleDouble" if in a normal directory,
157 and for ".Parent" in ".AppleDouble". */
158 if (dtype==HFS_NAT_NDIR) {
159 /* Check for ".AppleDouble" */
160 if (hfs_streq(cname.Name, cname.Len,
161 DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) {
162 ++entry->count; /* __hfs_iget() eats one */
163 inode = hfs_iget(entry, HFS_NAT_HDIR, dentry);
164 goto done;
166 } else if (dtype==HFS_NAT_HDIR) {
167 if (hfs_streq(cname.Name, cname.Len,
168 DOT_PARENT->Name, DOT_PARENT_LEN)) {
169 ++entry->count; /* __hfs_iget() eats one */
170 inode = hfs_iget(entry, HFS_NAT_HDR, dentry);
171 goto done;
174 if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
175 hfs_streq(cname.Name, cname.Len,
176 ROOTINFO->Name, ROOTINFO_LEN)) {
177 ++entry->count; /* __hfs_iget() eats one */
178 inode = hfs_iget(entry, HFS_NAT_HDR, dentry);
179 goto done;
183 /* Do an hfs_iget() on the mangled name. */
184 hfs_cat_build_key(entry->cnid, &cname, &key);
185 inode = hfs_iget(hfs_cat_get(entry->mdb, &key),
186 HFS_I(dir)->file_type, dentry);
188 /* Don't return a header file for a directory other than .Parent */
189 if (inode && (dtype == HFS_NAT_HDIR) &&
190 (HFS_I(inode)->entry != entry) &&
191 (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
192 iput(inode); /* this does an hfs_cat_put */
193 inode = NULL;
196 done:
197 d_add(dentry, inode);
198 return NULL;
202 * nat_readdir()
204 * This is the readdir() entry in the file_operations structure for
205 * HFS directories in the netatalk scheme. The purpose is to
206 * enumerate the entries in a directory, given the inode of the
207 * directory and a struct file which indicates the location in the
208 * directory. The struct file is updated so that the next call with
209 * the same dir and filp will produce the next directory entry. The
210 * entries are returned in dirent, which is "filled-in" by calling
211 * filldir(). This allows the same readdir() function be used for
212 * different dirent formats. We try to read in as many entries as we
213 * can before filldir() refuses to take any more.
215 * Note that the Netatalk format doesn't have the problem with
216 * metadata for covered directories that exists in the other formats,
217 * since the metadata is contained within the directory.
219 static int nat_readdir(struct file * filp,
220 void * dirent, filldir_t filldir)
222 ino_t type;
223 int skip_dirs;
224 struct hfs_brec brec;
225 struct hfs_cat_entry *entry;
226 struct inode *dir = filp->f_dentry->d_inode;
228 if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
229 return -EBADF;
232 entry = HFS_I(dir)->entry;
233 type = HFS_ITYPE(dir->i_ino);
234 skip_dirs = (type == HFS_NAT_HDIR);
236 if (filp->f_pos == 0) {
237 /* Entry 0 is for "." */
238 if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) {
239 return 0;
241 filp->f_pos = 1;
244 if (filp->f_pos == 1) {
245 /* Entry 1 is for ".." */
246 hfs_u32 cnid;
248 if (type == HFS_NAT_NDIR) {
249 cnid = hfs_get_nl(entry->key.ParID);
250 } else {
251 cnid = entry->cnid;
254 if (filldir(dirent, DOT_DOT->Name,
255 DOT_DOT_LEN, 1, ntohl(cnid))) {
256 return 0;
258 filp->f_pos = 2;
261 if (filp->f_pos < (dir->i_size - 2)) {
262 hfs_u32 cnid;
263 hfs_u8 type;
265 if (hfs_cat_open(entry, &brec) ||
266 hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) {
267 return 0;
269 while (filp->f_pos < (dir->i_size - 2)) {
270 if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) {
271 return 0;
273 if (!skip_dirs || (type != HFS_CDR_DIR)) {
274 ino_t ino;
275 unsigned int len;
276 unsigned char tmp_name[HFS_NAMEMAX];
278 ino = ntohl(cnid) | HFS_I(dir)->file_type;
279 len = hfs_namein(dir, tmp_name,
280 &((struct hfs_cat_key *)brec.key)->CName);
281 if (filldir(dirent, tmp_name, len,
282 filp->f_pos, ino)) {
283 hfs_cat_close(entry, &brec);
284 return 0;
287 ++filp->f_pos;
289 hfs_cat_close(entry, &brec);
292 if (filp->f_pos == (dir->i_size - 2)) {
293 if (type == HFS_NAT_NDIR) {
294 /* In normal dirs entry 2 is for ".AppleDouble" */
295 if (filldir(dirent, DOT_APPLEDOUBLE->Name,
296 DOT_APPLEDOUBLE_LEN, filp->f_pos,
297 ntohl(entry->cnid) | HFS_NAT_HDIR)) {
298 return 0;
300 } else if (type == HFS_NAT_HDIR) {
301 /* In .AppleDouble entry 2 is for ".Parent" */
302 if (filldir(dirent, DOT_PARENT->Name,
303 DOT_PARENT_LEN, filp->f_pos,
304 ntohl(entry->cnid) | HFS_NAT_HDR)) {
305 return 0;
308 ++filp->f_pos;
311 if (filp->f_pos == (dir->i_size - 1)) {
312 /* handle ROOT/.AppleDouble/RootInfo as the last entry. */
313 if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
314 (type == HFS_NAT_HDIR)) {
315 if (filldir(dirent, ROOTINFO->Name,
316 ROOTINFO_LEN, filp->f_pos,
317 ntohl(entry->cnid) | HFS_NAT_HDR)) {
318 return 0;
321 ++filp->f_pos;
324 return 0;
327 /* due to the dcache caching negative dentries for non-existent files,
328 * we need to drop those entries when a file silently gets created.
329 * as far as i can tell, the calls that need to do this are the file
330 * related calls (create, rename, and mknod). the directory calls
331 * should be immune. the relevant calls in dir.c call drop_dentry
332 * upon successful completion. */
333 void hfs_nat_drop_dentry(struct dentry *dentry, const ino_t type)
335 struct dentry *de;
337 switch (type) {
338 case HFS_NAT_HDR: /* given .AppleDouble/name */
339 /* look for name */
340 de = hfs_lookup_dentry(dentry->d_parent->d_parent,
341 dentry->d_name.name, dentry->d_name.len);
343 if (de) {
344 if (!de->d_inode)
345 d_drop(de);
346 dput(de);
348 break;
349 case HFS_NAT_DATA: /* given name */
350 /* look for .AppleDouble/name */
351 hfs_drop_special(dentry->d_parent, DOT_APPLEDOUBLE, dentry);
352 break;
358 * nat_rmdir()
360 * This is the rmdir() entry in the inode_operations structure for
361 * Netatalk directories. The purpose is to delete an existing
362 * directory, given the inode for the parent directory and the name
363 * (and its length) of the existing directory.
365 * We handle .AppleDouble and call hfs_rmdir() for all other cases.
367 static int nat_rmdir(struct inode *parent, struct dentry *dentry)
369 struct hfs_cat_entry *entry = HFS_I(parent)->entry;
370 struct hfs_name cname;
371 int error;
373 hfs_nameout(parent, &cname, dentry->d_name.name, dentry->d_name.len);
374 if (hfs_streq(cname.Name, cname.Len,
375 DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) {
376 if (!HFS_SB(parent->i_sb)->s_afpd) {
377 /* Not in AFPD compatibility mode */
378 error = -EPERM;
379 } else if (entry->u.dir.files || entry->u.dir.dirs) {
380 /* AFPD compatible, but the directory is not empty */
381 error = -ENOTEMPTY;
382 } else {
383 /* AFPD compatible, so pretend to succeed */
384 error = 0;
386 } else {
387 error = hfs_rmdir(parent, dentry);
389 return error;
393 * nat_hdr_unlink()
395 * This is the unlink() entry in the inode_operations structure for
396 * Netatalk .AppleDouble directories. The purpose is to delete an
397 * existing file, given the inode for the parent directory and the name
398 * (and its length) of the existing file.
400 * WE DON'T ACTUALLY DELETE HEADER THE FILE.
401 * In non-afpd-compatible mode:
402 * We return -EPERM.
403 * In afpd-compatible mode:
404 * We return success if the file exists or is .Parent.
405 * Otherwise we return -ENOENT.
407 static int nat_hdr_unlink(struct inode *dir, struct dentry *dentry)
409 struct hfs_cat_entry *entry = HFS_I(dir)->entry;
410 int error = 0;
412 if (!HFS_SB(dir->i_sb)->s_afpd) {
413 /* Not in AFPD compatibility mode */
414 error = -EPERM;
415 } else {
416 struct hfs_name cname;
418 hfs_nameout(dir, &cname, dentry->d_name.name,
419 dentry->d_name.len);
420 if (!hfs_streq(cname.Name, cname.Len,
421 DOT_PARENT->Name, DOT_PARENT_LEN)) {
422 struct hfs_cat_entry *victim;
423 struct hfs_cat_key key;
425 hfs_cat_build_key(entry->cnid, &cname, &key);
426 victim = hfs_cat_get(entry->mdb, &key);
428 if (victim) {
429 /* pretend to succeed */
430 hfs_cat_put(victim);
431 } else {
432 error = -ENOENT;
436 return error;
440 * nat_hdr_rename()
442 * This is the rename() entry in the inode_operations structure for
443 * Netatalk header directories. The purpose is to rename an existing
444 * file given the inode for the current directory and the name
445 * (and its length) of the existing file and the inode for the new
446 * directory and the name (and its length) of the new file/directory.
448 * WE NEVER MOVE ANYTHING.
449 * In non-afpd-compatible mode:
450 * We return -EPERM.
451 * In afpd-compatible mode:
452 * If the source header doesn't exist, we return -ENOENT.
453 * If the destination is not a header directory we return -EPERM.
454 * We return success if the destination is also a header directory
455 * and the header exists or is ".Parent".
457 static int nat_hdr_rename(struct inode *old_dir, struct dentry *old_dentry,
458 struct inode *new_dir, struct dentry *new_dentry)
460 struct hfs_cat_entry *entry = HFS_I(old_dir)->entry;
461 int error = 0;
463 if (!HFS_SB(old_dir->i_sb)->s_afpd) {
464 /* Not in AFPD compatibility mode */
465 error = -EPERM;
466 } else {
467 struct hfs_name cname;
469 hfs_nameout(old_dir, &cname, old_dentry->d_name.name,
470 old_dentry->d_name.len);
471 if (!hfs_streq(cname.Name, cname.Len,
472 DOT_PARENT->Name, DOT_PARENT_LEN)) {
473 struct hfs_cat_entry *victim;
474 struct hfs_cat_key key;
476 hfs_cat_build_key(entry->cnid, &cname, &key);
477 victim = hfs_cat_get(entry->mdb, &key);
479 if (victim) {
480 /* pretend to succeed */
481 hfs_cat_put(victim);
482 } else {
483 error = -ENOENT;
487 if (!error && (HFS_ITYPE(new_dir->i_ino) != HFS_NAT_HDIR)) {
488 error = -EPERM;
491 return error;