4 #include "carefulputc.h"
9 * @short_description: text based sfdisk compatible description of partition table
11 * The libfdisk scripts are based on original sfdisk script (dumps). Each
12 * script has two parts: script headers and partition table entries
15 * For more details about script format see sfdisk man page.
18 /* script header (e.g. unit: sectors) */
19 struct fdisk_scriptheader
{
20 struct list_head headers
;
25 /* script control struct */
27 struct fdisk_table
*table
;
28 struct list_head headers
;
29 struct fdisk_context
*cxt
;
32 char *(*fn_fgets
)(struct fdisk_script
*, char *, size_t, FILE *);
37 struct fdisk_label
*label
;
39 unsigned int json
: 1, /* JSON output */
40 force_label
: 1; /* label: <name> specified */
44 static void fdisk_script_free_header(struct fdisk_scriptheader
*fi
)
49 DBG(SCRIPT
, ul_debugobj(fi
, "free header %s", fi
->name
));
52 list_del(&fi
->headers
);
60 * The script hold fdisk_table and additional information to read/write
63 * Returns: newly allocated script struct.
65 struct fdisk_script
*fdisk_new_script(struct fdisk_context
*cxt
)
67 struct fdisk_script
*dp
= NULL
;
69 dp
= calloc(1, sizeof(*dp
));
73 DBG(SCRIPT
, ul_debugobj(dp
, "alloc"));
76 fdisk_ref_context(cxt
);
78 dp
->table
= fdisk_new_table();
80 fdisk_unref_script(dp
);
84 INIT_LIST_HEAD(&dp
->headers
);
89 * fdisk_new_script_from_file:
91 * @filename: path to the script file
93 * Allocates a new script and reads script from @filename.
95 * Returns: new script instance or NULL in case of error (check errno for more details).
97 struct fdisk_script
*fdisk_new_script_from_file(struct fdisk_context
*cxt
,
102 struct fdisk_script
*dp
, *res
= NULL
;
107 DBG(SCRIPT
, ul_debug("opening %s", filename
));
108 f
= fopen(filename
, "r");
112 dp
= fdisk_new_script(cxt
);
116 rc
= fdisk_script_read_file(dp
, f
);
126 fdisk_unref_script(dp
);
135 * @dp: script pointer
137 * Increments reference counter.
139 void fdisk_ref_script(struct fdisk_script
*dp
)
145 static void fdisk_reset_script(struct fdisk_script
*dp
)
149 DBG(SCRIPT
, ul_debugobj(dp
, "reset"));
150 fdisk_unref_table(dp
->table
);
153 while (!list_empty(&dp
->headers
)) {
154 struct fdisk_scriptheader
*fi
= list_entry(dp
->headers
.next
,
155 struct fdisk_scriptheader
, headers
);
156 fdisk_script_free_header(fi
);
158 INIT_LIST_HEAD(&dp
->headers
);
162 * fdisk_unref_script:
163 * @dp: script pointer
165 * Decrements reference counter, on zero the @dp is automatically
168 void fdisk_unref_script(struct fdisk_script
*dp
)
174 if (dp
->refcount
<= 0) {
175 fdisk_reset_script(dp
);
176 fdisk_unref_context(dp
->cxt
);
177 DBG(SCRIPT
, ul_debugobj(dp
, "free script"));
183 * fdisk_script_set_userdata
187 * Sets data usable for example in callbacks (e.g fdisk_script_set_fgets()).
189 * Returns: 0 on success, <0 on error.
191 int fdisk_script_set_userdata(struct fdisk_script
*dp
, void *data
)
199 * fdisk_script_get_userdata
202 * Returns: user data or NULL.
204 void *fdisk_script_get_userdata(struct fdisk_script
*dp
)
210 static struct fdisk_scriptheader
*script_get_header(struct fdisk_script
*dp
,
215 list_for_each(p
, &dp
->headers
) {
216 struct fdisk_scriptheader
*fi
= list_entry(p
, struct fdisk_scriptheader
, headers
);
218 if (strcasecmp(fi
->name
, name
) == 0)
226 * fdisk_script_get_header:
227 * @dp: script instance
230 * Returns: pointer to header data or NULL.
232 const char *fdisk_script_get_header(struct fdisk_script
*dp
, const char *name
)
234 struct fdisk_scriptheader
*fi
;
239 fi
= script_get_header(dp
, name
);
240 return fi
? fi
->data
: NULL
;
245 * fdisk_script_set_header:
246 * @dp: script instance
248 * @data: header data (or NULL)
250 * The headers are used as global options for whole partition
251 * table, always one header per line.
253 * If no @data is specified then the header is removed. If header does not exist
254 * and @data is specified then a new header is added.
256 * Note that libfdisk allows to specify arbitrary custom header, the default
257 * build-in headers are "unit" and "label", and some label specific headers
258 * (for example "uuid" and "name" for GPT).
260 * Returns: 0 on success, <0 on error
262 int fdisk_script_set_header(struct fdisk_script
*dp
,
266 struct fdisk_scriptheader
*fi
;
271 fi
= script_get_header(dp
, name
);
273 return 0; /* want to remove header that does not exist, success */
276 DBG(SCRIPT
, ul_debugobj(dp
, "freeing header %s", name
));
278 /* no data, remove the header */
279 fdisk_script_free_header(fi
);
284 DBG(SCRIPT
, ul_debugobj(dp
, "setting new header %s='%s'", name
, data
));
287 fi
= calloc(1, sizeof(*fi
));
290 INIT_LIST_HEAD(&fi
->headers
);
291 fi
->name
= strdup(name
);
292 fi
->data
= strdup(data
);
293 if (!fi
->data
|| !fi
->name
) {
294 fdisk_script_free_header(fi
);
297 list_add_tail(&fi
->headers
, &dp
->headers
);
299 /* update existing */
300 char *x
= strdup(data
);
302 DBG(SCRIPT
, ul_debugobj(dp
, "update '%s' header '%s' -> '%s'", name
, fi
->data
, data
));
310 if (strcmp(name
, "label") == 0)
317 * fdisk_script_get_table:
320 * The table (container with partitions) is possible to create by
321 * fdisk_script_read_context() or fdisk_script_read_file(), otherwise
322 * this function returns NULL.
324 * Returns: NULL or script.
326 struct fdisk_table
*fdisk_script_get_table(struct fdisk_script
*dp
)
329 return dp
? dp
->table
: NULL
;
332 static struct fdisk_label
*script_get_label(struct fdisk_script
*dp
)
338 dp
->label
= fdisk_get_label(dp
->cxt
,
339 fdisk_script_get_header(dp
, "label"));
340 DBG(SCRIPT
, ul_debugobj(dp
, "label '%s'", dp
->label
? dp
->label
->name
: ""));
346 * fdisk_script_get_nlines:
349 * Returns: number of parsed lines or <0 on error.
351 int fdisk_script_get_nlines(struct fdisk_script
*dp
)
358 * fdisk_script_has_force_label:
361 * Label has been explicitly specified in the script.
365 * Returns: true if "label: name" has been parsed.
367 int fdisk_script_has_force_label(struct fdisk_script
*dp
)
370 return dp
->force_label
;
375 * fdisk_script_read_context:
379 * Reads data from the @cxt context (on disk partition table) into the script.
380 * If the context is no specified than defaults to context used for fdisk_new_script().
382 * Return: 0 on success, <0 on error.
384 int fdisk_script_read_context(struct fdisk_script
*dp
, struct fdisk_context
*cxt
)
386 struct fdisk_label
*lb
;
390 if (!dp
|| (!cxt
&& !dp
->cxt
))
396 DBG(SCRIPT
, ul_debugobj(dp
, "reading context into script"));
397 fdisk_reset_script(dp
);
399 lb
= fdisk_get_label(cxt
, NULL
);
403 /* allocate and fill new table */
404 rc
= fdisk_get_partitions(cxt
, &dp
->table
);
408 /* generate headers */
409 rc
= fdisk_script_set_header(dp
, "label", fdisk_label_get_name(lb
));
411 if (!rc
&& fdisk_get_disklabel_id(cxt
, &p
) == 0 && p
) {
412 rc
= fdisk_script_set_header(dp
, "label-id", p
);
415 if (!rc
&& cxt
->dev_path
)
416 rc
= fdisk_script_set_header(dp
, "device", cxt
->dev_path
);
418 rc
= fdisk_script_set_header(dp
, "unit", "sectors");
420 if (!rc
&& fdisk_is_label(cxt
, GPT
)) {
421 struct fdisk_labelitem item
;
425 rc
= fdisk_get_disklabel_item(cxt
, GPT_LABELITEM_FIRSTLBA
, &item
);
427 snprintf(buf
, sizeof(buf
), "%"PRIu64
, item
.data
.num64
);
428 rc
= fdisk_script_set_header(dp
, "first-lba", buf
);
433 rc
= fdisk_get_disklabel_item(cxt
, GPT_LABELITEM_LASTLBA
, &item
);
435 snprintf(buf
, sizeof(buf
), "%"PRIu64
, item
.data
.num64
);
436 rc
= fdisk_script_set_header(dp
, "last-lba", buf
);
441 size_t n
= fdisk_get_npartitions(cxt
);
442 if (n
!= FDISK_GPT_NPARTITIONS_DEFAULT
) {
443 snprintf(buf
, sizeof(buf
), "%zu", n
);
444 rc
= fdisk_script_set_header(dp
, "table-length", buf
);
449 DBG(SCRIPT
, ul_debugobj(dp
, "read context done [rc=%d]", rc
));
454 * fdisk_script_enable_json:
458 * Disable/Enable JSON output format.
460 * Returns: 0 on success, <0 on error.
462 int fdisk_script_enable_json(struct fdisk_script
*dp
, int json
)
470 static void fput_indent(int indent
, FILE *f
)
474 for (i
= 0; i
<= indent
; i
++)
478 static int write_file_json(struct fdisk_script
*dp
, FILE *f
)
481 struct fdisk_partition
*pa
;
482 struct fdisk_iter itr
;
483 const char *devname
= NULL
;
484 int ct
= 0, indent
= 0;
489 DBG(SCRIPT
, ul_debugobj(dp
, "writing json dump to file"));
493 fput_indent(indent
, f
);
494 fputs("\"partitiontable\": {\n", f
);
498 list_for_each(h
, &dp
->headers
) {
499 struct fdisk_scriptheader
*fi
= list_entry(h
, struct fdisk_scriptheader
, headers
);
500 const char *name
= fi
->name
;
503 if (strcmp(name
, "first-lba") == 0) {
506 } else if (strcmp(name
, "last-lba") == 0) {
509 } else if (strcmp(name
, "label-id") == 0)
512 fput_indent(indent
, f
);
513 fputs_quoted_lower(name
, f
);
516 fputs_quoted(fi
->data
, f
);
519 if (!dp
->table
&& fi
== list_last_entry(&dp
->headers
, struct fdisk_scriptheader
, headers
))
524 if (strcmp(name
, "device") == 0)
530 DBG(SCRIPT
, ul_debugobj(dp
, "script table empty"));
534 DBG(SCRIPT
, ul_debugobj(dp
, "%zu entries", fdisk_table_get_nents(dp
->table
)));
536 fput_indent(indent
, f
);
537 fputs("\"partitions\": [\n", f
);
540 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
541 while (fdisk_table_next_partition(dp
->table
, &itr
, &pa
) == 0) {
545 fput_indent(indent
, f
);
548 p
= fdisk_partname(devname
, pa
->partno
+ 1);
550 DBG(SCRIPT
, ul_debugobj(dp
, "write %s entry", p
));
551 fputs("\"node\": ", f
);
555 if (fdisk_partition_has_start(pa
))
556 fprintf(f
, ", \"start\": %ju", (uintmax_t)pa
->start
);
557 if (fdisk_partition_has_size(pa
))
558 fprintf(f
, ", \"size\": %ju", (uintmax_t)pa
->size
);
560 if (pa
->type
&& fdisk_parttype_get_string(pa
->type
))
561 fprintf(f
, ", \"type\": \"%s\"", fdisk_parttype_get_string(pa
->type
));
563 fprintf(f
, ", \"type\": \"%x\"", fdisk_parttype_get_code(pa
->type
));
566 fprintf(f
, ", \"uuid\": \"%s\"", pa
->uuid
);
567 if (pa
->name
&& *pa
->name
) {
568 fputs(", \"name\": ", f
),
569 fputs_quoted(pa
->name
, f
);
572 /* for MBR attr=80 means bootable */
574 struct fdisk_label
*lb
= script_get_label(dp
);
576 if (!lb
|| fdisk_label_get_type(lb
) != FDISK_DISKLABEL_DOS
)
577 fprintf(f
, ", \"attrs\": \"%s\"", pa
->attrs
);
579 if (fdisk_partition_is_bootable(pa
))
580 fprintf(f
, ", \"bootable\": true");
582 if ((size_t)ct
< fdisk_table_get_nents(dp
->table
))
589 fput_indent(indent
, f
);
593 fput_indent(indent
, f
);
596 DBG(SCRIPT
, ul_debugobj(dp
, "write script done"));
600 static int write_file_sfdisk(struct fdisk_script
*dp
, FILE *f
)
603 struct fdisk_partition
*pa
;
604 struct fdisk_iter itr
;
605 const char *devname
= NULL
;
610 DBG(SCRIPT
, ul_debugobj(dp
, "writing sfdisk-like script to file"));
613 list_for_each(h
, &dp
->headers
) {
614 struct fdisk_scriptheader
*fi
= list_entry(h
, struct fdisk_scriptheader
, headers
);
615 fprintf(f
, "%s: %s\n", fi
->name
, fi
->data
);
616 if (strcmp(fi
->name
, "device") == 0)
621 DBG(SCRIPT
, ul_debugobj(dp
, "script table empty"));
625 DBG(SCRIPT
, ul_debugobj(dp
, "%zu entries", fdisk_table_get_nents(dp
->table
)));
629 fdisk_reset_iter(&itr
, FDISK_ITER_FORWARD
);
630 while (fdisk_table_next_partition(dp
->table
, &itr
, &pa
) == 0) {
634 p
= fdisk_partname(devname
, pa
->partno
+ 1);
636 DBG(SCRIPT
, ul_debugobj(dp
, "write %s entry", p
));
637 fprintf(f
, "%s :", p
);
639 fprintf(f
, "%zu :", pa
->partno
+ 1);
641 if (fdisk_partition_has_start(pa
))
642 fprintf(f
, " start=%12ju", (uintmax_t)pa
->start
);
643 if (fdisk_partition_has_size(pa
))
644 fprintf(f
, ", size=%12ju", (uintmax_t)pa
->size
);
646 if (pa
->type
&& fdisk_parttype_get_string(pa
->type
))
647 fprintf(f
, ", type=%s", fdisk_parttype_get_string(pa
->type
));
649 fprintf(f
, ", type=%x", fdisk_parttype_get_code(pa
->type
));
652 fprintf(f
, ", uuid=%s", pa
->uuid
);
653 if (pa
->name
&& *pa
->name
)
654 fprintf(f
, ", name=\"%s\"", pa
->name
);
656 /* for MBR attr=80 means bootable */
658 struct fdisk_label
*lb
= script_get_label(dp
);
660 if (!lb
|| fdisk_label_get_type(lb
) != FDISK_DISKLABEL_DOS
)
661 fprintf(f
, ", attrs=\"%s\"", pa
->attrs
);
663 if (fdisk_partition_is_bootable(pa
))
664 fprintf(f
, ", bootable");
668 DBG(SCRIPT
, ul_debugobj(dp
, "write script done"));
673 * fdisk_script_write_file:
677 * Writes script @dp to the file @f.
679 * Returns: 0 on success, <0 on error.
681 int fdisk_script_write_file(struct fdisk_script
*dp
, FILE *f
)
686 return write_file_json(dp
, f
);
688 return write_file_sfdisk(dp
, f
);
691 static inline int is_header_line(const char *s
)
693 const char *p
= strchr(s
, ':');
695 if (!p
|| p
== s
|| !*(p
+ 1) || strchr(s
, '='))
701 /* parses "<name>: value", note modifies @s*/
702 static int parse_line_header(struct fdisk_script
*dp
, char *s
)
707 DBG(SCRIPT
, ul_debugobj(dp
, " parse header '%s'", s
));
713 value
= strchr(s
, ':');
719 ltrim_whitespace((unsigned char *) name
);
720 rtrim_whitespace((unsigned char *) name
);
721 ltrim_whitespace((unsigned char *) value
);
722 rtrim_whitespace((unsigned char *) value
);
724 if (strcmp(name
, "label") == 0) {
725 if (dp
->cxt
&& !fdisk_get_label(dp
->cxt
, value
))
726 goto done
; /* unknown label name */
728 } else if (strcmp(name
, "unit") == 0) {
729 if (strcmp(value
, "sectors") != 0)
730 goto done
; /* only "sectors" supported */
731 } else if (strcmp(name
, "label-id") == 0
732 || strcmp(name
, "device") == 0
733 || strcmp(name
, "first-lba") == 0
734 || strcmp(name
, "last-lba") == 0
735 || strcmp(name
, "table-length") == 0) {
736 ; /* whatever is possible */
738 goto done
; /* unknown header */
741 rc
= fdisk_script_set_header(dp
, name
, value
);
744 DBG(SCRIPT
, ul_debugobj(dp
, "header parse error: "
745 "[rc=%d, name='%s', value='%s']",
751 /* returns zero terminated string with next token and @str is updated */
752 static char *next_token(char **str
)
754 char *tk_begin
= NULL
,
758 int open_quote
= 0, terminated
= 0;
760 for (p
= *str
; p
&& *p
; p
++) {
764 tk_begin
= *p
== '"' ? p
+ 1 : p
;
770 if (isblank(*p
) || *p
== ',' || *p
== ';' || *p
== '"' )
772 else if (*(p
+ 1) == '\0')
774 if (tk_begin
&& tk_end
)
783 /* skip closing quotes */
787 /* token is terminated by blank (or blank is before "," or ";") */
789 end
= (char *) skip_blank(end
);
793 /* token is terminated by "," or ";" */
794 if (*end
== ',' || *end
== ';') {
798 /* token is terminated by \0 */
803 DBG(SCRIPT
, ul_debug("unterminated token '%s'", end
));
807 /* skip extra space after terminator */
808 end
= (char *) skip_blank(end
);
815 static int next_number(char **s
, uint64_t *num
, int *power
)
825 rc
= parse_size(tk
, (uintmax_t *) num
, power
);
829 static int next_string(char **s
, char **str
)
840 rc
= !*str
? -ENOMEM
: 0;
845 static int partno_from_devname(char *s
)
851 sz
= rtrim_whitespace((unsigned char *)s
);
854 while (p
> s
&& isdigit(*(p
- 1)))
858 pno
= strtol(p
, &end
, 10);
859 if (errno
|| !end
|| p
== end
)
865 * <device>: start=<num>, size=<num>, type=<string>, ...
867 static int parse_line_nameval(struct fdisk_script
*dp
, char *s
)
870 struct fdisk_partition
*pa
;
878 DBG(SCRIPT
, ul_debugobj(dp
, " parse script line: '%s'", s
));
880 pa
= fdisk_new_partition();
884 fdisk_partition_start_follow_default(pa
, 1);
885 fdisk_partition_end_follow_default(pa
, 1);
886 fdisk_partition_partno_follow_default(pa
, 1);
891 if (p
&& (!x
|| p
< x
)) {
895 pno
= partno_from_devname(s
);
897 fdisk_partition_partno_follow_default(pa
, 0);
898 fdisk_partition_set_partno(pa
, pno
);
903 while (rc
== 0 && p
&& *p
) {
905 DBG(SCRIPT
, ul_debugobj(dp
, " parsing '%s'", p
));
906 p
= (char *) skip_blank(p
);
908 if (!strncasecmp(p
, "start=", 6)) {
911 rc
= next_number(&p
, &num
, &pow
);
913 if (pow
) /* specified as <num><suffix> */
914 num
/= dp
->cxt
->sector_size
;
915 fdisk_partition_set_start(pa
, num
);
916 fdisk_partition_start_follow_default(pa
, 0);
918 } else if (!strncasecmp(p
, "size=", 5)) {
922 rc
= next_number(&p
, &num
, &pow
);
924 if (pow
) /* specified as <num><suffix> */
925 num
/= dp
->cxt
->sector_size
;
926 else /* specified as number of sectors */
927 fdisk_partition_size_explicit(pa
, 1);
928 fdisk_partition_set_size(pa
, num
);
929 fdisk_partition_end_follow_default(pa
, 0);
932 } else if (!strncasecmp(p
, "bootable", 8)) {
933 /* we use next_token() to skip possible extra space */
934 char *tk
= next_token(&p
);
935 if (tk
&& strcasecmp(tk
, "bootable") == 0)
940 } else if (!strncasecmp(p
, "attrs=", 6)) {
942 rc
= next_string(&p
, &pa
->attrs
);
944 } else if (!strncasecmp(p
, "uuid=", 5)) {
946 rc
= next_string(&p
, &pa
->uuid
);
948 } else if (!strncasecmp(p
, "name=", 5)) {
950 rc
= next_string(&p
, &pa
->name
);
952 } else if (!strncasecmp(p
, "type=", 5) ||
953 !strncasecmp(p
, "Id=", 3)) { /* backward compatibility */
956 p
+= ((*p
== 'I' || *p
== 'i') ? 3 : 5); /* "Id=", "type=" */
958 rc
= next_string(&p
, &type
);
961 pa
->type
= fdisk_label_parse_parttype(
962 script_get_label(dp
), type
);
967 fdisk_unref_parttype(pa
->type
);
973 DBG(SCRIPT
, ul_debugobj(dp
, "script parse error: unknown field '%s'", p
));
980 rc
= fdisk_table_add_partition(dp
->table
, pa
);
982 DBG(SCRIPT
, ul_debugobj(dp
, "script parse error: [rc=%d]", rc
));
984 fdisk_unref_partition(pa
);
988 /* original sfdisk supports partition types shortcuts like 'L' = Linux native
990 static struct fdisk_parttype
*translate_type_shortcuts(struct fdisk_script
*dp
, char *str
)
992 struct fdisk_label
*lb
;
993 const char *type
= NULL
;
995 if (strlen(str
) != 1)
998 lb
= script_get_label(dp
);
1002 if (lb
->id
== FDISK_DISKLABEL_DOS
) {
1004 case 'L': /* Linux */
1007 case 'S': /* Swap */
1010 case 'E': /* Dos extended */
1013 case 'X': /* Linux extended */
1016 case 'U': /* UEFI system */
1020 } else if (lb
->id
== FDISK_DISKLABEL_GPT
) {
1022 case 'L': /* Linux */
1023 type
= "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
1025 case 'S': /* Swap */
1026 type
= "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F";
1028 case 'H': /* Home */
1029 type
= "933AC7E1-2EB4-4F13-B844-0E14E2AEF915";
1031 case 'U': /* UEFI system */
1032 type
= "C12A7328-F81F-11D2-BA4B-00A0C93EC93B";
1037 return type
? fdisk_label_parse_parttype(lb
, type
) : NULL
;
1043 #define alone_sign(_sign, _p) (_sign && (*_p == '\0' || isblank(*_p)))
1046 * <start>, <size>, <type>, <bootable>, ...
1048 static int parse_line_valcommas(struct fdisk_script
*dp
, char *s
)
1052 struct fdisk_partition
*pa
;
1053 enum { ITEM_START
, ITEM_SIZE
, ITEM_TYPE
, ITEM_BOOTABLE
};
1059 pa
= fdisk_new_partition();
1063 fdisk_partition_start_follow_default(pa
, 1);
1064 fdisk_partition_end_follow_default(pa
, 1);
1065 fdisk_partition_partno_follow_default(pa
, 1);
1067 while (rc
== 0 && p
&& *p
) {
1072 p
= (char *) skip_blank(p
);
1075 if (item
!= ITEM_BOOTABLE
) {
1076 sign
= *p
== '-' ? TK_MINUS
: *p
== '+' ? TK_PLUS
: 0;
1081 DBG(SCRIPT
, ul_debugobj(dp
, " parsing item %d ('%s')", item
, p
));
1086 if (*p
== ',' || *p
== ';' || alone_sign(sign
, p
))
1087 fdisk_partition_start_follow_default(pa
, 1);
1091 rc
= next_number(&p
, &num
, &pow
);
1093 if (pow
) /* specified as <num><suffix> */
1094 num
/= dp
->cxt
->sector_size
;
1095 fdisk_partition_set_start(pa
, num
);
1096 pa
->movestart
= sign
== TK_MINUS
? FDISK_MOVE_DOWN
:
1097 sign
== TK_PLUS
? FDISK_MOVE_UP
:
1100 fdisk_partition_start_follow_default(pa
, 0);
1104 if (*p
== ',' || *p
== ';' || alone_sign(sign
, p
)) {
1105 fdisk_partition_end_follow_default(pa
, 1);
1106 if (sign
== TK_PLUS
)
1107 /* '+' alone means use all possible space, '-' alone means nothing */
1108 pa
->resize
= FDISK_RESIZE_ENLARGE
;
1111 rc
= next_number(&p
, &num
, &pow
);
1113 if (pow
) /* specified as <size><suffix> */
1114 num
/= dp
->cxt
->sector_size
;
1115 else /* specified as number of sectors */
1116 fdisk_partition_size_explicit(pa
, 1);
1117 fdisk_partition_set_size(pa
, num
);
1118 pa
->resize
= sign
== TK_MINUS
? FDISK_RESIZE_REDUCE
:
1119 sign
== TK_PLUS
? FDISK_RESIZE_ENLARGE
:
1122 fdisk_partition_end_follow_default(pa
, 0);
1126 if (*p
== ',' || *p
== ';' || alone_sign(sign
, p
))
1127 break; /* use default type */
1129 rc
= next_string(&p
, &str
);
1133 pa
->type
= translate_type_shortcuts(dp
, str
);
1135 pa
->type
= fdisk_label_parse_parttype(
1136 script_get_label(dp
), str
);
1141 fdisk_unref_parttype(pa
->type
);
1147 if (*p
== ',' || *p
== ';')
1150 char *tk
= next_token(&p
);
1151 if (tk
&& *tk
== '*' && *(tk
+ 1) == '\0')
1153 else if (tk
&& *tk
== '-' && *(tk
+ 1) == '\0')
1155 else if (tk
&& *tk
== '+' && *(tk
+ 1) == '\0')
1170 rc
= fdisk_table_add_partition(dp
->table
, pa
);
1172 DBG(SCRIPT
, ul_debugobj(dp
, "script parse error: [rc=%d]", rc
));
1174 fdisk_unref_partition(pa
);
1179 static int fdisk_script_read_buffer(struct fdisk_script
*dp
, char *s
)
1186 DBG(SCRIPT
, ul_debugobj(dp
, " parsing buffer"));
1188 s
= (char *) skip_blank(s
);
1190 return 0; /* nothing baby, ignore */
1193 dp
->table
= fdisk_new_table();
1198 /* parse header lines only if no partition specified yet */
1199 if (fdisk_table_is_empty(dp
->table
) && is_header_line(s
))
1200 rc
= parse_line_header(dp
, s
);
1202 /* parse script format */
1203 else if (strchr(s
, '='))
1204 rc
= parse_line_nameval(dp
, s
);
1206 /* parse simple <value>, ... format */
1208 rc
= parse_line_valcommas(dp
, s
);
1211 DBG(SCRIPT
, ul_debugobj(dp
, "%zu: parse error [rc=%d]",
1217 * fdisk_script_set_fgets:
1219 * @fn_fgets: callback function
1221 * The library uses fgets() function to read the next line from the script.
1222 * This default maybe overridden by another function. Note that the function has
1223 * to return the line terminated by \n (for example readline(3) removes \n).
1225 * Return: 0 on success, <0 on error
1227 int fdisk_script_set_fgets(struct fdisk_script
*dp
,
1228 char *(*fn_fgets
)(struct fdisk_script
*, char *, size_t, FILE *))
1232 dp
->fn_fgets
= fn_fgets
;
1237 * fdisk_script_read_line:
1240 * @buf: buffer to store one line of the file
1241 * @bufsz: buffer size
1243 * Reads next line into dump.
1245 * Returns: 0 on success, <0 on error, 1 when nothing to read.
1247 int fdisk_script_read_line(struct fdisk_script
*dp
, FILE *f
, char *buf
, size_t bufsz
)
1254 DBG(SCRIPT
, ul_debugobj(dp
, " parsing line %zu", dp
->nlines
));
1256 /* read the next non-blank non-comment line */
1259 if (dp
->fn_fgets(dp
, buf
, bufsz
, f
) == NULL
)
1261 } else if (fgets(buf
, bufsz
, f
) == NULL
)
1265 s
= strchr(buf
, '\n');
1267 /* Missing final newline? Otherwise an extremely */
1268 /* long line - assume file was corrupted */
1270 DBG(SCRIPT
, ul_debugobj(dp
, "no final newline"));
1271 s
= strchr(buf
, '\0');
1273 DBG(SCRIPT
, ul_debugobj(dp
,
1274 "%zu: missing newline at line", dp
->nlines
));
1280 if (--s
>= buf
&& *s
== '\r')
1282 s
= (char *) skip_blank(buf
);
1283 } while (*s
== '\0' || *s
== '#');
1285 return fdisk_script_read_buffer(dp
, s
);
1290 * fdisk_script_read_file:
1294 * Reads file @f into script @dp.
1296 * Returns: 0 on success, <0 on error.
1298 int fdisk_script_read_file(struct fdisk_script
*dp
, FILE *f
)
1306 DBG(SCRIPT
, ul_debugobj(dp
, "parsing file"));
1309 rc
= fdisk_script_read_line(dp
, f
, buf
, sizeof(buf
));
1315 rc
= 0; /* end of file */
1317 DBG(SCRIPT
, ul_debugobj(dp
, "parsing file done [rc=%d]", rc
));
1324 * @dp: script (or NULL to remove previous reference)
1326 * Sets reference to the @dp script and remove reference to the previously used
1329 * The script headers might be used by label drivers to overwrite
1330 * built-in defaults (for example disk label Id) and label driver might
1331 * optimize the default semantic to be more usable for scripts (for example to
1332 * not ask for primary/logical/extended partition type).
1334 * Note that script also contains reference to the fdisk context (see
1335 * fdisk_new_script()). This context may be completely independent on
1336 * context used for fdisk_set_script().
1338 * Returns: <0 on error, 0 on success.
1340 int fdisk_set_script(struct fdisk_context
*cxt
, struct fdisk_script
*dp
)
1346 fdisk_unref_script(cxt
->script
);
1351 DBG(CXT
, ul_debugobj(cxt
, "setting reference to script %p", cxt
->script
));
1352 fdisk_ref_script(cxt
->script
);
1362 * Returns: the current script or NULL.
1364 struct fdisk_script
*fdisk_get_script(struct fdisk_context
*cxt
)
1371 * fdisk_apply_script_headers:
1375 * Associate context @cxt with script @dp and creates a new empty disklabel.
1377 * Returns: 0 on success, <0 on error.
1379 int fdisk_apply_script_headers(struct fdisk_context
*cxt
, struct fdisk_script
*dp
)
1388 DBG(SCRIPT
, ul_debugobj(dp
, "applying script headers"));
1389 fdisk_set_script(cxt
, dp
);
1391 /* create empty label */
1392 name
= fdisk_script_get_header(dp
, "label");
1396 rc
= fdisk_create_disklabel(cxt
, name
);
1400 str
= fdisk_script_get_header(dp
, "table-length");
1404 rc
= parse_size(str
, &sz
, NULL
);
1406 rc
= fdisk_gpt_set_npartitions(cxt
, sz
);
1413 * fdisk_apply_script:
1417 * This function creates a new disklabel and partition within context @cxt. You
1418 * have to call fdisk_write_disklabel() to apply changes to the device.
1420 * Returns: 0 on error, <0 on error.
1422 int fdisk_apply_script(struct fdisk_context
*cxt
, struct fdisk_script
*dp
)
1425 struct fdisk_script
*old
;
1430 DBG(CXT
, ul_debugobj(cxt
, "applying script %p", dp
));
1432 old
= fdisk_get_script(cxt
);
1433 fdisk_ref_script(old
);
1435 /* create empty disk label */
1436 rc
= fdisk_apply_script_headers(cxt
, dp
);
1438 /* create partitions */
1439 if (!rc
&& dp
->table
)
1440 rc
= fdisk_apply_table(cxt
, dp
->table
);
1442 fdisk_set_script(cxt
, old
);
1443 fdisk_unref_script(old
);
1445 DBG(CXT
, ul_debugobj(cxt
, "script done [rc=%d]", rc
));
1450 static int test_dump(struct fdisk_test
*ts
, int argc
, char *argv
[])
1452 char *devname
= argv
[1];
1453 struct fdisk_context
*cxt
;
1454 struct fdisk_script
*dp
;
1456 cxt
= fdisk_new_context();
1457 fdisk_assign_device(cxt
, devname
, 1);
1459 dp
= fdisk_new_script(cxt
);
1460 fdisk_script_read_context(dp
, NULL
);
1462 fdisk_script_write_file(dp
, stdout
);
1463 fdisk_unref_script(dp
);
1464 fdisk_unref_context(cxt
);
1469 static int test_read(struct fdisk_test
*ts
, int argc
, char *argv
[])
1471 char *filename
= argv
[1];
1472 struct fdisk_script
*dp
;
1473 struct fdisk_context
*cxt
;
1476 if (!(f
= fopen(filename
, "r")))
1477 err(EXIT_FAILURE
, "%s: cannot open", filename
);
1479 cxt
= fdisk_new_context();
1480 dp
= fdisk_new_script(cxt
);
1482 fdisk_script_read_file(dp
, f
);
1485 fdisk_script_write_file(dp
, stdout
);
1486 fdisk_unref_script(dp
);
1487 fdisk_unref_context(cxt
);
1492 static int test_stdin(struct fdisk_test
*ts
, int argc
, char *argv
[])
1495 struct fdisk_script
*dp
;
1496 struct fdisk_context
*cxt
;
1499 cxt
= fdisk_new_context();
1500 dp
= fdisk_new_script(cxt
);
1501 fdisk_script_set_header(dp
, "label", "dos");
1503 printf("<start>, <size>, <type>, <bootable: *|->\n");
1505 struct fdisk_partition
*pa
;
1506 size_t n
= fdisk_table_get_nents(dp
->table
);
1508 printf(" #%zu :\n", n
+ 1);
1509 rc
= fdisk_script_read_line(dp
, stdin
, buf
, sizeof(buf
));
1512 pa
= fdisk_table_get_partition(dp
->table
, n
);
1513 printf(" #%zu %12ju %12ju\n", n
+ 1,
1514 (uintmax_t)fdisk_partition_get_start(pa
),
1515 (uintmax_t)fdisk_partition_get_size(pa
));
1520 fdisk_script_write_file(dp
, stdout
);
1521 fdisk_unref_script(dp
);
1522 fdisk_unref_context(cxt
);
1527 static int test_apply(struct fdisk_test
*ts
, int argc
, char *argv
[])
1529 char *devname
= argv
[1], *scriptname
= argv
[2];
1530 struct fdisk_context
*cxt
;
1531 struct fdisk_script
*dp
;
1532 struct fdisk_table
*tb
= NULL
;
1533 struct fdisk_iter
*itr
= NULL
;
1534 struct fdisk_partition
*pa
= NULL
;
1537 cxt
= fdisk_new_context();
1538 fdisk_assign_device(cxt
, devname
, 0);
1540 dp
= fdisk_new_script_from_file(cxt
, scriptname
);
1544 rc
= fdisk_apply_script(cxt
, dp
);
1547 fdisk_unref_script(dp
);
1550 fdisk_list_disklabel(cxt
);
1551 fdisk_get_partitions(cxt
, &tb
);
1553 itr
= fdisk_new_iter(FDISK_ITER_FORWARD
);
1554 while (fdisk_table_next_partition(tb
, itr
, &pa
) == 0) {
1555 printf(" #%zu %12ju %12ju\n", fdisk_partition_get_partno(pa
),
1556 (uintmax_t)fdisk_partition_get_start(pa
),
1557 (uintmax_t)fdisk_partition_get_size(pa
));
1561 fdisk_free_iter(itr
);
1562 fdisk_unref_table(tb
);
1564 /*fdisk_write_disklabel(cxt);*/
1565 fdisk_unref_context(cxt
);
1569 static int test_tokens(struct fdisk_test
*ts
, int argc
, char *argv
[])
1571 char *p
, *str
= argc
== 2 ? strdup(argv
[1]) : NULL
;
1574 for (i
= 1, p
= str
; p
&& *p
; i
++) {
1575 char *tk
= next_token(&p
);
1580 printf("#%d: '%s'\n", i
, tk
);
1587 int main(int argc
, char *argv
[])
1589 struct fdisk_test tss
[] = {
1590 { "--dump", test_dump
, "<device> dump PT as script" },
1591 { "--read", test_read
, "<file> read PT script from file" },
1592 { "--apply", test_apply
, "<device> <file> try apply script from file to device" },
1593 { "--stdin", test_stdin
, " read input like sfdisk" },
1594 { "--tokens", test_tokens
, "<string> parse string" },
1598 return fdisk_run_test(tss
, argc
, argv
);