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