mtdsuper.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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 <dwmw2@infradead.org>
  6. *
  7. * Written by: David Howells <dhowells@redhat.com>
  8. * David Woodhouse <dwmw2@infradead.org>
  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. * get a superblock on an MTD-backed filesystem
  22. */
  23. static int mtd_get_sb(struct fs_context *fc,
  24. struct mtd_info *mtd,
  25. int (*fill_super)(struct super_block *,
  26. struct fs_context *))
  27. {
  28. struct super_block *sb;
  29. int ret;
  30. sb = sget_dev(fc, MKDEV(MTD_BLOCK_MAJOR, mtd->index));
  31. if (IS_ERR(sb))
  32. return PTR_ERR(sb);
  33. if (sb->s_root) {
  34. /* new mountpoint for an already mounted superblock */
  35. pr_debug("MTDSB: Device %d (\"%s\") is already mounted\n",
  36. mtd->index, mtd->name);
  37. put_mtd_device(mtd);
  38. } else {
  39. /* fresh new superblock */
  40. pr_debug("MTDSB: New superblock for device %d (\"%s\")\n",
  41. mtd->index, mtd->name);
  42. /*
  43. * Would usually have been set with @sb_lock held but in
  44. * contrast to sb->s_bdev that's checked with only
  45. * @sb_lock held, nothing checks sb->s_mtd without also
  46. * holding sb->s_umount and we're holding sb->s_umount
  47. * here.
  48. */
  49. sb->s_mtd = mtd;
  50. sb->s_bdi = bdi_get(mtd_bdi);
  51. ret = fill_super(sb, fc);
  52. if (ret < 0)
  53. goto error_sb;
  54. sb->s_flags |= SB_ACTIVE;
  55. }
  56. BUG_ON(fc->root);
  57. fc->root = dget(sb->s_root);
  58. return 0;
  59. error_sb:
  60. deactivate_locked_super(sb);
  61. return ret;
  62. }
  63. /*
  64. * get a superblock on an MTD-backed filesystem by MTD device number
  65. */
  66. static int mtd_get_sb_by_nr(struct fs_context *fc, int mtdnr,
  67. int (*fill_super)(struct super_block *,
  68. struct fs_context *))
  69. {
  70. struct mtd_info *mtd;
  71. mtd = get_mtd_device(NULL, mtdnr);
  72. if (IS_ERR(mtd)) {
  73. errorf(fc, "MTDSB: Device #%u doesn't appear to exist\n", mtdnr);
  74. return PTR_ERR(mtd);
  75. }
  76. return mtd_get_sb(fc, mtd, fill_super);
  77. }
  78. /**
  79. * get_tree_mtd - Get a superblock based on a single MTD device
  80. * @fc: The filesystem context holding the parameters
  81. * @fill_super: Helper to initialise a new superblock
  82. */
  83. int get_tree_mtd(struct fs_context *fc,
  84. int (*fill_super)(struct super_block *sb,
  85. struct fs_context *fc))
  86. {
  87. #ifdef CONFIG_BLOCK
  88. dev_t dev;
  89. int ret;
  90. #endif
  91. int mtdnr;
  92. if (!fc->source)
  93. return invalf(fc, "No source specified");
  94. pr_debug("MTDSB: dev_name \"%s\"\n", fc->source);
  95. /* the preferred way of mounting in future; especially when
  96. * CONFIG_BLOCK=n - we specify the underlying MTD device by number or
  97. * by name, so that we don't require block device support to be present
  98. * in the kernel.
  99. */
  100. if (fc->source[0] == 'm' &&
  101. fc->source[1] == 't' &&
  102. fc->source[2] == 'd') {
  103. if (fc->source[3] == ':') {
  104. struct mtd_info *mtd;
  105. /* mount by MTD device name */
  106. pr_debug("MTDSB: mtd:%%s, name \"%s\"\n",
  107. fc->source + 4);
  108. mtd = get_mtd_device_nm(fc->source + 4);
  109. if (!IS_ERR(mtd))
  110. return mtd_get_sb(fc, mtd, fill_super);
  111. errorf(fc, "MTD: MTD device with name \"%s\" not found",
  112. fc->source + 4);
  113. } else if (isdigit(fc->source[3])) {
  114. /* mount by MTD device number name */
  115. char *endptr;
  116. mtdnr = simple_strtoul(fc->source + 3, &endptr, 0);
  117. if (!*endptr) {
  118. /* It was a valid number */
  119. pr_debug("MTDSB: mtd%%d, mtdnr %d\n", mtdnr);
  120. return mtd_get_sb_by_nr(fc, mtdnr, fill_super);
  121. }
  122. }
  123. }
  124. #ifdef CONFIG_BLOCK
  125. /* try the old way - the hack where we allowed users to mount
  126. * /dev/mtdblock$(n) but didn't actually _use_ the blockdev
  127. */
  128. ret = lookup_bdev(fc->source, &dev);
  129. if (ret) {
  130. errorf(fc, "MTD: Couldn't look up '%s': %d", fc->source, ret);
  131. return ret;
  132. }
  133. pr_debug("MTDSB: lookup_bdev() returned 0\n");
  134. if (MAJOR(dev) == MTD_BLOCK_MAJOR)
  135. return mtd_get_sb_by_nr(fc, MINOR(dev), fill_super);
  136. #endif /* CONFIG_BLOCK */
  137. if (!(fc->sb_flags & SB_SILENT))
  138. errorf(fc, "MTD: Attempt to mount non-MTD device \"%s\"",
  139. fc->source);
  140. return -EINVAL;
  141. }
  142. EXPORT_SYMBOL_GPL(get_tree_mtd);
  143. /*
  144. * destroy an MTD-based superblock
  145. */
  146. void kill_mtd_super(struct super_block *sb)
  147. {
  148. generic_shutdown_super(sb);
  149. put_mtd_device(sb->s_mtd);
  150. sb->s_mtd = NULL;
  151. }
  152. EXPORT_SYMBOL_GPL(kill_mtd_super);