]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - disk-utils/cfdisk.c
fstrim shouldn't run inside a container
[thirdparty/util-linux.git] / disk-utils / cfdisk.c
index 4ad52842ccb5dfd9485e8fc84ce70b6dea586901..61f3258b3ceb0889294f105e49cb672200e2301c 100644 (file)
 # include <slang/slang.h>
 #endif
 
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 500 /* for inclusion of get_wch */
+#endif
+
 #ifdef HAVE_SLCURSES_H
 # include <slcurses.h>
 #elif defined(HAVE_SLANG_SLCURSES_H)
@@ -134,6 +138,7 @@ static struct cfdisk_menu *menu_push(struct cfdisk *cf, struct cfdisk_menuitem *
 static struct cfdisk_menu *menu_pop(struct cfdisk *cf);
 static void menu_refresh_size(struct cfdisk *cf);
 
+static int ui_end(void);
 static int ui_refresh(struct cfdisk *cf);
 static void ui_warnx(const char *fmt, ...);
 static void ui_warn(const char *fmt, ...);
@@ -142,16 +147,17 @@ static void ui_draw_menu(struct cfdisk *cf);
 static int ui_menu_move(struct cfdisk *cf, int key);
 static void ui_menu_resize(struct cfdisk *cf);
 
-static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
-                      uintmax_t low, uintmax_t up, int *expsize);
+static int ui_get_size(struct cfdisk *cf, const char *prompt, uint64_t *res,
+                      uint64_t low, uint64_t up, int *expsize);
 
 static int ui_enabled;
-static int ui_resize;
+static volatile sig_atomic_t sig_resize;
+static volatile sig_atomic_t sig_die;
 
 /* ncurses LINES and COLS may be actual variables or *macros*, but we need
  * something portable and writable */
-size_t ui_lines;
-size_t ui_cols;
+static size_t ui_lines;
+static size_t ui_cols;
 
 /* menu item */
 struct cfdisk_menuitem {
@@ -183,6 +189,7 @@ struct cfdisk_menu {
 static struct cfdisk_menuitem main_menuitems[] = {
        { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
        { 'd', N_("Delete"), N_("Delete the current partition") },
+       { 'r', N_("Resize"), N_("Reduce or enlarge the current partition") },
        { 'n', N_("New"), N_("Create new partition from free space") },
        { 'q', N_("Quit"), N_("Quit program without writing changes") },
        { 't', N_("Type"), N_("Change the partition type") },
@@ -212,6 +219,7 @@ struct cfdisk_line {
 struct cfdisk {
        struct fdisk_context    *cxt;   /* libfdisk context */
        struct fdisk_table      *table; /* partition table */
+       struct fdisk_table      *original_layout; /* original on-disk PT */
 
        struct cfdisk_menu      *menu;  /* the current menu */
 
@@ -238,6 +246,7 @@ struct cfdisk {
 #endif
        unsigned int    wrong_order :1,         /* PT not in right order */
                        zero_start :1,          /* ignore existing partition table */
+                       device_is_used : 1,     /* don't use re-read ioctl */
                        show_extra :1;          /* show extra partinfo */
 };
 
@@ -245,7 +254,7 @@ struct cfdisk {
 /*
  * let's use include/debug.h stuff for cfdisk too
  */
-UL_DEBUG_DEFINE_MASK(cfdisk);
+static UL_DEBUG_DEFINE_MASK(cfdisk);
 UL_DEBUG_DEFINE_MASKNAMES(cfdisk) = UL_DEBUG_EMPTY_MASKNAMES;
 
 #define CFDISK_DEBUG_INIT      (1 << 1)
@@ -259,7 +268,7 @@ UL_DEBUG_DEFINE_MASKNAMES(cfdisk) = UL_DEBUG_EMPTY_MASKNAMES;
 
 static void cfdisk_init_debug(void)
 {
-       __UL_INIT_DEBUG(cfdisk, CFDISK_DEBUG_, 0, CFDISK_DEBUG);
+       __UL_INIT_DEBUG_FROM_ENV(cfdisk, CFDISK_DEBUG_, 0, CFDISK_DEBUG);
 }
 
 /* Initialize output columns -- we follow libfdisk fields (usually specific
@@ -276,6 +285,13 @@ static int cols_init(struct cfdisk *cf)
        return fdisk_label_get_fields_ids(NULL, cf->cxt, &cf->fields, &cf->nfields);
 }
 
+static void die_on_signal(void)
+{
+       DBG(MISC, ul_debug("die on signal."));
+       ui_end();
+       exit(EXIT_FAILURE);
+}
+
 static void resize(void)
 {
        struct winsize ws;
@@ -293,7 +309,7 @@ static void resize(void)
 
        DBG(UI, ul_debug("ui: resize refresh ui_cols=%zu, ui_lines=%zu",
                                ui_cols, ui_lines));
-       ui_resize = 0;
+       sig_resize = 0;
 }
 
 /* Reads partition in tree-like order from scols
@@ -323,7 +339,7 @@ static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb)
 {
        struct fdisk_partition *pa;
        struct fdisk_label *lb;
-       struct fdisk_iter *itr = NULL;
+       struct fdisk_iter *itr;
        struct libscols_table *table = NULL;
        struct libscols_iter *s_itr = NULL;
        char *res = NULL;
@@ -420,9 +436,7 @@ static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb)
         * parno stored within struct fdisk_partition)  */
 
        /* remove all */
-       fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
-       while (fdisk_table_next_partition(tb, itr, &pa) == 0)
-               fdisk_table_remove_partition(tb, pa);
+       fdisk_reset_table(tb);
 
        s_itr = scols_new_iter(SCOLS_ITER_FORWARD);
        if (!s_itr)
@@ -572,10 +586,12 @@ static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf)
        refresh();
 
        /* wait for keys */
-       do {
+       while (!sig_die) {
                key = getch();
 
-               if (ui_resize)
+               if (sig_die)
+                       break;
+               if (sig_resize)
                        ui_menu_resize(cf);
                if (ui_menu_move(cf, key) == 0)
                        continue;
@@ -591,7 +607,10 @@ static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf)
                        free(cm);
                        return 0;
                }
-       } while (1);
+       }
+
+       if (sig_die)
+               die_on_signal();
 
        menu_pop(cf);
        free(cm);
@@ -781,17 +800,15 @@ static void ui_clean_hint(void)
        clrtoeol();
 }
 
-static void die_on_signal(int dummy __attribute__((__unused__)))
+
+static void sig_handler_die(int dummy __attribute__((__unused__)))
 {
-       DBG(MISC, ul_debug("die on signal."));
-       ui_end();
-       exit(EXIT_FAILURE);
+       sig_die = 1;
 }
 
-static void resize_on_signal(int dummy __attribute__((__unused__)))
+static void sig_handler_resize(int dummy __attribute__((__unused__)))
 {
-       DBG(MISC, ul_debug("resize on signal."));
-       ui_resize = 1;
+       sig_resize = 1;
 }
 
 static void menu_refresh_size(struct cfdisk *cf)
@@ -919,11 +936,11 @@ static int ui_init(struct cfdisk *cf __attribute__((__unused__)))
        /* setup SIGCHLD handler */
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
-       sa.sa_handler = die_on_signal;
+       sa.sa_handler = sig_handler_die;
        sigaction(SIGINT, &sa, NULL);
        sigaction(SIGTERM, &sa, NULL);
 
-       sa.sa_handler = resize_on_signal;
+       sa.sa_handler = sig_handler_resize;
        sigaction(SIGWINCH, &sa, NULL);
 
        ui_enabled = 1;
@@ -1471,7 +1488,7 @@ static int ui_menu_move(struct cfdisk *cf, int key)
        assert(cf);
        assert(cf->menu);
 
-       if (key == ERR)
+       if (key == (int) ERR)
                return 0;       /* ignore errors */
 
        m = cf->menu;
@@ -1530,14 +1547,14 @@ static int ui_menu_move(struct cfdisk *cf, int key)
                return 0;
        }
 
-       DBG(MENU, ul_debug(" no memu move key"));
+       DBG(MENU, ul_debug(" no menu move key"));
        return 1;
 }
 
 /* but don't call me from ui_run(), this is for pop-up menus only */
 static void ui_menu_resize(struct cfdisk *cf)
 {
-       DBG(MENU, ul_debug("memu resize/refresh"));
+       DBG(MENU, ul_debug("menu resize/refresh"));
        resize();
        ui_clean_menu(cf);
        menu_refresh_size(cf);
@@ -1680,7 +1697,8 @@ static int ui_refresh(struct cfdisk *cf)
        if (!ui_enabled)
                return -EINVAL;
 
-       strsz = size_to_human_string(SIZE_SUFFIX_SPACE
+       strsz = size_to_human_string(SIZE_DECIMAL_2DIGITS
+                               | SIZE_SUFFIX_SPACE
                                | SIZE_SUFFIX_3LETTER, bytes);
 
        lb = fdisk_get_label(cf->cxt, NULL);
@@ -1692,7 +1710,7 @@ static int ui_refresh(struct cfdisk *cf)
        attron(A_BOLD);
        ui_center(0, _("Disk: %s"), fdisk_get_devname(cf->cxt));
        attroff(A_BOLD);
-       ui_center(1, _("Size: %s, %ju bytes, %ju sectors"),
+       ui_center(1, _("Size: %s, %"PRIu64" bytes, %ju sectors"),
                        strsz, bytes, (uintmax_t) fdisk_get_nsectors(cf->cxt));
        if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id)
                ui_center(2, _("Label: %s, identifier: %s"),
@@ -1726,7 +1744,7 @@ static ssize_t ui_get_string(const char *prompt,
        clrtoeol();
 
        if (prompt) {
-               mvaddstr(ln, cl, (char *) prompt);
+               mvaddstr(ln, cl, prompt);
                cl += mbs_safe_width(prompt);
        }
 
@@ -1743,7 +1761,7 @@ static ssize_t ui_get_string(const char *prompt,
 
        curs_set(1);
 
-       while (1) {
+       while (!sig_die) {
                wint_t c;       /* we have fallback in widechar.h */
 
                move(ln, cl);
@@ -1756,9 +1774,11 @@ static ssize_t ui_get_string(const char *prompt,
     defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
                if (get_wch(&c) == ERR) {
 #else
-               if ((c = getch()) == ERR) {
+               if ((c = getch()) == (wint_t) ERR) {
 #endif
-                       if (ui_resize) {
+                       if (sig_die)
+                               break;
+                       if (sig_resize) {
                                resize();
                                continue;
                        }
@@ -1798,6 +1818,7 @@ static ssize_t ui_get_string(const char *prompt,
                        rc = mbs_edit_delete(edit);
                        break;
                case '\b':
+               case KEY_DELETE:
                case KEY_BACKSPACE:
                        rc = mbs_edit_backspace(edit);
                        break;
@@ -1809,6 +1830,9 @@ static ssize_t ui_get_string(const char *prompt,
                        beep();
        }
 
+       if (sig_die)
+               die_on_signal();
+
        rc = strlen(edit->buf);         /* success */
 done:
        move(ln, 0);
@@ -1820,16 +1844,19 @@ done:
        return rc;
 }
 
-/* @res is default value as well as result in bytes */
-static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
-                      uintmax_t low, uintmax_t up, int *expsize)
+static int ui_get_size(struct cfdisk *cf,      /* context */
+                      const char *prompt,      /* UI dialog string */
+                      uint64_t *res,           /* result in bytes */
+                      uint64_t low,            /* minimal size */
+                      uint64_t up,             /* maximal size */
+                      int *expsize)            /* explicitly specified size */
 {
        char buf[128];
-       uintmax_t user = 0;
+       uint64_t user = 0;
        ssize_t rc;
        char *dflt = size_to_human_string(0, *res);
 
-       DBG(UI, ul_debug("get_size (default=%ju)", *res));
+       DBG(UI, ul_debug("get_size (default=%"PRIu64")", *res));
 
        ui_clean_info();
 
@@ -1858,16 +1885,16 @@ static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
                                insec = 1;
                                buf[len - 1] = '\0';
                        }
-                       rc = parse_size(buf, &user, &pwr);      /* parse */
+                       rc = parse_size(buf, (uintmax_t *)&user, &pwr); /* parse */
                }
 
                if (rc == 0) {
-                       DBG(UI, ul_debug("get_size user=%ju, power=%d, sectors=%s",
+                       DBG(UI, ul_debug("get_size user=%"PRIu64", power=%d, in-sectors=%s",
                                                user, pwr, insec ? "yes" : "no"));
                        if (insec)
                                user *= fdisk_get_sector_size(cf->cxt);
                        if (user < low) {
-                               ui_warnx(_("Minimum size is %ju bytes."), low);
+                               ui_warnx(_("Minimum size is %"PRIu64" bytes."), low);
                                rc = -ERANGE;
                        }
                        if (user > up && pwr && user < up + (1ULL << pwr * 10))
@@ -1876,7 +1903,7 @@ static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
                                user = up;
 
                        if (user > up) {
-                               ui_warnx(_("Maximum size is %ju bytes."), up);
+                               ui_warnx(_("Maximum size is %"PRIu64" bytes."), up);
                                rc = -ERANGE;
                        }
                        if (rc == 0 && insec && expsize)
@@ -1890,7 +1917,7 @@ static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
                *res = user;
        free(dflt);
 
-       DBG(UI, ul_debug("get_size (result=%ju, rc=%zd)", *res, rc));
+       DBG(UI, ul_debug("get_size (result=%"PRIu64", rc=%zd)", *res, rc));
        return rc;
 }
 
@@ -1945,10 +1972,12 @@ static struct fdisk_parttype *ui_get_parttype(struct cfdisk *cf,
        ui_draw_menu(cf);
        refresh();
 
-       do {
+       while (!sig_die) {
                int key = getch();
 
-               if (ui_resize)
+               if (sig_die)
+                       break;
+               if (sig_resize)
                        ui_menu_resize(cf);
                if (ui_menu_move(cf, key) == 0)
                        continue;
@@ -1966,8 +1995,10 @@ static struct fdisk_parttype *ui_get_parttype(struct cfdisk *cf,
                case 'Q':
                        goto done;
                }
-       } while (1);
+       }
 
+       if (sig_die)
+               die_on_signal();
 done:
        menu_pop(cf);
        if (codetypes) {
@@ -2091,7 +2122,9 @@ static int ui_create_label(struct cfdisk *cf)
                ui_info(_("Device does not contain a recognized partition table."));
 
 
-       do {
+       while (!sig_die) {
+               int key;
+
                if (refresh_menu) {
                        ui_draw_menu(cf);
                        ui_hint(_("Select a type to create a new label or press 'L' to load script file."));
@@ -2099,9 +2132,11 @@ static int ui_create_label(struct cfdisk *cf)
                        refresh_menu = 0;
                }
 
-               int key = getch();
+               key = getch();
 
-               if (ui_resize)
+               if (sig_die)
+                       break;
+               if (sig_resize)
                        ui_menu_resize(cf);
                if (ui_menu_move(cf, key) == 0)
                        continue;
@@ -2125,8 +2160,10 @@ static int ui_create_label(struct cfdisk *cf)
                        refresh_menu = 1;
                        break;
                }
-       } while (1);
+       }
 
+       if (sig_die)
+               die_on_signal();
 done:
        menu_pop(cf);
        free(cm);
@@ -2177,6 +2214,9 @@ static int ui_help(void)
        ui_info(_("Press a key to continue."));
 
        getch();
+
+       if (sig_die)
+               die_on_signal();
        return 0;
 }
 
@@ -2193,6 +2233,7 @@ static int main_menu_ignore_keys(struct cfdisk *cf, char *ignore,
                ignore[i++] = 'd';      /* delete */
                ignore[i++] = 't';      /* set type */
                ignore[i++] = 'b';      /* set bootable */
+               ignore[i++] = 'r';      /* resize */
                cf->menu->prefkey = 'n';
        } else {
                cf->menu->prefkey = 'q';
@@ -2278,7 +2319,7 @@ static int main_menu_action(struct cfdisk *cf, int key)
                break;
        case 'n': /* New */
        {
-               uint64_t start, size, dflt_size, secs;
+               uint64_t start, size, dflt_size, secs, max_size;
                struct fdisk_partition *npa;    /* the new partition */
                int expsize = 0;                /* size specified explicitly in sectors */
 
@@ -2287,11 +2328,11 @@ static int main_menu_action(struct cfdisk *cf, int key)
 
                /* free space range */
                start = fdisk_partition_get_start(pa);
-               size = dflt_size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cf->cxt);
+               size = max_size = dflt_size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cf->cxt);
 
                if (ui_get_size(cf, _("Partition size: "), &size,
                                fdisk_get_sector_size(cf->cxt),
-                               size, &expsize) == -CFDISK_ERR_ESC)
+                               max_size, &expsize) == -CFDISK_ERR_ESC)
                        break;
 
                secs = size / fdisk_get_sector_size(cf->cxt);
@@ -2335,6 +2376,43 @@ static int main_menu_action(struct cfdisk *cf, int key)
                        info = _("The type of partition %zu is unchanged.");
                break;
        }
+       case 'r': /* resize */
+       {
+               struct fdisk_partition *npa, *next;
+               uint64_t size, max_size, secs;
+
+               if (fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
+                       return -EINVAL;
+
+               size = fdisk_partition_get_size(pa);
+
+               /* is the next freespace? */
+               next = fdisk_table_get_partition(cf->table, cf->lines_idx + 1);
+               if (next && fdisk_partition_is_freespace(next))
+                       size += fdisk_partition_get_size(next);
+
+               size *= fdisk_get_sector_size(cf->cxt);
+               max_size = size;
+
+               if (ui_get_size(cf, _("New size: "), &size,
+                               fdisk_get_sector_size(cf->cxt),
+                               max_size, NULL) == -CFDISK_ERR_ESC)
+                       break;
+               secs = size / fdisk_get_sector_size(cf->cxt);
+               npa = fdisk_new_partition();
+               if (!npa)
+                       return -ENOMEM;
+
+               fdisk_partition_set_size(npa, secs);
+
+               rc = fdisk_set_partition(cf->cxt, n, npa);
+               fdisk_unref_partition(npa);
+               if (rc == 0) {
+                       ref = 1;
+                       info = _("Partition %zu resized.");
+               }
+               break;
+       }
        case 's': /* Sort */
                if (cf->wrong_order) {
                        fdisk_reorder_partitions(cf->cxt);
@@ -2369,7 +2447,10 @@ static int main_menu_action(struct cfdisk *cf, int key)
                if (rc)
                        warn = _("Failed to write disklabel.");
                else {
-                       fdisk_reread_partition_table(cf->cxt);
+                       if (cf->device_is_used)
+                               fdisk_reread_changes(cf->cxt, cf->original_layout);
+                       else
+                               fdisk_reread_partition_table(cf->cxt);
                        info = _("The partition table has been altered.");
                }
                cf->nwrites++;
@@ -2466,11 +2547,14 @@ static int ui_run(struct cfdisk *cf)
        else if (cf->wrong_order)
                ui_info(_("Note that partition table entries are not in disk order now."));
 
-       do {
+       while (!sig_die) {
                int key = getch();
 
                rc = 0;
-               if (ui_resize)
+
+               if (sig_die)
+                       break;
+               if (sig_resize)
                        /* Note that ncurses getch() returns ERR when interrupted
                         * by signal, but SLang does not interrupt at all. */
                        ui_resize_refresh(cf);
@@ -2501,6 +2585,7 @@ static int ui_run(struct cfdisk *cf)
                                ui_table_goto(cf, (int) cf->lines_idx - cf->page_sz);
                                break;
                        }
+                       /* fallthrough */
                case KEY_HOME:
                        ui_table_goto(cf, 0);
                        break;
@@ -2509,6 +2594,7 @@ static int ui_run(struct cfdisk *cf)
                                ui_table_goto(cf, cf->lines_idx + cf->page_sz);
                                break;
                        }
+                       /* fallthrough */
                case KEY_END:
                        ui_table_goto(cf, (int) cf->nlines - 1);
                        break;
@@ -2530,7 +2616,7 @@ static int ui_run(struct cfdisk *cf)
 
                if (rc == 1)
                        break; /* quit */
-       } while (1);
+       }
 
        menu_pop(cf);
 
@@ -2538,8 +2624,9 @@ static int ui_run(struct cfdisk *cf)
        return 0;
 }
 
-static void __attribute__ ((__noreturn__)) usage(FILE *out)
+static void __attribute__((__noreturn__)) usage(void)
 {
+       FILE *out = stdout;
        fputs(USAGE_HEADER, out);
        fprintf(out,
              _(" %1$s [options] <disk>\n"), program_invocation_short_name);
@@ -2554,11 +2641,10 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out)
        fputs(_(" -z, --zero               start with zeroed partition table\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
-       fputs(USAGE_HELP, out);
-       fputs(USAGE_VERSION, out);
+       printf(USAGE_HELP_OPTIONS(26));
 
-       fprintf(out, USAGE_MAN_TAIL("cfdisk(8)"));
-       exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+       printf(USAGE_MAN_TAIL("cfdisk(8)"));
+       exit(EXIT_SUCCESS);
 }
 
 int main(int argc, char *argv[])
@@ -2579,12 +2665,12 @@ int main(int argc, char *argv[])
        setlocale(LC_ALL, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
-       atexit(close_stdout);
+       close_stdout_atexit();
 
        while((c = getopt_long(argc, argv, "L::hVz", longopts, NULL)) != -1) {
                switch(c) {
                case 'h':
-                       usage(stdout);
+                       usage();
                        break;
                case 'L':
                        colormode = UL_COLORMODE_AUTO;
@@ -2593,8 +2679,7 @@ int main(int argc, char *argv[])
                                                _("unsupported color mode"));
                        break;
                case 'V':
-                       printf(UTIL_LINUX_VERSION);
-                       return EXIT_SUCCESS;
+                       print_version(EXIT_SUCCESS);
                case 'z':
                        cf->zero_start = 1;
                        break;
@@ -2634,6 +2719,11 @@ int main(int argc, char *argv[])
        if (rc != 0)
                err(EXIT_FAILURE, _("cannot open %s"), diskpath);
 
+       if (!fdisk_is_readonly(cf->cxt)) {
+               cf->device_is_used = fdisk_device_is_used(cf->cxt);
+               fdisk_get_partitions(cf->cxt, &cf->original_layout);
+       }
+
        /* Don't use err(), warn() from this point */
        ui_init(cf);
        ui_run(cf);
@@ -2641,6 +2731,7 @@ int main(int argc, char *argv[])
 
        cfdisk_free_lines(cf);
        free(cf->linesbuf);
+       free(cf->fields);
 
        fdisk_unref_table(cf->table);
 #ifdef HAVE_LIBMOUNT