2 * linux/fs/hfsplus/dir.c
5 * Brad Boyer (flar@allandria.com)
6 * (C) 2003 Ardis Technologies <roman@ardistech.com>
8 * Handling of directories
11 #include <linux/errno.h>
13 #include <linux/sched.h>
14 #include <linux/slab.h>
15 #include <linux/random.h>
16 #include <linux/version.h>
18 #include "hfsplus_fs.h"
19 #include "hfsplus_raw.h"
21 static inline void hfsplus_instantiate(struct dentry
*dentry
,
22 struct inode
*inode
, u32 cnid
)
24 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
25 d_instantiate(dentry
, inode
);
28 /* Find the entry inside dir named dentry->d_name */
29 static struct dentry
*hfsplus_lookup(struct inode
*dir
, struct dentry
*dentry
,
32 struct inode
*inode
= NULL
;
33 struct hfs_find_data fd
;
34 struct super_block
*sb
;
35 hfsplus_cat_entry entry
;
41 dentry
->d_fsdata
= NULL
;
42 hfs_find_init(HFSPLUS_SB(sb
).cat_tree
, &fd
);
43 hfsplus_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
, &dentry
->d_name
);
45 err
= hfs_brec_read(&fd
, &entry
, sizeof(entry
));
55 type
= be16_to_cpu(entry
.type
);
56 if (type
== HFSPLUS_FOLDER
) {
57 if (fd
.entrylength
< sizeof(struct hfsplus_cat_folder
)) {
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
)) {
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
)) {
74 if (dentry
->d_fsdata
) {
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
);
83 hfsplus_cat_build_key(sb
, fd
.search_key
, HFSPLUS_SB(sb
).hidden_dir
->i_ino
, &str
);
85 } else if (!dentry
->d_fsdata
)
86 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
88 printk("HFS+-fs: Illegal catalog entry type in lookup\n");
93 inode
= iget(dir
->i_sb
, cnid
);
95 return ERR_PTR(-EACCES
);
96 if (S_ISREG(inode
->i_mode
))
97 HFSPLUS_I(inode
).dev
= linkid
;
106 static int hfsplus_readdir(struct file
*filp
, void *dirent
, filldir_t filldir
)
108 struct inode
*inode
= filp
->f_dentry
->d_inode
;
109 struct super_block
*sb
= inode
->i_sb
;
111 char strbuf
[HFSPLUS_MAX_STRLEN
+ 1];
112 hfsplus_cat_entry entry
;
113 struct hfs_find_data fd
;
114 struct hfsplus_readdir_data
*rd
;
117 if (filp
->f_pos
>= inode
->i_size
)
120 hfs_find_init(HFSPLUS_SB(sb
).cat_tree
, &fd
);
121 hfsplus_cat_build_key(sb
, fd
.search_key
, inode
->i_ino
, NULL
);
122 err
= hfs_brec_find(&fd
);
126 switch ((u32
)filp
->f_pos
) {
128 /* This is completely artificial... */
129 if (filldir(dirent
, ".", 1, 0, inode
->i_ino
, DT_DIR
))
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");
140 if (fd
.entrylength
< HFSPLUS_MIN_THREAD_SZ
) {
141 printk("HFS+-fs: truncated catalog thread\n");
145 if (filldir(dirent
, "..", 2, 1,
146 be32_to_cpu(entry
.thread
.parentID
), DT_DIR
))
151 if (filp
->f_pos
>= inode
->i_size
)
153 err
= hfs_brec_goto(&fd
, filp
->f_pos
- 1);
159 if (be32_to_cpu(fd
.key
->cat
.parent
) != inode
->i_ino
) {
160 printk("HFS+-fs: walked past end of dir\n");
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(sb
, &fd
.key
->cat
.name
, strbuf
, &len
);
170 if (type
== HFSPLUS_FOLDER
) {
171 if (fd
.entrylength
< sizeof(struct hfsplus_cat_folder
)) {
172 printk("HFS+-fs: small dir entry\n");
176 if (HFSPLUS_SB(sb
).hidden_dir
&&
177 HFSPLUS_SB(sb
).hidden_dir
->i_ino
== be32_to_cpu(entry
.folder
.id
))
179 if (filldir(dirent
, strbuf
, len
, filp
->f_pos
,
180 be32_to_cpu(entry
.folder
.id
), DT_DIR
))
182 } else if (type
== HFSPLUS_FILE
) {
183 if (fd
.entrylength
< sizeof(struct hfsplus_cat_file
)) {
184 printk("HFS+-fs: small file entry\n");
188 if (filldir(dirent
, strbuf
, len
, filp
->f_pos
,
189 be32_to_cpu(entry
.file
.id
), DT_REG
))
192 printk("HFS+-fs: bad catalog entry type\n");
198 if (filp
->f_pos
>= inode
->i_size
)
200 err
= hfs_brec_goto(&fd
, 1);
204 rd
= filp
->private_data
;
206 rd
= kmalloc(sizeof(struct hfsplus_readdir_data
), GFP_KERNEL
);
211 filp
->private_data
= rd
;
213 list_add(&rd
->list
, &HFSPLUS_I(inode
).open_dir_list
);
215 memcpy(&rd
->key
, fd
.key
, sizeof(struct hfsplus_cat_key
));
221 static int hfsplus_dir_release(struct inode
*inode
, struct file
*file
)
223 struct hfsplus_readdir_data
*rd
= file
->private_data
;
231 static int hfsplus_create(struct inode
*dir
, struct dentry
*dentry
, int mode
,
232 struct nameidata
*nd
)
237 inode
= hfsplus_new_inode(dir
->i_sb
, mode
);
241 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
244 hfsplus_delete_inode(inode
);
248 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
249 mark_inode_dirty(inode
);
253 static int hfsplus_link(struct dentry
*src_dentry
, struct inode
*dst_dir
,
254 struct dentry
*dst_dentry
)
256 struct super_block
*sb
= dst_dir
->i_sb
;
257 struct inode
*inode
= src_dentry
->d_inode
;
258 struct inode
*src_dir
= src_dentry
->d_parent
->d_inode
;
264 if (HFSPLUS_IS_RSRC(inode
))
267 if (inode
->i_ino
== (u32
)(unsigned long)src_dentry
->d_fsdata
) {
269 get_random_bytes(&id
, sizeof(cnid
));
272 str
.len
= sprintf(name
, "iNode%d", id
);
273 res
= hfsplus_rename_cat(inode
->i_ino
,
274 src_dir
, &src_dentry
->d_name
,
275 HFSPLUS_SB(sb
).hidden_dir
, &str
);
281 HFSPLUS_I(inode
).dev
= id
;
282 cnid
= HFSPLUS_SB(sb
).next_cnid
++;
283 src_dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
284 res
= hfsplus_create_cat(cnid
, src_dir
, &src_dentry
->d_name
, inode
);
288 HFSPLUS_SB(sb
).file_count
++;
290 cnid
= HFSPLUS_SB(sb
).next_cnid
++;
291 res
= hfsplus_create_cat(cnid
, dst_dir
, &dst_dentry
->d_name
, inode
);
296 hfsplus_instantiate(dst_dentry
, inode
, cnid
);
297 atomic_inc(&inode
->i_count
);
298 inode
->i_ctime
= CURRENT_TIME_SEC
;
299 mark_inode_dirty(inode
);
300 HFSPLUS_SB(sb
).file_count
++;
306 static int hfsplus_unlink(struct inode
*dir
, struct dentry
*dentry
)
308 struct super_block
*sb
= dir
->i_sb
;
309 struct inode
*inode
= dentry
->d_inode
;
315 if (HFSPLUS_IS_RSRC(inode
))
318 cnid
= (u32
)(unsigned long)dentry
->d_fsdata
;
319 if (inode
->i_ino
== cnid
&&
320 atomic_read(&HFSPLUS_I(inode
).opencnt
)) {
322 str
.len
= sprintf(name
, "temp%lu", inode
->i_ino
);
323 res
= hfsplus_rename_cat(inode
->i_ino
,
324 dir
, &dentry
->d_name
,
325 HFSPLUS_SB(sb
).hidden_dir
, &str
);
327 inode
->i_flags
|= S_DEAD
;
330 res
= hfsplus_delete_cat(cnid
, dir
, &dentry
->d_name
);
335 hfsplus_delete_inode(inode
);
336 if (inode
->i_ino
!= cnid
&& !inode
->i_nlink
) {
337 if (!atomic_read(&HFSPLUS_I(inode
).opencnt
)) {
338 res
= hfsplus_delete_cat(inode
->i_ino
, HFSPLUS_SB(sb
).hidden_dir
, NULL
);
340 hfsplus_delete_inode(inode
);
342 inode
->i_flags
|= S_DEAD
;
344 inode
->i_ctime
= CURRENT_TIME_SEC
;
345 mark_inode_dirty(inode
);
350 static int hfsplus_mkdir(struct inode
*dir
, struct dentry
*dentry
, int mode
)
355 inode
= hfsplus_new_inode(dir
->i_sb
, S_IFDIR
| mode
);
359 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
362 hfsplus_delete_inode(inode
);
366 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
367 mark_inode_dirty(inode
);
371 static int hfsplus_rmdir(struct inode
*dir
, struct dentry
*dentry
)
376 inode
= dentry
->d_inode
;
377 if (inode
->i_size
!= 2)
379 res
= hfsplus_delete_cat(inode
->i_ino
, dir
, &dentry
->d_name
);
383 inode
->i_ctime
= CURRENT_TIME_SEC
;
384 hfsplus_delete_inode(inode
);
385 mark_inode_dirty(inode
);
389 static int hfsplus_symlink(struct inode
*dir
, struct dentry
*dentry
,
392 struct super_block
*sb
;
397 inode
= hfsplus_new_inode(sb
, S_IFLNK
| S_IRWXUGO
);
401 res
= page_symlink(inode
, symname
, strlen(symname
) + 1);
404 hfsplus_delete_inode(inode
);
409 mark_inode_dirty(inode
);
410 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
413 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
414 mark_inode_dirty(inode
);
420 static int hfsplus_mknod(struct inode
*dir
, struct dentry
*dentry
,
421 int mode
, dev_t rdev
)
423 struct super_block
*sb
;
428 inode
= hfsplus_new_inode(sb
, mode
);
432 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
435 hfsplus_delete_inode(inode
);
439 init_special_inode(inode
, mode
, rdev
);
440 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
441 mark_inode_dirty(inode
);
446 static int hfsplus_rename(struct inode
*old_dir
, struct dentry
*old_dentry
,
447 struct inode
*new_dir
, struct dentry
*new_dentry
)
451 /* Unlink destination if it already exists */
452 if (new_dentry
->d_inode
) {
453 res
= hfsplus_unlink(new_dir
, new_dentry
);
458 res
= hfsplus_rename_cat((u32
)(unsigned long)old_dentry
->d_fsdata
,
459 old_dir
, &old_dentry
->d_name
,
460 new_dir
, &new_dentry
->d_name
);
462 new_dentry
->d_fsdata
= old_dentry
->d_fsdata
;
466 struct inode_operations hfsplus_dir_inode_operations
= {
467 .lookup
= hfsplus_lookup
,
468 .create
= hfsplus_create
,
469 .link
= hfsplus_link
,
470 .unlink
= hfsplus_unlink
,
471 .mkdir
= hfsplus_mkdir
,
472 .rmdir
= hfsplus_rmdir
,
473 .symlink
= hfsplus_symlink
,
474 .mknod
= hfsplus_mknod
,
475 .rename
= hfsplus_rename
,
478 struct file_operations hfsplus_dir_operations
= {
479 .read
= generic_read_dir
,
480 .readdir
= hfsplus_readdir
,
481 .ioctl
= hfsplus_ioctl
,
482 .llseek
= generic_file_llseek
,
483 .release
= hfsplus_dir_release
,