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>
17 #include "hfsplus_fs.h"
18 #include "hfsplus_raw.h"
20 static inline void hfsplus_instantiate(struct dentry
*dentry
,
21 struct inode
*inode
, u32 cnid
)
23 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
24 d_instantiate(dentry
, inode
);
27 /* Find the entry inside dir named dentry->d_name */
28 static struct dentry
*hfsplus_lookup(struct inode
*dir
, struct dentry
*dentry
,
31 struct inode
*inode
= NULL
;
32 struct hfs_find_data fd
;
33 struct super_block
*sb
;
34 hfsplus_cat_entry entry
;
40 dentry
->d_fsdata
= NULL
;
41 hfs_find_init(HFSPLUS_SB(sb
).cat_tree
, &fd
);
42 hfsplus_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
, &dentry
->d_name
);
44 err
= hfs_brec_read(&fd
, &entry
, sizeof(entry
));
54 type
= be16_to_cpu(entry
.type
);
55 if (type
== HFSPLUS_FOLDER
) {
56 if (fd
.entrylength
< sizeof(struct hfsplus_cat_folder
)) {
60 cnid
= be32_to_cpu(entry
.folder
.id
);
61 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
62 } else if (type
== HFSPLUS_FILE
) {
63 if (fd
.entrylength
< sizeof(struct hfsplus_cat_file
)) {
67 cnid
= be32_to_cpu(entry
.file
.id
);
68 if (entry
.file
.user_info
.fdType
== cpu_to_be32(HFSP_HARDLINK_TYPE
) &&
69 entry
.file
.user_info
.fdCreator
== cpu_to_be32(HFSP_HFSPLUS_CREATOR
)) {
73 if (dentry
->d_fsdata
) {
78 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
79 linkid
= be32_to_cpu(entry
.file
.permissions
.dev
);
80 str
.len
= sprintf(name
, "iNode%d", linkid
);
82 hfsplus_cat_build_key(sb
, fd
.search_key
, HFSPLUS_SB(sb
).hidden_dir
->i_ino
, &str
);
84 } else if (!dentry
->d_fsdata
)
85 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
87 printk("HFS+-fs: Illegal catalog entry type in lookup\n");
92 inode
= iget(dir
->i_sb
, cnid
);
94 return ERR_PTR(-EACCES
);
95 if (S_ISREG(inode
->i_mode
))
96 HFSPLUS_I(inode
).dev
= linkid
;
105 static int hfsplus_readdir(struct file
*filp
, void *dirent
, filldir_t filldir
)
107 struct inode
*inode
= filp
->f_dentry
->d_inode
;
108 struct super_block
*sb
= inode
->i_sb
;
110 char strbuf
[HFSPLUS_MAX_STRLEN
+ 1];
111 hfsplus_cat_entry entry
;
112 struct hfs_find_data fd
;
113 struct hfsplus_readdir_data
*rd
;
116 if (filp
->f_pos
>= inode
->i_size
)
119 hfs_find_init(HFSPLUS_SB(sb
).cat_tree
, &fd
);
120 hfsplus_cat_build_key(sb
, fd
.search_key
, inode
->i_ino
, NULL
);
121 err
= hfs_brec_find(&fd
);
125 switch ((u32
)filp
->f_pos
) {
127 /* This is completely artificial... */
128 if (filldir(dirent
, ".", 1, 0, inode
->i_ino
, DT_DIR
))
133 hfs_bnode_read(fd
.bnode
, &entry
, fd
.entryoffset
, fd
.entrylength
);
134 if (be16_to_cpu(entry
.type
) != HFSPLUS_FOLDER_THREAD
) {
135 printk("HFS+-fs: bad catalog folder thread\n");
139 if (fd
.entrylength
< HFSPLUS_MIN_THREAD_SZ
) {
140 printk("HFS+-fs: truncated catalog thread\n");
144 if (filldir(dirent
, "..", 2, 1,
145 be32_to_cpu(entry
.thread
.parentID
), DT_DIR
))
150 if (filp
->f_pos
>= inode
->i_size
)
152 err
= hfs_brec_goto(&fd
, filp
->f_pos
- 1);
158 if (be32_to_cpu(fd
.key
->cat
.parent
) != inode
->i_ino
) {
159 printk("HFS+-fs: walked past end of dir\n");
163 hfs_bnode_read(fd
.bnode
, &entry
, fd
.entryoffset
, fd
.entrylength
);
164 type
= be16_to_cpu(entry
.type
);
165 len
= HFSPLUS_MAX_STRLEN
;
166 err
= hfsplus_uni2asc(sb
, &fd
.key
->cat
.name
, strbuf
, &len
);
169 if (type
== HFSPLUS_FOLDER
) {
170 if (fd
.entrylength
< sizeof(struct hfsplus_cat_folder
)) {
171 printk("HFS+-fs: small dir entry\n");
175 if (HFSPLUS_SB(sb
).hidden_dir
&&
176 HFSPLUS_SB(sb
).hidden_dir
->i_ino
== be32_to_cpu(entry
.folder
.id
))
178 if (filldir(dirent
, strbuf
, len
, filp
->f_pos
,
179 be32_to_cpu(entry
.folder
.id
), DT_DIR
))
181 } else if (type
== HFSPLUS_FILE
) {
182 if (fd
.entrylength
< sizeof(struct hfsplus_cat_file
)) {
183 printk("HFS+-fs: small file entry\n");
187 if (filldir(dirent
, strbuf
, len
, filp
->f_pos
,
188 be32_to_cpu(entry
.file
.id
), DT_REG
))
191 printk("HFS+-fs: bad catalog entry type\n");
197 if (filp
->f_pos
>= inode
->i_size
)
199 err
= hfs_brec_goto(&fd
, 1);
203 rd
= filp
->private_data
;
205 rd
= kmalloc(sizeof(struct hfsplus_readdir_data
), GFP_KERNEL
);
210 filp
->private_data
= rd
;
212 list_add(&rd
->list
, &HFSPLUS_I(inode
).open_dir_list
);
214 memcpy(&rd
->key
, fd
.key
, sizeof(struct hfsplus_cat_key
));
220 static int hfsplus_dir_release(struct inode
*inode
, struct file
*file
)
222 struct hfsplus_readdir_data
*rd
= file
->private_data
;
230 static int hfsplus_create(struct inode
*dir
, struct dentry
*dentry
, int mode
,
231 struct nameidata
*nd
)
236 inode
= hfsplus_new_inode(dir
->i_sb
, mode
);
240 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
243 hfsplus_delete_inode(inode
);
247 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
248 mark_inode_dirty(inode
);
252 static int hfsplus_link(struct dentry
*src_dentry
, struct inode
*dst_dir
,
253 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_SEC
;
298 mark_inode_dirty(inode
);
299 HFSPLUS_SB(sb
).file_count
++;
305 static 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_SEC
;
344 mark_inode_dirty(inode
);
349 static 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 static 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_SEC
;
383 hfsplus_delete_inode(inode
);
384 mark_inode_dirty(inode
);
388 static int hfsplus_symlink(struct inode
*dir
, struct dentry
*dentry
,
391 struct super_block
*sb
;
396 inode
= hfsplus_new_inode(sb
, S_IFLNK
| S_IRWXUGO
);
400 res
= page_symlink(inode
, symname
, strlen(symname
) + 1);
403 hfsplus_delete_inode(inode
);
408 mark_inode_dirty(inode
);
409 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
412 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
413 mark_inode_dirty(inode
);
419 static int hfsplus_mknod(struct inode
*dir
, struct dentry
*dentry
,
420 int mode
, dev_t rdev
)
422 struct super_block
*sb
;
427 inode
= hfsplus_new_inode(sb
, mode
);
431 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
434 hfsplus_delete_inode(inode
);
438 init_special_inode(inode
, mode
, rdev
);
439 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
440 mark_inode_dirty(inode
);
445 static int hfsplus_rename(struct inode
*old_dir
, struct dentry
*old_dentry
,
446 struct inode
*new_dir
, struct dentry
*new_dentry
)
450 /* Unlink destination if it already exists */
451 if (new_dentry
->d_inode
) {
452 res
= hfsplus_unlink(new_dir
, new_dentry
);
457 res
= hfsplus_rename_cat((u32
)(unsigned long)old_dentry
->d_fsdata
,
458 old_dir
, &old_dentry
->d_name
,
459 new_dir
, &new_dentry
->d_name
);
461 new_dentry
->d_fsdata
= old_dentry
->d_fsdata
;
465 struct inode_operations hfsplus_dir_inode_operations
= {
466 .lookup
= hfsplus_lookup
,
467 .create
= hfsplus_create
,
468 .link
= hfsplus_link
,
469 .unlink
= hfsplus_unlink
,
470 .mkdir
= hfsplus_mkdir
,
471 .rmdir
= hfsplus_rmdir
,
472 .symlink
= hfsplus_symlink
,
473 .mknod
= hfsplus_mknod
,
474 .rename
= hfsplus_rename
,
477 struct file_operations hfsplus_dir_operations
= {
478 .read
= generic_read_dir
,
479 .readdir
= hfsplus_readdir
,
480 .ioctl
= hfsplus_ioctl
,
481 .llseek
= generic_file_llseek
,
482 .release
= hfsplus_dir_release
,