1 
2 #include "fdiskP.h"
3 #include "strutils.h"
4 #include "carefulputc.h"
5 #include "mangle.h"
6 
7 /**
8  * SECTION: script
9  * @title: Script
10  * @short_description: complex way to create and dump partition table
11  *
12  * This interface can be used to compose in-memory partition table with all details,
13  * write all partition table description to human readable text file, read it
14  * from the file, and apply the script to on-disk label.
15  *
16  * The libfdisk scripts are based on original sfdisk script (dumps). Each
17  * script has two parts: script headers and partition table entries
18  * (partitions). The script is possible to dump in JSON too (read JSON is not
19  * implemented yet).
20  *
21  * For more details about script format see sfdisk man page.
22  *
23  * There are four ways how to build the script:
24  *
25  * - read the current on-disk partition table by fdisk_script_read_context())
26  * - read it from text file by fdisk_script_read_file()
27  * - read it interactively from user by fdisk_script_read_line() and fdisk_script_set_fgets()
28  * - manually in code by fdisk_script_set_header() and fdisk_script_set_table()
29  *
30  * The read functions fdisk_script_read_context() and fdisk_script_read_file()
31  * creates always a new script partition table. The table (see
32  * fdisk_script_get_table()) is possible to modify by standard
33  * fdisk_table_...() functions and then apply by fdisk_apply_script().
34  *
35  * Note that script API is fully non-interactive and forces libfdisk to not use
36  * standard dialog driven partitioning as we have in fdisk(8).
37  */
38 
39 /* script header (e.g. unit: sectors) */
40 struct fdisk_scriptheader {
41 	struct list_head	headers;
42 	char			*name;
43 	char			*data;
44 };
45 
46 /* script control struct */
47 struct fdisk_script {
48 	struct fdisk_table	*table;
49 	struct list_head	headers;
50 	struct fdisk_context	*cxt;
51 
52 	int			refcount;
53 	char			*(*fn_fgets)(struct fdisk_script *, char *, size_t, FILE *);
54 	void			*userdata;
55 
56 	/* parser's state */
57 	size_t			nlines;
58 	struct fdisk_label	*label;
59 
60 	unsigned int		json : 1,		/* JSON output */
61 				force_label : 1;	/* label: <name> specified */
62 };
63 
fdisk_script_free_header(struct fdisk_scriptheader * fi)64 static void fdisk_script_free_header(struct fdisk_scriptheader *fi)
65 {
66 	if (!fi)
67 		return;
68 
69 	DBG(SCRIPT, ul_debugobj(fi, "free header %s", fi->name));
70 	free(fi->name);
71 	free(fi->data);
72 	list_del(&fi->headers);
73 	free(fi);
74 }
75 
76 /**
77  * fdisk_new_script:
78  * @cxt: context
79  *
80  * The script hold fdisk_table and additional information to read/write
81  * script to the file.
82  *
83  * Returns: newly allocated script struct.
84  */
fdisk_new_script(struct fdisk_context * cxt)85 struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt)
86 {
87 	struct fdisk_script *dp = NULL;
88 
89 	dp = calloc(1, sizeof(*dp));
90 	if (!dp)
91 		return NULL;
92 
93 	DBG(SCRIPT, ul_debugobj(dp, "alloc"));
94 	dp->refcount = 1;
95 	dp->cxt = cxt;
96 	fdisk_ref_context(cxt);
97 
98 	INIT_LIST_HEAD(&dp->headers);
99 	return dp;
100 }
101 
102 /**
103  * fdisk_new_script_from_file:
104  * @cxt: context
105  * @filename: path to the script file
106  *
107  * Allocates a new script and reads script from @filename.
108  *
109  * Returns: new script instance or NULL in case of error (check errno for more details).
110  */
fdisk_new_script_from_file(struct fdisk_context * cxt,const char * filename)111 struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
112 						 const char *filename)
113 {
114 	int rc;
115 	FILE *f;
116 	struct fdisk_script *dp, *res = NULL;
117 
118 	assert(cxt);
119 	assert(filename);
120 
121 	DBG(SCRIPT, ul_debug("opening %s", filename));
122 	f = fopen(filename, "r");
123 	if (!f)
124 		return NULL;
125 
126 	dp = fdisk_new_script(cxt);
127 	if (!dp)
128 		goto done;
129 
130 	rc = fdisk_script_read_file(dp, f);
131 	if (rc) {
132 		errno = -rc;
133 		goto done;
134 	}
135 
136 	res = dp;
137 done:
138 	fclose(f);
139 	if (!res)
140 		fdisk_unref_script(dp);
141 	else
142 		errno = 0;
143 
144 	return res;
145 }
146 
147 /**
148  * fdisk_ref_script:
149  * @dp: script pointer
150  *
151  * Increments reference counter.
152  */
fdisk_ref_script(struct fdisk_script * dp)153 void fdisk_ref_script(struct fdisk_script *dp)
154 {
155 	if (dp)
156 		dp->refcount++;
157 }
158 
fdisk_reset_script(struct fdisk_script * dp)159 static void fdisk_reset_script(struct fdisk_script *dp)
160 {
161 	assert(dp);
162 
163 	DBG(SCRIPT, ul_debugobj(dp, "reset"));
164 
165 	if (dp->table)
166 		fdisk_reset_table(dp->table);
167 
168 	while (!list_empty(&dp->headers)) {
169 		struct fdisk_scriptheader *fi = list_entry(dp->headers.next,
170 						  struct fdisk_scriptheader, headers);
171 		fdisk_script_free_header(fi);
172 	}
173 	INIT_LIST_HEAD(&dp->headers);
174 }
175 
176 /**
177  * fdisk_unref_script:
178  * @dp: script pointer
179  *
180  * Decrements reference counter, on zero the @dp is automatically
181  * deallocated.
182  */
fdisk_unref_script(struct fdisk_script * dp)183 void fdisk_unref_script(struct fdisk_script *dp)
184 {
185 	if (!dp)
186 		return;
187 
188 	dp->refcount--;
189 	if (dp->refcount <= 0) {
190 		fdisk_reset_script(dp);
191 		fdisk_unref_context(dp->cxt);
192 		fdisk_unref_table(dp->table);
193 		DBG(SCRIPT, ul_debugobj(dp, "free script"));
194 		free(dp);
195 	}
196 }
197 
198 /**
199  * fdisk_script_set_userdata
200  * @dp: script
201  * @data: your data
202  *
203  * Sets data usable for example in callbacks (e.g fdisk_script_set_fgets()).
204  *
205  * Returns: 0 on success, <0 on error.
206  */
fdisk_script_set_userdata(struct fdisk_script * dp,void * data)207 int fdisk_script_set_userdata(struct fdisk_script *dp, void *data)
208 {
209 	assert(dp);
210 	dp->userdata = data;
211 	return 0;
212 }
213 
214 /**
215  * fdisk_script_get_userdata
216  * @dp: script
217  *
218  * Returns: user data or NULL.
219  */
fdisk_script_get_userdata(struct fdisk_script * dp)220 void *fdisk_script_get_userdata(struct fdisk_script *dp)
221 {
222 	assert(dp);
223 	return dp->userdata;
224 }
225 
script_get_header(struct fdisk_script * dp,const char * name)226 static struct fdisk_scriptheader *script_get_header(struct fdisk_script *dp,
227 						     const char *name)
228 {
229 	struct list_head *p;
230 
231 	list_for_each(p, &dp->headers) {
232 		struct fdisk_scriptheader *fi = list_entry(p, struct fdisk_scriptheader, headers);
233 
234 		if (strcasecmp(fi->name, name) == 0)
235 			return fi;
236 	}
237 
238 	return NULL;
239 }
240 
241 /**
242  * fdisk_script_get_header:
243  * @dp: script instance
244  * @name: header name
245  *
246  * Returns: pointer to header data or NULL.
247  */
fdisk_script_get_header(struct fdisk_script * dp,const char * name)248 const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name)
249 {
250 	struct fdisk_scriptheader *fi;
251 
252 	assert(dp);
253 	assert(name);
254 
255 	fi = script_get_header(dp, name);
256 	return fi ? fi->data : NULL;
257 }
258 
259 /**
260  * fdisk_script_set_header:
261  * @dp: script instance
262  * @name: header name
263  * @data: header data (or NULL)
264  *
265  * The headers are used as global options for whole partition
266  * table, always one header per line.
267  *
268  * If no @data is specified then the header is removed. If header does not exist
269  * and @data is specified then a new header is added.
270  *
271  * Note that libfdisk can be used to specify arbitrary custom header, the default
272  * built-in headers are "unit" and "label", and some label specific headers
273  * (for example "uuid" and "name" for GPT).
274  *
275  * Returns: 0 on success, <0 on error
276  */
fdisk_script_set_header(struct fdisk_script * dp,const char * name,const char * data)277 int fdisk_script_set_header(struct fdisk_script *dp,
278 			    const char *name,
279 			    const char *data)
280 {
281 	struct fdisk_scriptheader *fi;
282 
283 	if (!dp || !name)
284 		return -EINVAL;
285 
286 	fi = script_get_header(dp, name);
287 	if (!fi && !data)
288 		return 0;	/* want to remove header that does not exist, success */
289 
290 	if (!data) {
291 		DBG(SCRIPT, ul_debugobj(dp, "freeing header %s", name));
292 
293 		/* no data, remove the header */
294 		fdisk_script_free_header(fi);
295 		return 0;
296 	}
297 
298 	if (!fi) {
299 		int rc;
300 
301 		DBG(SCRIPT, ul_debugobj(dp, "setting new header %s='%s'", name, data));
302 
303 		/* new header */
304 		fi = calloc(1, sizeof(*fi));
305 		if (!fi)
306 			return -ENOMEM;
307 		INIT_LIST_HEAD(&fi->headers);
308 
309 		rc = strdup_to_struct_member(fi, name, name);
310 		if (!rc)
311 			rc = strdup_to_struct_member(fi, data, data);
312 		if (rc) {
313 			fdisk_script_free_header(fi);
314 			return rc;
315 		}
316 		list_add_tail(&fi->headers, &dp->headers);
317 	} else {
318 		/* update existing */
319 		char *x = strdup(data);
320 
321 		DBG(SCRIPT, ul_debugobj(dp, "update '%s' header '%s' -> '%s'", name, fi->data, data));
322 
323 		if (!x)
324 			return -ENOMEM;
325 		free(fi->data);
326 		fi->data = x;
327 	}
328 
329 	if (strcmp(name, "label") == 0)
330 		dp->label = NULL;
331 
332 	return 0;
333 }
334 
335 /**
336  * fdisk_script_get_table:
337  * @dp: script
338  *
339  * The table represents partitions holded by the script. The table is possible to
340  * fill by fdisk_script_read_context() or fdisk_script_read_file(). All the "read"
341  * functions remove old partitions from the table. See also fdisk_script_set_table().
342  *
343  * Returns: NULL or script table.
344  */
fdisk_script_get_table(struct fdisk_script * dp)345 struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp)
346 {
347 	assert(dp);
348 
349 	if (!dp->table)
350 		/*
351 		 * Make sure user has access to the same table as script. If
352 		 * there is no table then create a new one and reuse it later.
353 		 */
354 		dp->table = fdisk_new_table();
355 
356 	return dp->table;
357 }
358 
359 /**
360  * fdisk_script_set_table:
361  * @dp: script
362  * @tb: table
363  *
364  * Replaces table used by script and creates a new reference to @tb. This
365  * function can be used to generate a new script table independently on the current
366  * context and without any file reading.
367  *
368  * This is useful for example to create partition table with the same basic
369  * settings (e.g. label-id, ...) but with different partitions -- just call
370  * fdisk_script_read_context() to get current settings and then
371  * fdisk_script_set_table() to set a different layout.
372  *
373  * If @tb is NULL then the current script table is unreferenced.
374  *
375  * Note that script read_ functions (e.g. fdisk_script_read_context()) create
376  * always a new script table.
377  *
378  * Returns: 0 on success, <0 on error
379  *
380  * Since: 2.35
381  */
fdisk_script_set_table(struct fdisk_script * dp,struct fdisk_table * tb)382 int fdisk_script_set_table(struct fdisk_script *dp, struct fdisk_table *tb)
383 {
384 	if (!dp)
385 		return -EINVAL;
386 
387 	fdisk_ref_table(tb);
388 	fdisk_unref_table(dp->table);
389 	dp->table = tb;
390 
391 	DBG(SCRIPT, ul_debugobj(dp, "table replaced"));
392 	return 0;
393 }
394 
script_get_label(struct fdisk_script * dp)395 static struct fdisk_label *script_get_label(struct fdisk_script *dp)
396 {
397 	assert(dp);
398 	assert(dp->cxt);
399 
400 	if (!dp->label) {
401 		dp->label = fdisk_get_label(dp->cxt,
402 					fdisk_script_get_header(dp, "label"));
403 		DBG(SCRIPT, ul_debugobj(dp, "label '%s'", dp->label ? dp->label->name : ""));
404 	}
405 	return dp->label;
406 }
407 
408 /**
409  * fdisk_script_get_nlines:
410  * @dp: script
411  *
412  * Returns: number of parsed lines or <0 on error.
413  */
fdisk_script_get_nlines(struct fdisk_script * dp)414 int fdisk_script_get_nlines(struct fdisk_script *dp)
415 {
416 	assert(dp);
417 	return dp->nlines;
418 }
419 
420 /**
421  * fdisk_script_has_force_label:
422  * @dp: script
423  *
424  * Label has been explicitly specified in the script.
425  *
426  * Since: 2.30
427  *
428  * Returns: true if "label: name" has been parsed.
429  */
fdisk_script_has_force_label(struct fdisk_script * dp)430 int fdisk_script_has_force_label(struct fdisk_script *dp)
431 {
432 	assert(dp);
433 	return dp->force_label;
434 }
435 
436 
437 /**
438  * fdisk_script_read_context:
439  * @dp: script
440  * @cxt: context
441  *
442  * Reads data from the @cxt context (on disk partition table) into the script.
443  * If the context is not specified then defaults to context used for fdisk_new_script().
444  *
445  * Return: 0 on success, <0 on error.
446  */
fdisk_script_read_context(struct fdisk_script * dp,struct fdisk_context * cxt)447 int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt)
448 {
449 	struct fdisk_label *lb;
450 	int rc;
451 	char *p = NULL;
452 	char buf[64];
453 
454 	if (!dp || (!cxt && !dp->cxt))
455 		return -EINVAL;
456 
457 	if (!cxt)
458 		cxt = dp->cxt;
459 
460 	DBG(SCRIPT, ul_debugobj(dp, "reading context into script"));
461 	fdisk_reset_script(dp);
462 
463 	lb = fdisk_get_label(cxt, NULL);
464 	if (!lb)
465 		return -EINVAL;
466 
467 	/* allocate (if not yet) and fill table */
468 	rc = fdisk_get_partitions(cxt, &dp->table);
469 	if (rc)
470 		return rc;
471 
472 	/* generate headers */
473 	rc = fdisk_script_set_header(dp, "label", fdisk_label_get_name(lb));
474 
475 	if (!rc && fdisk_get_disklabel_id(cxt, &p) == 0 && p) {
476 		rc = fdisk_script_set_header(dp, "label-id", p);
477 		free(p);
478 	}
479 	if (!rc && cxt->dev_path)
480 		rc = fdisk_script_set_header(dp, "device", cxt->dev_path);
481 	if (!rc)
482 		rc = fdisk_script_set_header(dp, "unit", "sectors");
483 
484 	if (!rc && fdisk_is_label(cxt, GPT)) {
485 		struct fdisk_labelitem item = FDISK_LABELITEM_INIT;
486 
487 		/* first-lba */
488 		rc = fdisk_get_disklabel_item(cxt, GPT_LABELITEM_FIRSTLBA, &item);
489 		if (!rc) {
490 			snprintf(buf, sizeof(buf), "%"PRIu64, item.data.num64);
491 			rc = fdisk_script_set_header(dp, "first-lba", buf);
492 		}
493 
494 		/* last-lba */
495 		if (!rc)
496 			rc = fdisk_get_disklabel_item(cxt, GPT_LABELITEM_LASTLBA, &item);
497 		if (!rc) {
498 			snprintf(buf, sizeof(buf), "%"PRIu64, item.data.num64);
499 			rc = fdisk_script_set_header(dp, "last-lba", buf);
500 		}
501 
502 		/* table-length */
503 		if (!rc) {
504 			size_t n = fdisk_get_npartitions(cxt);
505 			if (n != FDISK_GPT_NPARTITIONS_DEFAULT) {
506 				snprintf(buf, sizeof(buf), "%zu", n);
507 				rc = fdisk_script_set_header(dp, "table-length", buf);
508 			}
509 		}
510 	}
511 
512 	if (!rc && fdisk_get_grain_size(cxt) != 2048 * 512) {
513 		snprintf(buf, sizeof(buf), "%lu", fdisk_get_grain_size(cxt));
514 		rc = fdisk_script_set_header(dp, "grain", buf);
515 	}
516 
517 	if (!rc) {
518 		snprintf(buf, sizeof(buf), "%lu", fdisk_get_sector_size(cxt));
519 		rc = fdisk_script_set_header(dp, "sector-size", buf);
520 	}
521 
522 	DBG(SCRIPT, ul_debugobj(dp, "read context done [rc=%d]", rc));
523 	return rc;
524 }
525 
526 /**
527  * fdisk_script_enable_json:
528  * @dp: script
529  * @json: 0 or 1
530  *
531  * Disable/Enable JSON output format.
532  *
533  * Returns: 0 on success, <0 on error.
534  */
fdisk_script_enable_json(struct fdisk_script * dp,int json)535 int fdisk_script_enable_json(struct fdisk_script *dp, int json)
536 {
537 	assert(dp);
538 
539 	dp->json = json;
540 	return 0;
541 }
542 
fput_indent(int indent,FILE * f)543 static void fput_indent(int indent, FILE *f)
544 {
545 	int i;
546 
547 	for (i = 0; i <= indent; i++)
548 		fputs("   ", f);
549 }
550 
fput_var_separator(int * nvars,FILE * f)551 static void fput_var_separator(int *nvars, FILE *f)
552 {
553 	if (*nvars > 0)
554 		fputs(", ", f);
555 	++(*nvars);
556 }
557 
write_file_json(struct fdisk_script * dp,FILE * f)558 static int write_file_json(struct fdisk_script *dp, FILE *f)
559 {
560 	struct list_head *h;
561 	struct fdisk_partition *pa;
562 	struct fdisk_iter itr;
563 	const char *devname = NULL;
564 	int ct = 0, indent = 0;
565 
566 	assert(dp);
567 	assert(f);
568 
569 	DBG(SCRIPT, ul_debugobj(dp, "writing json dump to file"));
570 
571 	fputs("{\n", f);
572 
573 	fput_indent(indent, f);
574 	fputs("\"partitiontable\": {\n", f);
575 	indent++;
576 
577 	/* script headers */
578 	list_for_each(h, &dp->headers) {
579 		struct fdisk_scriptheader *fi = list_entry(h, struct fdisk_scriptheader, headers);
580 		const char *name = fi->name;
581 		int num = 0;
582 
583 		if (strcmp(name, "first-lba") == 0) {
584 			name = "firstlba";
585 			num = 1;
586 		} else if (strcmp(name, "last-lba") == 0) {
587 			name = "lastlba";
588 			num = 1;
589 		} else if (strcmp(name, "sector-size") == 0) {
590 			name = "sectorsize";
591 			num = 1;
592 		} else if (strcmp(name, "label-id") == 0)
593 			name = "id";
594 
595 		fput_indent(indent, f);
596 		fputs_quoted_json_lower(name, f);
597 		fputs(":", f);
598 		if (!num)
599 			fputs_quoted_json(fi->data, f);
600 		else
601 			fputs(fi->data, f);
602 
603 		if (!dp->table && fi == list_last_entry(&dp->headers, struct fdisk_scriptheader, headers))
604 			fputc('\n', f);
605 		else
606 			fputs(",\n", f);
607 
608 		if (strcmp(name, "device") == 0)
609 			devname = fi->data;
610 	}
611 
612 
613 	if (!dp->table || fdisk_table_is_empty(dp->table)) {
614 		DBG(SCRIPT, ul_debugobj(dp, "script table empty"));
615 		goto done;
616 	}
617 
618 	DBG(SCRIPT, ul_debugobj(dp, "%zu entries", fdisk_table_get_nents(dp->table)));
619 
620 	fput_indent(indent, f);
621 	fputs("\"partitions\": [\n", f);
622 	indent++;
623 
624 	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
625 	while (fdisk_table_next_partition(dp->table, &itr, &pa) == 0) {
626 		char *p = NULL;
627 		int nvars = 0;
628 
629 		ct++;
630 		fput_indent(indent, f);
631 		fputc('{', f);
632 		if (devname)
633 			p = fdisk_partname(devname, pa->partno + 1);
634 		if (p) {
635 			DBG(SCRIPT, ul_debugobj(dp, "write %s entry", p));
636 			fputs("\"node\":", f);
637 			fputs_quoted_json(p, f);
638 			nvars++;
639 		}
640 
641 		if (fdisk_partition_has_start(pa)) {
642 			fput_var_separator(&nvars, f);
643 			fprintf(f, "\"start\":%ju", (uintmax_t)pa->start);
644 		}
645 		if (fdisk_partition_has_size(pa)) {
646 			fput_var_separator(&nvars, f);
647 			fprintf(f, "\"size\":%ju", (uintmax_t)pa->size);
648 		}
649 		if (pa->type && fdisk_parttype_get_string(pa->type)) {
650 			fput_var_separator(&nvars, f);
651 			fputs("\"type\":", f);
652 			fputs_quoted_json(fdisk_parttype_get_string(pa->type), f);
653 		} else if (pa->type) {
654 			fput_var_separator(&nvars, f);
655 			fprintf(f, "\"type\":\"%x\"", fdisk_parttype_get_code(pa->type));
656 		}
657 
658 		if (pa->uuid) {
659 			fput_var_separator(&nvars, f);
660 			fputs("\"uuid\":", f);
661 			fputs_quoted_json(pa->uuid, f);
662 		}
663 		if (pa->name && *pa->name) {
664 			fput_var_separator(&nvars, f);
665 			fputs("\"name\":", f),
666 			fputs_quoted_json(pa->name, f);
667 		}
668 
669 		/* for MBR attr=80 means bootable */
670 		if (pa->attrs) {
671 			struct fdisk_label *lb = script_get_label(dp);
672 
673 			if (!lb || fdisk_label_get_type(lb) != FDISK_DISKLABEL_DOS) {
674 				fput_var_separator(&nvars, f);
675 				fputs("\"attrs\":", f);
676 				fputs_quoted_json(pa->attrs, f);
677 			}
678 		}
679 		if (fdisk_partition_is_bootable(pa)) {
680 			fput_var_separator(&nvars, f);
681 			fprintf(f, "\"bootable\":true");
682 		}
683 
684 		if ((size_t)ct < fdisk_table_get_nents(dp->table))
685 			fputs("},\n", f);
686 		else
687 			fputs("}\n", f);
688 	}
689 
690 	indent--;
691 	fput_indent(indent, f);
692 	fputs("]\n", f);
693 done:
694 	indent--;
695 	fput_indent(indent, f);
696 	fputs("}\n}\n", f);
697 
698 	DBG(SCRIPT, ul_debugobj(dp, "write script done"));
699 	return 0;
700 }
701 
write_file_sfdisk(struct fdisk_script * dp,FILE * f)702 static int write_file_sfdisk(struct fdisk_script *dp, FILE *f)
703 {
704 	struct list_head *h;
705 	struct fdisk_partition *pa;
706 	struct fdisk_iter itr;
707 	const char *devname = NULL;
708 
709 	assert(dp);
710 	assert(f);
711 
712 	DBG(SCRIPT, ul_debugobj(dp, "writing sfdisk-like script to file"));
713 
714 	/* script headers */
715 	list_for_each(h, &dp->headers) {
716 		struct fdisk_scriptheader *fi = list_entry(h, struct fdisk_scriptheader, headers);
717 		fprintf(f, "%s: %s\n", fi->name, fi->data);
718 		if (strcmp(fi->name, "device") == 0)
719 			devname = fi->data;
720 	}
721 
722 	if (!dp->table || fdisk_table_is_empty(dp->table)) {
723 		DBG(SCRIPT, ul_debugobj(dp, "script table empty"));
724 		return 0;
725 	}
726 
727 	DBG(SCRIPT, ul_debugobj(dp, "%zu entries", fdisk_table_get_nents(dp->table)));
728 
729 	fputc('\n', f);
730 
731 	fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
732 	while (fdisk_table_next_partition(dp->table, &itr, &pa) == 0) {
733 		char *p = NULL;
734 
735 		if (devname)
736 			p = fdisk_partname(devname, pa->partno + 1);
737 		if (p) {
738 			DBG(SCRIPT, ul_debugobj(dp, "write %s entry", p));
739 			fprintf(f, "%s :", p);
740 		} else
741 			fprintf(f, "%zu :", pa->partno + 1);
742 
743 		if (fdisk_partition_has_start(pa))
744 			fprintf(f, " start=%12ju", (uintmax_t)pa->start);
745 		if (fdisk_partition_has_size(pa))
746 			fprintf(f, ", size=%12ju", (uintmax_t)pa->size);
747 
748 		if (pa->type && fdisk_parttype_get_string(pa->type))
749 			fprintf(f, ", type=%s", fdisk_parttype_get_string(pa->type));
750 		else if (pa->type)
751 			fprintf(f, ", type=%x", fdisk_parttype_get_code(pa->type));
752 
753 		if (pa->uuid)
754 			fprintf(f, ", uuid=%s", pa->uuid);
755 		if (pa->name && *pa->name) {
756 			fputs(", name=", f);
757 			fputs_quoted(pa->name, f);
758 		}
759 
760 		/* for MBR attr=80 means bootable */
761 		if (pa->attrs) {
762 			struct fdisk_label *lb = script_get_label(dp);
763 
764 			if (!lb || fdisk_label_get_type(lb) != FDISK_DISKLABEL_DOS)
765 				fprintf(f, ", attrs=\"%s\"", pa->attrs);
766 		}
767 		if (fdisk_partition_is_bootable(pa))
768 			fprintf(f, ", bootable");
769 		fputc('\n', f);
770 	}
771 
772 	DBG(SCRIPT, ul_debugobj(dp, "write script done"));
773 	return 0;
774 }
775 
776 /**
777  * fdisk_script_write_file:
778  * @dp: script
779  * @f: output file
780  *
781  * Writes script @dp to the file @f.
782  *
783  * Returns: 0 on success, <0 on error.
784  */
fdisk_script_write_file(struct fdisk_script * dp,FILE * f)785 int fdisk_script_write_file(struct fdisk_script *dp, FILE *f)
786 {
787 	assert(dp);
788 
789 	if (dp->json)
790 		return write_file_json(dp, f);
791 
792 	return write_file_sfdisk(dp, f);
793 }
794 
is_header_line(const char * s)795 static inline int is_header_line(const char *s)
796 {
797 	const char *p = strchr(s, ':');
798 
799 	if (!p || p == s || !*(p + 1) || strchr(s, '='))
800 		return 0;
801 
802 	return 1;
803 }
804 
805 /* parses "<name>: value", note modifies @s*/
parse_line_header(struct fdisk_script * dp,char * s)806 static int parse_line_header(struct fdisk_script *dp, char *s)
807 {
808 	size_t i;
809 	char *name, *value;
810 	static const char *supported[] = {
811 		"label", "unit", "label-id", "device", "grain",
812 		"first-lba", "last-lba", "table-length", "sector-size"
813 	};
814 
815 	DBG(SCRIPT, ul_debugobj(dp, "   parse header '%s'", s));
816 
817 	if (!s || !*s)
818 		return -EINVAL;
819 
820 	name = s;
821 	value = strchr(s, ':');
822 	if (!value)
823 		return -EINVAL;
824 	*value = '\0';
825 	value++;
826 
827 	ltrim_whitespace((unsigned char *) name);
828 	rtrim_whitespace((unsigned char *) name);
829 	ltrim_whitespace((unsigned char *) value);
830 	rtrim_whitespace((unsigned char *) value);
831 
832 	if (!*name || !*value)
833 		return -EINVAL;
834 
835 	/* check header name */
836 	for (i = 0; i < ARRAY_SIZE(supported); i++) {
837 		if (strcmp(name, supported[i]) == 0)
838 			break;
839 	}
840 	if (i == ARRAY_SIZE(supported))
841 		return -ENOTSUP;
842 
843 	/* header specific actions */
844 	if (strcmp(name, "label") == 0) {
845 		if (dp->cxt && !fdisk_get_label(dp->cxt, value))
846 			return -EINVAL;			/* unknown label name */
847 		dp->force_label = 1;
848 
849 	} else if (strcmp(name, "unit") == 0) {
850 		if (strcmp(value, "sectors") != 0)
851 			return -EINVAL;			/* only "sectors" supported */
852 
853 	}
854 
855 	return fdisk_script_set_header(dp, name, value);
856 }
857 
858 /* returns zero terminated string with next token and @str is updated */
next_token(char ** str)859 static char *next_token(char **str)
860 {
861 	char *tk_begin = NULL,
862 	     *tk_end = NULL,
863 	     *end = NULL,
864 	     *p;
865 	int open_quote = 0, terminated = 0;
866 
867 	for (p = *str; p && *p; p++) {
868 		if (!tk_begin) {
869 			if (isblank(*p))
870 				continue;
871 			tk_begin = *p == '"' ? p + 1 : p;
872 		}
873 		if (*p == '"')
874 			open_quote ^= 1;
875 		if (open_quote)
876 			continue;
877 		if (isblank(*p) || *p == ',' || *p == ';' || *p == '"' )
878 			tk_end = p;
879 		else if (*(p + 1) == '\0')
880 			tk_end = p + 1;
881 		if (tk_begin && tk_end)
882 			break;
883 	}
884 
885 	if (!tk_end)
886 		return NULL;
887 
888 	end = tk_end;
889 
890 	/* skip closing quotes */
891 	if (*end == '"')
892 		end++;
893 
894 	/* token is terminated by blank (or blank is before "," or ";") */
895 	if (isblank(*end)) {
896 		end = (char *) skip_blank(end);
897 		terminated++;
898 	}
899 
900 	/* token is terminated by "," or ";" */
901 	if (*end == ',' || *end == ';') {
902 		end++;
903 		terminated++;
904 
905 	/* token is terminated by \0 */
906 	} else if (!*end)
907 		terminated++;
908 
909 	if (!terminated) {
910 		DBG(SCRIPT, ul_debug("unterminated token '%s'", end));
911 		return NULL;
912 	}
913 
914 	/* skip extra space after terminator */
915 	end = (char *) skip_blank(end);
916 
917 	*tk_end = '\0';
918 	*str = end;
919 	return tk_begin;
920 }
921 
next_number(char ** s,uint64_t * num,int * power)922 static int next_number(char **s, uint64_t *num, int *power)
923 {
924 	char *tk;
925 	int rc = -EINVAL;
926 
927 	assert(num);
928 	assert(s);
929 
930 	tk = next_token(s);
931 	if (tk)
932 		rc = parse_size(tk, (uintmax_t *) num, power);
933 	return rc;
934 }
935 
next_string(char ** s,char ** str)936 static int next_string(char **s, char **str)
937 {
938 	char *tk;
939 	int rc = -EINVAL;
940 
941 	assert(s);
942 	assert(str);
943 
944 	tk = next_token(s);
945 	if (tk) {
946 		*str = strdup(tk);
947 		rc = !*str ? -ENOMEM : 0;
948 	}
949 	return rc;
950 }
951 
partno_from_devname(char * s)952 static int partno_from_devname(char *s)
953 {
954 	int pno;
955 	size_t sz;
956 	char *end, *p;
957 
958 	if (!s || !*s)
959 		return -1;
960 
961 	sz = rtrim_whitespace((unsigned char *)s);
962 	end = p = s + sz;
963 
964 	while (p > s && isdigit(*(p - 1)))
965 		p--;
966 	if (p == end)
967 		return -1;
968 	end = NULL;
969 	errno = 0;
970 	pno = strtol(p, &end, 10);
971 	if (errno || !end || p == end)
972 		return -1;
973 	return pno - 1;
974 }
975 
976 #define FDISK_SCRIPT_PARTTYPE_PARSE_FLAGS \
977 	(FDISK_PARTTYPE_PARSE_DATA | FDISK_PARTTYPE_PARSE_DATALAST | \
978 	 FDISK_PARTTYPE_PARSE_SHORTCUT | FDISK_PARTTYPE_PARSE_ALIAS | \
979 	 FDISK_PARTTYPE_PARSE_DEPRECATED)
980 
981 /* dump format
982  * <device>: start=<num>, size=<num>, type=<string>, ...
983  */
parse_line_nameval(struct fdisk_script * dp,char * s)984 static int parse_line_nameval(struct fdisk_script *dp, char *s)
985 {
986 	char *p, *x;
987 	struct fdisk_partition *pa;
988 	int rc = 0;
989 	uint64_t num;
990 	int pno;
991 
992 	assert(dp);
993 	assert(s);
994 	assert(dp->table);
995 
996 	DBG(SCRIPT, ul_debugobj(dp, "   parse script line: '%s'", s));
997 
998 	pa = fdisk_new_partition();
999 	if (!pa)
1000 		return -ENOMEM;
1001 
1002 	fdisk_partition_start_follow_default(pa, 1);
1003 	fdisk_partition_end_follow_default(pa, 1);
1004 	fdisk_partition_partno_follow_default(pa, 1);
1005 
1006 	/* set partno */
1007 	p = strchr(s, ':');
1008 	x = strchr(s, '=');
1009 	if (p && (!x || p < x)) {
1010 		*p = '\0';
1011 		p++;
1012 
1013 		pno = partno_from_devname(s);
1014 		if (pno >= 0) {
1015 			fdisk_partition_partno_follow_default(pa, 0);
1016 			fdisk_partition_set_partno(pa, pno);
1017 		}
1018 	} else
1019 		p = s;
1020 
1021 	while (rc == 0 && p && *p) {
1022 
1023 		DBG(SCRIPT, ul_debugobj(dp, " parsing '%s'", p));
1024 		p = (char *) skip_blank(p);
1025 
1026 		if (!strncasecmp(p, "start=", 6)) {
1027 			int pow = 0;
1028 			p += 6;
1029 			rc = next_number(&p, &num, &pow);
1030 			if (!rc) {
1031 				if (pow)	/* specified as <num><suffix> */
1032 					num /= dp->cxt->sector_size;
1033 				fdisk_partition_set_start(pa, num);
1034 				fdisk_partition_start_follow_default(pa, 0);
1035 			}
1036 		} else if (!strncasecmp(p, "size=", 5)) {
1037 			int pow = 0;
1038 
1039 			p += 5;
1040 			rc = next_number(&p, &num, &pow);
1041 			if (!rc) {
1042 				if (pow)	/* specified as <num><suffix> */
1043 					num /= dp->cxt->sector_size;
1044 				else		/* specified as number of sectors */
1045 					fdisk_partition_size_explicit(pa, 1);
1046 				fdisk_partition_set_size(pa, num);
1047 				fdisk_partition_end_follow_default(pa, 0);
1048 			}
1049 
1050 		} else if (!strncasecmp(p, "bootable", 8)) {
1051 			/* we use next_token() to skip possible extra space */
1052 			char *tk = next_token(&p);
1053 			if (tk && strcasecmp(tk, "bootable") == 0)
1054 				pa->boot = 1;
1055 			else
1056 				rc = -EINVAL;
1057 
1058 		} else if (!strncasecmp(p, "attrs=", 6)) {
1059 			p += 6;
1060 			rc = next_string(&p, &pa->attrs);
1061 
1062 		} else if (!strncasecmp(p, "uuid=", 5)) {
1063 			p += 5;
1064 			rc = next_string(&p, &pa->uuid);
1065 
1066 		} else if (!strncasecmp(p, "name=", 5)) {
1067 			p += 5;
1068 			rc = next_string(&p, &pa->name);
1069 			if (!rc)
1070 				unhexmangle_string(pa->name);
1071 
1072 		} else if (!strncasecmp(p, "type=", 5) ||
1073 			   !strncasecmp(p, "Id=", 3)) {		/* backward compatibility */
1074 			char *type;
1075 
1076 			p += ((*p == 'I' || *p == 'i') ? 3 : 5); /* "Id=", "type=" */
1077 
1078 			rc = next_string(&p, &type);
1079 			if (rc)
1080 				break;
1081 
1082 			pa->type = fdisk_label_advparse_parttype(script_get_label(dp),
1083 					type, FDISK_SCRIPT_PARTTYPE_PARSE_FLAGS);
1084 			free(type);
1085 
1086 			if (!pa->type) {
1087 				rc = -EINVAL;
1088 				break;
1089 			}
1090 		} else {
1091 			DBG(SCRIPT, ul_debugobj(dp, "script parse error: unknown field '%s'", p));
1092 			rc = -EINVAL;
1093 			break;
1094 		}
1095 	}
1096 
1097 	if (!rc)
1098 		rc = fdisk_table_add_partition(dp->table, pa);
1099 	if (rc)
1100 		DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
1101 
1102 	fdisk_unref_partition(pa);
1103 	return rc;
1104 }
1105 
1106 #define TK_PLUS		1
1107 #define TK_MINUS	-1
1108 
1109 #define alone_sign(_sign, _p)	(_sign && (*_p == '\0' || isblank(*_p)))
1110 
1111 /* simple format:
1112  * <start>, <size>, <type>, <bootable>, ...
1113  */
parse_line_valcommas(struct fdisk_script * dp,char * s)1114 static int parse_line_valcommas(struct fdisk_script *dp, char *s)
1115 {
1116 	int rc = 0;
1117 	char *p = s, *str;
1118 	struct fdisk_partition *pa;
1119 	enum { ITEM_START, ITEM_SIZE, ITEM_TYPE, ITEM_BOOTABLE };
1120 	int item = -1;
1121 
1122 	assert(dp);
1123 	assert(s);
1124 	assert(dp->table);
1125 
1126 	pa = fdisk_new_partition();
1127 	if (!pa)
1128 		return -ENOMEM;
1129 
1130 	fdisk_partition_start_follow_default(pa, 1);
1131 	fdisk_partition_end_follow_default(pa, 1);
1132 	fdisk_partition_partno_follow_default(pa, 1);
1133 
1134 	while (rc == 0 && p && *p) {
1135 		uint64_t num;
1136 		char *begin;
1137 		int sign = 0;
1138 
1139 		p = (char *) skip_blank(p);
1140 		item++;
1141 
1142 		if (item != ITEM_BOOTABLE) {
1143 			sign = *p == '-' ? TK_MINUS : *p == '+' ? TK_PLUS : 0;
1144 			if (sign)
1145 				p++;
1146 		}
1147 
1148 		DBG(SCRIPT, ul_debugobj(dp, " parsing item %d ('%s')", item, p));
1149 		begin = p;
1150 
1151 		switch (item) {
1152 		case ITEM_START:
1153 			if (*p == ',' || *p == ';' || alone_sign(sign, p))
1154 				fdisk_partition_start_follow_default(pa, 1);
1155 			else {
1156 				int pow = 0;
1157 
1158 				rc = next_number(&p, &num, &pow);
1159 				if (!rc) {
1160 					if (pow)	/* specified as <num><suffix> */
1161 						num /= dp->cxt->sector_size;
1162 					fdisk_partition_set_start(pa, num);
1163 					pa->movestart = sign == TK_MINUS ? FDISK_MOVE_DOWN :
1164 							sign == TK_PLUS  ? FDISK_MOVE_UP :
1165 							FDISK_MOVE_NONE;
1166 				}
1167 				fdisk_partition_start_follow_default(pa, 0);
1168 			}
1169 			break;
1170 		case ITEM_SIZE:
1171 			if (*p == ',' || *p == ';' || alone_sign(sign, p)) {
1172 				fdisk_partition_end_follow_default(pa, 1);
1173 				if (sign == TK_PLUS)
1174 					/* '+' alone means use all possible space, '-' alone means nothing */
1175 					pa->resize = FDISK_RESIZE_ENLARGE;
1176 			} else {
1177 				int pow = 0;
1178 				rc = next_number(&p, &num, &pow);
1179 				if (!rc) {
1180 					if (pow) /* specified as <size><suffix> */
1181 						num /= dp->cxt->sector_size;
1182 					else	 /* specified as number of sectors */
1183 						fdisk_partition_size_explicit(pa, 1);
1184 					fdisk_partition_set_size(pa, num);
1185 					pa->resize = sign == TK_MINUS ? FDISK_RESIZE_REDUCE :
1186 						     sign == TK_PLUS  ? FDISK_RESIZE_ENLARGE :
1187 							FDISK_RESIZE_NONE;
1188 				}
1189 				fdisk_partition_end_follow_default(pa, 0);
1190 			}
1191 			break;
1192 		case ITEM_TYPE:
1193 			if (*p == ',' || *p == ';' || alone_sign(sign, p))
1194 				break;	/* use default type */
1195 
1196 			rc = next_string(&p, &str);
1197 			if (rc)
1198 				break;
1199 
1200 			pa->type = fdisk_label_advparse_parttype(script_get_label(dp),
1201 						str, FDISK_SCRIPT_PARTTYPE_PARSE_FLAGS);
1202 			free(str);
1203 
1204 			if (!pa->type)
1205 				rc = -EINVAL;
1206 			break;
1207 		case ITEM_BOOTABLE:
1208 			if (*p == ',' || *p == ';')
1209 				break;
1210 			else {
1211 				char *tk = next_token(&p);
1212 				if (tk && *tk == '*' && *(tk + 1) == '\0')
1213 					pa->boot = 1;
1214 				else if (tk && *tk == '-' && *(tk + 1) == '\0')
1215 					pa->boot = 0;
1216 				else if (tk && *tk == '+' && *(tk + 1) == '\0')
1217 					pa->boot = 1;
1218 				else
1219 					rc = -EINVAL;
1220 			}
1221 			break;
1222 		default:
1223 			break;
1224 		}
1225 
1226 		if (begin == p)
1227 			p++;
1228 	}
1229 
1230 	if (!rc)
1231 		rc = fdisk_table_add_partition(dp->table, pa);
1232 	if (rc)
1233 		DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
1234 
1235 	fdisk_unref_partition(pa);
1236 	return rc;
1237 }
1238 
1239 /* modifies @s ! */
fdisk_script_read_buffer(struct fdisk_script * dp,char * s)1240 static int fdisk_script_read_buffer(struct fdisk_script *dp, char *s)
1241 {
1242 	int rc = 0;
1243 
1244 	assert(dp);
1245 	assert(s);
1246 
1247 	DBG(SCRIPT, ul_debugobj(dp, "  parsing buffer"));
1248 
1249 	s = (char *) skip_blank(s);
1250 	if (!s || !*s)
1251 		return 0;	/* nothing baby, ignore */
1252 
1253 	if (!dp->table && fdisk_script_get_table(dp) == NULL)
1254 		return -ENOMEM;
1255 
1256 	/* parse header lines only if no partition specified yet */
1257 	if (fdisk_table_is_empty(dp->table) && is_header_line(s))
1258 		rc = parse_line_header(dp, s);
1259 
1260 	/* parse script format */
1261 	else if (strchr(s, '='))
1262 		rc = parse_line_nameval(dp, s);
1263 
1264 	/* parse simple <value>, ... format */
1265 	else
1266 		rc = parse_line_valcommas(dp, s);
1267 
1268 	if (rc)
1269 		DBG(SCRIPT, ul_debugobj(dp, "%zu: parse error [rc=%d]",
1270 				dp->nlines, rc));
1271 	return rc;
1272 }
1273 
1274 /**
1275  * fdisk_script_set_fgets:
1276  * @dp: script
1277  * @fn_fgets: callback function
1278  *
1279  * The library uses fgets() function to read the next line from the script.
1280  * This default maybe overridden by another function. Note that the function has
1281  * to return the line terminated by \n (for example readline(3) removes \n).
1282  *
1283  * Return: 0 on success, <0 on error
1284  */
fdisk_script_set_fgets(struct fdisk_script * dp,char * (* fn_fgets)(struct fdisk_script *,char *,size_t,FILE *))1285 int fdisk_script_set_fgets(struct fdisk_script *dp,
1286 			  char *(*fn_fgets)(struct fdisk_script *, char *, size_t, FILE *))
1287 {
1288 	assert(dp);
1289 
1290 	dp->fn_fgets = fn_fgets;
1291 	return 0;
1292 }
1293 
1294 /**
1295  * fdisk_script_read_line:
1296  * @dp: script
1297  * @f: file
1298  * @buf: buffer to store one line of the file
1299  * @bufsz: buffer size
1300  *
1301  * Reads next line into dump.
1302  *
1303  * Returns: 0 on success, <0 on error, 1 when nothing to read. For unknown headers
1304  *          returns -ENOTSUP, it's usually safe to ignore this error.
1305  */
fdisk_script_read_line(struct fdisk_script * dp,FILE * f,char * buf,size_t bufsz)1306 int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz)
1307 {
1308 	char *s;
1309 
1310 	assert(dp);
1311 	assert(f);
1312 
1313 	DBG(SCRIPT, ul_debugobj(dp, " parsing line %zu", dp->nlines));
1314 
1315 	/* read the next non-blank non-comment line */
1316 	do {
1317 		if (dp->fn_fgets) {
1318 			if (dp->fn_fgets(dp, buf, bufsz, f) == NULL)
1319 				return 1;
1320 		} else if (fgets(buf, bufsz, f) == NULL)
1321 			return 1;
1322 
1323 		dp->nlines++;
1324 		s = strchr(buf, '\n');
1325 		if (!s) {
1326 			/* Missing final newline?  Otherwise an extremely */
1327 			/* long line - assume file was corrupted */
1328 			if (feof(f)) {
1329 				DBG(SCRIPT, ul_debugobj(dp, "no final newline"));
1330 				s = strchr(buf, '\0');
1331 			} else {
1332 				DBG(SCRIPT, ul_debugobj(dp,
1333 					"%zu: missing newline at line", dp->nlines));
1334 				return -EINVAL;
1335 			}
1336 		}
1337 
1338 		*s = '\0';
1339 		if (--s >= buf && *s == '\r')
1340 			*s = '\0';
1341 		s = (char *) skip_blank(buf);
1342 	} while (*s == '\0' || *s == '#');
1343 
1344 	return fdisk_script_read_buffer(dp, s);
1345 }
1346 
1347 
1348 /**
1349  * fdisk_script_read_file:
1350  * @dp: script
1351  * @f: input file
1352  *
1353  * Reads file @f into script @dp.
1354  *
1355  * Returns: 0 on success, <0 on error.
1356  */
fdisk_script_read_file(struct fdisk_script * dp,FILE * f)1357 int fdisk_script_read_file(struct fdisk_script *dp, FILE *f)
1358 {
1359 	char buf[BUFSIZ];
1360 	int rc = 1;
1361 
1362 	assert(dp);
1363 	assert(f);
1364 
1365 	DBG(SCRIPT, ul_debugobj(dp, "parsing file"));
1366 
1367 	while (!feof(f)) {
1368 		rc = fdisk_script_read_line(dp, f, buf, sizeof(buf));
1369 		if (rc && rc != -ENOTSUP)
1370 			break;
1371 	}
1372 
1373 	if (rc == 1)
1374 		rc = 0;		/* end of file */
1375 
1376 	DBG(SCRIPT, ul_debugobj(dp, "parsing file done [rc=%d]", rc));
1377 	return rc;
1378 }
1379 
1380 /**
1381  * fdisk_set_script:
1382  * @cxt: context
1383  * @dp: script (or NULL to remove previous reference)
1384  *
1385  * Sets reference to the @dp script and remove reference to the previously used
1386  * script.
1387  *
1388  * The script headers might be used by label drivers to overwrite
1389  * built-in defaults (for example disk label Id) and label driver might
1390  * optimize the default semantic to be more usable for scripts (for example to
1391  * not ask for primary/logical/extended partition type).
1392  *
1393  * Note that script also contains reference to the fdisk context (see
1394  * fdisk_new_script()). This context may be completely independent on
1395  * context used for fdisk_set_script().
1396  *
1397  * Don't forget to call fdisk_set_script(cxt, NULL); to remove this reference
1398  * if no more necessary!
1399  *
1400  * Returns: <0 on error, 0 on success.
1401  */
fdisk_set_script(struct fdisk_context * cxt,struct fdisk_script * dp)1402 int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp)
1403 {
1404 	assert(cxt);
1405 
1406 	/* unref old */
1407 	if (cxt->script)
1408 		fdisk_unref_script(cxt->script);
1409 
1410 	/* ref new */
1411 	cxt->script = dp;
1412 	if (cxt->script) {
1413 		DBG(CXT, ul_debugobj(cxt, "setting reference to script %p", cxt->script));
1414 		fdisk_ref_script(cxt->script);
1415 	}
1416 
1417 	return 0;
1418 }
1419 
1420 /**
1421  * fdisk_get_script:
1422  * @cxt: context
1423  *
1424  * Returns: the current script or NULL.
1425  */
fdisk_get_script(struct fdisk_context * cxt)1426 struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt)
1427 {
1428 	assert(cxt);
1429 	return cxt->script;
1430 }
1431 
1432 /**
1433  * fdisk_apply_script_headers:
1434  * @cxt: context
1435  * @dp: script
1436  *
1437  * Associate context @cxt with script @dp and creates a new empty disklabel.
1438  * The script may be later unreference by fdisk_set_script() with NULL as script.
1439  *
1440  * Returns: 0 on success, <0 on error.
1441  */
fdisk_apply_script_headers(struct fdisk_context * cxt,struct fdisk_script * dp)1442 int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp)
1443 {
1444 	const char *name;
1445 	const char *str;
1446 	int rc;
1447 
1448 	assert(cxt);
1449 	assert(dp);
1450 
1451 	DBG(SCRIPT, ul_debugobj(dp, "applying script headers"));
1452 	fdisk_set_script(cxt, dp);
1453 
1454 	str = fdisk_script_get_header(dp, "grain");
1455 	if (str) {
1456 		uintmax_t sz;
1457 
1458 		rc = parse_size(str, &sz, NULL);
1459 		if (rc == 0)
1460 			rc = fdisk_save_user_grain(cxt, sz);
1461 		if (rc)
1462 			return rc;
1463 	}
1464 
1465 	if (fdisk_has_user_device_properties(cxt))
1466 		fdisk_apply_user_device_properties(cxt);
1467 
1468 	/* create empty label */
1469 	name = fdisk_script_get_header(dp, "label");
1470 	if (!name)
1471 		return -EINVAL;
1472 
1473 	rc = fdisk_create_disklabel(cxt, name);
1474 	if (rc)
1475 		return rc;
1476 
1477 	str = fdisk_script_get_header(dp, "table-length");
1478 	if (str) {
1479 		uintmax_t sz;
1480 
1481 		rc = parse_size(str, &sz, NULL);
1482 		if (rc == 0)
1483 			rc = fdisk_gpt_set_npartitions(cxt, sz);
1484 	}
1485 
1486 	return rc;
1487 }
1488 
1489 /**
1490  * fdisk_apply_script:
1491  * @cxt: context
1492  * @dp: script
1493  *
1494  * This function creates a new disklabel and partition within context @cxt. You
1495  * have to call fdisk_write_disklabel() to apply changes to the device.
1496  *
1497  * Returns: 0 on error, <0 on error.
1498  */
fdisk_apply_script(struct fdisk_context * cxt,struct fdisk_script * dp)1499 int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp)
1500 {
1501 	int rc;
1502 	struct fdisk_script *old;
1503 
1504 	assert(dp);
1505 	assert(cxt);
1506 
1507 	DBG(CXT, ul_debugobj(cxt, "applying script %p", dp));
1508 
1509 	old = fdisk_get_script(cxt);
1510 	fdisk_ref_script(old);
1511 
1512 	/* create empty disk label */
1513 	rc = fdisk_apply_script_headers(cxt, dp);
1514 
1515 	/* create partitions */
1516 	if (!rc && dp->table)
1517 		rc = fdisk_apply_table(cxt, dp->table);
1518 
1519 	fdisk_set_script(cxt, old);
1520 	fdisk_unref_script(old);
1521 
1522 	DBG(CXT, ul_debugobj(cxt, "script done [rc=%d]", rc));
1523 	return rc;
1524 }
1525 
1526 #ifdef TEST_PROGRAM
test_dump(struct fdisk_test * ts,int argc,char * argv[])1527 static int test_dump(struct fdisk_test *ts, int argc, char *argv[])
1528 {
1529 	char *devname = argv[1];
1530 	struct fdisk_context *cxt;
1531 	struct fdisk_script *dp;
1532 
1533 	cxt = fdisk_new_context();
1534 	fdisk_assign_device(cxt, devname, 1);
1535 
1536 	dp = fdisk_new_script(cxt);
1537 	fdisk_script_read_context(dp, NULL);
1538 
1539 	fdisk_script_write_file(dp, stdout);
1540 	fdisk_unref_script(dp);
1541 	fdisk_unref_context(cxt);
1542 
1543 	return 0;
1544 }
1545 
test_read(struct fdisk_test * ts,int argc,char * argv[])1546 static int test_read(struct fdisk_test *ts, int argc, char *argv[])
1547 {
1548 	char *filename = argv[1];
1549 	struct fdisk_script *dp;
1550 	struct fdisk_context *cxt;
1551 	FILE *f;
1552 
1553 	if (!(f = fopen(filename, "r")))
1554 		err(EXIT_FAILURE, "%s: cannot open", filename);
1555 
1556 	cxt = fdisk_new_context();
1557 	dp = fdisk_new_script(cxt);
1558 
1559 	fdisk_script_read_file(dp, f);
1560 	fclose(f);
1561 
1562 	fdisk_script_write_file(dp, stdout);
1563 	fdisk_unref_script(dp);
1564 	fdisk_unref_context(cxt);
1565 
1566 	return 0;
1567 }
1568 
test_stdin(struct fdisk_test * ts,int argc,char * argv[])1569 static int test_stdin(struct fdisk_test *ts, int argc, char *argv[])
1570 {
1571 	char buf[BUFSIZ];
1572 	struct fdisk_script *dp;
1573 	struct fdisk_context *cxt;
1574 	int rc = 0;
1575 
1576 	cxt = fdisk_new_context();
1577 	dp = fdisk_new_script(cxt);
1578 	fdisk_script_set_header(dp, "label", "dos");
1579 
1580 	printf("<start>, <size>, <type>, <bootable: *|->\n");
1581 	do {
1582 		struct fdisk_partition *pa;
1583 		size_t n = dp->table ? fdisk_table_get_nents(dp->table) : 0;
1584 
1585 		printf(" #%zu :\n", n + 1);
1586 		rc = fdisk_script_read_line(dp, stdin, buf, sizeof(buf));
1587 
1588 		if (rc == 0) {
1589 			pa = fdisk_table_get_partition(dp->table, n);
1590 			printf(" #%zu  %12ju %12ju\n",	n + 1,
1591 						(uintmax_t)fdisk_partition_get_start(pa),
1592 						(uintmax_t)fdisk_partition_get_size(pa));
1593 		}
1594 	} while (rc == 0);
1595 
1596 	if (!rc)
1597 		fdisk_script_write_file(dp, stdout);
1598 	fdisk_unref_script(dp);
1599 	fdisk_unref_context(cxt);
1600 
1601 	return rc;
1602 }
1603 
test_apply(struct fdisk_test * ts,int argc,char * argv[])1604 static int test_apply(struct fdisk_test *ts, int argc, char *argv[])
1605 {
1606 	char *devname = argv[1], *scriptname = argv[2];
1607 	struct fdisk_context *cxt;
1608 	struct fdisk_script *dp;
1609 	struct fdisk_table *tb = NULL;
1610 	struct fdisk_iter *itr = NULL;
1611 	struct fdisk_partition *pa = NULL;
1612 	int rc;
1613 
1614 	cxt = fdisk_new_context();
1615 	fdisk_assign_device(cxt, devname, 0);
1616 
1617 	dp = fdisk_new_script_from_file(cxt, scriptname);
1618 	if (!dp)
1619 		return -errno;
1620 
1621 	rc = fdisk_apply_script(cxt, dp);
1622 	if (rc)
1623 		goto done;
1624 	fdisk_unref_script(dp);
1625 
1626 	/* list result */
1627 	fdisk_list_disklabel(cxt);
1628 	fdisk_get_partitions(cxt, &tb);
1629 
1630 	itr = fdisk_new_iter(FDISK_ITER_FORWARD);
1631 	while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
1632 		printf(" #%zu  %12ju %12ju\n",	fdisk_partition_get_partno(pa),
1633 						(uintmax_t)fdisk_partition_get_start(pa),
1634 						(uintmax_t)fdisk_partition_get_size(pa));
1635 	}
1636 
1637 done:
1638 	fdisk_free_iter(itr);
1639 	fdisk_unref_table(tb);
1640 
1641 	/*fdisk_write_disklabel(cxt);*/
1642 	fdisk_unref_context(cxt);
1643 	return 0;
1644 }
1645 
test_tokens(struct fdisk_test * ts,int argc,char * argv[])1646 static int test_tokens(struct fdisk_test *ts, int argc, char *argv[])
1647 {
1648 	char *p, *str = argc == 2 ? strdup(argv[1]) : NULL;
1649 	int i;
1650 
1651 	for (i = 1, p = str; p && *p; i++) {
1652 		char *tk = next_token(&p);
1653 
1654 		if (!tk)
1655 			break;
1656 
1657 		printf("#%d: '%s'\n", i, tk);
1658 	}
1659 
1660 	free(str);
1661 	return 0;
1662 }
1663 
main(int argc,char * argv[])1664 int main(int argc, char *argv[])
1665 {
1666 	struct fdisk_test tss[] = {
1667 	{ "--dump",    test_dump,    "<device>            dump PT as script" },
1668 	{ "--read",    test_read,    "<file>              read PT script from file" },
1669 	{ "--apply",   test_apply,   "<device> <file>     try apply script from file to device" },
1670 	{ "--stdin",   test_stdin,   "                    read input like sfdisk" },
1671 	{ "--tokens",  test_tokens,  "<string>            parse string" },
1672 	{ NULL }
1673 	};
1674 
1675 	return fdisk_run_test(tss, argc, argv);
1676 }
1677 
1678 #endif
1679