Blame view

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

2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
345
346
347
  	dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s
  ",
  		cnid, src_dir->i_ino, src_name->name,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
  		dst_dir->i_ino, dst_name->name);
5bd9d99d1   Alexey Khoroshilov   hfsplus: add erro...
349
350
351
  	err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
  	dst_fd = src_fd;
  
  	/* find the old dir entry and read the data */
  	hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
  	err = hfs_brec_find(&src_fd);
  	if (err)
  		goto out;
  
  	hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
  				src_fd.entrylength);
  
  	/* create new dir entry with the data from the old entry */
  	hfsplus_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name);
  	err = hfs_brec_find(&dst_fd);
  	if (err != -ENOENT) {
  		if (!err)
  			err = -EEXIST;
  		goto out;
  	}
  
  	err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength);
  	if (err)
  		goto out;
  	dst_dir->i_size++;
  	dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
381
382
383
384
385
386
387
  
  	/* finally remove the old entry */
  	hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
  	err = hfs_brec_find(&src_fd);
  	if (err)
  		goto out;
  	err = hfs_brec_remove(&src_fd);
  	if (err)
  		goto out;
  	src_dir->i_size--;
  	src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
390
391
392
393
394
395
396
397
398
399
400
  
  	/* remove old thread entry */
  	hfsplus_cat_build_key(sb, src_fd.search_key, cnid, NULL);
  	err = hfs_brec_find(&src_fd);
  	if (err)
  		goto out;
  	type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset);
  	err = hfs_brec_remove(&src_fd);
  	if (err)
  		goto out;
  
  	/* create new thread entry */
  	hfsplus_cat_build_key(sb, dst_fd.search_key, cnid, NULL);
2753cc281   Anton Salikhmetov   hfsplus: over 80 ...
401
402
  	entry_size = hfsplus_fill_cat_thread(sb, &entry, type,
  		dst_dir->i_ino, dst_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
405
406
407
408
409
  	err = hfs_brec_find(&dst_fd);
  	if (err != -ENOENT) {
  		if (!err)
  			err = -EEXIST;
  		goto out;
  	}
  	err = hfs_brec_insert(&dst_fd, &entry, entry_size);
e34947056   Christoph Hellwig   hfsplus: optimize...
410
411
412
  
  	hfsplus_mark_inode_dirty(dst_dir, HFSPLUS_I_CAT_DIRTY);
  	hfsplus_mark_inode_dirty(src_dir, HFSPLUS_I_CAT_DIRTY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
416
417
  out:
  	hfs_bnode_put(dst_fd.bnode);
  	hfs_find_exit(&src_fd);
  	return err;
  }