root/drivers/md/dm-init.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dm_verify_target_type
  2. dm_setup_cleanup
  3. str_field_delimit
  4. dm_parse_table_entry
  5. dm_parse_table
  6. dm_parse_device_entry
  7. dm_parse_devices
  8. dm_init_init

   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/admin-guide/device-mapper/dm-init.rst 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 * const 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], DM_MAX_STR_SIZE,
 144                                              GFP_KERNEL);
 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 devices %lu > %d",
 247                               ndev, DM_MAX_DEVICES);
 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", DM_MAX_STR_SIZE);
 276                 return -EINVAL;
 277         }
 278         str = kstrndup(create, DM_MAX_STR_SIZE, GFP_KERNEL);
 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");
 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");

/* [<][>][^][v][top][bottom][index][help] */