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(size: 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(ptr: partdef, retptr: &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(ptr: partdef, retptr: &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;
104fail:
105 kfree(objp: new_subpart);
106 return ret;
107}
108
109static 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(objp: subpart);
117 }
118}
119
120static 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(size: 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(subpart: next_subpart, partdef: 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;
173fail:
174 free_subpart(parts: newparts);
175 kfree(objp: newparts);
176 return ret;
177}
178
179static 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: *parts);
186 kfree(objp: *parts);
187 *parts = next_parts;
188 }
189}
190
191static 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(s: 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(parts: next_parts, bdevdef: 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;
230done:
231 kfree(objp: buf);
232 return ret;
233
234fail:
235 cmdline_parts_free(parts);
236 goto done;
237}
238
239static 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
247static char *cmdline;
248static struct cmdline_parts *bdev_parts;
249
250static 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(p: state, n: slot, from: subpart->from >> 9,
261 size: 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(buf: tmp, size: sizeof(tmp), fmt: "(%s)", info->volname);
270 strlcat(p: state->pp_buf, q: tmp, PAGE_SIZE);
271
272 state->parts[slot].has_info = true;
273
274 return 0;
275}
276
277static 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
306static int __init cmdline_parts_setup(char *s)
307{
308 cmdline = s;
309 return 1;
310}
311__setup("blkdevparts=", cmdline_parts_setup);
312
313static 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
334static 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
340static 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(from: state->parts[slot].from,
349 size: state->parts[slot].size,
350 from2: state->parts[i].from,
351 size2: 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 */
376int 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(parts: &bdev_parts);
384
385 if (cmdline_parts_parse(parts: &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(parts: bdev_parts, bdev: state->disk->disk_name);
396 if (!parts)
397 return 0;
398
399 disk_size = get_capacity(disk: state->disk) << 9;
400
401 cmdline_parts_set(parts, disk_size, state);
402 cmdline_parts_verifier(slot: 1, state);
403
404 strlcat(p: state->pp_buf, q: "\n", PAGE_SIZE);
405
406 return 1;
407}
408

source code of linux/block/partitions/cmdline.c