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
11 #include <linux/sched.h>
13 #include "hfsplus_fs.h"
14 #include "hfsplus_raw.h"
16 int hfsplus_cat_cmp_key(hfsplus_btree_key
*k1
, hfsplus_btree_key
*k2
)
23 return be32_to_cpu(k1p
) < be32_to_cpu(k2p
) ? -1 : 1;
25 return hfsplus_unistrcmp(&k1
->cat
.name
, &k2
->cat
.name
);
28 void hfsplus_cat_build_key(struct super_block
*sb
, hfsplus_btree_key
*key
,
29 u32 parent
, struct qstr
*str
)
33 key
->cat
.parent
= cpu_to_be32(parent
);
35 hfsplus_asc2uni(sb
, &key
->cat
.name
, str
->name
, str
->len
);
36 len
= be16_to_cpu(key
->cat
.name
.length
);
38 key
->cat
.name
.length
= 0;
41 key
->key_len
= cpu_to_be16(6 + 2 * len
);
44 static void hfsplus_cat_build_key_uni(hfsplus_btree_key
*key
, u32 parent
,
45 struct hfsplus_unistr
*name
)
49 ustrlen
= be16_to_cpu(name
->length
);
50 key
->cat
.parent
= cpu_to_be32(parent
);
51 key
->cat
.name
.length
= cpu_to_be16(ustrlen
);
53 memcpy(key
->cat
.name
.unicode
, name
->unicode
, ustrlen
);
54 key
->key_len
= cpu_to_be16(6 + ustrlen
);
57 static void hfsplus_set_perms(struct inode
*inode
, struct hfsplus_perm
*perms
)
59 if (inode
->i_flags
& S_IMMUTABLE
)
60 perms
->rootflags
|= HFSPLUS_FLG_IMMUTABLE
;
62 perms
->rootflags
&= ~HFSPLUS_FLG_IMMUTABLE
;
63 if (inode
->i_flags
& S_APPEND
)
64 perms
->rootflags
|= HFSPLUS_FLG_APPEND
;
66 perms
->rootflags
&= ~HFSPLUS_FLG_APPEND
;
67 HFSPLUS_I(inode
).rootflags
= perms
->rootflags
;
68 HFSPLUS_I(inode
).userflags
= perms
->userflags
;
69 perms
->mode
= cpu_to_be16(inode
->i_mode
);
70 perms
->owner
= cpu_to_be32(inode
->i_uid
);
71 perms
->group
= cpu_to_be32(inode
->i_gid
);
74 static int hfsplus_cat_build_record(hfsplus_cat_entry
*entry
, u32 cnid
, struct inode
*inode
)
76 if (S_ISDIR(inode
->i_mode
)) {
77 struct hfsplus_cat_folder
*folder
;
79 folder
= &entry
->folder
;
80 memset(folder
, 0, sizeof(*folder
));
81 folder
->type
= cpu_to_be16(HFSPLUS_FOLDER
);
82 folder
->id
= cpu_to_be32(inode
->i_ino
);
83 folder
->create_date
= folder
->content_mod_date
=
84 folder
->attribute_mod_date
= folder
->access_date
= hfsp_now2mt();
85 hfsplus_set_perms(inode
, &folder
->permissions
);
86 if (inode
== HFSPLUS_SB(inode
->i_sb
).hidden_dir
)
87 /* invisible and namelocked */
88 folder
->user_info
.frFlags
= cpu_to_be16(0x5000);
89 return sizeof(*folder
);
91 struct hfsplus_cat_file
*file
;
94 memset(file
, 0, sizeof(*file
));
95 file
->type
= cpu_to_be16(HFSPLUS_FILE
);
96 file
->flags
= cpu_to_be16(HFSPLUS_FILE_THREAD_EXISTS
);
97 file
->id
= cpu_to_be32(cnid
);
98 file
->create_date
= file
->content_mod_date
=
99 file
->attribute_mod_date
= file
->access_date
= hfsp_now2mt();
100 if (cnid
== inode
->i_ino
) {
101 hfsplus_set_perms(inode
, &file
->permissions
);
102 file
->user_info
.fdType
= cpu_to_be32(HFSPLUS_SB(inode
->i_sb
).type
);
103 file
->user_info
.fdCreator
= cpu_to_be32(HFSPLUS_SB(inode
->i_sb
).creator
);
104 if ((file
->permissions
.rootflags
| file
->permissions
.userflags
) & HFSPLUS_FLG_IMMUTABLE
)
105 file
->flags
|= cpu_to_be16(HFSPLUS_FILE_LOCKED
);
107 file
->user_info
.fdType
= cpu_to_be32(HFSP_HARDLINK_TYPE
);
108 file
->user_info
.fdCreator
= cpu_to_be32(HFSP_HFSPLUS_CREATOR
);
109 file
->user_info
.fdFlags
= cpu_to_be16(0x100);
110 file
->permissions
.dev
= cpu_to_be32(HFSPLUS_I(inode
).dev
);
112 return sizeof(*file
);
116 static int hfsplus_fill_cat_thread(struct super_block
*sb
,
117 hfsplus_cat_entry
*entry
, int type
,
118 u32 parentid
, struct qstr
*str
)
120 entry
->type
= cpu_to_be16(type
);
121 entry
->thread
.reserved
= 0;
122 entry
->thread
.parentID
= cpu_to_be32(parentid
);
123 hfsplus_asc2uni(sb
, &entry
->thread
.nodeName
, str
->name
, str
->len
);
124 return 10 + be16_to_cpu(entry
->thread
.nodeName
.length
) * 2;
127 /* Try to get a catalog entry for given catalog id */
128 int hfsplus_find_cat(struct super_block
*sb
, u32 cnid
,
129 struct hfs_find_data
*fd
)
131 hfsplus_cat_entry tmp
;
135 hfsplus_cat_build_key(sb
, fd
->search_key
, cnid
, NULL
);
136 err
= hfs_brec_read(fd
, &tmp
, sizeof(hfsplus_cat_entry
));
140 type
= be16_to_cpu(tmp
.type
);
141 if (type
!= HFSPLUS_FOLDER_THREAD
&& type
!= HFSPLUS_FILE_THREAD
) {
142 printk("HFS+-fs: Found bad thread record in catalog\n");
146 hfsplus_cat_build_key_uni(fd
->search_key
, be32_to_cpu(tmp
.thread
.parentID
),
147 &tmp
.thread
.nodeName
);
148 return hfs_brec_find(fd
);
151 int hfsplus_create_cat(u32 cnid
, struct inode
*dir
, struct qstr
*str
, struct inode
*inode
)
153 struct hfs_find_data fd
;
154 struct super_block
*sb
;
155 hfsplus_cat_entry entry
;
159 dprint(DBG_CAT_MOD
, "create_cat: %s,%u(%d)\n", str
->name
, cnid
, inode
->i_nlink
);
161 hfs_find_init(HFSPLUS_SB(sb
).cat_tree
, &fd
);
163 hfsplus_cat_build_key(sb
, fd
.search_key
, cnid
, NULL
);
164 entry_size
= hfsplus_fill_cat_thread(sb
, &entry
, S_ISDIR(inode
->i_mode
) ?
165 HFSPLUS_FOLDER_THREAD
: HFSPLUS_FILE_THREAD
,
167 err
= hfs_brec_find(&fd
);
168 if (err
!= -ENOENT
) {
173 err
= hfs_brec_insert(&fd
, &entry
, entry_size
);
177 hfsplus_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
, str
);
178 entry_size
= hfsplus_cat_build_record(&entry
, cnid
, inode
);
179 err
= hfs_brec_find(&fd
);
180 if (err
!= -ENOENT
) {
186 err
= hfs_brec_insert(&fd
, &entry
, entry_size
);
191 dir
->i_mtime
= dir
->i_ctime
= CURRENT_TIME_SEC
;
192 mark_inode_dirty(dir
);
197 hfsplus_cat_build_key(sb
, fd
.search_key
, cnid
, NULL
);
198 if (!hfs_brec_find(&fd
))
199 hfs_brec_remove(&fd
);
205 int hfsplus_delete_cat(u32 cnid
, struct inode
*dir
, struct qstr
*str
)
207 struct super_block
*sb
;
208 struct hfs_find_data fd
;
209 struct hfsplus_fork_raw fork
;
210 struct list_head
*pos
;
214 dprint(DBG_CAT_MOD
, "delete_cat: %s,%u\n", str
? str
->name
: NULL
, cnid
);
216 hfs_find_init(HFSPLUS_SB(sb
).cat_tree
, &fd
);
221 hfsplus_cat_build_key(sb
, fd
.search_key
, cnid
, NULL
);
222 err
= hfs_brec_find(&fd
);
226 off
= fd
.entryoffset
+ offsetof(struct hfsplus_cat_thread
, nodeName
);
227 fd
.search_key
->cat
.parent
= cpu_to_be32(dir
->i_ino
);
228 hfs_bnode_read(fd
.bnode
, &fd
.search_key
->cat
.name
.length
, off
, 2);
229 len
= be16_to_cpu(fd
.search_key
->cat
.name
.length
) * 2;
230 hfs_bnode_read(fd
.bnode
, &fd
.search_key
->cat
.name
.unicode
, off
+ 2, len
);
231 fd
.search_key
->key_len
= cpu_to_be16(6 + len
);
233 hfsplus_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
, str
);
235 err
= hfs_brec_find(&fd
);
239 type
= hfs_bnode_read_u16(fd
.bnode
, fd
.entryoffset
);
240 if (type
== HFSPLUS_FILE
) {
242 off
= fd
.entryoffset
+ offsetof(hfsplus_cat_file
, data_fork
);
243 hfs_bnode_read(fd
.bnode
, &fork
, off
, sizeof(fork
));
244 hfsplus_free_fork(sb
, cnid
, &fork
, HFSPLUS_TYPE_DATA
);
247 off
= fd
.entryoffset
+ offsetof(struct hfsplus_cat_file
, rsrc_fork
);
248 hfs_bnode_read(fd
.bnode
, &fork
, off
, sizeof(fork
));
249 hfsplus_free_fork(sb
, cnid
, &fork
, HFSPLUS_TYPE_RSRC
);
252 list_for_each(pos
, &HFSPLUS_I(dir
).open_dir_list
) {
253 struct hfsplus_readdir_data
*rd
=
254 list_entry(pos
, struct hfsplus_readdir_data
, list
);
255 if (fd
.tree
->keycmp(fd
.search_key
, (void *)&rd
->key
) < 0)
259 err
= hfs_brec_remove(&fd
);
263 hfsplus_cat_build_key(sb
, fd
.search_key
, cnid
, NULL
);
264 err
= hfs_brec_find(&fd
);
268 err
= hfs_brec_remove(&fd
);
273 dir
->i_mtime
= dir
->i_ctime
= CURRENT_TIME_SEC
;
274 mark_inode_dirty(dir
);
281 int hfsplus_rename_cat(u32 cnid
,
282 struct inode
*src_dir
, struct qstr
*src_name
,
283 struct inode
*dst_dir
, struct qstr
*dst_name
)
285 struct super_block
*sb
;
286 struct hfs_find_data src_fd
, dst_fd
;
287 hfsplus_cat_entry entry
;
288 int entry_size
, type
;
291 dprint(DBG_CAT_MOD
, "rename_cat: %u - %lu,%s - %lu,%s\n", cnid
, src_dir
->i_ino
, src_name
->name
,
292 dst_dir
->i_ino
, dst_name
->name
);
294 hfs_find_init(HFSPLUS_SB(sb
).cat_tree
, &src_fd
);
297 /* find the old dir entry and read the data */
298 hfsplus_cat_build_key(sb
, src_fd
.search_key
, src_dir
->i_ino
, src_name
);
299 err
= hfs_brec_find(&src_fd
);
303 hfs_bnode_read(src_fd
.bnode
, &entry
, src_fd
.entryoffset
,
306 /* create new dir entry with the data from the old entry */
307 hfsplus_cat_build_key(sb
, dst_fd
.search_key
, dst_dir
->i_ino
, dst_name
);
308 err
= hfs_brec_find(&dst_fd
);
309 if (err
!= -ENOENT
) {
315 err
= hfs_brec_insert(&dst_fd
, &entry
, src_fd
.entrylength
);
319 dst_dir
->i_mtime
= dst_dir
->i_ctime
= CURRENT_TIME_SEC
;
320 mark_inode_dirty(dst_dir
);
322 /* finally remove the old entry */
323 hfsplus_cat_build_key(sb
, src_fd
.search_key
, src_dir
->i_ino
, src_name
);
324 err
= hfs_brec_find(&src_fd
);
327 err
= hfs_brec_remove(&src_fd
);
331 src_dir
->i_mtime
= src_dir
->i_ctime
= CURRENT_TIME_SEC
;
332 mark_inode_dirty(src_dir
);
334 /* remove old thread entry */
335 hfsplus_cat_build_key(sb
, src_fd
.search_key
, cnid
, NULL
);
336 err
= hfs_brec_find(&src_fd
);
339 type
= hfs_bnode_read_u16(src_fd
.bnode
, src_fd
.entryoffset
);
340 err
= hfs_brec_remove(&src_fd
);
344 /* create new thread entry */
345 hfsplus_cat_build_key(sb
, dst_fd
.search_key
, cnid
, NULL
);
346 entry_size
= hfsplus_fill_cat_thread(sb
, &entry
, type
, dst_dir
->i_ino
, dst_name
);
347 err
= hfs_brec_find(&dst_fd
);
348 if (err
!= -ENOENT
) {
353 err
= hfs_brec_insert(&dst_fd
, &entry
, entry_size
);
355 hfs_bnode_put(dst_fd
.bnode
);
356 hfs_find_exit(&src_fd
);