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.
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 ================*/
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
[] = {
50 {DOT_APPLEDOUBLE_LEN
, ".AppleDouble"},
51 {DOT_PARENT_LEN
, ".Parent"},
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 struct file_operations hfs_nat_dir_operations
= {
66 read
: generic_read_dir
,
71 struct inode_operations hfs_nat_ndir_inode_operations
= {
78 setattr
: hfs_notify_change
,
81 struct inode_operations hfs_nat_hdir_inode_operations
= {
84 unlink
: nat_hdr_unlink
,
85 rename
: nat_hdr_rename
,
86 setattr
: hfs_notify_change
,
89 /*================ File-local functions ================*/
94 * This is the lookup() entry in the inode_operations structure for
95 * HFS directories in the Netatalk scheme. The purpose is to generate
96 * the inode corresponding to an entry in a directory, given the inode
97 * for the directory and the name (and its length) of the entry.
99 static struct dentry
*nat_lookup(struct inode
* dir
, struct dentry
*dentry
)
102 struct hfs_name cname
;
103 struct hfs_cat_entry
*entry
;
104 struct hfs_cat_key key
;
105 struct inode
*inode
= NULL
;
107 dentry
->d_op
= &hfs_dentry_operations
;
108 entry
= HFS_I(dir
)->entry
;
109 dtype
= HFS_ITYPE(dir
->i_ino
);
111 /* Perform name-mangling */
112 hfs_nameout(dir
, &cname
, dentry
->d_name
.name
, dentry
->d_name
.len
);
114 /* no need to check for "." or ".." */
116 /* Check for ".AppleDouble" if in a normal directory,
117 and for ".Parent" in ".AppleDouble". */
118 if (dtype
==HFS_NAT_NDIR
) {
119 /* Check for ".AppleDouble" */
120 if (hfs_streq(cname
.Name
, cname
.Len
,
121 DOT_APPLEDOUBLE
->Name
, DOT_APPLEDOUBLE_LEN
)) {
122 ++entry
->count
; /* __hfs_iget() eats one */
123 inode
= hfs_iget(entry
, HFS_NAT_HDIR
, dentry
);
126 } else if (dtype
==HFS_NAT_HDIR
) {
127 if (hfs_streq(cname
.Name
, cname
.Len
,
128 DOT_PARENT
->Name
, DOT_PARENT_LEN
)) {
129 ++entry
->count
; /* __hfs_iget() eats one */
130 inode
= hfs_iget(entry
, HFS_NAT_HDR
, dentry
);
134 if ((entry
->cnid
== htonl(HFS_ROOT_CNID
)) &&
135 hfs_streq(cname
.Name
, cname
.Len
,
136 ROOTINFO
->Name
, ROOTINFO_LEN
)) {
137 ++entry
->count
; /* __hfs_iget() eats one */
138 inode
= hfs_iget(entry
, HFS_NAT_HDR
, dentry
);
143 /* Do an hfs_iget() on the mangled name. */
144 hfs_cat_build_key(entry
->cnid
, &cname
, &key
);
145 inode
= hfs_iget(hfs_cat_get(entry
->mdb
, &key
),
146 HFS_I(dir
)->file_type
, dentry
);
148 /* Don't return a header file for a directory other than .Parent */
149 if (inode
&& (dtype
== HFS_NAT_HDIR
) &&
150 (HFS_I(inode
)->entry
!= entry
) &&
151 (HFS_I(inode
)->entry
->type
== HFS_CDR_DIR
)) {
152 iput(inode
); /* this does an hfs_cat_put */
157 d_add(dentry
, inode
);
164 * This is the readdir() entry in the file_operations structure for
165 * HFS directories in the netatalk scheme. The purpose is to
166 * enumerate the entries in a directory, given the inode of the
167 * directory and a struct file which indicates the location in the
168 * directory. The struct file is updated so that the next call with
169 * the same dir and filp will produce the next directory entry. The
170 * entries are returned in dirent, which is "filled-in" by calling
171 * filldir(). This allows the same readdir() function be used for
172 * different dirent formats. We try to read in as many entries as we
173 * can before filldir() refuses to take any more.
175 * Note that the Netatalk format doesn't have the problem with
176 * metadata for covered directories that exists in the other formats,
177 * since the metadata is contained within the directory.
179 static int nat_readdir(struct file
* filp
,
180 void * dirent
, filldir_t filldir
)
184 struct hfs_brec brec
;
185 struct hfs_cat_entry
*entry
;
186 struct inode
*dir
= filp
->f_dentry
->d_inode
;
188 entry
= HFS_I(dir
)->entry
;
189 type
= HFS_ITYPE(dir
->i_ino
);
190 skip_dirs
= (type
== HFS_NAT_HDIR
);
192 if (filp
->f_pos
== 0) {
193 /* Entry 0 is for "." */
194 if (filldir(dirent
, DOT
->Name
, DOT_LEN
, 0, dir
->i_ino
)) {
200 if (filp
->f_pos
== 1) {
201 /* Entry 1 is for ".." */
204 if (type
== HFS_NAT_NDIR
) {
205 cnid
= hfs_get_nl(entry
->key
.ParID
);
210 if (filldir(dirent
, DOT_DOT
->Name
,
211 DOT_DOT_LEN
, 1, ntohl(cnid
))) {
217 if (filp
->f_pos
< (dir
->i_size
- 2)) {
221 if (hfs_cat_open(entry
, &brec
) ||
222 hfs_cat_next(entry
, &brec
, filp
->f_pos
- 2, &cnid
, &type
)) {
225 while (filp
->f_pos
< (dir
->i_size
- 2)) {
226 if (hfs_cat_next(entry
, &brec
, 1, &cnid
, &type
)) {
229 if (!skip_dirs
|| (type
!= HFS_CDR_DIR
)) {
232 unsigned char tmp_name
[HFS_NAMEMAX
];
234 ino
= ntohl(cnid
) | HFS_I(dir
)->file_type
;
235 len
= hfs_namein(dir
, tmp_name
,
236 &((struct hfs_cat_key
*)brec
.key
)->CName
);
237 if (filldir(dirent
, tmp_name
, len
,
239 hfs_cat_close(entry
, &brec
);
245 hfs_cat_close(entry
, &brec
);
248 if (filp
->f_pos
== (dir
->i_size
- 2)) {
249 if (type
== HFS_NAT_NDIR
) {
250 /* In normal dirs entry 2 is for ".AppleDouble" */
251 if (filldir(dirent
, DOT_APPLEDOUBLE
->Name
,
252 DOT_APPLEDOUBLE_LEN
, filp
->f_pos
,
253 ntohl(entry
->cnid
) | HFS_NAT_HDIR
)) {
256 } else if (type
== HFS_NAT_HDIR
) {
257 /* In .AppleDouble entry 2 is for ".Parent" */
258 if (filldir(dirent
, DOT_PARENT
->Name
,
259 DOT_PARENT_LEN
, filp
->f_pos
,
260 ntohl(entry
->cnid
) | HFS_NAT_HDR
)) {
267 if (filp
->f_pos
== (dir
->i_size
- 1)) {
268 /* handle ROOT/.AppleDouble/RootInfo as the last entry. */
269 if ((entry
->cnid
== htonl(HFS_ROOT_CNID
)) &&
270 (type
== HFS_NAT_HDIR
)) {
271 if (filldir(dirent
, ROOTINFO
->Name
,
272 ROOTINFO_LEN
, filp
->f_pos
,
273 ntohl(entry
->cnid
) | HFS_NAT_HDR
)) {
283 /* due to the dcache caching negative dentries for non-existent files,
284 * we need to drop those entries when a file silently gets created.
285 * as far as i can tell, the calls that need to do this are the file
286 * related calls (create, rename, and mknod). the directory calls
287 * should be immune. the relevant calls in dir.c call drop_dentry
288 * upon successful completion. */
289 void hfs_nat_drop_dentry(struct dentry
*dentry
, const ino_t type
)
294 case HFS_NAT_HDR
: /* given .AppleDouble/name */
296 de
= hfs_lookup_dentry(dentry
->d_parent
->d_parent
,
297 dentry
->d_name
.name
, dentry
->d_name
.len
);
305 case HFS_NAT_DATA
: /* given name */
306 /* look for .AppleDouble/name */
307 hfs_drop_special(dentry
->d_parent
, DOT_APPLEDOUBLE
, dentry
);
316 * This is the rmdir() entry in the inode_operations structure for
317 * Netatalk directories. The purpose is to delete an existing
318 * directory, given the inode for the parent directory and the name
319 * (and its length) of the existing directory.
321 * We handle .AppleDouble and call hfs_rmdir() for all other cases.
323 static int nat_rmdir(struct inode
*parent
, struct dentry
*dentry
)
325 struct hfs_cat_entry
*entry
= HFS_I(parent
)->entry
;
326 struct hfs_name cname
;
329 hfs_nameout(parent
, &cname
, dentry
->d_name
.name
, dentry
->d_name
.len
);
330 if (hfs_streq(cname
.Name
, cname
.Len
,
331 DOT_APPLEDOUBLE
->Name
, DOT_APPLEDOUBLE_LEN
)) {
332 if (!HFS_SB(parent
->i_sb
)->s_afpd
) {
333 /* Not in AFPD compatibility mode */
335 } else if (entry
->u
.dir
.files
|| entry
->u
.dir
.dirs
) {
336 /* AFPD compatible, but the directory is not empty */
339 /* AFPD compatible, so pretend to succeed */
343 error
= hfs_rmdir(parent
, dentry
);
351 * This is the unlink() entry in the inode_operations structure for
352 * Netatalk .AppleDouble directories. The purpose is to delete an
353 * existing file, given the inode for the parent directory and the name
354 * (and its length) of the existing file.
356 * WE DON'T ACTUALLY DELETE HEADER THE FILE.
357 * In non-afpd-compatible mode:
359 * In afpd-compatible mode:
360 * We return success if the file exists or is .Parent.
361 * Otherwise we return -ENOENT.
363 static int nat_hdr_unlink(struct inode
*dir
, struct dentry
*dentry
)
365 struct hfs_cat_entry
*entry
= HFS_I(dir
)->entry
;
368 if (!HFS_SB(dir
->i_sb
)->s_afpd
) {
369 /* Not in AFPD compatibility mode */
372 struct hfs_name cname
;
374 hfs_nameout(dir
, &cname
, dentry
->d_name
.name
,
376 if (!hfs_streq(cname
.Name
, cname
.Len
,
377 DOT_PARENT
->Name
, DOT_PARENT_LEN
)) {
378 struct hfs_cat_entry
*victim
;
379 struct hfs_cat_key key
;
381 hfs_cat_build_key(entry
->cnid
, &cname
, &key
);
382 victim
= hfs_cat_get(entry
->mdb
, &key
);
385 /* pretend to succeed */
398 * This is the rename() entry in the inode_operations structure for
399 * Netatalk header directories. The purpose is to rename an existing
400 * file given the inode for the current directory and the name
401 * (and its length) of the existing file and the inode for the new
402 * directory and the name (and its length) of the new file/directory.
404 * WE NEVER MOVE ANYTHING.
405 * In non-afpd-compatible mode:
407 * In afpd-compatible mode:
408 * If the source header doesn't exist, we return -ENOENT.
409 * If the destination is not a header directory we return -EPERM.
410 * We return success if the destination is also a header directory
411 * and the header exists or is ".Parent".
413 static int nat_hdr_rename(struct inode
*old_dir
, struct dentry
*old_dentry
,
414 struct inode
*new_dir
, struct dentry
*new_dentry
)
416 struct hfs_cat_entry
*entry
= HFS_I(old_dir
)->entry
;
419 if (!HFS_SB(old_dir
->i_sb
)->s_afpd
) {
420 /* Not in AFPD compatibility mode */
423 struct hfs_name cname
;
425 hfs_nameout(old_dir
, &cname
, old_dentry
->d_name
.name
,
426 old_dentry
->d_name
.len
);
427 if (!hfs_streq(cname
.Name
, cname
.Len
,
428 DOT_PARENT
->Name
, DOT_PARENT_LEN
)) {
429 struct hfs_cat_entry
*victim
;
430 struct hfs_cat_key key
;
432 hfs_cat_build_key(entry
->cnid
, &cname
, &key
);
433 victim
= hfs_cat_get(entry
->mdb
, &key
);
436 /* pretend to succeed */
443 if (!error
&& (HFS_ITYPE(new_dir
->i_ino
) != HFS_NAT_HDIR
)) {