2 * linux/fs/hfsplus/catalog.c
5 * Brad Boyer (flar@allandria.com)
6 * (C) 2003 Ardis Technologies <roman@ardistech.com>
8 * Handling of catalog records
12 #include "hfsplus_fs.h"
13 #include "hfsplus_raw.h"
15 int hfsplus_cat_case_cmp_key(const hfsplus_btree_key
*k1
,
16 const hfsplus_btree_key
*k2
)
23 return be32_to_cpu(k1p
) < be32_to_cpu(k2p
) ? -1 : 1;
25 return hfsplus_strcasecmp(&k1
->cat
.name
, &k2
->cat
.name
);
28 int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key
*k1
,
29 const hfsplus_btree_key
*k2
)
36 return be32_to_cpu(k1p
) < be32_to_cpu(k2p
) ? -1 : 1;
38 return hfsplus_strcmp(&k1
->cat
.name
, &k2
->cat
.name
);
41 void hfsplus_cat_build_key(struct super_block
*sb
, hfsplus_btree_key
*key
,
42 u32 parent
, struct qstr
*str
)
46 key
->cat
.parent
= cpu_to_be32(parent
);
48 hfsplus_asc2uni(sb
, &key
->cat
.name
, HFSPLUS_MAX_STRLEN
,
50 len
= be16_to_cpu(key
->cat
.name
.length
);
52 key
->cat
.name
.length
= 0;
55 key
->key_len
= cpu_to_be16(6 + 2 * len
);
58 static void hfsplus_cat_build_key_uni(hfsplus_btree_key
*key
, u32 parent
,
59 struct hfsplus_unistr
*name
)
63 ustrlen
= be16_to_cpu(name
->length
);
64 key
->cat
.parent
= cpu_to_be32(parent
);
65 key
->cat
.name
.length
= cpu_to_be16(ustrlen
);
67 memcpy(key
->cat
.name
.unicode
, name
->unicode
, ustrlen
);
68 key
->key_len
= cpu_to_be16(6 + ustrlen
);
71 void hfsplus_cat_set_perms(struct inode
*inode
, struct hfsplus_perm
*perms
)
73 if (inode
->i_flags
& S_IMMUTABLE
)
74 perms
->rootflags
|= HFSPLUS_FLG_IMMUTABLE
;
76 perms
->rootflags
&= ~HFSPLUS_FLG_IMMUTABLE
;
77 if (inode
->i_flags
& S_APPEND
)
78 perms
->rootflags
|= HFSPLUS_FLG_APPEND
;
80 perms
->rootflags
&= ~HFSPLUS_FLG_APPEND
;
82 perms
->userflags
= HFSPLUS_I(inode
)->userflags
;
83 perms
->mode
= cpu_to_be16(inode
->i_mode
);
84 perms
->owner
= cpu_to_be32(i_uid_read(inode
));
85 perms
->group
= cpu_to_be32(i_gid_read(inode
));
87 if (S_ISREG(inode
->i_mode
))
88 perms
->dev
= cpu_to_be32(inode
->i_nlink
);
89 else if (S_ISBLK(inode
->i_mode
) || S_ISCHR(inode
->i_mode
))
90 perms
->dev
= cpu_to_be32(inode
->i_rdev
);
95 static int hfsplus_cat_build_record(hfsplus_cat_entry
*entry
,
96 u32 cnid
, struct inode
*inode
)
98 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(inode
->i_sb
);
100 if (S_ISDIR(inode
->i_mode
)) {
101 struct hfsplus_cat_folder
*folder
;
103 folder
= &entry
->folder
;
104 memset(folder
, 0, sizeof(*folder
));
105 folder
->type
= cpu_to_be16(HFSPLUS_FOLDER
);
106 if (test_bit(HFSPLUS_SB_HFSX
, &sbi
->flags
))
107 folder
->flags
|= cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT
);
108 folder
->id
= cpu_to_be32(inode
->i_ino
);
109 HFSPLUS_I(inode
)->create_date
=
110 folder
->create_date
=
111 folder
->content_mod_date
=
112 folder
->attribute_mod_date
=
113 folder
->access_date
= hfsp_now2mt();
114 hfsplus_cat_set_perms(inode
, &folder
->permissions
);
115 if (inode
== sbi
->hidden_dir
)
116 /* invisible and namelocked */
117 folder
->user_info
.frFlags
= cpu_to_be16(0x5000);
118 return sizeof(*folder
);
120 struct hfsplus_cat_file
*file
;
123 memset(file
, 0, sizeof(*file
));
124 file
->type
= cpu_to_be16(HFSPLUS_FILE
);
125 file
->flags
= cpu_to_be16(HFSPLUS_FILE_THREAD_EXISTS
);
126 file
->id
= cpu_to_be32(cnid
);
127 HFSPLUS_I(inode
)->create_date
=
129 file
->content_mod_date
=
130 file
->attribute_mod_date
=
131 file
->access_date
= hfsp_now2mt();
132 if (cnid
== inode
->i_ino
) {
133 hfsplus_cat_set_perms(inode
, &file
->permissions
);
134 if (S_ISLNK(inode
->i_mode
)) {
135 file
->user_info
.fdType
=
136 cpu_to_be32(HFSP_SYMLINK_TYPE
);
137 file
->user_info
.fdCreator
=
138 cpu_to_be32(HFSP_SYMLINK_CREATOR
);
140 file
->user_info
.fdType
=
141 cpu_to_be32(sbi
->type
);
142 file
->user_info
.fdCreator
=
143 cpu_to_be32(sbi
->creator
);
145 if (HFSPLUS_FLG_IMMUTABLE
&
146 (file
->permissions
.rootflags
|
147 file
->permissions
.userflags
))
149 cpu_to_be16(HFSPLUS_FILE_LOCKED
);
151 file
->user_info
.fdType
=
152 cpu_to_be32(HFSP_HARDLINK_TYPE
);
153 file
->user_info
.fdCreator
=
154 cpu_to_be32(HFSP_HFSPLUS_CREATOR
);
155 file
->user_info
.fdFlags
=
158 HFSPLUS_I(sbi
->hidden_dir
)->create_date
;
159 file
->permissions
.dev
=
160 cpu_to_be32(HFSPLUS_I(inode
)->linkid
);
162 return sizeof(*file
);
166 static int hfsplus_fill_cat_thread(struct super_block
*sb
,
167 hfsplus_cat_entry
*entry
, int type
,
168 u32 parentid
, struct qstr
*str
)
170 entry
->type
= cpu_to_be16(type
);
171 entry
->thread
.reserved
= 0;
172 entry
->thread
.parentID
= cpu_to_be32(parentid
);
173 hfsplus_asc2uni(sb
, &entry
->thread
.nodeName
, HFSPLUS_MAX_STRLEN
,
174 str
->name
, str
->len
);
175 return 10 + be16_to_cpu(entry
->thread
.nodeName
.length
) * 2;
178 /* Try to get a catalog entry for given catalog id */
179 int hfsplus_find_cat(struct super_block
*sb
, u32 cnid
,
180 struct hfs_find_data
*fd
)
182 hfsplus_cat_entry tmp
;
186 hfsplus_cat_build_key(sb
, fd
->search_key
, cnid
, NULL
);
187 err
= hfs_brec_read(fd
, &tmp
, sizeof(hfsplus_cat_entry
));
191 type
= be16_to_cpu(tmp
.type
);
192 if (type
!= HFSPLUS_FOLDER_THREAD
&& type
!= HFSPLUS_FILE_THREAD
) {
193 pr_err("found bad thread record in catalog\n");
197 if (be16_to_cpu(tmp
.thread
.nodeName
.length
) > 255) {
198 pr_err("catalog name length corrupted\n");
202 hfsplus_cat_build_key_uni(fd
->search_key
,
203 be32_to_cpu(tmp
.thread
.parentID
),
204 &tmp
.thread
.nodeName
);
205 return hfs_brec_find(fd
, hfs_find_rec_by_key
);
208 static void hfsplus_subfolders_inc(struct inode
*dir
)
210 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
212 if (test_bit(HFSPLUS_SB_HFSX
, &sbi
->flags
)) {
214 * Increment subfolder count. Note, the value is only meaningful
215 * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
217 HFSPLUS_I(dir
)->subfolders
++;
221 static void hfsplus_subfolders_dec(struct inode
*dir
)
223 struct hfsplus_sb_info
*sbi
= HFSPLUS_SB(dir
->i_sb
);
225 if (test_bit(HFSPLUS_SB_HFSX
, &sbi
->flags
)) {
227 * Decrement subfolder count. Note, the value is only meaningful
228 * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
230 * Check for zero. Some subfolders may have been created
231 * by an implementation ignorant of this counter.
233 if (HFSPLUS_I(dir
)->subfolders
)
234 HFSPLUS_I(dir
)->subfolders
--;
238 int hfsplus_create_cat(u32 cnid
, struct inode
*dir
,
239 struct qstr
*str
, struct inode
*inode
)
241 struct super_block
*sb
= dir
->i_sb
;
242 struct hfs_find_data fd
;
243 hfsplus_cat_entry entry
;
247 hfs_dbg(CAT_MOD
, "create_cat: %s,%u(%d)\n",
248 str
->name
, cnid
, inode
->i_nlink
);
249 err
= hfs_find_init(HFSPLUS_SB(sb
)->cat_tree
, &fd
);
253 hfsplus_cat_build_key(sb
, fd
.search_key
, cnid
, NULL
);
254 entry_size
= hfsplus_fill_cat_thread(sb
, &entry
,
255 S_ISDIR(inode
->i_mode
) ?
256 HFSPLUS_FOLDER_THREAD
: HFSPLUS_FILE_THREAD
,
258 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
259 if (err
!= -ENOENT
) {
264 err
= hfs_brec_insert(&fd
, &entry
, entry_size
);
268 hfsplus_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
, str
);
269 entry_size
= hfsplus_cat_build_record(&entry
, cnid
, inode
);
270 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
271 if (err
!= -ENOENT
) {
277 err
= hfs_brec_insert(&fd
, &entry
, entry_size
);
282 if (S_ISDIR(inode
->i_mode
))
283 hfsplus_subfolders_inc(dir
);
284 dir
->i_mtime
= dir
->i_ctime
= CURRENT_TIME_SEC
;
285 hfsplus_mark_inode_dirty(dir
, HFSPLUS_I_CAT_DIRTY
);
291 hfsplus_cat_build_key(sb
, fd
.search_key
, cnid
, NULL
);
292 if (!hfs_brec_find(&fd
, hfs_find_rec_by_key
))
293 hfs_brec_remove(&fd
);
299 int hfsplus_delete_cat(u32 cnid
, struct inode
*dir
, struct qstr
*str
)
301 struct super_block
*sb
= dir
->i_sb
;
302 struct hfs_find_data fd
;
303 struct hfsplus_fork_raw fork
;
304 struct list_head
*pos
;
308 hfs_dbg(CAT_MOD
, "delete_cat: %s,%u\n", str
? str
->name
: NULL
, cnid
);
309 err
= hfs_find_init(HFSPLUS_SB(sb
)->cat_tree
, &fd
);
316 hfsplus_cat_build_key(sb
, fd
.search_key
, cnid
, NULL
);
317 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
321 off
= fd
.entryoffset
+
322 offsetof(struct hfsplus_cat_thread
, nodeName
);
323 fd
.search_key
->cat
.parent
= cpu_to_be32(dir
->i_ino
);
324 hfs_bnode_read(fd
.bnode
,
325 &fd
.search_key
->cat
.name
.length
, off
, 2);
326 len
= be16_to_cpu(fd
.search_key
->cat
.name
.length
) * 2;
327 hfs_bnode_read(fd
.bnode
,
328 &fd
.search_key
->cat
.name
.unicode
,
330 fd
.search_key
->key_len
= cpu_to_be16(6 + len
);
332 hfsplus_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
, str
);
334 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
338 type
= hfs_bnode_read_u16(fd
.bnode
, fd
.entryoffset
);
339 if (type
== HFSPLUS_FILE
) {
341 off
= fd
.entryoffset
+ offsetof(hfsplus_cat_file
, data_fork
);
342 hfs_bnode_read(fd
.bnode
, &fork
, off
, sizeof(fork
));
343 hfsplus_free_fork(sb
, cnid
, &fork
, HFSPLUS_TYPE_DATA
);
346 off
= fd
.entryoffset
+
347 offsetof(struct hfsplus_cat_file
, rsrc_fork
);
348 hfs_bnode_read(fd
.bnode
, &fork
, off
, sizeof(fork
));
349 hfsplus_free_fork(sb
, cnid
, &fork
, HFSPLUS_TYPE_RSRC
);
352 list_for_each(pos
, &HFSPLUS_I(dir
)->open_dir_list
) {
353 struct hfsplus_readdir_data
*rd
=
354 list_entry(pos
, struct hfsplus_readdir_data
, list
);
355 if (fd
.tree
->keycmp(fd
.search_key
, (void *)&rd
->key
) < 0)
359 err
= hfs_brec_remove(&fd
);
363 hfsplus_cat_build_key(sb
, fd
.search_key
, cnid
, NULL
);
364 err
= hfs_brec_find(&fd
, hfs_find_rec_by_key
);
368 err
= hfs_brec_remove(&fd
);
373 if (type
== HFSPLUS_FOLDER
)
374 hfsplus_subfolders_dec(dir
);
375 dir
->i_mtime
= dir
->i_ctime
= CURRENT_TIME_SEC
;
376 hfsplus_mark_inode_dirty(dir
, HFSPLUS_I_CAT_DIRTY
);
378 if (type
== HFSPLUS_FILE
|| type
== HFSPLUS_FOLDER
) {
379 if (HFSPLUS_SB(sb
)->attr_tree
)
380 hfsplus_delete_all_attrs(dir
, cnid
);
389 int hfsplus_rename_cat(u32 cnid
,
390 struct inode
*src_dir
, struct qstr
*src_name
,
391 struct inode
*dst_dir
, struct qstr
*dst_name
)
393 struct super_block
*sb
= src_dir
->i_sb
;
394 struct hfs_find_data src_fd
, dst_fd
;
395 hfsplus_cat_entry entry
;
396 int entry_size
, type
;
399 hfs_dbg(CAT_MOD
, "rename_cat: %u - %lu,%s - %lu,%s\n",
400 cnid
, src_dir
->i_ino
, src_name
->name
,
401 dst_dir
->i_ino
, dst_name
->name
);
402 err
= hfs_find_init(HFSPLUS_SB(sb
)->cat_tree
, &src_fd
);
407 /* find the old dir entry and read the data */
408 hfsplus_cat_build_key(sb
, src_fd
.search_key
, src_dir
->i_ino
, src_name
);
409 err
= hfs_brec_find(&src_fd
, hfs_find_rec_by_key
);
412 if (src_fd
.entrylength
> sizeof(entry
) || src_fd
.entrylength
< 0) {
417 hfs_bnode_read(src_fd
.bnode
, &entry
, src_fd
.entryoffset
,
419 type
= be16_to_cpu(entry
.type
);
421 /* create new dir entry with the data from the old entry */
422 hfsplus_cat_build_key(sb
, dst_fd
.search_key
, dst_dir
->i_ino
, dst_name
);
423 err
= hfs_brec_find(&dst_fd
, hfs_find_rec_by_key
);
424 if (err
!= -ENOENT
) {
430 err
= hfs_brec_insert(&dst_fd
, &entry
, src_fd
.entrylength
);
434 if (type
== HFSPLUS_FOLDER
)
435 hfsplus_subfolders_inc(dst_dir
);
436 dst_dir
->i_mtime
= dst_dir
->i_ctime
= CURRENT_TIME_SEC
;
438 /* finally remove the old entry */
439 hfsplus_cat_build_key(sb
, src_fd
.search_key
, src_dir
->i_ino
, src_name
);
440 err
= hfs_brec_find(&src_fd
, hfs_find_rec_by_key
);
443 err
= hfs_brec_remove(&src_fd
);
447 if (type
== HFSPLUS_FOLDER
)
448 hfsplus_subfolders_dec(src_dir
);
449 src_dir
->i_mtime
= src_dir
->i_ctime
= CURRENT_TIME_SEC
;
451 /* remove old thread entry */
452 hfsplus_cat_build_key(sb
, src_fd
.search_key
, cnid
, NULL
);
453 err
= hfs_brec_find(&src_fd
, hfs_find_rec_by_key
);
456 type
= hfs_bnode_read_u16(src_fd
.bnode
, src_fd
.entryoffset
);
457 err
= hfs_brec_remove(&src_fd
);
461 /* create new thread entry */
462 hfsplus_cat_build_key(sb
, dst_fd
.search_key
, cnid
, NULL
);
463 entry_size
= hfsplus_fill_cat_thread(sb
, &entry
, type
,
464 dst_dir
->i_ino
, dst_name
);
465 err
= hfs_brec_find(&dst_fd
, hfs_find_rec_by_key
);
466 if (err
!= -ENOENT
) {
471 err
= hfs_brec_insert(&dst_fd
, &entry
, entry_size
);
473 hfsplus_mark_inode_dirty(dst_dir
, HFSPLUS_I_CAT_DIRTY
);
474 hfsplus_mark_inode_dirty(src_dir
, HFSPLUS_I_CAT_DIRTY
);
476 hfs_bnode_put(dst_fd
.bnode
);
477 hfs_find_exit(&src_fd
);