1
2 #include "fdiskP.h"
3 #include "strutils.h"
4 #include "carefulputc.h"
5 #include "mangle.h"
6 #include "jsonwrt.h"
7 #include "fileutils.h"
8
9 #ifdef FUZZ_TARGET
10 #include "fuzz.h"
11 #endif
12
13 /**
14 * SECTION: script
15 * @title: Script
16 * @short_description: complex way to create and dump partition table
17 *
18 * This interface can be used to compose in-memory partition table with all details,
19 * write all partition table description to human readable text file, read it
20 * from the file, and apply the script to on-disk label.
21 *
22 * The libfdisk scripts are based on original sfdisk script (dumps). Each
23 * script has two parts: script headers and partition table entries
24 * (partitions). The script is possible to dump in JSON too (read JSON is not
25 * implemented yet).
26 *
27 * For more details about script format see sfdisk man page.
28 *
29 * There are four ways how to build the script:
30 *
31 * - read the current on-disk partition table by fdisk_script_read_context())
32 * - read it from text file by fdisk_script_read_file()
33 * - read it interactively from user by fdisk_script_read_line() and fdisk_script_set_fgets()
34 * - manually in code by fdisk_script_set_header() and fdisk_script_set_table()
35 *
36 * The read functions fdisk_script_read_context() and fdisk_script_read_file()
37 * creates always a new script partition table. The table (see
38 * fdisk_script_get_table()) is possible to modify by standard
39 * fdisk_table_...() functions and then apply by fdisk_apply_script().
40 *
41 * Note that script API is fully non-interactive and forces libfdisk to not use
42 * standard dialog driven partitioning as we have in fdisk(8).
43 */
44
45 /* script header (e.g. unit: sectors) */
46 struct fdisk_scriptheader {
47 struct list_head headers;
48 char *name;
49 char *data;
50 };
51
52 /* script control struct */
53 struct fdisk_script {
54 struct fdisk_table *table;
55 struct list_head headers;
56 struct fdisk_context *cxt;
57
58 int refcount;
59 char *(*fn_fgets)(struct fdisk_script *, char *, size_t, FILE *);
60 void *userdata;
61
62 /* parser's state */
63 size_t nlines;
64 struct fdisk_label *label;
65
66 unsigned long sector_size; /* as defined by script */
67
68 unsigned int json : 1, /* JSON output */
69 force_label : 1; /* label: <name> specified */
70 };
71
72 static void fdisk_script_free_header(struct fdisk_scriptheader *fi)
73 {
74 if (!fi)
75 return;
76
77 DBG(SCRIPT, ul_debugobj(fi, "free header %s", fi->name));
78 free(fi->name);
79 free(fi->data);
80 list_del(&fi->headers);
81 free(fi);
82 }
83
84 /**
85 * fdisk_new_script:
86 * @cxt: context
87 *
88 * The script hold fdisk_table and additional information to read/write
89 * script to the file.
90 *
91 * Returns: newly allocated script struct.
92 */
93 struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt)
94 {
95 struct fdisk_script *dp = NULL;
96
97 dp = calloc(1, sizeof(*dp));
98 if (!dp)
99 return NULL;
100
101 DBG(SCRIPT, ul_debugobj(dp, "alloc"));
102 dp->refcount = 1;
103 dp->cxt = cxt;
104 fdisk_ref_context(cxt);
105
106 INIT_LIST_HEAD(&dp->headers);
107 return dp;
108 }
109
110 /**
111 * fdisk_new_script_from_file:
112 * @cxt: context
113 * @filename: path to the script file
114 *
115 * Allocates a new script and reads script from @filename.
116 *
117 * Returns: new script instance or NULL in case of error (check errno for more details).
118 */
119 struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
120 const char *filename)
121 {
122 int rc;
123 FILE *f;
124 struct fdisk_script *dp, *res = NULL;
125
126 assert(cxt);
127 assert(filename);
128
129 DBG(SCRIPT, ul_debug("opening %s", filename));
130 f = fopen(filename, "r");
131 if (!f)
132 return NULL;
133
134 dp = fdisk_new_script(cxt);
135 if (!dp)
136 goto done;
137
138 rc = fdisk_script_read_file(dp, f);
139 if (rc) {
140 errno = -rc;
141 goto done;
142 }
143
144 res = dp;
145 done:
146 fclose(f);
147 if (!res)
148 fdisk_unref_script(dp);
149 else
150 errno = 0;
151
152 return res;
153 }
154
155 /**
156 * fdisk_ref_script:
157 * @dp: script pointer
158 *
159 * Increments reference counter.
160 */
161 void fdisk_ref_script(struct fdisk_script *dp)
162 {
163 if (dp)
164 dp->refcount++;
165 }
166
167 static void fdisk_reset_script(struct fdisk_script *dp)
168 {
169 assert(dp);
170
171 DBG(SCRIPT, ul_debugobj(dp, "reset"));
172
173 if (dp->table)
174 fdisk_reset_table(dp->table);
175
176 while (!list_empty(&dp->headers)) {
177 struct fdisk_scriptheader *fi = list_entry(dp->headers.next,
178 struct fdisk_scriptheader, headers);
179 fdisk_script_free_header(fi);
180 }
181 INIT_LIST_HEAD(&dp->headers);
182 }
183
184 /**
185 * fdisk_unref_script:
186 * @dp: script pointer
187 *
188 * Decrements reference counter, on zero the @dp is automatically
189 * deallocated.
190 */
191 void fdisk_unref_script(struct fdisk_script *dp)
192 {
193 if (!dp)
194 return;
195
196 dp->refcount--;
197 if (dp->refcount <= 0) {
198 fdisk_reset_script(dp);
199 fdisk_unref_context(dp->cxt);
200 fdisk_unref_table(dp->table);
201 DBG(SCRIPT, ul_debugobj(dp, "free script"));
202 free(dp);
203 }
204 }
205
206 /**
207 * fdisk_script_set_userdata
208 * @dp: script
209 * @data: your data
210 *
211 * Sets data usable for example in callbacks (e.g fdisk_script_set_fgets()).
212 *
213 * Returns: 0 on success, <0 on error.
214 */
215 int fdisk_script_set_userdata(struct fdisk_script *dp, void *data)
216 {
217 assert(dp);
218 dp->userdata = data;
219 return 0;
220 }
221
222 /**
223 * fdisk_script_get_userdata
224 * @dp: script
225 *
226 * Returns: user data or NULL.
227 */
228 void *fdisk_script_get_userdata(struct fdisk_script *dp)
229 {
230 assert(dp);
231 return dp->userdata;
232 }
233
234 static struct fdisk_scriptheader *script_get_header(struct fdisk_script *dp,
235 const char *name)
236 {
237 struct list_head *p;
238
239 list_for_each(p, &dp->headers) {
240 struct fdisk_scriptheader *fi = list_entry(p, struct fdisk_scriptheader, headers);
241
242 if (strcasecmp(fi->name, name) == 0)
243 return fi;
244 }
245
246 return NULL;
247 }
248
249 /**
250 * fdisk_script_get_header:
251 * @dp: script instance
252 * @name: header name
253 *
254 * Returns: pointer to header data or NULL.
255 */
256 const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name)
257 {
258 struct fdisk_scriptheader *fi;
259
260 assert(dp);
261 assert(name);
262
263 fi = script_get_header(dp, name);
264 return fi ? fi->data : NULL;
265 }
266
267 /**
268 * fdisk_script_set_header:
269 * @dp: script instance
270 * @name: header name
271 * @data: header data (or NULL)
272 *
273 * The headers are used as global options for whole partition
274 * table, always one header per line.
275 *
276 * If no @data is specified then the header is removed. If header does not exist
277 * and @data is specified then a new header is added.
278 *
279 * Note that libfdisk can be used to specify arbitrary custom header, the default
280 * built-in headers are "unit" and "label", and some label specific headers
281 * (for example "uuid" and "name" for GPT).
282 *
283 * Returns: 0 on success, <0 on error
284 */
285 int fdisk_script_set_header(struct fdisk_script *dp,
286 const char *name,
287 const char *data)
288 {
289 struct fdisk_scriptheader *fi;
290
291 if (!dp || !name)
292 return -EINVAL;
293
294 fi = script_get_header(dp, name);
295 if (!fi && !data)
296 return 0; /* want to remove header that does not exist, success */
297
298 if (!data) {
299 DBG(SCRIPT, ul_debugobj(dp, "freeing header %s", name));
300
301 /* no data, remove the header */
302 fdisk_script_free_header(fi);
303 return 0;
304 }
305
306 if (!fi) {
307 int rc;
308
309 DBG(SCRIPT, ul_debugobj(dp, "setting new header %s='%s'", name, data));
310
311 /* new header */
312 fi = calloc(1, sizeof(*fi));
313 if (!fi)
314 return -ENOMEM;
315 INIT_LIST_HEAD(&fi->headers);
316
317 rc = strdup_to_struct_member(fi, name, name);
318 if (!rc)
319 rc = strdup_to_struct_member(fi, data, data);
320 if (rc) {
321 fdisk_script_free_header(fi);
322 return rc;
323 }
324 list_add_tail(&fi->headers, &dp->headers);
325 } else {
326 /* update existing */
327 char *x = strdup(data);
328
329 DBG(SCRIPT, ul_debugobj(dp, "update '%s' header '%s' -> '%s'", name, fi->data, data));
330
331 if (!x)
332 return -ENOMEM;
333 free(fi->data);
334 fi->data = x;
335 }
336
337 if (strcmp(name, "label") == 0)
338 dp->label = NULL;
339
340 return 0;
341 }
342
343 /**
344 * fdisk_script_get_table:
345 * @dp: script
346 *
347 * The table represents partitions holded by the script. The table is possible to
348 * fill by fdisk_script_read_context() or fdisk_script_read_file(). All the "read"
349 * functions remove old partitions from the table. See also fdisk_script_set_table().
350 *
351 * Returns: NULL or script table.
352 */
353 struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp)
354 {
355 assert(dp);
356
357 if (!dp->table)
358 /*
359 * Make sure user has access to the same table as script. If
360 * there is no table then create a new one and reuse it later.
361 */
362 dp->table = fdisk_new_table();
363
364 return dp->table;
365 }
366
367 /**
368 * fdisk_script_set_table:
369 * @dp: script
370 * @tb: table
371 *
372 * Replaces table used by script and creates a new reference to @tb. This
373 * function can be used to generate a new script table independently on the current
374 * context and without any file reading.
375 *
376 * This is useful for example to create partition table with the same basic
377 * settings (e.g. label-id, ...) but with different partitions -- just call
378 * fdisk_script_read_context() to get current settings and then
379 * fdisk_script_set_table() to set a different layout.
380 *
381 * If @tb is NULL then the current script table is unreferenced.
382 *
383 * Note that script read_ functions (e.g. fdisk_script_read_context()) create
384 * always a new script table.
385 *
386 * Returns: 0 on success, <0 on error
387 *
388 * Since: 2.35
389 */
390 int fdisk_script_set_table(struct fdisk_script *dp, struct fdisk_table *tb)
391 {
392 if (!dp)
393 return -EINVAL;
394
395 fdisk_ref_table(tb);
396 fdisk_unref_table(dp->table);
397 dp->table = tb;
398
399 DBG(SCRIPT, ul_debugobj(dp, "table replaced"));
400 return 0;
401 }
402
403 static struct fdisk_label *script_get_label(struct fdisk_script *dp)
404 {
405 assert(dp);
406 assert(dp->cxt);
407
408 if (!dp->label) {
409 dp->label = fdisk_get_label(dp->cxt,
410 fdisk_script_get_header(dp, "label"));
411 DBG(SCRIPT, ul_debugobj(dp, "label '%s'", dp->label ? dp->label->name : ""));
412 }
413 return dp->label;
414 }
415
416 /**
417 * fdisk_script_get_nlines:
418 * @dp: script
419 *
420 * Returns: number of parsed lines or <0 on error.
421 */
422 int fdisk_script_get_nlines(struct fdisk_script *dp)
423 {
424 assert(dp);
425 return dp->nlines;
426 }
427
428 /**
429 * fdisk_script_has_force_label:
430 * @dp: script
431 *
432 * Label has been explicitly specified in the script.
433 *
434 * Since: 2.30
435 *
436 * Returns: true if "label: name" has been parsed.
437 */
438 int fdisk_script_has_force_label(struct fdisk_script *dp)
439 {
440 assert(dp);
441 return dp->force_label;
442 }
443
444
445 /**
446 * fdisk_script_read_context:
447 * @dp: script
448 * @cxt: context
449 *
450 * Reads data from the @cxt context (on disk partition table) into the script.
451 * If the context is not specified then defaults to context used for fdisk_new_script().
452 *
453 * Return: 0 on success, <0 on error.
454 */
455 int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt)
456 {
457 struct fdisk_label *lb;
458 int rc;
459 char *p = NULL;
460 char buf[64];
461
462 if (!dp || (!cxt && !dp->cxt))
463 return -EINVAL;
464
465 if (!cxt)
466 cxt = dp->cxt;
467
468 DBG(SCRIPT, ul_debugobj(dp, "reading context into script"));
469 fdisk_reset_script(dp);
470
471 lb = fdisk_get_label(cxt, NULL);
472 if (!lb)
473 return -EINVAL;
474
475 /* allocate (if not yet) and fill table */
476 rc = fdisk_get_partitions(cxt, &dp->table);
477 if (rc)
478 return rc;
479
480 /* generate headers */
481 rc = fdisk_script_set_header(dp, "label", fdisk_label_get_name(lb));
482
483 if (!rc && fdisk_get_disklabel_id(cxt, &p) == 0 && p) {
484 rc = fdisk_script_set_header(dp, "label-id", p);
485 free(p);
486 }
487 if (!rc && cxt->dev_path)
488 rc = fdisk_script_set_header(dp, "device", cxt->dev_path);
489 if (!rc)
490 rc = fdisk_script_set_header(dp, "unit", "sectors");
491
492 if (!rc && fdisk_is_label(cxt, GPT)) {
493 struct fdisk_labelitem item = FDISK_LABELITEM_INIT;
494
495 /* first-lba */
496 rc = fdisk_get_disklabel_item(cxt, GPT_LABELITEM_FIRSTLBA, &item);
497 if (!rc) {
498 snprintf(buf, sizeof(buf), "%"PRIu64, item.data.num64);
499 rc = fdisk_script_set_header(dp, "first-lba", buf);
500 }
501
502 /* last-lba */
503 if (!rc)
504 rc = fdisk_get_disklabel_item(cxt, GPT_LABELITEM_LASTLBA, &item);
505 if (!rc) {
506 snprintf(buf, sizeof(buf), "%"PRIu64, item.data.num64);
507 rc = fdisk_script_set_header(dp, "last-lba", buf);
508 }
509
510 /* table-length */
511 if (!rc) {
512 size_t n = fdisk_get_npartitions(cxt);
513 if (n != FDISK_GPT_NPARTITIONS_DEFAULT) {
514 snprintf(buf, sizeof(buf), "%zu", n);
515 rc = fdisk_script_set_header(dp, "table-length", buf);
516 }
517 }
518 }
519
520 if (!rc && fdisk_get_grain_size(cxt) != 2048 * 512) {
521 snprintf(buf, sizeof(buf), "%lu", fdisk_get_grain_size(cxt));
522 rc = fdisk_script_set_header(dp, "grain", buf);
523 }
524
525 if (!rc) {
526 snprintf(buf, sizeof(buf), "%lu", fdisk_get_sector_size(cxt));
527 rc = fdisk_script_set_header(dp, "sector-size", buf);
528 }
529
530 DBG(SCRIPT, ul_debugobj(dp, "read context done [rc=%d]", rc));
531 return rc;
532 }
533
534 /**
535 * fdisk_script_enable_json:
536 * @dp: script
537 * @json: 0 or 1
538 *
539 * Disable/Enable JSON output format.
540 *
541 * Returns: 0 on success, <0 on error.
542 */
543 int fdisk_script_enable_json(struct fdisk_script *dp, int json)
544 {
545 assert(dp);
546
547 dp->json = json;
548 return 0;
549 }
550
551 static int write_file_json(struct fdisk_script *dp, FILE *f)
552 {
553 struct list_head *h;
554 struct fdisk_partition *pa;
555 struct fdisk_iter itr;
556 const char *devname = NULL;
557 struct ul_jsonwrt json;
558
559 assert(dp);
560 assert(f);
561
562 DBG(SCRIPT, ul_debugobj(dp, "writing json dump to file"));
563
564 ul_jsonwrt_init(&json, f, 0);
565 ul_jsonwrt_root_open(&json);
566
567 ul_jsonwrt_object_open(&json, "partitiontable");
568
569 /* script headers */
570 list_for_each(h, &dp->headers) {
571 struct fdisk_scriptheader *fi = list_entry(h, struct fdisk_scriptheader, headers);
572 const char *name = fi->name;
573 int num = 0;
574
575 if (strcmp(name, "first-lba") == 0) {
576 name = "firstlba";
577 num = 1;
578 } else if (strcmp(name, "last-lba") == 0) {
579 name = "lastlba";
580 num = 1;
581 } else if (strcmp(name, "sector-size") == 0) {
582 name = "sectorsize";
583 num = 1;
584 } else if (strcmp(name, "label-id") == 0)
585 name = "id";
586
587 if (num)
588 ul_jsonwrt_value_raw(&json, name, fi->data);
589 else
590 ul_jsonwrt_value_s(&json, name, fi->data);
591
592 if (strcmp(name, "device") == 0)
593 devname = fi->data;
594 }
595
596
597 if (!dp->table || fdisk_table_is_empty(dp->table)) {
598 DBG(SCRIPT, ul_debugobj(dp, "script table empty"));
599 goto done;
600 }
601
602 DBG(SCRIPT, ul_debugobj(dp, "%zu entries", fdisk_table_get_nents(dp->table)));
603
604 ul_jsonwrt_array_open(&json, "partitions");
605
606 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
607 while (fdisk_table_next_partition(dp->table, &itr, &pa) == 0) {
608 char *p = NULL;
609
610 ul_jsonwrt_object_open(&json, NULL);
611 if (devname)
612 p = fdisk_partname(devname, pa->partno + 1);
613 if (p) {
614 DBG(SCRIPT, ul_debugobj(dp, "write %s entry", p));
615 ul_jsonwrt_value_s(&json, "node", p);
616 free(p);
617 }
618
619 if (fdisk_partition_has_start(pa))
620 ul_jsonwrt_value_u64(&json, "start", (uintmax_t)pa->start);
621
622 if (fdisk_partition_has_size(pa))
623 ul_jsonwrt_value_u64(&json, "size", (uintmax_t)pa->size);
624
625 if (pa->type && fdisk_parttype_get_string(pa->type))
626 ul_jsonwrt_value_s(&json, "type", fdisk_parttype_get_string(pa->type));
627
628 else if (pa->type) {
629 ul_jsonwrt_value_open(&json, "type");
630 fprintf(f, "\"%x\"", fdisk_parttype_get_code(pa->type));
631 ul_jsonwrt_value_close(&json);
632 }
633
634 if (pa->uuid)
635 ul_jsonwrt_value_s(&json, "uuid", pa->uuid);
636 if (pa->name && *pa->name)
637 ul_jsonwrt_value_s(&json, "name", pa->name);
638
639 /* for MBR attr=80 means bootable */
640 if (pa->attrs) {
641 struct fdisk_label *lb = script_get_label(dp);
642
643 if (!lb || fdisk_label_get_type(lb) != FDISK_DISKLABEL_DOS)
644 ul_jsonwrt_value_s(&json, "attrs", pa->attrs);
645 }
646
647 if (fdisk_partition_is_bootable(pa))
648 ul_jsonwrt_value_boolean(&json, "bootable", 1);
649 ul_jsonwrt_object_close(&json);
650 }
651
652 ul_jsonwrt_array_close(&json);
653 done:
654 ul_jsonwrt_object_close(&json);
655 ul_jsonwrt_root_close(&json);
656
657 DBG(SCRIPT, ul_debugobj(dp, "write script done"));
658 return 0;
659 }
660
661 static int write_file_sfdisk(struct fdisk_script *dp, FILE *f)
662 {
663 struct list_head *h;
664 struct fdisk_partition *pa;
665 struct fdisk_iter itr;
666 const char *devname = NULL;
667
668 assert(dp);
669 assert(f);
670
671 DBG(SCRIPT, ul_debugobj(dp, "writing sfdisk-like script to file"));
672
673 /* script headers */
674 list_for_each(h, &dp->headers) {
675 struct fdisk_scriptheader *fi = list_entry(h, struct fdisk_scriptheader, headers);
676 fprintf(f, "%s: %s\n", fi->name, fi->data);
677 if (strcmp(fi->name, "device") == 0)
678 devname = fi->data;
679 }
680
681 if (!dp->table || fdisk_table_is_empty(dp->table)) {
682 DBG(SCRIPT, ul_debugobj(dp, "script table empty"));
683 return 0;
684 }
685
686 DBG(SCRIPT, ul_debugobj(dp, "%zu entries", fdisk_table_get_nents(dp->table)));
687
688 fputc('\n', f);
689
690 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
691 while (fdisk_table_next_partition(dp->table, &itr, &pa) == 0) {
692 char *p = NULL;
693
694 if (devname)
695 p = fdisk_partname(devname, pa->partno + 1);
696 if (p) {
697 DBG(SCRIPT, ul_debugobj(dp, "write %s entry", p));
698 fprintf(f, "%s :", p);
699 free(p);
700 } else
701 fprintf(f, "%zu :", pa->partno + 1);
702
703 if (fdisk_partition_has_start(pa))
704 fprintf(f, " start=%12ju", (uintmax_t)pa->start);
705 if (fdisk_partition_has_size(pa))
706 fprintf(f, ", size=%12ju", (uintmax_t)pa->size);
707
708 if (pa->type && fdisk_parttype_get_string(pa->type))
709 fprintf(f, ", type=%s", fdisk_parttype_get_string(pa->type));
710 else if (pa->type)
711 fprintf(f, ", type=%x", fdisk_parttype_get_code(pa->type));
712
713 if (pa->uuid)
714 fprintf(f, ", uuid=%s", pa->uuid);
715 if (pa->name && *pa->name) {
716 fputs(", name=", f);
717 fputs_quoted(pa->name, f);
718 }
719
720 /* for MBR attr=80 means bootable */
721 if (pa->attrs) {
722 struct fdisk_label *lb = script_get_label(dp);
723
724 if (!lb || fdisk_label_get_type(lb) != FDISK_DISKLABEL_DOS)
725 fprintf(f, ", attrs=\"%s\"", pa->attrs);
726 }
727 if (fdisk_partition_is_bootable(pa))
728 fprintf(f, ", bootable");
729 fputc('\n', f);
730 }
731
732 DBG(SCRIPT, ul_debugobj(dp, "write script done"));
733 return 0;
734 }
735
736 /**
737 * fdisk_script_write_file:
738 * @dp: script
739 * @f: output file
740 *
741 * Writes script @dp to the file @f.
742 *
743 * Returns: 0 on success, <0 on error.
744 */
745 int fdisk_script_write_file(struct fdisk_script *dp, FILE *f)
746 {
747 assert(dp);
748
749 if (dp->json)
750 return write_file_json(dp, f);
751
752 return write_file_sfdisk(dp, f);
753 }
754
755 static inline int is_header_line(const char *s)
756 {
757 const char *p = strchr(s, ':');
758
759 if (!p || p == s || !*(p + 1) || strchr(s, '='))
760 return 0;
761
762 return 1;
763 }
764
765 /* parses "<name>: value", note modifies @s*/
766 static int parse_line_header(struct fdisk_script *dp, char *s)
767 {
768 size_t i;
769 char *name, *value;
770 static const char *supported[] = {
771 "label", "unit", "label-id", "device", "grain",
772 "first-lba", "last-lba", "table-length", "sector-size"
773 };
774
775 DBG(SCRIPT, ul_debugobj(dp, " parse header '%s'", s));
776
777 if (!s || !*s)
778 return -EINVAL;
779
780 name = s;
781 value = strchr(s, ':');
782 if (!value)
783 return -EINVAL;
784 *value = '\0';
785 value++;
786
787 ltrim_whitespace((unsigned char *) name);
788 rtrim_whitespace((unsigned char *) name);
789 ltrim_whitespace((unsigned char *) value);
790 rtrim_whitespace((unsigned char *) value);
791
792 if (!*name || !*value)
793 return -EINVAL;
794
795 /* check header name */
796 for (i = 0; i < ARRAY_SIZE(supported); i++) {
797 if (strcmp(name, supported[i]) == 0)
798 break;
799 }
800 if (i == ARRAY_SIZE(supported))
801 return -ENOTSUP;
802
803 /* header specific actions */
804 if (strcmp(name, "label") == 0) {
805 if (dp->cxt && !fdisk_get_label(dp->cxt, value))
806 return -EINVAL; /* unknown label name */
807 dp->force_label = 1;
808
809 } else if (strcmp(name, "sector-size") == 0) {
810 uint64_t x = 0;
811
812 if (ul_strtou64(value, &x, 10) != 0)
813 return -EINVAL;
814 if (x > ULONG_MAX || x % 512)
815 return -ERANGE;
816 dp->sector_size = (unsigned long) x;
817
818 if (dp->cxt && dp->sector_size && dp->cxt->sector_size
819 && dp->sector_size != dp->cxt->sector_size)
820 fdisk_warnx(dp->cxt, _("The script and device sector size differ; the sizes will be recalculated to match the device."));
821
822 } else if (strcmp(name, "unit") == 0) {
823 if (strcmp(value, "sectors") != 0)
824 return -EINVAL; /* only "sectors" supported */
825
826 }
827
828 return fdisk_script_set_header(dp, name, value);
829 }
830
831 static int partno_from_devname(char *s)
832 {
833 intmax_t num;
834 size_t sz;
835 char *end, *p;
836
837 if (!s || !*s)
838 return -1;
839
840 sz = rtrim_whitespace((unsigned char *)s);
841 end = p = s + sz;
842
843 while (p > s && isdigit(*(p - 1)))
844 p--;
845 if (p == end)
846 return -1;
847 end = NULL;
848 errno = 0;
849 num = strtol(p, &end, 10);
850 if (errno || !end || p == end)
851 return -1;
852
853 if (num < INT32_MIN || num > INT32_MAX) {
854 errno = ERANGE;
855 return -1;
856 }
857 return num - 1;
858 }
859
860
861 /* returns zero terminated string with next token and @str is updated */
862 static char *next_token(char **str)
863 {
864 char *tk_begin = NULL,
865 *tk_end = NULL,
866 *end = NULL,
867 *p;
868 int open_quote = 0, terminated = 0;
869
870 for (p = *str; p && *p; p++) {
871 if (!tk_begin) {
872 if (isblank(*p))
873 continue;
874 tk_begin = *p == '"' ? p + 1 : p;
875 }
876 if (*p == '"')
877 open_quote ^= 1;
878 if (open_quote)
879 continue;
880 if (isblank(*p) || *p == ',' || *p == ';' || *p == '"' )
881 tk_end = p;
882 else if (*(p + 1) == '\0')
883 tk_end = p + 1;
884 if (tk_begin && tk_end)
885 break;
886 }
887
888 if (!tk_end)
889 return NULL;
890
891 end = tk_end;
892
893 /* skip closing quotes */
894 if (*end == '"')
895 end++;
896
897 /* token is terminated by blank (or blank is before "," or ";") */
898 if (isblank(*end)) {
899 end = (char *) skip_blank(end);
900 terminated++;
901 }
902
903 /* token is terminated by "," or ";" */
904 if (*end == ',' || *end == ';') {
905 end++;
906 terminated++;
907
908 /* token is terminated by \0 */
909 } else if (!*end)
910 terminated++;
911
912 if (!terminated) {
913 DBG(SCRIPT, ul_debug("unterminated token '%s'", end));
914 return NULL;
915 }
916
917 /* skip extra space after terminator */
918 end = (char *) skip_blank(end);
919
920 *tk_end = '\0';
921 *str = end;
922 return tk_begin;
923 }
924
925 /*
926 * "[-]<,;>"
927 * "[ ]<,;>"
928 * "- <value>"
929 */
930 static int is_default_value(char **str)
931 {
932 char *p = (char *) skip_blank(*str);
933 int blank = 0;
934
935 if (*p == '-') {
936 char *x = ++p;
937 p = (char *) skip_blank(x);
938 blank = x < p; /* "- " */
939 }
940
941 if (*p == ';' || *p == ',') {
942 *str = ++p;
943 return 1;
944 }
945 if (*p == '\0' || blank) {
946 *str = p;
947 return 1;
948 }
949
950 return 0;
951 }
952
953 static int next_string(char **s, char **str)
954 {
955 char *tk, *p = NULL;
956 int rc = -EINVAL;
957
958 assert(s);
959 assert(str);
960
961 tk = next_token(s);
962 if (tk) {
963 p = strdup(tk);
964 rc = p ? 0 : -ENOMEM;
965 }
966
967 *str = p;
968 return rc;
969 }
970
971 static int skip_optional_sign(char **str)
972 {
973 char *p = (char *) skip_blank(*str);
974
975 if (*p == '-' || *p == '+') {
976 *str = p+1;
977 return *p;
978 }
979 return 0;
980 }
981
982 static int recount_script2device_sectors(struct fdisk_script *dp, uint64_t *num)
983 {
984 if (!dp->cxt ||
985 !dp->sector_size ||
986 !dp->cxt->sector_size)
987 return 0;
988
989 if (dp->sector_size > dp->cxt->sector_size)
990 *num *= (dp->sector_size / dp->cxt->sector_size);
991
992 else if (dp->sector_size < dp->cxt->sector_size) {
993 uint64_t x = dp->cxt->sector_size / dp->sector_size;
994
995 if (*num % x)
996 return -EINVAL;
997 *num /= x;
998 }
999
1000 return 0;
1001 }
1002
1003 static int parse_start_value(struct fdisk_script *dp, struct fdisk_partition *pa, char **str)
1004 {
1005 char *tk;
1006 int rc = 0;
1007
1008 assert(str);
1009
1010 if (is_default_value(str)) {
1011 fdisk_partition_start_follow_default(pa, 1);
1012 return 0;
1013 }
1014
1015 tk = next_token(str);
1016 if (!tk)
1017 return -EINVAL;
1018
1019 if (strcmp(tk, "+") == 0) {
1020 fdisk_partition_start_follow_default(pa, 1);
1021 pa->movestart = FDISK_MOVE_DOWN;
1022 } else {
1023 int pow = 0, sign = skip_optional_sign(&tk);
1024 uint64_t num;
1025
1026 rc = parse_size(tk, (uintmax_t *) &num, &pow);
1027 if (!rc) {
1028 if (pow) { /* specified as <num><suffix> */
1029 if (!dp->cxt->sector_size) {
1030 rc = -EINVAL;
1031 goto done;
1032 }
1033 num /= dp->cxt->sector_size;
1034 } else {
1035 rc = recount_script2device_sectors(dp, &num);
1036 if (rc) {
1037 fdisk_warnx(dp->cxt, _("Can't recalculate partition start to the device sectors"));
1038 goto done;
1039 }
1040 }
1041
1042 fdisk_partition_set_start(pa, num);
1043
1044 pa->movestart = sign == '-' ? FDISK_MOVE_DOWN :
1045 sign == '+' ? FDISK_MOVE_UP :
1046 FDISK_MOVE_NONE;
1047 }
1048 fdisk_partition_start_follow_default(pa, 0);
1049 }
1050
1051 done:
1052 DBG(SCRIPT, ul_debugobj(dp, " start parse result: rc=%d, move=%s, start=%ju, default=%s",
1053 rc, pa->movestart == FDISK_MOVE_DOWN ? "down" :
1054 pa->movestart == FDISK_MOVE_UP ? "up" : "none",
1055 pa->start,
1056 pa->start_follow_default ? "on" : "off"));
1057 return rc;
1058 }
1059
1060 static int parse_size_value(struct fdisk_script *dp, struct fdisk_partition *pa, char **str)
1061 {
1062 char *tk;
1063 int rc = 0;
1064
1065 if (is_default_value(str)) {
1066 fdisk_partition_end_follow_default(pa, 1);
1067 return 0;
1068 }
1069
1070 tk = next_token(str);
1071 if (!tk)
1072 return -EINVAL;
1073
1074 if (strcmp(tk, "+") == 0) {
1075 fdisk_partition_end_follow_default(pa, 1);
1076 pa->resize = FDISK_RESIZE_ENLARGE;
1077 } else {
1078 /* '[+-]<number>[<suffix] */
1079 int pow = 0, sign = skip_optional_sign(&tk);
1080 uint64_t num;
1081
1082 rc = parse_size(tk, (uintmax_t *) &num, &pow);
1083 if (!rc) {
1084 if (pow) { /* specified as <size><suffix> */
1085 if (!dp->cxt->sector_size) {
1086 rc = -EINVAL;
1087 goto done;
1088 }
1089 num /= dp->cxt->sector_size;
1090 } else {
1091 /* specified as number of sectors */
1092 fdisk_partition_size_explicit(pa, 1);
1093 rc = recount_script2device_sectors(dp, &num);
1094 if (rc) {
1095 fdisk_warnx(dp->cxt, _("Can't recalculate partition size to the device sectors"));
1096 goto done;
1097 }
1098 }
1099
1100 fdisk_partition_set_size(pa, num);
1101 pa->resize = sign == '-' ? FDISK_RESIZE_REDUCE :
1102 sign == '+' ? FDISK_RESIZE_ENLARGE :
1103 FDISK_RESIZE_NONE;
1104 }
1105 fdisk_partition_end_follow_default(pa, 0);
1106 }
1107
1108 done:
1109 DBG(SCRIPT, ul_debugobj(dp, " size parse result: rc=%d, move=%s, size=%ju, default=%s",
1110 rc, pa->resize == FDISK_RESIZE_REDUCE ? "reduce" :
1111 pa->resize == FDISK_RESIZE_ENLARGE ? "enlage" : "none",
1112 pa->size,
1113 pa->end_follow_default ? "on" : "off"));
1114 return rc;
1115 }
1116
1117
1118 #define FDISK_SCRIPT_PARTTYPE_PARSE_FLAGS \
1119 (FDISK_PARTTYPE_PARSE_DATA | FDISK_PARTTYPE_PARSE_DATALAST | \
1120 FDISK_PARTTYPE_PARSE_SHORTCUT | FDISK_PARTTYPE_PARSE_ALIAS | \
1121 FDISK_PARTTYPE_PARSE_NAME | \
1122 FDISK_PARTTYPE_PARSE_DEPRECATED)
1123
1124 /* dump format
1125 * <device>: start=<num>, size=<num>, type=<string>, ...
1126 */
1127 static int parse_line_nameval(struct fdisk_script *dp, char *s)
1128 {
1129 char *p, *x;
1130 struct fdisk_partition *pa;
1131 int rc = 0;
1132 int pno;
1133
1134 assert(dp);
1135 assert(s);
1136 assert(dp->table);
1137
1138 DBG(SCRIPT, ul_debugobj(dp, " parse script line: '%s'", s));
1139
1140 pa = fdisk_new_partition();
1141 if (!pa)
1142 return -ENOMEM;
1143
1144 fdisk_partition_start_follow_default(pa, 1);
1145 fdisk_partition_end_follow_default(pa, 1);
1146 fdisk_partition_partno_follow_default(pa, 1);
1147
1148 /* set partno */
1149 p = strchr(s, ':');
1150 x = strchr(s, '=');
1151 if (p && (!x || p < x)) {
1152 *p = '\0';
1153 p++;
1154
1155 pno = partno_from_devname(s);
1156 if (pno >= 0) {
1157 fdisk_partition_partno_follow_default(pa, 0);
1158 fdisk_partition_set_partno(pa, pno);
1159 }
1160 } else
1161 p = s;
1162
1163 while (rc == 0 && p && *p) {
1164
1165 DBG(SCRIPT, ul_debugobj(dp, " parsing '%s'", p));
1166 p = (char *) skip_blank(p);
1167
1168 if (!strncasecmp(p, "start=", 6)) {
1169 p += 6;
1170 rc = parse_start_value(dp, pa, &p);
1171
1172 } else if (!strncasecmp(p, "size=", 5)) {
1173 p += 5;
1174 rc = parse_size_value(dp, pa, &p);
1175
1176 } else if (!strncasecmp(p, "bootable", 8)) {
1177 /* we use next_token() to skip possible extra space */
1178 char *tk = next_token(&p);
1179 if (tk && strcasecmp(tk, "bootable") == 0)
1180 pa->boot = 1;
1181 else
1182 rc = -EINVAL;
1183
1184 } else if (!strncasecmp(p, "attrs=", 6)) {
1185 p += 6;
1186 free(pa->attrs);
1187 rc = next_string(&p, &pa->attrs);
1188
1189 } else if (!strncasecmp(p, "uuid=", 5)) {
1190 p += 5;
1191 free(pa->uuid);
1192 rc = next_string(&p, &pa->uuid);
1193
1194 } else if (!strncasecmp(p, "name=", 5)) {
1195 p += 5;
1196 free(pa->name);
1197 rc = next_string(&p, &pa->name);
1198 if (!rc)
1199 unhexmangle_string(pa->name);
1200
1201 } else if (!strncasecmp(p, "type=", 5) ||
1202 !strncasecmp(p, "Id=", 3)) { /* backward compatibility */
1203 char *type = NULL;
1204
1205 fdisk_unref_parttype(pa->type);
1206 pa->type = NULL;
1207
1208 p += ((*p == 'I' || *p == 'i') ? 3 : 5); /* "Id=", "type=" */
1209
1210 rc = next_string(&p, &type);
1211 if (rc == 0) {
1212 pa->type = fdisk_label_advparse_parttype(script_get_label(dp),
1213 type, FDISK_SCRIPT_PARTTYPE_PARSE_FLAGS);
1214 if (!pa->type)
1215 rc = -EINVAL;
1216 }
1217 free(type);
1218 } else {
1219 DBG(SCRIPT, ul_debugobj(dp, "script parse error: unknown field '%s'", p));
1220 rc = -EINVAL;
1221 break;
1222 }
1223 }
1224
1225 if (!rc)
1226 rc = fdisk_table_add_partition(dp->table, pa);
1227 if (rc)
1228 DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
1229
1230 fdisk_unref_partition(pa);
1231 return rc;
1232 }
1233
1234 /* simple format:
1235 * <start>, <size>, <type>, <bootable>, ...
1236 */
1237 static int parse_line_valcommas(struct fdisk_script *dp, char *s)
1238 {
1239 int rc = 0;
1240 char *p = s;
1241 struct fdisk_partition *pa;
1242 enum { ITEM_START, ITEM_SIZE, ITEM_TYPE, ITEM_BOOTABLE };
1243 int item = -1;
1244
1245 assert(dp);
1246 assert(s);
1247 assert(dp->table);
1248
1249 pa = fdisk_new_partition();
1250 if (!pa)
1251 return -ENOMEM;
1252
1253 fdisk_partition_start_follow_default(pa, 1);
1254 fdisk_partition_end_follow_default(pa, 1);
1255 fdisk_partition_partno_follow_default(pa, 1);
1256
1257 while (rc == 0 && p && *p) {
1258 char *begin;
1259
1260 p = (char *) skip_blank(p);
1261 item++;
1262
1263 DBG(SCRIPT, ul_debugobj(dp, " parsing item %d ('%s')", item, p));
1264 begin = p;
1265
1266 switch (item) {
1267 case ITEM_START:
1268 rc = parse_start_value(dp, pa, &p);
1269 break;
1270 case ITEM_SIZE:
1271 rc = parse_size_value(dp, pa, &p);
1272 break;
1273 case ITEM_TYPE:
1274 {
1275 char *str = NULL;
1276
1277 fdisk_unref_parttype(pa->type);
1278 pa->type = NULL;
1279
1280 if (*p == ',' || *p == ';' || is_default_value(&p))
1281 break; /* use default type */
1282
1283 rc = next_string(&p, &str);
1284 if (rc)
1285 break;
1286
1287 pa->type = fdisk_label_advparse_parttype(script_get_label(dp),
1288 str, FDISK_SCRIPT_PARTTYPE_PARSE_FLAGS);
1289 free(str);
1290 if (!pa->type)
1291 rc = -EINVAL;
1292 break;
1293 }
1294 case ITEM_BOOTABLE:
1295 if (*p == ',' || *p == ';')
1296 break;
1297 else {
1298 char *tk = next_token(&p);
1299 if (tk && *tk == '*' && *(tk + 1) == '\0')
1300 pa->boot = 1;
1301 else if (tk && *tk == '-' && *(tk + 1) == '\0')
1302 pa->boot = 0;
1303 else if (tk && *tk == '+' && *(tk + 1) == '\0')
1304 pa->boot = 1;
1305 else
1306 rc = -EINVAL;
1307 }
1308 break;
1309 default:
1310 break;
1311 }
1312
1313 if (begin == p)
1314 p++;
1315 }
1316
1317 if (!rc)
1318 rc = fdisk_table_add_partition(dp->table, pa);
1319 if (rc)
1320 DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
1321
1322 fdisk_unref_partition(pa);
1323 return rc;
1324 }
1325
1326 /* modifies @s ! */
1327 static int fdisk_script_read_buffer(struct fdisk_script *dp, char *s)
1328 {
1329 int rc = 0;
1330
1331 assert(dp);
1332 assert(s);
1333
1334 DBG(SCRIPT, ul_debugobj(dp, " parsing buffer"));
1335
1336 s = (char *) skip_blank(s);
1337 if (!s || !*s)
1338 return 0; /* nothing baby, ignore */
1339
1340 if (!dp->table && fdisk_script_get_table(dp) == NULL)
1341 return -ENOMEM;
1342
1343 /* parse header lines only if no partition specified yet */
1344 if (fdisk_table_is_empty(dp->table) && is_header_line(s))
1345 rc = parse_line_header(dp, s);
1346
1347 /* parse script format */
1348 else if (strchr(s, '='))
1349 rc = parse_line_nameval(dp, s);
1350
1351 /* parse simple <value>, ... format */
1352 else
1353 rc = parse_line_valcommas(dp, s);
1354
1355 if (rc)
1356 DBG(SCRIPT, ul_debugobj(dp, "%zu: parse error [rc=%d]",
1357 dp->nlines, rc));
1358 return rc;
1359 }
1360
1361 /**
1362 * fdisk_script_set_fgets:
1363 * @dp: script
1364 * @fn_fgets: callback function
1365 *
1366 * The library uses fgets() function to read the next line from the script.
1367 * This default maybe overridden by another function. Note that the function has
1368 * to return the line terminated by \n (for example readline(3) removes \n).
1369 *
1370 * Return: 0 on success, <0 on error
1371 */
1372 int fdisk_script_set_fgets(struct fdisk_script *dp,
1373 char *(*fn_fgets)(struct fdisk_script *, char *, size_t, FILE *))
1374 {
1375 assert(dp);
1376
1377 dp->fn_fgets = fn_fgets;
1378 return 0;
1379 }
1380
1381 /**
1382 * fdisk_script_read_line:
1383 * @dp: script
1384 * @f: file
1385 * @buf: buffer to store one line of the file
1386 * @bufsz: buffer size
1387 *
1388 * Reads next line into dump.
1389 *
1390 * Returns: 0 on success, <0 on error, 1 when nothing to read. For unknown headers
1391 * returns -ENOTSUP, it's usually safe to ignore this error.
1392 */
1393 int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz)
1394 {
1395 char *s;
1396
1397 assert(dp);
1398 assert(f);
1399 assert(bufsz);
1400
1401 DBG(SCRIPT, ul_debugobj(dp, " parsing line %zu", dp->nlines));
1402
1403 /* read the next non-blank non-comment line */
1404 do {
1405 buf[0] = '\0';
1406 if (dp->fn_fgets) {
1407 if (dp->fn_fgets(dp, buf, bufsz, f) == NULL)
1408 return 1;
1409 } else if (fgets(buf, bufsz, f) == NULL)
1410 return 1;
1411
1412 dp->nlines++;
1413 s = strchr(buf, '\n');
1414 if (!s) {
1415 /* Missing final newline? Otherwise an extremely */
1416 /* long line - assume file was corrupted */
1417 if (feof(f)) {
1418 DBG(SCRIPT, ul_debugobj(dp, "no final newline"));
1419 s = strchr(buf, '\0');
1420 } else {
1421 DBG(SCRIPT, ul_debugobj(dp,
1422 "%zu: missing newline at line", dp->nlines));
1423 return -EINVAL;
1424 }
1425 }
1426
1427 *s = '\0';
1428 if (--s >= buf && *s == '\r')
1429 *s = '\0';
1430 s = (char *) skip_blank(buf);
1431 } while (*s == '\0' || *s == '#');
1432
1433 return fdisk_script_read_buffer(dp, s);
1434 }
1435
1436
1437 /**
1438 * fdisk_script_read_file:
1439 * @dp: script
1440 * @f: input file
1441 *
1442 * Reads file @f into script @dp.
1443 *
1444 * Returns: 0 on success, <0 on error.
1445 */
1446 int fdisk_script_read_file(struct fdisk_script *dp, FILE *f)
1447 {
1448 char buf[BUFSIZ] = { '\0' };
1449 int rc = 1;
1450
1451 assert(dp);
1452 assert(f);
1453
1454 DBG(SCRIPT, ul_debugobj(dp, "parsing file"));
1455
1456 while (!feof(f)) {
1457 rc = fdisk_script_read_line(dp, f, buf, sizeof(buf));
1458 if (rc && rc != -ENOTSUP)
1459 break;
1460 }
1461
1462 if (rc == 1)
1463 rc = 0; /* end of file */
1464
1465 DBG(SCRIPT, ul_debugobj(dp, "parsing file done [rc=%d]", rc));
1466 return rc;
1467 }
1468
1469 /**
1470 * fdisk_set_script:
1471 * @cxt: context
1472 * @dp: script (or NULL to remove previous reference)
1473 *
1474 * Sets reference to the @dp script and remove reference to the previously used
1475 * script.
1476 *
1477 * The script headers might be used by label drivers to overwrite
1478 * built-in defaults (for example disk label Id) and label driver might
1479 * optimize the default semantic to be more usable for scripts (for example to
1480 * not ask for primary/logical/extended partition type).
1481 *
1482 * Note that script also contains reference to the fdisk context (see
1483 * fdisk_new_script()). This context may be completely independent on
1484 * context used for fdisk_set_script().
1485 *
1486 * Don't forget to call fdisk_set_script(cxt, NULL); to remove this reference
1487 * if no more necessary!
1488 *
1489 * Returns: <0 on error, 0 on success.
1490 */
1491 int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp)
1492 {
1493 assert(cxt);
1494
1495 /* unref old */
1496 if (cxt->script)
1497 fdisk_unref_script(cxt->script);
1498
1499 /* ref new */
1500 cxt->script = dp;
1501 if (cxt->script) {
1502 DBG(CXT, ul_debugobj(cxt, "setting reference to script %p", cxt->script));
1503 fdisk_ref_script(cxt->script);
1504 }
1505
1506 return 0;
1507 }
1508
1509 /**
1510 * fdisk_get_script:
1511 * @cxt: context
1512 *
1513 * Returns: the current script or NULL.
1514 */
1515 struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt)
1516 {
1517 assert(cxt);
1518 return cxt->script;
1519 }
1520
1521 /**
1522 * fdisk_apply_script_headers:
1523 * @cxt: context
1524 * @dp: script
1525 *
1526 * Associate context @cxt with script @dp and creates a new empty disklabel.
1527 * The script may be later unreference by fdisk_set_script() with NULL as script.
1528 *
1529 * Returns: 0 on success, <0 on error.
1530 */
1531 int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp)
1532 {
1533 const char *name;
1534 const char *str;
1535 int rc;
1536
1537 assert(cxt);
1538 assert(dp);
1539
1540 DBG(SCRIPT, ul_debugobj(dp, "applying script headers"));
1541 fdisk_set_script(cxt, dp);
1542
1543 if (dp->sector_size && dp->cxt->sector_size != dp->sector_size) {
1544 /*
1545 * Ignore last and first LBA if device sector size mismatch
1546 * with sector size in script. It would be possible to
1547 * recalculate it, but for GPT it will not work in some cases
1548 * as these offsets are calculated by relative number of
1549 * sectors. It's better to use library defaults than try
1550 * to be smart ...
1551 */
1552 if (fdisk_script_get_header(dp, "first-lba")) {
1553 fdisk_script_set_header(dp, "first-lba", NULL);
1554 fdisk_info(dp->cxt, _("Ignore \"first-lba\" header due to sector size mismatch."));
1555 }
1556 if (fdisk_script_get_header(dp, "last-lba")) {
1557 fdisk_script_set_header(dp, "last-lba", NULL);
1558 fdisk_info(dp->cxt, _("Ignore \"last-lba\" header due to sector size mismatch."));
1559 }
1560 }
1561
1562 str = fdisk_script_get_header(dp, "grain");
1563 if (str) {
1564 uintmax_t sz;
1565
1566 rc = parse_size(str, &sz, NULL);
1567 if (rc == 0)
1568 rc = fdisk_save_user_grain(cxt, sz);
1569 if (rc)
1570 return rc;
1571 }
1572
1573 if (fdisk_has_user_device_properties(cxt))
1574 fdisk_apply_user_device_properties(cxt);
1575
1576 /* create empty label */
1577 name = fdisk_script_get_header(dp, "label");
1578 if (!name)
1579 return -EINVAL;
1580
1581 rc = fdisk_create_disklabel(cxt, name);
1582 if (rc)
1583 return rc;
1584
1585 str = fdisk_script_get_header(dp, "table-length");
1586 if (str) {
1587 uintmax_t sz;
1588
1589 rc = parse_size(str, &sz, NULL);
1590 if (rc == 0)
1591 rc = fdisk_gpt_set_npartitions(cxt, sz);
1592 }
1593
1594 return rc;
1595 }
1596
1597 /**
1598 * fdisk_apply_script:
1599 * @cxt: context
1600 * @dp: script
1601 *
1602 * This function creates a new disklabel and partition within context @cxt. You
1603 * have to call fdisk_write_disklabel() to apply changes to the device.
1604 *
1605 * Returns: 0 on error, <0 on error.
1606 */
1607 int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp)
1608 {
1609 int rc;
1610 struct fdisk_script *old;
1611
1612 assert(dp);
1613 assert(cxt);
1614
1615 DBG(CXT, ul_debugobj(cxt, "applying script %p", dp));
1616
1617 old = fdisk_get_script(cxt);
1618 fdisk_ref_script(old);
1619
1620 /* create empty disk label */
1621 rc = fdisk_apply_script_headers(cxt, dp);
1622
1623 /* create partitions */
1624 if (!rc && dp->table)
1625 rc = fdisk_apply_table(cxt, dp->table);
1626
1627 fdisk_set_script(cxt, old);
1628 fdisk_unref_script(old);
1629
1630 DBG(CXT, ul_debugobj(cxt, "script done [rc=%d]", rc));
1631 return rc;
1632 }
1633
1634 #ifdef FUZZ_TARGET
1635 # include "all-io.h"
1636
1637 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
1638 {
1639 char name[] = "/tmp/test-script-fuzz.XXXXXX";
1640 int fd;
1641 struct fdisk_script *dp;
1642 struct fdisk_context *cxt;
1643 FILE *f;
1644
1645 fd = mkstemp_cloexec(name);
1646 if (fd < 0)
1647 err(EXIT_FAILURE, "mkstemp() failed");
1648 if (write_all(fd, data, size) != 0)
1649 err(EXIT_FAILURE, "write() failed");
1650 f = fopen(name, "r");
1651 if (!f)
1652 err(EXIT_FAILURE, "cannot open %s", name);
1653
1654 cxt = fdisk_new_context();
1655 dp = fdisk_new_script(cxt);
1656
1657 fdisk_script_read_file(dp, f);
1658 fclose(f);
1659
1660 fdisk_script_write_file(dp, stdout);
1661 fdisk_unref_script(dp);
1662 fdisk_unref_context(cxt);
1663
1664 close(fd);
1665 unlink(name);
1666
1667 return 0;
1668 }
1669 #endif
1670
1671 #ifdef TEST_PROGRAM
1672 static int test_dump(struct fdisk_test *ts, int argc, char *argv[])
1673 {
1674 char *devname = argv[1];
1675 struct fdisk_context *cxt;
1676 struct fdisk_script *dp;
1677
1678 cxt = fdisk_new_context();
1679 fdisk_assign_device(cxt, devname, 1);
1680
1681 dp = fdisk_new_script(cxt);
1682 fdisk_script_read_context(dp, NULL);
1683
1684 fdisk_script_write_file(dp, stdout);
1685 fdisk_unref_script(dp);
1686 fdisk_unref_context(cxt);
1687
1688 return 0;
1689 }
1690
1691 static int test_read(struct fdisk_test *ts, int argc, char *argv[])
1692 {
1693 char *filename = argv[1];
1694 struct fdisk_script *dp;
1695 struct fdisk_context *cxt;
1696 FILE *f;
1697
1698 if (!(f = fopen(filename, "r")))
1699 err(EXIT_FAILURE, "%s: cannot open", filename);
1700
1701 cxt = fdisk_new_context();
1702 dp = fdisk_new_script(cxt);
1703
1704 fdisk_script_read_file(dp, f);
1705 fclose(f);
1706
1707 fdisk_script_write_file(dp, stdout);
1708 fdisk_unref_script(dp);
1709 fdisk_unref_context(cxt);
1710
1711 return 0;
1712 }
1713
1714 static int test_stdin(struct fdisk_test *ts, int argc, char *argv[])
1715 {
1716 char buf[BUFSIZ] = { '\0' };
1717 struct fdisk_script *dp;
1718 struct fdisk_context *cxt;
1719 int rc = 0;
1720
1721 cxt = fdisk_new_context();
1722 dp = fdisk_new_script(cxt);
1723 fdisk_script_set_header(dp, "label", "dos");
1724
1725 printf("<start>, <size>, <type>, <bootable: *|->\n");
1726 do {
1727 struct fdisk_partition *pa;
1728 size_t n = dp->table ? fdisk_table_get_nents(dp->table) : 0;
1729
1730 printf(" #%zu :\n", n + 1);
1731 rc = fdisk_script_read_line(dp, stdin, buf, sizeof(buf));
1732
1733 if (rc == 0) {
1734 pa = fdisk_table_get_partition(dp->table, n);
1735 printf(" #%zu %12ju %12ju\n", n + 1,
1736 (uintmax_t)fdisk_partition_get_start(pa),
1737 (uintmax_t)fdisk_partition_get_size(pa));
1738 }
1739 } while (rc == 0);
1740
1741 if (!rc)
1742 fdisk_script_write_file(dp, stdout);
1743 fdisk_unref_script(dp);
1744 fdisk_unref_context(cxt);
1745
1746 return rc;
1747 }
1748
1749 static int test_apply(struct fdisk_test *ts, int argc, char *argv[])
1750 {
1751 char *devname = argv[1], *scriptname = argv[2];
1752 struct fdisk_context *cxt;
1753 struct fdisk_script *dp;
1754 struct fdisk_table *tb = NULL;
1755 struct fdisk_iter *itr = NULL;
1756 struct fdisk_partition *pa = NULL;
1757 int rc;
1758
1759 cxt = fdisk_new_context();
1760 fdisk_assign_device(cxt, devname, 0);
1761
1762 dp = fdisk_new_script_from_file(cxt, scriptname);
1763 if (!dp)
1764 return -errno;
1765
1766 rc = fdisk_apply_script(cxt, dp);
1767 if (rc)
1768 goto done;
1769 fdisk_unref_script(dp);
1770
1771 /* list result */
1772 fdisk_list_disklabel(cxt);
1773 fdisk_get_partitions(cxt, &tb);
1774
1775 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
1776 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
1777 printf(" #%zu %12ju %12ju\n", fdisk_partition_get_partno(pa),
1778 (uintmax_t)fdisk_partition_get_start(pa),
1779 (uintmax_t)fdisk_partition_get_size(pa));
1780 }
1781
1782 done:
1783 fdisk_free_iter(itr);
1784 fdisk_unref_table(tb);
1785
1786 /*fdisk_write_disklabel(cxt);*/
1787 fdisk_unref_context(cxt);
1788 return 0;
1789 }
1790
1791 static int test_tokens(struct fdisk_test *ts, int argc, char *argv[])
1792 {
1793 char *p, *str = argc == 2 ? strdup(argv[1]) : NULL;
1794 int i;
1795
1796 for (i = 1, p = str; p && *p; i++) {
1797 char *tk = next_token(&p);
1798
1799 if (!tk)
1800 break;
1801
1802 printf("#%d: '%s'\n", i, tk);
1803 }
1804
1805 free(str);
1806 return 0;
1807 }
1808
1809 int main(int argc, char *argv[])
1810 {
1811 struct fdisk_test tss[] = {
1812 { "--dump", test_dump, "<device> dump PT as script" },
1813 { "--read", test_read, "<file> read PT script from file" },
1814 { "--apply", test_apply, "<device> <file> try apply script from file to device" },
1815 { "--stdin", test_stdin, " read input like sfdisk" },
1816 { "--tokens", test_tokens, "<string> parse string" },
1817 { NULL }
1818 };
1819
1820 return fdisk_run_test(tss, argc, argv);
1821 }
1822
1823 #endif