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