Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
catalog.c
Go to the documentation of this file.
1 /*
2  * linux/fs/hfsplus/catalog.c
3  *
4  * Copyright (C) 2001
5  * Brad Boyer ([email protected])
6  * (C) 2003 Ardis Technologies <[email protected]>
7  *
8  * Handling of catalog records
9  */
10 
11 
12 #include "hfsplus_fs.h"
13 #include "hfsplus_raw.h"
14 
15 int 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 
28 int 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 
41 void 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 
57 static 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 
70 void hfsplus_cat_set_perms(struct inode *inode, struct hfsplus_perm *perms)
71 {
72  if (inode->i_flags & S_IMMUTABLE)
74  else
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(i_uid_read(inode));
84  perms->group = cpu_to_be32(i_gid_read(inode));
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 
94 static int hfsplus_cat_build_record(hfsplus_cat_entry *entry,
95  u32 cnid, struct inode *inode)
96 {
97  struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
98 
99  if (S_ISDIR(inode->i_mode)) {
100  struct hfsplus_cat_folder *folder;
101 
102  folder = &entry->folder;
103  memset(folder, 0, sizeof(*folder));
104  folder->type = cpu_to_be16(HFSPLUS_FOLDER);
105  folder->id = cpu_to_be32(inode->i_ino);
106  HFSPLUS_I(inode)->create_date =
107  folder->create_date =
108  folder->content_mod_date =
109  folder->attribute_mod_date =
110  folder->access_date = hfsp_now2mt();
111  hfsplus_cat_set_perms(inode, &folder->permissions);
112  if (inode == sbi->hidden_dir)
113  /* invisible and namelocked */
114  folder->user_info.frFlags = cpu_to_be16(0x5000);
115  return sizeof(*folder);
116  } else {
117  struct hfsplus_cat_file *file;
118 
119  file = &entry->file;
120  memset(file, 0, sizeof(*file));
121  file->type = cpu_to_be16(HFSPLUS_FILE);
123  file->id = cpu_to_be32(cnid);
124  HFSPLUS_I(inode)->create_date =
125  file->create_date =
126  file->content_mod_date =
127  file->attribute_mod_date =
128  file->access_date = hfsp_now2mt();
129  if (cnid == inode->i_ino) {
130  hfsplus_cat_set_perms(inode, &file->permissions);
131  if (S_ISLNK(inode->i_mode)) {
132  file->user_info.fdType =
134  file->user_info.fdCreator =
136  } else {
137  file->user_info.fdType =
138  cpu_to_be32(sbi->type);
139  file->user_info.fdCreator =
140  cpu_to_be32(sbi->creator);
141  }
143  (file->permissions.rootflags |
144  file->permissions.userflags))
145  file->flags |=
147  } else {
148  file->user_info.fdType =
150  file->user_info.fdCreator =
152  file->user_info.fdFlags =
153  cpu_to_be16(0x100);
154  file->create_date =
155  HFSPLUS_I(sbi->hidden_dir)->create_date;
156  file->permissions.dev =
157  cpu_to_be32(HFSPLUS_I(inode)->linkid);
158  }
159  return sizeof(*file);
160  }
161 }
162 
163 static int hfsplus_fill_cat_thread(struct super_block *sb,
164  hfsplus_cat_entry *entry, int type,
165  u32 parentid, struct qstr *str)
166 {
167  entry->type = cpu_to_be16(type);
168  entry->thread.reserved = 0;
169  entry->thread.parentID = cpu_to_be32(parentid);
170  hfsplus_asc2uni(sb, &entry->thread.nodeName, str->name, str->len);
171  return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2;
172 }
173 
174 /* Try to get a catalog entry for given catalog id */
175 int hfsplus_find_cat(struct super_block *sb, u32 cnid,
176  struct hfs_find_data *fd)
177 {
178  hfsplus_cat_entry tmp;
179  int err;
180  u16 type;
181 
182  hfsplus_cat_build_key(sb, fd->search_key, cnid, NULL);
183  err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry));
184  if (err)
185  return err;
186 
187  type = be16_to_cpu(tmp.type);
188  if (type != HFSPLUS_FOLDER_THREAD && type != HFSPLUS_FILE_THREAD) {
189  printk(KERN_ERR "hfs: found bad thread record in catalog\n");
190  return -EIO;
191  }
192 
193  if (be16_to_cpu(tmp.thread.nodeName.length) > 255) {
194  printk(KERN_ERR "hfs: catalog name length corrupted\n");
195  return -EIO;
196  }
197 
198  hfsplus_cat_build_key_uni(fd->search_key,
199  be32_to_cpu(tmp.thread.parentID),
200  &tmp.thread.nodeName);
201  return hfs_brec_find(fd);
202 }
203 
204 int hfsplus_create_cat(u32 cnid, struct inode *dir,
205  struct qstr *str, struct inode *inode)
206 {
207  struct super_block *sb = dir->i_sb;
208  struct hfs_find_data fd;
209  hfsplus_cat_entry entry;
210  int entry_size;
211  int err;
212 
213  dprint(DBG_CAT_MOD, "create_cat: %s,%u(%d)\n",
214  str->name, cnid, inode->i_nlink);
215  err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
216  if (err)
217  return err;
218 
219  hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
220  entry_size = hfsplus_fill_cat_thread(sb, &entry,
221  S_ISDIR(inode->i_mode) ?
223  dir->i_ino, str);
224  err = hfs_brec_find(&fd);
225  if (err != -ENOENT) {
226  if (!err)
227  err = -EEXIST;
228  goto err2;
229  }
230  err = hfs_brec_insert(&fd, &entry, entry_size);
231  if (err)
232  goto err2;
233 
234  hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
235  entry_size = hfsplus_cat_build_record(&entry, cnid, inode);
236  err = hfs_brec_find(&fd);
237  if (err != -ENOENT) {
238  /* panic? */
239  if (!err)
240  err = -EEXIST;
241  goto err1;
242  }
243  err = hfs_brec_insert(&fd, &entry, entry_size);
244  if (err)
245  goto err1;
246 
247  dir->i_size++;
248  dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
249  hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
250 
251  hfs_find_exit(&fd);
252  return 0;
253 
254 err1:
255  hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
256  if (!hfs_brec_find(&fd))
257  hfs_brec_remove(&fd);
258 err2:
259  hfs_find_exit(&fd);
260  return err;
261 }
262 
263 int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
264 {
265  struct super_block *sb = dir->i_sb;
266  struct hfs_find_data fd;
267  struct hfsplus_fork_raw fork;
268  struct list_head *pos;
269  int err, off;
270  u16 type;
271 
272  dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n",
273  str ? str->name : NULL, cnid);
274  err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
275  if (err)
276  return err;
277 
278  if (!str) {
279  int len;
280 
281  hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
282  err = hfs_brec_find(&fd);
283  if (err)
284  goto out;
285 
286  off = fd.entryoffset +
287  offsetof(struct hfsplus_cat_thread, nodeName);
288  fd.search_key->cat.parent = cpu_to_be32(dir->i_ino);
290  &fd.search_key->cat.name.length, off, 2);
291  len = be16_to_cpu(fd.search_key->cat.name.length) * 2;
293  &fd.search_key->cat.name.unicode,
294  off + 2, len);
295  fd.search_key->key_len = cpu_to_be16(6 + len);
296  } else
297  hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
298 
299  err = hfs_brec_find(&fd);
300  if (err)
301  goto out;
302 
303  type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
304  if (type == HFSPLUS_FILE) {
305 #if 0
306  off = fd.entryoffset + offsetof(hfsplus_cat_file, data_fork);
307  hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
308  hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_DATA);
309 #endif
310 
311  off = fd.entryoffset +
312  offsetof(struct hfsplus_cat_file, rsrc_fork);
313  hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
314  hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC);
315  }
316 
317  list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) {
318  struct hfsplus_readdir_data *rd =
319  list_entry(pos, struct hfsplus_readdir_data, list);
320  if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
321  rd->file->f_pos--;
322  }
323 
324  err = hfs_brec_remove(&fd);
325  if (err)
326  goto out;
327 
328  hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
329  err = hfs_brec_find(&fd);
330  if (err)
331  goto out;
332 
333  err = hfs_brec_remove(&fd);
334  if (err)
335  goto out;
336 
337  dir->i_size--;
338  dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
339  hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
340 out:
341  hfs_find_exit(&fd);
342 
343  return err;
344 }
345 
347  struct inode *src_dir, struct qstr *src_name,
348  struct inode *dst_dir, struct qstr *dst_name)
349 {
350  struct super_block *sb = src_dir->i_sb;
351  struct hfs_find_data src_fd, dst_fd;
352  hfsplus_cat_entry entry;
353  int entry_size, type;
354  int err;
355 
356  dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n",
357  cnid, src_dir->i_ino, src_name->name,
358  dst_dir->i_ino, dst_name->name);
359  err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd);
360  if (err)
361  return err;
362  dst_fd = src_fd;
363 
364  /* find the old dir entry and read the data */
365  hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
366  err = hfs_brec_find(&src_fd);
367  if (err)
368  goto out;
369  if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) {
370  err = -EIO;
371  goto out;
372  }
373 
374  hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
375  src_fd.entrylength);
376 
377  /* create new dir entry with the data from the old entry */
378  hfsplus_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name);
379  err = hfs_brec_find(&dst_fd);
380  if (err != -ENOENT) {
381  if (!err)
382  err = -EEXIST;
383  goto out;
384  }
385 
386  err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength);
387  if (err)
388  goto out;
389  dst_dir->i_size++;
390  dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC;
391 
392  /* finally remove the old entry */
393  hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
394  err = hfs_brec_find(&src_fd);
395  if (err)
396  goto out;
397  err = hfs_brec_remove(&src_fd);
398  if (err)
399  goto out;
400  src_dir->i_size--;
401  src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC;
402 
403  /* remove old thread entry */
404  hfsplus_cat_build_key(sb, src_fd.search_key, cnid, NULL);
405  err = hfs_brec_find(&src_fd);
406  if (err)
407  goto out;
408  type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset);
409  err = hfs_brec_remove(&src_fd);
410  if (err)
411  goto out;
412 
413  /* create new thread entry */
414  hfsplus_cat_build_key(sb, dst_fd.search_key, cnid, NULL);
415  entry_size = hfsplus_fill_cat_thread(sb, &entry, type,
416  dst_dir->i_ino, dst_name);
417  err = hfs_brec_find(&dst_fd);
418  if (err != -ENOENT) {
419  if (!err)
420  err = -EEXIST;
421  goto out;
422  }
423  err = hfs_brec_insert(&dst_fd, &entry, entry_size);
424 
425  hfsplus_mark_inode_dirty(dst_dir, HFSPLUS_I_CAT_DIRTY);
426  hfsplus_mark_inode_dirty(src_dir, HFSPLUS_I_CAT_DIRTY);
427 out:
428  hfs_bnode_put(dst_fd.bnode);
429  hfs_find_exit(&src_fd);
430  return err;
431 }