]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) | |
3 | * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org> | |
4 | * | |
5 | * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com> | |
6 | * | |
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. | |
11 | */ | |
12 | #include <unistd.h> | |
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <string.h> | |
16 | #include <fcntl.h> | |
17 | #include <ctype.h> | |
18 | #include <errno.h> | |
19 | #include <getopt.h> | |
20 | #include <sys/stat.h> | |
21 | #include <sys/time.h> | |
22 | #include <time.h> | |
23 | #include <limits.h> | |
24 | #include <signal.h> | |
25 | #include <poll.h> | |
26 | #include <libsmartcols.h> | |
27 | #ifdef HAVE_LIBREADLINE | |
28 | # define _FUNCTION_DEF | |
29 | # include <readline/readline.h> | |
30 | #endif | |
31 | ||
32 | #include "c.h" | |
33 | #include "xalloc.h" | |
34 | #include "all-io.h" | |
35 | #include "nls.h" | |
36 | #include "rpmatch.h" | |
37 | #include "blkdev.h" | |
38 | #include "mbsalign.h" | |
39 | #include "pathnames.h" | |
40 | #include "canonicalize.h" | |
41 | #include "strutils.h" | |
42 | #include "closestream.h" | |
43 | #include "pager.h" | |
44 | ||
45 | #include "fdisk.h" | |
46 | ||
47 | #include "pt-sun.h" /* to toggle flags */ | |
48 | ||
49 | #ifdef HAVE_LINUX_COMPILER_H | |
50 | # include <linux/compiler.h> | |
51 | #endif | |
52 | #ifdef HAVE_LINUX_BLKPG_H | |
53 | # include <linux/blkpg.h> | |
54 | #endif | |
55 | ||
56 | int pwipemode = WIPEMODE_AUTO; | |
57 | int device_is_used; | |
58 | int is_interactive; | |
59 | struct fdisk_table *original_layout; | |
60 | ||
61 | static int wipemode = WIPEMODE_AUTO; | |
62 | ||
63 | /* | |
64 | * fdisk debug stuff (see fdisk.h and include/debug.h) | |
65 | */ | |
66 | UL_DEBUG_DEFINE_MASK(fdisk); | |
67 | UL_DEBUG_DEFINE_MASKNAMES(fdisk) = UL_DEBUG_EMPTY_MASKNAMES; | |
68 | ||
69 | static void fdiskprog_init_debug(void) | |
70 | { | |
71 | __UL_INIT_DEBUG_FROM_ENV(fdisk, FDISKPROG_DEBUG_, 0, FDISK_DEBUG); | |
72 | } | |
73 | ||
74 | static void reply_sighandler(int sig __attribute__((unused))) | |
75 | { | |
76 | DBG(ASK, ul_debug("got signal")); | |
77 | } | |
78 | ||
79 | static int reply_running; | |
80 | ||
81 | #ifdef HAVE_LIBREADLINE | |
82 | static char *reply_line; | |
83 | ||
84 | static void reply_linehandler(char *line) | |
85 | { | |
86 | reply_line = line; | |
87 | reply_running = 0; | |
88 | rl_callback_handler_remove(); /* avoid duplicate prompt */ | |
89 | } | |
90 | #endif | |
91 | ||
92 | int get_user_reply(const char *prompt, char *buf, size_t bufsz) | |
93 | { | |
94 | struct sigaction oldact, act = { | |
95 | .sa_handler = reply_sighandler | |
96 | }; | |
97 | struct pollfd fds[] = { | |
98 | { .fd = fileno(stdin), .events = POLLIN } | |
99 | }; | |
100 | size_t sz; | |
101 | int ret = 0; | |
102 | ||
103 | DBG(ASK, ul_debug("asking for user reply %s", is_interactive ? "[interactive]" : "")); | |
104 | ||
105 | sigemptyset(&act.sa_mask); | |
106 | sigaction(SIGINT, &act, &oldact); | |
107 | ||
108 | #ifdef HAVE_LIBREADLINE | |
109 | if (is_interactive) | |
110 | rl_callback_handler_install(prompt, reply_linehandler); | |
111 | #endif | |
112 | reply_running = 1; | |
113 | do { | |
114 | int rc; | |
115 | ||
116 | *buf = '\0'; | |
117 | #ifdef HAVE_LIBREADLINE | |
118 | if (!is_interactive) | |
119 | #endif | |
120 | { | |
121 | fputs(prompt, stdout); | |
122 | fflush(stdout); | |
123 | } | |
124 | ||
125 | rc = poll(fds, 1, -1); | |
126 | if (rc == -1 && errno == EINTR) { /* interrupted by signal */ | |
127 | DBG(ASK, ul_debug("cancel by CTRL+C")); | |
128 | ret = -ECANCELED; | |
129 | goto done; | |
130 | } | |
131 | if (rc == -1 && errno != EAGAIN) { /* error */ | |
132 | ret = -errno; | |
133 | goto done; | |
134 | } | |
135 | #ifdef HAVE_LIBREADLINE | |
136 | if (is_interactive) { | |
137 | /* read input and copy to buf[] */ | |
138 | rl_callback_read_char(); | |
139 | if (!reply_running && reply_line) { | |
140 | sz = strlen(reply_line); | |
141 | if (sz == 0) | |
142 | buf[sz++] = '\n'; | |
143 | else | |
144 | memcpy(buf, reply_line, min(sz, bufsz)); | |
145 | buf[min(sz, bufsz - 1)] = '\0'; | |
146 | free(reply_line); | |
147 | reply_line = NULL; | |
148 | } | |
149 | } else | |
150 | #endif | |
151 | { | |
152 | if (!fgets(buf, bufsz, stdin)) | |
153 | *buf = '\0'; | |
154 | break; | |
155 | } | |
156 | } while (reply_running); | |
157 | ||
158 | if (!*buf) { | |
159 | DBG(ASK, ul_debug("cancel by CTRL+D")); | |
160 | ret = -ECANCELED; | |
161 | goto done; | |
162 | } | |
163 | ||
164 | /* | |
165 | * cleanup the reply | |
166 | */ | |
167 | sz = ltrim_whitespace((unsigned char *) buf); | |
168 | if (sz && *(buf + sz - 1) == '\n') | |
169 | *(buf + sz - 1) = '\0'; | |
170 | ||
171 | DBG(ASK, ul_debug("user's reply: >>>%s<<<", buf)); | |
172 | done: | |
173 | #ifdef HAVE_LIBREADLINE | |
174 | if (is_interactive) | |
175 | rl_callback_handler_remove(); | |
176 | #endif | |
177 | sigaction(SIGINT, &oldact, NULL); | |
178 | return ret; | |
179 | } | |
180 | ||
181 | static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask, | |
182 | char *buf, size_t bufsz) | |
183 | ||
184 | { | |
185 | const char *q = fdisk_ask_get_query(ask); | |
186 | int dft = fdisk_ask_menu_get_default(ask); | |
187 | ||
188 | if (q) { | |
189 | fputs(q, stdout); /* print header */ | |
190 | fputc('\n', stdout); | |
191 | } | |
192 | ||
193 | do { | |
194 | char prompt[128]; | |
195 | int key, c, rc; | |
196 | const char *name, *desc; | |
197 | size_t i = 0; | |
198 | ||
199 | /* print menu items */ | |
200 | while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0) | |
201 | fprintf(stdout, " %c %s (%s)\n", key, name, desc); | |
202 | ||
203 | /* ask for key */ | |
204 | snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft); | |
205 | rc = get_user_reply(prompt, buf, bufsz); | |
206 | if (rc) | |
207 | return rc; | |
208 | if (!*buf) { | |
209 | fdisk_info(cxt, _("Using default response %c."), dft); | |
210 | c = dft; | |
211 | } else | |
212 | c = tolower(buf[0]); | |
213 | ||
214 | /* check result */ | |
215 | i = 0; | |
216 | while (fdisk_ask_menu_get_item(ask, i++, &key, NULL, NULL) == 0) { | |
217 | if (c == key) { | |
218 | fdisk_ask_menu_set_result(ask, c); | |
219 | return 0; /* success */ | |
220 | } | |
221 | } | |
222 | fdisk_warnx(cxt, _("Value out of range.")); | |
223 | } while (1); | |
224 | ||
225 | return -EINVAL; | |
226 | } | |
227 | ||
228 | ||
229 | #define tochar(num) ((int) ('a' + num - 1)) | |
230 | static int ask_number(struct fdisk_context *cxt, | |
231 | struct fdisk_ask *ask, | |
232 | char *buf, size_t bufsz) | |
233 | { | |
234 | char prompt[128] = { '\0' }; | |
235 | const char *q = fdisk_ask_get_query(ask); | |
236 | const char *range = fdisk_ask_number_get_range(ask); | |
237 | ||
238 | uint64_t dflt = fdisk_ask_number_get_default(ask), | |
239 | low = fdisk_ask_number_get_low(ask), | |
240 | high = fdisk_ask_number_get_high(ask); | |
241 | int inchar = fdisk_ask_number_inchars(ask); | |
242 | ||
243 | assert(q); | |
244 | ||
245 | DBG(ASK, ul_debug("asking for number " | |
246 | "['%s', <%"PRIu64",%"PRIu64">, default=%"PRIu64", range: %s]", | |
247 | q, low, high, dflt, range)); | |
248 | ||
249 | if (range && dflt >= low && dflt <= high) { | |
250 | if (inchar) | |
251 | snprintf(prompt, sizeof(prompt), _("%s (%s, default %c): "), | |
252 | q, range, tochar(dflt)); | |
253 | else | |
254 | snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "), | |
255 | q, range, dflt); | |
256 | ||
257 | } else if (dflt >= low && dflt <= high) { | |
258 | if (inchar) | |
259 | snprintf(prompt, sizeof(prompt), _("%s (%c-%c, default %c): "), | |
260 | q, tochar(low), tochar(high), tochar(dflt)); | |
261 | else | |
262 | snprintf(prompt, sizeof(prompt), | |
263 | _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "), | |
264 | q, low, high, dflt); | |
265 | } else if (inchar) | |
266 | snprintf(prompt, sizeof(prompt), _("%s (%c-%c): "), | |
267 | q, tochar(low), tochar(high)); | |
268 | else | |
269 | snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "), | |
270 | q, low, high); | |
271 | ||
272 | do { | |
273 | int rc = get_user_reply(prompt, buf, bufsz); | |
274 | uint64_t num = 0; | |
275 | ||
276 | if (rc) | |
277 | return rc; | |
278 | if (!*buf && dflt >= low && dflt <= high) | |
279 | return fdisk_ask_number_set_result(ask, dflt); | |
280 | ||
281 | if (isdigit_string(buf)) { | |
282 | char *end; | |
283 | ||
284 | errno = 0; | |
285 | num = strtoumax(buf, &end, 10); | |
286 | if (errno || buf == end || (end && *end)) | |
287 | continue; | |
288 | } else if (inchar && isalpha(*buf)) { | |
289 | num = tolower(*buf) - 'a' + 1; | |
290 | } else | |
291 | rc = -EINVAL; | |
292 | ||
293 | if (rc == 0 && num >= low && num <= high) | |
294 | return fdisk_ask_number_set_result(ask, num); | |
295 | ||
296 | fdisk_warnx(cxt, _("Value out of range.")); | |
297 | } while (1); | |
298 | ||
299 | return -1; | |
300 | } | |
301 | ||
302 | static int ask_offset(struct fdisk_context *cxt, | |
303 | struct fdisk_ask *ask, | |
304 | char *buf, size_t bufsz) | |
305 | { | |
306 | char prompt[128] = { '\0' }; | |
307 | const char *q = fdisk_ask_get_query(ask); | |
308 | const char *range = fdisk_ask_number_get_range(ask); | |
309 | ||
310 | uint64_t dflt = fdisk_ask_number_get_default(ask), | |
311 | low = fdisk_ask_number_get_low(ask), | |
312 | high = fdisk_ask_number_get_high(ask), | |
313 | base = fdisk_ask_number_get_base(ask); | |
314 | ||
315 | assert(q); | |
316 | ||
317 | DBG(ASK, ul_debug("asking for offset ['%s', <%"PRIu64",%"PRIu64">, base=%"PRIu64", default=%"PRIu64", range: %s]", | |
318 | q, low, high, base, dflt, range)); | |
319 | ||
320 | if (range && dflt >= low && dflt <= high) | |
321 | snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "), | |
322 | q, range, dflt); | |
323 | else if (dflt >= low && dflt <= high) | |
324 | snprintf(prompt, sizeof(prompt), | |
325 | _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "), | |
326 | q, low, high, dflt); | |
327 | else | |
328 | snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "), | |
329 | q, low, high); | |
330 | ||
331 | do { | |
332 | uintmax_t num = 0; | |
333 | char sig = 0, *p; | |
334 | int pwr = 0; | |
335 | ||
336 | int rc = get_user_reply(prompt, buf, bufsz); | |
337 | if (rc) | |
338 | return rc; | |
339 | if (!*buf && dflt >= low && dflt <= high) | |
340 | return fdisk_ask_number_set_result(ask, dflt); | |
341 | ||
342 | p = buf; | |
343 | if (*p == '+' || *p == '-') { | |
344 | sig = *buf; | |
345 | p++; | |
346 | } | |
347 | ||
348 | rc = parse_size(p, &num, &pwr); | |
349 | if (rc) | |
350 | continue; | |
351 | DBG(ASK, ul_debug("parsed size: %ju", num)); | |
352 | if (sig && pwr) { | |
353 | /* +{size}{K,M,...} specified, the "num" is in bytes */ | |
354 | uint64_t unit = fdisk_ask_number_get_unit(ask); | |
355 | num += unit/2; /* round */ | |
356 | num /= unit; | |
357 | } | |
358 | if (sig == '+') | |
359 | num += base; | |
360 | else if (sig == '-' && fdisk_ask_number_is_wrap_negative(ask)) | |
361 | num = high - num; | |
362 | else if (sig == '-') | |
363 | num = base - num; | |
364 | ||
365 | DBG(ASK, ul_debug("final offset: %ju [sig: %c, power: %d, %s]", | |
366 | num, sig, pwr, | |
367 | sig ? "relative" : "absolute")); | |
368 | if (num >= low && num <= high) { | |
369 | if (sig && pwr) | |
370 | fdisk_ask_number_set_relative(ask, 1); | |
371 | return fdisk_ask_number_set_result(ask, (uint64_t)num); | |
372 | } | |
373 | fdisk_warnx(cxt, _("Value out of range.")); | |
374 | } while (1); | |
375 | ||
376 | return -1; | |
377 | } | |
378 | ||
379 | static unsigned int info_count; | |
380 | ||
381 | static void fputs_info(struct fdisk_ask *ask, FILE *out) | |
382 | { | |
383 | const char *msg; | |
384 | assert(ask); | |
385 | ||
386 | msg = fdisk_ask_print_get_mesg(ask); | |
387 | if (!msg) | |
388 | return; | |
389 | if (info_count == 1) | |
390 | fputc('\n', out); | |
391 | ||
392 | fputs(msg, out); | |
393 | fputc('\n', out); | |
394 | } | |
395 | ||
396 | int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask, | |
397 | void *data __attribute__((__unused__))) | |
398 | { | |
399 | int rc = 0; | |
400 | char buf[BUFSIZ] = { '\0' }; | |
401 | ||
402 | assert(cxt); | |
403 | assert(ask); | |
404 | ||
405 | if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_INFO) | |
406 | info_count = 0; | |
407 | ||
408 | switch(fdisk_ask_get_type(ask)) { | |
409 | case FDISK_ASKTYPE_MENU: | |
410 | return ask_menu(cxt, ask, buf, sizeof(buf)); | |
411 | case FDISK_ASKTYPE_NUMBER: | |
412 | return ask_number(cxt, ask, buf, sizeof(buf)); | |
413 | case FDISK_ASKTYPE_OFFSET: | |
414 | return ask_offset(cxt, ask, buf, sizeof(buf)); | |
415 | case FDISK_ASKTYPE_INFO: | |
416 | if (!fdisk_is_listonly(cxt)) | |
417 | info_count++; | |
418 | fputs_info(ask, stdout); | |
419 | break; | |
420 | case FDISK_ASKTYPE_WARNX: | |
421 | fflush(stdout); | |
422 | color_scheme_fenable("warn", UL_COLOR_RED, stderr); | |
423 | fputs(fdisk_ask_print_get_mesg(ask), stderr); | |
424 | color_fdisable(stderr); | |
425 | fputc('\n', stderr); | |
426 | break; | |
427 | case FDISK_ASKTYPE_WARN: | |
428 | fflush(stdout); | |
429 | color_scheme_fenable("warn", UL_COLOR_RED, stderr); | |
430 | fputs(fdisk_ask_print_get_mesg(ask), stderr); | |
431 | errno = fdisk_ask_print_get_errno(ask); | |
432 | fprintf(stderr, ": %m\n"); | |
433 | color_fdisable(stderr); | |
434 | break; | |
435 | case FDISK_ASKTYPE_YESNO: | |
436 | fputc('\n', stdout); | |
437 | do { | |
438 | int x; | |
439 | fputs(fdisk_ask_get_query(ask), stdout); | |
440 | rc = get_user_reply(_(" [Y]es/[N]o: "), buf, sizeof(buf)); | |
441 | if (rc) | |
442 | break; | |
443 | x = rpmatch(buf); | |
444 | if (x == RPMATCH_YES || x == RPMATCH_NO) { | |
445 | fdisk_ask_yesno_set_result(ask, x); | |
446 | break; | |
447 | } | |
448 | } while(1); | |
449 | DBG(ASK, ul_debug("yes-no ask: reply '%s' [rc=%d]", buf, rc)); | |
450 | break; | |
451 | case FDISK_ASKTYPE_STRING: | |
452 | { | |
453 | char prmt[BUFSIZ]; | |
454 | snprintf(prmt, sizeof(prmt), "%s: ", fdisk_ask_get_query(ask)); | |
455 | fputc('\n', stdout); | |
456 | rc = get_user_reply(prmt, buf, sizeof(buf)); | |
457 | if (rc == 0) | |
458 | fdisk_ask_string_set_result(ask, xstrdup(buf)); | |
459 | DBG(ASK, ul_debug("string ask: reply '%s' [rc=%d]", buf, rc)); | |
460 | break; | |
461 | } | |
462 | default: | |
463 | warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask)); | |
464 | return -EINVAL; | |
465 | } | |
466 | return rc; | |
467 | } | |
468 | ||
469 | static struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt, int *canceled) | |
470 | { | |
471 | const char *q; | |
472 | struct fdisk_label *lb; | |
473 | ||
474 | assert(cxt); | |
475 | lb = fdisk_get_label(cxt, NULL); | |
476 | ||
477 | if (!lb) | |
478 | return NULL; | |
479 | ||
480 | *canceled = 0; | |
481 | q = fdisk_label_has_code_parttypes(lb) ? | |
482 | _("Hex code (type L to list all codes): ") : | |
483 | _("Partition type (type L to list all types): "); | |
484 | do { | |
485 | char buf[256] = { '\0' }; | |
486 | int rc = get_user_reply(q, buf, sizeof(buf)); | |
487 | ||
488 | if (rc) { | |
489 | if (rc == -ECANCELED) | |
490 | *canceled = 1; | |
491 | break; | |
492 | } | |
493 | ||
494 | if (buf[1] == '\0' && toupper(*buf) == 'L') | |
495 | list_partition_types(cxt); | |
496 | else if (*buf) { | |
497 | struct fdisk_parttype *t = fdisk_label_parse_parttype(lb, buf); | |
498 | ||
499 | if (!t) | |
500 | fdisk_info(cxt, _("Failed to parse '%s' partition type."), buf); | |
501 | return t; | |
502 | } | |
503 | } while (1); | |
504 | ||
505 | return NULL; | |
506 | } | |
507 | ||
508 | ||
509 | void list_partition_types(struct fdisk_context *cxt) | |
510 | { | |
511 | size_t ntypes = 0; | |
512 | struct fdisk_label *lb; | |
513 | ||
514 | assert(cxt); | |
515 | lb = fdisk_get_label(cxt, NULL); | |
516 | if (!lb) | |
517 | return; | |
518 | ntypes = fdisk_label_get_nparttypes(lb); | |
519 | if (!ntypes) | |
520 | return; | |
521 | ||
522 | if (fdisk_label_has_code_parttypes(lb)) { | |
523 | /* | |
524 | * Prints in 4 columns in format <hex> <name> | |
525 | */ | |
526 | size_t last[4], done = 0, next = 0, size; | |
527 | int i; | |
528 | ||
529 | size = ntypes; | |
530 | ||
531 | for (i = 3; i >= 0; i--) | |
532 | last[3 - i] = done += (size + i - done) / (i + 1); | |
533 | i = done = 0; | |
534 | ||
535 | do { | |
536 | #define NAME_WIDTH 15 | |
537 | char name[NAME_WIDTH * MB_LEN_MAX]; | |
538 | size_t width = NAME_WIDTH; | |
539 | const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, next); | |
540 | size_t ret; | |
541 | ||
542 | if (fdisk_parttype_get_name(t)) { | |
543 | printf("%c%2x ", i ? ' ' : '\n', | |
544 | fdisk_parttype_get_code(t)); | |
545 | ret = mbsalign(_(fdisk_parttype_get_name(t)), | |
546 | name, sizeof(name), | |
547 | &width, MBS_ALIGN_LEFT, 0); | |
548 | ||
549 | if (ret == (size_t)-1 || ret >= sizeof(name)) | |
550 | printf("%-15.15s", | |
551 | _(fdisk_parttype_get_name(t))); | |
552 | else | |
553 | fputs(name, stdout); | |
554 | } | |
555 | ||
556 | next = last[i++] + done; | |
557 | if (i > 3 || next >= last[i]) { | |
558 | i = 0; | |
559 | next = ++done; | |
560 | } | |
561 | } while (done < last[0]); | |
562 | ||
563 | } else { | |
564 | /* | |
565 | * Prints 1 column in format <idx> <name> <typestr> | |
566 | */ | |
567 | size_t i; | |
568 | ||
569 | pager_open(); | |
570 | ||
571 | for (i = 0; i < ntypes; i++) { | |
572 | const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, i); | |
573 | printf("%3zu %-30s %s\n", i + 1, | |
574 | fdisk_parttype_get_name(t), | |
575 | fdisk_parttype_get_string(t)); | |
576 | } | |
577 | ||
578 | pager_close(); | |
579 | } | |
580 | putchar('\n'); | |
581 | } | |
582 | ||
583 | void toggle_dos_compatibility_flag(struct fdisk_context *cxt) | |
584 | { | |
585 | struct fdisk_label *lb = fdisk_get_label(cxt, "dos"); | |
586 | int flag; | |
587 | ||
588 | if (!lb) | |
589 | return; | |
590 | ||
591 | flag = !fdisk_dos_is_compatible(lb); | |
592 | fdisk_info(cxt, flag ? | |
593 | _("DOS Compatibility flag is set (DEPRECATED!)") : | |
594 | _("DOS Compatibility flag is not set")); | |
595 | ||
596 | fdisk_dos_enable_compatible(lb, flag); | |
597 | ||
598 | if (fdisk_is_label(cxt, DOS)) | |
599 | fdisk_reset_alignment(cxt); /* reset the current label */ | |
600 | } | |
601 | ||
602 | void change_partition_type(struct fdisk_context *cxt) | |
603 | { | |
604 | size_t i; | |
605 | struct fdisk_parttype *t = NULL; | |
606 | struct fdisk_partition *pa = NULL; | |
607 | const char *old = NULL; | |
608 | int canceled = 0; | |
609 | ||
610 | assert(cxt); | |
611 | ||
612 | if (fdisk_ask_partnum(cxt, &i, FALSE)) | |
613 | return; | |
614 | ||
615 | if (fdisk_get_partition(cxt, i, &pa)) { | |
616 | fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1); | |
617 | return; | |
618 | } | |
619 | ||
620 | t = (struct fdisk_parttype *) fdisk_partition_get_type(pa); | |
621 | old = t ? fdisk_parttype_get_name(t) : _("Unknown"); | |
622 | ||
623 | do { | |
624 | t = ask_partition_type(cxt, &canceled); | |
625 | if (canceled) | |
626 | break; | |
627 | } while (!t); | |
628 | ||
629 | if (canceled == 0 && t && fdisk_set_partition_type(cxt, i, t) == 0) | |
630 | fdisk_info(cxt, | |
631 | _("Changed type of partition '%s' to '%s'."), | |
632 | old, t ? fdisk_parttype_get_name(t) : _("Unknown")); | |
633 | else | |
634 | fdisk_info(cxt, | |
635 | _("Type of partition %zu is unchanged: %s."), | |
636 | i + 1, old); | |
637 | ||
638 | fdisk_unref_partition(pa); | |
639 | fdisk_unref_parttype(t); | |
640 | } | |
641 | ||
642 | int print_partition_info(struct fdisk_context *cxt) | |
643 | { | |
644 | struct fdisk_partition *pa = NULL; | |
645 | int rc = 0; | |
646 | size_t i, nfields; | |
647 | int *fields = NULL; | |
648 | struct fdisk_label *lb = fdisk_get_label(cxt, NULL); | |
649 | ||
650 | if ((rc = fdisk_ask_partnum(cxt, &i, FALSE))) | |
651 | return rc; | |
652 | ||
653 | if ((rc = fdisk_get_partition(cxt, i, &pa))) { | |
654 | fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1); | |
655 | return rc; | |
656 | } | |
657 | ||
658 | if ((rc = fdisk_label_get_fields_ids_all(lb, cxt, &fields, &nfields))) | |
659 | goto clean_data; | |
660 | ||
661 | for (i = 0; i < nfields; ++i) { | |
662 | int id = fields[i]; | |
663 | char *data = NULL; | |
664 | const struct fdisk_field *fd = fdisk_label_get_field(lb, id); | |
665 | ||
666 | if (!fd) | |
667 | continue; | |
668 | ||
669 | rc = fdisk_partition_to_string(pa, cxt, id, &data); | |
670 | if (rc < 0) | |
671 | goto clean_data; | |
672 | if (!data || !*data) | |
673 | continue; | |
674 | fdisk_info(cxt, "%15s: %s", fdisk_field_get_name(fd), data); | |
675 | free(data); | |
676 | } | |
677 | ||
678 | clean_data: | |
679 | fdisk_unref_partition(pa); | |
680 | free(fields); | |
681 | return rc; | |
682 | } | |
683 | ||
684 | static size_t skip_empty(const unsigned char *buf, size_t i, size_t sz) | |
685 | { | |
686 | size_t next; | |
687 | const unsigned char *p0 = buf + i; | |
688 | ||
689 | for (next = i + 16; next < sz; next += 16) { | |
690 | if (memcmp(p0, buf + next, 16) != 0) | |
691 | break; | |
692 | } | |
693 | ||
694 | return next == i + 16 ? i : next; | |
695 | } | |
696 | ||
697 | static void dump_buffer(off_t base, unsigned char *buf, size_t sz, int all) | |
698 | { | |
699 | size_t i, l, next = 0; | |
700 | ||
701 | if (!buf) | |
702 | return; | |
703 | for (i = 0, l = 0; i < sz; i++, l++) { | |
704 | if (l == 0) { | |
705 | if (all == 0 && !next) | |
706 | next = skip_empty(buf, i, sz); | |
707 | printf("%08jx ", (intmax_t)base + i); | |
708 | } | |
709 | printf(" %02x", buf[i]); | |
710 | if (l == 7) /* words separator */ | |
711 | fputs(" ", stdout); | |
712 | else if (l == 15) { | |
713 | fputc('\n', stdout); /* next line */ | |
714 | l = -1; | |
715 | if (next > i) { | |
716 | printf("*\n"); | |
717 | i = next - 1; | |
718 | } | |
719 | next = 0; | |
720 | } | |
721 | } | |
722 | if (l > 0) | |
723 | printf("\n"); | |
724 | } | |
725 | ||
726 | static void dump_blkdev(struct fdisk_context *cxt, const char *name, | |
727 | uint64_t offset, size_t size, int all) | |
728 | { | |
729 | int fd = fdisk_get_devfd(cxt); | |
730 | ||
731 | fdisk_info(cxt, _("\n%s: offset = %"PRIu64", size = %zu bytes."), | |
732 | name, offset, size); | |
733 | ||
734 | assert(fd >= 0); | |
735 | ||
736 | if (lseek(fd, (off_t) offset, SEEK_SET) == (off_t) -1) | |
737 | fdisk_warn(cxt, _("cannot seek")); | |
738 | else { | |
739 | unsigned char *buf = xmalloc(size); | |
740 | ||
741 | if (read_all(fd, (char *) buf, size) != (ssize_t) size) | |
742 | fdisk_warn(cxt, _("cannot read")); | |
743 | else | |
744 | dump_buffer(offset, buf, size, all); | |
745 | free(buf); | |
746 | } | |
747 | } | |
748 | ||
749 | void dump_firstsector(struct fdisk_context *cxt) | |
750 | { | |
751 | int all = !isatty(STDOUT_FILENO); | |
752 | ||
753 | assert(cxt); | |
754 | ||
755 | dump_blkdev(cxt, _("First sector"), 0, fdisk_get_sector_size(cxt), all); | |
756 | } | |
757 | ||
758 | void dump_disklabel(struct fdisk_context *cxt) | |
759 | { | |
760 | int all = !isatty(STDOUT_FILENO); | |
761 | int i = 0; | |
762 | const char *name = NULL; | |
763 | uint64_t offset = 0; | |
764 | size_t size = 0; | |
765 | ||
766 | assert(cxt); | |
767 | ||
768 | while (fdisk_locate_disklabel(cxt, i++, &name, &offset, &size) == 0 && size) | |
769 | dump_blkdev(cxt, name, offset, size, all); | |
770 | } | |
771 | ||
772 | static fdisk_sector_t get_dev_blocks(char *dev) | |
773 | { | |
774 | int fd, ret; | |
775 | fdisk_sector_t size; | |
776 | ||
777 | if ((fd = open(dev, O_RDONLY)) < 0) | |
778 | err(EXIT_FAILURE, _("cannot open %s"), dev); | |
779 | ret = blkdev_get_sectors(fd, (unsigned long long *) &size); | |
780 | close(fd); | |
781 | if (ret < 0) | |
782 | err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev); | |
783 | return size/2; | |
784 | } | |
785 | ||
786 | ||
787 | void follow_wipe_mode(struct fdisk_context *cxt) | |
788 | { | |
789 | int dowipe = wipemode == WIPEMODE_ALWAYS ? 1 : 0; | |
790 | ||
791 | if (isatty(STDIN_FILENO) && wipemode == WIPEMODE_AUTO) | |
792 | dowipe = 1; /* do it in interactive mode */ | |
793 | ||
794 | if (fdisk_is_ptcollision(cxt) && wipemode != WIPEMODE_NEVER) | |
795 | dowipe = 1; /* always remove old PT */ | |
796 | ||
797 | fdisk_enable_wipe(cxt, dowipe); | |
798 | if (dowipe) | |
799 | fdisk_warnx(cxt, _( | |
800 | "The old %s signature will be removed by a write command."), | |
801 | fdisk_get_collision(cxt)); | |
802 | else | |
803 | fdisk_warnx(cxt, _( | |
804 | "The old %s signature may remain on the device. " | |
805 | "It is recommended to wipe the device with wipefs(8) or " | |
806 | "fdisk --wipe, in order to avoid possible collisions."), | |
807 | fdisk_get_collision(cxt)); | |
808 | } | |
809 | ||
810 | static void __attribute__((__noreturn__)) usage(void) | |
811 | { | |
812 | FILE *out = stdout; | |
813 | ||
814 | fputs(USAGE_HEADER, out); | |
815 | ||
816 | fprintf(out, | |
817 | _(" %1$s [options] <disk> change partition table\n" | |
818 | " %1$s [options] -l [<disk>] list partition table(s)\n"), | |
819 | program_invocation_short_name); | |
820 | ||
821 | fputs(USAGE_SEPARATOR, out); | |
822 | fputs(_("Display or manipulate a disk partition table.\n"), out); | |
823 | ||
824 | fputs(USAGE_OPTIONS, out); | |
825 | fputs(_(" -b, --sector-size <size> physical and logical sector size\n"), out); | |
826 | fputs(_(" -B, --protect-boot don't erase bootbits when creating a new label\n"), out); | |
827 | fputs(_(" -c, --compatibility[=<mode>] mode is 'dos' or 'nondos' (default)\n"), out); | |
828 | fputs(_(" -L, --color[=<when>] colorize output (auto, always or never)\n"), out); | |
829 | fprintf(out, | |
830 | " %s\n", USAGE_COLORS_DEFAULT); | |
831 | fputs(_(" -l, --list display partitions and exit\n"), out); | |
832 | fputs(_(" -o, --output <list> output columns\n"), out); | |
833 | fputs(_(" -t, --type <type> recognize specified partition table type only\n"), out); | |
834 | fputs(_(" -u, --units[=<unit>] display units: 'cylinders' or 'sectors' (default)\n"), out); | |
835 | fputs(_(" -s, --getsz display device size in 512-byte sectors [DEPRECATED]\n"), out); | |
836 | fputs(_(" --bytes print SIZE in bytes rather than in human readable format\n"), out); | |
837 | fputs(_(" -w, --wipe <mode> wipe signatures (auto, always or never)\n"), out); | |
838 | fputs(_(" -W, --wipe-partitions <mode> wipe signatures from new partitions (auto, always or never)\n"), out); | |
839 | ||
840 | fputs(USAGE_SEPARATOR, out); | |
841 | fputs(_(" -C, --cylinders <number> specify the number of cylinders\n"), out); | |
842 | fputs(_(" -H, --heads <number> specify the number of heads\n"), out); | |
843 | fputs(_(" -S, --sectors <number> specify the number of sectors per track\n"), out); | |
844 | ||
845 | fputs(USAGE_SEPARATOR, out); | |
846 | printf(USAGE_HELP_OPTIONS(31)); | |
847 | ||
848 | list_available_columns(out); | |
849 | ||
850 | printf(USAGE_MAN_TAIL("fdisk(8)")); | |
851 | exit(EXIT_SUCCESS); | |
852 | } | |
853 | ||
854 | ||
855 | enum { | |
856 | ACT_FDISK = 0, /* default */ | |
857 | ACT_LIST, | |
858 | ACT_SHOWSIZE | |
859 | }; | |
860 | ||
861 | int main(int argc, char **argv) | |
862 | { | |
863 | int rc, i, c, act = ACT_FDISK; | |
864 | int colormode = UL_COLORMODE_UNDEF; | |
865 | struct fdisk_context *cxt; | |
866 | char *outarg = NULL; | |
867 | enum { | |
868 | OPT_BYTES = CHAR_MAX + 1 | |
869 | }; | |
870 | static const struct option longopts[] = { | |
871 | { "bytes", no_argument, NULL, OPT_BYTES }, | |
872 | { "color", optional_argument, NULL, 'L' }, | |
873 | { "compatibility", optional_argument, NULL, 'c' }, | |
874 | { "cylinders", required_argument, NULL, 'C' }, | |
875 | { "heads", required_argument, NULL, 'H' }, | |
876 | { "sectors", required_argument, NULL, 'S' }, | |
877 | { "getsz", no_argument, NULL, 's' }, | |
878 | { "help", no_argument, NULL, 'h' }, | |
879 | { "list", no_argument, NULL, 'l' }, | |
880 | { "sector-size", required_argument, NULL, 'b' }, | |
881 | { "type", required_argument, NULL, 't' }, | |
882 | { "units", optional_argument, NULL, 'u' }, | |
883 | { "version", no_argument, NULL, 'V' }, | |
884 | { "output", no_argument, NULL, 'o' }, | |
885 | { "protect-boot", no_argument, NULL, 'B' }, | |
886 | { "wipe", required_argument, NULL, 'w' }, | |
887 | { "wipe-partitions",required_argument, NULL, 'W' }, | |
888 | { NULL, 0, NULL, 0 } | |
889 | }; | |
890 | ||
891 | setlocale(LC_ALL, ""); | |
892 | bindtextdomain(PACKAGE, LOCALEDIR); | |
893 | textdomain(PACKAGE); | |
894 | close_stdout_atexit(); | |
895 | ||
896 | fdisk_init_debug(0); | |
897 | scols_init_debug(0); | |
898 | fdiskprog_init_debug(); | |
899 | ||
900 | cxt = fdisk_new_context(); | |
901 | if (!cxt) | |
902 | err(EXIT_FAILURE, _("failed to allocate libfdisk context")); | |
903 | ||
904 | fdisk_set_ask(cxt, ask_callback, NULL); | |
905 | ||
906 | while ((c = getopt_long(argc, argv, "b:Bc::C:hH:lL::o:sS:t:u::vVw:W:", | |
907 | longopts, NULL)) != -1) { | |
908 | switch (c) { | |
909 | case 'b': | |
910 | { | |
911 | size_t sz = strtou32_or_err(optarg, | |
912 | _("invalid sector size argument")); | |
913 | if (sz != 512 && sz != 1024 && sz != 2048 && sz != 4096) | |
914 | errx(EXIT_FAILURE, _("invalid sector size argument")); | |
915 | fdisk_save_user_sector_size(cxt, sz, sz); | |
916 | break; | |
917 | } | |
918 | case 'B': | |
919 | fdisk_enable_bootbits_protection(cxt, 1); | |
920 | break; | |
921 | case 'C': | |
922 | fdisk_save_user_geometry(cxt, | |
923 | strtou32_or_err(optarg, | |
924 | _("invalid cylinders argument")), | |
925 | 0, 0); | |
926 | break; | |
927 | case 'c': | |
928 | if (optarg) { | |
929 | /* this setting is independent on the current | |
930 | * actively used label | |
931 | */ | |
932 | char *p = *optarg == '=' ? optarg + 1 : optarg; | |
933 | struct fdisk_label *lb = fdisk_get_label(cxt, "dos"); | |
934 | ||
935 | if (!lb) | |
936 | err(EXIT_FAILURE, _("not found DOS label driver")); | |
937 | if (strcmp(p, "dos") == 0) | |
938 | fdisk_dos_enable_compatible(lb, TRUE); | |
939 | else if (strcmp(p, "nondos") == 0) | |
940 | fdisk_dos_enable_compatible(lb, FALSE); | |
941 | else | |
942 | errx(EXIT_FAILURE, _("unknown compatibility mode '%s'"), p); | |
943 | } | |
944 | /* use default if no optarg specified */ | |
945 | break; | |
946 | case 'H': | |
947 | fdisk_save_user_geometry(cxt, 0, | |
948 | strtou32_or_err(optarg, | |
949 | _("invalid heads argument")), | |
950 | 0); | |
951 | break; | |
952 | case 'S': | |
953 | fdisk_save_user_geometry(cxt, 0, 0, | |
954 | strtou32_or_err(optarg, | |
955 | _("invalid sectors argument"))); | |
956 | break; | |
957 | case 'l': | |
958 | act = ACT_LIST; | |
959 | break; | |
960 | case 'L': | |
961 | colormode = UL_COLORMODE_AUTO; | |
962 | if (optarg) | |
963 | colormode = colormode_or_err(optarg, | |
964 | _("unsupported color mode")); | |
965 | break; | |
966 | case 'o': | |
967 | outarg = optarg; | |
968 | break; | |
969 | case 's': | |
970 | act = ACT_SHOWSIZE; | |
971 | break; | |
972 | case 't': | |
973 | { | |
974 | struct fdisk_label *lb = NULL; | |
975 | ||
976 | while (fdisk_next_label(cxt, &lb) == 0) | |
977 | fdisk_label_set_disabled(lb, 1); | |
978 | ||
979 | lb = fdisk_get_label(cxt, optarg); | |
980 | if (!lb) | |
981 | errx(EXIT_FAILURE, _("unsupported disklabel: %s"), optarg); | |
982 | fdisk_label_set_disabled(lb, 0); | |
983 | break; | |
984 | } | |
985 | case 'u': | |
986 | if (optarg && *optarg == '=') | |
987 | optarg++; | |
988 | if (fdisk_set_unit(cxt, optarg) != 0) | |
989 | errx(EXIT_FAILURE, _("unsupported unit")); | |
990 | break; | |
991 | case 'V': /* preferred for util-linux */ | |
992 | case 'v': /* for backward compatibility only */ | |
993 | print_version(EXIT_SUCCESS); | |
994 | case 'w': | |
995 | wipemode = wipemode_from_string(optarg); | |
996 | if (wipemode < 0) | |
997 | errx(EXIT_FAILURE, _("unsupported wipe mode")); | |
998 | break; | |
999 | case 'W': | |
1000 | pwipemode = wipemode_from_string(optarg); | |
1001 | if (pwipemode < 0) | |
1002 | errx(EXIT_FAILURE, _("unsupported wipe mode")); | |
1003 | break; | |
1004 | case 'h': | |
1005 | usage(); | |
1006 | case OPT_BYTES: | |
1007 | fdisk_set_size_unit(cxt, FDISK_SIZEUNIT_BYTES); | |
1008 | break; | |
1009 | default: | |
1010 | errtryhelp(EXIT_FAILURE); | |
1011 | } | |
1012 | } | |
1013 | ||
1014 | if (argc-optind != 1 && fdisk_has_user_device_properties(cxt)) | |
1015 | warnx(_("The device properties (sector size and geometry) should" | |
1016 | " be used with one specified device only.")); | |
1017 | ||
1018 | colors_init(colormode, "fdisk"); | |
1019 | is_interactive = isatty(STDIN_FILENO); | |
1020 | ||
1021 | switch (act) { | |
1022 | case ACT_LIST: | |
1023 | fdisk_enable_listonly(cxt, 1); | |
1024 | init_fields(cxt, outarg, NULL); | |
1025 | ||
1026 | if (argc > optind) { | |
1027 | int k; | |
1028 | int ct = 0; | |
1029 | ||
1030 | for (rc = 0, k = optind; k < argc; k++) { | |
1031 | if (ct) | |
1032 | fputs("\n\n", stdout); | |
1033 | ||
1034 | rc += print_device_pt(cxt, argv[k], 1, 0); | |
1035 | ct++; | |
1036 | } | |
1037 | if (rc) | |
1038 | return EXIT_FAILURE; | |
1039 | } else | |
1040 | print_all_devices_pt(cxt, 0); | |
1041 | break; | |
1042 | ||
1043 | case ACT_SHOWSIZE: | |
1044 | /* deprecated */ | |
1045 | if (argc - optind <= 0) { | |
1046 | warnx(_("bad usage")); | |
1047 | errtryhelp(EXIT_FAILURE); | |
1048 | } | |
1049 | for (i = optind; i < argc; i++) { | |
1050 | uintmax_t blks = get_dev_blocks(argv[i]); | |
1051 | ||
1052 | if (argc - optind == 1) | |
1053 | printf("%ju\n", blks); | |
1054 | else | |
1055 | printf("%s: %ju\n", argv[i], blks); | |
1056 | } | |
1057 | break; | |
1058 | ||
1059 | case ACT_FDISK: | |
1060 | if (argc-optind != 1) { | |
1061 | warnx(_("bad usage")); | |
1062 | errtryhelp(EXIT_FAILURE); | |
1063 | } | |
1064 | ||
1065 | /* Here starts interactive mode, use fdisk_{warn,info,..} functions */ | |
1066 | color_scheme_enable("welcome", UL_COLOR_GREEN); | |
1067 | fdisk_info(cxt, _("Welcome to fdisk (%s)."), PACKAGE_STRING); | |
1068 | color_disable(); | |
1069 | fdisk_info(cxt, _("Changes will remain in memory only, until you decide to write them.\n" | |
1070 | "Be careful before using the write command.\n")); | |
1071 | ||
1072 | rc = fdisk_assign_device(cxt, argv[optind], 0); | |
1073 | if (rc == -EACCES) { | |
1074 | rc = fdisk_assign_device(cxt, argv[optind], 1); | |
1075 | if (rc == 0) | |
1076 | fdisk_warnx(cxt, _("Device is open in read-only mode.")); | |
1077 | } | |
1078 | if (rc) | |
1079 | err(EXIT_FAILURE, _("cannot open %s"), argv[optind]); | |
1080 | ||
1081 | fflush(stdout); | |
1082 | ||
1083 | if (fdisk_get_collision(cxt)) | |
1084 | follow_wipe_mode(cxt); | |
1085 | ||
1086 | if (!fdisk_has_label(cxt)) { | |
1087 | fdisk_info(cxt, _("Device does not contain a recognized partition table.")); | |
1088 | fdisk_create_disklabel(cxt, NULL); | |
1089 | ||
1090 | } else if (fdisk_is_label(cxt, GPT) && fdisk_gpt_is_hybrid(cxt)) | |
1091 | fdisk_warnx(cxt, _( | |
1092 | "A hybrid GPT was detected. You have to sync " | |
1093 | "the hybrid MBR manually (expert command 'M').")); | |
1094 | ||
1095 | init_fields(cxt, outarg, NULL); /* -o <columns> */ | |
1096 | ||
1097 | if (!fdisk_is_readonly(cxt)) { | |
1098 | fdisk_get_partitions(cxt, &original_layout); | |
1099 | device_is_used = fdisk_device_is_used(cxt); | |
1100 | } | |
1101 | ||
1102 | while (1) | |
1103 | process_fdisk_menu(&cxt); | |
1104 | } | |
1105 | ||
1106 | fdisk_unref_context(cxt); | |
1107 | return EXIT_SUCCESS; | |
1108 | } |