1 // SPDX-License-Identifier: GPL-2.0
3 * linux/fs/hfsplus/catalog.c
6 * Brad Boyer (flar@allandria.com)
7 * (C) 2003 Ardis Technologies <roman@ardistech.com>
9 * Handling of catalog records
13 #include "hfsplus_fs.h"
14 #include "hfsplus_raw.h"
16 int hfsplus_cat_case_cmp_key(const hfsplus_btree_key
*k1
,
17 const hfsplus_btree_key
*k2
)
24 return be32_to_cpu(k1p
) < be32_to_cpu(k2p
) ? -1 : 1;
26 return hfsplus_strcasecmp(&k1
->cat
.name
, &k2
->cat
.name
);
29 int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key
*k1
,
30 const hfsplus_btree_key
*k2
)
37 return be32_to_cpu(k1p
) < be32_to_cpu(k2p
) ? -1 : 1;
39 return hfsplus_strcmp(&k1
->cat
.name
, &k2
->cat
.name
);
42 /* Generates key for catalog file/folders record. */
43 int hfsplus_cat_build_key(struct super_block
*sb
,
44 hfsplus_btree_key
*key
, u32 parent
, const struct qstr
*str
)
48 key
->cat
.parent
= cpu_to_be32(parent
);
49 err
= hfsplus_asc2uni(sb
, &key
->cat
.name
, HFSPLUS_MAX_STRLEN
,
51 if (unlikely(err
< 0))
54 len
= be16_to_cpu(key
->cat
.name
.length
);
55 key
->key_len
= cpu_to_be16(6 + 2 * len
);
59 /* Generates key for catalog thread record. */
60 void hfsplus_cat_build_key_with_cnid(struct super_block
*sb
,
61 hfsplus_btree_key
*key
, u32 parent
)
63 key
->cat
.parent
= cpu_to_be32(parent
);
64 key
->cat
.name
.length
= 0;
65 key
->key_len
= cpu_to_be16(6);
68 static void hfsplus_cat_build_key_uni(hfsplus_btree_key
*key
, u32 parent
,
69 struct hfsplus_unistr
*name
)
73 ustrlen
= be16_to_cpu(name
->length
);
74 key
->cat
.parent
= cpu_to_be32(parent
);
75 key
->cat
.name
.length
= cpu_to_be16(ustrlen
);
77 memcpy(key
->cat
.name
.unicode
, name
->unicode
, ustrlen
);
78 key
->key_len
= cpu_to_be16(6 + ustrlen
);
81 void hfsplus_cat_set_perms(struct inode
*inode
, struct hfsplus_perm
*perms
)
83 if (inode
->i_flags
& S_IMMUTABLE
)
84 perms
->rootflags
|= HFSPLUS_FLG_IMMUTABLE
;
86 perms
->rootflags
&= ~HFSPLUS_FLG_IMMUTABLE
;
87 if (inode
->i_flags
& S_APPEND
)
88 perms
->rootflags
|= HFSPLUS_FLG_APPEND
;
90 perms
->rootflags
&= ~HFSPLUS_FLG_APPEND
;
92 perms
->userflags
= HFSPLUS_I(inode
)->userflags
;
93 perms
->mode
= cpu_to_be16(inode
->i_mode
);
94 perms
->owner
= cpu_to_be32(i_uid_read(inode
));
95 perms
->group
= cpu_to_be32(i_gid_read(inode
));
97 if (S_ISREG(inode
->i_mode
))
98 perms
->dev
= cpu_to_be32(inode
->i_nlink
);
99 else if (S_ISBLK(inode
->i_mode
) || S_ISCHR(inode
->i_mode
))
100 perms
->dev
= cpu_to_be32(inode
->i_rdev
);
105 static int hfsplus_cat_build_record(hfsplus_cat_entry
*entry
,
106 u32 cnid
, struct inode
*inode
)
108 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(inode
->i_sb
);
110 if (S_ISDIR(inode
->i_mode
)) {
111 struct hfsplus_cat_folder
*folder
;
113 folder
= &entry
->folder
;
114 memset(folder
, 0, sizeof(*folder
));
115 folder
->type
= cpu_to_be16(HFSPLUS_FOLDER
);
116 if (test_bit(HFSPLUS_SB_HFSX
, &sbi
->flags
))
117 folder
->flags
|= cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT
);
118 folder
->id
= cpu_to_be32(inode
->i_ino
);
119 HFSPLUS_I(inode
)->create_date
=
120 folder
->create_date
=
121 folder
->content_mod_date
=
122 folder
->attribute_mod_date
=
123 folder
->access_date
= hfsp_now2mt();
124 hfsplus_cat_set_perms(inode
, &folder
->permissions
);
125 if (inode
== sbi
->hidden_dir
)
126 /* invisible and namelocked */
127 folder
->user_info
.frFlags
= cpu_to_be16(0x5000);
128 return sizeof(*folder
);
130 struct hfsplus_cat_file
*file
;
133 memset(file
, 0, sizeof(*file
));
134 file
->type
= cpu_to_be16(HFSPLUS_FILE
);
135 file
->flags
= cpu_to_be16(HFSPLUS_FILE_THREAD_EXISTS
);
136 file
->id
= cpu_to_be32(cnid
);
137 HFSPLUS_I(inode
)->create_date
=
139 file
->content_mod_date
=
140 file
->attribute_mod_date
=
141 file
->access_date
= hfsp_now2mt();
142 if (cnid
== inode
->i_ino
) {
143 hfsplus_cat_set_perms(inode
, &file
->permissions
);
144 if (S_ISLNK(inode
->i_mode
)) {
145 file
->user_info
.fdType
=
146 cpu_to_be32(HFSP_SYMLINK_TYPE
);
147 file
->user_info
.fdCreator
=
148 cpu_to_be32(HFSP_SYMLINK_CREATOR
);
150 file
->user_info
.fdType
=
151 cpu_to_be32(sbi
->type
);
152 file
->user_info
.fdCreator
=
153 cpu_to_be32(sbi
->creator
);
155 if (HFSPLUS_FLG_IMMUTABLE
&
156 (file
->permissions
.rootflags
|
157 file
->permissions
.userflags
))
159 cpu_to_be16(HFSPLUS_FILE_LOCKED
);
161 file
->user_info
.fdType
=
162 cpu_to_be32(HFSP_HARDLINK_TYPE
);
163 file
->user_info
.fdCreator
=
164 cpu_to_be32(HFSP_HFSPLUS_CREATOR
);
165 file
->user_info
.fdFlags
=
168 HFSPLUS_I(sbi
->hidden_dir
)->create_date
;
169 file
->permissions
.dev
=
170 cpu_to_be32(HFSPLUS_I(inode
)->linkid
);
172 return sizeof(*file
);
176 static int hfsplus_fill_cat_thread(struct super_block
*sb
,
177 hfsplus_cat_entry
*entry
, int type
,
178 u32 parentid
, const struct qstr
*str
)
182 entry
->type
= cpu_to_be16(type
);
183 entry
->thread
.reserved
= 0;
184 entry
->thread
.parentID
= cpu_to_be32(parentid
);
185 err
= hfsplus_asc2uni(sb
, &entry
->thread
.nodeName
, HFSPLUS_MAX_STRLEN
,
186 str
->name
, str
->len
);
187 if (unlikely(err
< 0))
190 return 10 + be16_to_cpu(entry
->thread
.nodeName
.length
) * 2;
193 /* Try to get a catalog entry for given catalog id */
194 int hfsplus_find_cat(struct super_block
*sb
, u32 cnid
,
195 struct hfs_find_data
*fd
)
197 hfsplus_cat_entry tmp
;
201 hfsplus_cat_build_key_with_cnid(sb
, fd
->search_key
, cnid
);
202 err
= hfs_brec_read(fd
, &tmp
, sizeof(hfsplus_cat_entry
));
206 type
= be16_to_cpu(tmp
.type
);
207 if (type
!= HFSPLUS_FOLDER_THREAD
&& type
!= HFSPLUS_FILE_THREAD
) {
208 pr_err("found bad thread record in catalog\n");
212 if (be16_to_cpu(tmp
.thread
.nodeName
.length
) > 255) {
213 pr_err("catalog name length corrupted\n");
217 hfsplus_cat_build_key_uni(fd
->search_key
,
218 be32_to_cpu(tmp
.thread
.parentID
),
219 &tmp
.thread
.nodeName
);
220 return hfs_brec_find(fd
, hfs_find_rec_by_key
);
223 static void hfsplus_subfolders_inc(struct inode
*dir
)
225 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
227 if (test_bit(HFSPLUS_SB_HFSX
, &sbi
->flags
)) {
229 * Increment subfolder count. Note, the value is only meaningful
230 * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
232 HFSPLUS_I(dir
)->subfolders
++;
236 static void hfsplus_subfolders_dec(struct inode
*dir
)
238 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
240 if (test_bit(HFSPLUS_SB_HFSX
, &sbi
->flags
)) {
242 * Decrement subfolder count. Note, the value is only meaningful
243 * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
245 * Check for zero. Some subfolders may have been created
246 * by an implementation ignorant of this counter.
248 if (HFSPLUS_I(dir
)->subfolders
)
249 HFSPLUS_I(dir
)->subfolders
--;
253 int hfsplus_create_cat(u32 cnid
, struct inode
*dir
,
254 const struct qstr
*str
, struct inode
*inode
)
256 struct super_block
*sb
= dir
->i_sb
;
257 struct hfs_find_data fd
;
258 hfsplus_cat_entry entry
;
262 hfs_dbg(CAT_MOD
, "create_cat: %s,%u(%d)\n",
263 str
->name
, cnid
, inode
->i_nlink
);
264 err
= hfs_find_init(HFSPLUS_SB(sb
)->cat_tree
, &fd
);
269 * Fail early and avoid ENOSPC during the btree operations. We may
270 * have to split the root node at most once.
272 err
= hfs_bmap_reserve(fd
.tree
, 2 * fd
.tree
->depth
);
276 hfsplus_cat_build_key_with_cnid(sb
, fd
.search_key
, cnid
);
277 entry_size
= hfsplus_fill_cat_thread(sb
, &entry
,
278 S_ISDIR(inode
->i_mode
) ?
279 HFSPLUS_FOLDER_THREAD
: HFSPLUS_FILE_THREAD
,
281 if (unlikely(entry_size
< 0)) {
286 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
287 if (err
!= -ENOENT
) {
292 err
= hfs_brec_insert(&fd
, &entry
, entry_size
);
296 err
= hfsplus_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
, str
);
300 entry_size
= hfsplus_cat_build_record(&entry
, cnid
, inode
);
301 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
302 if (err
!= -ENOENT
) {
308 err
= hfs_brec_insert(&fd
, &entry
, entry_size
);
313 if (S_ISDIR(inode
->i_mode
))
314 hfsplus_subfolders_inc(dir
);
315 inode_set_mtime_to_ts(dir
, inode_set_ctime_current(dir
));
316 hfsplus_mark_inode_dirty(dir
, HFSPLUS_I_CAT_DIRTY
);
322 hfsplus_cat_build_key_with_cnid(sb
, fd
.search_key
, cnid
);
323 if (!hfs_brec_find(&fd
, hfs_find_rec_by_key
))
324 hfs_brec_remove(&fd
);
330 int hfsplus_delete_cat(u32 cnid
, struct inode
*dir
, const struct qstr
*str
)
332 struct super_block
*sb
= dir
->i_sb
;
333 struct hfs_find_data fd
;
334 struct hfsplus_fork_raw fork
;
335 struct list_head
*pos
;
339 hfs_dbg(CAT_MOD
, "delete_cat: %s,%u\n", str
? str
->name
: NULL
, cnid
);
340 err
= hfs_find_init(HFSPLUS_SB(sb
)->cat_tree
, &fd
);
345 * Fail early and avoid ENOSPC during the btree operations. We may
346 * have to split the root node at most once.
348 err
= hfs_bmap_reserve(fd
.tree
, 2 * (int)fd
.tree
->depth
- 2);
355 hfsplus_cat_build_key_with_cnid(sb
, fd
.search_key
, cnid
);
356 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
360 off
= fd
.entryoffset
+
361 offsetof(struct hfsplus_cat_thread
, nodeName
);
362 fd
.search_key
->cat
.parent
= cpu_to_be32(dir
->i_ino
);
363 hfs_bnode_read(fd
.bnode
,
364 &fd
.search_key
->cat
.name
.length
, off
, 2);
365 len
= be16_to_cpu(fd
.search_key
->cat
.name
.length
) * 2;
366 hfs_bnode_read(fd
.bnode
,
367 &fd
.search_key
->cat
.name
.unicode
,
369 fd
.search_key
->key_len
= cpu_to_be16(6 + len
);
371 err
= hfsplus_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
, str
);
376 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
380 type
= hfs_bnode_read_u16(fd
.bnode
, fd
.entryoffset
);
381 if (type
== HFSPLUS_FILE
) {
383 off
= fd
.entryoffset
+ offsetof(hfsplus_cat_file
, data_fork
);
384 hfs_bnode_read(fd
.bnode
, &fork
, off
, sizeof(fork
));
385 hfsplus_free_fork(sb
, cnid
, &fork
, HFSPLUS_TYPE_DATA
);
388 off
= fd
.entryoffset
+
389 offsetof(struct hfsplus_cat_file
, rsrc_fork
);
390 hfs_bnode_read(fd
.bnode
, &fork
, off
, sizeof(fork
));
391 hfsplus_free_fork(sb
, cnid
, &fork
, HFSPLUS_TYPE_RSRC
);
394 /* we only need to take spinlock for exclusion with ->release() */
395 spin_lock(&HFSPLUS_I(dir
)->open_dir_lock
);
396 list_for_each(pos
, &HFSPLUS_I(dir
)->open_dir_list
) {
397 struct hfsplus_readdir_data
*rd
=
398 list_entry(pos
, struct hfsplus_readdir_data
, list
);
399 if (fd
.tree
->keycmp(fd
.search_key
, (void *)&rd
->key
) < 0)
402 spin_unlock(&HFSPLUS_I(dir
)->open_dir_lock
);
404 err
= hfs_brec_remove(&fd
);
408 hfsplus_cat_build_key_with_cnid(sb
, fd
.search_key
, cnid
);
409 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
413 err
= hfs_brec_remove(&fd
);
418 if (type
== HFSPLUS_FOLDER
)
419 hfsplus_subfolders_dec(dir
);
420 inode_set_mtime_to_ts(dir
, inode_set_ctime_current(dir
));
421 hfsplus_mark_inode_dirty(dir
, HFSPLUS_I_CAT_DIRTY
);
423 if (type
== HFSPLUS_FILE
|| type
== HFSPLUS_FOLDER
) {
424 if (HFSPLUS_SB(sb
)->attr_tree
)
425 hfsplus_delete_all_attrs(dir
, cnid
);
434 int hfsplus_rename_cat(u32 cnid
,
435 struct inode
*src_dir
, const struct qstr
*src_name
,
436 struct inode
*dst_dir
, const struct qstr
*dst_name
)
438 struct super_block
*sb
= src_dir
->i_sb
;
439 struct hfs_find_data src_fd
, dst_fd
;
440 hfsplus_cat_entry entry
;
441 int entry_size
, type
;
444 hfs_dbg(CAT_MOD
, "rename_cat: %u - %lu,%s - %lu,%s\n",
445 cnid
, src_dir
->i_ino
, src_name
->name
,
446 dst_dir
->i_ino
, dst_name
->name
);
447 err
= hfs_find_init(HFSPLUS_SB(sb
)->cat_tree
, &src_fd
);
453 * Fail early and avoid ENOSPC during the btree operations. We may
454 * have to split the root node at most twice.
456 err
= hfs_bmap_reserve(src_fd
.tree
, 4 * (int)src_fd
.tree
->depth
- 1);
460 /* find the old dir entry and read the data */
461 err
= hfsplus_cat_build_key(sb
, src_fd
.search_key
,
462 src_dir
->i_ino
, src_name
);
466 err
= hfs_brec_find(&src_fd
, hfs_find_rec_by_key
);
469 if (src_fd
.entrylength
> sizeof(entry
) || src_fd
.entrylength
< 0) {
474 hfs_bnode_read(src_fd
.bnode
, &entry
, src_fd
.entryoffset
,
476 type
= be16_to_cpu(entry
.type
);
478 /* create new dir entry with the data from the old entry */
479 err
= hfsplus_cat_build_key(sb
, dst_fd
.search_key
,
480 dst_dir
->i_ino
, dst_name
);
484 err
= hfs_brec_find(&dst_fd
, hfs_find_rec_by_key
);
485 if (err
!= -ENOENT
) {
491 err
= hfs_brec_insert(&dst_fd
, &entry
, src_fd
.entrylength
);
495 if (type
== HFSPLUS_FOLDER
)
496 hfsplus_subfolders_inc(dst_dir
);
497 inode_set_mtime_to_ts(dst_dir
, inode_set_ctime_current(dst_dir
));
499 /* finally remove the old entry */
500 err
= hfsplus_cat_build_key(sb
, src_fd
.search_key
,
501 src_dir
->i_ino
, src_name
);
505 err
= hfs_brec_find(&src_fd
, hfs_find_rec_by_key
);
508 err
= hfs_brec_remove(&src_fd
);
512 if (type
== HFSPLUS_FOLDER
)
513 hfsplus_subfolders_dec(src_dir
);
514 inode_set_mtime_to_ts(src_dir
, inode_set_ctime_current(src_dir
));
516 /* remove old thread entry */
517 hfsplus_cat_build_key_with_cnid(sb
, src_fd
.search_key
, cnid
);
518 err
= hfs_brec_find(&src_fd
, hfs_find_rec_by_key
);
521 type
= hfs_bnode_read_u16(src_fd
.bnode
, src_fd
.entryoffset
);
522 err
= hfs_brec_remove(&src_fd
);
526 /* create new thread entry */
527 hfsplus_cat_build_key_with_cnid(sb
, dst_fd
.search_key
, cnid
);
528 entry_size
= hfsplus_fill_cat_thread(sb
, &entry
, type
,
529 dst_dir
->i_ino
, dst_name
);
530 if (unlikely(entry_size
< 0)) {
535 err
= hfs_brec_find(&dst_fd
, hfs_find_rec_by_key
);
536 if (err
!= -ENOENT
) {
541 err
= hfs_brec_insert(&dst_fd
, &entry
, entry_size
);
543 hfsplus_mark_inode_dirty(dst_dir
, HFSPLUS_I_CAT_DIRTY
);
544 hfsplus_mark_inode_dirty(src_dir
, HFSPLUS_I_CAT_DIRTY
);
546 hfs_bnode_put(dst_fd
.bnode
);
547 hfs_find_exit(&src_fd
);