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
) &&
70 (entry
.file
.create_date
== HFSPLUS_I(HFSPLUS_SB(sb
).hidden_dir
).create_date
||
71 entry
.file
.create_date
== HFSPLUS_I(sb
->s_root
->d_inode
).create_date
) &&
72 HFSPLUS_SB(sb
).hidden_dir
) {
76 if (dentry
->d_fsdata
) {
78 * We found a link pointing to another link,
79 * so ignore it and treat it as regular file.
81 cnid
= (unsigned long)dentry
->d_fsdata
;
84 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
85 linkid
= be32_to_cpu(entry
.file
.permissions
.dev
);
86 str
.len
= sprintf(name
, "iNode%d", linkid
);
88 hfsplus_cat_build_key(sb
, fd
.search_key
, HFSPLUS_SB(sb
).hidden_dir
->i_ino
, &str
);
91 } else if (!dentry
->d_fsdata
)
92 dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
94 printk(KERN_ERR
"hfs: invalid catalog entry type in lookup\n");
99 inode
= iget(dir
->i_sb
, cnid
);
101 return ERR_PTR(-EACCES
);
102 if (S_ISREG(inode
->i_mode
))
103 HFSPLUS_I(inode
).dev
= linkid
;
105 d_add(dentry
, inode
);
112 static int hfsplus_readdir(struct file
*filp
, void *dirent
, filldir_t filldir
)
114 struct inode
*inode
= filp
->f_dentry
->d_inode
;
115 struct super_block
*sb
= inode
->i_sb
;
117 char strbuf
[HFSPLUS_MAX_STRLEN
+ 1];
118 hfsplus_cat_entry entry
;
119 struct hfs_find_data fd
;
120 struct hfsplus_readdir_data
*rd
;
123 if (filp
->f_pos
>= inode
->i_size
)
126 hfs_find_init(HFSPLUS_SB(sb
).cat_tree
, &fd
);
127 hfsplus_cat_build_key(sb
, fd
.search_key
, inode
->i_ino
, NULL
);
128 err
= hfs_brec_find(&fd
);
132 switch ((u32
)filp
->f_pos
) {
134 /* This is completely artificial... */
135 if (filldir(dirent
, ".", 1, 0, inode
->i_ino
, DT_DIR
))
140 hfs_bnode_read(fd
.bnode
, &entry
, fd
.entryoffset
, fd
.entrylength
);
141 if (be16_to_cpu(entry
.type
) != HFSPLUS_FOLDER_THREAD
) {
142 printk(KERN_ERR
"hfs: bad catalog folder thread\n");
146 if (fd
.entrylength
< HFSPLUS_MIN_THREAD_SZ
) {
147 printk(KERN_ERR
"hfs: truncated catalog thread\n");
151 if (filldir(dirent
, "..", 2, 1,
152 be32_to_cpu(entry
.thread
.parentID
), DT_DIR
))
157 if (filp
->f_pos
>= inode
->i_size
)
159 err
= hfs_brec_goto(&fd
, filp
->f_pos
- 1);
165 if (be32_to_cpu(fd
.key
->cat
.parent
) != inode
->i_ino
) {
166 printk(KERN_ERR
"hfs: walked past end of dir\n");
170 hfs_bnode_read(fd
.bnode
, &entry
, fd
.entryoffset
, fd
.entrylength
);
171 type
= be16_to_cpu(entry
.type
);
172 len
= HFSPLUS_MAX_STRLEN
;
173 err
= hfsplus_uni2asc(sb
, &fd
.key
->cat
.name
, strbuf
, &len
);
176 if (type
== HFSPLUS_FOLDER
) {
177 if (fd
.entrylength
< sizeof(struct hfsplus_cat_folder
)) {
178 printk(KERN_ERR
"hfs: small dir entry\n");
182 if (HFSPLUS_SB(sb
).hidden_dir
&&
183 HFSPLUS_SB(sb
).hidden_dir
->i_ino
== be32_to_cpu(entry
.folder
.id
))
185 if (filldir(dirent
, strbuf
, len
, filp
->f_pos
,
186 be32_to_cpu(entry
.folder
.id
), DT_DIR
))
188 } else if (type
== HFSPLUS_FILE
) {
189 if (fd
.entrylength
< sizeof(struct hfsplus_cat_file
)) {
190 printk(KERN_ERR
"hfs: small file entry\n");
194 if (filldir(dirent
, strbuf
, len
, filp
->f_pos
,
195 be32_to_cpu(entry
.file
.id
), DT_REG
))
198 printk(KERN_ERR
"hfs: bad catalog entry type\n");
204 if (filp
->f_pos
>= inode
->i_size
)
206 err
= hfs_brec_goto(&fd
, 1);
210 rd
= filp
->private_data
;
212 rd
= kmalloc(sizeof(struct hfsplus_readdir_data
), GFP_KERNEL
);
217 filp
->private_data
= rd
;
219 list_add(&rd
->list
, &HFSPLUS_I(inode
).open_dir_list
);
221 memcpy(&rd
->key
, fd
.key
, sizeof(struct hfsplus_cat_key
));
227 static int hfsplus_dir_release(struct inode
*inode
, struct file
*file
)
229 struct hfsplus_readdir_data
*rd
= file
->private_data
;
237 static int hfsplus_create(struct inode
*dir
, struct dentry
*dentry
, int mode
,
238 struct nameidata
*nd
)
243 inode
= hfsplus_new_inode(dir
->i_sb
, mode
);
247 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
250 hfsplus_delete_inode(inode
);
254 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
255 mark_inode_dirty(inode
);
259 static int hfsplus_link(struct dentry
*src_dentry
, struct inode
*dst_dir
,
260 struct dentry
*dst_dentry
)
262 struct super_block
*sb
= dst_dir
->i_sb
;
263 struct inode
*inode
= src_dentry
->d_inode
;
264 struct inode
*src_dir
= src_dentry
->d_parent
->d_inode
;
270 if (HFSPLUS_IS_RSRC(inode
))
273 if (inode
->i_ino
== (u32
)(unsigned long)src_dentry
->d_fsdata
) {
275 get_random_bytes(&id
, sizeof(cnid
));
278 str
.len
= sprintf(name
, "iNode%d", id
);
279 res
= hfsplus_rename_cat(inode
->i_ino
,
280 src_dir
, &src_dentry
->d_name
,
281 HFSPLUS_SB(sb
).hidden_dir
, &str
);
287 HFSPLUS_I(inode
).dev
= id
;
288 cnid
= HFSPLUS_SB(sb
).next_cnid
++;
289 src_dentry
->d_fsdata
= (void *)(unsigned long)cnid
;
290 res
= hfsplus_create_cat(cnid
, src_dir
, &src_dentry
->d_name
, inode
);
294 HFSPLUS_SB(sb
).file_count
++;
296 cnid
= HFSPLUS_SB(sb
).next_cnid
++;
297 res
= hfsplus_create_cat(cnid
, dst_dir
, &dst_dentry
->d_name
, inode
);
302 hfsplus_instantiate(dst_dentry
, inode
, cnid
);
303 atomic_inc(&inode
->i_count
);
304 inode
->i_ctime
= CURRENT_TIME_SEC
;
305 mark_inode_dirty(inode
);
306 HFSPLUS_SB(sb
).file_count
++;
312 static int hfsplus_unlink(struct inode
*dir
, struct dentry
*dentry
)
314 struct super_block
*sb
= dir
->i_sb
;
315 struct inode
*inode
= dentry
->d_inode
;
321 if (HFSPLUS_IS_RSRC(inode
))
324 cnid
= (u32
)(unsigned long)dentry
->d_fsdata
;
325 if (inode
->i_ino
== cnid
&&
326 atomic_read(&HFSPLUS_I(inode
).opencnt
)) {
328 str
.len
= sprintf(name
, "temp%lu", inode
->i_ino
);
329 res
= hfsplus_rename_cat(inode
->i_ino
,
330 dir
, &dentry
->d_name
,
331 HFSPLUS_SB(sb
).hidden_dir
, &str
);
333 inode
->i_flags
|= S_DEAD
;
336 res
= hfsplus_delete_cat(cnid
, dir
, &dentry
->d_name
);
340 if (inode
->i_nlink
> 0)
342 hfsplus_delete_inode(inode
);
343 if (inode
->i_ino
!= cnid
&& !inode
->i_nlink
) {
344 if (!atomic_read(&HFSPLUS_I(inode
).opencnt
)) {
345 res
= hfsplus_delete_cat(inode
->i_ino
, HFSPLUS_SB(sb
).hidden_dir
, NULL
);
347 hfsplus_delete_inode(inode
);
349 inode
->i_flags
|= S_DEAD
;
352 inode
->i_ctime
= CURRENT_TIME_SEC
;
353 mark_inode_dirty(inode
);
358 static int hfsplus_mkdir(struct inode
*dir
, struct dentry
*dentry
, int mode
)
363 inode
= hfsplus_new_inode(dir
->i_sb
, S_IFDIR
| mode
);
367 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
370 hfsplus_delete_inode(inode
);
374 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
375 mark_inode_dirty(inode
);
379 static int hfsplus_rmdir(struct inode
*dir
, struct dentry
*dentry
)
384 inode
= dentry
->d_inode
;
385 if (inode
->i_size
!= 2)
387 res
= hfsplus_delete_cat(inode
->i_ino
, dir
, &dentry
->d_name
);
391 inode
->i_ctime
= CURRENT_TIME_SEC
;
392 hfsplus_delete_inode(inode
);
393 mark_inode_dirty(inode
);
397 static int hfsplus_symlink(struct inode
*dir
, struct dentry
*dentry
,
400 struct super_block
*sb
;
405 inode
= hfsplus_new_inode(sb
, S_IFLNK
| S_IRWXUGO
);
409 res
= page_symlink(inode
, symname
, strlen(symname
) + 1);
412 hfsplus_delete_inode(inode
);
417 mark_inode_dirty(inode
);
418 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
421 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
422 mark_inode_dirty(inode
);
428 static int hfsplus_mknod(struct inode
*dir
, struct dentry
*dentry
,
429 int mode
, dev_t rdev
)
431 struct super_block
*sb
;
436 inode
= hfsplus_new_inode(sb
, mode
);
440 res
= hfsplus_create_cat(inode
->i_ino
, dir
, &dentry
->d_name
, inode
);
443 hfsplus_delete_inode(inode
);
447 init_special_inode(inode
, mode
, rdev
);
448 hfsplus_instantiate(dentry
, inode
, inode
->i_ino
);
449 mark_inode_dirty(inode
);
454 static int hfsplus_rename(struct inode
*old_dir
, struct dentry
*old_dentry
,
455 struct inode
*new_dir
, struct dentry
*new_dentry
)
459 /* Unlink destination if it already exists */
460 if (new_dentry
->d_inode
) {
461 res
= hfsplus_unlink(new_dir
, new_dentry
);
466 res
= hfsplus_rename_cat((u32
)(unsigned long)old_dentry
->d_fsdata
,
467 old_dir
, &old_dentry
->d_name
,
468 new_dir
, &new_dentry
->d_name
);
470 new_dentry
->d_fsdata
= old_dentry
->d_fsdata
;
474 struct inode_operations hfsplus_dir_inode_operations
= {
475 .lookup
= hfsplus_lookup
,
476 .create
= hfsplus_create
,
477 .link
= hfsplus_link
,
478 .unlink
= hfsplus_unlink
,
479 .mkdir
= hfsplus_mkdir
,
480 .rmdir
= hfsplus_rmdir
,
481 .symlink
= hfsplus_symlink
,
482 .mknod
= hfsplus_mknod
,
483 .rename
= hfsplus_rename
,
486 const struct file_operations hfsplus_dir_operations
= {
487 .read
= generic_read_dir
,
488 .readdir
= hfsplus_readdir
,
489 .ioctl
= hfsplus_ioctl
,
490 .llseek
= generic_file_llseek
,
491 .release
= hfsplus_dir_release
,