383 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2013 HUAWEI4 * Author: Cai Zhiyong <caizhiyong@huawei.com>5 *6 * Read block device partition table from the command line.7 * Typically used for fixed block (eMMC) embedded devices.8 * It has no MBR, so saves storage space. Bootloader can be easily accessed9 * by absolute address of data on the block device.10 * Users can easily change the partition.11 *12 * The format for the command line is just like mtdparts.13 *14 * For further information, see "Documentation/block/cmdline-partition.rst"15 *16 */17#include <linux/blkdev.h>18#include <linux/fs.h>19#include <linux/slab.h>20#include "check.h"21 22 23/* partition flags */24#define PF_RDONLY 0x01 /* Device is read only */25#define PF_POWERUP_LOCK 0x02 /* Always locked after reset */26 27struct cmdline_subpart {28 char name[BDEVNAME_SIZE]; /* partition name, such as 'rootfs' */29 sector_t from;30 sector_t size;31 int flags;32 struct cmdline_subpart *next_subpart;33};34 35struct cmdline_parts {36 char name[BDEVNAME_SIZE]; /* block device, such as 'mmcblk0' */37 unsigned int nr_subparts;38 struct cmdline_subpart *subpart;39 struct cmdline_parts *next_parts;40};41 42static int parse_subpart(struct cmdline_subpart **subpart, char *partdef)43{44 int ret = 0;45 struct cmdline_subpart *new_subpart;46 47 *subpart = NULL;48 49 new_subpart = kzalloc(sizeof(struct cmdline_subpart), GFP_KERNEL);50 if (!new_subpart)51 return -ENOMEM;52 53 if (*partdef == '-') {54 new_subpart->size = (sector_t)(~0ULL);55 partdef++;56 } else {57 new_subpart->size = (sector_t)memparse(partdef, &partdef);58 if (new_subpart->size < (sector_t)PAGE_SIZE) {59 pr_warn("cmdline partition size is invalid.");60 ret = -EINVAL;61 goto fail;62 }63 }64 65 if (*partdef == '@') {66 partdef++;67 new_subpart->from = (sector_t)memparse(partdef, &partdef);68 } else {69 new_subpart->from = (sector_t)(~0ULL);70 }71 72 if (*partdef == '(') {73 partdef++;74 char *next = strsep(&partdef, ")");75 76 if (!next) {77 pr_warn("cmdline partition format is invalid.");78 ret = -EINVAL;79 goto fail;80 }81 82 strscpy(new_subpart->name, next, sizeof(new_subpart->name));83 } else84 new_subpart->name[0] = '\0';85 86 new_subpart->flags = 0;87 88 if (!strncmp(partdef, "ro", 2)) {89 new_subpart->flags |= PF_RDONLY;90 partdef += 2;91 }92 93 if (!strncmp(partdef, "lk", 2)) {94 new_subpart->flags |= PF_POWERUP_LOCK;95 partdef += 2;96 }97 98 *subpart = new_subpart;99 return 0;100fail:101 kfree(new_subpart);102 return ret;103}104 105static void free_subpart(struct cmdline_parts *parts)106{107 struct cmdline_subpart *subpart;108 109 while (parts->subpart) {110 subpart = parts->subpart;111 parts->subpart = subpart->next_subpart;112 kfree(subpart);113 }114}115 116static int parse_parts(struct cmdline_parts **parts, char *bdevdef)117{118 int ret = -EINVAL;119 char *next;120 struct cmdline_subpart **next_subpart;121 struct cmdline_parts *newparts;122 123 *parts = NULL;124 125 newparts = kzalloc(sizeof(struct cmdline_parts), GFP_KERNEL);126 if (!newparts)127 return -ENOMEM;128 129 next = strsep(&bdevdef, ":");130 if (!next) {131 pr_warn("cmdline partition has no block device.");132 goto fail;133 }134 135 strscpy(newparts->name, next, sizeof(newparts->name));136 newparts->nr_subparts = 0;137 138 next_subpart = &newparts->subpart;139 140 while ((next = strsep(&bdevdef, ","))) {141 ret = parse_subpart(next_subpart, next);142 if (ret)143 goto fail;144 145 newparts->nr_subparts++;146 next_subpart = &(*next_subpart)->next_subpart;147 }148 149 if (!newparts->subpart) {150 pr_warn("cmdline partition has no valid partition.");151 ret = -EINVAL;152 goto fail;153 }154 155 *parts = newparts;156 157 return 0;158fail:159 free_subpart(newparts);160 kfree(newparts);161 return ret;162}163 164static void cmdline_parts_free(struct cmdline_parts **parts)165{166 struct cmdline_parts *next_parts;167 168 while (*parts) {169 next_parts = (*parts)->next_parts;170 free_subpart(*parts);171 kfree(*parts);172 *parts = next_parts;173 }174}175 176static int cmdline_parts_parse(struct cmdline_parts **parts,177 const char *cmdline)178{179 int ret;180 char *buf;181 char *pbuf;182 char *next;183 struct cmdline_parts **next_parts;184 185 *parts = NULL;186 187 pbuf = buf = kstrdup(cmdline, GFP_KERNEL);188 if (!buf)189 return -ENOMEM;190 191 next_parts = parts;192 193 while ((next = strsep(&pbuf, ";"))) {194 ret = parse_parts(next_parts, next);195 if (ret)196 goto fail;197 198 next_parts = &(*next_parts)->next_parts;199 }200 201 if (!*parts) {202 pr_warn("cmdline partition has no valid partition.");203 ret = -EINVAL;204 goto fail;205 }206 207 ret = 0;208done:209 kfree(buf);210 return ret;211 212fail:213 cmdline_parts_free(parts);214 goto done;215}216 217static struct cmdline_parts *cmdline_parts_find(struct cmdline_parts *parts,218 const char *bdev)219{220 while (parts && strncmp(bdev, parts->name, sizeof(parts->name)))221 parts = parts->next_parts;222 return parts;223}224 225static char *cmdline;226static struct cmdline_parts *bdev_parts;227 228static int add_part(int slot, struct cmdline_subpart *subpart,229 struct parsed_partitions *state)230{231 struct partition_meta_info *info;232 char tmp[sizeof(info->volname) + 4];233 234 if (slot >= state->limit)235 return 1;236 237 put_partition(state, slot, subpart->from >> 9,238 subpart->size >> 9);239 240 info = &state->parts[slot].info;241 242 strscpy(info->volname, subpart->name, sizeof(info->volname));243 244 snprintf(tmp, sizeof(tmp), "(%s)", info->volname);245 strlcat(state->pp_buf, tmp, PAGE_SIZE);246 247 state->parts[slot].has_info = true;248 249 return 0;250}251 252static int cmdline_parts_set(struct cmdline_parts *parts, sector_t disk_size,253 struct parsed_partitions *state)254{255 sector_t from = 0;256 struct cmdline_subpart *subpart;257 int slot = 1;258 259 for (subpart = parts->subpart; subpart;260 subpart = subpart->next_subpart, slot++) {261 if (subpart->from == (sector_t)(~0ULL))262 subpart->from = from;263 else264 from = subpart->from;265 266 if (from >= disk_size)267 break;268 269 if (subpart->size > (disk_size - from))270 subpart->size = disk_size - from;271 272 from += subpart->size;273 274 if (add_part(slot, subpart, state))275 break;276 }277 278 return slot;279}280 281static int __init cmdline_parts_setup(char *s)282{283 cmdline = s;284 return 1;285}286__setup("blkdevparts=", cmdline_parts_setup);287 288static bool has_overlaps(sector_t from, sector_t size,289 sector_t from2, sector_t size2)290{291 sector_t end = from + size;292 sector_t end2 = from2 + size2;293 294 if (from >= from2 && from < end2)295 return true;296 297 if (end > from2 && end <= end2)298 return true;299 300 if (from2 >= from && from2 < end)301 return true;302 303 if (end2 > from && end2 <= end)304 return true;305 306 return false;307}308 309static inline void overlaps_warns_header(void)310{311 pr_warn("Overlapping partitions are used in command line partitions.");312 pr_warn("Don't use filesystems on overlapping partitions:");313}314 315static void cmdline_parts_verifier(int slot, struct parsed_partitions *state)316{317 int i;318 bool header = true;319 320 for (; slot < state->limit && state->parts[slot].has_info; slot++) {321 for (i = slot+1; i < state->limit && state->parts[i].has_info;322 i++) {323 if (has_overlaps(state->parts[slot].from,324 state->parts[slot].size,325 state->parts[i].from,326 state->parts[i].size)) {327 if (header) {328 header = false;329 overlaps_warns_header();330 }331 pr_warn("%s[%llu,%llu] overlaps with "332 "%s[%llu,%llu].",333 state->parts[slot].info.volname,334 (u64)state->parts[slot].from << 9,335 (u64)state->parts[slot].size << 9,336 state->parts[i].info.volname,337 (u64)state->parts[i].from << 9,338 (u64)state->parts[i].size << 9);339 }340 }341 }342}343 344/*345 * Purpose: allocate cmdline partitions.346 * Returns:347 * -1 if unable to read the partition table348 * 0 if this isn't our partition table349 * 1 if successful350 */351int cmdline_partition(struct parsed_partitions *state)352{353 sector_t disk_size;354 struct cmdline_parts *parts;355 356 if (cmdline) {357 if (bdev_parts)358 cmdline_parts_free(&bdev_parts);359 360 if (cmdline_parts_parse(&bdev_parts, cmdline)) {361 cmdline = NULL;362 return -1;363 }364 cmdline = NULL;365 }366 367 if (!bdev_parts)368 return 0;369 370 parts = cmdline_parts_find(bdev_parts, state->disk->disk_name);371 if (!parts)372 return 0;373 374 disk_size = get_capacity(state->disk) << 9;375 376 cmdline_parts_set(parts, disk_size, state);377 cmdline_parts_verifier(1, state);378 379 strlcat(state->pp_buf, "\n", PAGE_SIZE);380 381 return 1;382}383