dir.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /*
  2. * linux/fs/hfsplus/dir.c
  3. *
  4. * Copyright (C) 2001
  5. * Brad Boyer (flar@allandria.com)
  6. * (C) 2003 Ardis Technologies <roman@ardistech.com>
  7. *
  8. * Handling of directories
  9. */
  10. #include <linux/errno.h>
  11. #include <linux/fs.h>
  12. #include <linux/sched.h>
  13. #include <linux/slab.h>
  14. #include <linux/random.h>
  15. #include <linux/version.h>
  16. #include "hfsplus_fs.h"
  17. #include "hfsplus_raw.h"
  18. static inline void hfsplus_instantiate(struct dentry *dentry,
  19. struct inode *inode, u32 cnid)
  20. {
  21. dentry->d_fsdata = (void *)(unsigned long)cnid;
  22. d_instantiate(dentry, inode);
  23. }
  24. /* Find the entry inside dir named dentry->d_name */
  25. static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
  26. struct nameidata *nd)
  27. {
  28. struct inode *inode = NULL;
  29. struct hfs_find_data fd;
  30. struct super_block *sb;
  31. hfsplus_cat_entry entry;
  32. int err;
  33. u32 cnid, linkid = 0;
  34. u16 type;
  35. sb = dir->i_sb;
  36. dentry->d_fsdata = NULL;
  37. hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
  38. hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name);
  39. again:
  40. err = hfs_brec_read(&fd, &entry, sizeof(entry));
  41. if (err) {
  42. if (err == -ENOENT) {
  43. hfs_find_exit(&fd);
  44. /* No such entry */
  45. inode = NULL;
  46. goto out;
  47. }
  48. goto fail;
  49. }
  50. type = be16_to_cpu(entry.type);
  51. if (type == HFSPLUS_FOLDER) {
  52. if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
  53. err = -EIO;
  54. goto fail;
  55. }
  56. cnid = be32_to_cpu(entry.folder.id);
  57. dentry->d_fsdata = (void *)(unsigned long)cnid;
  58. } else if (type == HFSPLUS_FILE) {
  59. if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
  60. err = -EIO;
  61. goto fail;
  62. }
  63. cnid = be32_to_cpu(entry.file.id);
  64. if (entry.file.user_info.fdType == cpu_to_be32(HFSP_HARDLINK_TYPE) &&
  65. entry.file.user_info.fdCreator == cpu_to_be32(HFSP_HFSPLUS_CREATOR)) {
  66. struct qstr str;
  67. char name[32];
  68. if (dentry->d_fsdata) {
  69. err = -ENOENT;
  70. inode = NULL;
  71. goto out;
  72. }
  73. dentry->d_fsdata = (void *)(unsigned long)cnid;
  74. linkid = be32_to_cpu(entry.file.permissions.dev);
  75. str.len = sprintf(name, "iNode%d", linkid);
  76. str.name = name;
  77. hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_SB(sb).hidden_dir->i_ino, &str);
  78. goto again;
  79. } else if (!dentry->d_fsdata)
  80. dentry->d_fsdata = (void *)(unsigned long)cnid;
  81. } else {
  82. printk("HFS+-fs: Illegal catalog entry type in lookup\n");
  83. err = -EIO;
  84. goto fail;
  85. }
  86. hfs_find_exit(&fd);
  87. inode = iget(dir->i_sb, cnid);
  88. if (!inode)
  89. return ERR_PTR(-EACCES);
  90. if (S_ISREG(inode->i_mode))
  91. HFSPLUS_I(inode).dev = linkid;
  92. out:
  93. d_add(dentry, inode);
  94. return NULL;
  95. fail:
  96. hfs_find_exit(&fd);
  97. return ERR_PTR(err);
  98. }
  99. static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
  100. {
  101. struct inode *inode = filp->f_dentry->d_inode;
  102. struct super_block *sb = inode->i_sb;
  103. int len, err;
  104. char strbuf[HFSPLUS_MAX_STRLEN + 1];
  105. hfsplus_cat_entry entry;
  106. struct hfs_find_data fd;
  107. struct hfsplus_readdir_data *rd;
  108. u16 type;
  109. if (filp->f_pos >= inode->i_size)
  110. return 0;
  111. hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
  112. hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
  113. err = hfs_brec_find(&fd);
  114. if (err)
  115. goto out;
  116. switch ((u32)filp->f_pos) {
  117. case 0:
  118. /* This is completely artificial... */
  119. if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
  120. goto out;
  121. filp->f_pos++;
  122. /* fall through */
  123. case 1:
  124. hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
  125. if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
  126. printk("HFS+-fs: bad catalog folder thread\n");
  127. err = -EIO;
  128. goto out;
  129. }
  130. if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
  131. printk("HFS+-fs: truncated catalog thread\n");
  132. err = -EIO;
  133. goto out;
  134. }
  135. if (filldir(dirent, "..", 2, 1,
  136. be32_to_cpu(entry.thread.parentID), DT_DIR))
  137. goto out;
  138. filp->f_pos++;
  139. /* fall through */
  140. default:
  141. if (filp->f_pos >= inode->i_size)
  142. goto out;
  143. err = hfs_brec_goto(&fd, filp->f_pos - 1);
  144. if (err)
  145. goto out;
  146. }
  147. for (;;) {
  148. if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
  149. printk("HFS+-fs: walked past end of dir\n");
  150. err = -EIO;
  151. goto out;
  152. }
  153. hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
  154. type = be16_to_cpu(entry.type);
  155. len = HFSPLUS_MAX_STRLEN;
  156. err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
  157. if (err)
  158. goto out;
  159. if (type == HFSPLUS_FOLDER) {
  160. if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
  161. printk("HFS+-fs: small dir entry\n");
  162. err = -EIO;
  163. goto out;
  164. }
  165. if (HFSPLUS_SB(sb).hidden_dir &&
  166. HFSPLUS_SB(sb).hidden_dir->i_ino == be32_to_cpu(entry.folder.id))
  167. goto next;
  168. if (filldir(dirent, strbuf, len, filp->f_pos,
  169. be32_to_cpu(entry.folder.id), DT_DIR))
  170. break;
  171. } else if (type == HFSPLUS_FILE) {
  172. if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
  173. printk("HFS+-fs: small file entry\n");
  174. err = -EIO;
  175. goto out;
  176. }
  177. if (filldir(dirent, strbuf, len, filp->f_pos,
  178. be32_to_cpu(entry.file.id), DT_REG))
  179. break;
  180. } else {
  181. printk("HFS+-fs: bad catalog entry type\n");
  182. err = -EIO;
  183. goto out;
  184. }
  185. next:
  186. filp->f_pos++;
  187. if (filp->f_pos >= inode->i_size)
  188. goto out;
  189. err = hfs_brec_goto(&fd, 1);
  190. if (err)
  191. goto out;
  192. }
  193. rd = filp->private_data;
  194. if (!rd) {
  195. rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
  196. if (!rd) {
  197. err = -ENOMEM;
  198. goto out;
  199. }
  200. filp->private_data = rd;
  201. rd->file = filp;
  202. list_add(&rd->list, &HFSPLUS_I(inode).open_dir_list);
  203. }
  204. memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
  205. out:
  206. hfs_find_exit(&fd);
  207. return err;
  208. }
  209. static int hfsplus_dir_release(struct inode *inode, struct file *file)
  210. {
  211. struct hfsplus_readdir_data *rd = file->private_data;
  212. if (rd) {
  213. list_del(&rd->list);
  214. kfree(rd);
  215. }
  216. return 0;
  217. }
  218. static int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode,
  219. struct nameidata *nd)
  220. {
  221. struct inode *inode;
  222. int res;
  223. inode = hfsplus_new_inode(dir->i_sb, mode);
  224. if (!inode)
  225. return -ENOSPC;
  226. res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
  227. if (res) {
  228. inode->i_nlink = 0;
  229. hfsplus_delete_inode(inode);
  230. iput(inode);
  231. return res;
  232. }
  233. hfsplus_instantiate(dentry, inode, inode->i_ino);
  234. mark_inode_dirty(inode);
  235. return 0;
  236. }
  237. static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
  238. struct dentry *dst_dentry)
  239. {
  240. struct super_block *sb = dst_dir->i_sb;
  241. struct inode *inode = src_dentry->d_inode;
  242. struct inode *src_dir = src_dentry->d_parent->d_inode;
  243. struct qstr str;
  244. char name[32];
  245. u32 cnid, id;
  246. int res;
  247. if (HFSPLUS_IS_RSRC(inode))
  248. return -EPERM;
  249. if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
  250. for (;;) {
  251. get_random_bytes(&id, sizeof(cnid));
  252. id &= 0x3fffffff;
  253. str.name = name;
  254. str.len = sprintf(name, "iNode%d", id);
  255. res = hfsplus_rename_cat(inode->i_ino,
  256. src_dir, &src_dentry->d_name,
  257. HFSPLUS_SB(sb).hidden_dir, &str);
  258. if (!res)
  259. break;
  260. if (res != -EEXIST)
  261. return res;
  262. }
  263. HFSPLUS_I(inode).dev = id;
  264. cnid = HFSPLUS_SB(sb).next_cnid++;
  265. src_dentry->d_fsdata = (void *)(unsigned long)cnid;
  266. res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode);
  267. if (res)
  268. /* panic? */
  269. return res;
  270. HFSPLUS_SB(sb).file_count++;
  271. }
  272. cnid = HFSPLUS_SB(sb).next_cnid++;
  273. res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
  274. if (res)
  275. return res;
  276. inode->i_nlink++;
  277. hfsplus_instantiate(dst_dentry, inode, cnid);
  278. atomic_inc(&inode->i_count);
  279. inode->i_ctime = CURRENT_TIME_SEC;
  280. mark_inode_dirty(inode);
  281. HFSPLUS_SB(sb).file_count++;
  282. sb->s_dirt = 1;
  283. return 0;
  284. }
  285. static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
  286. {
  287. struct super_block *sb = dir->i_sb;
  288. struct inode *inode = dentry->d_inode;
  289. struct qstr str;
  290. char name[32];
  291. u32 cnid;
  292. int res;
  293. if (HFSPLUS_IS_RSRC(inode))
  294. return -EPERM;
  295. cnid = (u32)(unsigned long)dentry->d_fsdata;
  296. if (inode->i_ino == cnid &&
  297. atomic_read(&HFSPLUS_I(inode).opencnt)) {
  298. str.name = name;
  299. str.len = sprintf(name, "temp%lu", inode->i_ino);
  300. res = hfsplus_rename_cat(inode->i_ino,
  301. dir, &dentry->d_name,
  302. HFSPLUS_SB(sb).hidden_dir, &str);
  303. if (!res)
  304. inode->i_flags |= S_DEAD;
  305. return res;
  306. }
  307. res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
  308. if (res)
  309. return res;
  310. inode->i_nlink--;
  311. hfsplus_delete_inode(inode);
  312. if (inode->i_ino != cnid && !inode->i_nlink) {
  313. if (!atomic_read(&HFSPLUS_I(inode).opencnt)) {
  314. res = hfsplus_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL);
  315. if (!res)
  316. hfsplus_delete_inode(inode);
  317. } else
  318. inode->i_flags |= S_DEAD;
  319. }
  320. inode->i_ctime = CURRENT_TIME_SEC;
  321. mark_inode_dirty(inode);
  322. return res;
  323. }
  324. static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode)
  325. {
  326. struct inode *inode;
  327. int res;
  328. inode = hfsplus_new_inode(dir->i_sb, S_IFDIR | mode);
  329. if (!inode)
  330. return -ENOSPC;
  331. res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
  332. if (res) {
  333. inode->i_nlink = 0;
  334. hfsplus_delete_inode(inode);
  335. iput(inode);
  336. return res;
  337. }
  338. hfsplus_instantiate(dentry, inode, inode->i_ino);
  339. mark_inode_dirty(inode);
  340. return 0;
  341. }
  342. static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
  343. {
  344. struct inode *inode;
  345. int res;
  346. inode = dentry->d_inode;
  347. if (inode->i_size != 2)
  348. return -ENOTEMPTY;
  349. res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
  350. if (res)
  351. return res;
  352. inode->i_nlink = 0;
  353. inode->i_ctime = CURRENT_TIME_SEC;
  354. hfsplus_delete_inode(inode);
  355. mark_inode_dirty(inode);
  356. return 0;
  357. }
  358. static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
  359. const char *symname)
  360. {
  361. struct super_block *sb;
  362. struct inode *inode;
  363. int res;
  364. sb = dir->i_sb;
  365. inode = hfsplus_new_inode(sb, S_IFLNK | S_IRWXUGO);
  366. if (!inode)
  367. return -ENOSPC;
  368. res = page_symlink(inode, symname, strlen(symname) + 1);
  369. if (res) {
  370. inode->i_nlink = 0;
  371. hfsplus_delete_inode(inode);
  372. iput(inode);
  373. return res;
  374. }
  375. mark_inode_dirty(inode);
  376. res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
  377. if (!res) {
  378. hfsplus_instantiate(dentry, inode, inode->i_ino);
  379. mark_inode_dirty(inode);
  380. }
  381. return res;
  382. }
  383. static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
  384. int mode, dev_t rdev)
  385. {
  386. struct super_block *sb;
  387. struct inode *inode;
  388. int res;
  389. sb = dir->i_sb;
  390. inode = hfsplus_new_inode(sb, mode);
  391. if (!inode)
  392. return -ENOSPC;
  393. res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
  394. if (res) {
  395. inode->i_nlink = 0;
  396. hfsplus_delete_inode(inode);
  397. iput(inode);
  398. return res;
  399. }
  400. init_special_inode(inode, mode, rdev);
  401. hfsplus_instantiate(dentry, inode, inode->i_ino);
  402. mark_inode_dirty(inode);
  403. return 0;
  404. }
  405. static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
  406. struct inode *new_dir, struct dentry *new_dentry)
  407. {
  408. int res;
  409. /* Unlink destination if it already exists */
  410. if (new_dentry->d_inode) {
  411. res = hfsplus_unlink(new_dir, new_dentry);
  412. if (res)
  413. return res;
  414. }
  415. res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
  416. old_dir, &old_dentry->d_name,
  417. new_dir, &new_dentry->d_name);
  418. if (!res)
  419. new_dentry->d_fsdata = old_dentry->d_fsdata;
  420. return res;
  421. }
  422. struct inode_operations hfsplus_dir_inode_operations = {
  423. .lookup = hfsplus_lookup,
  424. .create = hfsplus_create,
  425. .link = hfsplus_link,
  426. .unlink = hfsplus_unlink,
  427. .mkdir = hfsplus_mkdir,
  428. .rmdir = hfsplus_rmdir,
  429. .symlink = hfsplus_symlink,
  430. .mknod = hfsplus_mknod,
  431. .rename = hfsplus_rename,
  432. };
  433. struct file_operations hfsplus_dir_operations = {
  434. .read = generic_read_dir,
  435. .readdir = hfsplus_readdir,
  436. .ioctl = hfsplus_ioctl,
  437. .llseek = generic_file_llseek,
  438. .release = hfsplus_dir_release,
  439. };