1 // SPDX-License-Identifier: GPL-2.0 2 3 /* 4 * dm-init.c 5 * Copyright (C) 2017 The Chromium OS Authors <chromium-os-dev@chromium.org> 6 * 7 * This file is released under the GPLv2. 8 */ 9 10 #include <linux/ctype.h> 11 #include <linux/device.h> 12 #include <linux/device-mapper.h> 13 #include <linux/init.h> 14 #include <linux/list.h> 15 #include <linux/moduleparam.h> 16 17 #define DM_MSG_PREFIX "init" 18 #define DM_MAX_DEVICES 256 19 #define DM_MAX_TARGETS 256 20 #define DM_MAX_STR_SIZE 4096 21 22 static char *create; 23 24 /* 25 * Format: dm-mod.create=<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+] 26 * Table format: <start_sector> <num_sectors> <target_type> <target_args> 27 * 28 * See Documentation/device-mapper/dm-init.txt for dm-mod.create="..." format 29 * details. 30 */ 31 32 struct dm_device { 33 struct dm_ioctl dmi; 34 struct dm_target_spec *table[DM_MAX_TARGETS]; 35 char *target_args_array[DM_MAX_TARGETS]; 36 struct list_head list; 37 }; 38 39 const char *dm_allowed_targets[] __initconst = { 40 "crypt", 41 "delay", 42 "linear", 43 "snapshot-origin", 44 "striped", 45 "verity", 46 }; 47 48 static int __init dm_verify_target_type(const char *target) 49 { 50 unsigned int i; 51 52 for (i = 0; i < ARRAY_SIZE(dm_allowed_targets); i++) { 53 if (!strcmp(dm_allowed_targets[i], target)) 54 return 0; 55 } 56 return -EINVAL; 57 } 58 59 static void __init dm_setup_cleanup(struct list_head *devices) 60 { 61 struct dm_device *dev, *tmp; 62 unsigned int i; 63 64 list_for_each_entry_safe(dev, tmp, devices, list) { 65 list_del(&dev->list); 66 for (i = 0; i < dev->dmi.target_count; i++) { 67 kfree(dev->table[i]); 68 kfree(dev->target_args_array[i]); 69 } 70 kfree(dev); 71 } 72 } 73 74 /** 75 * str_field_delimit - delimit a string based on a separator char. 76 * @str: the pointer to the string to delimit. 77 * @separator: char that delimits the field 78 * 79 * Find a @separator and replace it by '\0'. 80 * Remove leading and trailing spaces. 81 * Return the remainder string after the @separator. 82 */ 83 static char __init *str_field_delimit(char **str, char separator) 84 { 85 char *s; 86 87 /* TODO: add support for escaped characters */ 88 *str = skip_spaces(*str); 89 s = strchr(*str, separator); 90 /* Delimit the field and remove trailing spaces */ 91 if (s) 92 *s = '\0'; 93 *str = strim(*str); 94 return s ? ++s : NULL; 95 } 96 97 /** 98 * dm_parse_table_entry - parse a table entry 99 * @dev: device to store the parsed information. 100 * @str: the pointer to a string with the format: 101 * <start_sector> <num_sectors> <target_type> <target_args>[, ...] 102 * 103 * Return the remainder string after the table entry, i.e, after the comma which 104 * delimits the entry or NULL if reached the end of the string. 105 */ 106 static char __init *dm_parse_table_entry(struct dm_device *dev, char *str) 107 { 108 const unsigned int n = dev->dmi.target_count - 1; 109 struct dm_target_spec *sp; 110 unsigned int i; 111 /* fields: */ 112 char *field[4]; 113 char *next; 114 115 field[0] = str; 116 /* Delimit first 3 fields that are separated by space */ 117 for (i = 0; i < ARRAY_SIZE(field) - 1; i++) { 118 field[i + 1] = str_field_delimit(&field[i], ' '); 119 if (!field[i + 1]) 120 return ERR_PTR(-EINVAL); 121 } 122 /* Delimit last field that can be terminated by comma */ 123 next = str_field_delimit(&field[i], ','); 124 125 sp = kzalloc(sizeof(*sp), GFP_KERNEL); 126 if (!sp) 127 return ERR_PTR(-ENOMEM); 128 dev->table[n] = sp; 129 130 /* start_sector */ 131 if (kstrtoull(field[0], 0, &sp->sector_start)) 132 return ERR_PTR(-EINVAL); 133 /* num_sector */ 134 if (kstrtoull(field[1], 0, &sp->length)) 135 return ERR_PTR(-EINVAL); 136 /* target_type */ 137 strscpy(sp->target_type, field[2], sizeof(sp->target_type)); 138 if (dm_verify_target_type(sp->target_type)) { 139 DMERR("invalid type \"%s\"", sp->target_type); 140 return ERR_PTR(-EINVAL); 141 } 142 /* target_args */ 143 dev->target_args_array[n] = kstrndup(field[3], GFP_KERNEL, 144 DM_MAX_STR_SIZE); 145 if (!dev->target_args_array[n]) 146 return ERR_PTR(-ENOMEM); 147 148 return next; 149 } 150 151 /** 152 * dm_parse_table - parse "dm-mod.create=" table field 153 * @dev: device to store the parsed information. 154 * @str: the pointer to a string with the format: 155 * <table>[,<table>+] 156 */ 157 static int __init dm_parse_table(struct dm_device *dev, char *str) 158 { 159 char *table_entry = str; 160 161 while (table_entry) { 162 DMDEBUG("parsing table \"%s\"", str); 163 if (++dev->dmi.target_count >= DM_MAX_TARGETS) { 164 DMERR("too many targets %u > %d", 165 dev->dmi.target_count, DM_MAX_TARGETS); 166 return -EINVAL; 167 } 168 table_entry = dm_parse_table_entry(dev, table_entry); 169 if (IS_ERR(table_entry)) { 170 DMERR("couldn't parse table"); 171 return PTR_ERR(table_entry); 172 } 173 } 174 175 return 0; 176 } 177 178 /** 179 * dm_parse_device_entry - parse a device entry 180 * @dev: device to store the parsed information. 181 * @str: the pointer to a string with the format: 182 * name,uuid,minor,flags,table[; ...] 183 * 184 * Return the remainder string after the table entry, i.e, after the semi-colon 185 * which delimits the entry or NULL if reached the end of the string. 186 */ 187 static char __init *dm_parse_device_entry(struct dm_device *dev, char *str) 188 { 189 /* There are 5 fields: name,uuid,minor,flags,table; */ 190 char *field[5]; 191 unsigned int i; 192 char *next; 193 194 field[0] = str; 195 /* Delimit first 4 fields that are separated by comma */ 196 for (i = 0; i < ARRAY_SIZE(field) - 1; i++) { 197 field[i+1] = str_field_delimit(&field[i], ','); 198 if (!field[i+1]) 199 return ERR_PTR(-EINVAL); 200 } 201 /* Delimit last field that can be delimited by semi-colon */ 202 next = str_field_delimit(&field[i], ';'); 203 204 /* name */ 205 strscpy(dev->dmi.name, field[0], sizeof(dev->dmi.name)); 206 /* uuid */ 207 strscpy(dev->dmi.uuid, field[1], sizeof(dev->dmi.uuid)); 208 /* minor */ 209 if (strlen(field[2])) { 210 if (kstrtoull(field[2], 0, &dev->dmi.dev)) 211 return ERR_PTR(-EINVAL); 212 dev->dmi.flags |= DM_PERSISTENT_DEV_FLAG; 213 } 214 /* flags */ 215 if (!strcmp(field[3], "ro")) 216 dev->dmi.flags |= DM_READONLY_FLAG; 217 else if (strcmp(field[3], "rw")) 218 return ERR_PTR(-EINVAL); 219 /* table */ 220 if (dm_parse_table(dev, field[4])) 221 return ERR_PTR(-EINVAL); 222 223 return next; 224 } 225 226 /** 227 * dm_parse_devices - parse "dm-mod.create=" argument 228 * @devices: list of struct dm_device to store the parsed information. 229 * @str: the pointer to a string with the format: 230 * <device>[;<device>+] 231 */ 232 static int __init dm_parse_devices(struct list_head *devices, char *str) 233 { 234 unsigned long ndev = 0; 235 struct dm_device *dev; 236 char *device = str; 237 238 DMDEBUG("parsing \"%s\"", str); 239 while (device) { 240 dev = kzalloc(sizeof(*dev), GFP_KERNEL); 241 if (!dev) 242 return -ENOMEM; 243 list_add_tail(&dev->list, devices); 244 245 if (++ndev >= DM_MAX_DEVICES) { 246 DMERR("too many targets %u > %d", 247 dev->dmi.target_count, DM_MAX_TARGETS); 248 return -EINVAL; 249 } 250 251 device = dm_parse_device_entry(dev, device); 252 if (IS_ERR(device)) { 253 DMERR("couldn't parse device"); 254 return PTR_ERR(device); 255 } 256 } 257 258 return 0; 259 } 260 261 /** 262 * dm_init_init - parse "dm-mod.create=" argument and configure drivers 263 */ 264 static int __init dm_init_init(void) 265 { 266 struct dm_device *dev; 267 LIST_HEAD(devices); 268 char *str; 269 int r; 270 271 if (!create) 272 return 0; 273 274 if (strlen(create) >= DM_MAX_STR_SIZE) { 275 DMERR("Argument is too big. Limit is %d\n", DM_MAX_STR_SIZE); 276 return -EINVAL; 277 } 278 str = kstrndup(create, GFP_KERNEL, DM_MAX_STR_SIZE); 279 if (!str) 280 return -ENOMEM; 281 282 r = dm_parse_devices(&devices, str); 283 if (r) 284 goto out; 285 286 DMINFO("waiting for all devices to be available before creating mapped devices\n"); 287 wait_for_device_probe(); 288 289 list_for_each_entry(dev, &devices, list) { 290 if (dm_early_create(&dev->dmi, dev->table, 291 dev->target_args_array)) 292 break; 293 } 294 out: 295 kfree(str); 296 dm_setup_cleanup(&devices); 297 return r; 298 } 299 300 late_initcall(dm_init_init); 301 302 module_param(create, charp, 0); 303 MODULE_PARM_DESC(create, "Create a mapped device in early boot"); 304