]>
Commit | Line | Data |
---|---|---|
3f0ac587 | 1 | /* |
6dbe3af9 | 2 | * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) |
38b36353 | 3 | * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org> |
6dbe3af9 | 4 | * |
3f0ac587 KZ |
5 | * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com> |
6 | * | |
6dbe3af9 KZ |
7 | * This program is free software. You can redistribute it and/or |
8 | * modify it under the terms of the GNU General Public License as | |
9 | * published by the Free Software Foundation: either version 1 or | |
10 | * (at your option) any later version. | |
6dbe3af9 | 11 | */ |
6dbe3af9 KZ |
12 | #include <unistd.h> |
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <string.h> | |
16 | #include <fcntl.h> | |
17 | #include <ctype.h> | |
6dbe3af9 | 18 | #include <errno.h> |
2b6fc908 | 19 | #include <getopt.h> |
2b6fc908 | 20 | #include <sys/stat.h> |
f26873dc | 21 | #include <sys/time.h> |
bb6aacfe | 22 | #include <time.h> |
ee5355e0 | 23 | #include <limits.h> |
6dbe3af9 | 24 | |
3f0ac587 | 25 | #include "c.h" |
52b38677 | 26 | #include "xalloc.h" |
e916600f | 27 | #include "all-io.h" |
22853e4a | 28 | #include "nls.h" |
e66ac5d3 | 29 | #include "rpmatch.h" |
810f986b | 30 | #include "blkdev.h" |
b8d22034 | 31 | #include "mbsalign.h" |
726f69e2 | 32 | #include "fdisk.h" |
6bec8710 KZ |
33 | #include "pathnames.h" |
34 | #include "canonicalize.h" | |
20aa2570 | 35 | #include "strutils.h" |
b2d28533 | 36 | #include "closestream.h" |
a22c6eb2 | 37 | #include "sysfs.h" |
5c36a0eb | 38 | |
b4bfbadd | 39 | #include "pt-sun.h" /* to toggle flags */ |
5c36a0eb | 40 | |
48d7b13a | 41 | #ifdef HAVE_LINUX_COMPILER_H |
3f0ac587 | 42 | # include <linux/compiler.h> |
48d7b13a KZ |
43 | #endif |
44 | #ifdef HAVE_LINUX_BLKPG_H | |
3f0ac587 | 45 | # include <linux/blkpg.h> |
7eda085c | 46 | #endif |
6dbe3af9 | 47 | |
d6b3ba41 KZ |
48 | |
49 | ||
50 | int get_user_reply(struct fdisk_context *cxt, const char *prompt, | |
51 | char *buf, size_t bufsz) | |
52 | { | |
53 | char *p; | |
54 | size_t sz; | |
55 | ||
56 | do { | |
57 | fputs(prompt, stdout); | |
58 | fflush(stdout); | |
59 | ||
60 | if (!fgets(buf, bufsz, stdin)) { | |
61 | if (fdisk_label_is_changed(cxt->label)) { | |
62 | fprintf(stderr, _("\nDo you really want to quit? ")); | |
63 | ||
64 | if (fgets(buf, bufsz, stdin) && !rpmatch(buf)) | |
65 | continue; | |
66 | } | |
67 | fdisk_free_context(cxt); | |
68 | exit(EXIT_FAILURE); | |
69 | } else | |
70 | break; | |
71 | } while (1); | |
72 | ||
73 | for (p = buf; *p && !isgraph(*p); p++); /* get first non-blank */ | |
74 | ||
75 | if (p > buf) | |
76 | memmove(buf, p, p - buf); /* remove blank space */ | |
77 | sz = strlen(buf); | |
78 | if (sz && *(buf + sz - 1) == '\n') | |
79 | *(buf + sz - 1) = '\0'; | |
80 | ||
88141067 | 81 | DBG(ASK, ul_debug("user's reply: >>>%s<<<", buf)); |
d6b3ba41 KZ |
82 | return 0; |
83 | } | |
84 | ||
85 | static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask, | |
86 | char *buf, size_t bufsz) | |
87 | ||
88 | { | |
89 | const char *q = fdisk_ask_get_query(ask); | |
90 | int dft = fdisk_ask_menu_get_default(ask); | |
91 | ||
92 | if (q) { | |
93 | fputs(q, stdout); /* print header */ | |
94 | fputc('\n', stdout); | |
95 | } | |
96 | ||
97 | do { | |
98 | char prompt[128]; | |
99 | int key, c; | |
100 | const char *name, *desc; | |
101 | size_t i = 0; | |
102 | ||
103 | /* print menu items */ | |
104 | while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0) | |
105 | fprintf(stdout, " %c %s (%s)\n", key, name, desc); | |
106 | ||
107 | /* ask for key */ | |
108 | snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft); | |
109 | get_user_reply(cxt, prompt, buf, bufsz); | |
110 | if (!*buf) { | |
111 | fdisk_info(cxt, _("Using default response %c."), dft); | |
112 | c = dft; | |
113 | } else | |
114 | c = tolower(buf[0]); | |
115 | ||
116 | /* check result */ | |
117 | i = 0; | |
118 | while (fdisk_ask_menu_get_item(ask, i++, &key, NULL, NULL) == 0) { | |
119 | if (c == key) { | |
120 | fdisk_ask_menu_set_result(ask, c); | |
121 | return 0; /* success */ | |
122 | } | |
123 | } | |
124 | fdisk_warnx(cxt, _("Value out of range.")); | |
125 | } while (1); | |
126 | ||
127 | return -EINVAL; | |
128 | } | |
129 | ||
130 | ||
131 | #define tochar(num) ((int) ('a' + num - 1)) | |
132 | static int ask_number(struct fdisk_context *cxt, | |
133 | struct fdisk_ask *ask, | |
134 | char *buf, size_t bufsz) | |
135 | { | |
136 | char prompt[128] = { '\0' }; | |
137 | const char *q = fdisk_ask_get_query(ask); | |
138 | const char *range = fdisk_ask_number_get_range(ask); | |
139 | ||
140 | uint64_t dflt = fdisk_ask_number_get_default(ask), | |
141 | low = fdisk_ask_number_get_low(ask), | |
142 | high = fdisk_ask_number_get_high(ask); | |
143 | int inchar = fdisk_ask_number_inchars(ask); | |
144 | ||
145 | assert(q); | |
146 | ||
88141067 | 147 | DBG(ASK, ul_debug("asking for number " |
d6b3ba41 KZ |
148 | "['%s', <%ju,%ju>, default=%ju, range: %s]", |
149 | q, low, high, dflt, range)); | |
150 | ||
151 | if (range && dflt >= low && dflt <= high) { | |
152 | if (inchar) | |
153 | snprintf(prompt, sizeof(prompt), _("%s (%s, default %c): "), | |
154 | q, range, tochar(dflt)); | |
155 | else | |
156 | snprintf(prompt, sizeof(prompt), _("%s (%s, default %ju): "), | |
157 | q, range, dflt); | |
158 | ||
159 | } else if (dflt >= low && dflt <= high) { | |
160 | if (inchar) | |
161 | snprintf(prompt, sizeof(prompt), _("%s (%c-%c, default %c): "), | |
162 | q, tochar(low), tochar(high), tochar(dflt)); | |
163 | else | |
164 | snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju, default %ju): "), | |
165 | q, low, high, dflt); | |
166 | } else if (inchar) | |
167 | snprintf(prompt, sizeof(prompt), _("%s (%c-%c): "), | |
168 | q, tochar(low), tochar(high)); | |
169 | else | |
170 | snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju): "), | |
171 | q, low, high); | |
172 | ||
173 | do { | |
174 | int rc = get_user_reply(cxt, prompt, buf, bufsz); | |
175 | uint64_t num; | |
176 | ||
177 | if (rc) | |
178 | return rc; | |
179 | if (!*buf && dflt >= low && dflt <= high) | |
180 | return fdisk_ask_number_set_result(ask, dflt); | |
181 | ||
182 | if (isdigit_string(buf)) { | |
183 | char *end; | |
184 | ||
185 | errno = 0; | |
186 | num = strtoumax(buf, &end, 10); | |
187 | if (errno || buf == end || (end && *end)) | |
188 | continue; | |
189 | } else if (inchar && isalpha(*buf)) { | |
190 | num = tolower(*buf) - 'a' + 1; | |
191 | } else | |
192 | rc = -EINVAL; | |
193 | ||
194 | if (rc == 0 && num >= low && num <= high) | |
195 | return fdisk_ask_number_set_result(ask, num); | |
196 | ||
197 | fdisk_warnx(cxt, _("Value out of range.")); | |
198 | } while (1); | |
199 | ||
200 | return -1; | |
201 | } | |
202 | ||
203 | static int ask_offset(struct fdisk_context *cxt, | |
204 | struct fdisk_ask *ask, | |
205 | char *buf, size_t bufsz) | |
206 | { | |
207 | char prompt[128] = { '\0' }; | |
208 | const char *q = fdisk_ask_get_query(ask); | |
209 | const char *range = fdisk_ask_number_get_range(ask); | |
210 | ||
211 | uint64_t dflt = fdisk_ask_number_get_default(ask), | |
212 | low = fdisk_ask_number_get_low(ask), | |
213 | high = fdisk_ask_number_get_high(ask), | |
214 | base = fdisk_ask_number_get_base(ask); | |
215 | ||
216 | assert(q); | |
217 | ||
88141067 | 218 | DBG(ASK, ul_debug("asking for offset ['%s', <%ju,%ju>, base=%ju, default=%ju, range: %s]", |
d6b3ba41 KZ |
219 | q, low, high, base, dflt, range)); |
220 | ||
221 | if (range && dflt >= low && dflt <= high) | |
222 | snprintf(prompt, sizeof(prompt), _("%s (%s, default %ju): "), q, range, dflt); | |
223 | else if (dflt >= low && dflt <= high) | |
224 | snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju, default %ju): "), q, low, high, dflt); | |
225 | else | |
226 | snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju): "), q, low, high); | |
227 | ||
228 | do { | |
229 | uint64_t num = 0; | |
230 | char sig = 0, *p; | |
231 | int pwr = 0; | |
232 | ||
233 | int rc = get_user_reply(cxt, prompt, buf, bufsz); | |
234 | if (rc) | |
235 | return rc; | |
236 | if (!*buf && dflt >= low && dflt <= high) | |
237 | return fdisk_ask_number_set_result(ask, dflt); | |
238 | ||
239 | p = buf; | |
240 | if (*p == '+' || *p == '-') { | |
241 | sig = *buf; | |
242 | p++; | |
243 | } | |
244 | ||
245 | rc = parse_size(p, &num, &pwr); | |
246 | if (rc) | |
247 | continue; | |
88141067 | 248 | DBG(ASK, ul_debug("parsed size: %ju", num)); |
d6b3ba41 KZ |
249 | if (sig && pwr) { |
250 | /* +{size}{K,M,...} specified, the "num" is in bytes */ | |
251 | uint64_t unit = fdisk_ask_number_get_unit(ask); | |
252 | num += unit/2; /* round */ | |
253 | num /= unit; | |
254 | } | |
255 | if (sig == '+') | |
256 | num += base; | |
257 | else if (sig == '-') | |
258 | num = base - num; | |
259 | ||
88141067 | 260 | DBG(ASK, ul_debug("final offset: %ju [sig: %c, power: %d, %s]", |
d6b3ba41 KZ |
261 | num, sig, pwr, |
262 | sig ? "relative" : "absolute")); | |
263 | if (num >= low && num <= high) { | |
264 | if (sig) | |
265 | fdisk_ask_number_set_relative(ask, 1); | |
266 | return fdisk_ask_number_set_result(ask, num); | |
267 | } | |
268 | fdisk_warnx(cxt, _("Value out of range.")); | |
269 | } while (1); | |
270 | ||
271 | return -1; | |
272 | } | |
273 | ||
274 | static unsigned int info_count; | |
275 | ||
412791a9 | 276 | static void fputs_info(struct fdisk_ask *ask, FILE *out) |
d6b3ba41 KZ |
277 | { |
278 | const char *msg; | |
d6b3ba41 KZ |
279 | assert(ask); |
280 | ||
281 | msg = fdisk_ask_print_get_mesg(ask); | |
d6b3ba41 KZ |
282 | if (!msg) |
283 | return; | |
284 | if (info_count == 1) | |
285 | fputc('\n', out); | |
412791a9 KZ |
286 | |
287 | fputs(msg, out); | |
d6b3ba41 KZ |
288 | fputc('\n', out); |
289 | } | |
290 | ||
291 | int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask, | |
292 | void *data __attribute__((__unused__))) | |
293 | { | |
294 | int rc = 0; | |
295 | char buf[BUFSIZ]; | |
296 | ||
297 | assert(cxt); | |
298 | assert(ask); | |
299 | ||
300 | if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_INFO) | |
301 | info_count = 0; | |
302 | ||
303 | switch(fdisk_ask_get_type(ask)) { | |
304 | case FDISK_ASKTYPE_MENU: | |
305 | return ask_menu(cxt, ask, buf, sizeof(buf)); | |
306 | case FDISK_ASKTYPE_NUMBER: | |
307 | return ask_number(cxt, ask, buf, sizeof(buf)); | |
308 | case FDISK_ASKTYPE_OFFSET: | |
309 | return ask_offset(cxt, ask, buf, sizeof(buf)); | |
310 | case FDISK_ASKTYPE_INFO: | |
311 | info_count++; | |
412791a9 | 312 | fputs_info(ask, stdout); |
d6b3ba41 KZ |
313 | break; |
314 | case FDISK_ASKTYPE_WARNX: | |
496c979a | 315 | color_scheme_fenable("warn", UL_COLOR_RED, stderr); |
d6b3ba41 KZ |
316 | fputs(fdisk_ask_print_get_mesg(ask), stderr); |
317 | color_fdisable(stderr); | |
318 | fputc('\n', stderr); | |
319 | break; | |
320 | case FDISK_ASKTYPE_WARN: | |
496c979a | 321 | color_scheme_fenable("warn", UL_COLOR_RED, stderr); |
d6b3ba41 KZ |
322 | fputs(fdisk_ask_print_get_mesg(ask), stderr); |
323 | errno = fdisk_ask_print_get_errno(ask); | |
324 | fprintf(stderr, ": %m\n"); | |
325 | color_fdisable(stderr); | |
326 | break; | |
327 | case FDISK_ASKTYPE_YESNO: | |
328 | fputc('\n', stdout); | |
329 | fputs(fdisk_ask_get_query(ask), stdout); | |
330 | rc = get_user_reply(cxt, _(" [Y]es/[N]o: "), buf, sizeof(buf)); | |
331 | if (rc == 0) | |
332 | fdisk_ask_yesno_set_result(ask, rpmatch(buf)); | |
88141067 | 333 | DBG(ASK, ul_debug("yes-no ask: reply '%s' [rc=%d]", buf, rc)); |
d6b3ba41 KZ |
334 | break; |
335 | case FDISK_ASKTYPE_STRING: | |
336 | { | |
337 | char prmt[BUFSIZ]; | |
338 | snprintf(prmt, sizeof(prmt), "%s: ", fdisk_ask_get_query(ask)); | |
339 | fputc('\n', stdout); | |
340 | rc = get_user_reply(cxt, prmt, buf, sizeof(buf)); | |
341 | if (rc == 0) | |
342 | fdisk_ask_string_set_result(ask, xstrdup(buf)); | |
88141067 | 343 | DBG(ASK, ul_debug("string ask: reply '%s' [rc=%d]", buf, rc)); |
d6b3ba41 KZ |
344 | break; |
345 | } | |
346 | default: | |
347 | warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask)); | |
348 | return -EINVAL; | |
349 | } | |
350 | return rc; | |
351 | } | |
352 | ||
353 | struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt) | |
354 | { | |
355 | const char *q; | |
356 | ||
357 | if (!cxt || !cxt->label || !cxt->label->nparttypes) | |
358 | return NULL; | |
359 | ||
360 | q = fdisk_is_parttype_string(cxt) ? | |
361 | _("Partition type (type L to list all types): ") : | |
362 | _("Hex code (type L to list all codes): "); | |
363 | do { | |
364 | char buf[256]; | |
365 | int rc = get_user_reply(cxt, q, buf, sizeof(buf)); | |
366 | ||
367 | if (rc) | |
368 | break; | |
369 | ||
370 | if (buf[1] == '\0' && toupper(*buf) == 'L') | |
371 | list_partition_types(cxt); | |
372 | else if (*buf) | |
373 | return fdisk_parse_parttype(cxt, buf); | |
374 | } while (1); | |
375 | ||
376 | return NULL; | |
377 | } | |
378 | ||
7b575fcc | 379 | void list_partition_types(struct fdisk_context *cxt) |
6dbe3af9 | 380 | { |
7b575fcc | 381 | struct fdisk_parttype *types; |
76f17cf2 | 382 | size_t ntypes = 0; |
6dbe3af9 | 383 | |
7b575fcc KZ |
384 | if (!cxt || !cxt->label || !cxt->label->parttypes) |
385 | return; | |
2b6fc908 | 386 | |
7b575fcc | 387 | types = cxt->label->parttypes; |
76f17cf2 | 388 | ntypes = cxt->label->nparttypes; |
6dbe3af9 | 389 | |
7b575fcc KZ |
390 | if (types[0].typestr == NULL) { |
391 | /* | |
392 | * Prints in 4 columns in format <hex> <name> | |
393 | */ | |
76f17cf2 KZ |
394 | size_t last[4], done = 0, next = 0, size; |
395 | int i; | |
b8d22034 | 396 | |
76f17cf2 KZ |
397 | size = ntypes; |
398 | if (types[ntypes - 1].name == NULL) | |
399 | size--; | |
7b575fcc KZ |
400 | |
401 | for (i = 3; i >= 0; i--) | |
402 | last[3 - i] = done += (size + i - done) / (i + 1); | |
403 | i = done = 0; | |
404 | ||
405 | do { | |
406 | #define NAME_WIDTH 15 | |
407 | char name[NAME_WIDTH * MB_LEN_MAX]; | |
408 | size_t width = NAME_WIDTH; | |
409 | struct fdisk_parttype *t = &types[next]; | |
410 | size_t ret; | |
411 | ||
76f17cf2 KZ |
412 | if (t->name) { |
413 | printf("%c%2x ", i ? ' ' : '\n', t->type); | |
414 | ret = mbsalign(_(t->name), name, sizeof(name), | |
415 | &width, MBS_ALIGN_LEFT, 0); | |
7b575fcc | 416 | |
76f17cf2 KZ |
417 | if (ret == (size_t)-1 || ret >= sizeof(name)) |
418 | printf("%-15.15s", _(t->name)); | |
419 | else | |
420 | fputs(name, stdout); | |
421 | } | |
7b575fcc KZ |
422 | |
423 | next = last[i++] + done; | |
424 | if (i > 3 || next >= last[i]) { | |
425 | i = 0; | |
426 | next = ++done; | |
427 | } | |
428 | } while (done < last[0]); | |
429 | ||
430 | } else { | |
431 | /* | |
432 | * Prints 1 column in format <idx> <name> <typestr> | |
433 | */ | |
434 | struct fdisk_parttype *t; | |
76f17cf2 | 435 | size_t i; |
7b575fcc | 436 | |
76f17cf2 KZ |
437 | for (i = 0, t = types; t && i < ntypes; t++, i++) { |
438 | if (t->name) | |
439 | printf("%3zu %-30s %s\n", i + 1, | |
440 | t->name, t->typestr); | |
441 | } | |
7b575fcc | 442 | } |
6dbe3af9 KZ |
443 | putchar('\n'); |
444 | } | |
445 | ||
f02fecd1 | 446 | void toggle_dos_compatibility_flag(struct fdisk_context *cxt) |
852ce62b KZ |
447 | { |
448 | struct fdisk_label *lb = fdisk_context_get_label(cxt, "dos"); | |
449 | int flag; | |
450 | ||
451 | if (!lb) | |
452 | return; | |
453 | ||
454 | flag = !fdisk_dos_is_compatible(lb); | |
09f0c3d9 KZ |
455 | fdisk_info(cxt, flag ? |
456 | _("DOS Compatibility flag is set (DEPRECATED!)") : | |
457 | _("DOS Compatibility flag is not set")); | |
edd7b958 | 458 | |
852ce62b KZ |
459 | fdisk_dos_enable_compatible(lb, flag); |
460 | ||
461 | if (fdisk_is_disklabel(cxt, DOS)) | |
09f0c3d9 | 462 | fdisk_reset_alignment(cxt); /* reset the current label */ |
6dbe3af9 KZ |
463 | } |
464 | ||
27ddd4f1 | 465 | void change_partition_type(struct fdisk_context *cxt) |
e53ced85 | 466 | { |
641a75ca | 467 | size_t i; |
85151521 KZ |
468 | struct fdisk_parttype *t = NULL; |
469 | struct fdisk_partition *pa = NULL; | |
470 | const char *old = NULL; | |
6dbe3af9 | 471 | |
e3661531 KZ |
472 | assert(cxt); |
473 | assert(cxt->label); | |
474 | ||
641a75ca | 475 | if (fdisk_ask_partnum(cxt, &i, FALSE)) |
24f4bbff | 476 | return; |
2b6fc908 | 477 | |
85151521 KZ |
478 | if (fdisk_get_partition(cxt, i, &pa)) { |
479 | fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1); | |
480 | return; | |
481 | } | |
482 | ||
483 | t = (struct fdisk_parttype *) fdisk_partition_get_type(pa); | |
484 | old = t ? t->name : _("Unknown"); | |
485 | ||
486 | do { | |
487 | t = ask_partition_type(cxt); | |
488 | } while (!t); | |
489 | ||
85151521 KZ |
490 | if (fdisk_set_partition_type(cxt, i, t) == 0) |
491 | fdisk_sinfo(cxt, FDISK_INFO_SUCCESS, | |
492 | _("Changed type of partition '%s' to '%s'."), | |
493 | old, t ? t->name : _("Unknown")); | |
494 | else | |
495 | fdisk_info(cxt, | |
496 | _("Type of partition %zu is unchanged: %s."), | |
497 | i + 1, old); | |
498 | ||
d6cfa8b3 | 499 | fdisk_unref_partition(pa); |
6dbe3af9 KZ |
500 | } |
501 | ||
89309968 | 502 | void list_disk_geometry(struct fdisk_context *cxt) |
21fe3dde KZ |
503 | { |
504 | char *id = NULL; | |
9af73f84 KZ |
505 | uint64_t bytes = cxt->total_sectors * cxt->sector_size; |
506 | char *strsz = size_to_human_string(SIZE_SUFFIX_SPACE | |
507 | | SIZE_SUFFIX_3LETTER, bytes); | |
508 | ||
412791a9 | 509 | fdisk_info(cxt, _("Disk %s: %s, %ju bytes, %ju sectors"), |
e39966c6 | 510 | cxt->dev_path, strsz, |
48d4d931 | 511 | bytes, (uintmax_t) cxt->total_sectors); |
9af73f84 | 512 | free(strsz); |
b1b1a7b7 | 513 | |
96dc2798 | 514 | if (fdisk_require_geometry(cxt) || fdisk_context_use_cylinders(cxt)) |
412791a9 | 515 | fdisk_info(cxt, _("Geometry: %d heads, %llu sectors/track, %llu cylinders"), |
9af73f84 | 516 | cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders); |
b1b1a7b7 | 517 | |
412791a9 | 518 | fdisk_info(cxt, _("Units: %s of %d * %ld = %ld bytes"), |
ec10aa67 KZ |
519 | fdisk_context_get_unit(cxt, PLURAL), |
520 | fdisk_context_get_units_per_sector(cxt), | |
521 | cxt->sector_size, | |
522 | fdisk_context_get_units_per_sector(cxt) * cxt->sector_size); | |
05dc9645 | 523 | |
412791a9 | 524 | fdisk_info(cxt, _("Sector size (logical/physical): %lu bytes / %lu bytes"), |
e53ced85 | 525 | cxt->sector_size, cxt->phy_sector_size); |
412791a9 | 526 | fdisk_info(cxt, _("I/O size (minimum/optimal): %lu bytes / %lu bytes"), |
e53ced85 DB |
527 | cxt->min_io_size, cxt->io_size); |
528 | if (cxt->alignment_offset) | |
412791a9 | 529 | fdisk_info(cxt, _("Alignment offset: %lu bytes"), |
9af73f84 | 530 | cxt->alignment_offset); |
b546d442 | 531 | if (fdisk_dev_has_disklabel(cxt)) |
412791a9 | 532 | fdisk_info(cxt, _("Disklabel type: %s"), cxt->label->name); |
21fe3dde KZ |
533 | |
534 | if (fdisk_get_disklabel_id(cxt, &id) == 0 && id) | |
412791a9 | 535 | fdisk_info(cxt, _("Disk identifier: %s"), id); |
5c36a0eb KZ |
536 | } |
537 | ||
9f670072 KZ |
538 | void list_disklabel(struct fdisk_context *cxt) |
539 | { | |
540 | struct fdisk_table *tb = NULL; | |
c9400a1e KZ |
541 | struct fdisk_partition *pa = NULL; |
542 | struct fdisk_iter *itr; | |
543 | ||
9f670072 KZ |
544 | char *str; |
545 | ||
546 | /* print label specific stuff by libfdisk FDISK_ASK_INFO API */ | |
547 | fdisk_list_disklabel(cxt); | |
548 | ||
549 | /* print partitions */ | |
2cec7949 | 550 | if (fdisk_get_partitions(cxt, &tb)) |
9f670072 KZ |
551 | return; |
552 | if (fdisk_table_to_string(tb, cxt, NULL, 0, &str) == 0) { | |
553 | fputc('\n', stdout); | |
7fa74cb0 KZ |
554 | if (str) { |
555 | char *p = str; | |
556 | char *next = strchr(str, '\n'); | |
557 | if (next && colors_wanted()) { | |
558 | *next = '\0'; | |
496c979a | 559 | color_scheme_enable("header", UL_COLOR_BOLD); |
7fa74cb0 KZ |
560 | fputs(p, stdout); |
561 | color_disable(); | |
562 | fputc('\n', stdout); | |
563 | p = ++next; | |
564 | } | |
565 | fputs(p, stdout); | |
566 | free(str); | |
567 | } | |
9f670072 | 568 | } |
8b60872e | 569 | |
c9400a1e KZ |
570 | fputc('\n', stdout); |
571 | ||
4fdde144 KZ |
572 | itr = fdisk_new_iter(FDISK_ITER_FORWARD); |
573 | ||
c9400a1e KZ |
574 | while (itr && fdisk_table_next_partition(tb, itr, &pa) == 0) |
575 | fdisk_warn_alignment(cxt, fdisk_partition_get_start(pa), | |
576 | fdisk_partition_get_partno(pa) + 1); | |
577 | ||
4fdde144 | 578 | if (fdisk_table_wrong_order(tb)) |
8b60872e | 579 | fdisk_info(cxt, _("Partition table entries are not in disk order.")); |
8b60872e | 580 | |
9f670072 | 581 | fdisk_unref_table(tb); |
c9400a1e | 582 | fdisk_free_iter(itr); |
9f670072 KZ |
583 | } |
584 | ||
e916600f KZ |
585 | static size_t skip_empty(const unsigned char *buf, size_t i, size_t sz) |
586 | { | |
587 | size_t next; | |
588 | const unsigned char *p0 = buf + i; | |
589 | ||
590 | for (next = i + 16; next < sz; next += 16) { | |
591 | if (memcmp(p0, buf + next, 16) != 0) | |
592 | break; | |
593 | } | |
594 | ||
595 | return next == i + 16 ? i : next; | |
596 | } | |
6dbe3af9 | 597 | |
e916600f | 598 | static void dump_buffer(off_t base, unsigned char *buf, size_t sz, int all) |
09f0c3d9 | 599 | { |
e916600f KZ |
600 | size_t i, l, next = 0; |
601 | ||
602 | if (!buf) | |
603 | return; | |
604 | for (i = 0, l = 0; i < sz; i++, l++) { | |
605 | if (l == 0) { | |
606 | if (all == 0 && !next) | |
607 | next = skip_empty(buf, i, sz); | |
608 | printf("%08jx ", base + i); | |
609 | } | |
610 | printf(" %02x", buf[i]); | |
611 | if (l == 7) /* words separator */ | |
612 | fputs(" ", stdout); | |
613 | else if (l == 15) { | |
614 | fputc('\n', stdout); /* next line */ | |
6dbe3af9 | 615 | l = -1; |
e916600f KZ |
616 | if (next > i) { |
617 | printf("*\n"); | |
618 | i = next - 1; | |
619 | } | |
620 | next = 0; | |
6dbe3af9 KZ |
621 | } |
622 | } | |
623 | if (l > 0) | |
624 | printf("\n"); | |
6dbe3af9 KZ |
625 | } |
626 | ||
e916600f KZ |
627 | static void dump_blkdev(struct fdisk_context *cxt, const char *name, |
628 | off_t offset, size_t size, int all) | |
e53ced85 | 629 | { |
412791a9 | 630 | fdisk_info(cxt, _("\n%s: offset = %ju, size = %zu bytes."), |
e916600f KZ |
631 | name, offset, size); |
632 | ||
633 | if (lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1) | |
634 | fdisk_warn(cxt, _("cannot seek")); | |
5afed187 KZ |
635 | else { |
636 | unsigned char *buf = xmalloc(size); | |
637 | ||
638 | if (read_all(cxt->dev_fd, (char *) buf, size) != (ssize_t) size) | |
639 | fdisk_warn(cxt, _("cannot read")); | |
640 | else | |
641 | dump_buffer(offset, buf, size, all); | |
642 | free(buf); | |
643 | } | |
e916600f KZ |
644 | } |
645 | ||
646 | void dump_firstsector(struct fdisk_context *cxt) | |
647 | { | |
648 | int all = !isatty(STDOUT_FILENO); | |
649 | ||
e3661531 KZ |
650 | assert(cxt); |
651 | assert(cxt->label); | |
6dbe3af9 | 652 | |
e916600f KZ |
653 | dump_blkdev(cxt, _("First sector"), 0, cxt->sector_size, all); |
654 | } | |
655 | ||
656 | void dump_disklabel(struct fdisk_context *cxt) | |
657 | { | |
658 | int all = !isatty(STDOUT_FILENO); | |
659 | int i = 0; | |
660 | const char *name = NULL; | |
661 | off_t offset = 0; | |
662 | size_t size = 0; | |
663 | ||
664 | assert(cxt); | |
665 | assert(cxt->label); | |
e3661531 | 666 | |
e916600f KZ |
667 | while (fdisk_locate_disklabel(cxt, i++, &name, &offset, &size) == 0 && size) |
668 | dump_blkdev(cxt, name, offset, size, all); | |
6dbe3af9 KZ |
669 | } |
670 | ||
3b622ddd DB |
671 | static int is_ide_cdrom_or_tape(char *device) |
672 | { | |
673 | int fd, ret; | |
2b6fc908 | 674 | |
1c6b3378 | 675 | if ((fd = open(device, O_RDONLY)) < 0) |
e8f26419 | 676 | return 0; |
3b622ddd | 677 | ret = blkdev_is_cdrom(fd); |
2b6fc908 | 678 | |
3b622ddd DB |
679 | close(fd); |
680 | return ret; | |
2b6fc908 KZ |
681 | } |
682 | ||
52d92543 | 683 | static void print_device_pt(struct fdisk_context *cxt, char *device, int warnme) |
e27b42d5 | 684 | { |
52d92543 KZ |
685 | if (fdisk_context_assign_device(cxt, device, 1) != 0) { /* read-only */ |
686 | if (warnme || errno == EACCES) | |
687 | warn(_("cannot open %s"), device); | |
688 | return; | |
689 | } | |
823f0fd1 | 690 | |
5e860870 KZ |
691 | list_disk_geometry(cxt); |
692 | ||
b4fb2a61 | 693 | if (fdisk_dev_has_disklabel(cxt)) |
9f670072 | 694 | list_disklabel(cxt); |
6dbe3af9 KZ |
695 | } |
696 | ||
6aa2ed3b | 697 | static void print_all_devices_pt(struct fdisk_context *cxt) |
ea4824f1 | 698 | { |
6aa2ed3b KZ |
699 | FILE *f; |
700 | char line[128 + 1]; | |
701 | ||
702 | f = fopen(_PATH_PROC_PARTITIONS, "r"); | |
703 | if (!f) { | |
704 | warn(_("cannot open %s"), _PATH_PROC_PARTITIONS); | |
eb63b9b8 KZ |
705 | return; |
706 | } | |
707 | ||
88141067 | 708 | DBG(FRONTEND, ul_debug("reading "_PATH_PROC_PARTITIONS)); |
d31d3fc7 | 709 | |
6aa2ed3b | 710 | while (fgets(line, sizeof(line), f)) { |
a22c6eb2 KZ |
711 | char buf[PATH_MAX], *cn; |
712 | dev_t devno; | |
6aa2ed3b | 713 | |
a22c6eb2 | 714 | if (sscanf(line, " %*d %*d %*d %128[^\n ]", buf) != 1) |
eb63b9b8 | 715 | continue; |
6aa2ed3b | 716 | |
a22c6eb2 KZ |
717 | devno = sysfs_devname_to_devno(buf, NULL); |
718 | if (devno <= 0) | |
719 | continue; | |
d31d3fc7 | 720 | |
a22c6eb2 KZ |
721 | if (sysfs_devno_is_lvm_private(devno) || |
722 | sysfs_devno_is_wholedisk(devno) <= 0) | |
723 | continue; | |
d31d3fc7 | 724 | |
a22c6eb2 KZ |
725 | if (!sysfs_devno_to_devpath(devno, buf, sizeof(buf))) |
726 | continue; | |
727 | ||
728 | cn = canonicalize_path(buf); | |
729 | if (!cn) | |
730 | continue; | |
731 | if (!is_ide_cdrom_or_tape(cn)) | |
52d92543 | 732 | print_device_pt(cxt, cn, 0); |
a22c6eb2 | 733 | free(cn); |
eb63b9b8 | 734 | } |
6aa2ed3b | 735 | fclose(f); |
eb63b9b8 KZ |
736 | } |
737 | ||
50dec1eb | 738 | static sector_t get_dev_blocks(char *dev) |
6da365de | 739 | { |
bbe67996 | 740 | int fd, ret; |
50dec1eb | 741 | sector_t size; |
6da365de DB |
742 | |
743 | if ((fd = open(dev, O_RDONLY)) < 0) | |
289dcc90 | 744 | err(EXIT_FAILURE, _("cannot open %s"), dev); |
bbe67996 | 745 | ret = blkdev_get_sectors(fd, &size); |
6da365de | 746 | close(fd); |
bbe67996 SK |
747 | if (ret < 0) |
748 | err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev); | |
6da365de DB |
749 | return size/2; |
750 | } | |
751 | ||
e3a4aaa7 KZ |
752 | static void __attribute__ ((__noreturn__)) usage(FILE *out) |
753 | { | |
754 | fputs(USAGE_HEADER, out); | |
755 | ||
756 | fprintf(out, | |
757 | _(" %1$s [options] <disk> change partition table\n" | |
758 | " %1$s [options] -l [<disk>] list partition table(s)\n"), | |
759 | program_invocation_short_name); | |
760 | ||
761 | fputs(USAGE_OPTIONS, out); | |
762 | fputs(_(" -b, --sector-size <size> physical and logical sector size\n"), out); | |
763 | fputs(_(" -c, --compatibility[=<mode>] mode is 'dos' or 'nondos' (default)\n"), out); | |
764 | fputs(_(" -L, --color[=<when>] colorize output (auto, always or never)\n"), out); | |
765 | fputs(_(" -l, --list display partitions end exit\n"), out); | |
766 | fputs(_(" -t, --type <type> recognize specified partition table type only\n"), out); | |
767 | fputs(_(" -u, --units[=<unit>] display units: 'cylinders' or 'sectors' (default)\n"), out); | |
768 | fputs(_(" -s, --getsz display device size in 512-byte sectors [DEPRECATED]\n"), out); | |
769 | ||
770 | fputs(USAGE_SEPARATOR, out); | |
4b4e391a KZ |
771 | fputs(_(" -C, --cylinders <number> specify the number of cylinders\n"), out); |
772 | fputs(_(" -H, --heads <number> specify the number of heads\n"), out); | |
773 | fputs(_(" -S, --sectors <number> specify the number of sectors per track\n"), out); | |
e3a4aaa7 KZ |
774 | |
775 | fputs(USAGE_SEPARATOR, out); | |
776 | fputs(USAGE_HELP, out); | |
777 | fputs(USAGE_VERSION, out); | |
778 | ||
779 | fprintf(out, USAGE_MAN_TAIL("fdisk(8)")); | |
780 | exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); | |
781 | } | |
782 | ||
783 | ||
5b72b3dd KZ |
784 | enum { |
785 | ACT_FDISK = 0, /* default */ | |
786 | ACT_LIST, | |
787 | ACT_SHOWSIZE | |
788 | }; | |
789 | ||
e1144767 DB |
790 | int main(int argc, char **argv) |
791 | { | |
e146ae4e | 792 | int rc, i, c, act = ACT_FDISK; |
d0c9ddc3 | 793 | int colormode = UL_COLORMODE_UNDEF; |
4e0e8253 | 794 | struct fdisk_context *cxt; |
2b6fc908 | 795 | |
e3a4aaa7 KZ |
796 | static const struct option longopts[] = { |
797 | { "color", optional_argument, NULL, 'L' }, | |
798 | { "compatibility", optional_argument, NULL, 'c' }, | |
4b4e391a KZ |
799 | { "cylinders", required_argument, NULL, 'C' }, |
800 | { "heads", required_argument, NULL, 'H' }, | |
801 | { "sectors", required_argument, NULL, 'S' }, | |
e3a4aaa7 KZ |
802 | { "getsz", no_argument, NULL, 's' }, |
803 | { "help", no_argument, NULL, 'h' }, | |
804 | { "list", no_argument, NULL, 'l' }, | |
805 | { "sector-size", required_argument, NULL, 'b' }, | |
806 | { "type", required_argument, NULL, 't' }, | |
807 | { "units", optional_argument, NULL, 'u' }, | |
808 | { "version", no_argument, NULL, 'V' }, | |
809 | { NULL, 0, NULL, 0 } | |
810 | }; | |
811 | ||
7eda085c KZ |
812 | setlocale(LC_ALL, ""); |
813 | bindtextdomain(PACKAGE, LOCALEDIR); | |
814 | textdomain(PACKAGE); | |
b2d28533 | 815 | atexit(close_stdout); |
2b6fc908 | 816 | |
4e0e8253 KZ |
817 | fdisk_init_debug(0); |
818 | cxt = fdisk_new_context(); | |
819 | if (!cxt) | |
820 | err(EXIT_FAILURE, _("failed to allocate libfdisk context")); | |
821 | ||
416c43a9 KZ |
822 | fdisk_context_set_ask(cxt, ask_callback, NULL); |
823 | ||
e3a4aaa7 KZ |
824 | while ((c = getopt_long(argc, argv, "b:c::C:hH:lL::sS:t:u::vV", |
825 | longopts, NULL)) != -1) { | |
2b6fc908 KZ |
826 | switch (c) { |
827 | case 'b': | |
6bcd192c KZ |
828 | { |
829 | size_t sz = strtou32_or_err(optarg, | |
830 | _("invalid sector size argument")); | |
831 | if (sz != 512 && sz != 1024 && sz != 2048 && sz != 4096) | |
7c1db6b4 | 832 | usage(stderr); |
6bcd192c | 833 | fdisk_save_user_sector_size(cxt, sz, sz); |
2b6fc908 | 834 | break; |
6bcd192c | 835 | } |
0e6f4a20 | 836 | case 'C': |
1653f0b0 KZ |
837 | fdisk_save_user_geometry(cxt, |
838 | strtou32_or_err(optarg, | |
839 | _("invalid cylinders argument")), | |
840 | 0, 0); | |
0e6f4a20 | 841 | break; |
78498b7b | 842 | case 'c': |
852ce62b | 843 | if (optarg) { |
2b0bc17b | 844 | /* this setting is independent on the current |
e3a4aaa7 KZ |
845 | * actively used label |
846 | */ | |
847 | char *p = *optarg == '=' ? optarg + 1 : optarg; | |
5b72b3dd | 848 | struct fdisk_label *lb = fdisk_context_get_label(cxt, "dos"); |
e3a4aaa7 | 849 | |
852ce62b KZ |
850 | if (!lb) |
851 | err(EXIT_FAILURE, _("not found DOS label driver")); | |
e3a4aaa7 | 852 | if (strcmp(p, "dos") == 0) |
852ce62b | 853 | fdisk_dos_enable_compatible(lb, TRUE); |
e3a4aaa7 | 854 | else if (strcmp(p, "nondos") == 0) |
852ce62b | 855 | fdisk_dos_enable_compatible(lb, FALSE); |
e3a4aaa7 KZ |
856 | else { |
857 | warnx(_("unknown compatibility mode '%s'"), p); | |
852ce62b | 858 | usage(stderr); |
e3a4aaa7 | 859 | } |
852ce62b KZ |
860 | } |
861 | /* use default if no optarg specified */ | |
78498b7b | 862 | break; |
0e6f4a20 | 863 | case 'H': |
1653f0b0 KZ |
864 | fdisk_save_user_geometry(cxt, 0, |
865 | strtou32_or_err(optarg, | |
866 | _("invalid heads argument")), | |
867 | 0); | |
0e6f4a20 KZ |
868 | break; |
869 | case 'S': | |
1653f0b0 KZ |
870 | fdisk_save_user_geometry(cxt, 0, 0, |
871 | strtou32_or_err(optarg, | |
872 | _("invalid sectors argument"))); | |
0e6f4a20 | 873 | break; |
2b6fc908 | 874 | case 'l': |
5b72b3dd | 875 | act = ACT_LIST; |
2b6fc908 | 876 | break; |
80a1712f KZ |
877 | case 'L': |
878 | if (optarg) | |
879 | colormode = colormode_or_err(optarg, | |
880 | _("unsupported color mode")); | |
881 | break; | |
2b6fc908 | 882 | case 's': |
5b72b3dd | 883 | act = ACT_SHOWSIZE; |
2b6fc908 | 884 | break; |
565b7da6 KZ |
885 | case 't': |
886 | { | |
887 | struct fdisk_label *lb = NULL; | |
888 | ||
889 | while (fdisk_context_next_label(cxt, &lb) == 0) | |
890 | fdisk_label_set_disabled(lb, 1); | |
891 | ||
892 | lb = fdisk_context_get_label(cxt, optarg); | |
893 | if (!lb) | |
894 | errx(EXIT_FAILURE, _("unsupported disklabel: %s"), optarg); | |
895 | fdisk_label_set_disabled(lb, 0); | |
896 | } | |
2b6fc908 | 897 | case 'u': |
ec10aa67 KZ |
898 | if (optarg && *optarg == '=') |
899 | optarg++; | |
900 | if (fdisk_context_set_unit(cxt, optarg) != 0) | |
7c1db6b4 | 901 | usage(stderr); |
2b6fc908 | 902 | break; |
e3a4aaa7 KZ |
903 | case 'V': /* preferred for util-linux */ |
904 | case 'v': /* for backward compatibility only */ | |
a2482eb3 DB |
905 | printf(UTIL_LINUX_VERSION); |
906 | return EXIT_SUCCESS; | |
e1144767 DB |
907 | case 'h': |
908 | usage(stdout); | |
2b6fc908 | 909 | default: |
7c1db6b4 | 910 | usage(stderr); |
2b6fc908 | 911 | } |
6dbe3af9 | 912 | } |
2b6fc908 | 913 | |
6bcd192c KZ |
914 | if (argc-optind != 1 && fdisk_has_user_device_properties(cxt)) |
915 | warnx(_("The device properties (sector size and geometry) should" | |
916 | " be used with one specified device only.")); | |
7eda085c | 917 | |
d0c9ddc3 | 918 | colors_init(colormode, "fdisk"); |
ffcf0540 | 919 | |
5b72b3dd KZ |
920 | switch (act) { |
921 | case ACT_LIST: | |
c10937dc | 922 | fdisk_context_enable_listonly(cxt, 1); |
ffcf0540 | 923 | |
2b6fc908 KZ |
924 | if (argc > optind) { |
925 | int k; | |
c129767e | 926 | for (k = optind; k < argc; k++) |
52d92543 | 927 | print_device_pt(cxt, argv[k], 1); |
ea4824f1 | 928 | } else |
6aa2ed3b | 929 | print_all_devices_pt(cxt); |
5b72b3dd | 930 | break; |
2b6fc908 | 931 | |
5b72b3dd | 932 | case ACT_SHOWSIZE: |
9564e46c | 933 | /* deprecated */ |
5b72b3dd | 934 | if (argc - optind <= 0) |
7c1db6b4 | 935 | usage(stderr); |
2b6fc908 | 936 | |
6da365de | 937 | for (i = optind; i < argc; i++) { |
5b72b3dd | 938 | if (argc - optind == 1) |
6da365de | 939 | printf("%llu\n", get_dev_blocks(argv[i])); |
2b6fc908 | 940 | else |
6da365de | 941 | printf("%s: %llu\n", argv[i], get_dev_blocks(argv[i])); |
6dbe3af9 | 942 | } |
5b72b3dd | 943 | break; |
6dbe3af9 | 944 | |
5b72b3dd KZ |
945 | case ACT_FDISK: |
946 | if (argc-optind != 1) | |
947 | usage(stderr); | |
7eda085c | 948 | |
5b72b3dd | 949 | /* Here starts interactive mode, use fdisk_{warn,info,..} functions */ |
496c979a | 950 | color_scheme_enable("welcome", UL_COLOR_GREEN); |
09f0c3d9 | 951 | fdisk_info(cxt, _("Welcome to fdisk (%s)."), PACKAGE_STRING); |
80a1712f KZ |
952 | color_disable(); |
953 | fdisk_info(cxt, _("Changes will remain in memory only, until you decide to write them.\n" | |
954 | "Be careful before using the write command.\n")); | |
2d8988bd | 955 | |
e146ae4e KZ |
956 | rc = fdisk_context_assign_device(cxt, argv[optind], 0); |
957 | if (rc == -EACCES) { | |
958 | rc = fdisk_context_assign_device(cxt, argv[optind], 1); | |
959 | if (rc == 0) | |
960 | fdisk_warnx(cxt, _("Device open in read-only mode.")); | |
961 | } | |
962 | if (rc) | |
2d8988bd KZ |
963 | err(EXIT_FAILURE, _("cannot open %s"), argv[optind]); |
964 | ||
5b72b3dd | 965 | fflush(stdout); |
56c07b96 | 966 | |
5b72b3dd | 967 | if (!fdisk_dev_has_disklabel(cxt)) { |
2d8988bd | 968 | fdisk_info(cxt, _("Device does not contain a recognized partition table.")); |
5b72b3dd | 969 | fdisk_create_disklabel(cxt, NULL); |
433d05ff KZ |
970 | |
971 | } else if (fdisk_is_disklabel(cxt, GPT) && fdisk_gpt_is_hybrid(cxt)) | |
972 | fdisk_warnx(cxt, _( | |
09af3db4 | 973 | "A hybrid GPT was detected. You have to sync " |
433d05ff | 974 | "the hybrid MBR manually (expert command 'M').")); |
6dbe3af9 | 975 | |
5b72b3dd KZ |
976 | while (1) |
977 | process_fdisk_menu(&cxt); | |
978 | } | |
9777759a | 979 | |
4e0e8253 | 980 | fdisk_free_context(cxt); |
5b72b3dd | 981 | return EXIT_SUCCESS; |
6dbe3af9 | 982 | } |