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(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(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(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(&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 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 int hfsplus_link(struct dentry
*src_dentry
, struct inode
*dst_dir
, struct dentry
*dst_dentry
)
255 struct super_block
*sb
= dst_dir
->i_sb
;
256 struct inode
*inode
= src_dentry
->d_inode
;
257 struct inode
*src_dir
= src_dentry
->d_parent
->d_inode
;
263 if (HFSPLUS_IS_RSRC(inode
))
266 if (inode
->i_ino
== (u32
)(unsigned long)src_dentry
->d_fsdata
) {
268 get_random_bytes(&id
, sizeof(cnid
));
271 str
.len
= sprintf(name
, "iNode%d", id
);
272 res
= hfsplus_rename_cat(inode
->i_ino
,
273 src_dir
, &src_dentry
->d_name
,
274 HFSPLUS_SB(sb
).hidden_dir
, &str
);
280 HFSPLUS_I(inode
).dev
= id
;
281 cnid
= HFSPLUS_SB(sb
).next_cnid
++;
282 src_dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
283 res
= hfsplus_create_cat(cnid
, src_dir
, &src_dentry
->d_name
, inode
);
287 HFSPLUS_SB(sb
).file_count
++;
289 cnid
= HFSPLUS_SB(sb
).next_cnid
++;
290 res
= hfsplus_create_cat(cnid
, dst_dir
, &dst_dentry
->d_name
, inode
);
295 hfsplus_instantiate(dst_dentry
, inode
, cnid
);
296 atomic_inc(&inode
->i_count
);
297 inode
->i_ctime
= CURRENT_TIME
;
298 mark_inode_dirty(inode
);
299 HFSPLUS_SB(sb
).file_count
++;
305 int hfsplus_unlink(struct inode
*dir
, struct dentry
*dentry
)
307 struct super_block
*sb
= dir
->i_sb
;
308 struct inode
*inode
= dentry
->d_inode
;
314 if (HFSPLUS_IS_RSRC(inode
))
317 cnid
= (u32
)(unsigned long)dentry
->d_fsdata
;
318 if (inode
->i_ino
== cnid
&&
319 atomic_read(&HFSPLUS_I(inode
).opencnt
)) {
321 str
.len
= sprintf(name
, "temp%lu", inode
->i_ino
);
322 res
= hfsplus_rename_cat(inode
->i_ino
,
323 dir
, &dentry
->d_name
,
324 HFSPLUS_SB(sb
).hidden_dir
, &str
);
326 inode
->i_flags
|= S_DEAD
;
329 res
= hfsplus_delete_cat(cnid
, dir
, &dentry
->d_name
);
334 hfsplus_delete_inode(inode
);
335 if (inode
->i_ino
!= cnid
&& !inode
->i_nlink
) {
336 if (!atomic_read(&HFSPLUS_I(inode
).opencnt
)) {
337 res
= hfsplus_delete_cat(inode
->i_ino
, HFSPLUS_SB(sb
).hidden_dir
, NULL
);
339 hfsplus_delete_inode(inode
);
341 inode
->i_flags
|= S_DEAD
;
343 inode
->i_ctime
= CURRENT_TIME
;
344 mark_inode_dirty(inode
);
349 int hfsplus_mkdir(struct inode
*dir
, struct dentry
*dentry
, int mode
)
354 inode
= hfsplus_new_inode(dir
->i_sb
, S_IFDIR
| mode
);
358 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
361 hfsplus_delete_inode(inode
);
365 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
366 mark_inode_dirty(inode
);
370 int hfsplus_rmdir(struct inode
*dir
, struct dentry
*dentry
)
375 inode
= dentry
->d_inode
;
376 if (inode
->i_size
!= 2)
378 res
= hfsplus_delete_cat(inode
->i_ino
, dir
, &dentry
->d_name
);
382 inode
->i_ctime
= CURRENT_TIME
;
383 hfsplus_delete_inode(inode
);
384 mark_inode_dirty(inode
);
388 int hfsplus_symlink(struct inode
*dir
, struct dentry
*dentry
, const char *symname
)
390 struct super_block
*sb
;
395 inode
= hfsplus_new_inode(sb
, S_IFLNK
| S_IRWXUGO
);
399 res
= page_symlink(inode
, symname
, strlen(symname
) + 1);
402 hfsplus_delete_inode(inode
);
407 mark_inode_dirty(inode
);
408 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
411 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
412 mark_inode_dirty(inode
);
418 int hfsplus_mknod(struct inode
*dir
, struct dentry
*dentry
, int mode
, dev_t rdev
)
420 struct super_block
*sb
;
425 inode
= hfsplus_new_inode(sb
, mode
);
429 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
432 hfsplus_delete_inode(inode
);
436 init_special_inode(inode
, mode
, rdev
);
437 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
438 mark_inode_dirty(inode
);
443 int hfsplus_rename(struct inode
*old_dir
, struct dentry
*old_dentry
,
444 struct inode
*new_dir
, struct dentry
*new_dentry
)
448 /* Unlink destination if it already exists */
449 if (new_dentry
->d_inode
) {
450 res
= hfsplus_unlink(new_dir
, new_dentry
);
455 res
= hfsplus_rename_cat((u32
)(unsigned long)old_dentry
->d_fsdata
,
456 old_dir
, &old_dentry
->d_name
,
457 new_dir
, &new_dentry
->d_name
);
459 new_dentry
->d_fsdata
= old_dentry
->d_fsdata
;
463 struct inode_operations hfsplus_dir_inode_operations
= {
464 .lookup
= hfsplus_lookup
,
465 .create
= hfsplus_create
,
466 .link
= hfsplus_link
,
467 .unlink
= hfsplus_unlink
,
468 .mkdir
= hfsplus_mkdir
,
469 .rmdir
= hfsplus_rmdir
,
470 .symlink
= hfsplus_symlink
,
471 .mknod
= hfsplus_mknod
,
472 .rename
= hfsplus_rename
,
475 struct file_operations hfsplus_dir_operations
= {
476 .read
= generic_read_dir
,
477 .readdir
= hfsplus_readdir
,
478 .ioctl
= hfsplus_ioctl
,
479 .llseek
= generic_file_llseek
,
480 .release
= hfsplus_dir_release
,