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"
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
;
41 dentry
->d_fsdata
= NULL
;
42 err
= hfs_find_init(HFSPLUS_SB(sb
)->cat_tree
, &fd
);
45 hfsplus_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
, &dentry
->d_name
);
47 err
= hfs_brec_read(&fd
, &entry
, sizeof(entry
));
57 type
= be16_to_cpu(entry
.type
);
58 if (type
== HFSPLUS_FOLDER
) {
59 if (fd
.entrylength
< sizeof(struct hfsplus_cat_folder
)) {
63 cnid
= be32_to_cpu(entry
.folder
.id
);
64 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
65 } else if (type
== HFSPLUS_FILE
) {
66 if (fd
.entrylength
< sizeof(struct hfsplus_cat_file
)) {
70 cnid
= be32_to_cpu(entry
.file
.id
);
71 if (entry
.file
.user_info
.fdType
==
72 cpu_to_be32(HFSP_HARDLINK_TYPE
) &&
73 entry
.file
.user_info
.fdCreator
==
74 cpu_to_be32(HFSP_HFSPLUS_CREATOR
) &&
75 (entry
.file
.create_date
==
76 HFSPLUS_I(HFSPLUS_SB(sb
)->hidden_dir
)->
78 entry
.file
.create_date
==
79 HFSPLUS_I(sb
->s_root
->d_inode
)->
81 HFSPLUS_SB(sb
)->hidden_dir
) {
85 if (dentry
->d_fsdata
) {
87 * We found a link pointing to another link,
88 * so ignore it and treat it as regular file.
90 cnid
= (unsigned long)dentry
->d_fsdata
;
93 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
95 be32_to_cpu(entry
.file
.permissions
.dev
);
96 str
.len
= sprintf(name
, "iNode%d", linkid
);
98 hfsplus_cat_build_key(sb
, fd
.search_key
,
99 HFSPLUS_SB(sb
)->hidden_dir
->i_ino
,
103 } else if (!dentry
->d_fsdata
)
104 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
106 pr_err("invalid catalog entry type in lookup\n");
111 inode
= hfsplus_iget(dir
->i_sb
, cnid
);
113 return ERR_CAST(inode
);
114 if (S_ISREG(inode
->i_mode
))
115 HFSPLUS_I(inode
)->linkid
= linkid
;
117 d_add(dentry
, inode
);
124 static int hfsplus_readdir(struct file
*file
, struct dir_context
*ctx
)
126 struct inode
*inode
= file_inode(file
);
127 struct super_block
*sb
= inode
->i_sb
;
129 char strbuf
[HFSPLUS_MAX_STRLEN
+ 1];
130 hfsplus_cat_entry entry
;
131 struct hfs_find_data fd
;
132 struct hfsplus_readdir_data
*rd
;
135 if (file
->f_pos
>= inode
->i_size
)
138 err
= hfs_find_init(HFSPLUS_SB(sb
)->cat_tree
, &fd
);
141 hfsplus_cat_build_key(sb
, fd
.search_key
, inode
->i_ino
, NULL
);
142 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
147 /* This is completely artificial... */
148 if (!dir_emit_dot(file
, ctx
))
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 pr_err("bad catalog folder thread\n");
165 if (fd
.entrylength
< HFSPLUS_MIN_THREAD_SZ
) {
166 pr_err("truncated catalog thread\n");
170 if (!dir_emit(ctx
, "..", 2,
171 be32_to_cpu(entry
.thread
.parentID
), DT_DIR
))
175 if (ctx
->pos
>= inode
->i_size
)
177 err
= hfs_brec_goto(&fd
, ctx
->pos
- 1);
181 if (be32_to_cpu(fd
.key
->cat
.parent
) != inode
->i_ino
) {
182 pr_err("walked past end of dir\n");
187 if (fd
.entrylength
> sizeof(entry
) || fd
.entrylength
< 0) {
192 hfs_bnode_read(fd
.bnode
, &entry
, fd
.entryoffset
,
194 type
= be16_to_cpu(entry
.type
);
195 len
= HFSPLUS_MAX_STRLEN
;
196 err
= hfsplus_uni2asc(sb
, &fd
.key
->cat
.name
, strbuf
, &len
);
199 if (type
== HFSPLUS_FOLDER
) {
201 sizeof(struct hfsplus_cat_folder
)) {
202 pr_err("small dir entry\n");
206 if (HFSPLUS_SB(sb
)->hidden_dir
&&
207 HFSPLUS_SB(sb
)->hidden_dir
->i_ino
==
208 be32_to_cpu(entry
.folder
.id
))
210 if (!dir_emit(ctx
, strbuf
, len
,
211 be32_to_cpu(entry
.folder
.id
), DT_DIR
))
213 } else if (type
== HFSPLUS_FILE
) {
214 if (fd
.entrylength
< sizeof(struct hfsplus_cat_file
)) {
215 pr_err("small file entry\n");
219 if (!dir_emit(ctx
, strbuf
, len
,
220 be32_to_cpu(entry
.file
.id
), DT_REG
))
223 pr_err("bad catalog entry type\n");
229 if (ctx
->pos
>= inode
->i_size
)
231 err
= hfs_brec_goto(&fd
, 1);
235 rd
= file
->private_data
;
237 rd
= kmalloc(sizeof(struct hfsplus_readdir_data
), GFP_KERNEL
);
242 file
->private_data
= rd
;
244 list_add(&rd
->list
, &HFSPLUS_I(inode
)->open_dir_list
);
246 memcpy(&rd
->key
, fd
.key
, sizeof(struct hfsplus_cat_key
));
252 static int hfsplus_dir_release(struct inode
*inode
, struct file
*file
)
254 struct hfsplus_readdir_data
*rd
= file
->private_data
;
256 mutex_lock(&inode
->i_mutex
);
258 mutex_unlock(&inode
->i_mutex
);
264 static int hfsplus_link(struct dentry
*src_dentry
, struct inode
*dst_dir
,
265 struct dentry
*dst_dentry
)
267 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dst_dir
->i_sb
);
268 struct inode
*inode
= src_dentry
->d_inode
;
269 struct inode
*src_dir
= src_dentry
->d_parent
->d_inode
;
275 if (HFSPLUS_IS_RSRC(inode
))
277 if (!S_ISREG(inode
->i_mode
))
280 mutex_lock(&sbi
->vh_mutex
);
281 if (inode
->i_ino
== (u32
)(unsigned long)src_dentry
->d_fsdata
) {
283 get_random_bytes(&id
, sizeof(cnid
));
286 str
.len
= sprintf(name
, "iNode%d", id
);
287 res
= hfsplus_rename_cat(inode
->i_ino
,
288 src_dir
, &src_dentry
->d_name
,
289 sbi
->hidden_dir
, &str
);
295 HFSPLUS_I(inode
)->linkid
= id
;
296 cnid
= sbi
->next_cnid
++;
297 src_dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
298 res
= hfsplus_create_cat(cnid
, src_dir
,
299 &src_dentry
->d_name
, inode
);
305 cnid
= sbi
->next_cnid
++;
306 res
= hfsplus_create_cat(cnid
, dst_dir
, &dst_dentry
->d_name
, inode
);
311 hfsplus_instantiate(dst_dentry
, inode
, cnid
);
313 inode
->i_ctime
= CURRENT_TIME_SEC
;
314 mark_inode_dirty(inode
);
316 hfsplus_mark_mdb_dirty(dst_dir
->i_sb
);
318 mutex_unlock(&sbi
->vh_mutex
);
322 static int hfsplus_unlink(struct inode
*dir
, struct dentry
*dentry
)
324 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
325 struct inode
*inode
= dentry
->d_inode
;
331 if (HFSPLUS_IS_RSRC(inode
))
334 mutex_lock(&sbi
->vh_mutex
);
335 cnid
= (u32
)(unsigned long)dentry
->d_fsdata
;
336 if (inode
->i_ino
== cnid
&&
337 atomic_read(&HFSPLUS_I(inode
)->opencnt
)) {
339 str
.len
= sprintf(name
, "temp%lu", inode
->i_ino
);
340 res
= hfsplus_rename_cat(inode
->i_ino
,
341 dir
, &dentry
->d_name
,
342 sbi
->hidden_dir
, &str
);
344 inode
->i_flags
|= S_DEAD
;
349 res
= hfsplus_delete_cat(cnid
, dir
, &dentry
->d_name
);
353 if (inode
->i_nlink
> 0)
355 if (inode
->i_ino
== cnid
)
357 if (!inode
->i_nlink
) {
358 if (inode
->i_ino
!= cnid
) {
360 if (!atomic_read(&HFSPLUS_I(inode
)->opencnt
)) {
361 res
= hfsplus_delete_cat(inode
->i_ino
,
365 hfsplus_delete_inode(inode
);
367 inode
->i_flags
|= S_DEAD
;
369 hfsplus_delete_inode(inode
);
372 inode
->i_ctime
= CURRENT_TIME_SEC
;
373 mark_inode_dirty(inode
);
375 mutex_unlock(&sbi
->vh_mutex
);
379 static int hfsplus_rmdir(struct inode
*dir
, struct dentry
*dentry
)
381 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
382 struct inode
*inode
= dentry
->d_inode
;
385 if (inode
->i_size
!= 2)
388 mutex_lock(&sbi
->vh_mutex
);
389 res
= hfsplus_delete_cat(inode
->i_ino
, dir
, &dentry
->d_name
);
393 inode
->i_ctime
= CURRENT_TIME_SEC
;
394 hfsplus_delete_inode(inode
);
395 mark_inode_dirty(inode
);
397 mutex_unlock(&sbi
->vh_mutex
);
401 static int hfsplus_symlink(struct inode
*dir
, struct dentry
*dentry
,
404 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
408 mutex_lock(&sbi
->vh_mutex
);
409 inode
= hfsplus_new_inode(dir
->i_sb
, S_IFLNK
| S_IRWXUGO
);
413 res
= page_symlink(inode
, symname
, strlen(symname
) + 1);
417 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
421 res
= hfsplus_init_inode_security(inode
, dir
, &dentry
->d_name
);
422 if (res
== -EOPNOTSUPP
)
423 res
= 0; /* Operation is not supported. */
425 /* Try to delete anyway without error analysis. */
426 hfsplus_delete_cat(inode
->i_ino
, dir
, &dentry
->d_name
);
430 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
431 mark_inode_dirty(inode
);
436 hfsplus_delete_inode(inode
);
439 mutex_unlock(&sbi
->vh_mutex
);
443 static int hfsplus_mknod(struct inode
*dir
, struct dentry
*dentry
,
444 umode_t mode
, dev_t rdev
)
446 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
450 mutex_lock(&sbi
->vh_mutex
);
451 inode
= hfsplus_new_inode(dir
->i_sb
, mode
);
455 if (S_ISBLK(mode
) || S_ISCHR(mode
) || S_ISFIFO(mode
) || S_ISSOCK(mode
))
456 init_special_inode(inode
, mode
, rdev
);
458 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
462 res
= hfsplus_init_inode_security(inode
, dir
, &dentry
->d_name
);
463 if (res
== -EOPNOTSUPP
)
464 res
= 0; /* Operation is not supported. */
466 /* Try to delete anyway without error analysis. */
467 hfsplus_delete_cat(inode
->i_ino
, dir
, &dentry
->d_name
);
471 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
472 mark_inode_dirty(inode
);
477 hfsplus_delete_inode(inode
);
480 mutex_unlock(&sbi
->vh_mutex
);
484 static int hfsplus_create(struct inode
*dir
, struct dentry
*dentry
, umode_t mode
,
487 return hfsplus_mknod(dir
, dentry
, mode
, 0);
490 static int hfsplus_mkdir(struct inode
*dir
, struct dentry
*dentry
, umode_t mode
)
492 return hfsplus_mknod(dir
, dentry
, mode
| S_IFDIR
, 0);
495 static int hfsplus_rename(struct inode
*old_dir
, struct dentry
*old_dentry
,
496 struct inode
*new_dir
, struct dentry
*new_dentry
)
500 /* Unlink destination if it already exists */
501 if (new_dentry
->d_inode
) {
502 if (S_ISDIR(new_dentry
->d_inode
->i_mode
))
503 res
= hfsplus_rmdir(new_dir
, new_dentry
);
505 res
= hfsplus_unlink(new_dir
, new_dentry
);
510 res
= hfsplus_rename_cat((u32
)(unsigned long)old_dentry
->d_fsdata
,
511 old_dir
, &old_dentry
->d_name
,
512 new_dir
, &new_dentry
->d_name
);
514 new_dentry
->d_fsdata
= old_dentry
->d_fsdata
;
518 const struct inode_operations hfsplus_dir_inode_operations
= {
519 .lookup
= hfsplus_lookup
,
520 .create
= hfsplus_create
,
521 .link
= hfsplus_link
,
522 .unlink
= hfsplus_unlink
,
523 .mkdir
= hfsplus_mkdir
,
524 .rmdir
= hfsplus_rmdir
,
525 .symlink
= hfsplus_symlink
,
526 .mknod
= hfsplus_mknod
,
527 .rename
= hfsplus_rename
,
528 .setxattr
= generic_setxattr
,
529 .getxattr
= generic_getxattr
,
530 .listxattr
= hfsplus_listxattr
,
531 .removexattr
= hfsplus_removexattr
,
534 const struct file_operations hfsplus_dir_operations
= {
535 .fsync
= hfsplus_file_fsync
,
536 .read
= generic_read_dir
,
537 .iterate
= hfsplus_readdir
,
538 .unlocked_ioctl
= hfsplus_ioctl
,
539 .llseek
= generic_file_llseek
,
540 .release
= hfsplus_dir_release
,