1 // SPDX-License-Identifier: GPL-2.0
3 * linux/fs/hfsplus/dir.c
6 * Brad Boyer (flar@allandria.com)
7 * (C) 2003 Ardis Technologies <roman@ardistech.com>
9 * Handling of directories
12 #include <linux/errno.h>
14 #include <linux/slab.h>
15 #include <linux/random.h>
16 #include <linux/nls.h>
18 #include "hfsplus_fs.h"
19 #include "hfsplus_raw.h"
22 static inline void hfsplus_instantiate(struct dentry
*dentry
,
23 struct inode
*inode
, u32 cnid
)
25 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
26 d_instantiate(dentry
, inode
);
29 /* Find the entry inside dir named dentry->d_name */
30 static struct dentry
*hfsplus_lookup(struct inode
*dir
, struct dentry
*dentry
,
33 struct inode
*inode
= NULL
;
34 struct hfs_find_data fd
;
35 struct super_block
*sb
;
36 hfsplus_cat_entry entry
;
43 dentry
->d_fsdata
= NULL
;
44 err
= hfs_find_init(HFSPLUS_SB(sb
)->cat_tree
, &fd
);
47 err
= hfsplus_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
,
49 if (unlikely(err
< 0))
52 err
= hfs_brec_read(&fd
, &entry
, sizeof(entry
));
62 type
= be16_to_cpu(entry
.type
);
63 if (type
== HFSPLUS_FOLDER
) {
64 if (fd
.entrylength
< sizeof(struct hfsplus_cat_folder
)) {
68 cnid
= be32_to_cpu(entry
.folder
.id
);
69 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
70 } else if (type
== HFSPLUS_FILE
) {
71 if (fd
.entrylength
< sizeof(struct hfsplus_cat_file
)) {
75 cnid
= be32_to_cpu(entry
.file
.id
);
76 if (entry
.file
.user_info
.fdType
==
77 cpu_to_be32(HFSP_HARDLINK_TYPE
) &&
78 entry
.file
.user_info
.fdCreator
==
79 cpu_to_be32(HFSP_HFSPLUS_CREATOR
) &&
80 HFSPLUS_SB(sb
)->hidden_dir
&&
81 (entry
.file
.create_date
==
82 HFSPLUS_I(HFSPLUS_SB(sb
)->hidden_dir
)->
84 entry
.file
.create_date
==
85 HFSPLUS_I(d_inode(sb
->s_root
))->
90 if (dentry
->d_fsdata
) {
92 * We found a link pointing to another link,
93 * so ignore it and treat it as regular file.
95 cnid
= (unsigned long)dentry
->d_fsdata
;
98 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
100 be32_to_cpu(entry
.file
.permissions
.dev
);
101 str
.len
= sprintf(name
, "iNode%d", linkid
);
103 err
= hfsplus_cat_build_key(sb
, fd
.search_key
,
104 HFSPLUS_SB(sb
)->hidden_dir
->i_ino
,
106 if (unlikely(err
< 0))
110 } else if (!dentry
->d_fsdata
)
111 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
113 pr_err("invalid catalog entry type in lookup\n");
118 inode
= hfsplus_iget(dir
->i_sb
, cnid
);
120 return ERR_CAST(inode
);
121 if (S_ISREG(inode
->i_mode
))
122 HFSPLUS_I(inode
)->linkid
= linkid
;
124 return d_splice_alias(inode
, dentry
);
130 static int hfsplus_readdir(struct file
*file
, struct dir_context
*ctx
)
132 struct inode
*inode
= file_inode(file
);
133 struct super_block
*sb
= inode
->i_sb
;
136 hfsplus_cat_entry entry
;
137 struct hfs_find_data fd
;
138 struct hfsplus_readdir_data
*rd
;
141 if (file
->f_pos
>= inode
->i_size
)
144 err
= hfs_find_init(HFSPLUS_SB(sb
)->cat_tree
, &fd
);
147 strbuf
= kmalloc(NLS_MAX_CHARSET_SIZE
* HFSPLUS_MAX_STRLEN
+ 1, GFP_KERNEL
);
152 hfsplus_cat_build_key_with_cnid(sb
, fd
.search_key
, inode
->i_ino
);
153 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
158 /* This is completely artificial... */
159 if (!dir_emit_dot(file
, ctx
))
164 if (fd
.entrylength
> sizeof(entry
) || fd
.entrylength
< 0) {
169 hfs_bnode_read(fd
.bnode
, &entry
, fd
.entryoffset
,
171 if (be16_to_cpu(entry
.type
) != HFSPLUS_FOLDER_THREAD
) {
172 pr_err("bad catalog folder thread\n");
176 if (fd
.entrylength
< HFSPLUS_MIN_THREAD_SZ
) {
177 pr_err("truncated catalog thread\n");
181 if (!dir_emit(ctx
, "..", 2,
182 be32_to_cpu(entry
.thread
.parentID
), DT_DIR
))
186 if (ctx
->pos
>= inode
->i_size
)
188 err
= hfs_brec_goto(&fd
, ctx
->pos
- 1);
192 if (be32_to_cpu(fd
.key
->cat
.parent
) != inode
->i_ino
) {
193 pr_err("walked past end of dir\n");
198 if (fd
.entrylength
> sizeof(entry
) || fd
.entrylength
< 0) {
203 hfs_bnode_read(fd
.bnode
, &entry
, fd
.entryoffset
,
205 type
= be16_to_cpu(entry
.type
);
206 len
= NLS_MAX_CHARSET_SIZE
* HFSPLUS_MAX_STRLEN
;
207 err
= hfsplus_uni2asc(sb
, &fd
.key
->cat
.name
, strbuf
, &len
);
210 if (type
== HFSPLUS_FOLDER
) {
212 sizeof(struct hfsplus_cat_folder
)) {
213 pr_err("small dir entry\n");
217 if (HFSPLUS_SB(sb
)->hidden_dir
&&
218 HFSPLUS_SB(sb
)->hidden_dir
->i_ino
==
219 be32_to_cpu(entry
.folder
.id
))
221 if (!dir_emit(ctx
, strbuf
, len
,
222 be32_to_cpu(entry
.folder
.id
), DT_DIR
))
224 } else if (type
== HFSPLUS_FILE
) {
226 unsigned type
= DT_UNKNOWN
;
228 if (fd
.entrylength
< sizeof(struct hfsplus_cat_file
)) {
229 pr_err("small file entry\n");
234 mode
= be16_to_cpu(entry
.file
.permissions
.mode
);
237 else if (S_ISLNK(mode
))
239 else if (S_ISFIFO(mode
))
241 else if (S_ISCHR(mode
))
243 else if (S_ISBLK(mode
))
245 else if (S_ISSOCK(mode
))
248 if (!dir_emit(ctx
, strbuf
, len
,
249 be32_to_cpu(entry
.file
.id
), type
))
252 pr_err("bad catalog entry type\n");
258 if (ctx
->pos
>= inode
->i_size
)
260 err
= hfs_brec_goto(&fd
, 1);
264 rd
= file
->private_data
;
266 rd
= kmalloc(sizeof(struct hfsplus_readdir_data
), GFP_KERNEL
);
271 file
->private_data
= rd
;
273 spin_lock(&HFSPLUS_I(inode
)->open_dir_lock
);
274 list_add(&rd
->list
, &HFSPLUS_I(inode
)->open_dir_list
);
275 spin_unlock(&HFSPLUS_I(inode
)->open_dir_lock
);
278 * Can be done after the list insertion; exclusion with
279 * hfsplus_delete_cat() is provided by directory lock.
281 memcpy(&rd
->key
, fd
.key
, sizeof(struct hfsplus_cat_key
));
288 static int hfsplus_dir_release(struct inode
*inode
, struct file
*file
)
290 struct hfsplus_readdir_data
*rd
= file
->private_data
;
292 spin_lock(&HFSPLUS_I(inode
)->open_dir_lock
);
294 spin_unlock(&HFSPLUS_I(inode
)->open_dir_lock
);
300 static int hfsplus_link(struct dentry
*src_dentry
, struct inode
*dst_dir
,
301 struct dentry
*dst_dentry
)
303 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dst_dir
->i_sb
);
304 struct inode
*inode
= d_inode(src_dentry
);
305 struct inode
*src_dir
= d_inode(src_dentry
->d_parent
);
311 if (HFSPLUS_IS_RSRC(inode
))
313 if (!S_ISREG(inode
->i_mode
))
316 mutex_lock(&sbi
->vh_mutex
);
317 if (inode
->i_ino
== (u32
)(unsigned long)src_dentry
->d_fsdata
) {
319 get_random_bytes(&id
, sizeof(cnid
));
322 str
.len
= sprintf(name
, "iNode%d", id
);
323 res
= hfsplus_rename_cat(inode
->i_ino
,
324 src_dir
, &src_dentry
->d_name
,
325 sbi
->hidden_dir
, &str
);
331 HFSPLUS_I(inode
)->linkid
= id
;
332 cnid
= sbi
->next_cnid
++;
333 src_dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
334 res
= hfsplus_create_cat(cnid
, src_dir
,
335 &src_dentry
->d_name
, inode
);
341 cnid
= sbi
->next_cnid
++;
342 res
= hfsplus_create_cat(cnid
, dst_dir
, &dst_dentry
->d_name
, inode
);
347 hfsplus_instantiate(dst_dentry
, inode
, cnid
);
349 inode_set_ctime_current(inode
);
350 mark_inode_dirty(inode
);
352 hfsplus_mark_mdb_dirty(dst_dir
->i_sb
);
354 mutex_unlock(&sbi
->vh_mutex
);
358 static int hfsplus_unlink(struct inode
*dir
, struct dentry
*dentry
)
360 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
361 struct inode
*inode
= d_inode(dentry
);
367 if (HFSPLUS_IS_RSRC(inode
))
370 mutex_lock(&sbi
->vh_mutex
);
371 cnid
= (u32
)(unsigned long)dentry
->d_fsdata
;
372 if (inode
->i_ino
== cnid
&&
373 atomic_read(&HFSPLUS_I(inode
)->opencnt
)) {
375 str
.len
= sprintf(name
, "temp%lu", inode
->i_ino
);
376 res
= hfsplus_rename_cat(inode
->i_ino
,
377 dir
, &dentry
->d_name
,
378 sbi
->hidden_dir
, &str
);
380 inode
->i_flags
|= S_DEAD
;
385 res
= hfsplus_delete_cat(cnid
, dir
, &dentry
->d_name
);
389 if (inode
->i_nlink
> 0)
391 if (inode
->i_ino
== cnid
)
393 if (!inode
->i_nlink
) {
394 if (inode
->i_ino
!= cnid
) {
396 if (!atomic_read(&HFSPLUS_I(inode
)->opencnt
)) {
397 res
= hfsplus_delete_cat(inode
->i_ino
,
401 hfsplus_delete_inode(inode
);
403 inode
->i_flags
|= S_DEAD
;
405 hfsplus_delete_inode(inode
);
408 inode_set_ctime_current(inode
);
409 mark_inode_dirty(inode
);
411 mutex_unlock(&sbi
->vh_mutex
);
415 static int hfsplus_rmdir(struct inode
*dir
, struct dentry
*dentry
)
417 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
418 struct inode
*inode
= d_inode(dentry
);
421 if (inode
->i_size
!= 2)
424 mutex_lock(&sbi
->vh_mutex
);
425 res
= hfsplus_delete_cat(inode
->i_ino
, dir
, &dentry
->d_name
);
429 inode_set_ctime_current(inode
);
430 hfsplus_delete_inode(inode
);
431 mark_inode_dirty(inode
);
433 mutex_unlock(&sbi
->vh_mutex
);
437 static int hfsplus_symlink(struct mnt_idmap
*idmap
, struct inode
*dir
,
438 struct dentry
*dentry
, const char *symname
)
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
, dir
, S_IFLNK
| S_IRWXUGO
);
449 res
= page_symlink(inode
, symname
, strlen(symname
) + 1);
453 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
457 res
= hfsplus_init_security(inode
, dir
, &dentry
->d_name
);
458 if (res
== -EOPNOTSUPP
)
459 res
= 0; /* Operation is not supported. */
461 /* Try to delete anyway without error analysis. */
462 hfsplus_delete_cat(inode
->i_ino
, dir
, &dentry
->d_name
);
466 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
467 mark_inode_dirty(inode
);
472 hfsplus_delete_inode(inode
);
475 mutex_unlock(&sbi
->vh_mutex
);
479 static int hfsplus_mknod(struct mnt_idmap
*idmap
, struct inode
*dir
,
480 struct dentry
*dentry
, umode_t mode
, dev_t rdev
)
482 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
486 mutex_lock(&sbi
->vh_mutex
);
487 inode
= hfsplus_new_inode(dir
->i_sb
, dir
, mode
);
491 if (S_ISBLK(mode
) || S_ISCHR(mode
) || S_ISFIFO(mode
) || S_ISSOCK(mode
))
492 init_special_inode(inode
, mode
, rdev
);
494 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
498 res
= hfsplus_init_security(inode
, dir
, &dentry
->d_name
);
499 if (res
== -EOPNOTSUPP
)
500 res
= 0; /* Operation is not supported. */
502 /* Try to delete anyway without error analysis. */
503 hfsplus_delete_cat(inode
->i_ino
, dir
, &dentry
->d_name
);
507 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
508 mark_inode_dirty(inode
);
513 hfsplus_delete_inode(inode
);
516 mutex_unlock(&sbi
->vh_mutex
);
520 static int hfsplus_create(struct mnt_idmap
*idmap
, struct inode
*dir
,
521 struct dentry
*dentry
, umode_t mode
, bool excl
)
523 return hfsplus_mknod(&nop_mnt_idmap
, dir
, dentry
, mode
, 0);
526 static int hfsplus_mkdir(struct mnt_idmap
*idmap
, struct inode
*dir
,
527 struct dentry
*dentry
, umode_t mode
)
529 return hfsplus_mknod(&nop_mnt_idmap
, dir
, dentry
, mode
| S_IFDIR
, 0);
532 static int hfsplus_rename(struct mnt_idmap
*idmap
,
533 struct inode
*old_dir
, struct dentry
*old_dentry
,
534 struct inode
*new_dir
, struct dentry
*new_dentry
,
539 if (flags
& ~RENAME_NOREPLACE
)
542 /* Unlink destination if it already exists */
543 if (d_really_is_positive(new_dentry
)) {
544 if (d_is_dir(new_dentry
))
545 res
= hfsplus_rmdir(new_dir
, new_dentry
);
547 res
= hfsplus_unlink(new_dir
, new_dentry
);
552 res
= hfsplus_rename_cat((u32
)(unsigned long)old_dentry
->d_fsdata
,
553 old_dir
, &old_dentry
->d_name
,
554 new_dir
, &new_dentry
->d_name
);
556 new_dentry
->d_fsdata
= old_dentry
->d_fsdata
;
560 const struct inode_operations hfsplus_dir_inode_operations
= {
561 .lookup
= hfsplus_lookup
,
562 .create
= hfsplus_create
,
563 .link
= hfsplus_link
,
564 .unlink
= hfsplus_unlink
,
565 .mkdir
= hfsplus_mkdir
,
566 .rmdir
= hfsplus_rmdir
,
567 .symlink
= hfsplus_symlink
,
568 .mknod
= hfsplus_mknod
,
569 .rename
= hfsplus_rename
,
570 .getattr
= hfsplus_getattr
,
571 .listxattr
= hfsplus_listxattr
,
572 .fileattr_get
= hfsplus_fileattr_get
,
573 .fileattr_set
= hfsplus_fileattr_set
,
576 const struct file_operations hfsplus_dir_operations
= {
577 .fsync
= hfsplus_file_fsync
,
578 .read
= generic_read_dir
,
579 .iterate_shared
= hfsplus_readdir
,
580 .unlocked_ioctl
= hfsplus_ioctl
,
581 .llseek
= generic_file_llseek
,
582 .release
= hfsplus_dir_release
,