blob: 83bda995dd64f0c73ef5f9d06b7204b13e697081 [file] [log] [blame]
bigbiff7b4c7a62015-01-01 19:44:14 -05001
2#include "fdiskP.h"
3#include "strutils.h"
4
5/**
6 * SECTION: script
7 * @title: Script
8 * @short_description: text based sfdisk compatible description of partition table
9 *
10 * The libfdisk scripts are based on original sfdisk script (dumps). Each
11 * script has two parts: script headers and partition table entries
12 * (partitions).
13 *
14 * For more details about script format see sfdisk man page.
15 */
16
17/* script header (e.g. unit: sectors) */
18struct fdisk_scriptheader {
19 struct list_head headers;
20 char *name;
21 char *data;
22};
23
24/* script control struct */
25struct fdisk_script {
26 struct fdisk_table *table;
27 struct list_head headers;
28 struct fdisk_context *cxt;
29
30 int refcount;
31
32 /* parser's state */
33 size_t nlines;
34 int fmt; /* input format */
35 struct fdisk_label *label;
36};
37
38
39static void fdisk_script_free_header(struct fdisk_script *dp, struct fdisk_scriptheader *fi)
40{
41 if (!fi)
42 return;
43
44 DBG(SCRIPT, ul_debugobj(fi, "free header %s", fi->name));
45 free(fi->name);
46 free(fi->data);
47 list_del(&fi->headers);
48 free(fi);
49}
50
51/**
52 * fdisk_new_script:
53 * @cxt: context
54 *
55 * The script hold fdisk_table and additional information to read/write
56 * script to the file.
57 *
58 * Returns: newly allocated script struct.
59 */
60struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt)
61{
62 struct fdisk_script *dp = NULL;
63
64 dp = calloc(1, sizeof(*dp));
65 if (!dp)
66 return NULL;
67
68 DBG(SCRIPT, ul_debugobj(dp, "alloc"));
69 dp->refcount = 1;
70 dp->cxt = cxt;
71 fdisk_ref_context(cxt);
72
73 dp->table = fdisk_new_table();
74 if (!dp->table) {
75 fdisk_unref_script(dp);
76 return NULL;
77 }
78
79 INIT_LIST_HEAD(&dp->headers);
80 return dp;
81}
82
83/**
84 * fdisk_new_script_from_file:
85 * @cxt: context
86 * @filename: path to the script file
87 *
88 * Allocates a new script and reads script from @filename.
89 *
90 * Returns: new script instance or NULL in case of error (check errno for more details).
91 */
92struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
93 const char *filename)
94{
95 int rc;
96 FILE *f;
97 struct fdisk_script *dp, *res = NULL;
98
99 assert(cxt);
100 assert(filename);
101
102 DBG(SCRIPT, ul_debug("opening %s", filename));
103 f = fopen(filename, "r");
104 if (!f)
105 return NULL;
106
107 dp = fdisk_new_script(cxt);
108 if (!dp)
109 goto done;
110
111 rc = fdisk_script_read_file(dp, f);
112 if (rc) {
113 errno = -rc;
114 goto done;
115 }
116
117 res = dp;
118done:
119 fclose(f);
120 if (!res)
121 fdisk_unref_script(dp);
122 else
123 errno = 0;
124
125 return res;
126}
127
128/**
129 * fdisk_ref_script:
130 * @dp: script pointer
131 *
132 * Incremparts reference counter.
133 */
134void fdisk_ref_script(struct fdisk_script *dp)
135{
136 if (dp)
137 dp->refcount++;
138}
139
140static void fdisk_reset_script(struct fdisk_script *dp)
141{
142 assert(dp);
143
144 DBG(SCRIPT, ul_debugobj(dp, "reset"));
145 fdisk_unref_table(dp->table);
146 dp->table = NULL;
147
148 while (!list_empty(&dp->headers)) {
149 struct fdisk_scriptheader *fi = list_entry(dp->headers.next,
150 struct fdisk_scriptheader, headers);
151 fdisk_script_free_header(dp, fi);
152 }
153 INIT_LIST_HEAD(&dp->headers);
154}
155
156/**
157 * fdisk_unref_script:
158 * @dp: script pointer
159 *
160 * De-incremparts reference counter, on zero the @dp is automatically
161 * deallocated.
162 */
163void fdisk_unref_script(struct fdisk_script *dp)
164{
165 if (!dp)
166 return;
167
168 dp->refcount--;
169 if (dp->refcount <= 0) {
170 fdisk_reset_script(dp);
171 fdisk_unref_context(dp->cxt);
172 DBG(SCRIPT, ul_debugobj(dp, "free script"));
173 free(dp);
174 }
175}
176
177static struct fdisk_scriptheader *script_get_header(struct fdisk_script *dp,
178 const char *name)
179{
180 struct list_head *p;
181
182 list_for_each(p, &dp->headers) {
183 struct fdisk_scriptheader *fi = list_entry(p, struct fdisk_scriptheader, headers);
184
185 if (strcasecmp(fi->name, name) == 0)
186 return fi;
187 }
188
189 return NULL;
190}
191
192/**
193 * fdisk_script_get_header:
194 * @dp: script instance
195 * @name: header name
196 *
197 * Returns: pointer to header data or NULL.
198 */
199const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name)
200{
201 struct fdisk_scriptheader *fi;
202
203 assert(dp);
204 assert(name);
205
206 fi = script_get_header(dp, name);
207 return fi ? fi->data : NULL;
208}
209
210
211/**
212 * fdisk_script_set_header:
213 * @dp: script instance
214 * @name: header name
215 * @data: header data (or NULL)
216 *
217 * The headers are used as global options (in script) for whole partition
218 * table, always one header per line.
219 *
220 * If no @data specified then the header is removed. If header does not exist
221 * and @data specified then a new header added.
222 *
223 * Note that libfdisk allows to specify arbitrary custom header, the default
224 * build-in headers are "unit" and "label", and some label specific headers
225 * (for example "uuid" and "name" for GPT).
226 *
227 * Returns: 0 on success, <0 on error
228 */
229int fdisk_script_set_header(struct fdisk_script *dp,
230 const char *name,
231 const char *data)
232{
233 struct fdisk_scriptheader *fi;
234
235 assert(dp);
236 assert(name);
237
238 if (!dp || !name)
239 return -EINVAL;
240
241 fi = script_get_header(dp, name);
242 if (!fi && !data)
243 return 0; /* want to remove header that does not exist, success */
244
245 if (!data) {
246 /* no data, remove the header */
247 fdisk_script_free_header(dp, fi);
248 return 0;
249 }
250
251 if (!fi) {
252 /* new header */
253 fi = calloc(1, sizeof(*fi));
254 if (!fi)
255 return -ENOMEM;
256 INIT_LIST_HEAD(&fi->headers);
257 fi->name = strdup(name);
258 fi->data = strdup(data);
259 if (!fi->data || !fi->name) {
260 fdisk_script_free_header(dp, fi);
261 return -ENOMEM;
262 }
263 list_add_tail(&fi->headers, &dp->headers);
264 } else {
265 /* update existing */
266 char *x = strdup(data);
267
268 if (!x)
269 return -ENOMEM;
270 free(fi->data);
271 fi->data = x;
272 }
273
274 if (strcmp(name, "label") == 0)
275 dp->label = NULL;
276
277 return 0;
278}
279
280/**
281 * fdisk_script_get_table:
282 * @dp: script
283 *
284 * The table (container with partitions) is possible to create by
285 * fdisk_script_read_context() or fdisk_script_read_file(), otherwise
286 * this function returns NULL.
287 *
288 * Returns: NULL or script.
289 */
290struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp)
291{
292 assert(dp);
293 return dp ? dp->table : NULL;
294}
295
296static struct fdisk_label *script_get_label(struct fdisk_script *dp)
297{
298 assert(dp);
299 assert(dp->cxt);
300
301 if (!dp->label) {
302 dp->label = fdisk_get_label(dp->cxt,
303 fdisk_script_get_header(dp, "label"));
304 DBG(SCRIPT, ul_debugobj(dp, "label '%s'", dp->label ? dp->label->name : ""));
305 }
306 return dp->label;
307}
308
309/**
310 * fdisk_script_get_nlines:
311 * @dp: script
312 *
313 * Returns: number of parsed lines or <0 on error.
314 */
315int fdisk_script_get_nlines(struct fdisk_script *dp)
316{
317 assert(dp);
318 return dp->nlines;
319}
320
321/**
322 * fdisk_script_read_context:
323 * @dp: script
324 * @cxt: context
325 *
326 * Reads data from the @cxt context (on disk partition table) into the script.
327 * If the context is no specified than defaults to context used for fdisk_new_script().
328 *
329 * Return: 0 on success, <0 on error.
330 */
331int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt)
332{
333 struct fdisk_label *lb;
334 int rc;
335 char *p = NULL;
336
337 assert(dp);
338
339 if (!cxt)
340 cxt = dp->cxt;
341
342 if (!dp || !cxt)
343 return -EINVAL;
344
345 DBG(SCRIPT, ul_debugobj(dp, "reading context into script"));
346 fdisk_reset_script(dp);
347
348 lb = fdisk_get_label(cxt, NULL);
349 if (!lb)
350 return -EINVAL;
351
352 /* allocate and fill new table */
353 rc = fdisk_get_partitions(cxt, &dp->table);
354 if (rc)
355 return rc;
356
357 /* generate headers */
358 rc = fdisk_script_set_header(dp, "label", fdisk_label_get_name(lb));
359
360 if (!rc && fdisk_get_disklabel_id(cxt, &p) == 0 && p) {
361 rc = fdisk_script_set_header(dp, "label-id", p);
362 free(p);
363 }
364 if (!rc && cxt->dev_path)
365 rc = fdisk_script_set_header(dp, "device", cxt->dev_path);
366 if (!rc)
367 rc = fdisk_script_set_header(dp, "unit", "sectors");
368
369 /* TODO: label specific headers (e.g. uuid for GPT) */
370
371 DBG(SCRIPT, ul_debugobj(dp, "read context done [rc=%d]", rc));
372 return rc;
373}
374
375/**
376 * fdisk_script_write_file:
377 * @dp: script
378 * @f: output file
379 *
380 * Writes script @dp to the ile @f.
381 *
382 * Returns: 0 on success, <0 on error.
383 */
384int fdisk_script_write_file(struct fdisk_script *dp, FILE *f)
385{
386 struct list_head *h;
387 struct fdisk_partition *pa;
388 struct fdisk_iter itr;
389 const char *devname = NULL;
390
391 assert(dp);
392 assert(f);
393
394 DBG(SCRIPT, ul_debugobj(dp, "writing script to file"));
395
396 /* script headers */
397 list_for_each(h, &dp->headers) {
398 struct fdisk_scriptheader *fi = list_entry(h, struct fdisk_scriptheader, headers);
399 fprintf(f, "%s: %s\n", fi->name, fi->data);
400 if (strcmp(fi->name, "device") == 0)
401 devname = fi->data;
402 }
403
404 if (!dp->table) {
405 DBG(SCRIPT, ul_debugobj(dp, "script table empty"));
406 return 0;
407 }
408
409 DBG(SCRIPT, ul_debugobj(dp, "%zu entries", fdisk_table_get_nents(dp->table)));
410
411 fputc('\n', f);
412
413 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
414 while (fdisk_table_next_partition(dp->table, &itr, &pa) == 0) {
415 char *p = NULL;
416
417 if (devname)
418 p = fdisk_partname(devname, pa->partno + 1);
419 if (p) {
420 DBG(SCRIPT, ul_debugobj(dp, "write %s entry", p));
421 fprintf(f, "%s :", p);
422 } else
423 fprintf(f, "%zu :", pa->partno + 1);
424
425 if (fdisk_partition_has_start(pa))
426 fprintf(f, " start=%12ju", pa->start);
427 if (fdisk_partition_has_size(pa))
428 fprintf(f, ", size=%12ju", pa->size);
429
430 if (pa->type && fdisk_parttype_get_string(pa->type))
431 fprintf(f, ", type=%s", fdisk_parttype_get_string(pa->type));
432 else if (pa->type)
433 fprintf(f, ", type=%x", fdisk_parttype_get_code(pa->type));
434
435 if (pa->uuid)
436 fprintf(f, ", uuid=%s", pa->uuid);
437 if (pa->name && *pa->name)
438 fprintf(f, ", name=\"%s\"", pa->name);
439
440 /* for MBR attr=80 means bootable */
441 if (pa->attrs) {
442 struct fdisk_label *lb = script_get_label(dp);
443
444 if (!lb || fdisk_label_get_type(lb) != FDISK_DISKLABEL_DOS)
445 fprintf(f, ", attrs=\"%s\"", pa->attrs);
446 }
447 if (fdisk_partition_is_bootable(pa))
448 fprintf(f, ", bootable");
449 fputc('\n', f);
450 }
451
452 DBG(SCRIPT, ul_debugobj(dp, "write script done"));
453 return 0;
454}
455
456static inline int is_header_line(const char *s)
457{
458 const char *p = strchr(s, ':');
459
460 if (!p || p == s || !*(p + 1) || strchr(s, '='))
461 return 0;
462
463 return 1;
464}
465
466/* parses "<name>: value", note modifies @s*/
467static int parse_header_line(struct fdisk_script *dp, char *s)
468{
469 int rc = -EINVAL;
470 char *name, *value;
471
472 DBG(SCRIPT, ul_debugobj(dp, " parse header '%s'", s));
473
474 if (!s || !*s)
475 return -EINVAL;
476
477 name = s;
478 value = strchr(s, ':');
479 if (!value)
480 goto done;
481 *value = '\0';
482 value++;
483
484 ltrim_whitespace((unsigned char *) name);
485 rtrim_whitespace((unsigned char *) name);
486 ltrim_whitespace((unsigned char *) value);
487 rtrim_whitespace((unsigned char *) value);
488
489 if (strcmp(name, "label") == 0) {
490 if (dp->cxt && !fdisk_get_label(dp->cxt, value))
491 goto done; /* unknown label name */
492 } else if (strcmp(name, "unit") == 0) {
493 if (strcmp(value, "sectors") != 0)
494 goto done; /* only "sectors" supported */
495 } else if (strcmp(name, "label-id") == 0
496 || strcmp(name, "device") == 0) {
497 ; /* whatever is posssible */
498 } else
499 goto done; /* unknown header */
500
501 if (*name && *value)
502 rc = fdisk_script_set_header(dp, name, value);
503done:
504 if (rc)
505 DBG(SCRIPT, ul_debugobj(dp, "header parse error: "
506 "[rc=%d, name='%s', value='%s']",
507 rc, name, value));
508 return rc;
509
510}
511
512/* returns zero terminated string with next token and @str is updated */
513static char *next_token(char **str)
514{
515 char *tk_begin = NULL,
516 *tk_end = NULL,
517 *end = NULL,
518 *p;
519 int open_quote = 0;
520
521 for (p = *str; p && *p; p++) {
522 if (!tk_begin) {
523 if (isblank(*p))
524 continue;
525 tk_begin = *p == '"' ? p + 1 : p;
526 }
527 if (*p == '"')
528 open_quote ^= 1;
529 if (open_quote)
530 continue;
531 if (isblank(*p) || *p == ',' || *p == ';' || *p == '"' )
532 tk_end = p;
533 else if (*(p + 1) == '\0')
534 tk_end = p + 1;
535 if (tk_begin && tk_end)
536 break;
537 }
538
539 if (!tk_end)
540 return NULL;
541 end = isblank(*tk_end) ? (char *) skip_blank(tk_end) : tk_end;
542 if (*end == ',' || *end == ';')
543 end++;
544
545 *tk_end = '\0';
546 *str = end;
547 return tk_begin;
548}
549
550static int next_number(char **s, uint64_t *num, int *power)
551{
552 char *tk;
553 int rc = -EINVAL;
554
555 assert(num);
556 assert(s);
557
558 tk = next_token(s);
559 if (tk)
560 rc = parse_size(tk, (uintmax_t *) num, power);
561 return rc;
562}
563
564static int next_string(char **s, char **str)
565{
566 char *tk;
567 int rc = -EINVAL;
568
569 assert(s);
570 assert(str);
571
572 tk = next_token(s);
573 if (tk) {
574 *str = strdup(tk);
575 rc = !*str ? -ENOMEM : 0;
576 }
577 return rc;
578}
579
580static int partno_from_devname(char *s)
581{
582 int pno;
583 size_t sz;
584 char *end, *p;
585
586 sz = rtrim_whitespace((unsigned char *)s);
587 p = s + sz - 1;
588
589 while (p > s && isdigit(*(p - 1)))
590 p--;
591
592 errno = 0;
593 pno = strtol(p, &end, 10);
594 if (errno || !end || p == end)
595 return -1;
596 return pno - 1;
597}
598
599/* dump format
600 * <device>: start=<num>, size=<num>, type=<string>, ...
601 */
602static int parse_script_line(struct fdisk_script *dp, char *s)
603{
604 char *p, *x;
605 struct fdisk_partition *pa;
606 int rc = 0;
607 uint64_t num;
608 int pno;
609
610 assert(dp);
611 assert(s);
612
613 DBG(SCRIPT, ul_debugobj(dp, " parse script line: '%s'", s));
614
615 pa = fdisk_new_partition();
616 if (!pa)
617 return -ENOMEM;
618
619 fdisk_partition_start_follow_default(pa, 1);
620 fdisk_partition_end_follow_default(pa, 1);
621 fdisk_partition_partno_follow_default(pa, 1);
622
623 /* set partno */
624 p = strchr(s, ':');
625 x = strchr(s, '=');
626 if (p && (!x || p < x)) {
627 *p = '\0';
628 p++;
629
630 pno = partno_from_devname(s);
631 if (pno >= 0) {
632 fdisk_partition_partno_follow_default(pa, 0);
633 fdisk_partition_set_partno(pa, pno);
634 }
635 } else
636 p = s;
637
638 while (rc == 0 && p && *p) {
639
640 DBG(SCRIPT, ul_debugobj(dp, " parsing '%s'", p));
641 p = (char *) skip_blank(p);
642
643 if (!strncasecmp(p, "start=", 6)) {
644 p += 6;
645 rc = next_number(&p, &num, NULL);
646 if (!rc) {
647 fdisk_partition_set_start(pa, num);
648 fdisk_partition_start_follow_default(pa, 0);
649 }
650 } else if (!strncasecmp(p, "size=", 5)) {
651 int pow = 0;
652
653 p += 5;
654 rc = next_number(&p, &num, &pow);
655 if (!rc) {
656 if (pow) /* specified as <num><suffix> */
657 num /= dp->cxt->sector_size;
658 else /* specified as number of sectors */
659 fdisk_partition_size_explicit(pa, 1);
660 fdisk_partition_set_size(pa, num);
661 fdisk_partition_end_follow_default(pa, 0);
662 }
663
664 } else if (!strncasecmp(p, "bootable", 8)) {
665 char *tk = next_token(&p);
666 if (strcmp(tk, "bootable") == 0)
667 pa->boot = 1;
668 else
669 rc = -EINVAL;
670
671 } else if (!strncasecmp(p, "attrs=", 6)) {
672 p += 6;
673 rc = next_string(&p, &pa->attrs);
674
675 } else if (!strncasecmp(p, "uuid=", 5)) {
676 p += 5;
677 rc = next_string(&p, &pa->uuid);
678
679 } else if (!strncasecmp(p, "name=", 5)) {
680 p += 5;
681 rc = next_string(&p, &pa->name);
682
683 } else if (!strncasecmp(p, "type=", 5) ||
684
685 !strncasecmp(p, "Id=", 3)) { /* backward compatiility */
686 char *type;
687
688 p += (*p == 'I' ? 3 : 5); /* "Id=" or "type=" */
689
690 rc = next_string(&p, &type);
691 if (rc)
692 break;
693 pa->type = fdisk_label_parse_parttype(
694 script_get_label(dp), type);
695 free(type);
696
697 if (!pa->type || fdisk_parttype_is_unknown(pa->type)) {
698 rc = -EINVAL;
699 fdisk_unref_parttype(pa->type);
700 pa->type = NULL;
701 break;
702 }
703
704 } else {
705 DBG(SCRIPT, ul_debugobj(dp, "script parse error: unknown field '%s'", p));
706 rc = -EINVAL;
707 break;
708 }
709 }
710
711 if (!rc)
712 rc = fdisk_table_add_partition(dp->table, pa);
713 if (rc)
714 DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
715
716 fdisk_unref_partition(pa);
717 return rc;
718}
719
720/* original sfdisk supports partition types shortcuts like 'L' = Linux native
721 */
722static struct fdisk_parttype *translate_type_shortcuts(struct fdisk_script *dp, char *str)
723{
724 struct fdisk_label *lb;
725 const char *type = NULL;
726
727 if (strlen(str) != 1)
728 return NULL;
729
730 lb = script_get_label(dp);
731 if (!lb)
732 return NULL;
733
734 if (lb->id == FDISK_DISKLABEL_DOS) {
735 switch (*str) {
736 case 'L': /* Linux */
737 type = "83";
738 break;
739 case 'S': /* Swap */
740 type = "82";
741 break;
742 case 'E': /* Dos extended */
743 type = "05";
744 break;
745 case 'X': /* Linux extended */
746 type = "85";
747 break;
748 }
749 } else if (lb->id == FDISK_DISKLABEL_GPT) {
750 switch (*str) {
751 case 'L': /* Linux */
752 type = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
753 break;
754 case 'S': /* Swap */
755 type = "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F";
756 break;
757 case 'H': /* Home */
758 type = "933AC7E1-2EB4-4F13-B844-0E14E2AEF915";
759 break;
760 }
761 }
762
763 return type ? fdisk_label_parse_parttype(lb, type) : NULL;
764}
765
766/* simple format:
767 * <start>, <size>, <type>, <bootable>, ...
768 */
769static int parse_commas_line(struct fdisk_script *dp, char *s)
770{
771 int rc = 0;
772 char *p = s, *str;
773 struct fdisk_partition *pa;
774 enum { ITEM_START, ITEM_SIZE, ITEM_TYPE, ITEM_BOOTABLE };
775 int item = -1;
776
777 assert(dp);
778 assert(s);
779
780 pa = fdisk_new_partition();
781 if (!pa)
782 return -ENOMEM;
783
784 fdisk_partition_start_follow_default(pa, 1);
785 fdisk_partition_end_follow_default(pa, 1);
786 fdisk_partition_partno_follow_default(pa, 1);
787
788 while (rc == 0 && p && *p) {
789 uint64_t num;
790 char *begin;
791
792 p = (char *) skip_blank(p);
793 item++;
794
795 DBG(SCRIPT, ul_debugobj(dp, " parsing item %d ('%s')", item, p));
796 begin = p;
797
798 switch (item) {
799 case ITEM_START:
800 if (*p == ',' || *p == ';')
801 fdisk_partition_start_follow_default(pa, 1);
802 else {
803 rc = next_number(&p, &num, NULL);
804 if (!rc)
805 fdisk_partition_set_start(pa, num);
806 fdisk_partition_start_follow_default(pa, 0);
807 }
808 break;
809 case ITEM_SIZE:
810 if (*p == ',' || *p == ';' || *p == '+')
811 fdisk_partition_end_follow_default(pa, 1);
812 else {
813 int pow = 0;
814 rc = next_number(&p, &num, &pow);
815 if (!rc) {
816 if (pow) /* specified as <size><suffix> */
817 num /= dp->cxt->sector_size;
818 else /* specified as number of sectors */
819 fdisk_partition_size_explicit(pa, 1);
820 fdisk_partition_set_size(pa, num);
821 }
822 fdisk_partition_end_follow_default(pa, 0);
823 }
824 break;
825 case ITEM_TYPE:
826 if (*p == ',' || *p == ';')
827 break; /* use default type */
828
829 rc = next_string(&p, &str);
830 if (rc)
831 break;
832
833 pa->type = translate_type_shortcuts(dp, str);
834 if (!pa->type)
835 pa->type = fdisk_label_parse_parttype(
836 script_get_label(dp), str);
837 free(str);
838
839 if (!pa->type || fdisk_parttype_is_unknown(pa->type)) {
840 rc = -EINVAL;
841 fdisk_unref_parttype(pa->type);
842 pa->type = NULL;
843 break;
844 }
845 break;
846 case ITEM_BOOTABLE:
847 if (*p == ',' || *p == ';')
848 break;
849 else {
850 char *tk = next_token(&p);
851 if (tk && *tk == '*' && *(tk + 1) == '\0')
852 pa->boot = 1;
853 else if (tk && *tk == '-' && *(tk + 1) == '\0')
854 pa->boot = 0;
855 else
856 rc = -EINVAL;
857 }
858 break;
859 default:
860 break;
861 }
862
863 if (begin == p)
864 p++;
865 }
866
867 if (!rc)
868 rc = fdisk_table_add_partition(dp->table, pa);
869 if (rc)
870 DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
871
872 fdisk_unref_partition(pa);
873 return rc;
874}
875
876/* modifies @s ! */
877int fdisk_script_read_buffer(struct fdisk_script *dp, char *s)
878{
879 int rc = 0;
880
881 assert(dp);
882 assert(s);
883
884 DBG(SCRIPT, ul_debugobj(dp, " parsing buffer"));
885
886 s = (char *) skip_blank(s);
887 if (!s || !*s)
888 return 0; /* nothing baby, ignore */
889
890 if (!dp->table) {
891 dp->table = fdisk_new_table();
892 if (!dp->table)
893 return -ENOMEM;
894 }
895
896 /* parse header lines only if no partition specified yet */
897 if (fdisk_table_is_empty(dp->table) && is_header_line(s))
898 rc = parse_header_line(dp, s);
899
900 /* parse script format */
901 else if (strchr(s, '='))
902 rc = parse_script_line(dp, s);
903
904 /* parse simple <value>, ... format */
905 else
906 rc = parse_commas_line(dp, s);
907
908 if (rc)
909 DBG(SCRIPT, ul_debugobj(dp, "%zu: parse error [rc=%d]",
910 dp->nlines, rc));
911 return rc;
912}
913
914/**
915 * fdisk_script_read_line:
916 * @dp: script
917 * @f: file
918 * @buf: buffer to store one line of the file
919 * @bufsz: buffer size
920 *
921 * Reads next line into dump.
922 *
923 * Returns: 0 on success, <0 on error, 1 when nothing to read.
924 */
925int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz)
926{
927 char *s;
928
929 assert(dp);
930 assert(f);
931
932 DBG(SCRIPT, ul_debugobj(dp, " parsing line %zu", dp->nlines));
933
934 /* read the next non-blank non-comment line */
935 do {
936 if (fgets(buf, bufsz, f) == NULL)
937 return 1;
938 dp->nlines++;
939 s = strchr(buf, '\n');
940 if (!s) {
941 /* Missing final newline? Otherwise an extremely */
942 /* long line - assume file was corrupted */
943 if (feof(f)) {
944 DBG(SCRIPT, ul_debugobj(dp, "no final newline"));
945 s = strchr(buf, '\0');
946 } else {
947 DBG(SCRIPT, ul_debugobj(dp,
948 "%zu: missing newline at line", dp->nlines));
949 return -EINVAL;
950 }
951 }
952
953 *s = '\0';
954 if (--s >= buf && *s == '\r')
955 *s = '\0';
956 s = (char *) skip_blank(buf);
957 } while (*s == '\0' || *s == '#');
958
959 return fdisk_script_read_buffer(dp, s);
960}
961
962
963/**
964 * fdisk_script_read_file:
965 * @dp: script
966 * @f: input file
967 *
968 * Reads file @f into script @dp.
969 *
970 * Returns: 0 on success, <0 on error.
971 */
972int fdisk_script_read_file(struct fdisk_script *dp, FILE *f)
973{
974 char buf[BUFSIZ];
975 int rc;
976
977 assert(dp);
978 assert(f);
979
980 DBG(SCRIPT, ul_debugobj(dp, "parsing file"));
981
982 while (!feof(f)) {
983 rc = fdisk_script_read_line(dp, f, buf, sizeof(buf));
984 if (rc)
985 break;
986 }
987
988 if (rc == 1)
989 rc = 0; /* end of file */
990
991 DBG(SCRIPT, ul_debugobj(dp, "parsing file done [rc=%d]", rc));
992 return rc;
993}
994
995/**
996 * fdisk_set_script:
997 * @cxt: context
998 * @dp: script (or NULL to remove previous reference)
999 *
1000 * Sets reference to the @dp script. The script headers might be used by label
1001 * drivers to overwrite built-in defaults (for example disk label Id) and label
1002 * driver might optimize the default semantic to be more usable for scripts
1003 * (for example to not ask for primary/logical/extended partition type).
1004 *
1005 * Note that script also contains reference to the fdisk context (see
1006 * fdisk_new_script()). This context may be completely independent on
1007 * context used for fdisk_set_script().
1008 *
1009 * Returns: <0 on error, 0 on success.
1010 */
1011int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp)
1012{
1013 assert(cxt);
1014
1015 /* unref old */
1016 if (cxt->script)
1017 fdisk_unref_script(cxt->script);
1018
1019 /* ref new */
1020 cxt->script = dp;
1021 if (cxt->script) {
1022 DBG(CXT, ul_debugobj(cxt, "setting reference to script %p", cxt->script));
1023 fdisk_ref_script(cxt->script);
1024 }
1025
1026 return 0;
1027}
1028
1029/**
1030 * fdisk_get_script:
1031 * @cxt: context
1032 *
1033 * Returns: the current script or NULL.
1034 */
1035struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt)
1036{
1037 assert(cxt);
1038 return cxt->script;
1039}
1040
1041/**
1042 * fdisk_apply_script_headers:
1043 * @cxt: context
1044 * @dp: script
1045 *
1046 * Associte context @cxt with script @dp and creates a new empty disklabel.
1047 *
1048 * Returns: 0 on success, <0 on error.
1049 */
1050int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp)
1051{
1052 const char *name;
1053
1054 assert(cxt);
1055 assert(dp);
1056
1057 DBG(SCRIPT, ul_debugobj(dp, "applying script headers"));
1058 fdisk_set_script(cxt, dp);
1059
1060 /* create empty label */
1061 name = fdisk_script_get_header(dp, "label");
1062 if (!name)
1063 return -EINVAL;
1064
1065 return fdisk_create_disklabel(cxt, name);
1066}
1067
1068/**
1069 * fdisk_apply_script:
1070 * @cxt: context
1071 * @dp: script
1072 *
1073 * This function creates a new disklabel and partition within context @cxt. You
1074 * have to call fdisk_write_disklabel() to apply changes to the device.
1075 *
1076 * Returns: 0 on error, <0 on error.
1077 */
1078int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp)
1079{
1080 int rc;
1081 struct fdisk_script *old;
1082
1083 assert(dp);
1084 assert(cxt);
1085
1086 DBG(CXT, ul_debugobj(cxt, "applying script %p", dp));
1087
1088 old = fdisk_get_script(cxt);
1089
1090 /* create empty disk label */
1091 rc = fdisk_apply_script_headers(cxt, dp);
1092
1093 /* create partitions */
1094 if (!rc && dp->table)
1095 rc = fdisk_apply_table(cxt, dp->table);
1096
1097 fdisk_set_script(cxt, old);
1098 DBG(CXT, ul_debugobj(cxt, "script done [rc=%d]", rc));
1099 return rc;
1100}
1101
1102#ifdef TEST_PROGRAM
1103int test_dump(struct fdisk_test *ts, int argc, char *argv[])
1104{
1105 char *devname = argv[1];
1106 struct fdisk_context *cxt;
1107 struct fdisk_script *dp;
1108
1109 cxt = fdisk_new_context();
1110 fdisk_assign_device(cxt, devname, 1);
1111
1112 dp = fdisk_new_script(cxt);
1113 fdisk_script_read_context(dp, NULL);
1114
1115 fdisk_script_write_file(dp, stdout);
1116 fdisk_unref_script(dp);
1117 fdisk_unref_context(cxt);
1118
1119 return 0;
1120}
1121
1122int test_read(struct fdisk_test *ts, int argc, char *argv[])
1123{
1124 char *filename = argv[1];
1125 struct fdisk_script *dp;
1126 struct fdisk_context *cxt;
1127 FILE *f;
1128
1129 if (!(f = fopen(filename, "r")))
1130 err(EXIT_FAILURE, "%s: cannot open", filename);
1131
1132 cxt = fdisk_new_context();
1133 dp = fdisk_new_script(cxt);
1134
1135 fdisk_script_read_file(dp, f);
1136 fclose(f);
1137
1138 fdisk_script_write_file(dp, stdout);
1139 fdisk_unref_script(dp);
1140 fdisk_unref_context(cxt);
1141
1142 return 0;
1143}
1144
1145int test_stdin(struct fdisk_test *ts, int argc, char *argv[])
1146{
1147 char buf[BUFSIZ];
1148 struct fdisk_script *dp;
1149 struct fdisk_context *cxt;
1150 int rc = 0;
1151
1152 cxt = fdisk_new_context();
1153 dp = fdisk_new_script(cxt);
1154 fdisk_script_set_header(dp, "label", "dos");
1155
1156 printf("<start>, <size>, <type>, <bootable: *|->\n");
1157 do {
1158 struct fdisk_partition *pa;
1159 size_t n = fdisk_table_get_nents(dp->table);
1160
1161 printf(" #%zu :\n", n + 1);
1162 rc = fdisk_script_read_line(dp, stdin, buf, sizeof(buf));
1163
1164 if (rc == 0) {
1165 pa = fdisk_table_get_partition(dp->table, n);
1166 printf(" #%zu %12ju %12ju\n", n + 1,
1167 fdisk_partition_get_start(pa),
1168 fdisk_partition_get_size(pa));
1169 }
1170 } while (rc == 0);
1171
1172 if (!rc)
1173 fdisk_script_write_file(dp, stdout);
1174 fdisk_unref_script(dp);
1175 fdisk_unref_context(cxt);
1176
1177 return rc;
1178}
1179
1180int test_apply(struct fdisk_test *ts, int argc, char *argv[])
1181{
1182 char *devname = argv[1], *scriptname = argv[2];
1183 struct fdisk_context *cxt;
1184 struct fdisk_script *dp = NULL;
1185 struct fdisk_table *tb = NULL;
1186 struct fdisk_iter *itr = NULL;
1187 struct fdisk_partition *pa = NULL;
1188 int rc;
1189
1190 cxt = fdisk_new_context();
1191 fdisk_assign_device(cxt, devname, 0);
1192
1193 dp = fdisk_new_script_from_file(cxt, scriptname);
1194 if (!dp)
1195 return -errno;
1196
1197 rc = fdisk_apply_script(cxt, dp);
1198 if (rc)
1199 goto done;
1200 fdisk_unref_script(dp);
1201
1202 /* list result */
1203 fdisk_list_disklabel(cxt);
1204 fdisk_get_partitions(cxt, &tb);
1205
1206 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
1207 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
1208 printf(" #%zu %12ju %12ju\n", fdisk_partition_get_partno(pa),
1209 fdisk_partition_get_start(pa),
1210 fdisk_partition_get_size(pa));
1211 }
1212
1213done:
1214 fdisk_free_iter(itr);
1215 fdisk_unref_table(tb);
1216
1217 /*fdisk_write_disklabel(cxt);*/
1218 fdisk_unref_context(cxt);
1219 return 0;
1220}
1221
1222int main(int argc, char *argv[])
1223{
1224 struct fdisk_test tss[] = {
1225 { "--dump", test_dump, "<device> dump PT as script" },
1226 { "--read", test_read, "<file> read PT script from file" },
1227 { "--apply", test_apply, "<device> <file> try apply script from file to device" },
1228 { "--stdin", test_stdin, " read input like sfdisk" },
1229 { NULL }
1230 };
1231
1232 return fdisk_run_test(tss, argc, argv);
1233}
1234
1235#endif