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