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