]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libfdisk/src/script.c
libfdisk: (script) fix partno_from_devname()
[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 if (!s || !*s)
957 return -1;
958
959 sz = rtrim_whitespace((unsigned char *)s);
960 end = p = s + sz;
961
962 while (p > s && isdigit(*(p - 1)))
963 p--;
964 if (p == end)
965 return -1;
966 end = NULL;
967 errno = 0;
968 pno = strtol(p, &end, 10);
969 if (errno || !end || p == end)
970 return -1;
971 return pno - 1;
972 }
973
974 #define FDISK_SCRIPT_PARTTYPE_PARSE_FLAGS \
975 (FDISK_PARTTYPE_PARSE_DATA | FDISK_PARTTYPE_PARSE_DATALAST | \
976 FDISK_PARTTYPE_PARSE_SHORTCUT | FDISK_PARTTYPE_PARSE_ALIAS | \
977 FDISK_PARTTYPE_PARSE_DEPRECATED)
978
979 /* dump format
980 * <device>: start=<num>, size=<num>, type=<string>, ...
981 */
982 static int parse_line_nameval(struct fdisk_script *dp, char *s)
983 {
984 char *p, *x;
985 struct fdisk_partition *pa;
986 int rc = 0;
987 uint64_t num;
988 int pno;
989
990 assert(dp);
991 assert(s);
992 assert(dp->table);
993
994 DBG(SCRIPT, ul_debugobj(dp, " parse script line: '%s'", s));
995
996 pa = fdisk_new_partition();
997 if (!pa)
998 return -ENOMEM;
999
1000 fdisk_partition_start_follow_default(pa, 1);
1001 fdisk_partition_end_follow_default(pa, 1);
1002 fdisk_partition_partno_follow_default(pa, 1);
1003
1004 /* set partno */
1005 p = strchr(s, ':');
1006 x = strchr(s, '=');
1007 if (p && (!x || p < x)) {
1008 *p = '\0';
1009 p++;
1010
1011 pno = partno_from_devname(s);
1012 if (pno >= 0) {
1013 fdisk_partition_partno_follow_default(pa, 0);
1014 fdisk_partition_set_partno(pa, pno);
1015 }
1016 } else
1017 p = s;
1018
1019 while (rc == 0 && p && *p) {
1020
1021 DBG(SCRIPT, ul_debugobj(dp, " parsing '%s'", p));
1022 p = (char *) skip_blank(p);
1023
1024 if (!strncasecmp(p, "start=", 6)) {
1025 int pow = 0;
1026 p += 6;
1027 rc = next_number(&p, &num, &pow);
1028 if (!rc) {
1029 if (pow) /* specified as <num><suffix> */
1030 num /= dp->cxt->sector_size;
1031 fdisk_partition_set_start(pa, num);
1032 fdisk_partition_start_follow_default(pa, 0);
1033 }
1034 } else if (!strncasecmp(p, "size=", 5)) {
1035 int pow = 0;
1036
1037 p += 5;
1038 rc = next_number(&p, &num, &pow);
1039 if (!rc) {
1040 if (pow) /* specified as <num><suffix> */
1041 num /= dp->cxt->sector_size;
1042 else /* specified as number of sectors */
1043 fdisk_partition_size_explicit(pa, 1);
1044 fdisk_partition_set_size(pa, num);
1045 fdisk_partition_end_follow_default(pa, 0);
1046 }
1047
1048 } else if (!strncasecmp(p, "bootable", 8)) {
1049 /* we use next_token() to skip possible extra space */
1050 char *tk = next_token(&p);
1051 if (tk && strcasecmp(tk, "bootable") == 0)
1052 pa->boot = 1;
1053 else
1054 rc = -EINVAL;
1055
1056 } else if (!strncasecmp(p, "attrs=", 6)) {
1057 p += 6;
1058 rc = next_string(&p, &pa->attrs);
1059
1060 } else if (!strncasecmp(p, "uuid=", 5)) {
1061 p += 5;
1062 rc = next_string(&p, &pa->uuid);
1063
1064 } else if (!strncasecmp(p, "name=", 5)) {
1065 p += 5;
1066 rc = next_string(&p, &pa->name);
1067 if (!rc)
1068 unhexmangle_string(pa->name);
1069
1070 } else if (!strncasecmp(p, "type=", 5) ||
1071 !strncasecmp(p, "Id=", 3)) { /* backward compatibility */
1072 char *type;
1073
1074 p += ((*p == 'I' || *p == 'i') ? 3 : 5); /* "Id=", "type=" */
1075
1076 rc = next_string(&p, &type);
1077 if (rc)
1078 break;
1079
1080 pa->type = fdisk_label_advparse_parttype(script_get_label(dp),
1081 type, FDISK_SCRIPT_PARTTYPE_PARSE_FLAGS);
1082 free(type);
1083
1084 if (!pa->type) {
1085 rc = -EINVAL;
1086 break;
1087 }
1088 } else {
1089 DBG(SCRIPT, ul_debugobj(dp, "script parse error: unknown field '%s'", p));
1090 rc = -EINVAL;
1091 break;
1092 }
1093 }
1094
1095 if (!rc)
1096 rc = fdisk_table_add_partition(dp->table, pa);
1097 if (rc)
1098 DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
1099
1100 fdisk_unref_partition(pa);
1101 return rc;
1102 }
1103
1104 #define TK_PLUS 1
1105 #define TK_MINUS -1
1106
1107 #define alone_sign(_sign, _p) (_sign && (*_p == '\0' || isblank(*_p)))
1108
1109 /* simple format:
1110 * <start>, <size>, <type>, <bootable>, ...
1111 */
1112 static int parse_line_valcommas(struct fdisk_script *dp, char *s)
1113 {
1114 int rc = 0;
1115 char *p = s, *str;
1116 struct fdisk_partition *pa;
1117 enum { ITEM_START, ITEM_SIZE, ITEM_TYPE, ITEM_BOOTABLE };
1118 int item = -1;
1119
1120 assert(dp);
1121 assert(s);
1122 assert(dp->table);
1123
1124 pa = fdisk_new_partition();
1125 if (!pa)
1126 return -ENOMEM;
1127
1128 fdisk_partition_start_follow_default(pa, 1);
1129 fdisk_partition_end_follow_default(pa, 1);
1130 fdisk_partition_partno_follow_default(pa, 1);
1131
1132 while (rc == 0 && p && *p) {
1133 uint64_t num;
1134 char *begin;
1135 int sign = 0;
1136
1137 p = (char *) skip_blank(p);
1138 item++;
1139
1140 if (item != ITEM_BOOTABLE) {
1141 sign = *p == '-' ? TK_MINUS : *p == '+' ? TK_PLUS : 0;
1142 if (sign)
1143 p++;
1144 }
1145
1146 DBG(SCRIPT, ul_debugobj(dp, " parsing item %d ('%s')", item, p));
1147 begin = p;
1148
1149 switch (item) {
1150 case ITEM_START:
1151 if (*p == ',' || *p == ';' || alone_sign(sign, p))
1152 fdisk_partition_start_follow_default(pa, 1);
1153 else {
1154 int pow = 0;
1155
1156 rc = next_number(&p, &num, &pow);
1157 if (!rc) {
1158 if (pow) /* specified as <num><suffix> */
1159 num /= dp->cxt->sector_size;
1160 fdisk_partition_set_start(pa, num);
1161 pa->movestart = sign == TK_MINUS ? FDISK_MOVE_DOWN :
1162 sign == TK_PLUS ? FDISK_MOVE_UP :
1163 FDISK_MOVE_NONE;
1164 }
1165 fdisk_partition_start_follow_default(pa, 0);
1166 }
1167 break;
1168 case ITEM_SIZE:
1169 if (*p == ',' || *p == ';' || alone_sign(sign, p)) {
1170 fdisk_partition_end_follow_default(pa, 1);
1171 if (sign == TK_PLUS)
1172 /* '+' alone means use all possible space, '-' alone means nothing */
1173 pa->resize = FDISK_RESIZE_ENLARGE;
1174 } else {
1175 int pow = 0;
1176 rc = next_number(&p, &num, &pow);
1177 if (!rc) {
1178 if (pow) /* specified as <size><suffix> */
1179 num /= dp->cxt->sector_size;
1180 else /* specified as number of sectors */
1181 fdisk_partition_size_explicit(pa, 1);
1182 fdisk_partition_set_size(pa, num);
1183 pa->resize = sign == TK_MINUS ? FDISK_RESIZE_REDUCE :
1184 sign == TK_PLUS ? FDISK_RESIZE_ENLARGE :
1185 FDISK_RESIZE_NONE;
1186 }
1187 fdisk_partition_end_follow_default(pa, 0);
1188 }
1189 break;
1190 case ITEM_TYPE:
1191 if (*p == ',' || *p == ';' || alone_sign(sign, p))
1192 break; /* use default type */
1193
1194 rc = next_string(&p, &str);
1195 if (rc)
1196 break;
1197
1198 pa->type = fdisk_label_advparse_parttype(script_get_label(dp),
1199 str, FDISK_SCRIPT_PARTTYPE_PARSE_FLAGS);
1200 free(str);
1201
1202 if (!pa->type)
1203 rc = -EINVAL;
1204 break;
1205 case ITEM_BOOTABLE:
1206 if (*p == ',' || *p == ';')
1207 break;
1208 else {
1209 char *tk = next_token(&p);
1210 if (tk && *tk == '*' && *(tk + 1) == '\0')
1211 pa->boot = 1;
1212 else if (tk && *tk == '-' && *(tk + 1) == '\0')
1213 pa->boot = 0;
1214 else if (tk && *tk == '+' && *(tk + 1) == '\0')
1215 pa->boot = 1;
1216 else
1217 rc = -EINVAL;
1218 }
1219 break;
1220 default:
1221 break;
1222 }
1223
1224 if (begin == p)
1225 p++;
1226 }
1227
1228 if (!rc)
1229 rc = fdisk_table_add_partition(dp->table, pa);
1230 if (rc)
1231 DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
1232
1233 fdisk_unref_partition(pa);
1234 return rc;
1235 }
1236
1237 /* modifies @s ! */
1238 static int fdisk_script_read_buffer(struct fdisk_script *dp, char *s)
1239 {
1240 int rc = 0;
1241
1242 assert(dp);
1243 assert(s);
1244
1245 DBG(SCRIPT, ul_debugobj(dp, " parsing buffer"));
1246
1247 s = (char *) skip_blank(s);
1248 if (!s || !*s)
1249 return 0; /* nothing baby, ignore */
1250
1251 if (!dp->table && fdisk_script_get_table(dp) == NULL)
1252 return -ENOMEM;
1253
1254 /* parse header lines only if no partition specified yet */
1255 if (fdisk_table_is_empty(dp->table) && is_header_line(s))
1256 rc = parse_line_header(dp, s);
1257
1258 /* parse script format */
1259 else if (strchr(s, '='))
1260 rc = parse_line_nameval(dp, s);
1261
1262 /* parse simple <value>, ... format */
1263 else
1264 rc = parse_line_valcommas(dp, s);
1265
1266 if (rc)
1267 DBG(SCRIPT, ul_debugobj(dp, "%zu: parse error [rc=%d]",
1268 dp->nlines, rc));
1269 return rc;
1270 }
1271
1272 /**
1273 * fdisk_script_set_fgets:
1274 * @dp: script
1275 * @fn_fgets: callback function
1276 *
1277 * The library uses fgets() function to read the next line from the script.
1278 * This default maybe overridden by another function. Note that the function has
1279 * to return the line terminated by \n (for example readline(3) removes \n).
1280 *
1281 * Return: 0 on success, <0 on error
1282 */
1283 int fdisk_script_set_fgets(struct fdisk_script *dp,
1284 char *(*fn_fgets)(struct fdisk_script *, char *, size_t, FILE *))
1285 {
1286 assert(dp);
1287
1288 dp->fn_fgets = fn_fgets;
1289 return 0;
1290 }
1291
1292 /**
1293 * fdisk_script_read_line:
1294 * @dp: script
1295 * @f: file
1296 * @buf: buffer to store one line of the file
1297 * @bufsz: buffer size
1298 *
1299 * Reads next line into dump.
1300 *
1301 * Returns: 0 on success, <0 on error, 1 when nothing to read. For unknown headers
1302 * returns -ENOTSUP, it's usually safe to ignore this error.
1303 */
1304 int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz)
1305 {
1306 char *s;
1307
1308 assert(dp);
1309 assert(f);
1310
1311 DBG(SCRIPT, ul_debugobj(dp, " parsing line %zu", dp->nlines));
1312
1313 /* read the next non-blank non-comment line */
1314 do {
1315 if (dp->fn_fgets) {
1316 if (dp->fn_fgets(dp, buf, bufsz, f) == NULL)
1317 return 1;
1318 } else if (fgets(buf, bufsz, f) == NULL)
1319 return 1;
1320
1321 dp->nlines++;
1322 s = strchr(buf, '\n');
1323 if (!s) {
1324 /* Missing final newline? Otherwise an extremely */
1325 /* long line - assume file was corrupted */
1326 if (feof(f)) {
1327 DBG(SCRIPT, ul_debugobj(dp, "no final newline"));
1328 s = strchr(buf, '\0');
1329 } else {
1330 DBG(SCRIPT, ul_debugobj(dp,
1331 "%zu: missing newline at line", dp->nlines));
1332 return -EINVAL;
1333 }
1334 }
1335
1336 *s = '\0';
1337 if (--s >= buf && *s == '\r')
1338 *s = '\0';
1339 s = (char *) skip_blank(buf);
1340 } while (*s == '\0' || *s == '#');
1341
1342 return fdisk_script_read_buffer(dp, s);
1343 }
1344
1345
1346 /**
1347 * fdisk_script_read_file:
1348 * @dp: script
1349 * @f: input file
1350 *
1351 * Reads file @f into script @dp.
1352 *
1353 * Returns: 0 on success, <0 on error.
1354 */
1355 int fdisk_script_read_file(struct fdisk_script *dp, FILE *f)
1356 {
1357 char buf[BUFSIZ];
1358 int rc = 1;
1359
1360 assert(dp);
1361 assert(f);
1362
1363 DBG(SCRIPT, ul_debugobj(dp, "parsing file"));
1364
1365 while (!feof(f)) {
1366 rc = fdisk_script_read_line(dp, f, buf, sizeof(buf));
1367 if (rc && rc != -ENOTSUP)
1368 break;
1369 }
1370
1371 if (rc == 1)
1372 rc = 0; /* end of file */
1373
1374 DBG(SCRIPT, ul_debugobj(dp, "parsing file done [rc=%d]", rc));
1375 return rc;
1376 }
1377
1378 /**
1379 * fdisk_set_script:
1380 * @cxt: context
1381 * @dp: script (or NULL to remove previous reference)
1382 *
1383 * Sets reference to the @dp script and remove reference to the previously used
1384 * script.
1385 *
1386 * The script headers might be used by label drivers to overwrite
1387 * built-in defaults (for example disk label Id) and label driver might
1388 * optimize the default semantic to be more usable for scripts (for example to
1389 * not ask for primary/logical/extended partition type).
1390 *
1391 * Note that script also contains reference to the fdisk context (see
1392 * fdisk_new_script()). This context may be completely independent on
1393 * context used for fdisk_set_script().
1394 *
1395 * Don't forget to call fdisk_set_script(cxt, NULL); to remove this reference
1396 * if no more necessary!
1397 *
1398 * Returns: <0 on error, 0 on success.
1399 */
1400 int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp)
1401 {
1402 assert(cxt);
1403
1404 /* unref old */
1405 if (cxt->script)
1406 fdisk_unref_script(cxt->script);
1407
1408 /* ref new */
1409 cxt->script = dp;
1410 if (cxt->script) {
1411 DBG(CXT, ul_debugobj(cxt, "setting reference to script %p", cxt->script));
1412 fdisk_ref_script(cxt->script);
1413 }
1414
1415 return 0;
1416 }
1417
1418 /**
1419 * fdisk_get_script:
1420 * @cxt: context
1421 *
1422 * Returns: the current script or NULL.
1423 */
1424 struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt)
1425 {
1426 assert(cxt);
1427 return cxt->script;
1428 }
1429
1430 /**
1431 * fdisk_apply_script_headers:
1432 * @cxt: context
1433 * @dp: script
1434 *
1435 * Associate context @cxt with script @dp and creates a new empty disklabel.
1436 * The script may be later unreference by fdisk_set_script() with NULL as script.
1437 *
1438 * Returns: 0 on success, <0 on error.
1439 */
1440 int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp)
1441 {
1442 const char *name;
1443 const char *str;
1444 int rc;
1445
1446 assert(cxt);
1447 assert(dp);
1448
1449 DBG(SCRIPT, ul_debugobj(dp, "applying script headers"));
1450 fdisk_set_script(cxt, dp);
1451
1452 str = fdisk_script_get_header(dp, "grain");
1453 if (str) {
1454 uintmax_t sz;
1455
1456 rc = parse_size(str, &sz, NULL);
1457 if (rc == 0)
1458 rc = fdisk_save_user_grain(cxt, sz);
1459 if (rc)
1460 return rc;
1461 }
1462
1463 if (fdisk_has_user_device_properties(cxt))
1464 fdisk_apply_user_device_properties(cxt);
1465
1466 /* create empty label */
1467 name = fdisk_script_get_header(dp, "label");
1468 if (!name)
1469 return -EINVAL;
1470
1471 rc = fdisk_create_disklabel(cxt, name);
1472 if (rc)
1473 return rc;
1474
1475 str = fdisk_script_get_header(dp, "table-length");
1476 if (str) {
1477 uintmax_t sz;
1478
1479 rc = parse_size(str, &sz, NULL);
1480 if (rc == 0)
1481 rc = fdisk_gpt_set_npartitions(cxt, sz);
1482 }
1483
1484 return rc;
1485 }
1486
1487 /**
1488 * fdisk_apply_script:
1489 * @cxt: context
1490 * @dp: script
1491 *
1492 * This function creates a new disklabel and partition within context @cxt. You
1493 * have to call fdisk_write_disklabel() to apply changes to the device.
1494 *
1495 * Returns: 0 on error, <0 on error.
1496 */
1497 int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp)
1498 {
1499 int rc;
1500 struct fdisk_script *old;
1501
1502 assert(dp);
1503 assert(cxt);
1504
1505 DBG(CXT, ul_debugobj(cxt, "applying script %p", dp));
1506
1507 old = fdisk_get_script(cxt);
1508 fdisk_ref_script(old);
1509
1510 /* create empty disk label */
1511 rc = fdisk_apply_script_headers(cxt, dp);
1512
1513 /* create partitions */
1514 if (!rc && dp->table)
1515 rc = fdisk_apply_table(cxt, dp->table);
1516
1517 fdisk_set_script(cxt, old);
1518 fdisk_unref_script(old);
1519
1520 DBG(CXT, ul_debugobj(cxt, "script done [rc=%d]", rc));
1521 return rc;
1522 }
1523
1524 #ifdef TEST_PROGRAM
1525 static int test_dump(struct fdisk_test *ts, int argc, char *argv[])
1526 {
1527 char *devname = argv[1];
1528 struct fdisk_context *cxt;
1529 struct fdisk_script *dp;
1530
1531 cxt = fdisk_new_context();
1532 fdisk_assign_device(cxt, devname, 1);
1533
1534 dp = fdisk_new_script(cxt);
1535 fdisk_script_read_context(dp, NULL);
1536
1537 fdisk_script_write_file(dp, stdout);
1538 fdisk_unref_script(dp);
1539 fdisk_unref_context(cxt);
1540
1541 return 0;
1542 }
1543
1544 static int test_read(struct fdisk_test *ts, int argc, char *argv[])
1545 {
1546 char *filename = argv[1];
1547 struct fdisk_script *dp;
1548 struct fdisk_context *cxt;
1549 FILE *f;
1550
1551 if (!(f = fopen(filename, "r")))
1552 err(EXIT_FAILURE, "%s: cannot open", filename);
1553
1554 cxt = fdisk_new_context();
1555 dp = fdisk_new_script(cxt);
1556
1557 fdisk_script_read_file(dp, f);
1558 fclose(f);
1559
1560 fdisk_script_write_file(dp, stdout);
1561 fdisk_unref_script(dp);
1562 fdisk_unref_context(cxt);
1563
1564 return 0;
1565 }
1566
1567 static int test_stdin(struct fdisk_test *ts, int argc, char *argv[])
1568 {
1569 char buf[BUFSIZ];
1570 struct fdisk_script *dp;
1571 struct fdisk_context *cxt;
1572 int rc = 0;
1573
1574 cxt = fdisk_new_context();
1575 dp = fdisk_new_script(cxt);
1576 fdisk_script_set_header(dp, "label", "dos");
1577
1578 printf("<start>, <size>, <type>, <bootable: *|->\n");
1579 do {
1580 struct fdisk_partition *pa;
1581 size_t n = dp->table ? fdisk_table_get_nents(dp->table) : 0;
1582
1583 printf(" #%zu :\n", n + 1);
1584 rc = fdisk_script_read_line(dp, stdin, buf, sizeof(buf));
1585
1586 if (rc == 0) {
1587 pa = fdisk_table_get_partition(dp->table, n);
1588 printf(" #%zu %12ju %12ju\n", n + 1,
1589 (uintmax_t)fdisk_partition_get_start(pa),
1590 (uintmax_t)fdisk_partition_get_size(pa));
1591 }
1592 } while (rc == 0);
1593
1594 if (!rc)
1595 fdisk_script_write_file(dp, stdout);
1596 fdisk_unref_script(dp);
1597 fdisk_unref_context(cxt);
1598
1599 return rc;
1600 }
1601
1602 static int test_apply(struct fdisk_test *ts, int argc, char *argv[])
1603 {
1604 char *devname = argv[1], *scriptname = argv[2];
1605 struct fdisk_context *cxt;
1606 struct fdisk_script *dp;
1607 struct fdisk_table *tb = NULL;
1608 struct fdisk_iter *itr = NULL;
1609 struct fdisk_partition *pa = NULL;
1610 int rc;
1611
1612 cxt = fdisk_new_context();
1613 fdisk_assign_device(cxt, devname, 0);
1614
1615 dp = fdisk_new_script_from_file(cxt, scriptname);
1616 if (!dp)
1617 return -errno;
1618
1619 rc = fdisk_apply_script(cxt, dp);
1620 if (rc)
1621 goto done;
1622 fdisk_unref_script(dp);
1623
1624 /* list result */
1625 fdisk_list_disklabel(cxt);
1626 fdisk_get_partitions(cxt, &tb);
1627
1628 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
1629 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
1630 printf(" #%zu %12ju %12ju\n", fdisk_partition_get_partno(pa),
1631 (uintmax_t)fdisk_partition_get_start(pa),
1632 (uintmax_t)fdisk_partition_get_size(pa));
1633 }
1634
1635 done:
1636 fdisk_free_iter(itr);
1637 fdisk_unref_table(tb);
1638
1639 /*fdisk_write_disklabel(cxt);*/
1640 fdisk_unref_context(cxt);
1641 return 0;
1642 }
1643
1644 static int test_tokens(struct fdisk_test *ts, int argc, char *argv[])
1645 {
1646 char *p, *str = argc == 2 ? strdup(argv[1]) : NULL;
1647 int i;
1648
1649 for (i = 1, p = str; p && *p; i++) {
1650 char *tk = next_token(&p);
1651
1652 if (!tk)
1653 break;
1654
1655 printf("#%d: '%s'\n", i, tk);
1656 }
1657
1658 free(str);
1659 return 0;
1660 }
1661
1662 int main(int argc, char *argv[])
1663 {
1664 struct fdisk_test tss[] = {
1665 { "--dump", test_dump, "<device> dump PT as script" },
1666 { "--read", test_read, "<file> read PT script from file" },
1667 { "--apply", test_apply, "<device> <file> try apply script from file to device" },
1668 { "--stdin", test_stdin, " read input like sfdisk" },
1669 { "--tokens", test_tokens, "<string> parse string" },
1670 { NULL }
1671 };
1672
1673 return fdisk_run_test(tss, argc, argv);
1674 }
1675
1676 #endif