4 #include "carefulputc.h"
10 * @short_description: complex way to create and dump partition table
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.
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
21 * For more details about script format see sfdisk man page.
23 * There are four ways how to build the script:
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()
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().
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).
39 /* script header (e.g. unit: sectors) */
40 struct fdisk_scriptheader
{
41 struct list_head headers
;
46 /* script control struct */
48 struct fdisk_table
*table
;
49 struct list_head headers
;
50 struct fdisk_context
*cxt
;
53 char *(*fn_fgets
)(struct fdisk_script
*, char *, size_t, FILE *);
58 struct fdisk_label
*label
;
60 unsigned int json
: 1, /* JSON output */
61 force_label
: 1; /* label: <name> specified */
64 static struct fdisk_parttype
*translate_type_shortcuts(struct fdisk_script
*dp
, char *str
);
67 static void fdisk_script_free_header(struct fdisk_scriptheader
*fi
)
72 DBG(SCRIPT
, ul_debugobj(fi
, "free header %s", fi
->name
));
75 list_del(&fi
->headers
);
83 * The script hold fdisk_table and additional information to read/write
86 * Returns: newly allocated script struct.
88 struct fdisk_script
*fdisk_new_script(struct fdisk_context
*cxt
)
90 struct fdisk_script
*dp
= NULL
;
92 dp
= calloc(1, sizeof(*dp
));
96 DBG(SCRIPT
, ul_debugobj(dp
, "alloc"));
99 fdisk_ref_context(cxt
);
101 INIT_LIST_HEAD(&dp
->headers
);
106 * fdisk_new_script_from_file:
108 * @filename: path to the script file
110 * Allocates a new script and reads script from @filename.
112 * Returns: new script instance or NULL in case of error (check errno for more details).
114 struct fdisk_script
*fdisk_new_script_from_file(struct fdisk_context
*cxt
,
115 const char *filename
)
119 struct fdisk_script
*dp
, *res
= NULL
;
124 DBG(SCRIPT
, ul_debug("opening %s", filename
));
125 f
= fopen(filename
, "r");
129 dp
= fdisk_new_script(cxt
);
133 rc
= fdisk_script_read_file(dp
, f
);
143 fdisk_unref_script(dp
);
152 * @dp: script pointer
154 * Increments reference counter.
156 void fdisk_ref_script(struct fdisk_script
*dp
)
162 static void fdisk_reset_script(struct fdisk_script
*dp
)
166 DBG(SCRIPT
, ul_debugobj(dp
, "reset"));
169 fdisk_reset_table(dp
->table
);
171 while (!list_empty(&dp
->headers
)) {
172 struct fdisk_scriptheader
*fi
= list_entry(dp
->headers
.next
,
173 struct fdisk_scriptheader
, headers
);
174 fdisk_script_free_header(fi
);
176 INIT_LIST_HEAD(&dp
->headers
);
180 * fdisk_unref_script:
181 * @dp: script pointer
183 * Decrements reference counter, on zero the @dp is automatically
186 void fdisk_unref_script(struct fdisk_script
*dp
)
192 if (dp
->refcount
<= 0) {
193 fdisk_reset_script(dp
);
194 fdisk_unref_context(dp
->cxt
);
195 DBG(SCRIPT
, ul_debugobj(dp
, "free script"));
201 * fdisk_script_set_userdata
205 * Sets data usable for example in callbacks (e.g fdisk_script_set_fgets()).
207 * Returns: 0 on success, <0 on error.
209 int fdisk_script_set_userdata(struct fdisk_script
*dp
, void *data
)
217 * fdisk_script_get_userdata
220 * Returns: user data or NULL.
222 void *fdisk_script_get_userdata(struct fdisk_script
*dp
)
228 static struct fdisk_scriptheader
*script_get_header(struct fdisk_script
*dp
,
233 list_for_each(p
, &dp
->headers
) {
234 struct fdisk_scriptheader
*fi
= list_entry(p
, struct fdisk_scriptheader
, headers
);
236 if (strcasecmp(fi
->name
, name
) == 0)
244 * fdisk_script_get_header:
245 * @dp: script instance
248 * Returns: pointer to header data or NULL.
250 const char *fdisk_script_get_header(struct fdisk_script
*dp
, const char *name
)
252 struct fdisk_scriptheader
*fi
;
257 fi
= script_get_header(dp
, name
);
258 return fi
? fi
->data
: NULL
;
262 * fdisk_script_set_header:
263 * @dp: script instance
265 * @data: header data (or NULL)
267 * The headers are used as global options for whole partition
268 * table, always one header per line.
270 * If no @data is specified then the header is removed. If header does not exist
271 * and @data is specified then a new header is added.
273 * Note that libfdisk allows to specify arbitrary custom header, the default
274 * built-in headers are "unit" and "label", and some label specific headers
275 * (for example "uuid" and "name" for GPT).
277 * Returns: 0 on success, <0 on error
279 int fdisk_script_set_header(struct fdisk_script
*dp
,
283 struct fdisk_scriptheader
*fi
;
288 fi
= script_get_header(dp
, name
);
290 return 0; /* want to remove header that does not exist, success */
293 DBG(SCRIPT
, ul_debugobj(dp
, "freeing header %s", name
));
295 /* no data, remove the header */
296 fdisk_script_free_header(fi
);
301 DBG(SCRIPT
, ul_debugobj(dp
, "setting new header %s='%s'", name
, data
));
304 fi
= calloc(1, sizeof(*fi
));
307 INIT_LIST_HEAD(&fi
->headers
);
308 fi
->name
= strdup(name
);
309 fi
->data
= strdup(data
);
310 if (!fi
->data
|| !fi
->name
) {
311 fdisk_script_free_header(fi
);
314 list_add_tail(&fi
->headers
, &dp
->headers
);
316 /* update existing */
317 char *x
= strdup(data
);
319 DBG(SCRIPT
, ul_debugobj(dp
, "update '%s' header '%s' -> '%s'", name
, fi
->data
, data
));
327 if (strcmp(name
, "label") == 0)
334 * fdisk_script_get_table:
337 * The table represents partitions holded by the script. The table is possible to
338 * fill by fdisk_script_read_context() or fdisk_script_read_file(). All the "read"
339 * functions remove old partitions from the table. See also fdisk_script_set_table().
341 * Returns: NULL or script table.
343 struct fdisk_table
*fdisk_script_get_table(struct fdisk_script
*dp
)
349 * Make sure user has access to the same table as script. If
350 * there is no table then create a new one and reuse it later.
352 dp
->table
= fdisk_new_table();
358 * fdisk_script_set_table:
362 * Replaces table used by script and creates a new reference to @tb. This
363 * function allows to generate a new script table independently on the current
364 * context and without any file reading.
366 * This is useful for example to create partition table with the same basic
367 * settings (e.g. label-id, ...) but with different partitions -- just call
368 * fdisk_script_read_context() to get current settings and then
369 * fdisk_script_set_table() to set a different layout.
371 * If @tb is NULL then the current script table is unreferenced.
373 * Note that script read_ functions (e.g. fdisk_script_read_context()) create
374 * always a new script table.
376 * Returns: 0 on success, <0 on error
378 int fdisk_script_set_table(struct fdisk_script
*dp
, struct fdisk_table
*tb
)
384 fdisk_unref_table(dp
->table
);
387 DBG(SCRIPT
, ul_debugobj(dp
, "table replaced"));
391 static struct fdisk_label
*script_get_label(struct fdisk_script
*dp
)
397 dp
->label
= fdisk_get_label(dp
->cxt
,
398 fdisk_script_get_header(dp
, "label"));
399 DBG(SCRIPT
, ul_debugobj(dp
, "label '%s'", dp
->label
? dp
->label
->name
: ""));
405 * fdisk_script_get_nlines:
408 * Returns: number of parsed lines or <0 on error.
410 int fdisk_script_get_nlines(struct fdisk_script
*dp
)
417 * fdisk_script_has_force_label:
420 * Label has been explicitly specified in the script.
424 * Returns: true if "label: name" has been parsed.
426 int fdisk_script_has_force_label(struct fdisk_script
*dp
)
429 return dp
->force_label
;
434 * fdisk_script_read_context:
438 * Reads data from the @cxt context (on disk partition table) into the script.
439 * If the context is not specified then defaults to context used for fdisk_new_script().
441 * Return: 0 on success, <0 on error.
443 int fdisk_script_read_context(struct fdisk_script
*dp
, struct fdisk_context
*cxt
)
445 struct fdisk_label
*lb
;
449 if (!dp
|| (!cxt
&& !dp
->cxt
))
455 DBG(SCRIPT
, ul_debugobj(dp
, "reading context into script"));
456 fdisk_reset_script(dp
);
458 lb
= fdisk_get_label(cxt
, NULL
);
462 /* allocate (if not yet) and fill table */
463 rc
= fdisk_get_partitions(cxt
, &dp
->table
);
467 /* generate headers */
468 rc
= fdisk_script_set_header(dp
, "label", fdisk_label_get_name(lb
));
470 if (!rc
&& fdisk_get_disklabel_id(cxt
, &p
) == 0 && p
) {
471 rc
= fdisk_script_set_header(dp
, "label-id", p
);
474 if (!rc
&& cxt
->dev_path
)
475 rc
= fdisk_script_set_header(dp
, "device", cxt
->dev_path
);
477 rc
= fdisk_script_set_header(dp
, "unit", "sectors");
479 if (!rc
&& fdisk_is_label(cxt
, GPT
)) {
480 struct fdisk_labelitem item
= FDISK_LABELITEM_INIT
;
484 rc
= fdisk_get_disklabel_item(cxt
, GPT_LABELITEM_FIRSTLBA
, &item
);
486 snprintf(buf
, sizeof(buf
), "%"PRIu64
, item
.data
.num64
);
487 rc
= fdisk_script_set_header(dp
, "first-lba", buf
);
492 rc
= fdisk_get_disklabel_item(cxt
, GPT_LABELITEM_LASTLBA
, &item
);
494 snprintf(buf
, sizeof(buf
), "%"PRIu64
, item
.data
.num64
);
495 rc
= fdisk_script_set_header(dp
, "last-lba", buf
);
500 size_t n
= fdisk_get_npartitions(cxt
);
501 if (n
!= FDISK_GPT_NPARTITIONS_DEFAULT
) {
502 snprintf(buf
, sizeof(buf
), "%zu", n
);
503 rc
= fdisk_script_set_header(dp
, "table-length", buf
);
508 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
);
516 DBG(SCRIPT
, ul_debugobj(dp
, "read context done [rc=%d]", rc
));
521 * fdisk_script_enable_json:
525 * Disable/Enable JSON output format.
527 * Returns: 0 on success, <0 on error.
529 int fdisk_script_enable_json(struct fdisk_script
*dp
, int json
)
537 static void fput_indent(int indent
, FILE *f
)
541 for (i
= 0; i
<= indent
; i
++)
545 static void fput_var_separator(int *nvars
, FILE *f
)
552 static int write_file_json(struct fdisk_script
*dp
, FILE *f
)
555 struct fdisk_partition
*pa
;
556 struct fdisk_iter itr
;
557 const char *devname
= NULL
;
558 int ct
= 0, indent
= 0;
563 DBG(SCRIPT
, ul_debugobj(dp
, "writing json dump to file"));
567 fput_indent(indent
, f
);
568 fputs("\"partitiontable\": {\n", f
);
572 list_for_each(h
, &dp
->headers
) {
573 struct fdisk_scriptheader
*fi
= list_entry(h
, struct fdisk_scriptheader
, headers
);
574 const char *name
= fi
->name
;
577 if (strcmp(name
, "first-lba") == 0) {
580 } else if (strcmp(name
, "last-lba") == 0) {
583 } else if (strcmp(name
, "label-id") == 0)
586 fput_indent(indent
, f
);
587 fputs_quoted_json_lower(name
, f
);
590 fputs_quoted_json(fi
->data
, f
);
594 if (!dp
->table
&& fi
== list_last_entry(&dp
->headers
, struct fdisk_scriptheader
, headers
))
599 if (strcmp(name
, "device") == 0)
604 if (!dp
->table
|| fdisk_table_is_empty(dp
->table
)) {
605 DBG(SCRIPT
, ul_debugobj(dp
, "script table empty"));
609 DBG(SCRIPT
, ul_debugobj(dp
, "%zu entries", fdisk_table_get_nents(dp
->table
)));
611 fput_indent(indent
, f
);
612 fputs("\"partitions\": [\n", f
);
615 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
616 while (fdisk_table_next_partition(dp
->table
, &itr
, &pa
) == 0) {
621 fput_indent(indent
, f
);
624 p
= fdisk_partname(devname
, pa
->partno
+ 1);
626 DBG(SCRIPT
, ul_debugobj(dp
, "write %s entry", p
));
627 fputs("\"node\":", f
);
628 fputs_quoted_json(p
, f
);
632 if (fdisk_partition_has_start(pa
)) {
633 fput_var_separator(&nvars
, f
);
634 fprintf(f
, "\"start\":%ju", (uintmax_t)pa
->start
);
636 if (fdisk_partition_has_size(pa
)) {
637 fput_var_separator(&nvars
, f
);
638 fprintf(f
, "\"size\":%ju", (uintmax_t)pa
->size
);
640 if (pa
->type
&& fdisk_parttype_get_string(pa
->type
)) {
641 fput_var_separator(&nvars
, f
);
642 fputs("\"type\":", f
);
643 fputs_quoted_json(fdisk_parttype_get_string(pa
->type
), f
);
644 } else if (pa
->type
) {
645 fput_var_separator(&nvars
, f
);
646 fprintf(f
, "\"type\":\"%x\"", fdisk_parttype_get_code(pa
->type
));
650 fput_var_separator(&nvars
, f
);
651 fputs("\"uuid\":", f
);
652 fputs_quoted_json(pa
->uuid
, f
);
654 if (pa
->name
&& *pa
->name
) {
655 fput_var_separator(&nvars
, f
);
656 fputs("\"name\":", f
),
657 fputs_quoted_json(pa
->name
, f
);
660 /* for MBR attr=80 means bootable */
662 struct fdisk_label
*lb
= script_get_label(dp
);
664 if (!lb
|| fdisk_label_get_type(lb
) != FDISK_DISKLABEL_DOS
) {
665 fput_var_separator(&nvars
, f
);
666 fputs("\"attrs\":", f
);
667 fputs_quoted_json(pa
->attrs
, f
);
670 if (fdisk_partition_is_bootable(pa
)) {
671 fput_var_separator(&nvars
, f
);
672 fprintf(f
, "\"bootable\":true");
675 if ((size_t)ct
< fdisk_table_get_nents(dp
->table
))
682 fput_indent(indent
, f
);
686 fput_indent(indent
, f
);
689 DBG(SCRIPT
, ul_debugobj(dp
, "write script done"));
693 static int write_file_sfdisk(struct fdisk_script
*dp
, FILE *f
)
696 struct fdisk_partition
*pa
;
697 struct fdisk_iter itr
;
698 const char *devname
= NULL
;
703 DBG(SCRIPT
, ul_debugobj(dp
, "writing sfdisk-like script to file"));
706 list_for_each(h
, &dp
->headers
) {
707 struct fdisk_scriptheader
*fi
= list_entry(h
, struct fdisk_scriptheader
, headers
);
708 fprintf(f
, "%s: %s\n", fi
->name
, fi
->data
);
709 if (strcmp(fi
->name
, "device") == 0)
713 if (!dp
->table
|| fdisk_table_is_empty(dp
->table
)) {
714 DBG(SCRIPT
, ul_debugobj(dp
, "script table empty"));
718 DBG(SCRIPT
, ul_debugobj(dp
, "%zu entries", fdisk_table_get_nents(dp
->table
)));
722 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
723 while (fdisk_table_next_partition(dp
->table
, &itr
, &pa
) == 0) {
727 p
= fdisk_partname(devname
, pa
->partno
+ 1);
729 DBG(SCRIPT
, ul_debugobj(dp
, "write %s entry", p
));
730 fprintf(f
, "%s :", p
);
732 fprintf(f
, "%zu :", pa
->partno
+ 1);
734 if (fdisk_partition_has_start(pa
))
735 fprintf(f
, " start=%12ju", (uintmax_t)pa
->start
);
736 if (fdisk_partition_has_size(pa
))
737 fprintf(f
, ", size=%12ju", (uintmax_t)pa
->size
);
739 if (pa
->type
&& fdisk_parttype_get_string(pa
->type
))
740 fprintf(f
, ", type=%s", fdisk_parttype_get_string(pa
->type
));
742 fprintf(f
, ", type=%x", fdisk_parttype_get_code(pa
->type
));
745 fprintf(f
, ", uuid=%s", pa
->uuid
);
746 if (pa
->name
&& *pa
->name
) {
748 fputs_quoted(pa
->name
, f
);
751 /* for MBR attr=80 means bootable */
753 struct fdisk_label
*lb
= script_get_label(dp
);
755 if (!lb
|| fdisk_label_get_type(lb
) != FDISK_DISKLABEL_DOS
)
756 fprintf(f
, ", attrs=\"%s\"", pa
->attrs
);
758 if (fdisk_partition_is_bootable(pa
))
759 fprintf(f
, ", bootable");
763 DBG(SCRIPT
, ul_debugobj(dp
, "write script done"));
768 * fdisk_script_write_file:
772 * Writes script @dp to the file @f.
774 * Returns: 0 on success, <0 on error.
776 int fdisk_script_write_file(struct fdisk_script
*dp
, FILE *f
)
781 return write_file_json(dp
, f
);
783 return write_file_sfdisk(dp
, f
);
786 static inline int is_header_line(const char *s
)
788 const char *p
= strchr(s
, ':');
790 if (!p
|| p
== s
|| !*(p
+ 1) || strchr(s
, '='))
796 /* parses "<name>: value", note modifies @s*/
797 static int parse_line_header(struct fdisk_script
*dp
, char *s
)
802 DBG(SCRIPT
, ul_debugobj(dp
, " parse header '%s'", s
));
808 value
= strchr(s
, ':');
814 ltrim_whitespace((unsigned char *) name
);
815 rtrim_whitespace((unsigned char *) name
);
816 ltrim_whitespace((unsigned char *) value
);
817 rtrim_whitespace((unsigned char *) value
);
819 if (strcmp(name
, "label") == 0) {
820 if (dp
->cxt
&& !fdisk_get_label(dp
->cxt
, value
))
821 goto done
; /* unknown label name */
823 } else if (strcmp(name
, "unit") == 0) {
824 if (strcmp(value
, "sectors") != 0)
825 goto done
; /* only "sectors" supported */
826 } else if (strcmp(name
, "label-id") == 0
827 || strcmp(name
, "device") == 0
828 || strcmp(name
, "grain") == 0
829 || strcmp(name
, "first-lba") == 0
830 || strcmp(name
, "last-lba") == 0
831 || strcmp(name
, "table-length") == 0) {
832 ; /* whatever is possible */
834 goto done
; /* unknown header */
837 rc
= fdisk_script_set_header(dp
, name
, value
);
840 DBG(SCRIPT
, ul_debugobj(dp
, "header parse error: "
841 "[rc=%d, name='%s', value='%s']",
847 /* returns zero terminated string with next token and @str is updated */
848 static char *next_token(char **str
)
850 char *tk_begin
= NULL
,
854 int open_quote
= 0, terminated
= 0;
856 for (p
= *str
; p
&& *p
; p
++) {
860 tk_begin
= *p
== '"' ? p
+ 1 : p
;
866 if (isblank(*p
) || *p
== ',' || *p
== ';' || *p
== '"' )
868 else if (*(p
+ 1) == '\0')
870 if (tk_begin
&& tk_end
)
879 /* skip closing quotes */
883 /* token is terminated by blank (or blank is before "," or ";") */
885 end
= (char *) skip_blank(end
);
889 /* token is terminated by "," or ";" */
890 if (*end
== ',' || *end
== ';') {
894 /* token is terminated by \0 */
899 DBG(SCRIPT
, ul_debug("unterminated token '%s'", end
));
903 /* skip extra space after terminator */
904 end
= (char *) skip_blank(end
);
911 static int next_number(char **s
, uint64_t *num
, int *power
)
921 rc
= parse_size(tk
, (uintmax_t *) num
, power
);
925 static int next_string(char **s
, char **str
)
936 rc
= !*str
? -ENOMEM
: 0;
941 static int partno_from_devname(char *s
)
947 sz
= rtrim_whitespace((unsigned char *)s
);
950 while (p
> s
&& isdigit(*(p
- 1)))
954 pno
= strtol(p
, &end
, 10);
955 if (errno
|| !end
|| p
== end
)
961 * <device>: start=<num>, size=<num>, type=<string>, ...
963 static int parse_line_nameval(struct fdisk_script
*dp
, char *s
)
966 struct fdisk_partition
*pa
;
975 DBG(SCRIPT
, ul_debugobj(dp
, " parse script line: '%s'", s
));
977 pa
= fdisk_new_partition();
981 fdisk_partition_start_follow_default(pa
, 1);
982 fdisk_partition_end_follow_default(pa
, 1);
983 fdisk_partition_partno_follow_default(pa
, 1);
988 if (p
&& (!x
|| p
< x
)) {
992 pno
= partno_from_devname(s
);
994 fdisk_partition_partno_follow_default(pa
, 0);
995 fdisk_partition_set_partno(pa
, pno
);
1000 while (rc
== 0 && p
&& *p
) {
1002 DBG(SCRIPT
, ul_debugobj(dp
, " parsing '%s'", p
));
1003 p
= (char *) skip_blank(p
);
1005 if (!strncasecmp(p
, "start=", 6)) {
1008 rc
= next_number(&p
, &num
, &pow
);
1010 if (pow
) /* specified as <num><suffix> */
1011 num
/= dp
->cxt
->sector_size
;
1012 fdisk_partition_set_start(pa
, num
);
1013 fdisk_partition_start_follow_default(pa
, 0);
1015 } else if (!strncasecmp(p
, "size=", 5)) {
1019 rc
= next_number(&p
, &num
, &pow
);
1021 if (pow
) /* specified as <num><suffix> */
1022 num
/= dp
->cxt
->sector_size
;
1023 else /* specified as number of sectors */
1024 fdisk_partition_size_explicit(pa
, 1);
1025 fdisk_partition_set_size(pa
, num
);
1026 fdisk_partition_end_follow_default(pa
, 0);
1029 } else if (!strncasecmp(p
, "bootable", 8)) {
1030 /* we use next_token() to skip possible extra space */
1031 char *tk
= next_token(&p
);
1032 if (tk
&& strcasecmp(tk
, "bootable") == 0)
1037 } else if (!strncasecmp(p
, "attrs=", 6)) {
1039 rc
= next_string(&p
, &pa
->attrs
);
1041 } else if (!strncasecmp(p
, "uuid=", 5)) {
1043 rc
= next_string(&p
, &pa
->uuid
);
1045 } else if (!strncasecmp(p
, "name=", 5)) {
1047 rc
= next_string(&p
, &pa
->name
);
1048 unhexmangle_string(pa
->name
);
1050 } else if (!strncasecmp(p
, "type=", 5) ||
1051 !strncasecmp(p
, "Id=", 3)) { /* backward compatibility */
1054 p
+= ((*p
== 'I' || *p
== 'i') ? 3 : 5); /* "Id=", "type=" */
1056 rc
= next_string(&p
, &type
);
1060 pa
->type
= translate_type_shortcuts(dp
, type
);
1062 pa
->type
= fdisk_label_parse_parttype(
1063 script_get_label(dp
), type
);
1068 fdisk_unref_parttype(pa
->type
);
1074 DBG(SCRIPT
, ul_debugobj(dp
, "script parse error: unknown field '%s'", p
));
1081 rc
= fdisk_table_add_partition(dp
->table
, pa
);
1083 DBG(SCRIPT
, ul_debugobj(dp
, "script parse error: [rc=%d]", rc
));
1085 fdisk_unref_partition(pa
);
1089 /* original sfdisk supports partition types shortcuts like 'L' = Linux native
1091 static struct fdisk_parttype
*translate_type_shortcuts(struct fdisk_script
*dp
, char *str
)
1093 struct fdisk_label
*lb
;
1094 const char *type
= NULL
;
1096 if (strlen(str
) != 1)
1099 lb
= script_get_label(dp
);
1103 if (lb
->id
== FDISK_DISKLABEL_DOS
) {
1105 case 'L': /* Linux */
1108 case 'S': /* Swap */
1111 case 'E': /* Dos extended */
1114 case 'X': /* Linux extended */
1117 case 'U': /* UEFI system */
1120 case 'R': /* Linux RAID */
1128 } else if (lb
->id
== FDISK_DISKLABEL_GPT
) {
1130 case 'L': /* Linux */
1131 type
= "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
1133 case 'S': /* Swap */
1134 type
= "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F";
1136 case 'H': /* Home */
1137 type
= "933AC7E1-2EB4-4F13-B844-0E14E2AEF915";
1139 case 'U': /* UEFI system */
1140 type
= "C12A7328-F81F-11D2-BA4B-00A0C93EC93B";
1142 case 'R': /* Linux RAID */
1143 type
= "A19D880F-05FC-4D3B-A006-743F0F84911E";
1146 type
= "E6D6D379-F507-44C2-A23C-238F2A3DF928";
1151 return type
? fdisk_label_parse_parttype(lb
, type
) : NULL
;
1157 #define alone_sign(_sign, _p) (_sign && (*_p == '\0' || isblank(*_p)))
1160 * <start>, <size>, <type>, <bootable>, ...
1162 static int parse_line_valcommas(struct fdisk_script
*dp
, char *s
)
1166 struct fdisk_partition
*pa
;
1167 enum { ITEM_START
, ITEM_SIZE
, ITEM_TYPE
, ITEM_BOOTABLE
};
1174 pa
= fdisk_new_partition();
1178 fdisk_partition_start_follow_default(pa
, 1);
1179 fdisk_partition_end_follow_default(pa
, 1);
1180 fdisk_partition_partno_follow_default(pa
, 1);
1182 while (rc
== 0 && p
&& *p
) {
1187 p
= (char *) skip_blank(p
);
1190 if (item
!= ITEM_BOOTABLE
) {
1191 sign
= *p
== '-' ? TK_MINUS
: *p
== '+' ? TK_PLUS
: 0;
1196 DBG(SCRIPT
, ul_debugobj(dp
, " parsing item %d ('%s')", item
, p
));
1201 if (*p
== ',' || *p
== ';' || alone_sign(sign
, p
))
1202 fdisk_partition_start_follow_default(pa
, 1);
1206 rc
= next_number(&p
, &num
, &pow
);
1208 if (pow
) /* specified as <num><suffix> */
1209 num
/= dp
->cxt
->sector_size
;
1210 fdisk_partition_set_start(pa
, num
);
1211 pa
->movestart
= sign
== TK_MINUS
? FDISK_MOVE_DOWN
:
1212 sign
== TK_PLUS
? FDISK_MOVE_UP
:
1215 fdisk_partition_start_follow_default(pa
, 0);
1219 if (*p
== ',' || *p
== ';' || alone_sign(sign
, p
)) {
1220 fdisk_partition_end_follow_default(pa
, 1);
1221 if (sign
== TK_PLUS
)
1222 /* '+' alone means use all possible space, '-' alone means nothing */
1223 pa
->resize
= FDISK_RESIZE_ENLARGE
;
1226 rc
= next_number(&p
, &num
, &pow
);
1228 if (pow
) /* specified as <size><suffix> */
1229 num
/= dp
->cxt
->sector_size
;
1230 else /* specified as number of sectors */
1231 fdisk_partition_size_explicit(pa
, 1);
1232 fdisk_partition_set_size(pa
, num
);
1233 pa
->resize
= sign
== TK_MINUS
? FDISK_RESIZE_REDUCE
:
1234 sign
== TK_PLUS
? FDISK_RESIZE_ENLARGE
:
1237 fdisk_partition_end_follow_default(pa
, 0);
1241 if (*p
== ',' || *p
== ';' || alone_sign(sign
, p
))
1242 break; /* use default type */
1244 rc
= next_string(&p
, &str
);
1248 pa
->type
= translate_type_shortcuts(dp
, str
);
1250 pa
->type
= fdisk_label_parse_parttype(
1251 script_get_label(dp
), str
);
1256 fdisk_unref_parttype(pa
->type
);
1262 if (*p
== ',' || *p
== ';')
1265 char *tk
= next_token(&p
);
1266 if (tk
&& *tk
== '*' && *(tk
+ 1) == '\0')
1268 else if (tk
&& *tk
== '-' && *(tk
+ 1) == '\0')
1270 else if (tk
&& *tk
== '+' && *(tk
+ 1) == '\0')
1285 rc
= fdisk_table_add_partition(dp
->table
, pa
);
1287 DBG(SCRIPT
, ul_debugobj(dp
, "script parse error: [rc=%d]", rc
));
1289 fdisk_unref_partition(pa
);
1294 static int fdisk_script_read_buffer(struct fdisk_script
*dp
, char *s
)
1301 DBG(SCRIPT
, ul_debugobj(dp
, " parsing buffer"));
1303 s
= (char *) skip_blank(s
);
1305 return 0; /* nothing baby, ignore */
1307 if (!dp
->table
&& fdisk_script_get_table(dp
) == NULL
)
1310 /* parse header lines only if no partition specified yet */
1311 if (fdisk_table_is_empty(dp
->table
) && is_header_line(s
))
1312 rc
= parse_line_header(dp
, s
);
1314 /* parse script format */
1315 else if (strchr(s
, '='))
1316 rc
= parse_line_nameval(dp
, s
);
1318 /* parse simple <value>, ... format */
1320 rc
= parse_line_valcommas(dp
, s
);
1323 DBG(SCRIPT
, ul_debugobj(dp
, "%zu: parse error [rc=%d]",
1329 * fdisk_script_set_fgets:
1331 * @fn_fgets: callback function
1333 * The library uses fgets() function to read the next line from the script.
1334 * This default maybe overridden by another function. Note that the function has
1335 * to return the line terminated by \n (for example readline(3) removes \n).
1337 * Return: 0 on success, <0 on error
1339 int fdisk_script_set_fgets(struct fdisk_script
*dp
,
1340 char *(*fn_fgets
)(struct fdisk_script
*, char *, size_t, FILE *))
1344 dp
->fn_fgets
= fn_fgets
;
1349 * fdisk_script_read_line:
1352 * @buf: buffer to store one line of the file
1353 * @bufsz: buffer size
1355 * Reads next line into dump.
1357 * Returns: 0 on success, <0 on error, 1 when nothing to read.
1359 int fdisk_script_read_line(struct fdisk_script
*dp
, FILE *f
, char *buf
, size_t bufsz
)
1366 DBG(SCRIPT
, ul_debugobj(dp
, " parsing line %zu", dp
->nlines
));
1368 /* read the next non-blank non-comment line */
1371 if (dp
->fn_fgets(dp
, buf
, bufsz
, f
) == NULL
)
1373 } else if (fgets(buf
, bufsz
, f
) == NULL
)
1377 s
= strchr(buf
, '\n');
1379 /* Missing final newline? Otherwise an extremely */
1380 /* long line - assume file was corrupted */
1382 DBG(SCRIPT
, ul_debugobj(dp
, "no final newline"));
1383 s
= strchr(buf
, '\0');
1385 DBG(SCRIPT
, ul_debugobj(dp
,
1386 "%zu: missing newline at line", dp
->nlines
));
1392 if (--s
>= buf
&& *s
== '\r')
1394 s
= (char *) skip_blank(buf
);
1395 } while (*s
== '\0' || *s
== '#');
1397 return fdisk_script_read_buffer(dp
, s
);
1402 * fdisk_script_read_file:
1406 * Reads file @f into script @dp.
1408 * Returns: 0 on success, <0 on error.
1410 int fdisk_script_read_file(struct fdisk_script
*dp
, FILE *f
)
1418 DBG(SCRIPT
, ul_debugobj(dp
, "parsing file"));
1421 rc
= fdisk_script_read_line(dp
, f
, buf
, sizeof(buf
));
1427 rc
= 0; /* end of file */
1429 DBG(SCRIPT
, ul_debugobj(dp
, "parsing file done [rc=%d]", rc
));
1436 * @dp: script (or NULL to remove previous reference)
1438 * Sets reference to the @dp script and remove reference to the previously used
1441 * The script headers might be used by label drivers to overwrite
1442 * built-in defaults (for example disk label Id) and label driver might
1443 * optimize the default semantic to be more usable for scripts (for example to
1444 * not ask for primary/logical/extended partition type).
1446 * Note that script also contains reference to the fdisk context (see
1447 * fdisk_new_script()). This context may be completely independent on
1448 * context used for fdisk_set_script().
1450 * Returns: <0 on error, 0 on success.
1452 int fdisk_set_script(struct fdisk_context
*cxt
, struct fdisk_script
*dp
)
1458 fdisk_unref_script(cxt
->script
);
1463 DBG(CXT
, ul_debugobj(cxt
, "setting reference to script %p", cxt
->script
));
1464 fdisk_ref_script(cxt
->script
);
1474 * Returns: the current script or NULL.
1476 struct fdisk_script
*fdisk_get_script(struct fdisk_context
*cxt
)
1483 * fdisk_apply_script_headers:
1487 * Associate context @cxt with script @dp and creates a new empty disklabel.
1488 * The script may be later unreference by fdisk_set_script() with NULL as script.
1490 * Returns: 0 on success, <0 on error.
1492 int fdisk_apply_script_headers(struct fdisk_context
*cxt
, struct fdisk_script
*dp
)
1501 DBG(SCRIPT
, ul_debugobj(dp
, "applying script headers"));
1502 fdisk_set_script(cxt
, dp
);
1504 str
= fdisk_script_get_header(dp
, "grain");
1508 rc
= parse_size(str
, &sz
, NULL
);
1510 rc
= fdisk_save_user_grain(cxt
, sz
);
1515 if (fdisk_has_user_device_properties(cxt
))
1516 fdisk_apply_user_device_properties(cxt
);
1518 /* create empty label */
1519 name
= fdisk_script_get_header(dp
, "label");
1523 rc
= fdisk_create_disklabel(cxt
, name
);
1527 str
= fdisk_script_get_header(dp
, "table-length");
1531 rc
= parse_size(str
, &sz
, NULL
);
1533 rc
= fdisk_gpt_set_npartitions(cxt
, sz
);
1540 * fdisk_apply_script:
1544 * This function creates a new disklabel and partition within context @cxt. You
1545 * have to call fdisk_write_disklabel() to apply changes to the device.
1547 * Returns: 0 on error, <0 on error.
1549 int fdisk_apply_script(struct fdisk_context
*cxt
, struct fdisk_script
*dp
)
1552 struct fdisk_script
*old
;
1557 DBG(CXT
, ul_debugobj(cxt
, "applying script %p", dp
));
1559 old
= fdisk_get_script(cxt
);
1560 fdisk_ref_script(old
);
1562 /* create empty disk label */
1563 rc
= fdisk_apply_script_headers(cxt
, dp
);
1565 /* create partitions */
1566 if (!rc
&& dp
->table
)
1567 rc
= fdisk_apply_table(cxt
, dp
->table
);
1569 fdisk_set_script(cxt
, old
);
1570 fdisk_unref_script(old
);
1572 DBG(CXT
, ul_debugobj(cxt
, "script done [rc=%d]", rc
));
1577 static int test_dump(struct fdisk_test
*ts
, int argc
, char *argv
[])
1579 char *devname
= argv
[1];
1580 struct fdisk_context
*cxt
;
1581 struct fdisk_script
*dp
;
1583 cxt
= fdisk_new_context();
1584 fdisk_assign_device(cxt
, devname
, 1);
1586 dp
= fdisk_new_script(cxt
);
1587 fdisk_script_read_context(dp
, NULL
);
1589 fdisk_script_write_file(dp
, stdout
);
1590 fdisk_unref_script(dp
);
1591 fdisk_unref_context(cxt
);
1596 static int test_read(struct fdisk_test
*ts
, int argc
, char *argv
[])
1598 char *filename
= argv
[1];
1599 struct fdisk_script
*dp
;
1600 struct fdisk_context
*cxt
;
1603 if (!(f
= fopen(filename
, "r")))
1604 err(EXIT_FAILURE
, "%s: cannot open", filename
);
1606 cxt
= fdisk_new_context();
1607 dp
= fdisk_new_script(cxt
);
1609 fdisk_script_read_file(dp
, f
);
1612 fdisk_script_write_file(dp
, stdout
);
1613 fdisk_unref_script(dp
);
1614 fdisk_unref_context(cxt
);
1619 static int test_stdin(struct fdisk_test
*ts
, int argc
, char *argv
[])
1622 struct fdisk_script
*dp
;
1623 struct fdisk_context
*cxt
;
1626 cxt
= fdisk_new_context();
1627 dp
= fdisk_new_script(cxt
);
1628 fdisk_script_set_header(dp
, "label", "dos");
1630 printf("<start>, <size>, <type>, <bootable: *|->\n");
1632 struct fdisk_partition
*pa
;
1633 size_t n
= dp
->table
? fdisk_table_get_nents(dp
->table
) : 0;
1635 printf(" #%zu :\n", n
+ 1);
1636 rc
= fdisk_script_read_line(dp
, stdin
, buf
, sizeof(buf
));
1639 pa
= fdisk_table_get_partition(dp
->table
, n
);
1640 printf(" #%zu %12ju %12ju\n", n
+ 1,
1641 (uintmax_t)fdisk_partition_get_start(pa
),
1642 (uintmax_t)fdisk_partition_get_size(pa
));
1647 fdisk_script_write_file(dp
, stdout
);
1648 fdisk_unref_script(dp
);
1649 fdisk_unref_context(cxt
);
1654 static int test_apply(struct fdisk_test
*ts
, int argc
, char *argv
[])
1656 char *devname
= argv
[1], *scriptname
= argv
[2];
1657 struct fdisk_context
*cxt
;
1658 struct fdisk_script
*dp
;
1659 struct fdisk_table
*tb
= NULL
;
1660 struct fdisk_iter
*itr
= NULL
;
1661 struct fdisk_partition
*pa
= NULL
;
1664 cxt
= fdisk_new_context();
1665 fdisk_assign_device(cxt
, devname
, 0);
1667 dp
= fdisk_new_script_from_file(cxt
, scriptname
);
1671 rc
= fdisk_apply_script(cxt
, dp
);
1674 fdisk_unref_script(dp
);
1677 fdisk_list_disklabel(cxt
);
1678 fdisk_get_partitions(cxt
, &tb
);
1680 itr
= fdisk_new_iter(FDISK_ITER_FORWARD
);
1681 while (fdisk_table_next_partition(tb
, itr
, &pa
) == 0) {
1682 printf(" #%zu %12ju %12ju\n", fdisk_partition_get_partno(pa
),
1683 (uintmax_t)fdisk_partition_get_start(pa
),
1684 (uintmax_t)fdisk_partition_get_size(pa
));
1688 fdisk_free_iter(itr
);
1689 fdisk_unref_table(tb
);
1691 /*fdisk_write_disklabel(cxt);*/
1692 fdisk_unref_context(cxt
);
1696 static int test_tokens(struct fdisk_test
*ts
, int argc
, char *argv
[])
1698 char *p
, *str
= argc
== 2 ? strdup(argv
[1]) : NULL
;
1701 for (i
= 1, p
= str
; p
&& *p
; i
++) {
1702 char *tk
= next_token(&p
);
1707 printf("#%d: '%s'\n", i
, tk
);
1714 int main(int argc
, char *argv
[])
1716 struct fdisk_test tss
[] = {
1717 { "--dump", test_dump
, "<device> dump PT as script" },
1718 { "--read", test_read
, "<file> read PT script from file" },
1719 { "--apply", test_apply
, "<device> <file> try apply script from file to device" },
1720 { "--stdin", test_stdin
, " read input like sfdisk" },
1721 { "--tokens", test_tokens
, "<string> parse string" },
1725 return fdisk_run_test(tss
, argc
, argv
);