]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - disk-utils/cfdisk.c
include/pidfd-utils: remove hardcoded syscall fallback
[thirdparty/util-linux.git] / disk-utils / cfdisk.c
index f14ae82a850b948af6bd7a61935358b747847ed3..7b61929614cfa61e5c9ce51cc78358217a6e7060 100644 (file)
@@ -1,16 +1,18 @@
 /*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * 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 2 of the License, or
+ * (at your option) any later version.
+ *
  * cfdisk.c - Display or manipulate a disk partition table.
  *
- *     Copyright (C) 2014-2015 Karel Zak <kzak@redhat.com>
+ *     Copyright (C) 2014-2023 Karel Zak <kzak@redhat.com>
  *     Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu)
  *
  *     The original cfdisk was inspired by the fdisk program
  *           by A. V. Le Blanc (leblanc@mcc.ac.uk.
- *
- * cfdisk 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 2 of the License, or
- * (at your option) any later version.
  */
 #include <stdlib.h>
 #include <stdio.h>
@@ -21,6 +23,7 @@
 #include <assert.h>
 #include <libsmartcols.h>
 #include <sys/ioctl.h>
+#include <rpmatch.h>
 #include <libfdisk.h>
 
 #ifdef HAVE_LIBMOUNT
 # 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)
@@ -60,6 +67,7 @@
 #include "colors.h"
 #include "debug.h"
 #include "list.h"
+#include "blkdev.h"
 
 static const char *default_disks[] = {
 #ifdef __GNU__
@@ -119,12 +127,14 @@ enum {
        CFDISK_CL_FREESPACE,
        CFDISK_CL_INFO
 };
+#ifdef HAVE_USE_DEFAULT_COLORS
 static const int color_pairs[][2] = {
        /* color            foreground, background */
        [CFDISK_CL_WARNING]   = { COLOR_RED, -1 },
        [CFDISK_CL_FREESPACE] = { COLOR_GREEN, -1 },
        [CFDISK_CL_INFO]      = { COLOR_BLUE, -1 }
 };
+#endif
 
 struct cfdisk;
 
@@ -134,19 +144,26 @@ 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, ...);
-static void ui_info(const char *fmt, ...);
+
+static void ui_warnx(const char *fmt, ...)
+                       __attribute__((__format__ (__printf__, 1, 2)));
+static void ui_warn(const char *fmt, ...)
+                       __attribute__((__format__ (__printf__, 1, 2)));
+static void ui_info(const char *fmt, ...)
+                       __attribute__((__format__ (__printf__, 1, 2)));
+
 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 */
@@ -183,6 +200,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") },
@@ -193,14 +211,6 @@ static struct cfdisk_menuitem main_menuitems[] = {
        { 0, NULL, NULL }
 };
 
-/* extra partinfo in name:value pairs */
-struct cfdisk_extra {
-       char *name;
-       char *data;
-
-       struct list_head exs;
-};
-
 /* line and extra partinfo list_head */
 struct cfdisk_line {
        char                    *data;          /* line data */
@@ -261,7 +271,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
@@ -278,6 +288,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;
@@ -295,7 +312,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
@@ -422,9 +439,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)
@@ -574,10 +589,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;
@@ -593,7 +610,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);
@@ -612,13 +632,13 @@ static int ask_callback(struct fdisk_context *cxt __attribute__((__unused__)),
 
        switch(fdisk_ask_get_type(ask)) {
        case FDISK_ASKTYPE_INFO:
-               ui_info(fdisk_ask_print_get_mesg(ask));
+               ui_info("%s", fdisk_ask_print_get_mesg(ask));
                break;
        case FDISK_ASKTYPE_WARNX:
-               ui_warnx(fdisk_ask_print_get_mesg(ask));
+               ui_warnx("%s", fdisk_ask_print_get_mesg(ask));
                break;
        case FDISK_ASKTYPE_WARN:
-               ui_warn(fdisk_ask_print_get_mesg(ask));
+               ui_warn("%s", fdisk_ask_print_get_mesg(ask));
                break;
        case FDISK_ASKTYPE_MENU:
                ask_menu(ask, (struct cfdisk *) data);
@@ -650,7 +670,8 @@ static int ui_end(void)
        return 0;
 }
 
-static void ui_vprint_center(size_t line, int attrs, const char *fmt, va_list ap)
+static void __attribute__((__format__ (__printf__, 3, 0)))
+       ui_vprint_center(size_t line, int attrs, const char *fmt, va_list ap)
 {
        size_t width;
        char *buf = NULL;
@@ -680,7 +701,8 @@ static void ui_vprint_center(size_t line, int attrs, const char *fmt, va_list ap
        free(buf);
 }
 
-static void ui_center(size_t line, const char *fmt, ...)
+static void __attribute__((__format__ (__printf__, 2, 3)))
+       ui_center(size_t line, const char *fmt, ...)
 {
        va_list ap;
        va_start(ap, fmt);
@@ -688,7 +710,8 @@ static void ui_center(size_t line, const char *fmt, ...)
        va_end(ap);
 }
 
-static void ui_warnx(const char *fmt, ...)
+static void __attribute__((__format__ (__printf__, 1, 2)))
+       ui_warnx(const char *fmt, ...)
 {
        va_list ap;
        va_start(ap, fmt);
@@ -703,7 +726,8 @@ static void ui_warnx(const char *fmt, ...)
        va_end(ap);
 }
 
-static void ui_warn(const char *fmt, ...)
+static void __attribute__((__format__ (__printf__, 1, 2)))
+       ui_warn(const char *fmt, ...)
 {
        char *fmt_m;
        va_list ap;
@@ -729,7 +753,25 @@ static void ui_clean_warn(void)
        clrtoeol();
 }
 
-static int __attribute__((__noreturn__)) ui_errx(int rc, const char *fmt, ...)
+static int __attribute__((__noreturn__))
+          __attribute__((__format__ (__printf__, 2, 3)))
+       ui_err(int rc, const char *fmt, ...)
+{
+       va_list ap;
+       ui_end();
+
+       va_start(ap, fmt);
+       fprintf(stderr, "%s: ", program_invocation_short_name);
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, ": %s\n", strerror(errno));
+       va_end(ap);
+
+       exit(rc);
+}
+
+static int __attribute__((__noreturn__))
+          __attribute__((__format__ (__printf__, 2, 3)))
+       ui_errx(int rc, const char *fmt, ...)
                {
        va_list ap;
        ui_end();
@@ -743,7 +785,8 @@ static int __attribute__((__noreturn__)) ui_errx(int rc, const char *fmt, ...)
        exit(rc);
 }
 
-static void ui_info(const char *fmt, ...)
+static void __attribute__((__format__ (__printf__, 1, 2)))
+       ui_info(const char *fmt, ...)
 {
        va_list ap;
        va_start(ap, fmt);
@@ -764,7 +807,8 @@ static void ui_clean_info(void)
        clrtoeol();
 }
 
-static void ui_hint(const char *fmt, ...)
+static void __attribute__((__format__ (__printf__, 1, 2)))
+       ui_hint(const char *fmt, ...)
 {
        va_list ap;
        va_start(ap, fmt);
@@ -783,17 +827,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)
@@ -921,11 +963,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;
@@ -971,7 +1013,9 @@ static size_t menuitem_get_line(struct cfdisk *cf, size_t idx)
                if (!m->page_sz)                                /* small menu */
                        return (ui_lines - (cf->menu->nitems + 1)) / 2 + idx;
                return (idx % m->page_sz) + 1;
-       } else {
+       }
+
+       {
                size_t len = MENU_H_ITEMWIDTH(m) + MENU_H_BETWEEN; /** item width */
                size_t items = ui_cols / len;                   /* items per line */
 
@@ -988,7 +1032,9 @@ static int menuitem_get_column(struct cfdisk *cf, size_t idx)
                if ((size_t) ui_cols <= nc)
                        return 0;
                return (ui_cols - nc) / 2;
-       } else {
+       }
+
+       {
                size_t len = MENU_H_ITEMWIDTH(cf->menu) + MENU_H_BETWEEN; /* item width */
                size_t items = ui_cols / len;                           /* items per line */
                size_t extra = items < cf->menu->nitems ?               /* extra space on line */
@@ -1096,7 +1142,7 @@ static void ui_draw_menuitem(struct cfdisk *cf,
        if (cf->menu->idx == idx) {
                standend();
                if (d->desc)
-                       ui_hint(_(d->desc));
+                       ui_hint("%s", _(d->desc));
        }
 }
 
@@ -1251,7 +1297,10 @@ static char *get_mountpoint(struct cfdisk *cf, const char *tagname, const char *
                        cf->fstab = mnt_new_table();
                        if (cf->fstab) {
                                mnt_table_set_cache(cf->fstab, cf->mntcache);
-                               mnt_table_parse_fstab(cf->fstab, NULL);
+                               if (mnt_table_parse_fstab(cf->fstab, NULL) != 0) {
+                                       mnt_unref_table(cf->fstab);
+                                       cf->fstab = NULL;
+                               }
                        }
                }
                if (cf->fstab)
@@ -1270,6 +1319,34 @@ static char *get_mountpoint(struct cfdisk *cf, const char *tagname, const char *
 }
 #endif /* HAVE_LIBMOUNT */
 
+static inline int iszero(const char *str)
+{
+       const char *p;
+
+       for (p = str; p && *p == '0'; p++);
+
+       return !p || *p == '\0';
+}
+
+static int has_uuid(struct fdisk_table *tb, const char *uuid)
+{
+       struct fdisk_partition *pa;
+       struct fdisk_iter *itr;
+       int rc = 0;
+
+       if (!tb || !uuid || fdisk_table_is_empty(tb))
+               return 0;
+
+       itr = fdisk_new_iter(FDISK_ITER_FORWARD);
+       while (rc == 0 && fdisk_table_next_partition(tb, itr, &pa) == 0) {
+               const char *x = fdisk_partition_get_uuid(pa);
+               if (x)
+                       rc = strcmp(x, uuid) == 0;
+       }
+       fdisk_free_iter(itr);
+       return rc;
+}
+
 static void extra_prepare_data(struct cfdisk *cf)
 {
        struct fdisk_partition *pa = get_current_partition(cf);
@@ -1289,7 +1366,14 @@ static void extra_prepare_data(struct cfdisk *cf)
 
        if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_UUID, &data) && data) {
                extra_insert_pair(l, _("Partition UUID:"), data);
-               if (!mountpoint)
+
+               /* Search for mountpoint by PARTUUID= means that we need to
+                * check fstab and convert PARTUUID to the device name. This is
+                * unnecessary and overkill for newly created partitions. Let's
+                * check if the UUID already exist in the old layout, otherwise
+                * ignore it.
+                */
+               if (!mountpoint && has_uuid(cf->original_layout, data))
                        mountpoint = get_mountpoint(cf, "PARTUUID", data);
                free(data);
        }
@@ -1313,19 +1397,19 @@ static void extra_prepare_data(struct cfdisk *cf)
 
        /* for numeric data, only show non-zero rows */
        if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_BSIZE, &data) && data) {
-               if (atoi(data))
+               if (!iszero(data))
                        extra_insert_pair(l, "BSIZE:", data);
                free(data);
        }
 
        if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_CPG, &data) && data) {
-               if (atoi(data))
+               if (!iszero(data))
                        extra_insert_pair(l, "CPG:", data);
                free(data);
        }
 
        if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSIZE, &data) && data) {
-               if (atoi(data))
+               if (!iszero(data))
                        extra_insert_pair(l, "FSIZE:", data);
                free(data);
        }
@@ -1682,7 +1766,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);
@@ -1694,7 +1779,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"),
@@ -1702,6 +1787,7 @@ static int ui_refresh(struct cfdisk *cf)
        else
                ui_center(2, _("Label: %s"), fdisk_label_get_name(lb));
        free(strsz);
+       free(id);
 
        ui_draw_table(cf);
        ui_draw_menu(cf);
@@ -1728,7 +1814,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);
        }
 
@@ -1739,13 +1825,13 @@ static ssize_t ui_get_string(const char *prompt,
        mbs_edit_goto(edit, MBS_EDIT_END);
 
        if (hint)
-               ui_hint(hint);
+               ui_hint("%s", hint);
        else
                ui_clean_hint();
 
        curs_set(1);
 
-       while (1) {
+       while (!sig_die) {
                wint_t c;       /* we have fallback in widechar.h */
 
                move(ln, cl);
@@ -1760,7 +1846,9 @@ static ssize_t ui_get_string(const char *prompt,
 #else
                if ((c = getch()) == (wint_t) ERR) {
 #endif
-                       if (ui_resize) {
+                       if (sig_die)
+                               break;
+                       if (sig_resize) {
                                resize();
                                continue;
                        }
@@ -1800,6 +1888,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;
@@ -1811,6 +1900,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);
@@ -1822,16 +1914,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();
 
@@ -1849,7 +1944,7 @@ static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
                if (rc == 0) {
                        ui_warnx(_("Please, specify size."));
                        continue;                       /* nothing specified */
-               } else if (rc == -CFDISK_ERR_ESC)
+               } if (rc == -CFDISK_ERR_ESC)
                        break;                          /* cancel dialog */
 
                if (strcmp(buf, dflt) == 0)
@@ -1860,16 +1955,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))
@@ -1878,7 +1973,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)
@@ -1892,7 +1987,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;
 }
 
@@ -1947,10 +2042,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;
@@ -1968,8 +2065,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) {
@@ -1978,7 +2077,7 @@ done:
        }
        free(cm);
        DBG(UI, ul_debug("get parrtype done [type=%s] ", t ?
-                               fdisk_parttype_get_name(t) : NULL));
+                               fdisk_parttype_get_name(t) : ""));
        return t;
 }
 
@@ -2093,19 +2192,21 @@ 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."));
+                       ui_hint(_("Select a type to create a new label, press 'L' to load script file, 'Q' quits."));
                        refresh();
                        refresh_menu = 0;
                }
 
                key = getch();
 
-               if (ui_resize)
+               if (sig_die)
+                       break;
+               if (sig_resize)
                        ui_menu_resize(cf);
                if (ui_menu_move(cf, key) == 0)
                        continue;
@@ -2129,8 +2230,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);
@@ -2148,11 +2251,13 @@ static int ui_help(void)
                "  ",
                N_("Command      Meaning"),
                N_("-------      -------"),
-               N_("  b          Toggle bootable flag of the current partition"),
+               N_("  b          Toggle bootable flag of the current partition;"),
+               N_("               implemented for DOS (MBR) and SGI labels only"),
                N_("  d          Delete the current partition"),
                N_("  h          Print this screen"),
                N_("  n          Create new partition from free space"),
                N_("  q          Quit program without writing partition table"),
+               N_("  r          Reduce or enlarge the current partition"),
                N_("  s          Fix partitions order (only when in disarray)"),
                N_("  t          Change the partition type"),
                N_("  u          Dump disk layout to sfdisk compatible script file"),
@@ -2171,7 +2276,7 @@ static int ui_help(void)
                N_("Use lsblk(8) or partx(8) to see more details about the device."),
                "  ",
                "  ",
-               "Copyright (C) 2014-2017 Karel Zak <kzak@redhat.com>"
+               "Copyright (C) 2014-2023 Karel Zak <kzak@redhat.com>"
        };
 
        erase();
@@ -2181,6 +2286,9 @@ static int ui_help(void)
        ui_info(_("Press a key to continue."));
 
        getch();
+
+       if (sig_die)
+               die_on_signal();
        return 0;
 }
 
@@ -2197,6 +2305,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';
@@ -2282,7 +2391,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 */
 
@@ -2291,11 +2400,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);
@@ -2339,6 +2448,42 @@ static int main_menu_action(struct cfdisk *cf, int key)
                        info = _("The type of partition %zu is unchanged.");
                break;
        }
+       case 'r': /* resize */
+       {
+               uint64_t size, max_size, secs;
+               struct fdisk_partition *npa;
+
+               if (fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
+                       return -EINVAL;
+
+               rc = fdisk_partition_get_max_size(cf->cxt,
+                                                 fdisk_partition_get_partno(pa),
+                                                 &size);
+               if (rc)
+                       return rc;
+
+               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);
@@ -2373,11 +2518,15 @@ static int main_menu_action(struct cfdisk *cf, int key)
                if (rc)
                        warn = _("Failed to write disklabel.");
                else {
+                       size_t q_idx = 0;
+
                        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.");
+                       if (menu_get_menuitem_by_key(cf, 'q', &q_idx))
+                               ui_menu_goto(cf, q_idx);
                }
                cf->nwrites++;
                break;
@@ -2437,18 +2586,26 @@ static int ui_run(struct cfdisk *cf)
        DBG(UI, ul_debug("start cols=%zu, lines=%zu", ui_cols, ui_lines));
 
        if (fdisk_get_collision(cf->cxt)) {
-               ui_warnx(_("Device already contains a %s signature; it will be removed by a write command."),
-                               fdisk_get_collision(cf->cxt));
-               fdisk_enable_wipe(cf->cxt, 1);
-               ui_hint(_("Press a key to continue."));
-               getch();
+               ui_warnx(_("Device already contains a %s signature."), fdisk_get_collision(cf->cxt));
+               if (fdisk_is_readonly(cf->cxt)) {
+                       ui_hint(_("Press a key to continue."));
+                       getch();
+               } else {
+                       char buf[64] = { 0 };
+                       rc = ui_get_string(_("Do you want to remove it? [Y]es/[N]o: "), NULL,
+                                       buf, sizeof(buf));
+                       fdisk_enable_wipe(cf->cxt,
+                                       rc > 0 && rpmatch(buf) == RPMATCH_YES ? 1 : 0);
+               }
        }
 
        if (!fdisk_has_label(cf->cxt) || cf->zero_start) {
                rc = ui_create_label(cf);
-               if (rc < 0)
-                       ui_errx(EXIT_FAILURE,
+               if (rc < 0) {
+                       errno = -rc;
+                       ui_err(EXIT_FAILURE,
                                        _("failed to create a new disklabel"));
+               }
                if (rc)
                        return rc;
        }
@@ -2469,15 +2626,20 @@ static int ui_run(struct cfdisk *cf)
        ui_draw_extra(cf);
 
        if (fdisk_is_readonly(cf->cxt))
-               ui_warnx(_("Device is open in read-only mode."));
+               ui_warnx(_("Device is open in read-only mode. Changes will remain in memory only."));
+       else if (cf->device_is_used)
+               ui_warnx(_("Device is currently in use, repartitioning is probably a bad idea."));
        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);
@@ -2539,7 +2701,7 @@ static int ui_run(struct cfdisk *cf)
 
                if (rc == 1)
                        break; /* quit */
-       } while (1);
+       }
 
        menu_pop(cf);
 
@@ -2558,39 +2720,48 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_("Display or manipulate a disk partition table.\n"), out);
 
        fputs(USAGE_OPTIONS, out);
-       fputs(_(" -L, --color[=<when>]     colorize output (auto, always or never)\n"), out);
+       fprintf(out,
+             _(" -L, --color[=<when>]     colorize output (%s, %s or %s)\n"), "auto", "always", "never");
        fprintf(out,
                "                            %s\n", USAGE_COLORS_DEFAULT);
        fputs(_(" -z, --zero               start with zeroed partition table\n"), out);
+       fprintf(out,
+             _("     --lock[=<mode>]      use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
+       fputs(_(" -r, --read-only          forced open cfdisk in read-only mode\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
-       printf(USAGE_HELP_OPTIONS(26));
+       fprintf(out, USAGE_HELP_OPTIONS(26));
 
-       printf(USAGE_MAN_TAIL("cfdisk(8)"));
+       fprintf(out, USAGE_MAN_TAIL("cfdisk(8)"));
        exit(EXIT_SUCCESS);
 }
 
 int main(int argc, char *argv[])
 {
-       const char *diskpath = NULL;
+       const char *diskpath = NULL, *lockmode = NULL;
        int rc, c, colormode = UL_COLORMODE_UNDEF;
+       int read_only = 0;
        struct cfdisk _cf = { .lines_idx = 0 },
                      *cf = &_cf;
-
+       enum {
+               OPT_LOCK        = CHAR_MAX + 1
+       };
        static const struct option longopts[] = {
                { "color",   optional_argument, NULL, 'L' },
+               { "lock",    optional_argument, NULL, OPT_LOCK },
                { "help",    no_argument,       NULL, 'h' },
                { "version", no_argument,       NULL, 'V' },
                { "zero",    no_argument,       NULL, 'z' },
+               { "read-only", no_argument,     NULL, 'r' },
                { NULL, 0, NULL, 0 },
        };
 
        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) {
+       while((c = getopt_long(argc, argv, "L::hVzr", longopts, NULL)) != -1) {
                switch(c) {
                case 'h':
                        usage();
@@ -2601,12 +2772,22 @@ int main(int argc, char *argv[])
                                colormode = colormode_or_err(optarg,
                                                _("unsupported color mode"));
                        break;
+                case 'r':
+                        read_only = 1;
+                        break;
                case 'V':
-                       printf(UTIL_LINUX_VERSION);
-                       return EXIT_SUCCESS;
+                       print_version(EXIT_SUCCESS);
                case 'z':
                        cf->zero_start = 1;
                        break;
+               case OPT_LOCK:
+                       lockmode = "1";
+                       if (optarg) {
+                               if (*optarg == '=')
+                                       optarg++;
+                               lockmode = optarg;
+                       }
+                       break;
                default:
                        errtryhelp(EXIT_FAILURE);
                }
@@ -2637,13 +2818,16 @@ int main(int argc, char *argv[])
        } else
                diskpath = argv[optind];
 
-       rc = fdisk_assign_device(cf->cxt, diskpath, 0);
-       if (rc == -EACCES)
+       rc = fdisk_assign_device(cf->cxt, diskpath, read_only);
+       if (rc == -EACCES && read_only == 0)
                rc = fdisk_assign_device(cf->cxt, diskpath, 1);
        if (rc != 0)
                err(EXIT_FAILURE, _("cannot open %s"), diskpath);
 
        if (!fdisk_is_readonly(cf->cxt)) {
+               if (blkdev_lock(fdisk_get_devfd(cf->cxt), diskpath, lockmode) != 0)
+                       return EXIT_FAILURE;
+
                cf->device_is_used = fdisk_device_is_used(cf->cxt);
                fdisk_get_partitions(cf->cxt, &cf->original_layout);
        }
@@ -2655,6 +2839,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