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

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  *  linux/fs/adfs/dir_f.c
  3.  *
  4.  * Copyright (C) 1997-1999 Russell King
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License version 2 as
  8.  * published by the Free Software Foundation.
  9.  *
  10.  *  E and F format directory handling
  11.  */
  12. #include <linux/version.h>
  13. #include <linux/errno.h>
  14. #include <linux/fs.h>
  15. #include <linux/adfs_fs.h>
  16. #include <linux/sched.h>
  17. #include <linux/stat.h>
  18. #include <linux/spinlock.h>
  19. #include "adfs.h"
  20. #include "dir_f.h"
  21. static void adfs_f_free(struct adfs_dir *dir);
  22. /*
  23.  * Read an (unaligned) value of length 1..4 bytes
  24.  */
  25. static inline unsigned int adfs_readval(unsigned char *p, int len)
  26. {
  27. unsigned int val = 0;
  28. switch (len) {
  29. case 4: val |= p[3] << 24;
  30. case 3: val |= p[2] << 16;
  31. case 2: val |= p[1] << 8;
  32. default: val |= p[0];
  33. }
  34. return val;
  35. }
  36. static inline void adfs_writeval(unsigned char *p, int len, unsigned int val)
  37. {
  38. switch (len) {
  39. case 4: p[3] = val >> 24;
  40. case 3: p[2] = val >> 16;
  41. case 2: p[1] = val >> 8;
  42. default: p[0] = val;
  43. }
  44. }
  45. static inline int adfs_readname(char *buf, char *ptr, int maxlen)
  46. {
  47. char *old_buf = buf;
  48. while (*ptr >= ' ' && maxlen--) {
  49. if (*ptr == '/')
  50. *buf++ = '.';
  51. else
  52. *buf++ = *ptr;
  53. ptr++;
  54. }
  55. *buf = '';
  56. return buf - old_buf;
  57. }
  58. static inline void adfs_writename(char *to, char *from, int maxlen)
  59. {
  60. int i;
  61. for (i = 0; i < maxlen; i++) {
  62. if (from[i] == '')
  63. break;
  64. if (from[i] == '.')
  65. to[i] = '/';
  66. else
  67. to[i] = from[i];
  68. }
  69. for (; i < maxlen; i++)
  70. to[i] = '';
  71. }
  72. #define ror13(v) ((v >> 13) | (v << 19))
  73. #define dir_u8(idx)
  74. ({ int _buf = idx >> blocksize_bits;
  75.    int _off = idx - (_buf << blocksize_bits);
  76.   *(u8 *)(bh[_buf]->b_data + _off);
  77. })
  78. #define dir_u32(idx)
  79. ({ int _buf = idx >> blocksize_bits;
  80.    int _off = idx - (_buf << blocksize_bits);
  81.   *(u32 *)(bh[_buf]->b_data + _off);
  82. })
  83. #define bufoff(_bh,_idx)
  84. ({ int _buf = _idx >> blocksize_bits;
  85.    int _off = _idx - (_buf << blocksize_bits);
  86.   (u8 *)(_bh[_buf]->b_data + _off);
  87. })
  88. /*
  89.  * There are some algorithms that are nice in
  90.  * assembler, but a bitch in C...  This is one
  91.  * of them.
  92.  */
  93. static u8
  94. adfs_dir_checkbyte(const struct adfs_dir *dir)
  95. {
  96. struct buffer_head * const *bh = dir->bh;
  97. const int blocksize_bits = dir->sb->s_blocksize_bits;
  98. union { u32 *ptr32; u8 *ptr8; } ptr, end;
  99. u32 dircheck = 0;
  100. int last = 5 - 26;
  101. int i = 0;
  102. /*
  103.  * Accumulate each word up to the last whole
  104.  * word of the last directory entry.  This
  105.  * can spread across several buffer heads.
  106.  */
  107. do {
  108. last += 26;
  109. do {
  110. dircheck = cpu_to_le32(dir_u32(i)) ^ ror13(dircheck);
  111. i += sizeof(u32);
  112. } while (i < (last & ~3));
  113. } while (dir_u8(last) != 0);
  114. /*
  115.  * Accumulate the last few bytes.  These
  116.  * bytes will be within the same bh.
  117.  */
  118. if (i != last) {
  119. ptr.ptr8 = bufoff(bh, i);
  120. end.ptr8 = ptr.ptr8 + last - i;
  121. do
  122. dircheck = *ptr.ptr8++ ^ ror13(dircheck);
  123. while (ptr.ptr8 < end.ptr8);
  124. }
  125. /*
  126.  * The directory tail is in the final bh
  127.  * Note that contary to the RISC OS PRMs,
  128.  * the first few bytes are NOT included
  129.  * in the check.  All bytes are in the
  130.  * same bh.
  131.  */
  132. ptr.ptr8 = bufoff(bh, 2008);
  133. end.ptr8 = ptr.ptr8 + 36;
  134. do {
  135. unsigned int v = *ptr.ptr32++;
  136. dircheck = cpu_to_le32(v) ^ ror13(dircheck);
  137. } while (ptr.ptr32 < end.ptr32);
  138. return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff;
  139. }
  140. /*
  141.  * Read and check that a directory is valid
  142.  */
  143. int
  144. adfs_dir_read(struct super_block *sb, unsigned long object_id,
  145.       unsigned int size, struct adfs_dir *dir)
  146. {
  147. const unsigned int blocksize_bits = sb->s_blocksize_bits;
  148. int blk = 0;
  149. /*
  150.  * Directories which are not a multiple of 2048 bytes
  151.  * are considered bad v2 [3.6]
  152.  */
  153. if (size & 2047)
  154. goto bad_dir;
  155. size >>= blocksize_bits;
  156. dir->nr_buffers = 0;
  157. dir->sb = sb;
  158. for (blk = 0; blk < size; blk++) {
  159. int phys;
  160. phys = __adfs_block_map(sb, object_id, blk);
  161. if (!phys) {
  162. adfs_error(sb, "dir object %lX has a hole at offset %d",
  163.    object_id, blk);
  164. goto release_buffers;
  165. }
  166. dir->bh[blk] = sb_bread(sb, phys);
  167. if (!dir->bh[blk])
  168. goto release_buffers;
  169. }
  170. memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
  171. memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
  172. if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
  173.     memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
  174. goto bad_dir;
  175. if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
  176.     memcmp(&dir->dirhead.startname, "Hugo", 4))
  177. goto bad_dir;
  178. if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
  179. goto bad_dir;
  180. dir->nr_buffers = blk;
  181. return 0;
  182. bad_dir:
  183. adfs_error(sb, "corrupted directory fragment %lX",
  184.    object_id);
  185. release_buffers:
  186. for (blk -= 1; blk >= 0; blk -= 1)
  187. brelse(dir->bh[blk]);
  188. dir->sb = NULL;
  189. return -EIO;
  190. }
  191. /*
  192.  * convert a disk-based directory entry to a Linux ADFS directory entry
  193.  */
  194. static inline void
  195. adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de)
  196. {
  197. obj->name_len = adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN);
  198. obj->file_id  = adfs_readval(de->dirinddiscadd, 3);
  199. obj->loadaddr = adfs_readval(de->dirload, 4);
  200. obj->execaddr = adfs_readval(de->direxec, 4);
  201. obj->size     = adfs_readval(de->dirlen,  4);
  202. obj->attr     = de->newdiratts;
  203. }
  204. /*
  205.  * convert a Linux ADFS directory entry to a disk-based directory entry
  206.  */
  207. static inline void
  208. adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj)
  209. {
  210. adfs_writeval(de->dirinddiscadd, 3, obj->file_id);
  211. adfs_writeval(de->dirload, 4, obj->loadaddr);
  212. adfs_writeval(de->direxec, 4, obj->execaddr);
  213. adfs_writeval(de->dirlen,  4, obj->size);
  214. de->newdiratts = obj->attr;
  215. }
  216. /*
  217.  * get a directory entry.  Note that the caller is responsible
  218.  * for holding the relevent locks.
  219.  */
  220. int
  221. __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
  222. {
  223. struct super_block *sb = dir->sb;
  224. struct adfs_direntry de;
  225. int thissize, buffer, offset;
  226. buffer = pos >> sb->s_blocksize_bits;
  227. if (buffer > dir->nr_buffers)
  228. return -EINVAL;
  229. offset = pos & (sb->s_blocksize - 1);
  230. thissize = sb->s_blocksize - offset;
  231. if (thissize > 26)
  232. thissize = 26;
  233. memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
  234. if (thissize != 26)
  235. memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
  236.        26 - thissize);
  237. if (!de.dirobname[0])
  238. return -ENOENT;
  239. adfs_dir2obj(obj, &de);
  240. return 0;
  241. }
  242. int
  243. __adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj)
  244. {
  245. struct super_block *sb = dir->sb;
  246. struct adfs_direntry de;
  247. int thissize, buffer, offset;
  248. buffer = pos >> sb->s_blocksize_bits;
  249. if (buffer > dir->nr_buffers)
  250. return -EINVAL;
  251. offset = pos & (sb->s_blocksize - 1);
  252. thissize = sb->s_blocksize - offset;
  253. if (thissize > 26)
  254. thissize = 26;
  255. /*
  256.  * Get the entry in total
  257.  */
  258. memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
  259. if (thissize != 26)
  260. memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
  261.        26 - thissize);
  262. /*
  263.  * update it
  264.  */
  265. adfs_obj2dir(&de, obj);
  266. /*
  267.  * Put the new entry back
  268.  */
  269. memcpy(dir->bh[buffer]->b_data + offset, &de, thissize);
  270. if (thissize != 26)
  271. memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize,
  272.        26 - thissize);
  273. return 0;
  274. }
  275. /*
  276.  * the caller is responsible for holding the necessary
  277.  * locks.
  278.  */
  279. static int
  280. adfs_dir_find_entry(struct adfs_dir *dir, unsigned long object_id)
  281. {
  282. int pos, ret;
  283. ret = -ENOENT;
  284. for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) {
  285. struct object_info obj;
  286. if (!__adfs_dir_get(dir, pos, &obj))
  287. break;
  288. if (obj.file_id == object_id) {
  289. ret = pos;
  290. break;
  291. }
  292. }
  293. return ret;
  294. }
  295. static int
  296. adfs_f_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
  297. {
  298. int ret;
  299. if (sz != ADFS_NEWDIR_SIZE)
  300. return -EIO;
  301. ret = adfs_dir_read(sb, id, sz, dir);
  302. if (ret)
  303. adfs_error(sb, "unable to read directory");
  304. else
  305. dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3);
  306. return ret;
  307. }
  308. static int
  309. adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos)
  310. {
  311. if (fpos >= ADFS_NUM_DIR_ENTRIES)
  312. return -ENOENT;
  313. dir->pos = 5 + fpos * 26;
  314. return 0;
  315. }
  316. static int
  317. adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj)
  318. {
  319. unsigned int ret;
  320. ret = __adfs_dir_get(dir, dir->pos, obj);
  321. if (ret == 0)
  322. dir->pos += 26;
  323. return ret;
  324. }
  325. static int
  326. adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
  327. {
  328. struct super_block *sb = dir->sb;
  329. int ret, i;
  330. ret = adfs_dir_find_entry(dir, obj->file_id);
  331. if (ret < 0) {
  332. adfs_error(dir->sb, "unable to locate entry to update");
  333. goto out;
  334. }
  335. __adfs_dir_put(dir, ret, obj);
  336.  
  337. /*
  338.  * Increment directory sequence number
  339.  */
  340. dir->bh[0]->b_data[0] += 1;
  341. dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1;
  342. ret = adfs_dir_checkbyte(dir);
  343. /*
  344.  * Update directory check byte
  345.  */
  346. dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret;
  347. #if 1
  348. {
  349. const unsigned int blocksize_bits = sb->s_blocksize_bits;
  350. memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
  351. memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
  352. if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
  353.     memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
  354. goto bad_dir;
  355. if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
  356.     memcmp(&dir->dirhead.startname, "Hugo", 4))
  357. goto bad_dir;
  358. if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
  359. goto bad_dir;
  360. }
  361. #endif
  362. for (i = dir->nr_buffers - 1; i >= 0; i--)
  363. mark_buffer_dirty(dir->bh[i]);
  364. ret = 0;
  365. out:
  366. return ret;
  367. #if 1
  368. bad_dir:
  369. adfs_error(dir->sb, "whoops!  I broke a directory!");
  370. return -EIO;
  371. #endif
  372. }
  373. static void
  374. adfs_f_free(struct adfs_dir *dir)
  375. {
  376. int i;
  377. for (i = dir->nr_buffers - 1; i >= 0; i--) {
  378. brelse(dir->bh[i]);
  379. dir->bh[i] = NULL;
  380. }
  381. dir->nr_buffers = 0;
  382. dir->sb = NULL;
  383. }
  384. struct adfs_dir_ops adfs_f_dir_ops = {
  385. adfs_f_read,
  386. adfs_f_setpos,
  387. adfs_f_getnext,
  388. adfs_f_update,
  389. NULL,
  390. NULL,
  391. adfs_f_free
  392. };