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