1 /*
2  * Copyright (C) 2017 Karel Zak <kzak@redhat.com>
3  *
4  * This file may be redistributed under the terms of the
5  * GNU Lesser General Public License.
6  *
7  *
8  * Libfdisk sample to create partitions by specify all required partition
9  * properties (partno, start and size). The default is only partition type
10  * (except MBR where 4th partition is extended).
11  */
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <dirent.h>
19 #include <getopt.h>
20 
21 #include "c.h"
22 #include "nls.h"
23 #include "strutils.h"
24 #include "xalloc.h"
25 
26 #include "libfdisk.h"
27 
ask_callback(struct fdisk_context * cxt,struct fdisk_ask * ask,void * data)28 static int ask_callback(struct fdisk_context *cxt __attribute__((__unused__)),
29 			struct fdisk_ask *ask,
30 			void *data)
31 {
32 	switch(fdisk_ask_get_type(ask)) {
33 	case FDISK_ASKTYPE_INFO:
34 		fputs(fdisk_ask_print_get_mesg(ask), stdout);
35 		fputc('\n', stdout);
36 		break;
37 	case FDISK_ASKTYPE_WARNX:
38 		fflush(stdout);
39 		fputs(fdisk_ask_print_get_mesg(ask), stderr);
40 		fputc('\n', stderr);
41 		break;
42 	case FDISK_ASKTYPE_WARN:
43 		fflush(stdout);
44 		fputs(fdisk_ask_print_get_mesg(ask), stderr);
45 		errno = fdisk_ask_print_get_errno(ask);
46 		fprintf(stderr, ": %m\n");
47 		break;
48 	default:
49 		break;
50 	}
51 	return 0;
52 }
53 
main(int argc,char * argv[])54 int main(int argc, char *argv[])
55 {
56 	struct fdisk_context *cxt;
57 	struct fdisk_partition *pa;
58 	const char *label = NULL, *device = NULL;
59 	int c;
60 	size_t n = 1;
61 
62 	static const struct option longopts[] = {
63 		{ "label",  required_argument, NULL, 'x' },
64 		{ "device", required_argument, NULL, 'd' },
65 		{ "help",   no_argument, NULL, 'h' },
66 		{ NULL, 0, NULL, 0 },
67 	};
68 
69 	setlocale(LC_ALL, "");	/* just to have enable UTF8 chars */
70 
71 	fdisk_init_debug(0);
72 
73 	while((c = getopt_long(argc, argv, "x:d:h", longopts, NULL)) != -1) {
74 		switch(c) {
75 		case 'x':
76 			label = optarg;
77 			break;
78 		case 'd':
79 			device = optarg;
80 			break;
81 		case 'h':
82 			printf("%s [options] -- <partno,start,size> ...", program_invocation_short_name);
83 			fputs(USAGE_SEPARATOR, stdout);
84 			puts("Make disklabel and partitions.");
85 			puts(" <partno>                     1..n (4th is extended for MBR), or '-' for default");
86 			puts(" <start>                      partition start offset in sectors");
87 			puts(" <size>                       partition size in sectors");
88 			fputs(USAGE_OPTIONS, stdout);
89 			puts(" -x, --label <dos,gpt,...>    disk label type (default MBR)");
90 			puts(" -d, --device <path>          block device");
91 			puts(" -h, --help                   this help");
92 			fputs(USAGE_SEPARATOR, stdout);
93 			return EXIT_SUCCESS;
94 		}
95 	}
96 
97 	if (!device)
98 		errx(EXIT_FAILURE, "no device specified");
99 	if (!label)
100 		label = "dos";
101 
102 	cxt = fdisk_new_context();
103 	if (!cxt)
104 		err_oom();
105 	fdisk_set_ask(cxt, ask_callback, NULL);
106 
107 	pa = fdisk_new_partition();
108 	if (!pa)
109 		err_oom();
110 
111 	if (fdisk_assign_device(cxt, device, 0))
112 		err(EXIT_FAILURE, "failed to assign device");
113 	if (fdisk_create_disklabel(cxt, label))
114 		err(EXIT_FAILURE, "failed to create disk label");
115 
116 	fdisk_disable_dialogs(cxt, 1);
117 
118 	while (optind < argc) {
119 		int rc;
120 		unsigned int partno = 0;
121 		uint64_t start = 0, size = 0;
122 		const char *str = argv[optind];
123 
124 		fdisk_reset_partition(pa);
125 		fdisk_partition_end_follow_default(pa, 0);
126 
127 		if (*str == '-') {
128 			/* partno unspecified */
129 			if (sscanf(str, "-,%"SCNu64",%"SCNu64"", &start, &size) != 2)
130 				errx(EXIT_FAILURE, "failed to parse %s", str);
131 			fdisk_partition_partno_follow_default(pa, 1);
132 			fdisk_partition_unset_partno(pa);
133 		} else {
134 			/* partno specified */
135 			if (sscanf(str, "%u,%"SCNu64",%"SCNu64"", &partno, &start, &size) != 3)
136 				errx(EXIT_FAILURE, "failed to parse %s", str);
137 
138 			fdisk_partition_partno_follow_default(pa, 0);
139 			fdisk_partition_set_partno(pa, partno - 1);     /* library uses 0..n */
140 		}
141 
142 		fdisk_partition_set_start(pa, start);
143 		fdisk_partition_set_size(pa, size);
144 
145 		if (fdisk_partition_has_partno(pa))
146 			fprintf(stdout, "Requested partition: <partno=%zu,start=%ju,size=%ju>\n",
147 					fdisk_partition_get_partno(pa),
148 					(uintmax_t) fdisk_partition_get_start(pa),
149 					(uintmax_t) fdisk_partition_get_size(pa));
150 		else
151 			fprintf(stdout, "Requested partition: <partno=<default>,start=%ju,size=%ju>\n",
152 					(uintmax_t) fdisk_partition_get_start(pa),
153 					(uintmax_t) fdisk_partition_get_size(pa));
154 
155 		if (fdisk_is_label(cxt, DOS)) {
156 			/* Make sure last primary partition is extended if user
157 			 * wants more than 4 partitions.
158 			 */
159 			if ((partno == 4 || (n == 4 && !fdisk_partition_has_partno(pa)))
160 			    && optind + 1 < argc) {
161 				struct fdisk_parttype *type =
162 					fdisk_label_parse_parttype(
163 							fdisk_get_label(cxt, NULL), "05");
164 				if (!type)
165 					err_oom();
166 				fdisk_partition_set_type(pa, type);
167 				fdisk_unref_parttype(type);
168 			}
169 		}
170 
171 		rc = fdisk_add_partition(cxt, pa, NULL);
172 		if (rc) {
173 			errno = -rc;
174 			errx(EXIT_FAILURE, "failed to add #%zu partition",
175 					fdisk_partition_has_partno(pa) ?
176 					fdisk_partition_get_partno(pa) + 1: n);
177 		}
178 
179 		fdisk_reset_partition(pa);
180 		optind++;
181 		n++;
182 	}
183 
184 	if (fdisk_write_disklabel(cxt))
185 		err(EXIT_FAILURE, "failed to write disk label");
186 
187 	fdisk_deassign_device(cxt, 1);
188 	fdisk_unref_context(cxt);
189 	fdisk_unref_partition(pa);
190 
191 	return EXIT_SUCCESS;
192 }
193