/* * Copyright (C) 1992 A. V. Le Blanc (LeBlanc@mcc.ac.uk) * Copyright (C) 2012 Davidlohr Bueso * * Copyright (C) 2007-2013 Karel Zak * * This program is free software. You can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation: either version 1 or * (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBREADLINE # define _FUNCTION_DEF # include #endif #include "c.h" #include "xalloc.h" #include "all-io.h" #include "nls.h" #include "rpmatch.h" #include "blkdev.h" #include "mbsalign.h" #include "pathnames.h" #include "canonicalize.h" #include "strutils.h" #include "closestream.h" #include "pager.h" #include "fdisk.h" #include "pt-sun.h" /* to toggle flags */ #ifdef HAVE_LINUX_COMPILER_H # include #endif #ifdef HAVE_LINUX_BLKPG_H # include #endif int pwipemode = WIPEMODE_AUTO; int device_is_used; int is_interactive; struct fdisk_table *original_layout; static int wipemode = WIPEMODE_AUTO; /* * fdisk debug stuff (see fdisk.h and include/debug.h) */ UL_DEBUG_DEFINE_MASK(fdisk); UL_DEBUG_DEFINE_MASKNAMES(fdisk) = UL_DEBUG_EMPTY_MASKNAMES; static void fdiskprog_init_debug(void) { __UL_INIT_DEBUG_FROM_ENV(fdisk, FDISKPROG_DEBUG_, 0, FDISK_DEBUG); } static void reply_sighandler(int sig __attribute__((unused))) { DBG(ASK, ul_debug("got signal")); } static int reply_running; #ifdef HAVE_LIBREADLINE static char *reply_line; static void reply_linehandler(char *line) { reply_line = line; reply_running = 0; rl_callback_handler_remove(); /* avoid duplicate prompt */ } #endif int get_user_reply(const char *prompt, char *buf, size_t bufsz) { struct sigaction oldact, act = { .sa_handler = reply_sighandler }; struct pollfd fds[] = { { .fd = fileno(stdin), .events = POLLIN } }; size_t sz; int ret = 0; DBG(ASK, ul_debug("asking for user reply %s", is_interactive ? "[interactive]" : "")); sigemptyset(&act.sa_mask); sigaction(SIGINT, &act, &oldact); #ifdef HAVE_LIBREADLINE if (is_interactive) rl_callback_handler_install(prompt, reply_linehandler); #endif errno = 0; reply_running = 1; do { int rc; *buf = '\0'; #ifdef HAVE_LIBREADLINE if (!is_interactive) #endif { fputs(prompt, stdout); fflush(stdout); } rc = poll(fds, 1, -1); if (rc == -1 && errno == EINTR) { /* interrupted by signal */ DBG(ASK, ul_debug("cancel by CTRL+C")); ret = -ECANCELED; goto done; } if (rc == -1 && errno != EAGAIN) { /* error */ ret = -errno; goto done; } #ifdef HAVE_LIBREADLINE if (is_interactive) { /* read input and copy to buf[] */ rl_callback_read_char(); if (!reply_running && reply_line) { sz = strlen(reply_line); if (sz == 0) buf[sz++] = '\n'; else memcpy(buf, reply_line, min(sz, bufsz)); buf[min(sz, bufsz - 1)] = '\0'; free(reply_line); reply_line = NULL; } } else #endif { if (!fgets(buf, bufsz, stdin)) *buf = '\0'; break; } } while (reply_running); if (!*buf) { DBG(ASK, ul_debug("cancel by CTRL+D")); ret = -ECANCELED; clearerr(stdin); goto done; } /* * cleanup the reply */ sz = ltrim_whitespace((unsigned char *) buf); if (sz && *(buf + sz - 1) == '\n') *(buf + sz - 1) = '\0'; done: #ifdef HAVE_LIBREADLINE if (is_interactive) rl_callback_handler_remove(); #endif sigaction(SIGINT, &oldact, NULL); DBG(ASK, ul_debug("user's reply: >>>%s<<< [rc=%d]", buf, ret)); return ret; } static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask, char *buf, size_t bufsz) { const char *q = fdisk_ask_get_query(ask); int dft = fdisk_ask_menu_get_default(ask); if (q) { fputs(q, stdout); /* print header */ fputc('\n', stdout); } do { char prompt[128]; int key, c, rc; const char *name, *desc; size_t i = 0; /* print menu items */ while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0) fprintf(stdout, " %c %s (%s)\n", key, name, desc); /* ask for key */ snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft); rc = get_user_reply(prompt, buf, bufsz); if (rc) return rc; if (!*buf) { fdisk_info(cxt, _("Using default response %c."), dft); c = dft; } else c = tolower(buf[0]); /* check result */ i = 0; while (fdisk_ask_menu_get_item(ask, i++, &key, NULL, NULL) == 0) { if (c == key) { fdisk_ask_menu_set_result(ask, c); return 0; /* success */ } } fdisk_warnx(cxt, _("Value out of range.")); } while (1); return -EINVAL; } #define tochar(num) ((int) ('a' + num - 1)) static int ask_number(struct fdisk_context *cxt, struct fdisk_ask *ask, char *buf, size_t bufsz) { char prompt[128] = { '\0' }; const char *q = fdisk_ask_get_query(ask); const char *range = fdisk_ask_number_get_range(ask); uint64_t dflt = fdisk_ask_number_get_default(ask), low = fdisk_ask_number_get_low(ask), high = fdisk_ask_number_get_high(ask); int inchar = fdisk_ask_number_inchars(ask); assert(q); DBG(ASK, ul_debug("asking for number " "['%s', <%"PRIu64",%"PRIu64">, default=%"PRIu64", range: %s]", q, low, high, dflt, range)); if (range && dflt >= low && dflt <= high) { if (inchar) snprintf(prompt, sizeof(prompt), _("%s (%s, default %c): "), q, range, tochar(dflt)); else snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "), q, range, dflt); } else if (dflt >= low && dflt <= high) { if (inchar) snprintf(prompt, sizeof(prompt), _("%s (%c-%c, default %c): "), q, tochar(low), tochar(high), tochar(dflt)); else snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "), q, low, high, dflt); } else if (inchar) snprintf(prompt, sizeof(prompt), _("%s (%c-%c): "), q, tochar(low), tochar(high)); else snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "), q, low, high); do { int rc = get_user_reply(prompt, buf, bufsz); uint64_t num = 0; if (rc) return rc; if (!*buf && dflt >= low && dflt <= high) return fdisk_ask_number_set_result(ask, dflt); if (isdigit_string(buf)) { char *end; errno = 0; num = strtoumax(buf, &end, 10); if (errno || buf == end || (end && *end)) continue; } else if (inchar && isalpha(*buf)) { num = tolower(*buf) - 'a' + 1; } else rc = -EINVAL; if (rc == 0 && num >= low && num <= high) return fdisk_ask_number_set_result(ask, num); fdisk_warnx(cxt, _("Value out of range.")); } while (1); return -1; } static int ask_offset(struct fdisk_context *cxt, struct fdisk_ask *ask, char *buf, size_t bufsz) { char prompt[128] = { '\0' }; const char *q = fdisk_ask_get_query(ask); const char *range = fdisk_ask_number_get_range(ask); uint64_t dflt = fdisk_ask_number_get_default(ask), low = fdisk_ask_number_get_low(ask), high = fdisk_ask_number_get_high(ask), base = fdisk_ask_number_get_base(ask); assert(q); DBG(ASK, ul_debug("asking for offset ['%s', <%"PRIu64",%"PRIu64">, base=%"PRIu64", default=%"PRIu64", range: %s]", q, low, high, base, dflt, range)); if (range && dflt >= low && dflt <= high) snprintf(prompt, sizeof(prompt), _("%s (%s, default %"PRIu64"): "), q, range, dflt); else if (dflt >= low && dflt <= high) snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64", default %"PRIu64"): "), q, low, high, dflt); else snprintf(prompt, sizeof(prompt), _("%s (%"PRIu64"-%"PRIu64"): "), q, low, high); do { uintmax_t num = 0; char sig = 0, *p; int pwr = 0; int rc = get_user_reply(prompt, buf, bufsz); if (rc) return rc; if (!*buf && dflt >= low && dflt <= high) return fdisk_ask_number_set_result(ask, dflt); p = buf; if (*p == '+' || *p == '-') { sig = *buf; p++; } rc = parse_size(p, &num, &pwr); if (rc) continue; DBG(ASK, ul_debug("parsed size: %ju", num)); if (sig && pwr) { /* +{size}{K,M,...} specified, the "num" is in bytes */ uint64_t unit = fdisk_ask_number_get_unit(ask); num += unit/2; /* round */ num /= unit; } if (sig == '+') num += base; else if (sig == '-' && fdisk_ask_number_is_wrap_negative(ask)) num = high - num; else if (sig == '-') num = base - num; DBG(ASK, ul_debug("final offset: %ju [sig: %c, power: %d, %s]", num, sig, pwr, sig ? "relative" : "absolute")); if (num >= low && num <= high) { if (sig && pwr) fdisk_ask_number_set_relative(ask, 1); return fdisk_ask_number_set_result(ask, (uint64_t)num); } fdisk_warnx(cxt, _("Value out of range.")); } while (1); return -1; } static unsigned int info_count; static void fputs_info(struct fdisk_ask *ask, FILE *out) { const char *msg; assert(ask); msg = fdisk_ask_print_get_mesg(ask); if (!msg) return; if (info_count == 1) fputc('\n', out); fputs(msg, out); fputc('\n', out); } int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask, void *data __attribute__((__unused__))) { int rc = 0; char buf[BUFSIZ] = { '\0' }; assert(cxt); assert(ask); if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_INFO) info_count = 0; switch(fdisk_ask_get_type(ask)) { case FDISK_ASKTYPE_MENU: return ask_menu(cxt, ask, buf, sizeof(buf)); case FDISK_ASKTYPE_NUMBER: return ask_number(cxt, ask, buf, sizeof(buf)); case FDISK_ASKTYPE_OFFSET: return ask_offset(cxt, ask, buf, sizeof(buf)); case FDISK_ASKTYPE_INFO: if (!fdisk_is_listonly(cxt)) info_count++; fputs_info(ask, stdout); break; case FDISK_ASKTYPE_WARNX: fflush(stdout); color_scheme_fenable("warn", UL_COLOR_RED, stderr); fputs(fdisk_ask_print_get_mesg(ask), stderr); color_fdisable(stderr); fputc('\n', stderr); break; case FDISK_ASKTYPE_WARN: fflush(stdout); color_scheme_fenable("warn", UL_COLOR_RED, stderr); fputs(fdisk_ask_print_get_mesg(ask), stderr); errno = fdisk_ask_print_get_errno(ask); fprintf(stderr, ": %m\n"); color_fdisable(stderr); break; case FDISK_ASKTYPE_YESNO: fputc('\n', stdout); do { int x; fputs(fdisk_ask_get_query(ask), stdout); rc = get_user_reply(_(" [Y]es/[N]o: "), buf, sizeof(buf)); if (rc) break; x = rpmatch(buf); if (x == RPMATCH_YES || x == RPMATCH_NO) { fdisk_ask_yesno_set_result(ask, x); break; } } while(1); DBG(ASK, ul_debug("yes-no ask: reply '%s' [rc=%d]", buf, rc)); break; case FDISK_ASKTYPE_STRING: { char prmt[BUFSIZ]; snprintf(prmt, sizeof(prmt), "%s: ", fdisk_ask_get_query(ask)); fputc('\n', stdout); rc = get_user_reply(prmt, buf, sizeof(buf)); if (rc == 0) fdisk_ask_string_set_result(ask, xstrdup(buf)); DBG(ASK, ul_debug("string ask: reply '%s' [rc=%d]", buf, rc)); break; } default: warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask)); return -EINVAL; } return rc; } static struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt, int *canceled) { const char *q; struct fdisk_label *lb; assert(cxt); lb = fdisk_get_label(cxt, NULL); if (!lb) return NULL; *canceled = 0; if (fdisk_label_has_parttypes_shortcuts(lb)) q = fdisk_label_has_code_parttypes(lb) ? _("Hex code or alias (type L to list all): ") : _("Partition type or alias (type L to list all): "); else q = fdisk_label_has_code_parttypes(lb) ? _("Hex code (type L to list all codes): ") : _("Partition type (type L to list all types): "); do { char buf[256] = { '\0' }; int rc = get_user_reply(q, buf, sizeof(buf)); if (rc) { if (rc == -ECANCELED) *canceled = 1; break; } if (buf[1] == '\0' && toupper(*buf) == 'L') list_partition_types(cxt); else if (*buf) { struct fdisk_parttype *t = fdisk_label_advparse_parttype(lb, buf, FDISK_PARTTYPE_PARSE_DATA | FDISK_PARTTYPE_PARSE_ALIAS | FDISK_PARTTYPE_PARSE_SEQNUM); if (!t) fdisk_info(cxt, _("Failed to parse '%s' partition type."), buf); return t; } } while (1); return NULL; } void list_partition_types(struct fdisk_context *cxt) { size_t ntypes = 0, next = 0; struct fdisk_label *lb; int pager = 0; assert(cxt); lb = fdisk_get_label(cxt, NULL); if (!lb) return; ntypes = fdisk_label_get_nparttypes(lb); if (!ntypes) return; if (fdisk_label_has_code_parttypes(lb)) { /* * Prints in 4 columns in format */ size_t last[4], done = 0, size; int i; size = ntypes; for (i = 3; i >= 0; i--) last[3 - i] = done += (size + i - done) / (i + 1); i = done = 0; do { #define NAME_WIDTH 15 char name[NAME_WIDTH * MB_LEN_MAX]; size_t width = NAME_WIDTH; const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, next); size_t ret; if (fdisk_parttype_get_name(t)) { printf("%s%02x ", i ? " " : "\n", fdisk_parttype_get_code(t)); ret = mbsalign(_(fdisk_parttype_get_name(t)), name, sizeof(name), &width, MBS_ALIGN_LEFT, 0); if (ret == (size_t)-1 || ret >= sizeof(name)) printf("%-15.15s", _(fdisk_parttype_get_name(t))); else fputs(name, stdout); } next = last[i++] + done; if (i > 3 || next >= last[i]) { i = 0; next = ++done; } } while (done < last[0]); putchar('\n'); } else { /* * Prints 1 column in format */ size_t i; pager_open(); pager = 1; for (i = 0; i < ntypes; i++) { const struct fdisk_parttype *t = fdisk_label_get_parttype(lb, i); printf("%3zu %-30s %s\n", i + 1, fdisk_parttype_get_name(t), fdisk_parttype_get_string(t)); } } /* * Aliases */ if (fdisk_label_has_parttypes_shortcuts(lb)) { const char *alias = NULL, *typestr = NULL; int rc = 0; fputs(_("\nAliases:\n"), stdout); for (next = 0; rc == 0 || rc == 2; next++) { /* rc: <0 error, 0 success, 1 end, 2 deprecated */ rc = fdisk_label_get_parttype_shortcut(lb, next, &typestr, NULL, &alias); if (rc == 0) printf(" %-14s - %s\n", alias, typestr); } } if (pager) pager_close(); } void toggle_dos_compatibility_flag(struct fdisk_context *cxt) { struct fdisk_label *lb = fdisk_get_label(cxt, "dos"); int flag; if (!lb) return; flag = !fdisk_dos_is_compatible(lb); fdisk_info(cxt, flag ? _("DOS Compatibility flag is set (DEPRECATED!)") : _("DOS Compatibility flag is not set")); fdisk_dos_enable_compatible(lb, flag); if (fdisk_is_label(cxt, DOS)) fdisk_reset_alignment(cxt); /* reset the current label */ } void change_partition_type(struct fdisk_context *cxt) { size_t i; struct fdisk_parttype *t = NULL; struct fdisk_partition *pa = NULL; const char *old = NULL; int canceled = 0; assert(cxt); if (fdisk_ask_partnum(cxt, &i, FALSE)) return; if (fdisk_get_partition(cxt, i, &pa)) { fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1); return; } t = (struct fdisk_parttype *) fdisk_partition_get_type(pa); old = t ? fdisk_parttype_get_name(t) : _("Unknown"); do { t = ask_partition_type(cxt, &canceled); if (canceled) break; } while (!t); if (canceled == 0 && t && fdisk_set_partition_type(cxt, i, t) == 0) fdisk_info(cxt, _("Changed type of partition '%s' to '%s'."), old, t ? fdisk_parttype_get_name(t) : _("Unknown")); else fdisk_info(cxt, _("Type of partition %zu is unchanged: %s."), i + 1, old); fdisk_unref_partition(pa); fdisk_unref_parttype(t); } int print_partition_info(struct fdisk_context *cxt) { struct fdisk_partition *pa = NULL; int rc = 0; size_t i, nfields; int *fields = NULL; struct fdisk_label *lb = fdisk_get_label(cxt, NULL); if ((rc = fdisk_ask_partnum(cxt, &i, FALSE))) return rc; if ((rc = fdisk_get_partition(cxt, i, &pa))) { fdisk_warnx(cxt, _("Partition %zu does not exist yet!"), i + 1); return rc; } if ((rc = fdisk_label_get_fields_ids_all(lb, cxt, &fields, &nfields))) goto clean_data; for (i = 0; i < nfields; ++i) { int id = fields[i]; char *data = NULL; const struct fdisk_field *fd = fdisk_label_get_field(lb, id); if (!fd) continue; rc = fdisk_partition_to_string(pa, cxt, id, &data); if (rc < 0) goto clean_data; if (!data || !*data) continue; fdisk_info(cxt, "%15s: %s", fdisk_field_get_name(fd), data); free(data); } clean_data: fdisk_unref_partition(pa); free(fields); return rc; } static size_t skip_empty(const unsigned char *buf, size_t i, size_t sz) { size_t next; const unsigned char *p0 = buf + i; for (next = i + 16; next < sz; next += 16) { if (memcmp(p0, buf + next, 16) != 0) break; } return next == i + 16 ? i : next; } static void dump_buffer(off_t base, unsigned char *buf, size_t sz, int all) { size_t i, l, next = 0; if (!buf) return; for (i = 0, l = 0; i < sz; i++, l++) { if (l == 0) { if (all == 0 && !next) next = skip_empty(buf, i, sz); printf("%08jx ", (intmax_t)base + i); } printf(" %02x", buf[i]); if (l == 7) /* words separator */ fputs(" ", stdout); else if (l == 15) { fputc('\n', stdout); /* next line */ l = -1; if (next > i) { printf("*\n"); i = next - 1; } next = 0; } } if (l > 0) printf("\n"); } static void dump_blkdev(struct fdisk_context *cxt, const char *name, uint64_t offset, size_t size, int all) { int fd = fdisk_get_devfd(cxt); fdisk_info(cxt, _("\n%s: offset = %"PRIu64", size = %zu bytes."), name, offset, size); assert(fd >= 0); if (lseek(fd, (off_t) offset, SEEK_SET) == (off_t) -1) fdisk_warn(cxt, _("cannot seek")); else { unsigned char *buf = xmalloc(size); if (read_all(fd, (char *) buf, size) != (ssize_t) size) fdisk_warn(cxt, _("cannot read")); else dump_buffer(offset, buf, size, all); free(buf); } } void dump_firstsector(struct fdisk_context *cxt) { int all = !isatty(STDOUT_FILENO); assert(cxt); dump_blkdev(cxt, _("First sector"), 0, fdisk_get_sector_size(cxt), all); } void dump_disklabel(struct fdisk_context *cxt) { int all = !isatty(STDOUT_FILENO); int i = 0; const char *name = NULL; uint64_t offset = 0; size_t size = 0; assert(cxt); while (fdisk_locate_disklabel(cxt, i++, &name, &offset, &size) == 0 && size) dump_blkdev(cxt, name, offset, size, all); } static fdisk_sector_t get_dev_blocks(char *dev) { int fd, ret; fdisk_sector_t size; if ((fd = open(dev, O_RDONLY)) < 0) err(EXIT_FAILURE, _("cannot open %s"), dev); ret = blkdev_get_sectors(fd, (unsigned long long *) &size); close(fd); if (ret < 0) err(EXIT_FAILURE, _("BLKGETSIZE ioctl failed on %s"), dev); return size/2; } void follow_wipe_mode(struct fdisk_context *cxt) { int dowipe = wipemode == WIPEMODE_ALWAYS ? 1 : 0; if (isatty(STDIN_FILENO) && wipemode == WIPEMODE_AUTO) dowipe = 1; /* do it in interactive mode */ if (fdisk_is_ptcollision(cxt) && wipemode != WIPEMODE_NEVER) dowipe = 1; /* always remove old PT */ fdisk_enable_wipe(cxt, dowipe); if (dowipe) fdisk_warnx(cxt, _( "The device contains '%s' signature and it will be removed by a write command. " "See fdisk(8) man page and --wipe option for more details."), fdisk_get_collision(cxt)); else fdisk_warnx(cxt, _( "The device contains '%s' signature and it may remain on the device. " "It is recommended to wipe the device with wipefs(8) or " "fdisk --wipe, in order to avoid possible collisions."), fdisk_get_collision(cxt)); } static void __attribute__((__noreturn__)) usage(void) { FILE *out = stdout; fputs(USAGE_HEADER, out); fprintf(out, _(" %1$s [options] change partition table\n" " %1$s [options] -l [...] list partition table(s)\n"), program_invocation_short_name); fputs(USAGE_SEPARATOR, out); fputs(_("Display or manipulate a disk partition table.\n"), out); fputs(USAGE_OPTIONS, out); fputs(_(" -b, --sector-size physical and logical sector size\n"), out); fputs(_(" -B, --protect-boot don't erase bootbits when creating a new label\n"), out); fputs(_(" -c, --compatibility[=] mode is 'dos' or 'nondos' (default)\n"), out); fprintf(out, _(" -L, --color[=] colorize output (%s, %s or %s)\n"), "auto", "always", "never"); fprintf(out, " %s\n", USAGE_COLORS_DEFAULT); fputs(_(" -l, --list display partitions and exit\n"), out); fputs(_(" -x, --list-details like --list but with more details\n"), out); fputs(_(" -n, --noauto-pt don't create default partition table on empty devices\n"), out); fputs(_(" -o, --output output columns\n"), out); fputs(_(" -t, --type recognize specified partition table type only\n"), out); fputs(_(" -u, --units[=] display units: 'cylinders' or 'sectors' (default)\n"), out); fputs(_(" -s, --getsz display device size in 512-byte sectors [DEPRECATED]\n"), out); fputs(_(" --bytes print SIZE in bytes rather than in human readable format\n"), out); fprintf(out, _(" --lock[=] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock"); fprintf(out, _(" -w, --wipe wipe signatures (%s, %s or %s)\n"), "auto", "always", "never"); fprintf(out, _(" -W, --wipe-partitions wipe signatures from new partitions (%s, %s or %s)\n"), "auto", "always", "never"); fputs(USAGE_SEPARATOR, out); fputs(_(" -C, --cylinders specify the number of cylinders\n"), out); fputs(_(" -H, --heads specify the number of heads\n"), out); fputs(_(" -S, --sectors specify the number of sectors per track\n"), out); fputs(USAGE_SEPARATOR, out); printf(USAGE_HELP_OPTIONS(31)); list_available_columns(out); printf(USAGE_MAN_TAIL("fdisk(8)")); exit(EXIT_SUCCESS); } enum { ACT_FDISK = 0, /* default */ ACT_LIST, ACT_LIST_DETAILS, ACT_SHOWSIZE }; int main(int argc, char **argv) { int rc, i, c, act = ACT_FDISK, noauto_pt = 0; int colormode = UL_COLORMODE_UNDEF; struct fdisk_context *cxt; char *outarg = NULL; const char *devname, *lockmode = NULL; enum { OPT_BYTES = CHAR_MAX + 1, OPT_LOCK }; static const struct option longopts[] = { { "bytes", no_argument, NULL, OPT_BYTES }, { "color", optional_argument, NULL, 'L' }, { "compatibility", optional_argument, NULL, 'c' }, { "cylinders", required_argument, NULL, 'C' }, { "heads", required_argument, NULL, 'H' }, { "sectors", required_argument, NULL, 'S' }, { "getsz", no_argument, NULL, 's' }, { "help", no_argument, NULL, 'h' }, { "list", no_argument, NULL, 'l' }, { "list-details", no_argument, NULL, 'x' }, { "lock", optional_argument, NULL, OPT_LOCK }, { "noauto-pt", no_argument, NULL, 'n' }, { "sector-size", required_argument, NULL, 'b' }, { "type", required_argument, NULL, 't' }, { "units", optional_argument, NULL, 'u' }, { "version", no_argument, NULL, 'V' }, { "output", no_argument, NULL, 'o' }, { "protect-boot", no_argument, NULL, 'B' }, { "wipe", required_argument, NULL, 'w' }, { "wipe-partitions",required_argument, NULL, 'W' }, { NULL, 0, NULL, 0 } }; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); close_stdout_atexit(); fdisk_init_debug(0); scols_init_debug(0); fdiskprog_init_debug(); cxt = fdisk_new_context(); if (!cxt) err(EXIT_FAILURE, _("failed to allocate libfdisk context")); fdisk_set_ask(cxt, ask_callback, NULL); while ((c = getopt_long(argc, argv, "b:Bc::C:hH:lL::no:sS:t:u::vVw:W:x", longopts, NULL)) != -1) { switch (c) { case 'b': { size_t sz = strtou32_or_err(optarg, _("invalid sector size argument")); if (sz != 512 && sz != 1024 && sz != 2048 && sz != 4096) errx(EXIT_FAILURE, _("invalid sector size argument")); fdisk_save_user_sector_size(cxt, sz, sz); break; } case 'B': fdisk_enable_bootbits_protection(cxt, 1); break; case 'C': fdisk_save_user_geometry(cxt, strtou32_or_err(optarg, _("invalid cylinders argument")), 0, 0); break; case 'c': if (optarg) { /* this setting is independent on the current * actively used label */ char *p = *optarg == '=' ? optarg + 1 : optarg; struct fdisk_label *lb = fdisk_get_label(cxt, "dos"); if (!lb) err(EXIT_FAILURE, _("not found DOS label driver")); if (strcmp(p, "dos") == 0) fdisk_dos_enable_compatible(lb, TRUE); else if (strcmp(p, "nondos") == 0) fdisk_dos_enable_compatible(lb, FALSE); else errx(EXIT_FAILURE, _("unknown compatibility mode '%s'"), p); } /* use default if no optarg specified */ break; case 'H': fdisk_save_user_geometry(cxt, 0, strtou32_or_err(optarg, _("invalid heads argument")), 0); break; case 'S': fdisk_save_user_geometry(cxt, 0, 0, strtou32_or_err(optarg, _("invalid sectors argument"))); break; case 'l': act = ACT_LIST; break; case 'x': act = ACT_LIST_DETAILS; break; case 'L': colormode = UL_COLORMODE_AUTO; if (optarg) colormode = colormode_or_err(optarg, _("unsupported color mode")); break; case 'n': noauto_pt = 1; break; case 'o': outarg = optarg; break; case 's': act = ACT_SHOWSIZE; break; case 't': { struct fdisk_label *lb = NULL; while (fdisk_next_label(cxt, &lb) == 0) fdisk_label_set_disabled(lb, 1); lb = fdisk_get_label(cxt, optarg); if (!lb) errx(EXIT_FAILURE, _("unsupported disklabel: %s"), optarg); fdisk_label_set_disabled(lb, 0); break; } case 'u': if (optarg && *optarg == '=') optarg++; if (fdisk_set_unit(cxt, optarg) != 0) errx(EXIT_FAILURE, _("unsupported unit")); break; case 'V': /* preferred for util-linux */ case 'v': /* for backward compatibility only */ print_version(EXIT_SUCCESS); case 'w': wipemode = wipemode_from_string(optarg); if (wipemode < 0) errx(EXIT_FAILURE, _("unsupported wipe mode")); break; case 'W': pwipemode = wipemode_from_string(optarg); if (pwipemode < 0) errx(EXIT_FAILURE, _("unsupported wipe mode")); break; case 'h': usage(); case OPT_BYTES: fdisk_set_size_unit(cxt, FDISK_SIZEUNIT_BYTES); break; case OPT_LOCK: lockmode = "1"; if (optarg) { if (*optarg == '=') optarg++; lockmode = optarg; } break; default: errtryhelp(EXIT_FAILURE); } } if (argc-optind != 1 && fdisk_has_user_device_properties(cxt)) warnx(_("The device properties (sector size and geometry) should" " be used with one specified device only.")); colors_init(colormode, "fdisk"); is_interactive = isatty(STDIN_FILENO); switch (act) { case ACT_LIST: case ACT_LIST_DETAILS: fdisk_enable_listonly(cxt, 1); if (act == ACT_LIST_DETAILS) fdisk_enable_details(cxt, 1); init_fields(cxt, outarg, NULL); if (argc > optind) { int k; for (rc = 0, k = optind; k < argc; k++) rc += print_device_pt(cxt, argv[k], 1, 0, k != optind); if (rc) return EXIT_FAILURE; } else print_all_devices_pt(cxt, 0); break; case ACT_SHOWSIZE: /* deprecated */ if (argc - optind <= 0) { warnx(_("bad usage")); errtryhelp(EXIT_FAILURE); } for (i = optind; i < argc; i++) { uintmax_t blks = get_dev_blocks(argv[i]); if (argc - optind == 1) printf("%ju\n", blks); else printf("%s: %ju\n", argv[i], blks); } break; case ACT_FDISK: if (argc-optind != 1) { warnx(_("bad usage")); errtryhelp(EXIT_FAILURE); } /* Here starts interactive mode, use fdisk_{warn,info,..} functions */ color_scheme_enable("welcome", UL_COLOR_GREEN); fdisk_info(cxt, _("Welcome to fdisk (%s)."), PACKAGE_STRING); color_disable(); fdisk_info(cxt, _("Changes will remain in memory only, until you decide to write them.\n" "Be careful before using the write command.\n")); devname = argv[optind]; rc = fdisk_assign_device(cxt, devname, 0); if (rc == -EACCES) { rc = fdisk_assign_device(cxt, devname, 1); if (rc == 0) fdisk_warnx(cxt, _("Device is open in read-only mode.")); } if (rc) err(EXIT_FAILURE, _("cannot open %s"), devname); fflush(stdout); if (!fdisk_is_readonly(cxt) && blkdev_lock(fdisk_get_devfd(cxt), devname, lockmode) != 0) { fdisk_deassign_device(cxt, 1); fdisk_unref_context(cxt); return EXIT_FAILURE; } if (fdisk_get_collision(cxt)) follow_wipe_mode(cxt); if (!fdisk_has_label(cxt)) { fdisk_info(cxt, _("Device does not contain a recognized partition table.")); if (!noauto_pt) fdisk_create_disklabel(cxt, NULL); } else if (fdisk_is_label(cxt, GPT) && fdisk_gpt_is_hybrid(cxt)) fdisk_warnx(cxt, _( "A hybrid GPT was detected. You have to sync " "the hybrid MBR manually (expert command 'M').")); init_fields(cxt, outarg, NULL); /* -o */ if (!fdisk_is_readonly(cxt)) { fdisk_get_partitions(cxt, &original_layout); device_is_used = fdisk_device_is_used(cxt); } while (1) process_fdisk_menu(&cxt); } fdisk_unref_context(cxt); return EXIT_SUCCESS; }