Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dir.c
Go to the documentation of this file.
1 /*
2  * linux/fs/hfsplus/dir.c
3  *
4  * Copyright (C) 2001
5  * Brad Boyer ([email protected])
6  * (C) 2003 Ardis Technologies <[email protected]>
7  *
8  * Handling of directories
9  */
10 
11 #include <linux/errno.h>
12 #include <linux/fs.h>
13 #include <linux/slab.h>
14 #include <linux/random.h>
15 
16 #include "hfsplus_fs.h"
17 #include "hfsplus_raw.h"
18 
19 static inline void hfsplus_instantiate(struct dentry *dentry,
20  struct inode *inode, u32 cnid)
21 {
22  dentry->d_fsdata = (void *)(unsigned long)cnid;
23  d_instantiate(dentry, inode);
24 }
25 
26 /* Find the entry inside dir named dentry->d_name */
27 static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
28  unsigned int flags)
29 {
30  struct inode *inode = NULL;
31  struct hfs_find_data fd;
32  struct super_block *sb;
33  hfsplus_cat_entry entry;
34  int err;
35  u32 cnid, linkid = 0;
36  u16 type;
37 
38  sb = dir->i_sb;
39 
40  dentry->d_fsdata = NULL;
41  err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
42  if (err)
43  return ERR_PTR(err);
44  hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name);
45 again:
46  err = hfs_brec_read(&fd, &entry, sizeof(entry));
47  if (err) {
48  if (err == -ENOENT) {
49  hfs_find_exit(&fd);
50  /* No such entry */
51  inode = NULL;
52  goto out;
53  }
54  goto fail;
55  }
56  type = be16_to_cpu(entry.type);
57  if (type == HFSPLUS_FOLDER) {
58  if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
59  err = -EIO;
60  goto fail;
61  }
62  cnid = be32_to_cpu(entry.folder.id);
63  dentry->d_fsdata = (void *)(unsigned long)cnid;
64  } else if (type == HFSPLUS_FILE) {
65  if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
66  err = -EIO;
67  goto fail;
68  }
69  cnid = be32_to_cpu(entry.file.id);
70  if (entry.file.user_info.fdType ==
72  entry.file.user_info.fdCreator ==
74  (entry.file.create_date ==
75  HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->
76  create_date ||
77  entry.file.create_date ==
78  HFSPLUS_I(sb->s_root->d_inode)->
79  create_date) &&
80  HFSPLUS_SB(sb)->hidden_dir) {
81  struct qstr str;
82  char name[32];
83 
84  if (dentry->d_fsdata) {
85  /*
86  * We found a link pointing to another link,
87  * so ignore it and treat it as regular file.
88  */
89  cnid = (unsigned long)dentry->d_fsdata;
90  linkid = 0;
91  } else {
92  dentry->d_fsdata = (void *)(unsigned long)cnid;
93  linkid =
94  be32_to_cpu(entry.file.permissions.dev);
95  str.len = sprintf(name, "iNode%d", linkid);
96  str.name = name;
97  hfsplus_cat_build_key(sb, fd.search_key,
98  HFSPLUS_SB(sb)->hidden_dir->i_ino,
99  &str);
100  goto again;
101  }
102  } else if (!dentry->d_fsdata)
103  dentry->d_fsdata = (void *)(unsigned long)cnid;
104  } else {
105  printk(KERN_ERR "hfs: invalid catalog entry type in lookup\n");
106  err = -EIO;
107  goto fail;
108  }
109  hfs_find_exit(&fd);
110  inode = hfsplus_iget(dir->i_sb, cnid);
111  if (IS_ERR(inode))
112  return ERR_CAST(inode);
113  if (S_ISREG(inode->i_mode))
114  HFSPLUS_I(inode)->linkid = linkid;
115 out:
116  d_add(dentry, inode);
117  return NULL;
118 fail:
119  hfs_find_exit(&fd);
120  return ERR_PTR(err);
121 }
122 
123 static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
124 {
125  struct inode *inode = filp->f_path.dentry->d_inode;
126  struct super_block *sb = inode->i_sb;
127  int len, err;
128  char strbuf[HFSPLUS_MAX_STRLEN + 1];
129  hfsplus_cat_entry entry;
130  struct hfs_find_data fd;
131  struct hfsplus_readdir_data *rd;
132  u16 type;
133 
134  if (filp->f_pos >= inode->i_size)
135  return 0;
136 
137  err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
138  if (err)
139  return err;
140  hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
141  err = hfs_brec_find(&fd);
142  if (err)
143  goto out;
144 
145  switch ((u32)filp->f_pos) {
146  case 0:
147  /* This is completely artificial... */
148  if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
149  goto out;
150  filp->f_pos++;
151  /* fall through */
152  case 1:
153  if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
154  err = -EIO;
155  goto out;
156  }
157 
158  hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
159  fd.entrylength);
160  if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
161  printk(KERN_ERR "hfs: bad catalog folder thread\n");
162  err = -EIO;
163  goto out;
164  }
165  if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
166  printk(KERN_ERR "hfs: truncated catalog thread\n");
167  err = -EIO;
168  goto out;
169  }
170  if (filldir(dirent, "..", 2, 1,
171  be32_to_cpu(entry.thread.parentID), DT_DIR))
172  goto out;
173  filp->f_pos++;
174  /* fall through */
175  default:
176  if (filp->f_pos >= inode->i_size)
177  goto out;
178  err = hfs_brec_goto(&fd, filp->f_pos - 1);
179  if (err)
180  goto out;
181  }
182 
183  for (;;) {
184  if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
185  printk(KERN_ERR "hfs: walked past end of dir\n");
186  err = -EIO;
187  goto out;
188  }
189 
190  if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
191  err = -EIO;
192  goto out;
193  }
194 
195  hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
196  fd.entrylength);
197  type = be16_to_cpu(entry.type);
198  len = HFSPLUS_MAX_STRLEN;
199  err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
200  if (err)
201  goto out;
202  if (type == HFSPLUS_FOLDER) {
203  if (fd.entrylength <
204  sizeof(struct hfsplus_cat_folder)) {
205  printk(KERN_ERR "hfs: small dir entry\n");
206  err = -EIO;
207  goto out;
208  }
209  if (HFSPLUS_SB(sb)->hidden_dir &&
210  HFSPLUS_SB(sb)->hidden_dir->i_ino ==
211  be32_to_cpu(entry.folder.id))
212  goto next;
213  if (filldir(dirent, strbuf, len, filp->f_pos,
214  be32_to_cpu(entry.folder.id), DT_DIR))
215  break;
216  } else if (type == HFSPLUS_FILE) {
217  if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
218  printk(KERN_ERR "hfs: small file entry\n");
219  err = -EIO;
220  goto out;
221  }
222  if (filldir(dirent, strbuf, len, filp->f_pos,
223  be32_to_cpu(entry.file.id), DT_REG))
224  break;
225  } else {
226  printk(KERN_ERR "hfs: bad catalog entry type\n");
227  err = -EIO;
228  goto out;
229  }
230 next:
231  filp->f_pos++;
232  if (filp->f_pos >= inode->i_size)
233  goto out;
234  err = hfs_brec_goto(&fd, 1);
235  if (err)
236  goto out;
237  }
238  rd = filp->private_data;
239  if (!rd) {
240  rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
241  if (!rd) {
242  err = -ENOMEM;
243  goto out;
244  }
245  filp->private_data = rd;
246  rd->file = filp;
247  list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
248  }
249  memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
250 out:
251  hfs_find_exit(&fd);
252  return err;
253 }
254 
255 static int hfsplus_dir_release(struct inode *inode, struct file *file)
256 {
257  struct hfsplus_readdir_data *rd = file->private_data;
258  if (rd) {
259  mutex_lock(&inode->i_mutex);
260  list_del(&rd->list);
261  mutex_unlock(&inode->i_mutex);
262  kfree(rd);
263  }
264  return 0;
265 }
266 
267 static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
268  struct dentry *dst_dentry)
269 {
270  struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb);
271  struct inode *inode = src_dentry->d_inode;
272  struct inode *src_dir = src_dentry->d_parent->d_inode;
273  struct qstr str;
274  char name[32];
275  u32 cnid, id;
276  int res;
277 
278  if (HFSPLUS_IS_RSRC(inode))
279  return -EPERM;
280  if (!S_ISREG(inode->i_mode))
281  return -EPERM;
282 
283  mutex_lock(&sbi->vh_mutex);
284  if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
285  for (;;) {
286  get_random_bytes(&id, sizeof(cnid));
287  id &= 0x3fffffff;
288  str.name = name;
289  str.len = sprintf(name, "iNode%d", id);
290  res = hfsplus_rename_cat(inode->i_ino,
291  src_dir, &src_dentry->d_name,
292  sbi->hidden_dir, &str);
293  if (!res)
294  break;
295  if (res != -EEXIST)
296  goto out;
297  }
298  HFSPLUS_I(inode)->linkid = id;
299  cnid = sbi->next_cnid++;
300  src_dentry->d_fsdata = (void *)(unsigned long)cnid;
301  res = hfsplus_create_cat(cnid, src_dir,
302  &src_dentry->d_name, inode);
303  if (res)
304  /* panic? */
305  goto out;
306  sbi->file_count++;
307  }
308  cnid = sbi->next_cnid++;
309  res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
310  if (res)
311  goto out;
312 
313  inc_nlink(inode);
314  hfsplus_instantiate(dst_dentry, inode, cnid);
315  ihold(inode);
316  inode->i_ctime = CURRENT_TIME_SEC;
317  mark_inode_dirty(inode);
318  sbi->file_count++;
319  hfsplus_mark_mdb_dirty(dst_dir->i_sb);
320 out:
321  mutex_unlock(&sbi->vh_mutex);
322  return res;
323 }
324 
325 static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
326 {
327  struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
328  struct inode *inode = dentry->d_inode;
329  struct qstr str;
330  char name[32];
331  u32 cnid;
332  int res;
333 
334  if (HFSPLUS_IS_RSRC(inode))
335  return -EPERM;
336 
337  mutex_lock(&sbi->vh_mutex);
338  cnid = (u32)(unsigned long)dentry->d_fsdata;
339  if (inode->i_ino == cnid &&
340  atomic_read(&HFSPLUS_I(inode)->opencnt)) {
341  str.name = name;
342  str.len = sprintf(name, "temp%lu", inode->i_ino);
343  res = hfsplus_rename_cat(inode->i_ino,
344  dir, &dentry->d_name,
345  sbi->hidden_dir, &str);
346  if (!res) {
347  inode->i_flags |= S_DEAD;
348  drop_nlink(inode);
349  }
350  goto out;
351  }
352  res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
353  if (res)
354  goto out;
355 
356  if (inode->i_nlink > 0)
357  drop_nlink(inode);
358  if (inode->i_ino == cnid)
359  clear_nlink(inode);
360  if (!inode->i_nlink) {
361  if (inode->i_ino != cnid) {
362  sbi->file_count--;
363  if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) {
364  res = hfsplus_delete_cat(inode->i_ino,
365  sbi->hidden_dir,
366  NULL);
367  if (!res)
368  hfsplus_delete_inode(inode);
369  } else
370  inode->i_flags |= S_DEAD;
371  } else
372  hfsplus_delete_inode(inode);
373  } else
374  sbi->file_count--;
375  inode->i_ctime = CURRENT_TIME_SEC;
376  mark_inode_dirty(inode);
377 out:
378  mutex_unlock(&sbi->vh_mutex);
379  return res;
380 }
381 
382 static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
383 {
384  struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
385  struct inode *inode = dentry->d_inode;
386  int res;
387 
388  if (inode->i_size != 2)
389  return -ENOTEMPTY;
390 
391  mutex_lock(&sbi->vh_mutex);
392  res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
393  if (res)
394  goto out;
395  clear_nlink(inode);
396  inode->i_ctime = CURRENT_TIME_SEC;
397  hfsplus_delete_inode(inode);
398  mark_inode_dirty(inode);
399 out:
400  mutex_unlock(&sbi->vh_mutex);
401  return res;
402 }
403 
404 static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
405  const char *symname)
406 {
407  struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
408  struct inode *inode;
409  int res = -ENOSPC;
410 
411  mutex_lock(&sbi->vh_mutex);
412  inode = hfsplus_new_inode(dir->i_sb, S_IFLNK | S_IRWXUGO);
413  if (!inode)
414  goto out;
415 
416  res = page_symlink(inode, symname, strlen(symname) + 1);
417  if (res)
418  goto out_err;
419 
420  res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
421  if (res)
422  goto out_err;
423 
424  hfsplus_instantiate(dentry, inode, inode->i_ino);
425  mark_inode_dirty(inode);
426  goto out;
427 
428 out_err:
429  clear_nlink(inode);
430  hfsplus_delete_inode(inode);
431  iput(inode);
432 out:
433  mutex_unlock(&sbi->vh_mutex);
434  return res;
435 }
436 
437 static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
439 {
440  struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
441  struct inode *inode;
442  int res = -ENOSPC;
443 
444  mutex_lock(&sbi->vh_mutex);
445  inode = hfsplus_new_inode(dir->i_sb, mode);
446  if (!inode)
447  goto out;
448 
449  if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))
450  init_special_inode(inode, mode, rdev);
451 
452  res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
453  if (res) {
454  clear_nlink(inode);
455  hfsplus_delete_inode(inode);
456  iput(inode);
457  goto out;
458  }
459 
460  hfsplus_instantiate(dentry, inode, inode->i_ino);
461  mark_inode_dirty(inode);
462 out:
463  mutex_unlock(&sbi->vh_mutex);
464  return res;
465 }
466 
467 static int hfsplus_create(struct inode *dir, struct dentry *dentry, umode_t mode,
468  bool excl)
469 {
470  return hfsplus_mknod(dir, dentry, mode, 0);
471 }
472 
473 static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
474 {
475  return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0);
476 }
477 
478 static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
479  struct inode *new_dir, struct dentry *new_dentry)
480 {
481  int res;
482 
483  /* Unlink destination if it already exists */
484  if (new_dentry->d_inode) {
485  if (S_ISDIR(new_dentry->d_inode->i_mode))
486  res = hfsplus_rmdir(new_dir, new_dentry);
487  else
488  res = hfsplus_unlink(new_dir, new_dentry);
489  if (res)
490  return res;
491  }
492 
493  res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
494  old_dir, &old_dentry->d_name,
495  new_dir, &new_dentry->d_name);
496  if (!res)
497  new_dentry->d_fsdata = old_dentry->d_fsdata;
498  return res;
499 }
500 
502  .lookup = hfsplus_lookup,
503  .create = hfsplus_create,
504  .link = hfsplus_link,
505  .unlink = hfsplus_unlink,
506  .mkdir = hfsplus_mkdir,
507  .rmdir = hfsplus_rmdir,
508  .symlink = hfsplus_symlink,
509  .mknod = hfsplus_mknod,
510  .rename = hfsplus_rename,
511 };
512 
514  .fsync = hfsplus_file_fsync,
515  .read = generic_read_dir,
516  .readdir = hfsplus_readdir,
517  .unlocked_ioctl = hfsplus_ioctl,
518  .llseek = generic_file_llseek,
519  .release = hfsplus_dir_release,
520 };