cmdline.c (8713B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2013 HUAWEI 4 * 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 accessed 9 * 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 int length; 74 char *next = strchr(++partdef, ')'); 75 76 if (!next) { 77 pr_warn("cmdline partition format is invalid."); 78 ret = -EINVAL; 79 goto fail; 80 } 81 82 length = min_t(int, next - partdef, 83 sizeof(new_subpart->name) - 1); 84 strncpy(new_subpart->name, partdef, length); 85 new_subpart->name[length] = '\0'; 86 87 partdef = ++next; 88 } else 89 new_subpart->name[0] = '\0'; 90 91 new_subpart->flags = 0; 92 93 if (!strncmp(partdef, "ro", 2)) { 94 new_subpart->flags |= PF_RDONLY; 95 partdef += 2; 96 } 97 98 if (!strncmp(partdef, "lk", 2)) { 99 new_subpart->flags |= PF_POWERUP_LOCK; 100 partdef += 2; 101 } 102 103 *subpart = new_subpart; 104 return 0; 105fail: 106 kfree(new_subpart); 107 return ret; 108} 109 110static void free_subpart(struct cmdline_parts *parts) 111{ 112 struct cmdline_subpart *subpart; 113 114 while (parts->subpart) { 115 subpart = parts->subpart; 116 parts->subpart = subpart->next_subpart; 117 kfree(subpart); 118 } 119} 120 121static int parse_parts(struct cmdline_parts **parts, const char *bdevdef) 122{ 123 int ret = -EINVAL; 124 char *next; 125 int length; 126 struct cmdline_subpart **next_subpart; 127 struct cmdline_parts *newparts; 128 char buf[BDEVNAME_SIZE + 32 + 4]; 129 130 *parts = NULL; 131 132 newparts = kzalloc(sizeof(struct cmdline_parts), GFP_KERNEL); 133 if (!newparts) 134 return -ENOMEM; 135 136 next = strchr(bdevdef, ':'); 137 if (!next) { 138 pr_warn("cmdline partition has no block device."); 139 goto fail; 140 } 141 142 length = min_t(int, next - bdevdef, sizeof(newparts->name) - 1); 143 strncpy(newparts->name, bdevdef, length); 144 newparts->name[length] = '\0'; 145 newparts->nr_subparts = 0; 146 147 next_subpart = &newparts->subpart; 148 149 while (next && *(++next)) { 150 bdevdef = next; 151 next = strchr(bdevdef, ','); 152 153 length = (!next) ? (sizeof(buf) - 1) : 154 min_t(int, next - bdevdef, sizeof(buf) - 1); 155 156 strncpy(buf, bdevdef, length); 157 buf[length] = '\0'; 158 159 ret = parse_subpart(next_subpart, buf); 160 if (ret) 161 goto fail; 162 163 newparts->nr_subparts++; 164 next_subpart = &(*next_subpart)->next_subpart; 165 } 166 167 if (!newparts->subpart) { 168 pr_warn("cmdline partition has no valid partition."); 169 ret = -EINVAL; 170 goto fail; 171 } 172 173 *parts = newparts; 174 175 return 0; 176fail: 177 free_subpart(newparts); 178 kfree(newparts); 179 return ret; 180} 181 182static void cmdline_parts_free(struct cmdline_parts **parts) 183{ 184 struct cmdline_parts *next_parts; 185 186 while (*parts) { 187 next_parts = (*parts)->next_parts; 188 free_subpart(*parts); 189 kfree(*parts); 190 *parts = next_parts; 191 } 192} 193 194static int cmdline_parts_parse(struct cmdline_parts **parts, 195 const char *cmdline) 196{ 197 int ret; 198 char *buf; 199 char *pbuf; 200 char *next; 201 struct cmdline_parts **next_parts; 202 203 *parts = NULL; 204 205 next = pbuf = buf = kstrdup(cmdline, GFP_KERNEL); 206 if (!buf) 207 return -ENOMEM; 208 209 next_parts = parts; 210 211 while (next && *pbuf) { 212 next = strchr(pbuf, ';'); 213 if (next) 214 *next = '\0'; 215 216 ret = parse_parts(next_parts, pbuf); 217 if (ret) 218 goto fail; 219 220 if (next) 221 pbuf = ++next; 222 223 next_parts = &(*next_parts)->next_parts; 224 } 225 226 if (!*parts) { 227 pr_warn("cmdline partition has no valid partition."); 228 ret = -EINVAL; 229 goto fail; 230 } 231 232 ret = 0; 233done: 234 kfree(buf); 235 return ret; 236 237fail: 238 cmdline_parts_free(parts); 239 goto done; 240} 241 242static struct cmdline_parts *cmdline_parts_find(struct cmdline_parts *parts, 243 const char *bdev) 244{ 245 while (parts && strncmp(bdev, parts->name, sizeof(parts->name))) 246 parts = parts->next_parts; 247 return parts; 248} 249 250static char *cmdline; 251static struct cmdline_parts *bdev_parts; 252 253static int add_part(int slot, struct cmdline_subpart *subpart, 254 struct parsed_partitions *state) 255{ 256 int label_min; 257 struct partition_meta_info *info; 258 char tmp[sizeof(info->volname) + 4]; 259 260 if (slot >= state->limit) 261 return 1; 262 263 put_partition(state, slot, subpart->from >> 9, 264 subpart->size >> 9); 265 266 info = &state->parts[slot].info; 267 268 label_min = min_t(int, sizeof(info->volname) - 1, 269 sizeof(subpart->name)); 270 strncpy(info->volname, subpart->name, label_min); 271 info->volname[label_min] = '\0'; 272 273 snprintf(tmp, sizeof(tmp), "(%s)", info->volname); 274 strlcat(state->pp_buf, tmp, PAGE_SIZE); 275 276 state->parts[slot].has_info = true; 277 278 return 0; 279} 280 281static int cmdline_parts_set(struct cmdline_parts *parts, sector_t disk_size, 282 struct parsed_partitions *state) 283{ 284 sector_t from = 0; 285 struct cmdline_subpart *subpart; 286 int slot = 1; 287 288 for (subpart = parts->subpart; subpart; 289 subpart = subpart->next_subpart, slot++) { 290 if (subpart->from == (sector_t)(~0ULL)) 291 subpart->from = from; 292 else 293 from = subpart->from; 294 295 if (from >= disk_size) 296 break; 297 298 if (subpart->size > (disk_size - from)) 299 subpart->size = disk_size - from; 300 301 from += subpart->size; 302 303 if (add_part(slot, subpart, state)) 304 break; 305 } 306 307 return slot; 308} 309 310static int __init cmdline_parts_setup(char *s) 311{ 312 cmdline = s; 313 return 1; 314} 315__setup("blkdevparts=", cmdline_parts_setup); 316 317static bool has_overlaps(sector_t from, sector_t size, 318 sector_t from2, sector_t size2) 319{ 320 sector_t end = from + size; 321 sector_t end2 = from2 + size2; 322 323 if (from >= from2 && from < end2) 324 return true; 325 326 if (end > from2 && end <= end2) 327 return true; 328 329 if (from2 >= from && from2 < end) 330 return true; 331 332 if (end2 > from && end2 <= end) 333 return true; 334 335 return false; 336} 337 338static inline void overlaps_warns_header(void) 339{ 340 pr_warn("Overlapping partitions are used in command line partitions."); 341 pr_warn("Don't use filesystems on overlapping partitions:"); 342} 343 344static void cmdline_parts_verifier(int slot, struct parsed_partitions *state) 345{ 346 int i; 347 bool header = true; 348 349 for (; slot < state->limit && state->parts[slot].has_info; slot++) { 350 for (i = slot+1; i < state->limit && state->parts[i].has_info; 351 i++) { 352 if (has_overlaps(state->parts[slot].from, 353 state->parts[slot].size, 354 state->parts[i].from, 355 state->parts[i].size)) { 356 if (header) { 357 header = false; 358 overlaps_warns_header(); 359 } 360 pr_warn("%s[%llu,%llu] overlaps with " 361 "%s[%llu,%llu].", 362 state->parts[slot].info.volname, 363 (u64)state->parts[slot].from << 9, 364 (u64)state->parts[slot].size << 9, 365 state->parts[i].info.volname, 366 (u64)state->parts[i].from << 9, 367 (u64)state->parts[i].size << 9); 368 } 369 } 370 } 371} 372 373/* 374 * Purpose: allocate cmdline partitions. 375 * Returns: 376 * -1 if unable to read the partition table 377 * 0 if this isn't our partition table 378 * 1 if successful 379 */ 380int cmdline_partition(struct parsed_partitions *state) 381{ 382 sector_t disk_size; 383 struct cmdline_parts *parts; 384 385 if (cmdline) { 386 if (bdev_parts) 387 cmdline_parts_free(&bdev_parts); 388 389 if (cmdline_parts_parse(&bdev_parts, cmdline)) { 390 cmdline = NULL; 391 return -1; 392 } 393 cmdline = NULL; 394 } 395 396 if (!bdev_parts) 397 return 0; 398 399 parts = cmdline_parts_find(bdev_parts, state->disk->disk_name); 400 if (!parts) 401 return 0; 402 403 disk_size = get_capacity(state->disk) << 9; 404 405 cmdline_parts_set(parts, disk_size, state); 406 cmdline_parts_verifier(1, state); 407 408 strlcat(state->pp_buf, "\n", PAGE_SIZE); 409 410 return 1; 411}