do_mounts_dm.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. /* do_mounts_dm.c
  2. * Copyright (C) 2010 The Chromium OS Authors <chromium-os-dev@chromium.org>
  3. * All Rights Reserved.
  4. * Based on do_mounts_md.c
  5. *
  6. * This file is released under the GPL.
  7. */
  8. #include <linux/device-mapper.h>
  9. #include <linux/fs.h>
  10. #include <linux/string.h>
  11. #include "do_mounts.h"
  12. #define DM_MAX_NAME 32
  13. #define DM_MAX_UUID 129
  14. #define DM_NO_UUID "none"
  15. #define DM_MSG_PREFIX "init"
  16. /* Separators used for parsing the dm= argument. */
  17. #define DM_FIELD_SEP ' '
  18. #define DM_LINE_SEP ','
  19. /*
  20. * When the device-mapper and any targets are compiled into the kernel
  21. * (not a module), one target may be created and used as the root device at
  22. * boot time with the parameters given with the boot line dm=...
  23. * The code for that is here.
  24. */
  25. struct dm_setup_target {
  26. sector_t begin;
  27. sector_t length;
  28. char *type;
  29. char *params;
  30. /* simple singly linked list */
  31. struct dm_setup_target *next;
  32. };
  33. static struct {
  34. int minor;
  35. int ro;
  36. char name[DM_MAX_NAME];
  37. char uuid[DM_MAX_UUID];
  38. char *targets;
  39. struct dm_setup_target *target;
  40. int target_count;
  41. } dm_setup_args __initdata;
  42. static __initdata int dm_early_setup;
  43. static size_t __init get_dm_option(char *str, char **next, char sep)
  44. {
  45. size_t len = 0;
  46. char *endp = NULL;
  47. if (!str)
  48. return 0;
  49. endp = strchr(str, sep);
  50. if (!endp) { /* act like strchrnul */
  51. len = strlen(str);
  52. endp = str + len;
  53. } else {
  54. len = endp - str;
  55. }
  56. if (endp == str)
  57. return 0;
  58. if (!next)
  59. return len;
  60. if (*endp == 0) {
  61. /* Don't advance past the nul. */
  62. *next = endp;
  63. } else {
  64. *next = endp + 1;
  65. }
  66. return len;
  67. }
  68. static int __init dm_setup_args_init(void)
  69. {
  70. dm_setup_args.minor = 0;
  71. dm_setup_args.ro = 0;
  72. dm_setup_args.target = NULL;
  73. dm_setup_args.target_count = 0;
  74. return 0;
  75. }
  76. static int __init dm_setup_cleanup(void)
  77. {
  78. struct dm_setup_target *target = dm_setup_args.target;
  79. struct dm_setup_target *old_target = NULL;
  80. while (target) {
  81. kfree(target->type);
  82. kfree(target->params);
  83. old_target = target;
  84. target = target->next;
  85. kfree(old_target);
  86. dm_setup_args.target_count--;
  87. }
  88. BUG_ON(dm_setup_args.target_count);
  89. return 0;
  90. }
  91. static char * __init dm_setup_parse_device_args(char *str)
  92. {
  93. char *next = NULL;
  94. size_t len = 0;
  95. /* Grab the logical name of the device to be exported to udev */
  96. len = get_dm_option(str, &next, DM_FIELD_SEP);
  97. if (!len) {
  98. DMERR("failed to parse device name");
  99. goto parse_fail;
  100. }
  101. len = min(len + 1, sizeof(dm_setup_args.name));
  102. strlcpy(dm_setup_args.name, str, len); /* includes nul */
  103. str = skip_spaces(next);
  104. /* Grab the UUID value or "none" */
  105. len = get_dm_option(str, &next, DM_FIELD_SEP);
  106. if (!len) {
  107. DMERR("failed to parse device uuid");
  108. goto parse_fail;
  109. }
  110. len = min(len + 1, sizeof(dm_setup_args.uuid));
  111. strlcpy(dm_setup_args.uuid, str, len);
  112. str = skip_spaces(next);
  113. /* Determine if the table/device will be read only or read-write */
  114. if (!strncmp("ro,", str, 3)) {
  115. dm_setup_args.ro = 1;
  116. } else if (!strncmp("rw,", str, 3)) {
  117. dm_setup_args.ro = 0;
  118. } else {
  119. DMERR("failed to parse table mode");
  120. goto parse_fail;
  121. }
  122. str = skip_spaces(str + 3);
  123. return str;
  124. parse_fail:
  125. return NULL;
  126. }
  127. static void __init dm_substitute_devices(char *str, size_t str_len)
  128. {
  129. char *candidate = str;
  130. char *candidate_end = str;
  131. char old_char;
  132. size_t len = 0;
  133. dev_t dev;
  134. if (str_len < 3)
  135. return;
  136. while (str && *str) {
  137. candidate = strchr(str, '/');
  138. if (!candidate)
  139. break;
  140. /* Avoid embedded slashes */
  141. if (candidate != str && *(candidate - 1) != DM_FIELD_SEP) {
  142. str = strchr(candidate, DM_FIELD_SEP);
  143. continue;
  144. }
  145. len = get_dm_option(candidate, &candidate_end, DM_FIELD_SEP);
  146. str = skip_spaces(candidate_end);
  147. if (len < 3 || len > 37) /* name_to_dev_t max; maj:mix min */
  148. continue;
  149. /* Temporarily terminate with a nul */
  150. candidate_end--;
  151. old_char = *candidate_end;
  152. *candidate_end = '\0';
  153. DMDEBUG("converting candidate device '%s' to dev_t", candidate);
  154. /* Use the boot-time specific device naming */
  155. dev = name_to_dev_t(candidate);
  156. *candidate_end = old_char;
  157. DMDEBUG(" -> %u", dev);
  158. /* No suitable replacement found */
  159. if (!dev)
  160. continue;
  161. /* Rewrite the /dev/path as a major:minor */
  162. len = snprintf(candidate, len, "%u:%u", MAJOR(dev), MINOR(dev));
  163. if (!len) {
  164. DMERR("error substituting device major/minor.");
  165. break;
  166. }
  167. candidate += len;
  168. /* Pad out with spaces (fixing our nul) */
  169. while (candidate < candidate_end)
  170. *(candidate++) = DM_FIELD_SEP;
  171. }
  172. }
  173. static int __init dm_setup_parse_targets(char *str)
  174. {
  175. char *next = NULL;
  176. size_t len = 0;
  177. struct dm_setup_target **target = NULL;
  178. /* Targets are defined as per the table format but with a
  179. * comma as a newline separator. */
  180. target = &dm_setup_args.target;
  181. while (str && *str) {
  182. *target = kzalloc(sizeof(struct dm_setup_target), GFP_KERNEL);
  183. if (!*target) {
  184. DMERR("failed to allocate memory for target %d",
  185. dm_setup_args.target_count);
  186. goto parse_fail;
  187. }
  188. dm_setup_args.target_count++;
  189. (*target)->begin = simple_strtoull(str, &next, 10);
  190. if (!next || *next != DM_FIELD_SEP) {
  191. DMERR("failed to parse starting sector for target %d",
  192. dm_setup_args.target_count - 1);
  193. goto parse_fail;
  194. }
  195. str = skip_spaces(next + 1);
  196. (*target)->length = simple_strtoull(str, &next, 10);
  197. if (!next || *next != DM_FIELD_SEP) {
  198. DMERR("failed to parse length for target %d",
  199. dm_setup_args.target_count - 1);
  200. goto parse_fail;
  201. }
  202. str = skip_spaces(next + 1);
  203. len = get_dm_option(str, &next, DM_FIELD_SEP);
  204. if (!len ||
  205. !((*target)->type = kstrndup(str, len, GFP_KERNEL))) {
  206. DMERR("failed to parse type for target %d",
  207. dm_setup_args.target_count - 1);
  208. goto parse_fail;
  209. }
  210. str = skip_spaces(next);
  211. len = get_dm_option(str, &next, DM_LINE_SEP);
  212. if (!len ||
  213. !((*target)->params = kstrndup(str, len, GFP_KERNEL))) {
  214. DMERR("failed to parse params for target %d",
  215. dm_setup_args.target_count - 1);
  216. goto parse_fail;
  217. }
  218. str = skip_spaces(next);
  219. /* Before moving on, walk through the copied target and
  220. * attempt to replace all /dev/xxx with the major:minor number.
  221. * It may not be possible to resolve them traditionally at
  222. * boot-time. */
  223. dm_substitute_devices((*target)->params, len);
  224. target = &((*target)->next);
  225. }
  226. DMDEBUG("parsed %d targets", dm_setup_args.target_count);
  227. return 0;
  228. parse_fail:
  229. return 1;
  230. }
  231. /*
  232. * Parse the command-line parameters given our kernel, but do not
  233. * actually try to invoke the DM device now; that is handled by
  234. * dm_setup_drive after the low-level disk drivers have initialised.
  235. * dm format is as follows:
  236. * dm="name uuid fmode,[table line 1],[table line 2],..."
  237. * May be used with root=/dev/dm-0 as it always uses the first dm minor.
  238. */
  239. static int __init dm_setup(char *str)
  240. {
  241. dm_setup_args_init();
  242. str = dm_setup_parse_device_args(str);
  243. if (!str) {
  244. DMDEBUG("str is NULL");
  245. goto parse_fail;
  246. }
  247. /* Target parsing is delayed until we have dynamic memory */
  248. dm_setup_args.targets = str;
  249. printk(KERN_INFO "dm: will configure '%s' on dm-%d\n",
  250. dm_setup_args.name, dm_setup_args.minor);
  251. dm_early_setup = 1;
  252. return 1;
  253. parse_fail:
  254. printk(KERN_WARNING "dm: Invalid arguments supplied to dm=.\n");
  255. return 0;
  256. }
  257. static void __init dm_setup_drive(void)
  258. {
  259. struct mapped_device *md = NULL;
  260. struct dm_table *table = NULL;
  261. struct dm_setup_target *target;
  262. char *uuid = dm_setup_args.uuid;
  263. fmode_t fmode = FMODE_READ;
  264. /* Finish parsing the targets. */
  265. if (dm_setup_parse_targets(dm_setup_args.targets))
  266. goto parse_fail;
  267. if (dm_create(dm_setup_args.minor, &md)) {
  268. DMDEBUG("failed to create the device");
  269. goto dm_create_fail;
  270. }
  271. DMDEBUG("created device '%s'", dm_device_name(md));
  272. /* In addition to flagging the table below, the disk must be
  273. * set explicitly ro/rw. */
  274. set_disk_ro(dm_disk(md), dm_setup_args.ro);
  275. if (!dm_setup_args.ro)
  276. fmode |= FMODE_WRITE;
  277. if (dm_table_create(&table, fmode, dm_setup_args.target_count, md)) {
  278. DMDEBUG("failed to create the table");
  279. goto dm_table_create_fail;
  280. }
  281. target = dm_setup_args.target;
  282. while (target) {
  283. DMINFO("adding target '%llu %llu %s %s'",
  284. (unsigned long long) target->begin,
  285. (unsigned long long) target->length, target->type,
  286. target->params);
  287. if (dm_table_add_target(table, target->type, target->begin,
  288. target->length, target->params)) {
  289. DMDEBUG("failed to add the target to the table");
  290. goto add_target_fail;
  291. }
  292. target = target->next;
  293. }
  294. if (dm_table_complete(table)) {
  295. DMDEBUG("failed to complete the table");
  296. goto table_complete_fail;
  297. }
  298. /* Suspend the device so that we can bind it to the table. */
  299. if (dm_suspend(md, 0)) {
  300. DMDEBUG("failed to suspend the device pre-bind");
  301. goto suspend_fail;
  302. }
  303. /* Bind the table to the device. This is the only way to associate
  304. * md->map with the table and set the disk capacity directly. */
  305. if (dm_swap_table(md, table)) { /* should return NULL. */
  306. DMDEBUG("failed to bind the device to the table");
  307. goto table_bind_fail;
  308. }
  309. /* Finally, resume and the device should be ready. */
  310. if (dm_resume(md)) {
  311. DMDEBUG("failed to resume the device");
  312. goto resume_fail;
  313. }
  314. /* Export the dm device via the ioctl interface */
  315. if (!strcmp(DM_NO_UUID, dm_setup_args.uuid))
  316. uuid = NULL;
  317. if (dm_ioctl_export(md, dm_setup_args.name, uuid)) {
  318. DMDEBUG("failed to export device with given name and uuid");
  319. goto export_fail;
  320. }
  321. printk(KERN_INFO "dm: dm-%d is ready\n", dm_setup_args.minor);
  322. dm_setup_cleanup();
  323. return;
  324. export_fail:
  325. resume_fail:
  326. table_bind_fail:
  327. suspend_fail:
  328. table_complete_fail:
  329. add_target_fail:
  330. dm_table_create_fail:
  331. dm_put(md);
  332. dm_create_fail:
  333. dm_setup_cleanup();
  334. parse_fail:
  335. printk(KERN_WARNING "dm: starting dm-%d (%s) failed\n",
  336. dm_setup_args.minor, dm_setup_args.name);
  337. }
  338. __setup("dm=", dm_setup);
  339. void __init dm_run_setup(void)
  340. {
  341. if (!dm_early_setup)
  342. return;
  343. printk(KERN_INFO "dm: attempting early device configuration.\n");
  344. dm_setup_drive();
  345. }