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 void fdisk_script_free_header(struct fdisk_scriptheader
*fi
)
69 DBG(SCRIPT
, ul_debugobj(fi
, "free header %s", fi
->name
));
72 list_del(&fi
->headers
);
80 * The script hold fdisk_table and additional information to read/write
83 * Returns: newly allocated script struct.
85 struct fdisk_script
*fdisk_new_script(struct fdisk_context
*cxt
)
87 struct fdisk_script
*dp
= NULL
;
89 dp
= calloc(1, sizeof(*dp
));
93 DBG(SCRIPT
, ul_debugobj(dp
, "alloc"));
96 fdisk_ref_context(cxt
);
98 INIT_LIST_HEAD(&dp
->headers
);
103 * fdisk_new_script_from_file:
105 * @filename: path to the script file
107 * Allocates a new script and reads script from @filename.
109 * Returns: new script instance or NULL in case of error (check errno for more details).
111 struct fdisk_script
*fdisk_new_script_from_file(struct fdisk_context
*cxt
,
112 const char *filename
)
116 struct fdisk_script
*dp
, *res
= NULL
;
121 DBG(SCRIPT
, ul_debug("opening %s", filename
));
122 f
= fopen(filename
, "r");
126 dp
= fdisk_new_script(cxt
);
130 rc
= fdisk_script_read_file(dp
, f
);
140 fdisk_unref_script(dp
);
149 * @dp: script pointer
151 * Increments reference counter.
153 void fdisk_ref_script(struct fdisk_script
*dp
)
159 static void fdisk_reset_script(struct fdisk_script
*dp
)
163 DBG(SCRIPT
, ul_debugobj(dp
, "reset"));
166 fdisk_reset_table(dp
->table
);
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
);
173 INIT_LIST_HEAD(&dp
->headers
);
177 * fdisk_unref_script:
178 * @dp: script pointer
180 * Decrements reference counter, on zero the @dp is automatically
183 void fdisk_unref_script(struct fdisk_script
*dp
)
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"));
199 * fdisk_script_set_userdata
203 * Sets data usable for example in callbacks (e.g fdisk_script_set_fgets()).
205 * Returns: 0 on success, <0 on error.
207 int fdisk_script_set_userdata(struct fdisk_script
*dp
, void *data
)
215 * fdisk_script_get_userdata
218 * Returns: user data or NULL.
220 void *fdisk_script_get_userdata(struct fdisk_script
*dp
)
226 static struct fdisk_scriptheader
*script_get_header(struct fdisk_script
*dp
,
231 list_for_each(p
, &dp
->headers
) {
232 struct fdisk_scriptheader
*fi
= list_entry(p
, struct fdisk_scriptheader
, headers
);
234 if (strcasecmp(fi
->name
, name
) == 0)
242 * fdisk_script_get_header:
243 * @dp: script instance
246 * Returns: pointer to header data or NULL.
248 const char *fdisk_script_get_header(struct fdisk_script
*dp
, const char *name
)
250 struct fdisk_scriptheader
*fi
;
255 fi
= script_get_header(dp
, name
);
256 return fi
? fi
->data
: NULL
;
260 * fdisk_script_set_header:
261 * @dp: script instance
263 * @data: header data (or NULL)
265 * The headers are used as global options for whole partition
266 * table, always one header per line.
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.
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).
275 * Returns: 0 on success, <0 on error
277 int fdisk_script_set_header(struct fdisk_script
*dp
,
281 struct fdisk_scriptheader
*fi
;
286 fi
= script_get_header(dp
, name
);
288 return 0; /* want to remove header that does not exist, success */
291 DBG(SCRIPT
, ul_debugobj(dp
, "freeing header %s", name
));
293 /* no data, remove the header */
294 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
);
309 rc
= strdup_to_struct_member(fi
, name
, name
);
311 rc
= strdup_to_struct_member(fi
, data
, data
);
313 fdisk_script_free_header(fi
);
316 list_add_tail(&fi
->headers
, &dp
->headers
);
318 /* update existing */
319 char *x
= strdup(data
);
321 DBG(SCRIPT
, ul_debugobj(dp
, "update '%s' header '%s' -> '%s'", name
, fi
->data
, data
));
329 if (strcmp(name
, "label") == 0)
336 * fdisk_script_get_table:
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().
343 * Returns: NULL or script table.
345 struct fdisk_table
*fdisk_script_get_table(struct fdisk_script
*dp
)
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.
354 dp
->table
= fdisk_new_table();
360 * fdisk_script_set_table:
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.
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.
373 * If @tb is NULL then the current script table is unreferenced.
375 * Note that script read_ functions (e.g. fdisk_script_read_context()) create
376 * always a new script table.
378 * Returns: 0 on success, <0 on error
380 int fdisk_script_set_table(struct fdisk_script
*dp
, struct fdisk_table
*tb
)
386 fdisk_unref_table(dp
->table
);
389 DBG(SCRIPT
, ul_debugobj(dp
, "table replaced"));
393 static struct fdisk_label
*script_get_label(struct fdisk_script
*dp
)
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
: ""));
407 * fdisk_script_get_nlines:
410 * Returns: number of parsed lines or <0 on error.
412 int fdisk_script_get_nlines(struct fdisk_script
*dp
)
419 * fdisk_script_has_force_label:
422 * Label has been explicitly specified in the script.
426 * Returns: true if "label: name" has been parsed.
428 int fdisk_script_has_force_label(struct fdisk_script
*dp
)
431 return dp
->force_label
;
436 * fdisk_script_read_context:
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().
443 * Return: 0 on success, <0 on error.
445 int fdisk_script_read_context(struct fdisk_script
*dp
, struct fdisk_context
*cxt
)
447 struct fdisk_label
*lb
;
452 if (!dp
|| (!cxt
&& !dp
->cxt
))
458 DBG(SCRIPT
, ul_debugobj(dp
, "reading context into script"));
459 fdisk_reset_script(dp
);
461 lb
= fdisk_get_label(cxt
, NULL
);
465 /* allocate (if not yet) and fill table */
466 rc
= fdisk_get_partitions(cxt
, &dp
->table
);
470 /* generate headers */
471 rc
= fdisk_script_set_header(dp
, "label", fdisk_label_get_name(lb
));
473 if (!rc
&& fdisk_get_disklabel_id(cxt
, &p
) == 0 && p
) {
474 rc
= fdisk_script_set_header(dp
, "label-id", p
);
477 if (!rc
&& cxt
->dev_path
)
478 rc
= fdisk_script_set_header(dp
, "device", cxt
->dev_path
);
480 rc
= fdisk_script_set_header(dp
, "unit", "sectors");
482 if (!rc
&& fdisk_is_label(cxt
, GPT
)) {
483 struct fdisk_labelitem item
= FDISK_LABELITEM_INIT
;
486 rc
= fdisk_get_disklabel_item(cxt
, GPT_LABELITEM_FIRSTLBA
, &item
);
488 snprintf(buf
, sizeof(buf
), "%"PRIu64
, item
.data
.num64
);
489 rc
= fdisk_script_set_header(dp
, "first-lba", buf
);
494 rc
= fdisk_get_disklabel_item(cxt
, GPT_LABELITEM_LASTLBA
, &item
);
496 snprintf(buf
, sizeof(buf
), "%"PRIu64
, item
.data
.num64
);
497 rc
= fdisk_script_set_header(dp
, "last-lba", buf
);
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
);
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
);
516 snprintf(buf
, sizeof(buf
), "%lu", fdisk_get_sector_size(cxt
));
517 rc
= fdisk_script_set_header(dp
, "sector-size", buf
);
520 DBG(SCRIPT
, ul_debugobj(dp
, "read context done [rc=%d]", rc
));
525 * fdisk_script_enable_json:
529 * Disable/Enable JSON output format.
531 * Returns: 0 on success, <0 on error.
533 int fdisk_script_enable_json(struct fdisk_script
*dp
, int json
)
541 static void fput_indent(int indent
, FILE *f
)
545 for (i
= 0; i
<= indent
; i
++)
549 static void fput_var_separator(int *nvars
, FILE *f
)
556 static int write_file_json(struct fdisk_script
*dp
, FILE *f
)
559 struct fdisk_partition
*pa
;
560 struct fdisk_iter itr
;
561 const char *devname
= NULL
;
562 int ct
= 0, indent
= 0;
567 DBG(SCRIPT
, ul_debugobj(dp
, "writing json dump to file"));
571 fput_indent(indent
, f
);
572 fputs("\"partitiontable\": {\n", f
);
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
;
581 if (strcmp(name
, "first-lba") == 0) {
584 } else if (strcmp(name
, "last-lba") == 0) {
587 } else if (strcmp(name
, "sector-size") == 0) {
590 } else if (strcmp(name
, "label-id") == 0)
593 fput_indent(indent
, f
);
594 fputs_quoted_json_lower(name
, f
);
597 fputs_quoted_json(fi
->data
, f
);
601 if (!dp
->table
&& fi
== list_last_entry(&dp
->headers
, struct fdisk_scriptheader
, headers
))
606 if (strcmp(name
, "device") == 0)
611 if (!dp
->table
|| fdisk_table_is_empty(dp
->table
)) {
612 DBG(SCRIPT
, ul_debugobj(dp
, "script table empty"));
616 DBG(SCRIPT
, ul_debugobj(dp
, "%zu entries", fdisk_table_get_nents(dp
->table
)));
618 fput_indent(indent
, f
);
619 fputs("\"partitions\": [\n", f
);
622 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
623 while (fdisk_table_next_partition(dp
->table
, &itr
, &pa
) == 0) {
628 fput_indent(indent
, f
);
631 p
= fdisk_partname(devname
, pa
->partno
+ 1);
633 DBG(SCRIPT
, ul_debugobj(dp
, "write %s entry", p
));
634 fputs("\"node\":", f
);
635 fputs_quoted_json(p
, f
);
639 if (fdisk_partition_has_start(pa
)) {
640 fput_var_separator(&nvars
, f
);
641 fprintf(f
, "\"start\":%ju", (uintmax_t)pa
->start
);
643 if (fdisk_partition_has_size(pa
)) {
644 fput_var_separator(&nvars
, f
);
645 fprintf(f
, "\"size\":%ju", (uintmax_t)pa
->size
);
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
));
657 fput_var_separator(&nvars
, f
);
658 fputs("\"uuid\":", f
);
659 fputs_quoted_json(pa
->uuid
, f
);
661 if (pa
->name
&& *pa
->name
) {
662 fput_var_separator(&nvars
, f
);
663 fputs("\"name\":", f
),
664 fputs_quoted_json(pa
->name
, f
);
667 /* for MBR attr=80 means bootable */
669 struct fdisk_label
*lb
= script_get_label(dp
);
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
);
677 if (fdisk_partition_is_bootable(pa
)) {
678 fput_var_separator(&nvars
, f
);
679 fprintf(f
, "\"bootable\":true");
682 if ((size_t)ct
< fdisk_table_get_nents(dp
->table
))
689 fput_indent(indent
, f
);
693 fput_indent(indent
, f
);
696 DBG(SCRIPT
, ul_debugobj(dp
, "write script done"));
700 static int write_file_sfdisk(struct fdisk_script
*dp
, FILE *f
)
703 struct fdisk_partition
*pa
;
704 struct fdisk_iter itr
;
705 const char *devname
= NULL
;
710 DBG(SCRIPT
, ul_debugobj(dp
, "writing sfdisk-like script to file"));
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)
720 if (!dp
->table
|| fdisk_table_is_empty(dp
->table
)) {
721 DBG(SCRIPT
, ul_debugobj(dp
, "script table empty"));
725 DBG(SCRIPT
, ul_debugobj(dp
, "%zu entries", fdisk_table_get_nents(dp
->table
)));
729 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
730 while (fdisk_table_next_partition(dp
->table
, &itr
, &pa
) == 0) {
734 p
= fdisk_partname(devname
, pa
->partno
+ 1);
736 DBG(SCRIPT
, ul_debugobj(dp
, "write %s entry", p
));
737 fprintf(f
, "%s :", p
);
739 fprintf(f
, "%zu :", pa
->partno
+ 1);
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
);
746 if (pa
->type
&& fdisk_parttype_get_string(pa
->type
))
747 fprintf(f
, ", type=%s", fdisk_parttype_get_string(pa
->type
));
749 fprintf(f
, ", type=%x", fdisk_parttype_get_code(pa
->type
));
752 fprintf(f
, ", uuid=%s", pa
->uuid
);
753 if (pa
->name
&& *pa
->name
) {
755 fputs_quoted(pa
->name
, f
);
758 /* for MBR attr=80 means bootable */
760 struct fdisk_label
*lb
= script_get_label(dp
);
762 if (!lb
|| fdisk_label_get_type(lb
) != FDISK_DISKLABEL_DOS
)
763 fprintf(f
, ", attrs=\"%s\"", pa
->attrs
);
765 if (fdisk_partition_is_bootable(pa
))
766 fprintf(f
, ", bootable");
770 DBG(SCRIPT
, ul_debugobj(dp
, "write script done"));
775 * fdisk_script_write_file:
779 * Writes script @dp to the file @f.
781 * Returns: 0 on success, <0 on error.
783 int fdisk_script_write_file(struct fdisk_script
*dp
, FILE *f
)
788 return write_file_json(dp
, f
);
790 return write_file_sfdisk(dp
, f
);
793 static inline int is_header_line(const char *s
)
795 const char *p
= strchr(s
, ':');
797 if (!p
|| p
== s
|| !*(p
+ 1) || strchr(s
, '='))
803 /* parses "<name>: value", note modifies @s*/
804 static int parse_line_header(struct fdisk_script
*dp
, char *s
)
808 static const char *supported
[] = {
809 "label", "unit", "label-id", "device", "grain",
810 "first-lba", "last-lba", "table-length", "sector-size"
813 DBG(SCRIPT
, ul_debugobj(dp
, " parse header '%s'", s
));
819 value
= strchr(s
, ':');
825 ltrim_whitespace((unsigned char *) name
);
826 rtrim_whitespace((unsigned char *) name
);
827 ltrim_whitespace((unsigned char *) value
);
828 rtrim_whitespace((unsigned char *) value
);
830 if (!*name
|| !*value
)
833 /* check header name */
834 for (i
= 0; i
< ARRAY_SIZE(supported
); i
++) {
835 if (strcmp(name
, supported
[i
]) == 0)
838 if (i
== ARRAY_SIZE(supported
))
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 */
847 } else if (strcmp(name
, "unit") == 0) {
848 if (strcmp(value
, "sectors") != 0)
849 return -EINVAL
; /* only "sectors" supported */
853 return fdisk_script_set_header(dp
, name
, value
);
856 /* returns zero terminated string with next token and @str is updated */
857 static char *next_token(char **str
)
859 char *tk_begin
= NULL
,
863 int open_quote
= 0, terminated
= 0;
865 for (p
= *str
; p
&& *p
; p
++) {
869 tk_begin
= *p
== '"' ? p
+ 1 : p
;
875 if (isblank(*p
) || *p
== ',' || *p
== ';' || *p
== '"' )
877 else if (*(p
+ 1) == '\0')
879 if (tk_begin
&& tk_end
)
888 /* skip closing quotes */
892 /* token is terminated by blank (or blank is before "," or ";") */
894 end
= (char *) skip_blank(end
);
898 /* token is terminated by "," or ";" */
899 if (*end
== ',' || *end
== ';') {
903 /* token is terminated by \0 */
908 DBG(SCRIPT
, ul_debug("unterminated token '%s'", end
));
912 /* skip extra space after terminator */
913 end
= (char *) skip_blank(end
);
920 static int next_number(char **s
, uint64_t *num
, int *power
)
930 rc
= parse_size(tk
, (uintmax_t *) num
, power
);
934 static int next_string(char **s
, char **str
)
945 rc
= !*str
? -ENOMEM
: 0;
950 static int partno_from_devname(char *s
)
959 sz
= rtrim_whitespace((unsigned char *)s
);
962 while (p
> s
&& isdigit(*(p
- 1)))
968 pno
= strtol(p
, &end
, 10);
969 if (errno
|| !end
|| p
== end
)
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)
980 * <device>: start=<num>, size=<num>, type=<string>, ...
982 static int parse_line_nameval(struct fdisk_script
*dp
, char *s
)
985 struct fdisk_partition
*pa
;
994 DBG(SCRIPT
, ul_debugobj(dp
, " parse script line: '%s'", s
));
996 pa
= fdisk_new_partition();
1000 fdisk_partition_start_follow_default(pa
, 1);
1001 fdisk_partition_end_follow_default(pa
, 1);
1002 fdisk_partition_partno_follow_default(pa
, 1);
1007 if (p
&& (!x
|| p
< x
)) {
1011 pno
= partno_from_devname(s
);
1013 fdisk_partition_partno_follow_default(pa
, 0);
1014 fdisk_partition_set_partno(pa
, pno
);
1019 while (rc
== 0 && p
&& *p
) {
1021 DBG(SCRIPT
, ul_debugobj(dp
, " parsing '%s'", p
));
1022 p
= (char *) skip_blank(p
);
1024 if (!strncasecmp(p
, "start=", 6)) {
1027 rc
= next_number(&p
, &num
, &pow
);
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);
1034 } else if (!strncasecmp(p
, "size=", 5)) {
1038 rc
= next_number(&p
, &num
, &pow
);
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);
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)
1056 } else if (!strncasecmp(p
, "attrs=", 6)) {
1058 rc
= next_string(&p
, &pa
->attrs
);
1060 } else if (!strncasecmp(p
, "uuid=", 5)) {
1062 rc
= next_string(&p
, &pa
->uuid
);
1064 } else if (!strncasecmp(p
, "name=", 5)) {
1066 rc
= next_string(&p
, &pa
->name
);
1068 unhexmangle_string(pa
->name
);
1070 } else if (!strncasecmp(p
, "type=", 5) ||
1071 !strncasecmp(p
, "Id=", 3)) { /* backward compatibility */
1074 p
+= ((*p
== 'I' || *p
== 'i') ? 3 : 5); /* "Id=", "type=" */
1076 rc
= next_string(&p
, &type
);
1080 pa
->type
= fdisk_label_advparse_parttype(script_get_label(dp
),
1081 type
, FDISK_SCRIPT_PARTTYPE_PARSE_FLAGS
);
1089 DBG(SCRIPT
, ul_debugobj(dp
, "script parse error: unknown field '%s'", p
));
1096 rc
= fdisk_table_add_partition(dp
->table
, pa
);
1098 DBG(SCRIPT
, ul_debugobj(dp
, "script parse error: [rc=%d]", rc
));
1100 fdisk_unref_partition(pa
);
1107 #define alone_sign(_sign, _p) (_sign && (*_p == '\0' || isblank(*_p)))
1110 * <start>, <size>, <type>, <bootable>, ...
1112 static int parse_line_valcommas(struct fdisk_script
*dp
, char *s
)
1116 struct fdisk_partition
*pa
;
1117 enum { ITEM_START
, ITEM_SIZE
, ITEM_TYPE
, ITEM_BOOTABLE
};
1124 pa
= fdisk_new_partition();
1128 fdisk_partition_start_follow_default(pa
, 1);
1129 fdisk_partition_end_follow_default(pa
, 1);
1130 fdisk_partition_partno_follow_default(pa
, 1);
1132 while (rc
== 0 && p
&& *p
) {
1137 p
= (char *) skip_blank(p
);
1140 if (item
!= ITEM_BOOTABLE
) {
1141 sign
= *p
== '-' ? TK_MINUS
: *p
== '+' ? TK_PLUS
: 0;
1146 DBG(SCRIPT
, ul_debugobj(dp
, " parsing item %d ('%s')", item
, p
));
1151 if (*p
== ',' || *p
== ';' || alone_sign(sign
, p
))
1152 fdisk_partition_start_follow_default(pa
, 1);
1156 rc
= next_number(&p
, &num
, &pow
);
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
:
1165 fdisk_partition_start_follow_default(pa
, 0);
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
;
1176 rc
= next_number(&p
, &num
, &pow
);
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
:
1187 fdisk_partition_end_follow_default(pa
, 0);
1191 if (*p
== ',' || *p
== ';' || alone_sign(sign
, p
))
1192 break; /* use default type */
1194 rc
= next_string(&p
, &str
);
1198 pa
->type
= fdisk_label_advparse_parttype(script_get_label(dp
),
1199 str
, FDISK_SCRIPT_PARTTYPE_PARSE_FLAGS
);
1206 if (*p
== ',' || *p
== ';')
1209 char *tk
= next_token(&p
);
1210 if (tk
&& *tk
== '*' && *(tk
+ 1) == '\0')
1212 else if (tk
&& *tk
== '-' && *(tk
+ 1) == '\0')
1214 else if (tk
&& *tk
== '+' && *(tk
+ 1) == '\0')
1229 rc
= fdisk_table_add_partition(dp
->table
, pa
);
1231 DBG(SCRIPT
, ul_debugobj(dp
, "script parse error: [rc=%d]", rc
));
1233 fdisk_unref_partition(pa
);
1238 static int fdisk_script_read_buffer(struct fdisk_script
*dp
, char *s
)
1245 DBG(SCRIPT
, ul_debugobj(dp
, " parsing buffer"));
1247 s
= (char *) skip_blank(s
);
1249 return 0; /* nothing baby, ignore */
1251 if (!dp
->table
&& fdisk_script_get_table(dp
) == NULL
)
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
);
1258 /* parse script format */
1259 else if (strchr(s
, '='))
1260 rc
= parse_line_nameval(dp
, s
);
1262 /* parse simple <value>, ... format */
1264 rc
= parse_line_valcommas(dp
, s
);
1267 DBG(SCRIPT
, ul_debugobj(dp
, "%zu: parse error [rc=%d]",
1273 * fdisk_script_set_fgets:
1275 * @fn_fgets: callback function
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).
1281 * Return: 0 on success, <0 on error
1283 int fdisk_script_set_fgets(struct fdisk_script
*dp
,
1284 char *(*fn_fgets
)(struct fdisk_script
*, char *, size_t, FILE *))
1288 dp
->fn_fgets
= fn_fgets
;
1293 * fdisk_script_read_line:
1296 * @buf: buffer to store one line of the file
1297 * @bufsz: buffer size
1299 * Reads next line into dump.
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.
1304 int fdisk_script_read_line(struct fdisk_script
*dp
, FILE *f
, char *buf
, size_t bufsz
)
1311 DBG(SCRIPT
, ul_debugobj(dp
, " parsing line %zu", dp
->nlines
));
1313 /* read the next non-blank non-comment line */
1316 if (dp
->fn_fgets(dp
, buf
, bufsz
, f
) == NULL
)
1318 } else if (fgets(buf
, bufsz
, f
) == NULL
)
1322 s
= strchr(buf
, '\n');
1324 /* Missing final newline? Otherwise an extremely */
1325 /* long line - assume file was corrupted */
1327 DBG(SCRIPT
, ul_debugobj(dp
, "no final newline"));
1328 s
= strchr(buf
, '\0');
1330 DBG(SCRIPT
, ul_debugobj(dp
,
1331 "%zu: missing newline at line", dp
->nlines
));
1337 if (--s
>= buf
&& *s
== '\r')
1339 s
= (char *) skip_blank(buf
);
1340 } while (*s
== '\0' || *s
== '#');
1342 return fdisk_script_read_buffer(dp
, s
);
1347 * fdisk_script_read_file:
1351 * Reads file @f into script @dp.
1353 * Returns: 0 on success, <0 on error.
1355 int fdisk_script_read_file(struct fdisk_script
*dp
, FILE *f
)
1363 DBG(SCRIPT
, ul_debugobj(dp
, "parsing file"));
1366 rc
= fdisk_script_read_line(dp
, f
, buf
, sizeof(buf
));
1367 if (rc
&& rc
!= -ENOTSUP
)
1372 rc
= 0; /* end of file */
1374 DBG(SCRIPT
, ul_debugobj(dp
, "parsing file done [rc=%d]", rc
));
1381 * @dp: script (or NULL to remove previous reference)
1383 * Sets reference to the @dp script and remove reference to the previously used
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).
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().
1395 * Don't forget to call fdisk_set_script(cxt, NULL); to remove this reference
1396 * if no more necessary!
1398 * Returns: <0 on error, 0 on success.
1400 int fdisk_set_script(struct fdisk_context
*cxt
, struct fdisk_script
*dp
)
1406 fdisk_unref_script(cxt
->script
);
1411 DBG(CXT
, ul_debugobj(cxt
, "setting reference to script %p", cxt
->script
));
1412 fdisk_ref_script(cxt
->script
);
1422 * Returns: the current script or NULL.
1424 struct fdisk_script
*fdisk_get_script(struct fdisk_context
*cxt
)
1431 * fdisk_apply_script_headers:
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.
1438 * Returns: 0 on success, <0 on error.
1440 int fdisk_apply_script_headers(struct fdisk_context
*cxt
, struct fdisk_script
*dp
)
1449 DBG(SCRIPT
, ul_debugobj(dp
, "applying script headers"));
1450 fdisk_set_script(cxt
, dp
);
1452 str
= fdisk_script_get_header(dp
, "grain");
1456 rc
= parse_size(str
, &sz
, NULL
);
1458 rc
= fdisk_save_user_grain(cxt
, sz
);
1463 if (fdisk_has_user_device_properties(cxt
))
1464 fdisk_apply_user_device_properties(cxt
);
1466 /* create empty label */
1467 name
= fdisk_script_get_header(dp
, "label");
1471 rc
= fdisk_create_disklabel(cxt
, name
);
1475 str
= fdisk_script_get_header(dp
, "table-length");
1479 rc
= parse_size(str
, &sz
, NULL
);
1481 rc
= fdisk_gpt_set_npartitions(cxt
, sz
);
1488 * fdisk_apply_script:
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.
1495 * Returns: 0 on error, <0 on error.
1497 int fdisk_apply_script(struct fdisk_context
*cxt
, struct fdisk_script
*dp
)
1500 struct fdisk_script
*old
;
1505 DBG(CXT
, ul_debugobj(cxt
, "applying script %p", dp
));
1507 old
= fdisk_get_script(cxt
);
1508 fdisk_ref_script(old
);
1510 /* create empty disk label */
1511 rc
= fdisk_apply_script_headers(cxt
, dp
);
1513 /* create partitions */
1514 if (!rc
&& dp
->table
)
1515 rc
= fdisk_apply_table(cxt
, dp
->table
);
1517 fdisk_set_script(cxt
, old
);
1518 fdisk_unref_script(old
);
1520 DBG(CXT
, ul_debugobj(cxt
, "script done [rc=%d]", rc
));
1525 static int test_dump(struct fdisk_test
*ts
, int argc
, char *argv
[])
1527 char *devname
= argv
[1];
1528 struct fdisk_context
*cxt
;
1529 struct fdisk_script
*dp
;
1531 cxt
= fdisk_new_context();
1532 fdisk_assign_device(cxt
, devname
, 1);
1534 dp
= fdisk_new_script(cxt
);
1535 fdisk_script_read_context(dp
, NULL
);
1537 fdisk_script_write_file(dp
, stdout
);
1538 fdisk_unref_script(dp
);
1539 fdisk_unref_context(cxt
);
1544 static int test_read(struct fdisk_test
*ts
, int argc
, char *argv
[])
1546 char *filename
= argv
[1];
1547 struct fdisk_script
*dp
;
1548 struct fdisk_context
*cxt
;
1551 if (!(f
= fopen(filename
, "r")))
1552 err(EXIT_FAILURE
, "%s: cannot open", filename
);
1554 cxt
= fdisk_new_context();
1555 dp
= fdisk_new_script(cxt
);
1557 fdisk_script_read_file(dp
, f
);
1560 fdisk_script_write_file(dp
, stdout
);
1561 fdisk_unref_script(dp
);
1562 fdisk_unref_context(cxt
);
1567 static int test_stdin(struct fdisk_test
*ts
, int argc
, char *argv
[])
1570 struct fdisk_script
*dp
;
1571 struct fdisk_context
*cxt
;
1574 cxt
= fdisk_new_context();
1575 dp
= fdisk_new_script(cxt
);
1576 fdisk_script_set_header(dp
, "label", "dos");
1578 printf("<start>, <size>, <type>, <bootable: *|->\n");
1580 struct fdisk_partition
*pa
;
1581 size_t n
= dp
->table
? fdisk_table_get_nents(dp
->table
) : 0;
1583 printf(" #%zu :\n", n
+ 1);
1584 rc
= fdisk_script_read_line(dp
, stdin
, buf
, sizeof(buf
));
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
));
1595 fdisk_script_write_file(dp
, stdout
);
1596 fdisk_unref_script(dp
);
1597 fdisk_unref_context(cxt
);
1602 static int test_apply(struct fdisk_test
*ts
, int argc
, char *argv
[])
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
;
1612 cxt
= fdisk_new_context();
1613 fdisk_assign_device(cxt
, devname
, 0);
1615 dp
= fdisk_new_script_from_file(cxt
, scriptname
);
1619 rc
= fdisk_apply_script(cxt
, dp
);
1622 fdisk_unref_script(dp
);
1625 fdisk_list_disklabel(cxt
);
1626 fdisk_get_partitions(cxt
, &tb
);
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
));
1636 fdisk_free_iter(itr
);
1637 fdisk_unref_table(tb
);
1639 /*fdisk_write_disklabel(cxt);*/
1640 fdisk_unref_context(cxt
);
1644 static int test_tokens(struct fdisk_test
*ts
, int argc
, char *argv
[])
1646 char *p
, *str
= argc
== 2 ? strdup(argv
[1]) : NULL
;
1649 for (i
= 1, p
= str
; p
&& *p
; i
++) {
1650 char *tk
= next_token(&p
);
1655 printf("#%d: '%s'\n", i
, tk
);
1662 int main(int argc
, char *argv
[])
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" },
1673 return fdisk_run_test(tss
, argc
, argv
);