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

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  *  linux/fs/adfs/super.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. #include <linux/version.h>
  11. #include <linux/module.h>
  12. #include <linux/errno.h>
  13. #include <linux/fs.h>
  14. #include <linux/adfs_fs.h>
  15. #include <linux/slab.h>
  16. #include <linux/sched.h>
  17. #include <linux/stat.h>
  18. #include <linux/string.h>
  19. #include <linux/locks.h>
  20. #include <linux/init.h>
  21. #include <asm/bitops.h>
  22. #include <asm/uaccess.h>
  23. #include <asm/system.h>
  24. #include <stdarg.h>
  25. #include "adfs.h"
  26. #include "dir_f.h"
  27. #include "dir_fplus.h"
  28. void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...)
  29. {
  30. char error_buf[128];
  31. va_list args;
  32. va_start(args, fmt);
  33. vsprintf(error_buf, fmt, args);
  34. va_end(args);
  35. printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %sn",
  36. bdevname(sb->s_dev), function ? ": " : "",
  37. function ? function : "", error_buf);
  38. }
  39. static int adfs_checkdiscrecord(struct adfs_discrecord *dr)
  40. {
  41. int i;
  42. /* sector size must be 256, 512 or 1024 bytes */
  43. if (dr->log2secsize != 8 &&
  44.     dr->log2secsize != 9 &&
  45.     dr->log2secsize != 10)
  46. return 1;
  47. /* idlen must be at least log2secsize + 3 */
  48. if (dr->idlen < dr->log2secsize + 3)
  49. return 1;
  50. /* we cannot have such a large disc that we
  51.  * are unable to represent sector offsets in
  52.  * 32 bits.  This works out at 2.0 TB.
  53.  */
  54. if (dr->disc_size_high >> dr->log2secsize)
  55. return 1;
  56. /*
  57.  * The following checks are not required for F+
  58.  * stage 1.
  59.  */
  60. #if 0
  61. /* idlen must be smaller be no greater than 15 */
  62. if (dr->idlen > 15)
  63. return 1;
  64. /* nzones must be less than 128 for the root
  65.  * directory to be addressable
  66.  */
  67. if (dr->nzones >= 128 && dr->nzones_high == 0)
  68. return 1;
  69. /* root must be of the form 0x2.. */
  70. if ((le32_to_cpu(dr->root) & 0xffffff00) != 0x00000200)
  71. return 1;
  72. #else
  73. /*
  74.  * Stage 2 F+ does not require the following check
  75.  */
  76. #if 0
  77. /* idlen must be no greater than 16 v2 [1.0] */
  78. if (dr->idlen > 16)
  79. return 1;
  80. /* we can't handle F+ discs yet */
  81. if (dr->format_version || dr->root_size)
  82. return 1;
  83. #else
  84. /* idlen must be no greater than 19 v2 [1.0] */
  85. if (dr->idlen > 19)
  86. return 1;
  87. #endif
  88. #endif
  89. /* reserved bytes should be zero */
  90. for (i = 0; i < sizeof(dr->unused52); i++)
  91. if (dr->unused52[i] != 0)
  92. return 1;
  93. return 0;
  94. }
  95. static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map)
  96. {
  97. unsigned int v0, v1, v2, v3;
  98. int i;
  99. v0 = v1 = v2 = v3 = 0;
  100. for (i = sb->s_blocksize - 4; i; i -= 4) {
  101. v0 += map[i]     + (v3 >> 8);
  102. v3 &= 0xff;
  103. v1 += map[i + 1] + (v0 >> 8);
  104. v0 &= 0xff;
  105. v2 += map[i + 2] + (v1 >> 8);
  106. v1 &= 0xff;
  107. v3 += map[i + 3] + (v2 >> 8);
  108. v2 &= 0xff;
  109. }
  110. v0 +=           v3 >> 8;
  111. v1 += map[1] + (v0 >> 8);
  112. v2 += map[2] + (v1 >> 8);
  113. v3 += map[3] + (v2 >> 8);
  114. return v0 ^ v1 ^ v2 ^ v3;
  115. }
  116. static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)
  117. {
  118. unsigned char crosscheck = 0, zonecheck = 1;
  119. int i;
  120. for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
  121. unsigned char *map;
  122. map = dm[i].dm_bh->b_data;
  123. if (adfs_calczonecheck(sb, map) != map[0]) {
  124. adfs_error(sb, "zone %d fails zonecheck", i);
  125. zonecheck = 0;
  126. }
  127. crosscheck ^= map[3];
  128. }
  129. if (crosscheck != 0xff)
  130. adfs_error(sb, "crosscheck != 0xff");
  131. return crosscheck == 0xff && zonecheck;
  132. }
  133. static void adfs_put_super(struct super_block *sb)
  134. {
  135. int i;
  136. for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
  137. brelse(sb->u.adfs_sb.s_map[i].dm_bh);
  138. kfree(sb->u.adfs_sb.s_map);
  139. }
  140. static int parse_options(struct super_block *sb, char *options)
  141. {
  142. char *value, *opt;
  143. if (!options)
  144. return 0;
  145. for (opt = strtok(options, ","); opt != NULL; opt = strtok(NULL, ",")) {
  146. value = strchr(opt, '=');
  147. if (value)
  148. *value++ = '';
  149. if (!strcmp(opt, "uid")) { /* owner of all files */
  150. if (!value || !*value)
  151. return -EINVAL;
  152. sb->u.adfs_sb.s_uid = simple_strtoul(value, &value, 0);
  153. if (*value)
  154. return -EINVAL;
  155. } else
  156. if (!strcmp(opt, "gid")) { /* group owner of all files */
  157. if (!value || !*value)
  158. return -EINVAL;
  159. sb->u.adfs_sb.s_gid = simple_strtoul(value, &value, 0);
  160. if (*value)
  161. return -EINVAL;
  162. } else
  163. if (!strcmp(opt, "ownmask")) { /* owner permission mask */
  164. if (!value || !*value)
  165. return -EINVAL;
  166. sb->u.adfs_sb.s_owner_mask = simple_strtoul(value, &value, 8);
  167. if (*value)
  168. return -EINVAL;
  169. } else
  170. if (!strcmp(opt, "othmask")) { /* others permission mask */
  171. if (!value || !*value)
  172. return -EINVAL;
  173. sb->u.adfs_sb.s_other_mask = simple_strtoul(value, &value, 8);
  174. if (*value)
  175. return -EINVAL;
  176. } else { /* eh? say again. */
  177. printk("ADFS-fs: unrecognised mount option %sn", opt);
  178. return -EINVAL;
  179. }
  180. }
  181. return 0;
  182. }
  183. static int adfs_remount(struct super_block *sb, int *flags, char *data)
  184. {
  185. return parse_options(sb, data);
  186. }
  187. static int adfs_statfs(struct super_block *sb, struct statfs *buf)
  188. {
  189. struct adfs_sb_info *asb = &sb->u.adfs_sb;
  190. buf->f_type    = ADFS_SUPER_MAGIC;
  191. buf->f_namelen = asb->s_namelen;
  192. buf->f_bsize   = sb->s_blocksize;
  193. buf->f_blocks  = asb->s_size;
  194. buf->f_files   = asb->s_ids_per_zone * asb->s_map_size;
  195. buf->f_bavail  =
  196. buf->f_bfree   = adfs_map_free(sb);
  197. buf->f_ffree   = buf->f_bfree * buf->f_files / buf->f_blocks;
  198. return 0;
  199. }
  200. static struct super_operations adfs_sops = {
  201. write_inode: adfs_write_inode,
  202. put_super: adfs_put_super,
  203. statfs: adfs_statfs,
  204. remount_fs: adfs_remount,
  205. };
  206. static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)
  207. {
  208. struct adfs_discmap *dm;
  209. unsigned int map_addr, zone_size, nzones;
  210. int i, zone;
  211. nzones    = sb->u.adfs_sb.s_map_size;
  212. zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
  213. map_addr  = (nzones >> 1) * zone_size -
  214.      ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
  215. map_addr  = signed_asl(map_addr, sb->u.adfs_sb.s_map2blk);
  216. sb->u.adfs_sb.s_ids_per_zone = zone_size / (sb->u.adfs_sb.s_idlen + 1);
  217. dm = kmalloc(nzones * sizeof(*dm), GFP_KERNEL);
  218. if (dm == NULL) {
  219. adfs_error(sb, "not enough memory");
  220. return NULL;
  221. }
  222. for (zone = 0; zone < nzones; zone++, map_addr++) {
  223. dm[zone].dm_startbit = 0;
  224. dm[zone].dm_endbit   = zone_size;
  225. dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
  226. dm[zone].dm_bh       = sb_bread(sb, map_addr);
  227. if (!dm[zone].dm_bh) {
  228. adfs_error(sb, "unable to read map");
  229. goto error_free;
  230. }
  231. }
  232. /* adjust the limits for the first and last map zones */
  233. i = zone - 1;
  234. dm[0].dm_startblk = 0;
  235. dm[0].dm_startbit = ADFS_DR_SIZE_BITS;
  236. dm[i].dm_endbit   = (dr->disc_size_high << (32 - dr->log2bpmb)) +
  237.     (dr->disc_size >> dr->log2bpmb) +
  238.     (ADFS_DR_SIZE_BITS - i * zone_size);
  239. if (adfs_checkmap(sb, dm))
  240. return dm;
  241. adfs_error(sb, NULL, "map corrupted");
  242. error_free:
  243. while (--zone >= 0)
  244. brelse(dm[zone].dm_bh);
  245. kfree(dm);
  246. return NULL;
  247. }
  248. static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits)
  249. {
  250. unsigned long discsize;
  251. discsize  = le32_to_cpu(dr->disc_size_high) << (32 - block_bits);
  252. discsize |= le32_to_cpu(dr->disc_size) >> block_bits;
  253. return discsize;
  254. }
  255. struct super_block *adfs_read_super(struct super_block *sb, void *data, int silent)
  256. {
  257. struct adfs_discrecord *dr;
  258. struct buffer_head *bh;
  259. struct object_info root_obj;
  260. unsigned char *b_data;
  261. kdev_t dev = sb->s_dev;
  262. /* set default options */
  263. sb->u.adfs_sb.s_uid = 0;
  264. sb->u.adfs_sb.s_gid = 0;
  265. sb->u.adfs_sb.s_owner_mask = S_IRWXU;
  266. sb->u.adfs_sb.s_other_mask = S_IRWXG | S_IRWXO;
  267. if (parse_options(sb, data))
  268. goto error;
  269. sb->s_blocksize = BLOCK_SIZE;
  270. set_blocksize(dev, BLOCK_SIZE);
  271. if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) {
  272. adfs_error(sb, "unable to read superblock");
  273. goto error;
  274. }
  275. b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE);
  276. if (adfs_checkbblk(b_data)) {
  277. if (!silent)
  278. printk("VFS: Can't find an adfs filesystem on dev "
  279. "%s.n", bdevname(dev));
  280. goto error_free_bh;
  281. }
  282. dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
  283. /*
  284.  * Do some sanity checks on the ADFS disc record
  285.  */
  286. if (adfs_checkdiscrecord(dr)) {
  287. if (!silent)
  288. printk("VPS: Can't find an adfs filesystem on dev "
  289. "%s.n", bdevname(dev));
  290. goto error_free_bh;
  291. }
  292. sb->s_blocksize_bits = dr->log2secsize;
  293. sb->s_blocksize = 1 << sb->s_blocksize_bits;
  294. if (sb->s_blocksize != BLOCK_SIZE &&
  295.     (sb->s_blocksize == 512 || sb->s_blocksize == 1024 ||
  296.      sb->s_blocksize == 2048 || sb->s_blocksize == 4096)) {
  297. brelse(bh);
  298. set_blocksize(dev, sb->s_blocksize);
  299. bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize);
  300. if (!bh) {
  301. adfs_error(sb, "couldn't read superblock on "
  302. "2nd try.");
  303. goto error;
  304. }
  305. b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
  306. if (adfs_checkbblk(b_data)) {
  307. adfs_error(sb, "disc record mismatch, very weird!");
  308. goto error_free_bh;
  309. }
  310. dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
  311. }
  312. if (sb->s_blocksize != bh->b_size) {
  313. if (!silent)
  314. printk(KERN_ERR "VFS: Unsupported blocksize on dev "
  315. "%s.n", bdevname(dev));
  316. goto error_free_bh;
  317. }
  318. /*
  319.  * blocksize on this device should now be set to the ADFS log2secsize
  320.  */
  321. sb->s_magic  = ADFS_SUPER_MAGIC;
  322. sb->u.adfs_sb.s_idlen  = dr->idlen;
  323. sb->u.adfs_sb.s_map_size = dr->nzones | (dr->nzones_high << 8);
  324. sb->u.adfs_sb.s_map2blk  = dr->log2bpmb - dr->log2secsize;
  325. sb->u.adfs_sb.s_size     = adfs_discsize(dr, sb->s_blocksize_bits);
  326. sb->u.adfs_sb.s_version  = dr->format_version;
  327. sb->u.adfs_sb.s_log2sharesize = dr->log2sharesize;
  328. printk(KERN_DEBUG "ADFS: idlen %d map bit size %d sector size %dn",
  329. dr->idlen, 1 << dr->log2bpmb, 1 << dr->log2secsize);
  330. printk(KERN_DEBUG "ADFS: map size %d map2blk %d version %d share size %dn",
  331. sb->u.adfs_sb.s_map_size,
  332. sb->u.adfs_sb.s_map2blk,
  333. sb->u.adfs_sb.s_version,
  334. 1 << sb->u.adfs_sb.s_log2sharesize);
  335. sb->u.adfs_sb.s_map = adfs_read_map(sb, dr);
  336. if (!sb->u.adfs_sb.s_map)
  337. goto error_free_bh;
  338. brelse(bh);
  339. printk(KERN_DEBUG "ADFS: ids per zone %dn", sb->u.adfs_sb.s_ids_per_zone);
  340. /*
  341.  * set up enough so that we can read an inode
  342.  */
  343. sb->s_op = &adfs_sops;
  344. dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0].dm_bh->b_data + 4);
  345. root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root);
  346. root_obj.name_len  = 0;
  347. root_obj.loadaddr  = 0;
  348. root_obj.execaddr  = 0;
  349. root_obj.size    = ADFS_NEWDIR_SIZE;
  350. root_obj.attr    = ADFS_NDA_DIRECTORY   | ADFS_NDA_OWNER_READ |
  351.      ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ;
  352. /*
  353.  * If this is a F+ disk with variable length directories,
  354.  * get the root_size from the disc record.
  355.  */
  356. if (sb->u.adfs_sb.s_version) {
  357. root_obj.size = dr->root_size;
  358. sb->u.adfs_sb.s_dir     = &adfs_fplus_dir_ops;
  359. sb->u.adfs_sb.s_namelen = ADFS_FPLUS_NAME_LEN;
  360. } else {
  361. sb->u.adfs_sb.s_dir     = &adfs_f_dir_ops;
  362. sb->u.adfs_sb.s_namelen = ADFS_F_NAME_LEN;
  363. }
  364. sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj));
  365. if (!sb->s_root) {
  366. int i;
  367. for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
  368. brelse(sb->u.adfs_sb.s_map[i].dm_bh);
  369. kfree(sb->u.adfs_sb.s_map);
  370. adfs_error(sb, "get root inode failedn");
  371. goto error;
  372. } else
  373. sb->s_root->d_op = &adfs_dentry_operations;
  374. return sb;
  375. error_free_bh:
  376. brelse(bh);
  377. error:
  378. return NULL;
  379. }
  380. static DECLARE_FSTYPE_DEV(adfs_fs_type, "adfs", adfs_read_super);
  381. static int __init init_adfs_fs(void)
  382. {
  383. return register_filesystem(&adfs_fs_type);
  384. }
  385. static void __exit exit_adfs_fs(void)
  386. {
  387. unregister_filesystem(&adfs_fs_type);
  388. }
  389. EXPORT_NO_SYMBOLS;
  390. module_init(init_adfs_fs)
  391. module_exit(exit_adfs_fs)