xref: /linux/fs/hfsplus/catalog.c (revision f3d9478b2ce468c3115b02ecae7e975990697f15)
1 /*
2  *  linux/fs/hfsplus/catalog.c
3  *
4  * Copyright (C) 2001
5  * Brad Boyer (flar@allandria.com)
6  * (C) 2003 Ardis Technologies <roman@ardistech.com>
7  *
8  * Handling of catalog records
9  */
10 
11 #include <linux/sched.h>
12 
13 #include "hfsplus_fs.h"
14 #include "hfsplus_raw.h"
15 
16 int hfsplus_cat_case_cmp_key(const hfsplus_btree_key *k1,
17 			     const hfsplus_btree_key *k2)
18 {
19 	__be32 k1p, k2p;
20 
21 	k1p = k1->cat.parent;
22 	k2p = k2->cat.parent;
23 	if (k1p != k2p)
24 		return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
25 
26 	return hfsplus_strcasecmp(&k1->cat.name, &k2->cat.name);
27 }
28 
29 int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1,
30 			    const hfsplus_btree_key *k2)
31 {
32 	__be32 k1p, k2p;
33 
34 	k1p = k1->cat.parent;
35 	k2p = k2->cat.parent;
36 	if (k1p != k2p)
37 		return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
38 
39 	return hfsplus_strcmp(&k1->cat.name, &k2->cat.name);
40 }
41 
42 void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key,
43 			   u32 parent, struct qstr *str)
44 {
45 	int len;
46 
47 	key->cat.parent = cpu_to_be32(parent);
48 	if (str) {
49 		hfsplus_asc2uni(sb, &key->cat.name, str->name, str->len);
50 		len = be16_to_cpu(key->cat.name.length);
51 	} else {
52 		key->cat.name.length = 0;
53 		len = 0;
54 	}
55 	key->key_len = cpu_to_be16(6 + 2 * len);
56 }
57 
58 static void hfsplus_cat_build_key_uni(hfsplus_btree_key *key, u32 parent,
59 				      struct hfsplus_unistr *name)
60 {
61 	int ustrlen;
62 
63 	ustrlen = be16_to_cpu(name->length);
64 	key->cat.parent = cpu_to_be32(parent);
65 	key->cat.name.length = cpu_to_be16(ustrlen);
66 	ustrlen *= 2;
67 	memcpy(key->cat.name.unicode, name->unicode, ustrlen);
68 	key->key_len = cpu_to_be16(6 + ustrlen);
69 }
70 
71 static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms)
72 {
73 	if (inode->i_flags & S_IMMUTABLE)
74 		perms->rootflags |= HFSPLUS_FLG_IMMUTABLE;
75 	else
76 		perms->rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
77 	if (inode->i_flags & S_APPEND)
78 		perms->rootflags |= HFSPLUS_FLG_APPEND;
79 	else
80 		perms->rootflags &= ~HFSPLUS_FLG_APPEND;
81 	HFSPLUS_I(inode).rootflags = perms->rootflags;
82 	HFSPLUS_I(inode).userflags = perms->userflags;
83 	perms->mode = cpu_to_be16(inode->i_mode);
84 	perms->owner = cpu_to_be32(inode->i_uid);
85 	perms->group = cpu_to_be32(inode->i_gid);
86 }
87 
88 static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct inode *inode)
89 {
90 	if (S_ISDIR(inode->i_mode)) {
91 		struct hfsplus_cat_folder *folder;
92 
93 		folder = &entry->folder;
94 		memset(folder, 0, sizeof(*folder));
95 		folder->type = cpu_to_be16(HFSPLUS_FOLDER);
96 		folder->id = cpu_to_be32(inode->i_ino);
97 		HFSPLUS_I(inode).create_date =
98 			folder->create_date =
99 			folder->content_mod_date =
100 			folder->attribute_mod_date =
101 			folder->access_date = hfsp_now2mt();
102 		hfsplus_set_perms(inode, &folder->permissions);
103 		if (inode == HFSPLUS_SB(inode->i_sb).hidden_dir)
104 			/* invisible and namelocked */
105 			folder->user_info.frFlags = cpu_to_be16(0x5000);
106 		return sizeof(*folder);
107 	} else {
108 		struct hfsplus_cat_file *file;
109 
110 		file = &entry->file;
111 		memset(file, 0, sizeof(*file));
112 		file->type = cpu_to_be16(HFSPLUS_FILE);
113 		file->flags = cpu_to_be16(HFSPLUS_FILE_THREAD_EXISTS);
114 		file->id = cpu_to_be32(cnid);
115 		HFSPLUS_I(inode).create_date =
116 			file->create_date =
117 			file->content_mod_date =
118 			file->attribute_mod_date =
119 			file->access_date = hfsp_now2mt();
120 		if (cnid == inode->i_ino) {
121 			hfsplus_set_perms(inode, &file->permissions);
122 			if (S_ISLNK(inode->i_mode)) {
123 				file->user_info.fdType = cpu_to_be32(HFSP_SYMLINK_TYPE);
124 				file->user_info.fdCreator = cpu_to_be32(HFSP_SYMLINK_CREATOR);
125 			} else {
126 				file->user_info.fdType = cpu_to_be32(HFSPLUS_SB(inode->i_sb).type);
127 				file->user_info.fdCreator = cpu_to_be32(HFSPLUS_SB(inode->i_sb).creator);
128 			}
129 			if ((file->permissions.rootflags | file->permissions.userflags) & HFSPLUS_FLG_IMMUTABLE)
130 				file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED);
131 		} else {
132 			file->user_info.fdType = cpu_to_be32(HFSP_HARDLINK_TYPE);
133 			file->user_info.fdCreator = cpu_to_be32(HFSP_HFSPLUS_CREATOR);
134 			file->user_info.fdFlags = cpu_to_be16(0x100);
135 			file->create_date = HFSPLUS_I(HFSPLUS_SB(inode->i_sb).hidden_dir).create_date;
136 			file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode).dev);
137 		}
138 		return sizeof(*file);
139 	}
140 }
141 
142 static int hfsplus_fill_cat_thread(struct super_block *sb,
143 				   hfsplus_cat_entry *entry, int type,
144 				   u32 parentid, struct qstr *str)
145 {
146 	entry->type = cpu_to_be16(type);
147 	entry->thread.reserved = 0;
148 	entry->thread.parentID = cpu_to_be32(parentid);
149 	hfsplus_asc2uni(sb, &entry->thread.nodeName, str->name, str->len);
150 	return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2;
151 }
152 
153 /* Try to get a catalog entry for given catalog id */
154 int hfsplus_find_cat(struct super_block *sb, u32 cnid,
155 		     struct hfs_find_data *fd)
156 {
157 	hfsplus_cat_entry tmp;
158 	int err;
159 	u16 type;
160 
161 	hfsplus_cat_build_key(sb, fd->search_key, cnid, NULL);
162 	err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry));
163 	if (err)
164 		return err;
165 
166 	type = be16_to_cpu(tmp.type);
167 	if (type != HFSPLUS_FOLDER_THREAD && type != HFSPLUS_FILE_THREAD) {
168 		printk(KERN_ERR "hfs: found bad thread record in catalog\n");
169 		return -EIO;
170 	}
171 
172 	hfsplus_cat_build_key_uni(fd->search_key, be32_to_cpu(tmp.thread.parentID),
173 				 &tmp.thread.nodeName);
174 	return hfs_brec_find(fd);
175 }
176 
177 int hfsplus_create_cat(u32 cnid, struct inode *dir, struct qstr *str, struct inode *inode)
178 {
179 	struct hfs_find_data fd;
180 	struct super_block *sb;
181 	hfsplus_cat_entry entry;
182 	int entry_size;
183 	int err;
184 
185 	dprint(DBG_CAT_MOD, "create_cat: %s,%u(%d)\n", str->name, cnid, inode->i_nlink);
186 	sb = dir->i_sb;
187 	hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
188 
189 	hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
190 	entry_size = hfsplus_fill_cat_thread(sb, &entry, S_ISDIR(inode->i_mode) ?
191 			HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD,
192 			dir->i_ino, str);
193 	err = hfs_brec_find(&fd);
194 	if (err != -ENOENT) {
195 		if (!err)
196 			err = -EEXIST;
197 		goto err2;
198 	}
199 	err = hfs_brec_insert(&fd, &entry, entry_size);
200 	if (err)
201 		goto err2;
202 
203 	hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
204 	entry_size = hfsplus_cat_build_record(&entry, cnid, inode);
205 	err = hfs_brec_find(&fd);
206 	if (err != -ENOENT) {
207 		/* panic? */
208 		if (!err)
209 			err = -EEXIST;
210 		goto err1;
211 	}
212 	err = hfs_brec_insert(&fd, &entry, entry_size);
213 	if (err)
214 		goto err1;
215 
216 	dir->i_size++;
217 	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
218 	mark_inode_dirty(dir);
219 	hfs_find_exit(&fd);
220 	return 0;
221 
222 err1:
223 	hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
224 	if (!hfs_brec_find(&fd))
225 		hfs_brec_remove(&fd);
226 err2:
227 	hfs_find_exit(&fd);
228 	return err;
229 }
230 
231 int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
232 {
233 	struct super_block *sb;
234 	struct hfs_find_data fd;
235 	struct hfsplus_fork_raw fork;
236 	struct list_head *pos;
237 	int err, off;
238 	u16 type;
239 
240 	dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid);
241 	sb = dir->i_sb;
242 	hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
243 
244 	if (!str) {
245 		int len;
246 
247 		hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
248 		err = hfs_brec_find(&fd);
249 		if (err)
250 			goto out;
251 
252 		off = fd.entryoffset + offsetof(struct hfsplus_cat_thread, nodeName);
253 		fd.search_key->cat.parent = cpu_to_be32(dir->i_ino);
254 		hfs_bnode_read(fd.bnode, &fd.search_key->cat.name.length, off, 2);
255 		len = be16_to_cpu(fd.search_key->cat.name.length) * 2;
256 		hfs_bnode_read(fd.bnode, &fd.search_key->cat.name.unicode, off + 2, len);
257 		fd.search_key->key_len = cpu_to_be16(6 + len);
258 	} else
259 		hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
260 
261 	err = hfs_brec_find(&fd);
262 	if (err)
263 		goto out;
264 
265 	type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
266 	if (type == HFSPLUS_FILE) {
267 #if 0
268 		off = fd.entryoffset + offsetof(hfsplus_cat_file, data_fork);
269 		hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
270 		hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_DATA);
271 #endif
272 
273 		off = fd.entryoffset + offsetof(struct hfsplus_cat_file, rsrc_fork);
274 		hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
275 		hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC);
276 	}
277 
278 	list_for_each(pos, &HFSPLUS_I(dir).open_dir_list) {
279 		struct hfsplus_readdir_data *rd =
280 			list_entry(pos, struct hfsplus_readdir_data, list);
281 		if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
282 			rd->file->f_pos--;
283 	}
284 
285 	err = hfs_brec_remove(&fd);
286 	if (err)
287 		goto out;
288 
289 	hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
290 	err = hfs_brec_find(&fd);
291 	if (err)
292 		goto out;
293 
294 	err = hfs_brec_remove(&fd);
295 	if (err)
296 		goto out;
297 
298 	dir->i_size--;
299 	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
300 	mark_inode_dirty(dir);
301 out:
302 	hfs_find_exit(&fd);
303 
304 	return err;
305 }
306 
307 int hfsplus_rename_cat(u32 cnid,
308 		       struct inode *src_dir, struct qstr *src_name,
309 		       struct inode *dst_dir, struct qstr *dst_name)
310 {
311 	struct super_block *sb;
312 	struct hfs_find_data src_fd, dst_fd;
313 	hfsplus_cat_entry entry;
314 	int entry_size, type;
315 	int err = 0;
316 
317 	dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n", cnid, src_dir->i_ino, src_name->name,
318 		dst_dir->i_ino, dst_name->name);
319 	sb = src_dir->i_sb;
320 	hfs_find_init(HFSPLUS_SB(sb).cat_tree, &src_fd);
321 	dst_fd = src_fd;
322 
323 	/* find the old dir entry and read the data */
324 	hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
325 	err = hfs_brec_find(&src_fd);
326 	if (err)
327 		goto out;
328 
329 	hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
330 				src_fd.entrylength);
331 
332 	/* create new dir entry with the data from the old entry */
333 	hfsplus_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name);
334 	err = hfs_brec_find(&dst_fd);
335 	if (err != -ENOENT) {
336 		if (!err)
337 			err = -EEXIST;
338 		goto out;
339 	}
340 
341 	err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength);
342 	if (err)
343 		goto out;
344 	dst_dir->i_size++;
345 	dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC;
346 	mark_inode_dirty(dst_dir);
347 
348 	/* finally remove the old entry */
349 	hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
350 	err = hfs_brec_find(&src_fd);
351 	if (err)
352 		goto out;
353 	err = hfs_brec_remove(&src_fd);
354 	if (err)
355 		goto out;
356 	src_dir->i_size--;
357 	src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC;
358 	mark_inode_dirty(src_dir);
359 
360 	/* remove old thread entry */
361 	hfsplus_cat_build_key(sb, src_fd.search_key, cnid, NULL);
362 	err = hfs_brec_find(&src_fd);
363 	if (err)
364 		goto out;
365 	type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset);
366 	err = hfs_brec_remove(&src_fd);
367 	if (err)
368 		goto out;
369 
370 	/* create new thread entry */
371 	hfsplus_cat_build_key(sb, dst_fd.search_key, cnid, NULL);
372 	entry_size = hfsplus_fill_cat_thread(sb, &entry, type, dst_dir->i_ino, dst_name);
373 	err = hfs_brec_find(&dst_fd);
374 	if (err != -ENOENT) {
375 		if (!err)
376 			err = -EEXIST;
377 		goto out;
378 	}
379 	err = hfs_brec_insert(&dst_fd, &entry, entry_size);
380 out:
381 	hfs_bnode_put(dst_fd.bnode);
382 	hfs_find_exit(&src_fd);
383 	return err;
384 }
385