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/slab.h>
14 #include <linux/random.h>
16 #include "hfsplus_fs.h"
17 #include "hfsplus_raw.h"
19 static inline void hfsplus_instantiate(struct dentry
*dentry
,
20 struct inode
*inode
, u32 cnid
)
22 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
23 d_instantiate(dentry
, inode
);
26 /* Find the entry inside dir named dentry->d_name */
27 static struct dentry
*hfsplus_lookup(struct inode
*dir
, struct dentry
*dentry
,
30 struct inode
*inode
= NULL
;
31 struct hfs_find_data fd
;
32 struct super_block
*sb
;
33 hfsplus_cat_entry entry
;
40 dentry
->d_fsdata
= NULL
;
41 err
= hfs_find_init(HFSPLUS_SB(sb
)->cat_tree
, &fd
);
44 hfsplus_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
, &dentry
->d_name
);
46 err
= hfs_brec_read(&fd
, &entry
, sizeof(entry
));
56 type
= be16_to_cpu(entry
.type
);
57 if (type
== HFSPLUS_FOLDER
) {
58 if (fd
.entrylength
< sizeof(struct hfsplus_cat_folder
)) {
62 cnid
= be32_to_cpu(entry
.folder
.id
);
63 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
64 } else if (type
== HFSPLUS_FILE
) {
65 if (fd
.entrylength
< sizeof(struct hfsplus_cat_file
)) {
69 cnid
= be32_to_cpu(entry
.file
.id
);
70 if (entry
.file
.user_info
.fdType
==
71 cpu_to_be32(HFSP_HARDLINK_TYPE
) &&
72 entry
.file
.user_info
.fdCreator
==
73 cpu_to_be32(HFSP_HFSPLUS_CREATOR
) &&
74 (entry
.file
.create_date
==
75 HFSPLUS_I(HFSPLUS_SB(sb
)->hidden_dir
)->
77 entry
.file
.create_date
==
78 HFSPLUS_I(sb
->s_root
->d_inode
)->
80 HFSPLUS_SB(sb
)->hidden_dir
) {
84 if (dentry
->d_fsdata
) {
86 * We found a link pointing to another link,
87 * so ignore it and treat it as regular file.
89 cnid
= (unsigned long)dentry
->d_fsdata
;
92 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
94 be32_to_cpu(entry
.file
.permissions
.dev
);
95 str
.len
= sprintf(name
, "iNode%d", linkid
);
97 hfsplus_cat_build_key(sb
, fd
.search_key
,
98 HFSPLUS_SB(sb
)->hidden_dir
->i_ino
,
102 } else if (!dentry
->d_fsdata
)
103 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
105 printk(KERN_ERR
"hfs: invalid catalog entry type in lookup\n");
110 inode
= hfsplus_iget(dir
->i_sb
, cnid
);
112 return ERR_CAST(inode
);
113 if (S_ISREG(inode
->i_mode
))
114 HFSPLUS_I(inode
)->linkid
= linkid
;
116 d_add(dentry
, inode
);
123 static int hfsplus_readdir(struct file
*filp
, void *dirent
, filldir_t filldir
)
125 struct inode
*inode
= filp
->f_path
.dentry
->d_inode
;
126 struct super_block
*sb
= inode
->i_sb
;
128 char strbuf
[HFSPLUS_MAX_STRLEN
+ 1];
129 hfsplus_cat_entry entry
;
130 struct hfs_find_data fd
;
131 struct hfsplus_readdir_data
*rd
;
134 if (filp
->f_pos
>= inode
->i_size
)
137 err
= hfs_find_init(HFSPLUS_SB(sb
)->cat_tree
, &fd
);
140 hfsplus_cat_build_key(sb
, fd
.search_key
, inode
->i_ino
, NULL
);
141 err
= hfs_brec_find(&fd
);
145 switch ((u32
)filp
->f_pos
) {
147 /* This is completely artificial... */
148 if (filldir(dirent
, ".", 1, 0, inode
->i_ino
, DT_DIR
))
153 if (fd
.entrylength
> sizeof(entry
) || fd
.entrylength
< 0) {
158 hfs_bnode_read(fd
.bnode
, &entry
, fd
.entryoffset
,
160 if (be16_to_cpu(entry
.type
) != HFSPLUS_FOLDER_THREAD
) {
161 printk(KERN_ERR
"hfs: bad catalog folder thread\n");
165 if (fd
.entrylength
< HFSPLUS_MIN_THREAD_SZ
) {
166 printk(KERN_ERR
"hfs: truncated catalog thread\n");
170 if (filldir(dirent
, "..", 2, 1,
171 be32_to_cpu(entry
.thread
.parentID
), DT_DIR
))
176 if (filp
->f_pos
>= inode
->i_size
)
178 err
= hfs_brec_goto(&fd
, filp
->f_pos
- 1);
184 if (be32_to_cpu(fd
.key
->cat
.parent
) != inode
->i_ino
) {
185 printk(KERN_ERR
"hfs: walked past end of dir\n");
190 if (fd
.entrylength
> sizeof(entry
) || fd
.entrylength
< 0) {
195 hfs_bnode_read(fd
.bnode
, &entry
, fd
.entryoffset
,
197 type
= be16_to_cpu(entry
.type
);
198 len
= HFSPLUS_MAX_STRLEN
;
199 err
= hfsplus_uni2asc(sb
, &fd
.key
->cat
.name
, strbuf
, &len
);
202 if (type
== HFSPLUS_FOLDER
) {
204 sizeof(struct hfsplus_cat_folder
)) {
205 printk(KERN_ERR
"hfs: small dir entry\n");
209 if (HFSPLUS_SB(sb
)->hidden_dir
&&
210 HFSPLUS_SB(sb
)->hidden_dir
->i_ino
==
211 be32_to_cpu(entry
.folder
.id
))
213 if (filldir(dirent
, strbuf
, len
, filp
->f_pos
,
214 be32_to_cpu(entry
.folder
.id
), DT_DIR
))
216 } else if (type
== HFSPLUS_FILE
) {
217 if (fd
.entrylength
< sizeof(struct hfsplus_cat_file
)) {
218 printk(KERN_ERR
"hfs: small file entry\n");
222 if (filldir(dirent
, strbuf
, len
, filp
->f_pos
,
223 be32_to_cpu(entry
.file
.id
), DT_REG
))
226 printk(KERN_ERR
"hfs: bad catalog entry type\n");
232 if (filp
->f_pos
>= inode
->i_size
)
234 err
= hfs_brec_goto(&fd
, 1);
238 rd
= filp
->private_data
;
240 rd
= kmalloc(sizeof(struct hfsplus_readdir_data
), GFP_KERNEL
);
245 filp
->private_data
= rd
;
247 list_add(&rd
->list
, &HFSPLUS_I(inode
)->open_dir_list
);
249 memcpy(&rd
->key
, fd
.key
, sizeof(struct hfsplus_cat_key
));
255 static int hfsplus_dir_release(struct inode
*inode
, struct file
*file
)
257 struct hfsplus_readdir_data
*rd
= file
->private_data
;
259 mutex_lock(&inode
->i_mutex
);
261 mutex_unlock(&inode
->i_mutex
);
267 static int hfsplus_link(struct dentry
*src_dentry
, struct inode
*dst_dir
,
268 struct dentry
*dst_dentry
)
270 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dst_dir
->i_sb
);
271 struct inode
*inode
= src_dentry
->d_inode
;
272 struct inode
*src_dir
= src_dentry
->d_parent
->d_inode
;
278 if (HFSPLUS_IS_RSRC(inode
))
280 if (!S_ISREG(inode
->i_mode
))
283 mutex_lock(&sbi
->vh_mutex
);
284 if (inode
->i_ino
== (u32
)(unsigned long)src_dentry
->d_fsdata
) {
286 get_random_bytes(&id
, sizeof(cnid
));
289 str
.len
= sprintf(name
, "iNode%d", id
);
290 res
= hfsplus_rename_cat(inode
->i_ino
,
291 src_dir
, &src_dentry
->d_name
,
292 sbi
->hidden_dir
, &str
);
298 HFSPLUS_I(inode
)->linkid
= id
;
299 cnid
= sbi
->next_cnid
++;
300 src_dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
301 res
= hfsplus_create_cat(cnid
, src_dir
,
302 &src_dentry
->d_name
, inode
);
308 cnid
= sbi
->next_cnid
++;
309 res
= hfsplus_create_cat(cnid
, dst_dir
, &dst_dentry
->d_name
, inode
);
314 hfsplus_instantiate(dst_dentry
, inode
, cnid
);
316 inode
->i_ctime
= CURRENT_TIME_SEC
;
317 mark_inode_dirty(inode
);
319 dst_dir
->i_sb
->s_dirt
= 1;
321 mutex_unlock(&sbi
->vh_mutex
);
325 static int hfsplus_unlink(struct inode
*dir
, struct dentry
*dentry
)
327 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
328 struct inode
*inode
= dentry
->d_inode
;
334 if (HFSPLUS_IS_RSRC(inode
))
337 mutex_lock(&sbi
->vh_mutex
);
338 cnid
= (u32
)(unsigned long)dentry
->d_fsdata
;
339 if (inode
->i_ino
== cnid
&&
340 atomic_read(&HFSPLUS_I(inode
)->opencnt
)) {
342 str
.len
= sprintf(name
, "temp%lu", inode
->i_ino
);
343 res
= hfsplus_rename_cat(inode
->i_ino
,
344 dir
, &dentry
->d_name
,
345 sbi
->hidden_dir
, &str
);
347 inode
->i_flags
|= S_DEAD
;
352 res
= hfsplus_delete_cat(cnid
, dir
, &dentry
->d_name
);
356 if (inode
->i_nlink
> 0)
358 if (inode
->i_ino
== cnid
)
360 if (!inode
->i_nlink
) {
361 if (inode
->i_ino
!= cnid
) {
363 if (!atomic_read(&HFSPLUS_I(inode
)->opencnt
)) {
364 res
= hfsplus_delete_cat(inode
->i_ino
,
368 hfsplus_delete_inode(inode
);
370 inode
->i_flags
|= S_DEAD
;
372 hfsplus_delete_inode(inode
);
375 inode
->i_ctime
= CURRENT_TIME_SEC
;
376 mark_inode_dirty(inode
);
378 mutex_unlock(&sbi
->vh_mutex
);
382 static int hfsplus_rmdir(struct inode
*dir
, struct dentry
*dentry
)
384 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
385 struct inode
*inode
= dentry
->d_inode
;
388 if (inode
->i_size
!= 2)
391 mutex_lock(&sbi
->vh_mutex
);
392 res
= hfsplus_delete_cat(inode
->i_ino
, dir
, &dentry
->d_name
);
396 inode
->i_ctime
= CURRENT_TIME_SEC
;
397 hfsplus_delete_inode(inode
);
398 mark_inode_dirty(inode
);
400 mutex_unlock(&sbi
->vh_mutex
);
404 static int hfsplus_symlink(struct inode
*dir
, struct dentry
*dentry
,
407 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
411 mutex_lock(&sbi
->vh_mutex
);
412 inode
= hfsplus_new_inode(dir
->i_sb
, S_IFLNK
| S_IRWXUGO
);
416 res
= page_symlink(inode
, symname
, strlen(symname
) + 1);
420 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
424 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
425 mark_inode_dirty(inode
);
430 hfsplus_delete_inode(inode
);
433 mutex_unlock(&sbi
->vh_mutex
);
437 static int hfsplus_mknod(struct inode
*dir
, struct dentry
*dentry
,
438 umode_t mode
, dev_t rdev
)
440 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
444 mutex_lock(&sbi
->vh_mutex
);
445 inode
= hfsplus_new_inode(dir
->i_sb
, mode
);
449 if (S_ISBLK(mode
) || S_ISCHR(mode
) || S_ISFIFO(mode
) || S_ISSOCK(mode
))
450 init_special_inode(inode
, mode
, rdev
);
452 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
455 hfsplus_delete_inode(inode
);
460 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
461 mark_inode_dirty(inode
);
463 mutex_unlock(&sbi
->vh_mutex
);
467 static int hfsplus_create(struct inode
*dir
, struct dentry
*dentry
, umode_t mode
,
468 struct nameidata
*nd
)
470 return hfsplus_mknod(dir
, dentry
, mode
, 0);
473 static int hfsplus_mkdir(struct inode
*dir
, struct dentry
*dentry
, umode_t mode
)
475 return hfsplus_mknod(dir
, dentry
, mode
| S_IFDIR
, 0);
478 static int hfsplus_rename(struct inode
*old_dir
, struct dentry
*old_dentry
,
479 struct inode
*new_dir
, struct dentry
*new_dentry
)
483 /* Unlink destination if it already exists */
484 if (new_dentry
->d_inode
) {
485 if (S_ISDIR(new_dentry
->d_inode
->i_mode
))
486 res
= hfsplus_rmdir(new_dir
, new_dentry
);
488 res
= hfsplus_unlink(new_dir
, new_dentry
);
493 res
= hfsplus_rename_cat((u32
)(unsigned long)old_dentry
->d_fsdata
,
494 old_dir
, &old_dentry
->d_name
,
495 new_dir
, &new_dentry
->d_name
);
497 new_dentry
->d_fsdata
= old_dentry
->d_fsdata
;
501 const struct inode_operations hfsplus_dir_inode_operations
= {
502 .lookup
= hfsplus_lookup
,
503 .create
= hfsplus_create
,
504 .link
= hfsplus_link
,
505 .unlink
= hfsplus_unlink
,
506 .mkdir
= hfsplus_mkdir
,
507 .rmdir
= hfsplus_rmdir
,
508 .symlink
= hfsplus_symlink
,
509 .mknod
= hfsplus_mknod
,
510 .rename
= hfsplus_rename
,
513 const struct file_operations hfsplus_dir_operations
= {
514 .fsync
= hfsplus_file_fsync
,
515 .read
= generic_read_dir
,
516 .readdir
= hfsplus_readdir
,
517 .unlocked_ioctl
= hfsplus_ioctl
,
518 .llseek
= generic_file_llseek
,
519 .release
= hfsplus_dir_release
,