linux/block/cmdline-parser.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Parse command line, get partition information
   4 *
   5 * Written by Cai Zhiyong <caizhiyong@huawei.com>
   6 *
   7 */
   8#include <linux/export.h>
   9#include <linux/cmdline-parser.h>
  10
  11static int parse_subpart(struct cmdline_subpart **subpart, char *partdef)
  12{
  13        int ret = 0;
  14        struct cmdline_subpart *new_subpart;
  15
  16        *subpart = NULL;
  17
  18        new_subpart = kzalloc(sizeof(struct cmdline_subpart), GFP_KERNEL);
  19        if (!new_subpart)
  20                return -ENOMEM;
  21
  22        if (*partdef == '-') {
  23                new_subpart->size = (sector_t)(~0ULL);
  24                partdef++;
  25        } else {
  26                new_subpart->size = (sector_t)memparse(partdef, &partdef);
  27                if (new_subpart->size < (sector_t)PAGE_SIZE) {
  28                        pr_warn("cmdline partition size is invalid.");
  29                        ret = -EINVAL;
  30                        goto fail;
  31                }
  32        }
  33
  34        if (*partdef == '@') {
  35                partdef++;
  36                new_subpart->from = (sector_t)memparse(partdef, &partdef);
  37        } else {
  38                new_subpart->from = (sector_t)(~0ULL);
  39        }
  40
  41        if (*partdef == '(') {
  42                int length;
  43                char *next = strchr(++partdef, ')');
  44
  45                if (!next) {
  46                        pr_warn("cmdline partition format is invalid.");
  47                        ret = -EINVAL;
  48                        goto fail;
  49                }
  50
  51                length = min_t(int, next - partdef,
  52                               sizeof(new_subpart->name) - 1);
  53                strncpy(new_subpart->name, partdef, length);
  54                new_subpart->name[length] = '\0';
  55
  56                partdef = ++next;
  57        } else
  58                new_subpart->name[0] = '\0';
  59
  60        new_subpart->flags = 0;
  61
  62        if (!strncmp(partdef, "ro", 2)) {
  63                new_subpart->flags |= PF_RDONLY;
  64                partdef += 2;
  65        }
  66
  67        if (!strncmp(partdef, "lk", 2)) {
  68                new_subpart->flags |= PF_POWERUP_LOCK;
  69                partdef += 2;
  70        }
  71
  72        *subpart = new_subpart;
  73        return 0;
  74fail:
  75        kfree(new_subpart);
  76        return ret;
  77}
  78
  79static void free_subpart(struct cmdline_parts *parts)
  80{
  81        struct cmdline_subpart *subpart;
  82
  83        while (parts->subpart) {
  84                subpart = parts->subpart;
  85                parts->subpart = subpart->next_subpart;
  86                kfree(subpart);
  87        }
  88}
  89
  90static int parse_parts(struct cmdline_parts **parts, const char *bdevdef)
  91{
  92        int ret = -EINVAL;
  93        char *next;
  94        int length;
  95        struct cmdline_subpart **next_subpart;
  96        struct cmdline_parts *newparts;
  97        char buf[BDEVNAME_SIZE + 32 + 4];
  98
  99        *parts = NULL;
 100
 101        newparts = kzalloc(sizeof(struct cmdline_parts), GFP_KERNEL);
 102        if (!newparts)
 103                return -ENOMEM;
 104
 105        next = strchr(bdevdef, ':');
 106        if (!next) {
 107                pr_warn("cmdline partition has no block device.");
 108                goto fail;
 109        }
 110
 111        length = min_t(int, next - bdevdef, sizeof(newparts->name) - 1);
 112        strncpy(newparts->name, bdevdef, length);
 113        newparts->name[length] = '\0';
 114        newparts->nr_subparts = 0;
 115
 116        next_subpart = &newparts->subpart;
 117
 118        while (next && *(++next)) {
 119                bdevdef = next;
 120                next = strchr(bdevdef, ',');
 121
 122                length = (!next) ? (sizeof(buf) - 1) :
 123                        min_t(int, next - bdevdef, sizeof(buf) - 1);
 124
 125                strncpy(buf, bdevdef, length);
 126                buf[length] = '\0';
 127
 128                ret = parse_subpart(next_subpart, buf);
 129                if (ret)
 130                        goto fail;
 131
 132                newparts->nr_subparts++;
 133                next_subpart = &(*next_subpart)->next_subpart;
 134        }
 135
 136        if (!newparts->subpart) {
 137                pr_warn("cmdline partition has no valid partition.");
 138                ret = -EINVAL;
 139                goto fail;
 140        }
 141
 142        *parts = newparts;
 143
 144        return 0;
 145fail:
 146        free_subpart(newparts);
 147        kfree(newparts);
 148        return ret;
 149}
 150
 151void cmdline_parts_free(struct cmdline_parts **parts)
 152{
 153        struct cmdline_parts *next_parts;
 154
 155        while (*parts) {
 156                next_parts = (*parts)->next_parts;
 157                free_subpart(*parts);
 158                kfree(*parts);
 159                *parts = next_parts;
 160        }
 161}
 162EXPORT_SYMBOL(cmdline_parts_free);
 163
 164int cmdline_parts_parse(struct cmdline_parts **parts, const char *cmdline)
 165{
 166        int ret;
 167        char *buf;
 168        char *pbuf;
 169        char *next;
 170        struct cmdline_parts **next_parts;
 171
 172        *parts = NULL;
 173
 174        next = pbuf = buf = kstrdup(cmdline, GFP_KERNEL);
 175        if (!buf)
 176                return -ENOMEM;
 177
 178        next_parts = parts;
 179
 180        while (next && *pbuf) {
 181                next = strchr(pbuf, ';');
 182                if (next)
 183                        *next = '\0';
 184
 185                ret = parse_parts(next_parts, pbuf);
 186                if (ret)
 187                        goto fail;
 188
 189                if (next)
 190                        pbuf = ++next;
 191
 192                next_parts = &(*next_parts)->next_parts;
 193        }
 194
 195        if (!*parts) {
 196                pr_warn("cmdline partition has no valid partition.");
 197                ret = -EINVAL;
 198                goto fail;
 199        }
 200
 201        ret = 0;
 202done:
 203        kfree(buf);
 204        return ret;
 205
 206fail:
 207        cmdline_parts_free(parts);
 208        goto done;
 209}
 210EXPORT_SYMBOL(cmdline_parts_parse);
 211
 212struct cmdline_parts *cmdline_parts_find(struct cmdline_parts *parts,
 213                                         const char *bdev)
 214{
 215        while (parts && strncmp(bdev, parts->name, sizeof(parts->name)))
 216                parts = parts->next_parts;
 217        return parts;
 218}
 219EXPORT_SYMBOL(cmdline_parts_find);
 220
 221/*
 222 *  add_part()
 223 *    0 success.
 224 *    1 can not add so many partitions.
 225 */
 226int cmdline_parts_set(struct cmdline_parts *parts, sector_t disk_size,
 227                      int slot,
 228                      int (*add_part)(int, struct cmdline_subpart *, void *),
 229                      void *param)
 230{
 231        sector_t from = 0;
 232        struct cmdline_subpart *subpart;
 233
 234        for (subpart = parts->subpart; subpart;
 235             subpart = subpart->next_subpart, slot++) {
 236                if (subpart->from == (sector_t)(~0ULL))
 237                        subpart->from = from;
 238                else
 239                        from = subpart->from;
 240
 241                if (from >= disk_size)
 242                        break;
 243
 244                if (subpart->size > (disk_size - from))
 245                        subpart->size = disk_size - from;
 246
 247                from += subpart->size;
 248
 249                if (add_part(slot, subpart, param))
 250                        break;
 251        }
 252
 253        return slot;
 254}
 255EXPORT_SYMBOL(cmdline_parts_set);
 256