dir_nat.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:13k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * linux/fs/hfs/dir_nat.c
  3.  *
  4.  * Copyright (C) 1995-1997  Paul H. Hargrove
  5.  * This file may be distributed under the terms of the GNU General Public License.
  6.  *
  7.  * This file contains the inode_operations and file_operations
  8.  * structures for HFS directories.
  9.  *
  10.  * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
  11.  *
  12.  * The source code distributions of Netatalk, versions 1.3.3b2 and
  13.  * 1.4b2, were used as a specification of the location and format of
  14.  * files used by Netatalk's afpd.  No code from Netatalk appears in
  15.  * hfs_fs.  hfs_fs is not a work ``derived'' from Netatalk in the
  16.  * sense of intellectual property law.
  17.  *
  18.  * "XXX" in a comment is a note to myself to consider changing something.
  19.  *
  20.  * In function preconditions the term "valid" applied to a pointer to
  21.  * a structure means that the pointer is non-NULL and the structure it
  22.  * points to has all fields initialized to consistent values.
  23.  */
  24. #include "hfs.h"
  25. #include <linux/hfs_fs_sb.h>
  26. #include <linux/hfs_fs_i.h>
  27. #include <linux/hfs_fs.h>
  28. /*================ Forward declarations ================*/
  29. static struct dentry *nat_lookup(struct inode *, struct dentry *);
  30. static int nat_readdir(struct file *, void *, filldir_t);
  31. static int nat_rmdir(struct inode *, struct dentry *);
  32. static int nat_hdr_unlink(struct inode *, struct dentry *);
  33. static int nat_hdr_rename(struct inode *, struct dentry *,
  34.   struct inode *, struct dentry *);
  35. /*================ Global variables ================*/
  36. #define DOT_LEN 1
  37. #define DOT_DOT_LEN 2
  38. #define DOT_APPLEDOUBLE_LEN 12
  39. #define DOT_PARENT_LEN 7
  40. #define ROOTINFO_LEN            8
  41. const struct hfs_name hfs_nat_reserved1[] = {
  42. {DOT_LEN, "."},
  43. {DOT_DOT_LEN, ".."},
  44. {DOT_APPLEDOUBLE_LEN, ".AppleDouble"},
  45. {DOT_PARENT_LEN, ".Parent"},
  46. {0, ""},
  47. };
  48. const struct hfs_name hfs_nat_reserved2[] = {
  49. {ROOTINFO_LEN, "RootInfo"},
  50. };
  51. #define DOT (&hfs_nat_reserved1[0])
  52. #define DOT_DOT (&hfs_nat_reserved1[1])
  53. #define DOT_APPLEDOUBLE (&hfs_nat_reserved1[2])
  54. #define DOT_PARENT (&hfs_nat_reserved1[3])
  55. #define ROOTINFO        (&hfs_nat_reserved2[0])
  56. struct file_operations hfs_nat_dir_operations = {
  57. read: generic_read_dir,
  58. readdir: nat_readdir,
  59. fsync: file_fsync,
  60. };
  61. struct inode_operations hfs_nat_ndir_inode_operations = {
  62. create: hfs_create,
  63. lookup: nat_lookup,
  64. unlink: hfs_unlink,
  65. mkdir: hfs_mkdir,
  66. rmdir: nat_rmdir,
  67. rename: hfs_rename,
  68. setattr: hfs_notify_change,
  69. };
  70. struct inode_operations hfs_nat_hdir_inode_operations = {
  71. create: hfs_create,
  72. lookup: nat_lookup,
  73. unlink: nat_hdr_unlink,
  74. rename: nat_hdr_rename,
  75. setattr: hfs_notify_change,
  76. };
  77. /*================ File-local functions ================*/
  78. /*
  79.  * nat_lookup()
  80.  *
  81.  * This is the lookup() entry in the inode_operations structure for
  82.  * HFS directories in the Netatalk scheme.  The purpose is to generate
  83.  * the inode corresponding to an entry in a directory, given the inode
  84.  * for the directory and the name (and its length) of the entry.
  85.  */
  86. static struct dentry *nat_lookup(struct inode * dir, struct dentry *dentry)
  87. {
  88. ino_t dtype;
  89. struct hfs_name cname;
  90. struct hfs_cat_entry *entry;
  91. struct hfs_cat_key key;
  92. struct inode *inode = NULL;
  93. dentry->d_op = &hfs_dentry_operations;
  94. entry = HFS_I(dir)->entry;
  95. dtype = HFS_ITYPE(dir->i_ino);
  96. /* Perform name-mangling */
  97. hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len);
  98. /* no need to check for "."  or ".." */
  99. /* Check for ".AppleDouble" if in a normal directory,
  100.    and for ".Parent" in ".AppleDouble". */
  101. if (dtype==HFS_NAT_NDIR) {
  102. /* Check for ".AppleDouble" */
  103. if (hfs_streq(cname.Name, cname.Len, 
  104.       DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) {
  105. ++entry->count; /* __hfs_iget() eats one */
  106. inode = hfs_iget(entry, HFS_NAT_HDIR, dentry);
  107. goto done;
  108. }
  109. } else if (dtype==HFS_NAT_HDIR) {
  110. if (hfs_streq(cname.Name, cname.Len, 
  111.       DOT_PARENT->Name, DOT_PARENT_LEN)) {
  112. ++entry->count; /* __hfs_iget() eats one */
  113. inode = hfs_iget(entry, HFS_NAT_HDR, dentry);
  114. goto done;
  115. }
  116. if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
  117.     hfs_streq(cname.Name, cname.Len, 
  118.       ROOTINFO->Name, ROOTINFO_LEN)) {
  119. ++entry->count; /* __hfs_iget() eats one */
  120. inode = hfs_iget(entry, HFS_NAT_HDR, dentry);
  121.                         goto done;
  122. }
  123. }
  124. /* Do an hfs_iget() on the mangled name. */
  125. hfs_cat_build_key(entry->cnid, &cname, &key);
  126. inode = hfs_iget(hfs_cat_get(entry->mdb, &key), 
  127.  HFS_I(dir)->file_type, dentry);
  128. /* Don't return a header file for a directory other than .Parent */
  129. if (inode && (dtype == HFS_NAT_HDIR) &&
  130.     (HFS_I(inode)->entry != entry) &&
  131.     (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
  132.         iput(inode); /* this does an hfs_cat_put */
  133. inode = NULL;
  134. }
  135. done:
  136. d_add(dentry, inode);
  137. return NULL;
  138. }
  139. /*
  140.  * nat_readdir()
  141.  *
  142.  * This is the readdir() entry in the file_operations structure for
  143.  * HFS directories in the netatalk scheme.  The purpose is to
  144.  * enumerate the entries in a directory, given the inode of the
  145.  * directory and a struct file which indicates the location in the
  146.  * directory.  The struct file is updated so that the next call with
  147.  * the same dir and filp will produce the next directory entry.  The
  148.  * entries are returned in dirent, which is "filled-in" by calling
  149.  * filldir().  This allows the same readdir() function be used for
  150.  * different dirent formats.  We try to read in as many entries as we
  151.  * can before filldir() refuses to take any more.
  152.  *
  153.  * Note that the Netatalk format doesn't have the problem with
  154.  * metadata for covered directories that exists in the other formats,
  155.  * since the metadata is contained within the directory.
  156.  */
  157. static int nat_readdir(struct file * filp,
  158.        void * dirent, filldir_t filldir)
  159. {
  160. ino_t type;
  161. int skip_dirs;
  162. struct hfs_brec brec;
  163.         struct hfs_cat_entry *entry;
  164. struct inode *dir = filp->f_dentry->d_inode;
  165. entry = HFS_I(dir)->entry;
  166. type = HFS_ITYPE(dir->i_ino);
  167. skip_dirs = (type == HFS_NAT_HDIR);
  168. if (filp->f_pos == 0) {
  169. /* Entry 0 is for "." */
  170. if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino,
  171.     DT_DIR)) {
  172. return 0;
  173. }
  174. filp->f_pos = 1;
  175. }
  176. if (filp->f_pos == 1) {
  177. /* Entry 1 is for ".." */
  178. hfs_u32 cnid;
  179. if (type == HFS_NAT_NDIR) {
  180. cnid = hfs_get_nl(entry->key.ParID);
  181. } else {
  182. cnid = entry->cnid;
  183. }
  184. if (filldir(dirent, DOT_DOT->Name,
  185.     DOT_DOT_LEN, 1, ntohl(cnid), DT_DIR)) {
  186. return 0;
  187. }
  188. filp->f_pos = 2;
  189. }
  190. if (filp->f_pos < (dir->i_size - 2)) {
  191. hfs_u32 cnid;
  192. hfs_u8 type;
  193.      if (hfs_cat_open(entry, &brec) ||
  194.     hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) {
  195. return 0;
  196. }
  197. while (filp->f_pos < (dir->i_size - 2)) {
  198. if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) {
  199. return 0;
  200. }
  201. if (!skip_dirs || (type != HFS_CDR_DIR)) {
  202. ino_t ino;
  203. unsigned int len;
  204. unsigned char tmp_name[HFS_NAMEMAX];
  205. ino = ntohl(cnid) | HFS_I(dir)->file_type;
  206. len = hfs_namein(dir, tmp_name,
  207.     &((struct hfs_cat_key *)brec.key)->CName);
  208. if (filldir(dirent, tmp_name, len,
  209.     filp->f_pos, ino, DT_UNKNOWN)) {
  210. hfs_cat_close(entry, &brec);
  211. return 0;
  212. }
  213. }
  214. ++filp->f_pos;
  215. }
  216. hfs_cat_close(entry, &brec);
  217. }
  218. if (filp->f_pos == (dir->i_size - 2)) {
  219. if (type == HFS_NAT_NDIR) {
  220. /* In normal dirs entry 2 is for ".AppleDouble" */
  221. if (filldir(dirent, DOT_APPLEDOUBLE->Name,
  222.     DOT_APPLEDOUBLE_LEN, filp->f_pos,
  223.     ntohl(entry->cnid) | HFS_NAT_HDIR,
  224.     DT_UNKNOWN)) {
  225. return 0;
  226. }
  227. } else if (type == HFS_NAT_HDIR) {
  228. /* In .AppleDouble entry 2 is for ".Parent" */
  229. if (filldir(dirent, DOT_PARENT->Name,
  230.     DOT_PARENT_LEN, filp->f_pos,
  231.     ntohl(entry->cnid) | HFS_NAT_HDR,
  232.     DT_UNKNOWN)) {
  233. return 0;
  234. }
  235. }
  236. ++filp->f_pos;
  237. }
  238. if (filp->f_pos == (dir->i_size - 1)) {
  239. /* handle ROOT/.AppleDouble/RootInfo as the last entry. */
  240. if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
  241.     (type == HFS_NAT_HDIR)) {
  242. if (filldir(dirent, ROOTINFO->Name,
  243.     ROOTINFO_LEN, filp->f_pos,
  244.     ntohl(entry->cnid) | HFS_NAT_HDR,
  245.     DT_UNKNOWN)) {
  246. return 0;
  247. }
  248. }
  249. ++filp->f_pos;
  250. }
  251. return 0;
  252. }
  253. /* due to the dcache caching negative dentries for non-existent files,
  254.  * we need to drop those entries when a file silently gets created.
  255.  * as far as i can tell, the calls that need to do this are the file
  256.  * related calls (create, rename, and mknod). the directory calls
  257.  * should be immune. the relevant calls in dir.c call drop_dentry 
  258.  * upon successful completion. */
  259. void hfs_nat_drop_dentry(struct dentry *dentry, const ino_t type)
  260. {
  261.   struct dentry *de;
  262.   
  263.   switch (type) {
  264.   case HFS_NAT_HDR: /* given .AppleDouble/name */
  265.     /* look for name */
  266.     de = hfs_lookup_dentry(dentry->d_parent->d_parent,
  267.    dentry->d_name.name, dentry->d_name.len);
  268.     if (de) {
  269.       if (!de->d_inode)
  270. d_drop(de);
  271.       dput(de);
  272.     }
  273.     break;
  274.   case HFS_NAT_DATA: /* given name */
  275.     /* look for .AppleDouble/name */
  276.     hfs_drop_special(dentry->d_parent, DOT_APPLEDOUBLE, dentry);
  277.     break;
  278.   }
  279. }
  280. /*
  281.  * nat_rmdir()
  282.  *
  283.  * This is the rmdir() entry in the inode_operations structure for
  284.  * Netatalk directories.  The purpose is to delete an existing
  285.  * directory, given the inode for the parent directory and the name
  286.  * (and its length) of the existing directory.
  287.  *
  288.  * We handle .AppleDouble and call hfs_rmdir() for all other cases.
  289.  */
  290. static int nat_rmdir(struct inode *parent, struct dentry *dentry)
  291. {
  292. struct hfs_cat_entry *entry = HFS_I(parent)->entry;
  293. struct hfs_name cname;
  294. int error;
  295. hfs_nameout(parent, &cname, dentry->d_name.name, dentry->d_name.len);
  296. if (hfs_streq(cname.Name, cname.Len,
  297.       DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) {
  298. if (!HFS_SB(parent->i_sb)->s_afpd) {
  299. /* Not in AFPD compatibility mode */
  300. error = -EPERM;
  301. } else if (entry->u.dir.files || entry->u.dir.dirs) {
  302. /* AFPD compatible, but the directory is not empty */
  303. error = -ENOTEMPTY;
  304. } else {
  305. /* AFPD compatible, so pretend to succeed */
  306. error = 0;
  307. }
  308. } else {
  309. error = hfs_rmdir(parent, dentry);
  310. }
  311. return error;
  312. }
  313. /*
  314.  * nat_hdr_unlink()
  315.  *
  316.  * This is the unlink() entry in the inode_operations structure for
  317.  * Netatalk .AppleDouble directories.  The purpose is to delete an
  318.  * existing file, given the inode for the parent directory and the name
  319.  * (and its length) of the existing file.
  320.  *
  321.  * WE DON'T ACTUALLY DELETE HEADER THE FILE.
  322.  * In non-afpd-compatible mode:
  323.  *   We return -EPERM.
  324.  * In afpd-compatible mode:
  325.  *   We return success if the file exists or is .Parent.
  326.  *   Otherwise we return -ENOENT.
  327.  */
  328. static int nat_hdr_unlink(struct inode *dir, struct dentry *dentry)
  329. {
  330. struct hfs_cat_entry *entry = HFS_I(dir)->entry;
  331. int error = 0;
  332. if (!HFS_SB(dir->i_sb)->s_afpd) {
  333. /* Not in AFPD compatibility mode */
  334. error = -EPERM;
  335. } else {
  336. struct hfs_name cname;
  337. hfs_nameout(dir, &cname, dentry->d_name.name, 
  338.     dentry->d_name.len);
  339. if (!hfs_streq(cname.Name, cname.Len,
  340.        DOT_PARENT->Name, DOT_PARENT_LEN)) {
  341. struct hfs_cat_entry *victim;
  342. struct hfs_cat_key key;
  343. hfs_cat_build_key(entry->cnid, &cname, &key);
  344. victim = hfs_cat_get(entry->mdb, &key);
  345. if (victim) {
  346. /* pretend to succeed */
  347. hfs_cat_put(victim);
  348. } else {
  349. error = -ENOENT;
  350. }
  351. }
  352. }
  353. return error;
  354. }
  355. /*
  356.  * nat_hdr_rename()
  357.  *
  358.  * This is the rename() entry in the inode_operations structure for
  359.  * Netatalk header directories.  The purpose is to rename an existing
  360.  * file given the inode for the current directory and the name 
  361.  * (and its length) of the existing file and the inode for the new
  362.  * directory and the name (and its length) of the new file/directory.
  363.  *
  364.  * WE NEVER MOVE ANYTHING.
  365.  * In non-afpd-compatible mode:
  366.  *   We return -EPERM.
  367.  * In afpd-compatible mode:
  368.  *   If the source header doesn't exist, we return -ENOENT.
  369.  *   If the destination is not a header directory we return -EPERM.
  370.  *   We return success if the destination is also a header directory
  371.  *    and the header exists or is ".Parent".
  372.  */
  373. static int nat_hdr_rename(struct inode *old_dir, struct dentry *old_dentry,
  374.   struct inode *new_dir, struct dentry *new_dentry)
  375. {
  376. struct hfs_cat_entry *entry = HFS_I(old_dir)->entry;
  377. int error = 0;
  378. if (!HFS_SB(old_dir->i_sb)->s_afpd) {
  379. /* Not in AFPD compatibility mode */
  380. error = -EPERM;
  381. } else {
  382. struct hfs_name cname;
  383. hfs_nameout(old_dir, &cname, old_dentry->d_name.name,
  384.     old_dentry->d_name.len);
  385. if (!hfs_streq(cname.Name, cname.Len, 
  386.        DOT_PARENT->Name, DOT_PARENT_LEN)) {
  387. struct hfs_cat_entry *victim;
  388. struct hfs_cat_key key;
  389. hfs_cat_build_key(entry->cnid, &cname, &key);
  390. victim = hfs_cat_get(entry->mdb, &key);
  391. if (victim) {
  392. /* pretend to succeed */
  393. hfs_cat_put(victim);
  394. } else {
  395. error = -ENOENT;
  396. }
  397. }
  398. if (!error && (HFS_ITYPE(new_dir->i_ino) != HFS_NAT_HDIR)) {
  399. error = -EPERM;
  400. }
  401. }
  402. return error;
  403. }