Blame view

fs/hfs/catalog.c 9.76 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  /*
   *  linux/fs/hfs/catalog.c
   *
   * Copyright (C) 1995-1997  Paul H. Hargrove
   * (C) 2003 Ardis Technologies <roman@ardistech.com>
   * This file may be distributed under the terms of the GNU General Public License.
   *
   * This file contains the functions related to the catalog B-tree.
   *
   * Cache code shamelessly stolen from
   *     linux/fs/inode.c Copyright (C) 1991, 1992  Linus Torvalds
   *     re-shamelessly stolen Copyright (C) 1997 Linus Torvalds
   */
  
  #include "hfs_fs.h"
  #include "btree.h"
  
  /*
   * hfs_cat_build_key()
   *
   * Given the ID of the parent and the name build a search key.
   */
71e939634   Al Viro   qstr: constify in...
23
  void hfs_cat_build_key(struct super_block *sb, btree_key *key, u32 parent, const struct qstr *name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
  {
  	key->cat.reserved = 0;
  	key->cat.ParID = cpu_to_be32(parent);
  	if (name) {
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
28
  		hfs_asc2mac(sb, &key->cat.CName, name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
33
34
35
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
  		key->key_len = 6 + key->cat.CName.len;
  	} else {
  		memset(&key->cat.CName, 0, sizeof(struct hfs_name));
  		key->key_len = 6;
  	}
  }
  
  static int hfs_cat_build_record(hfs_cat_rec *rec, u32 cnid, struct inode *inode)
  {
  	__be32 mtime = hfs_mtime();
  
  	memset(rec, 0, sizeof(*rec));
  	if (S_ISDIR(inode->i_mode)) {
  		rec->type = HFS_CDR_DIR;
  		rec->dir.DirID = cpu_to_be32(cnid);
  		rec->dir.CrDat = mtime;
  		rec->dir.MdDat = mtime;
  		rec->dir.BkDat = 0;
  		rec->dir.UsrInfo.frView = cpu_to_be16(0xff);
  		return sizeof(struct hfs_cat_dir);
  	} else {
  		/* init some fields for the file record */
  		rec->type = HFS_CDR_FIL;
  		rec->file.Flags = HFS_FIL_USED | HFS_FIL_THD;
  		if (!(inode->i_mode & S_IWUSR))
  			rec->file.Flags |= HFS_FIL_LOCK;
  		rec->file.FlNum = cpu_to_be32(cnid);
  		rec->file.CrDat = mtime;
  		rec->file.MdDat = mtime;
  		rec->file.BkDat = 0;
  		rec->file.UsrWds.fdType = HFS_SB(inode->i_sb)->s_type;
  		rec->file.UsrWds.fdCreator = HFS_SB(inode->i_sb)->s_creator;
  		return sizeof(struct hfs_cat_file);
  	}
  }
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
64
65
  static int hfs_cat_build_thread(struct super_block *sb,
  				hfs_cat_rec *rec, int type,
71e939634   Al Viro   qstr: constify in...
66
  				u32 parentid, const struct qstr *name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
70
  {
  	rec->type = type;
  	memset(rec->thread.reserved, 0, sizeof(rec->thread.reserved));
  	rec->thread.ParID = cpu_to_be32(parentid);
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
71
  	hfs_asc2mac(sb, &rec->thread.CName, name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
76
77
78
79
80
  	return sizeof(struct hfs_cat_thread);
  }
  
  /*
   * create_entry()
   *
   * Add a new file or directory to the catalog B-tree and
   * return a (struct hfs_cat_entry) for it in '*result'.
   */
71e939634   Al Viro   qstr: constify in...
81
  int hfs_cat_create(u32 cnid, struct inode *dir, const struct qstr *str, struct inode *inode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
84
85
86
87
  {
  	struct hfs_find_data fd;
  	struct super_block *sb;
  	union hfs_cat_rec entry;
  	int entry_size;
  	int err;
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
88
89
90
  	hfs_dbg(CAT_MOD, "create_cat: %s,%u(%d)
  ",
  		str->name, cnid, inode->i_nlink);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
93
94
  	if (dir->i_size >= HFS_MAX_VALENCE)
  		return -ENOSPC;
  
  	sb = dir->i_sb;
9509f1785   Alexey Khoroshilov   hfs: add error ch...
95
96
97
  	err = hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98

54640c750   Ernesto A. Fernández   hfs: prevent btre...
99
100
101
102
103
104
105
  	/*
  	 * Fail early and avoid ENOSPC during the btree operations. We may
  	 * have to split the root node at most once.
  	 */
  	err = hfs_bmap_reserve(fd.tree, 2 * fd.tree->depth);
  	if (err)
  		goto err2;
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
106
107
  	hfs_cat_build_key(sb, fd.search_key, cnid, NULL);
  	entry_size = hfs_cat_build_thread(sb, &entry, S_ISDIR(inode->i_mode) ?
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
111
112
113
114
115
116
117
118
  			HFS_CDR_THD : HFS_CDR_FTH,
  			dir->i_ino, str);
  	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;
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
119
  	hfs_cat_build_key(sb, fd.search_key, dir->i_ino, str);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
122
123
124
125
126
127
128
129
130
131
132
  	entry_size = hfs_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++;
02027d42c   Deepa Dinamani   fs: Replace CURRE...
133
  	dir->i_mtime = dir->i_ctime = current_time(dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134
135
136
137
138
  	mark_inode_dirty(dir);
  	hfs_find_exit(&fd);
  	return 0;
  
  err1:
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
139
  	hfs_cat_build_key(sb, fd.search_key, cnid, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  	if (!hfs_brec_find(&fd))
  		hfs_brec_remove(&fd);
  err2:
  	hfs_find_exit(&fd);
  	return err;
  }
  
  /*
   * hfs_cat_compare()
   *
   * Description:
   *   This is the comparison function used for the catalog B-tree.  In
   *   comparing catalog B-tree entries, the parent id is the most
   *   significant field (compared as unsigned ints).  The name field is
   *   the least significant (compared in "Macintosh lexical order",
   *   see hfs_strcmp() in string.c)
   * Input Variable(s):
   *   struct hfs_cat_key *key1: pointer to the first key to compare
   *   struct hfs_cat_key *key2: pointer to the second key to compare
   * Output Variable(s):
   *   NONE
   * Returns:
   *   int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
   * Preconditions:
   *   key1 and key2 point to "valid" (struct hfs_cat_key)s.
   * Postconditions:
   *   This function has no side-effects
   */
  int hfs_cat_keycmp(const btree_key *key1, const btree_key *key2)
  {
ddbc22e27   Rasmus Villemoes   fs/hfs/catalog.c:...
170
  	__be32 k1p, k2p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171

ddbc22e27   Rasmus Villemoes   fs/hfs/catalog.c:...
172
173
  	k1p = key1->cat.ParID;
  	k2p = key2->cat.ParID;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174

ddbc22e27   Rasmus Villemoes   fs/hfs/catalog.c:...
175
176
177
178
179
  	if (k1p != k2p)
  		return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
  
  	return hfs_strcmp(key1->cat.CName.name, key1->cat.CName.len,
  			  key2->cat.CName.name, key2->cat.CName.len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
182
183
184
185
186
187
188
  }
  
  /* Try to get a catalog entry for given catalog id */
  // move to read_super???
  int hfs_cat_find_brec(struct super_block *sb, u32 cnid,
  		      struct hfs_find_data *fd)
  {
  	hfs_cat_rec rec;
  	int res, len, type;
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
189
  	hfs_cat_build_key(sb, fd->search_key, cnid, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
194
195
  	res = hfs_brec_read(fd, &rec, sizeof(rec));
  	if (res)
  		return res;
  
  	type = rec.type;
  	if (type != HFS_CDR_THD && type != HFS_CDR_FTH) {
d61426732   Joe Perches   hfs/hfsplus: conv...
196
197
  		pr_err("found bad thread record in catalog
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
201
202
  		return -EIO;
  	}
  
  	fd->search_key->cat.ParID = rec.thread.ParID;
  	len = fd->search_key->cat.CName.len = rec.thread.CName.len;
d38b7aa7f   Eric Sesterhenn   hfs: fix nameleng...
203
  	if (len > HFS_NAMELEN) {
d61426732   Joe Perches   hfs/hfsplus: conv...
204
205
  		pr_err("bad catalog namelength
  ");
d38b7aa7f   Eric Sesterhenn   hfs: fix nameleng...
206
207
  		return -EIO;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
211
212
213
214
215
216
217
218
  	memcpy(fd->search_key->cat.CName.name, rec.thread.CName.name, len);
  	return hfs_brec_find(fd);
  }
  
  
  /*
   * hfs_cat_delete()
   *
   * Delete the indicated file or directory.
   * The associated thread is also removed unless ('with_thread'==0).
   */
71e939634   Al Viro   qstr: constify in...
219
  int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
220
221
222
  {
  	struct super_block *sb;
  	struct hfs_find_data fd;
2c35dea27   Geliang Tang   fs/hfs/catalog.c:...
223
  	struct hfs_readdir_data *rd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  	int res, type;
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
225
226
  	hfs_dbg(CAT_MOD, "delete_cat: %s,%u
  ", str ? str->name : NULL, cnid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
  	sb = dir->i_sb;
9509f1785   Alexey Khoroshilov   hfs: add error ch...
228
229
230
  	res = hfs_find_init(HFS_SB(sb)->cat_tree, &fd);
  	if (res)
  		return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
231

328b92278   Roman Zippel   [PATCH] hfs: NLS ...
232
  	hfs_cat_build_key(sb, fd.search_key, dir->i_ino, str);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  	res = hfs_brec_find(&fd);
  	if (res)
  		goto out;
  
  	type = hfs_bnode_read_u8(fd.bnode, fd.entryoffset);
  	if (type == HFS_CDR_FIL) {
  		struct hfs_cat_file file;
  		hfs_bnode_read(fd.bnode, &file, fd.entryoffset, sizeof(file));
  		if (be32_to_cpu(file.FlNum) == cnid) {
  #if 0
  			hfs_free_fork(sb, &file, HFS_FK_DATA);
  #endif
  			hfs_free_fork(sb, &file, HFS_FK_RSRC);
  		}
  	}
9717a91b0   Al Viro   hfs: switch to ->...
248
249
  	/* we only need to take spinlock for exclusion with ->release() */
  	spin_lock(&HFS_I(dir)->open_dir_lock);
2c35dea27   Geliang Tang   fs/hfs/catalog.c:...
250
  	list_for_each_entry(rd, &HFS_I(dir)->open_dir_list, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
  		if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
  			rd->file->f_pos--;
  	}
9717a91b0   Al Viro   hfs: switch to ->...
254
  	spin_unlock(&HFS_I(dir)->open_dir_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
258
  
  	res = hfs_brec_remove(&fd);
  	if (res)
  		goto out;
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
259
  	hfs_cat_build_key(sb, fd.search_key, cnid, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
262
263
264
265
266
267
  	res = hfs_brec_find(&fd);
  	if (!res) {
  		res = hfs_brec_remove(&fd);
  		if (res)
  			goto out;
  	}
  
  	dir->i_size--;
02027d42c   Deepa Dinamani   fs: Replace CURRE...
268
  	dir->i_mtime = dir->i_ctime = current_time(dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
  	mark_inode_dirty(dir);
  	res = 0;
  out:
  	hfs_find_exit(&fd);
  
  	return res;
  }
  
  /*
   * hfs_cat_move()
   *
   * Rename a file or directory, possibly to a new directory.
   * If the destination exists it is removed and a
   * (struct hfs_cat_entry) for it is returned in '*result'.
   */
71e939634   Al Viro   qstr: constify in...
284
285
  int hfs_cat_move(u32 cnid, struct inode *src_dir, const struct qstr *src_name,
  		 struct inode *dst_dir, const struct qstr *dst_name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
288
289
290
291
  {
  	struct super_block *sb;
  	struct hfs_find_data src_fd, dst_fd;
  	union hfs_cat_rec entry;
  	int entry_size, type;
  	int err;
c2b3e1f76   Joe Perches   hfs/hfsplus: conv...
292
293
294
  	hfs_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
295
296
  		dst_dir->i_ino, dst_name->name);
  	sb = src_dir->i_sb;
9509f1785   Alexey Khoroshilov   hfs: add error ch...
297
298
299
  	err = hfs_find_init(HFS_SB(sb)->cat_tree, &src_fd);
  	if (err)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
  	dst_fd = src_fd;
54640c750   Ernesto A. Fernández   hfs: prevent btre...
301
302
303
304
305
306
307
  	/*
  	 * Fail early and avoid ENOSPC during the btree operations. We may
  	 * have to split the root node at most once.
  	 */
  	err = hfs_bmap_reserve(src_fd.tree, 2 * src_fd.tree->depth);
  	if (err)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
  	/* find the old dir entry and read the data */
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
309
  	hfs_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
311
312
  	err = hfs_brec_find(&src_fd);
  	if (err)
  		goto out;
ec81aecb2   Amerigo Wang   hfs: fix a potent...
313
314
315
316
  	if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) {
  		err = -EIO;
  		goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
318
319
320
321
  
  	hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
  			    src_fd.entrylength);
  
  	/* create new dir entry with the data from the old entry */
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
322
  	hfs_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
323
324
325
326
327
328
329
330
331
332
333
  	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++;
02027d42c   Deepa Dinamani   fs: Replace CURRE...
334
  	dst_dir->i_mtime = dst_dir->i_ctime = current_time(dst_dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
  	mark_inode_dirty(dst_dir);
  
  	/* finally remove the old entry */
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
338
  	hfs_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
340
341
342
343
344
345
  	err = hfs_brec_find(&src_fd);
  	if (err)
  		goto out;
  	err = hfs_brec_remove(&src_fd);
  	if (err)
  		goto out;
  	src_dir->i_size--;
02027d42c   Deepa Dinamani   fs: Replace CURRE...
346
  	src_dir->i_mtime = src_dir->i_ctime = current_time(src_dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
350
351
352
353
  	mark_inode_dirty(src_dir);
  
  	type = entry.type;
  	if (type == HFS_CDR_FIL && !(entry.file.Flags & HFS_FIL_THD))
  		goto out;
  
  	/* remove old thread entry */
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
354
  	hfs_cat_build_key(sb, src_fd.search_key, cnid, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
356
357
358
359
360
361
362
  	err = hfs_brec_find(&src_fd);
  	if (err)
  		goto out;
  	err = hfs_brec_remove(&src_fd);
  	if (err)
  		goto out;
  
  	/* create new thread entry */
328b92278   Roman Zippel   [PATCH] hfs: NLS ...
363
364
  	hfs_cat_build_key(sb, dst_fd.search_key, cnid, NULL);
  	entry_size = hfs_cat_build_thread(sb, &entry, type == HFS_CDR_FIL ? HFS_CDR_FTH : HFS_CDR_THD,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
368
369
370
371
372
373
374
375
376
377
  					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, entry_size);
  out:
  	hfs_bnode_put(dst_fd.bnode);
  	hfs_find_exit(&src_fd);
  	return err;
  }