/*
* cfdisk.c - Display or manipulate a disk partition table.
*
- * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2014-2015 Karel Zak <kzak@redhat.com>
* Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu)
*
* The original cfdisk was inspired by the fdisk program
#include <sys/ioctl.h>
#include <libfdisk.h>
+#ifdef HAVE_LIBBLKID
+# include <blkid.h> /* keep it optional */
+#endif
+
+#ifdef HAVE_LIBMOUNT
+# include <libmount.h> /* keep it optional for non-linux systems */
+#endif
+
#ifdef HAVE_SLANG_H
# include <slang.h>
#elif defined(HAVE_SLANG_SLANG_H)
#include "mbsalign.h"
#include "colors.h"
#include "debug.h"
+#include "list.h"
#ifdef __GNU__
# define DEFAULT_DEVICE "/dev/hd0"
{ 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 */
+ struct libscols_table *extra; /* extra info ('X') */
+ WINDOW *w; /* window with extra info */
+};
+
/* top level control struct */
struct cfdisk {
struct fdisk_context *cxt; /* libfdisk context */
struct fdisk_table *table; /* partition table */
+
struct cfdisk_menu *menu; /* the current menu */
int *fields; /* output columns IDs */
char *linesbuf; /* table as string */
size_t linesbufsz; /* size of the tb_buf */
- char **lines; /* array with lines */
+ struct cfdisk_line *lines; /* list of lines */
+
size_t nlines; /* number of lines */
size_t lines_idx; /* current line <0..N>, exclude header */
size_t page_sz;
unsigned int nwrites; /* fdisk_write_disklabel() counter */
+ WINDOW *act_win; /* the window currently on the screen */
+
+#ifdef HAVE_LIBMOUNT
+ struct libmnt_table *mtab;
+ struct libmnt_table *fstab;
+ struct libmnt_cache *mntcache;
+#endif
unsigned int wrong_order :1, /* PT not in right order */
- zero_start :1; /* ignore existing partition table */
+ zero_start :1, /* ignore existing partition table */
+ show_extra :1; /* show extra partinfo */
};
return res;
}
+static void cfdisk_free_lines(struct cfdisk *cf)
+{
+ size_t i = 0;
+ while(i < cf->nlines) {
+ scols_unref_table(cf->lines[i].extra);
+
+ DBG(UI, ul_debug("delete window: %p",
+ cf->lines[i].w));
+
+ delwin(cf->lines[i].w);
+ ++i;
+ }
+ cf->act_win = NULL;
+ free(cf->lines);
+}
/*
* Read data about partitions from libfdisk and prepare output lines.
*/
DBG(TABLE, ul_debug("refreshing buffer"));
free(cf->linesbuf);
- free(cf->lines);
+ cfdisk_free_lines(cf);
cf->linesbuf = NULL;
cf->linesbufsz = 0;
cf->lines = NULL;
if (MENU_START_LINE - TABLE_START_LINE < cf->nlines)
cf->page_sz = MENU_START_LINE - TABLE_START_LINE - 1;
- cf->lines = xcalloc(cf->nlines, sizeof(char *));
+ cf->lines = xcalloc(cf->nlines, sizeof(struct cfdisk_line));
for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) {
- cf->lines[i] = p;
+ cf->lines[i].data = p;
p = strchr(p, '\n');
if (p) {
*p = '\0';
p++;
}
+ cf->lines[i].extra = scols_new_table();
+ scols_table_enable_noheadings(cf->lines[i].extra, 1);
+ scols_table_new_column(cf->lines[i].extra, NULL, 0, SCOLS_FL_RIGHT);
+ scols_table_new_column(cf->lines[i].extra, NULL, 0, SCOLS_FL_TRUNC);
}
return 0;
DBG(MENU, ul_debug("draw end."));
}
+inline static int extra_insert_pair(struct cfdisk_line *l, const char *name, const char *data)
+{
+ struct libscols_line *lsl;
+
+ assert(l);
+
+ if (!data && !*data)
+ return 0;
+
+ lsl = scols_table_new_line(l->extra, NULL);
+ if (!lsl)
+ return -ENOMEM;
+
+ scols_line_set_data(lsl, 0, name);
+ scols_line_set_data(lsl, 1, data);
+
+ return 0;
+}
+
+#ifdef HAVE_LIBMOUNT
+static char *get_mountpoint(struct cfdisk *cf, const char *uuid, const char *label)
+{
+ struct libmnt_fs *fs = NULL;
+ char *target = NULL;
+ int mounted = 0;
+
+ assert(uuid || label);
+
+ DBG(UI, ul_debug("asking for mountpoint [uuid=%s, label=%s]", uuid, label));
+
+ if (!cf->mntcache)
+ cf->mntcache = mnt_new_cache();
+
+ /* 1st try between mounted filesystems */
+ if (!cf->mtab) {
+ cf->mtab = mnt_new_table();
+ if (cf->mtab) {
+ mnt_table_set_cache(cf->mtab, cf->mntcache);
+ mnt_table_parse_mtab(cf->mtab, NULL);
+ }
+ }
+
+ if (cf->mtab)
+ fs = mnt_table_find_tag(cf->mtab,
+ uuid ? "UUID" : "LABEL",
+ uuid ? : label, MNT_ITER_FORWARD);
+
+ /* 2nd try fstab */
+ if (!fs) {
+ if (!cf->fstab) {
+ cf->fstab = mnt_new_table();
+ if (cf->fstab) {
+ mnt_table_set_cache(cf->fstab, cf->mntcache);
+ mnt_table_parse_fstab(cf->fstab, NULL);
+ }
+ }
+ if (cf->fstab)
+ fs = mnt_table_find_tag(cf->fstab,
+ uuid ? "UUID" : "LABEL",
+ uuid ? : label, MNT_ITER_FORWARD);
+ } else
+ mounted = 1;
+
+ if (fs) {
+ if (mounted)
+ xasprintf(&target, _("%s (mounted)"), mnt_fs_get_target(fs));
+ else
+ target = xstrdup(mnt_fs_get_target(fs));
+ }
+
+ return target;
+}
+#endif /* HAVE_LIBMOUNT */
+
+static void extra_prepare_data(struct cfdisk *cf)
+{
+ struct fdisk_partition *pa = get_current_partition(cf);
+ struct cfdisk_line *l = &cf->lines[cf->lines_idx];
+ char *data = NULL;
+ char *devuuid = NULL, *devlabel = NULL;
+
+ DBG(UI, ul_debug("preparing extra data"));
+
+ /* string data should not equal an empty string */
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_NAME, &data) && data) {
+ extra_insert_pair(l, _("Partition name:"), data);
+ free(data);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_UUID, &data) && data) {
+ extra_insert_pair(l, _("Partition UUID:"), data);
+ free(data);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_TYPE, &data) && data) {
+ char *code = NULL, *type = NULL;
+
+ fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_TYPEID, &code);
+ xasprintf(&type, "%s (%s)", data, code);
+
+ extra_insert_pair(l, _("Partition type:"), type);
+ free(data);
+ free(code);
+ free(type);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_ATTR, &data) && data) {
+ extra_insert_pair(l, _("Attributes:"), data);
+ free(data);
+ }
+
+ /* for numeric data, only show non-zero rows */
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_BSIZE, &data) && data) {
+ if (atoi(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))
+ extra_insert_pair(l, "CPG:", data);
+ free(data);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSIZE, &data) && data) {
+ if (atoi(data))
+ extra_insert_pair(l, "FSIZE:", data);
+ free(data);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_SADDR, &data) && data) {
+ extra_insert_pair(l, _("Start C/H/S:"), data);
+ free(data);
+ }
+
+ if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_EADDR, &data) && data) {
+ extra_insert_pair(l, _("End C/H/S:"), data);
+ free(data);
+ }
+
+#ifdef HAVE_LIBBLKID
+ if (fdisk_partition_has_start(pa) && fdisk_partition_has_size(pa)) {
+ int fd;
+ uintmax_t start, size;
+ blkid_probe pr = blkid_new_probe();
+
+ if (!pr)
+ goto done;
+
+ DBG(UI, ul_debug("blkid prober: %p", pr));
+
+ start = fdisk_partition_get_start(pa) * fdisk_get_sector_size(cf->cxt);
+ size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cf->cxt);
+ fd = fdisk_get_devfd(cf->cxt);
+
+ if (blkid_probe_set_device(pr, fd, start, size) == 0 &&
+ blkid_do_fullprobe(pr) == 0) {
+ const char *bdata = NULL;
+
+ if (!blkid_probe_lookup_value(pr, "TYPE", &bdata, NULL))
+ extra_insert_pair(l, _("Filesystem:"), bdata);
+ if (!blkid_probe_lookup_value(pr, "LABEL", &bdata, NULL)) {
+ extra_insert_pair(l, _("Filesystem LABEL:"), bdata);
+ devlabel = xstrdup(bdata);
+ }
+ if (!blkid_probe_lookup_value(pr, "UUID", &bdata, NULL)) {
+ extra_insert_pair(l, _("Filesystem UUID:"), bdata);
+ devuuid = xstrdup(bdata);
+ }
+ }
+ blkid_free_probe(pr);
+ }
+#endif /* HAVE_LIBBLKID */
+
+#ifdef HAVE_LIBMOUNT
+ if (devuuid || devlabel) {
+ data = get_mountpoint(cf, devuuid, devlabel);
+ if (data) {
+ extra_insert_pair(l, _("Mountpoint:"), data);
+ free(data);
+ }
+ }
+#endif /* HAVE_LIBMOUNT */
+done:
+ free(devlabel);
+ free(devuuid);
+}
+
+static void ui_clear_extra(struct cfdisk *cf)
+{
+ DBG(UI, ul_debug("clear window: %p", cf->act_win));
+
+ wclear(cf->act_win);
+ wrefresh(cf->act_win);
+}
+
+static int ui_draw_extra(struct cfdisk *cf)
+{
+ WINDOW *win_ex;
+ int wline = 1;
+ struct cfdisk_line *ln = &cf->lines[cf->lines_idx];
+ char *tbstr = NULL, *end;
+ int win_ex_start_line, win_height, tblen;
+ int ndatalines;
+
+ if (scols_table_is_empty(ln->extra))
+ extra_prepare_data(cf);
+
+ ndatalines = fdisk_table_get_nents(cf->table) + 1;
+
+ /* nents + header + one free line */
+ win_ex_start_line = TABLE_START_LINE + ndatalines;
+ win_height = MENU_START_LINE - win_ex_start_line;
+ tblen = scols_table_get_nlines(ln->extra);
+
+ /* we can't get a single line of data under the partlist*/
+ if (win_height < 3)
+ return 1;
+
+ /* number of data lines + 2 for top/bottom lines */
+ win_height = win_height < tblen + 2 ? win_height : tblen + 2;
+
+ if ((size_t) win_ex_start_line + win_height + 1 < MENU_START_LINE)
+ win_ex_start_line = MENU_START_LINE - win_height;
+
+ win_ex = newwin(win_height, ui_cols - 2, win_ex_start_line, 1);
+
+ scols_table_reduce_termwidth(ln->extra, 4);
+ scols_print_table_to_string(ln->extra, &tbstr);
+
+ end = tbstr;
+ while ((end = strchr(end, '\n')))
+ *end++ = '\0';
+
+ box(win_ex, 0, 0);
+
+ end = tbstr;
+ while (--win_height > 1) {
+ mvwaddstr(win_ex, wline++, 1 /* window column*/, tbstr);
+ tbstr += strlen(tbstr) + 1;
+ }
+ free(end);
+
+#if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H)
+ /* clear the currently shown extra window */
+ ui_clear_extra(cf);
+#endif
+
+ DBG(UI, ul_debug("delete window: %p", ln->w));
+ delwin(ln->w);
+
+ DBG(UI, ul_debug("draw window: %p", win_ex));
+ wrefresh(win_ex);
+
+ cf->act_win = ln->w = win_ex;
+ return 0;
+}
+
static void ui_menu_goto(struct cfdisk *cf, int where)
{
struct cfdisk_menuitem *d;
d = menu_get_menuitem(cf, where);
ui_draw_menuitem(cf, d, where);
+
}
static int ui_menu_move(struct cfdisk *cf, int key)
if (cur) {
attron(A_REVERSE);
mvaddstr(ln, 0, ARROW_CURSOR_STRING);
- mvaddstr(ln, cl, cf->lines[i + 1]);
+ mvaddstr(ln, cl, cf->lines[i + 1].data);
attroff(A_REVERSE);
} else {
int at = 0;
at = 1;
}
mvaddstr(ln, 0, ARROW_CURSOR_DUMMY);
- mvaddstr(ln, cl, cf->lines[i + 1]);
+ mvaddstr(ln, cl, cf->lines[i + 1].data);
if (at)
- attroff(COLOR_PAIR(CFDISK_CL_FREESPACE));
+ attroff(COLOR_PAIR(CFDISK_CL_FREESPACE));
}
if ((size_t) ln == MENU_START_LINE - 1 &&
if (cur)
attroff(A_REVERSE);
}
+
}
static int ui_draw_table(struct cfdisk *cf)
/* print header */
attron(A_BOLD);
- mvaddstr(TABLE_START_LINE, cl, cf->lines[0]);
+ mvaddstr(TABLE_START_LINE, cl, cf->lines[0].data);
attroff(A_BOLD);
/* print partitions */
ui_clean_info();
ui_draw_menu(cf);
refresh();
+
+ if (cf->show_extra)
+ ui_draw_extra(cf);
return 0;
}
N_(" W Write partition table to disk (you must enter uppercase W);"),
N_(" since this might destroy data on the disk, you must either"),
N_(" confirm or deny the write by entering 'yes' or 'no'"),
+ N_(" x Display/hide extra information about a partition"),
N_("Up Arrow Move cursor to the previous partition"),
N_("Down Arrow Move cursor to the next partition"),
N_("Left Arrow Move cursor to the previous menu item"),
if (fdisk_is_readonly(cf->cxt))
ignore[i++] = 'W';
+
return i;
}
case 'u': /* dUmp */
ui_script_write(cf);
break;
+ case 'x': /* Extra */
+ cf->show_extra = cf->show_extra ? 0 : 1;
+ ref = 1;
+ break;
case 'W': /* Write */
{
char buf[64] = { 0 };
} else
ui_draw_menu(cf);
+ if (cf->show_extra)
+ ui_draw_extra(cf);
+
ui_clean_hint();
if (warn)
menu_refresh_size(cf);
lines_refresh(cf);
ui_refresh(cf);
+
+ if (cf->show_extra)
+ ui_draw_extra(cf);
}
static int ui_run(struct cfdisk *cf)
menu_push(cf, main_menuitems);
cf->menu->ignore_cb = main_menu_ignore_keys;
+
rc = ui_refresh(cf);
if (rc)
return rc;
+ cf->show_extra = 1;
+ ui_draw_extra(cf);
+
if (fdisk_is_readonly(cf->cxt))
ui_warnx(_("Device open in read-only mode."));
else if (cf->wrong_order)
ui_info(_("Note that partition table entries are not in disk order now."));
do {
+#if defined(HAVE_SLANG_H) || defined(HAVE_SLCURSES_H)
+ /* slang getch() seems to clear
+ * the extra window from the screen,
+ * so turn off the flag as well
+ */
+ cf->show_extra = 0;
+#endif
int key = getch();
rc = 0;
ui_run(cf);
ui_end();
- free(cf->lines);
+ cfdisk_free_lines(cf);
free(cf->linesbuf);
- fdisk_unref_table(cf->table);
+ fdisk_unref_table(cf->table);
+#ifdef HAVE_LIBMOUNT
+ mnt_unref_table(cf->fstab);
+ mnt_unref_table(cf->mtab);
+ mnt_unref_cache(cf->mntcache);
+#endif
rc = fdisk_deassign_device(cf->cxt, cf->nwrites == 0);
fdisk_unref_context(cf->cxt);
DBG(MISC, ul_debug("bye! [rc=%d]", rc));