mtdsuper.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /* MTD-based superblock management
  3. *
  4. * Copyright © 2001-2007 Red Hat, Inc. All Rights Reserved.
  5. * Copyright © 2001-2010 David Woodhouse <[email protected]>
  6. *
  7. * Written by: David Howells <[email protected]>
  8. * David Woodhouse <[email protected]>
  9. */
  10. #include <linux/mtd/super.h>
  11. #include <linux/namei.h>
  12. #include <linux/export.h>
  13. #include <linux/ctype.h>
  14. #include <linux/slab.h>
  15. #include <linux/major.h>
  16. #include <linux/backing-dev.h>
  17. #include <linux/blkdev.h>
  18. #include <linux/fs_context.h>
  19. #include "mtdcore.h"
  20. /*
  21. * compare superblocks to see if they're equivalent
  22. * - they are if the underlying MTD device is the same
  23. */
  24. static int mtd_test_super(struct super_block *sb, struct fs_context *fc)
  25. {
  26. struct mtd_info *mtd = fc->sget_key;
  27. if (sb->s_mtd == fc->sget_key) {
  28. pr_debug("MTDSB: Match on device %d (\"%s\")\n",
  29. mtd->index, mtd->name);
  30. return 1;
  31. }
  32. pr_debug("MTDSB: No match, device %d (\"%s\"), device %d (\"%s\")\n",
  33. sb->s_mtd->index, sb->s_mtd->name, mtd->index, mtd->name);
  34. return 0;
  35. }
  36. /*
  37. * mark the superblock by the MTD device it is using
  38. * - set the device number to be the correct MTD block device for pesuperstence
  39. * of NFS exports
  40. */
  41. static int mtd_set_super(struct super_block *sb, struct fs_context *fc)
  42. {
  43. sb->s_mtd = fc->sget_key;
  44. sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, sb->s_mtd->index);
  45. sb->s_bdi = bdi_get(mtd_bdi);
  46. return 0;
  47. }
  48. /*
  49. * get a superblock on an MTD-backed filesystem
  50. */
  51. static int mtd_get_sb(struct fs_context *fc,
  52. struct mtd_info *mtd,
  53. int (*fill_super)(struct super_block *,
  54. struct fs_context *))
  55. {
  56. struct super_block *sb;
  57. int ret;
  58. fc->sget_key = mtd;
  59. sb = sget_fc(fc, mtd_test_super, mtd_set_super);
  60. if (IS_ERR(sb))
  61. return PTR_ERR(sb);
  62. if (sb->s_root) {
  63. /* new mountpoint for an already mounted superblock */
  64. pr_debug("MTDSB: Device %d (\"%s\") is already mounted\n",
  65. mtd->index, mtd->name);
  66. put_mtd_device(mtd);
  67. } else {
  68. /* fresh new superblock */
  69. pr_debug("MTDSB: New superblock for device %d (\"%s\")\n",
  70. mtd->index, mtd->name);
  71. ret = fill_super(sb, fc);
  72. if (ret < 0)
  73. goto error_sb;
  74. sb->s_flags |= SB_ACTIVE;
  75. }
  76. BUG_ON(fc->root);
  77. fc->root = dget(sb->s_root);
  78. return 0;
  79. error_sb:
  80. deactivate_locked_super(sb);
  81. return ret;
  82. }
  83. /*
  84. * get a superblock on an MTD-backed filesystem by MTD device number
  85. */
  86. static int mtd_get_sb_by_nr(struct fs_context *fc, int mtdnr,
  87. int (*fill_super)(struct super_block *,
  88. struct fs_context *))
  89. {
  90. struct mtd_info *mtd;
  91. mtd = get_mtd_device(NULL, mtdnr);
  92. if (IS_ERR(mtd)) {
  93. errorf(fc, "MTDSB: Device #%u doesn't appear to exist\n", mtdnr);
  94. return PTR_ERR(mtd);
  95. }
  96. return mtd_get_sb(fc, mtd, fill_super);
  97. }
  98. /**
  99. * get_tree_mtd - Get a superblock based on a single MTD device
  100. * @fc: The filesystem context holding the parameters
  101. * @fill_super: Helper to initialise a new superblock
  102. */
  103. int get_tree_mtd(struct fs_context *fc,
  104. int (*fill_super)(struct super_block *sb,
  105. struct fs_context *fc))
  106. {
  107. #ifdef CONFIG_BLOCK
  108. dev_t dev;
  109. int ret;
  110. #endif
  111. int mtdnr;
  112. if (!fc->source)
  113. return invalf(fc, "No source specified");
  114. pr_debug("MTDSB: dev_name \"%s\"\n", fc->source);
  115. /* the preferred way of mounting in future; especially when
  116. * CONFIG_BLOCK=n - we specify the underlying MTD device by number or
  117. * by name, so that we don't require block device support to be present
  118. * in the kernel.
  119. */
  120. if (fc->source[0] == 'm' &&
  121. fc->source[1] == 't' &&
  122. fc->source[2] == 'd') {
  123. if (fc->source[3] == ':') {
  124. struct mtd_info *mtd;
  125. /* mount by MTD device name */
  126. pr_debug("MTDSB: mtd:%%s, name \"%s\"\n",
  127. fc->source + 4);
  128. mtd = get_mtd_device_nm(fc->source + 4);
  129. if (!IS_ERR(mtd))
  130. return mtd_get_sb(fc, mtd, fill_super);
  131. errorf(fc, "MTD: MTD device with name \"%s\" not found",
  132. fc->source + 4);
  133. } else if (isdigit(fc->source[3])) {
  134. /* mount by MTD device number name */
  135. char *endptr;
  136. mtdnr = simple_strtoul(fc->source + 3, &endptr, 0);
  137. if (!*endptr) {
  138. /* It was a valid number */
  139. pr_debug("MTDSB: mtd%%d, mtdnr %d\n", mtdnr);
  140. return mtd_get_sb_by_nr(fc, mtdnr, fill_super);
  141. }
  142. }
  143. }
  144. #ifdef CONFIG_BLOCK
  145. /* try the old way - the hack where we allowed users to mount
  146. * /dev/mtdblock$(n) but didn't actually _use_ the blockdev
  147. */
  148. ret = lookup_bdev(fc->source, &dev);
  149. if (ret) {
  150. errorf(fc, "MTD: Couldn't look up '%s': %d", fc->source, ret);
  151. return ret;
  152. }
  153. pr_debug("MTDSB: lookup_bdev() returned 0\n");
  154. if (MAJOR(dev) == MTD_BLOCK_MAJOR)
  155. return mtd_get_sb_by_nr(fc, MINOR(dev), fill_super);
  156. #endif /* CONFIG_BLOCK */
  157. if (!(fc->sb_flags & SB_SILENT))
  158. errorf(fc, "MTD: Attempt to mount non-MTD device \"%s\"",
  159. fc->source);
  160. return -EINVAL;
  161. }
  162. EXPORT_SYMBOL_GPL(get_tree_mtd);
  163. /*
  164. * destroy an MTD-based superblock
  165. */
  166. void kill_mtd_super(struct super_block *sb)
  167. {
  168. generic_shutdown_super(sb);
  169. put_mtd_device(sb->s_mtd);
  170. sb->s_mtd = NULL;
  171. }
  172. EXPORT_SYMBOL_GPL(kill_mtd_super);