2 * linux/fs/hfs/catalog.c
4 * Copyright (C) 1995-1997 Paul H. Hargrove
5 * (C) 2003 Ardis Technologies <roman@ardistech.com>
6 * This file may be distributed under the terms of the GNU General Public License.
8 * This file contains the functions related to the catalog B-tree.
10 * Cache code shamelessly stolen from
11 * linux/fs/inode.c Copyright (C) 1991, 1992 Linus Torvalds
12 * re-shamelessly stolen Copyright (C) 1997 Linus Torvalds
21 * Given the ID of the parent and the name build a search key.
23 void hfs_cat_build_key(struct super_block
*sb
, btree_key
*key
, u32 parent
, const struct qstr
*name
)
25 key
->cat
.reserved
= 0;
26 key
->cat
.ParID
= cpu_to_be32(parent
);
28 hfs_asc2mac(sb
, &key
->cat
.CName
, name
);
29 key
->key_len
= 6 + key
->cat
.CName
.len
;
31 memset(&key
->cat
.CName
, 0, sizeof(struct hfs_name
));
36 static int hfs_cat_build_record(hfs_cat_rec
*rec
, u32 cnid
, struct inode
*inode
)
38 __be32 mtime
= hfs_mtime();
40 memset(rec
, 0, sizeof(*rec
));
41 if (S_ISDIR(inode
->i_mode
)) {
42 rec
->type
= HFS_CDR_DIR
;
43 rec
->dir
.DirID
= cpu_to_be32(cnid
);
44 rec
->dir
.CrDat
= mtime
;
45 rec
->dir
.MdDat
= mtime
;
47 rec
->dir
.UsrInfo
.frView
= cpu_to_be16(0xff);
48 return sizeof(struct hfs_cat_dir
);
50 /* init some fields for the file record */
51 rec
->type
= HFS_CDR_FIL
;
52 rec
->file
.Flags
= HFS_FIL_USED
| HFS_FIL_THD
;
53 if (!(inode
->i_mode
& S_IWUSR
))
54 rec
->file
.Flags
|= HFS_FIL_LOCK
;
55 rec
->file
.FlNum
= cpu_to_be32(cnid
);
56 rec
->file
.CrDat
= mtime
;
57 rec
->file
.MdDat
= mtime
;
59 rec
->file
.UsrWds
.fdType
= HFS_SB(inode
->i_sb
)->s_type
;
60 rec
->file
.UsrWds
.fdCreator
= HFS_SB(inode
->i_sb
)->s_creator
;
61 return sizeof(struct hfs_cat_file
);
65 static int hfs_cat_build_thread(struct super_block
*sb
,
66 hfs_cat_rec
*rec
, int type
,
67 u32 parentid
, const struct qstr
*name
)
70 memset(rec
->thread
.reserved
, 0, sizeof(rec
->thread
.reserved
));
71 rec
->thread
.ParID
= cpu_to_be32(parentid
);
72 hfs_asc2mac(sb
, &rec
->thread
.CName
, name
);
73 return sizeof(struct hfs_cat_thread
);
79 * Add a new file or directory to the catalog B-tree and
80 * return a (struct hfs_cat_entry) for it in '*result'.
82 int hfs_cat_create(u32 cnid
, struct inode
*dir
, const struct qstr
*str
, struct inode
*inode
)
84 struct hfs_find_data fd
;
85 struct super_block
*sb
;
86 union hfs_cat_rec entry
;
90 hfs_dbg(CAT_MOD
, "create_cat: %s,%u(%d)\n",
91 str
->name
, cnid
, inode
->i_nlink
);
92 if (dir
->i_size
>= HFS_MAX_VALENCE
)
96 err
= hfs_find_init(HFS_SB(sb
)->cat_tree
, &fd
);
101 * Fail early and avoid ENOSPC during the btree operations. We may
102 * have to split the root node at most once.
104 err
= hfs_bmap_reserve(fd
.tree
, 2 * fd
.tree
->depth
);
108 hfs_cat_build_key(sb
, fd
.search_key
, cnid
, NULL
);
109 entry_size
= hfs_cat_build_thread(sb
, &entry
, S_ISDIR(inode
->i_mode
) ?
110 HFS_CDR_THD
: HFS_CDR_FTH
,
112 err
= hfs_brec_find(&fd
);
113 if (err
!= -ENOENT
) {
118 err
= hfs_brec_insert(&fd
, &entry
, entry_size
);
122 hfs_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
, str
);
123 entry_size
= hfs_cat_build_record(&entry
, cnid
, inode
);
124 err
= hfs_brec_find(&fd
);
125 if (err
!= -ENOENT
) {
131 err
= hfs_brec_insert(&fd
, &entry
, entry_size
);
136 dir
->i_mtime
= dir
->i_ctime
= current_time(dir
);
137 mark_inode_dirty(dir
);
142 hfs_cat_build_key(sb
, fd
.search_key
, cnid
, NULL
);
143 if (!hfs_brec_find(&fd
))
144 hfs_brec_remove(&fd
);
154 * This is the comparison function used for the catalog B-tree. In
155 * comparing catalog B-tree entries, the parent id is the most
156 * significant field (compared as unsigned ints). The name field is
157 * the least significant (compared in "Macintosh lexical order",
158 * see hfs_strcmp() in string.c)
160 * struct hfs_cat_key *key1: pointer to the first key to compare
161 * struct hfs_cat_key *key2: pointer to the second key to compare
162 * Output Variable(s):
165 * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
167 * key1 and key2 point to "valid" (struct hfs_cat_key)s.
169 * This function has no side-effects
171 int hfs_cat_keycmp(const btree_key
*key1
, const btree_key
*key2
)
175 k1p
= key1
->cat
.ParID
;
176 k2p
= key2
->cat
.ParID
;
179 return be32_to_cpu(k1p
) < be32_to_cpu(k2p
) ? -1 : 1;
181 return hfs_strcmp(key1
->cat
.CName
.name
, key1
->cat
.CName
.len
,
182 key2
->cat
.CName
.name
, key2
->cat
.CName
.len
);
185 /* Try to get a catalog entry for given catalog id */
186 // move to read_super???
187 int hfs_cat_find_brec(struct super_block
*sb
, u32 cnid
,
188 struct hfs_find_data
*fd
)
193 hfs_cat_build_key(sb
, fd
->search_key
, cnid
, NULL
);
194 res
= hfs_brec_read(fd
, &rec
, sizeof(rec
));
199 if (type
!= HFS_CDR_THD
&& type
!= HFS_CDR_FTH
) {
200 pr_err("found bad thread record in catalog\n");
204 fd
->search_key
->cat
.ParID
= rec
.thread
.ParID
;
205 len
= fd
->search_key
->cat
.CName
.len
= rec
.thread
.CName
.len
;
206 if (len
> HFS_NAMELEN
) {
207 pr_err("bad catalog namelength\n");
210 memcpy(fd
->search_key
->cat
.CName
.name
, rec
.thread
.CName
.name
, len
);
211 return hfs_brec_find(fd
);
218 * Delete the indicated file or directory.
219 * The associated thread is also removed unless ('with_thread'==0).
221 int hfs_cat_delete(u32 cnid
, struct inode
*dir
, const struct qstr
*str
)
223 struct super_block
*sb
;
224 struct hfs_find_data fd
;
225 struct hfs_readdir_data
*rd
;
228 hfs_dbg(CAT_MOD
, "delete_cat: %s,%u\n", str
? str
->name
: NULL
, cnid
);
230 res
= hfs_find_init(HFS_SB(sb
)->cat_tree
, &fd
);
234 hfs_cat_build_key(sb
, fd
.search_key
, dir
->i_ino
, str
);
235 res
= hfs_brec_find(&fd
);
239 type
= hfs_bnode_read_u8(fd
.bnode
, fd
.entryoffset
);
240 if (type
== HFS_CDR_FIL
) {
241 struct hfs_cat_file file
;
242 hfs_bnode_read(fd
.bnode
, &file
, fd
.entryoffset
, sizeof(file
));
243 if (be32_to_cpu(file
.FlNum
) == cnid
) {
245 hfs_free_fork(sb
, &file
, HFS_FK_DATA
);
247 hfs_free_fork(sb
, &file
, HFS_FK_RSRC
);
251 /* we only need to take spinlock for exclusion with ->release() */
252 spin_lock(&HFS_I(dir
)->open_dir_lock
);
253 list_for_each_entry(rd
, &HFS_I(dir
)->open_dir_list
, list
) {
254 if (fd
.tree
->keycmp(fd
.search_key
, (void *)&rd
->key
) < 0)
257 spin_unlock(&HFS_I(dir
)->open_dir_lock
);
259 res
= hfs_brec_remove(&fd
);
263 hfs_cat_build_key(sb
, fd
.search_key
, cnid
, NULL
);
264 res
= hfs_brec_find(&fd
);
266 res
= hfs_brec_remove(&fd
);
272 dir
->i_mtime
= dir
->i_ctime
= current_time(dir
);
273 mark_inode_dirty(dir
);
284 * Rename a file or directory, possibly to a new directory.
285 * If the destination exists it is removed and a
286 * (struct hfs_cat_entry) for it is returned in '*result'.
288 int hfs_cat_move(u32 cnid
, struct inode
*src_dir
, const struct qstr
*src_name
,
289 struct inode
*dst_dir
, const struct qstr
*dst_name
)
291 struct super_block
*sb
;
292 struct hfs_find_data src_fd
, dst_fd
;
293 union hfs_cat_rec entry
;
294 int entry_size
, type
;
297 hfs_dbg(CAT_MOD
, "rename_cat: %u - %lu,%s - %lu,%s\n",
298 cnid
, src_dir
->i_ino
, src_name
->name
,
299 dst_dir
->i_ino
, dst_name
->name
);
301 err
= hfs_find_init(HFS_SB(sb
)->cat_tree
, &src_fd
);
307 * Fail early and avoid ENOSPC during the btree operations. We may
308 * have to split the root node at most once.
310 err
= hfs_bmap_reserve(src_fd
.tree
, 2 * src_fd
.tree
->depth
);
314 /* find the old dir entry and read the data */
315 hfs_cat_build_key(sb
, src_fd
.search_key
, src_dir
->i_ino
, src_name
);
316 err
= hfs_brec_find(&src_fd
);
319 if (src_fd
.entrylength
> sizeof(entry
) || src_fd
.entrylength
< 0) {
324 hfs_bnode_read(src_fd
.bnode
, &entry
, src_fd
.entryoffset
,
327 /* create new dir entry with the data from the old entry */
328 hfs_cat_build_key(sb
, dst_fd
.search_key
, dst_dir
->i_ino
, dst_name
);
329 err
= hfs_brec_find(&dst_fd
);
330 if (err
!= -ENOENT
) {
336 err
= hfs_brec_insert(&dst_fd
, &entry
, src_fd
.entrylength
);
340 dst_dir
->i_mtime
= dst_dir
->i_ctime
= current_time(dst_dir
);
341 mark_inode_dirty(dst_dir
);
343 /* finally remove the old entry */
344 hfs_cat_build_key(sb
, src_fd
.search_key
, src_dir
->i_ino
, src_name
);
345 err
= hfs_brec_find(&src_fd
);
348 err
= hfs_brec_remove(&src_fd
);
352 src_dir
->i_mtime
= src_dir
->i_ctime
= current_time(src_dir
);
353 mark_inode_dirty(src_dir
);
356 if (type
== HFS_CDR_FIL
&& !(entry
.file
.Flags
& HFS_FIL_THD
))
359 /* remove old thread entry */
360 hfs_cat_build_key(sb
, src_fd
.search_key
, cnid
, NULL
);
361 err
= hfs_brec_find(&src_fd
);
364 err
= hfs_brec_remove(&src_fd
);
368 /* create new thread entry */
369 hfs_cat_build_key(sb
, dst_fd
.search_key
, cnid
, NULL
);
370 entry_size
= hfs_cat_build_thread(sb
, &entry
, type
== HFS_CDR_FIL
? HFS_CDR_FTH
: HFS_CDR_THD
,
371 dst_dir
->i_ino
, dst_name
);
372 err
= hfs_brec_find(&dst_fd
);
373 if (err
!= -ENOENT
) {
378 err
= hfs_brec_insert(&dst_fd
, &entry
, entry_size
);
380 hfs_bnode_put(dst_fd
.bnode
);
381 hfs_find_exit(&src_fd
);