]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/cfdisk.c
cfdisk: optimize mountpoint detection for PARTUUID
[thirdparty/util-linux.git] / disk-utils / cfdisk.c
1 /*
2 * cfdisk.c - Display or manipulate a disk partition table.
3 *
4 * Copyright (C) 2014-2015 Karel Zak <kzak@redhat.com>
5 * Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu)
6 *
7 * The original cfdisk was inspired by the fdisk program
8 * by A. V. Le Blanc (leblanc@mcc.ac.uk.
9 *
10 * cfdisk is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 */
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <errno.h>
18 #include <signal.h>
19 #include <ctype.h>
20 #include <getopt.h>
21 #include <assert.h>
22 #include <libsmartcols.h>
23 #include <sys/ioctl.h>
24 #include <libfdisk.h>
25
26 #ifdef HAVE_LIBMOUNT
27 # include <libmount.h> /* keep it optional for non-linux systems */
28 #endif
29
30 #ifdef HAVE_SLANG_H
31 # include <slang.h>
32 #elif defined(HAVE_SLANG_SLANG_H)
33 # include <slang/slang.h>
34 #endif
35
36 #ifndef _XOPEN_SOURCE
37 # define _XOPEN_SOURCE 500 /* for inclusion of get_wch */
38 #endif
39
40 #ifdef HAVE_SLCURSES_H
41 # include <slcurses.h>
42 #elif defined(HAVE_SLANG_SLCURSES_H)
43 # include <slang/slcurses.h>
44 #elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
45 # include <ncursesw/ncurses.h>
46 #elif defined(HAVE_NCURSES_H)
47 # include <ncurses.h>
48 #elif defined(HAVE_NCURSES_NCURSES_H)
49 # include <ncurses/ncurses.h>
50 #endif
51
52 #ifdef HAVE_WIDECHAR
53 # include <wctype.h>
54 # include <wchar.h>
55 #endif
56
57 #include "c.h"
58 #include "closestream.h"
59 #include "nls.h"
60 #include "strutils.h"
61 #include "xalloc.h"
62 #include "mbsalign.h"
63 #include "mbsedit.h"
64 #include "colors.h"
65 #include "debug.h"
66 #include "list.h"
67 #include "blkdev.h"
68
69 static const char *default_disks[] = {
70 #ifdef __GNU__
71 "/dev/hd0",
72 "/dev/sd0",
73 #elif defined(__FreeBSD__)
74 "/dev/ad0",
75 "/dev/da0",
76 #else
77 "/dev/sda",
78 "/dev/vda",
79 "/dev/hda",
80 #endif
81 };
82
83 #define ARROW_CURSOR_STRING ">> "
84 #define ARROW_CURSOR_DUMMY " "
85 #define ARROW_CURSOR_WIDTH (sizeof(ARROW_CURSOR_STRING) - 1)
86
87 /* vertical menu */
88 #define MENU_V_SPADDING 1 /* space around menu item string */
89
90 /* horizontal menu */
91 #define MENU_H_SPADDING 0 /* space around menu item string */
92 #define MENU_H_BETWEEN 2 /* space between menu items */
93 #define MENU_H_PRESTR "["
94 #define MENU_H_POSTSTR "]"
95
96 #define MENU_TITLE_PADDING 3
97
98 #define MENU_H_PRESTR_SZ (sizeof(MENU_H_PRESTR) - 1)
99 #define MENU_H_POSTSTR_SZ (sizeof(MENU_H_POSTSTR) - 1)
100
101 #define TABLE_START_LINE 4
102 #define MENU_START_LINE (ui_lines - 4) /* The menu maybe use two lines */
103 #define INFO_LINE (ui_lines - 2)
104 #define WARN_LINE INFO_LINE
105 #define HINT_LINE (ui_lines - 1)
106
107 #define CFDISK_ERR_ESC 5000
108
109 #ifndef KEY_ESC
110 # define KEY_ESC '\033'
111 #endif
112 #ifndef KEY_DELETE
113 # define KEY_DELETE '\177'
114 #endif
115 #ifndef KEY_DC
116 # define KEY_DC 0423
117 #endif
118
119
120 /* colors */
121 enum {
122 CFDISK_CL_NONE = 0,
123 CFDISK_CL_WARNING,
124 CFDISK_CL_FREESPACE,
125 CFDISK_CL_INFO
126 };
127 static const int color_pairs[][2] = {
128 /* color foreground, background */
129 [CFDISK_CL_WARNING] = { COLOR_RED, -1 },
130 [CFDISK_CL_FREESPACE] = { COLOR_GREEN, -1 },
131 [CFDISK_CL_INFO] = { COLOR_BLUE, -1 }
132 };
133
134 struct cfdisk;
135
136 static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx);
137 static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf, int key, size_t *idx);
138 static struct cfdisk_menu *menu_push(struct cfdisk *cf, struct cfdisk_menuitem *item);
139 static struct cfdisk_menu *menu_pop(struct cfdisk *cf);
140 static void menu_refresh_size(struct cfdisk *cf);
141
142 static int ui_end(void);
143 static int ui_refresh(struct cfdisk *cf);
144
145 static void ui_warnx(const char *fmt, ...)
146 __attribute__((__format__ (__printf__, 1, 2)));
147 static void ui_warn(const char *fmt, ...)
148 __attribute__((__format__ (__printf__, 1, 2)));
149 static void ui_info(const char *fmt, ...)
150 __attribute__((__format__ (__printf__, 1, 2)));
151
152 static void ui_draw_menu(struct cfdisk *cf);
153 static int ui_menu_move(struct cfdisk *cf, int key);
154 static void ui_menu_resize(struct cfdisk *cf);
155
156 static int ui_get_size(struct cfdisk *cf, const char *prompt, uint64_t *res,
157 uint64_t low, uint64_t up, int *expsize);
158
159 static int ui_enabled;
160 static volatile sig_atomic_t sig_resize;
161 static volatile sig_atomic_t sig_die;
162
163 /* ncurses LINES and COLS may be actual variables or *macros*, but we need
164 * something portable and writable */
165 static size_t ui_lines;
166 static size_t ui_cols;
167
168 /* menu item */
169 struct cfdisk_menuitem {
170 int key; /* keyboard shortcut */
171 const char *name; /* item name */
172 const char *desc; /* item description (hint) */
173 void *userdata;
174 };
175
176 /* menu */
177 struct cfdisk_menu {
178 char *title; /* optional menu title */
179 struct cfdisk_menuitem *items; /* array with menu items */
180 char *ignore;/* string with keys to ignore */
181 size_t width; /* maximal width of the menu item */
182 size_t nitems; /* number of the active menu items */
183 size_t page_sz;/* when menu longer than screen */
184 size_t idx; /* the current menu item */
185 int prefkey;/* preferred menu item */
186 struct cfdisk_menu *prev;
187
188 /* @ignore keys generator */
189 int (*ignore_cb) (struct cfdisk *, char *, size_t);
190
191 unsigned int vertical : 1; /* enable vertical mode */
192 };
193
194 /* main menu */
195 static struct cfdisk_menuitem main_menuitems[] = {
196 { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
197 { 'd', N_("Delete"), N_("Delete the current partition") },
198 { 'r', N_("Resize"), N_("Reduce or enlarge the current partition") },
199 { 'n', N_("New"), N_("Create new partition from free space") },
200 { 'q', N_("Quit"), N_("Quit program without writing changes") },
201 { 't', N_("Type"), N_("Change the partition type") },
202 { 'h', N_("Help"), N_("Print help screen") },
203 { 's', N_("Sort"), N_("Fix partitions order") },
204 { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
205 { 'u', N_("Dump"), N_("Dump partition table to sfdisk compatible script file") },
206 { 0, NULL, NULL }
207 };
208
209 /* extra partinfo in name:value pairs */
210 struct cfdisk_extra {
211 char *name;
212 char *data;
213
214 struct list_head exs;
215 };
216
217 /* line and extra partinfo list_head */
218 struct cfdisk_line {
219 char *data; /* line data */
220 struct libscols_table *extra; /* extra info ('X') */
221 WINDOW *w; /* window with extra info */
222 };
223
224 /* top level control struct */
225 struct cfdisk {
226 struct fdisk_context *cxt; /* libfdisk context */
227 struct fdisk_table *table; /* partition table */
228 struct fdisk_table *original_layout; /* original on-disk PT */
229
230 struct cfdisk_menu *menu; /* the current menu */
231
232 int *fields; /* output columns IDs */
233 size_t nfields; /* number of columns IDs */
234
235 char *linesbuf; /* table as string */
236 size_t linesbufsz; /* size of the tb_buf */
237
238 struct cfdisk_line *lines; /* list of lines */
239
240 size_t nlines; /* number of lines */
241 size_t lines_idx; /* current line <0..N>, exclude header */
242 size_t page_sz;
243
244 unsigned int nwrites; /* fdisk_write_disklabel() counter */
245
246 WINDOW *act_win; /* the window currently on the screen */
247
248 #ifdef HAVE_LIBMOUNT
249 struct libmnt_table *mtab;
250 struct libmnt_table *fstab;
251 struct libmnt_cache *mntcache;
252 #endif
253 unsigned int wrong_order :1, /* PT not in right order */
254 zero_start :1, /* ignore existing partition table */
255 device_is_used : 1, /* don't use re-read ioctl */
256 show_extra :1; /* show extra partinfo */
257 };
258
259
260 /*
261 * let's use include/debug.h stuff for cfdisk too
262 */
263 static UL_DEBUG_DEFINE_MASK(cfdisk);
264 UL_DEBUG_DEFINE_MASKNAMES(cfdisk) = UL_DEBUG_EMPTY_MASKNAMES;
265
266 #define CFDISK_DEBUG_INIT (1 << 1)
267 #define CFDISK_DEBUG_UI (1 << 2)
268 #define CFDISK_DEBUG_MENU (1 << 3)
269 #define CFDISK_DEBUG_MISC (1 << 4)
270 #define CFDISK_DEBUG_TABLE (1 << 5)
271 #define CFDISK_DEBUG_ALL 0xFFFF
272
273 #define DBG(m, x) __UL_DBG(cfdisk, CFDISK_DEBUG_, m, x)
274
275 static void cfdisk_init_debug(void)
276 {
277 __UL_INIT_DEBUG_FROM_ENV(cfdisk, CFDISK_DEBUG_, 0, CFDISK_DEBUG);
278 }
279
280 /* Initialize output columns -- we follow libfdisk fields (usually specific
281 * to the label type.
282 */
283 static int cols_init(struct cfdisk *cf)
284 {
285 assert(cf);
286
287 free(cf->fields);
288 cf->fields = NULL;
289 cf->nfields = 0;
290
291 return fdisk_label_get_fields_ids(NULL, cf->cxt, &cf->fields, &cf->nfields);
292 }
293
294 static void die_on_signal(void)
295 {
296 DBG(MISC, ul_debug("die on signal."));
297 ui_end();
298 exit(EXIT_FAILURE);
299 }
300
301 static void resize(void)
302 {
303 struct winsize ws;
304
305 if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) != -1
306 && ws.ws_row && ws.ws_col) {
307 ui_lines = ws.ws_row;
308 ui_cols = ws.ws_col;
309 #if HAVE_RESIZETERM
310 resizeterm(ws.ws_row, ws.ws_col);
311 #endif
312 clearok(stdscr, TRUE);
313 }
314 touchwin(stdscr);
315
316 DBG(UI, ul_debug("ui: resize refresh ui_cols=%zu, ui_lines=%zu",
317 ui_cols, ui_lines));
318 sig_resize = 0;
319 }
320
321 /* Reads partition in tree-like order from scols
322 */
323 static int partition_from_scols(struct fdisk_table *tb,
324 struct libscols_line *ln)
325 {
326 struct fdisk_partition *pa = scols_line_get_userdata(ln);
327
328 fdisk_table_add_partition(tb, pa);
329 fdisk_unref_partition(pa);
330
331 if (scols_line_has_children(ln)) {
332 struct libscols_line *chln;
333 struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
334
335 if (!itr)
336 return -EINVAL;
337 while (scols_line_next_child(ln, itr, &chln) == 0)
338 partition_from_scols(tb, chln);
339 scols_free_iter(itr);
340 }
341 return 0;
342 }
343
344 static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb)
345 {
346 struct fdisk_partition *pa;
347 struct fdisk_label *lb;
348 struct fdisk_iter *itr;
349 struct libscols_table *table = NULL;
350 struct libscols_iter *s_itr = NULL;
351 char *res = NULL;
352 size_t i;
353 int tree = 0;
354 struct libscols_line *ln, *ln_cont = NULL;
355
356 DBG(TABLE, ul_debug("convert to string"));
357
358 assert(cf);
359 assert(cf->cxt);
360 assert(cf->fields);
361 assert(tb);
362
363 lb = fdisk_get_label(cf->cxt, NULL);
364 assert(lb);
365
366 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
367 if (!itr)
368 goto done;
369
370 /* get container (e.g. extended partition) */
371 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
372 if (fdisk_partition_is_nested(pa)) {
373 DBG(TABLE, ul_debug("nested detected, using tree"));
374 tree = SCOLS_FL_TREE;
375 break;
376 }
377 }
378
379 table = scols_new_table();
380 if (!table)
381 goto done;
382 scols_table_enable_maxout(table, 1);
383 scols_table_enable_nowrap(table, 1);
384
385 #if !defined(HAVE_LIBNCURSESW) || !defined(HAVE_WIDECHAR)
386 scols_table_enable_ascii(table, 1);
387 #endif
388
389 /* headers */
390 for (i = 0; i < cf->nfields; i++) {
391 int fl = 0;
392 const struct fdisk_field *field =
393 fdisk_label_get_field(lb, cf->fields[i]);
394 if (!field)
395 continue;
396
397 if (fdisk_field_is_number(field))
398 fl |= SCOLS_FL_RIGHT;
399 if (fdisk_field_get_id(field) == FDISK_FIELD_TYPE)
400 fl |= SCOLS_FL_TRUNC;
401 if (tree && fdisk_field_get_id(field) == FDISK_FIELD_DEVICE)
402 fl |= SCOLS_FL_TREE;
403
404 if (!scols_table_new_column(table,
405 _(fdisk_field_get_name(field)),
406 fdisk_field_get_width(field), fl))
407 goto done;
408 }
409
410 /* data */
411 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
412
413 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
414 struct libscols_line *parent = fdisk_partition_is_nested(pa) ? ln_cont : NULL;
415
416 ln = scols_table_new_line(table, parent);
417 if (!ln)
418 goto done;
419 for (i = 0; i < cf->nfields; i++) {
420 char *cdata = NULL;
421
422 if (fdisk_partition_to_string(pa, cf->cxt,
423 cf->fields[i], &cdata))
424 continue;
425 scols_line_refer_data(ln, i, cdata);
426 }
427 if (tree && fdisk_partition_is_container(pa))
428 ln_cont = ln;
429
430 scols_line_set_userdata(ln, (void *) pa);
431 fdisk_ref_partition(pa);
432 }
433
434 if (scols_table_is_empty(table))
435 goto done;
436
437 scols_table_reduce_termwidth(table, ARROW_CURSOR_WIDTH);
438 scols_print_table_to_string(table, &res);
439
440 /* scols_* code might reorder lines, let's reorder @tb according to the
441 * final output (it's no problem because partitions are addressed by
442 * parno stored within struct fdisk_partition) */
443
444 /* remove all */
445 fdisk_reset_table(tb);
446
447 s_itr = scols_new_iter(SCOLS_ITER_FORWARD);
448 if (!s_itr)
449 goto done;
450
451 /* add all in the right order (don't forget the output is tree) */
452 while (scols_table_next_line(table, s_itr, &ln) == 0) {
453 if (scols_line_get_parent(ln))
454 continue;
455 if (partition_from_scols(tb, ln))
456 break;
457 }
458 done:
459 scols_unref_table(table);
460 scols_free_iter(s_itr);
461 fdisk_free_iter(itr);
462
463 return res;
464 }
465
466 static void cfdisk_free_lines(struct cfdisk *cf)
467 {
468 size_t i = 0;
469 while(i < cf->nlines) {
470 scols_unref_table(cf->lines[i].extra);
471
472 DBG(UI, ul_debug("delete window: %p",
473 cf->lines[i].w));
474
475 if (cf->lines[i].w)
476 delwin(cf->lines[i].w);
477 cf->lines[i].w = NULL;
478 ++i;
479 }
480 cf->act_win = NULL;
481 free(cf->lines);
482 cf->lines = NULL;
483 }
484 /*
485 * Read data about partitions from libfdisk and prepare output lines.
486 */
487 static int lines_refresh(struct cfdisk *cf)
488 {
489 int rc;
490 char *p;
491 size_t i;
492
493 assert(cf);
494
495 DBG(TABLE, ul_debug("refreshing buffer"));
496
497 free(cf->linesbuf);
498 cfdisk_free_lines(cf);
499 cf->linesbuf = NULL;
500 cf->linesbufsz = 0;
501 cf->lines = NULL;
502 cf->nlines = 0;
503
504 fdisk_unref_table(cf->table);
505 cf->table = NULL;
506
507 /* read partitions and free spaces into cf->table */
508 rc = fdisk_get_partitions(cf->cxt, &cf->table);
509 if (!rc)
510 rc = fdisk_get_freespaces(cf->cxt, &cf->table);
511 if (rc)
512 return rc;
513
514 cf->linesbuf = table_to_string(cf, cf->table);
515 if (!cf->linesbuf)
516 return -ENOMEM;
517
518 cf->linesbufsz = strlen(cf->linesbuf);
519 cf->nlines = fdisk_table_get_nents(cf->table) + 1; /* 1 for header line */
520 cf->page_sz = 0;
521 cf->wrong_order = fdisk_table_wrong_order(cf->table) ? 1 : 0;
522
523 if (MENU_START_LINE - TABLE_START_LINE < cf->nlines)
524 cf->page_sz = MENU_START_LINE - TABLE_START_LINE - 1;
525
526 cf->lines = xcalloc(cf->nlines, sizeof(struct cfdisk_line));
527
528 for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) {
529 cf->lines[i].data = p;
530 p = strchr(p, '\n');
531 if (p) {
532 *p = '\0';
533 p++;
534 }
535 cf->lines[i].extra = scols_new_table();
536 scols_table_enable_noheadings(cf->lines[i].extra, 1);
537 scols_table_new_column(cf->lines[i].extra, NULL, 0, SCOLS_FL_RIGHT);
538 scols_table_new_column(cf->lines[i].extra, NULL, 0, SCOLS_FL_TRUNC);
539 }
540
541 return 0;
542 }
543
544 static struct fdisk_partition *get_current_partition(struct cfdisk *cf)
545 {
546 assert(cf);
547 assert(cf->table);
548
549 return fdisk_table_get_partition(cf->table, cf->lines_idx);
550 }
551
552 static int is_freespace(struct cfdisk *cf, size_t i)
553 {
554 struct fdisk_partition *pa;
555
556 assert(cf);
557 assert(cf->table);
558
559 pa = fdisk_table_get_partition(cf->table, i);
560 return fdisk_partition_is_freespace(pa);
561 }
562
563 /* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's
564 * response back to libfdisk
565 */
566 static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf)
567 {
568 struct cfdisk_menuitem *d, *cm;
569 int key;
570 size_t i = 0, nitems;
571 const char *name, *desc;
572
573 assert(ask);
574 assert(cf);
575
576 /* create cfdisk menu according to libfdisk ask-menu, note that the
577 * last cm[] item has to be empty -- so nitems + 1 */
578 nitems = fdisk_ask_menu_get_nitems(ask);
579 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
580
581 for (i = 0; i < nitems; i++) {
582 if (fdisk_ask_menu_get_item(ask, i, &key, &name, &desc))
583 break;
584 cm[i].key = key;
585 cm[i].desc = desc;
586 cm[i].name = name;
587 }
588
589 /* make the new menu active */
590 menu_push(cf, cm);
591 ui_draw_menu(cf);
592 refresh();
593
594 /* wait for keys */
595 while (!sig_die) {
596 key = getch();
597
598 if (sig_die)
599 break;
600 if (sig_resize)
601 ui_menu_resize(cf);
602 if (ui_menu_move(cf, key) == 0)
603 continue;
604
605 switch (key) {
606 case KEY_ENTER:
607 case '\n':
608 case '\r':
609 d = menu_get_menuitem(cf, cf->menu->idx);
610 if (d)
611 fdisk_ask_menu_set_result(ask, d->key);
612 menu_pop(cf);
613 free(cm);
614 return 0;
615 }
616 }
617
618 if (sig_die)
619 die_on_signal();
620
621 menu_pop(cf);
622 free(cm);
623 return -1;
624 }
625
626 /* libfdisk callback
627 */
628 static int ask_callback(struct fdisk_context *cxt __attribute__((__unused__)),
629 struct fdisk_ask *ask,
630 void *data __attribute__((__unused__)))
631 {
632 int rc = 0;
633
634 assert(ask);
635
636 switch(fdisk_ask_get_type(ask)) {
637 case FDISK_ASKTYPE_INFO:
638 ui_info("%s", fdisk_ask_print_get_mesg(ask));
639 break;
640 case FDISK_ASKTYPE_WARNX:
641 ui_warnx("%s", fdisk_ask_print_get_mesg(ask));
642 break;
643 case FDISK_ASKTYPE_WARN:
644 ui_warn("%s", fdisk_ask_print_get_mesg(ask));
645 break;
646 case FDISK_ASKTYPE_MENU:
647 ask_menu(ask, (struct cfdisk *) data);
648 break;
649 default:
650 ui_warnx(_("internal error: unsupported dialog type %d"),
651 fdisk_ask_get_type(ask));
652 return -EINVAL;
653 }
654 return rc;
655 }
656
657 static int ui_end(void)
658 {
659 if (!ui_enabled)
660 return -EINVAL;
661
662 #if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
663 SLsmg_gotorc(ui_lines - 1, 0);
664 SLsmg_refresh();
665 #else
666 mvcur(0, ui_cols - 1, ui_lines-1, 0);
667 #endif
668 curs_set(1);
669 nl();
670 endwin();
671 printf("\n");
672 ui_enabled = 0;
673 return 0;
674 }
675
676 static void __attribute__((__format__ (__printf__, 3, 0)))
677 ui_vprint_center(size_t line, int attrs, const char *fmt, va_list ap)
678 {
679 size_t width;
680 char *buf = NULL;
681
682 move(line, 0);
683 clrtoeol();
684
685 xvasprintf(&buf, fmt, ap);
686
687 width = mbs_safe_width(buf);
688 if (width > (size_t) ui_cols) {
689 char *p = strrchr(buf + ui_cols, ' ');
690 if (!p)
691 p = buf + ui_cols;
692 *p = '\0';
693 if (line + 1 >= ui_lines)
694 line--;
695 attron(attrs);
696 mvaddstr(line, 0, buf);
697 mvaddstr(line + 1, 0, p+1);
698 attroff(attrs);
699 } else {
700 attron(attrs);
701 mvaddstr(line, (ui_cols - width) / 2, buf);
702 attroff(attrs);
703 }
704 free(buf);
705 }
706
707 static void __attribute__((__format__ (__printf__, 2, 3)))
708 ui_center(size_t line, const char *fmt, ...)
709 {
710 va_list ap;
711 va_start(ap, fmt);
712 ui_vprint_center(line, 0, fmt, ap);
713 va_end(ap);
714 }
715
716 static void __attribute__((__format__ (__printf__, 1, 2)))
717 ui_warnx(const char *fmt, ...)
718 {
719 va_list ap;
720 va_start(ap, fmt);
721 if (ui_enabled)
722 ui_vprint_center(WARN_LINE,
723 colors_wanted() ? COLOR_PAIR(CFDISK_CL_WARNING) : 0,
724 fmt, ap);
725 else {
726 vfprintf(stderr, fmt, ap);
727 fputc('\n', stderr);
728 }
729 va_end(ap);
730 }
731
732 static void __attribute__((__format__ (__printf__, 1, 2)))
733 ui_warn(const char *fmt, ...)
734 {
735 char *fmt_m;
736 va_list ap;
737
738 xasprintf(&fmt_m, "%s: %m", fmt);
739
740 va_start(ap, fmt);
741 if (ui_enabled)
742 ui_vprint_center(WARN_LINE,
743 colors_wanted() ? COLOR_PAIR(CFDISK_CL_WARNING) : 0,
744 fmt_m, ap);
745 else {
746 vfprintf(stderr, fmt_m, ap);
747 fputc('\n', stderr);
748 }
749 va_end(ap);
750 free(fmt_m);
751 }
752
753 static void ui_clean_warn(void)
754 {
755 move(WARN_LINE, 0);
756 clrtoeol();
757 }
758
759 static int __attribute__((__noreturn__))
760 __attribute__((__format__ (__printf__, 2, 3)))
761 ui_err(int rc, const char *fmt, ...)
762 {
763 va_list ap;
764 ui_end();
765
766 va_start(ap, fmt);
767 fprintf(stderr, "%s: ", program_invocation_short_name);
768 vfprintf(stderr, fmt, ap);
769 fprintf(stderr, ": %s\n", strerror(errno));
770 va_end(ap);
771
772 exit(rc);
773 }
774
775 static int __attribute__((__noreturn__))
776 __attribute__((__format__ (__printf__, 2, 3)))
777 ui_errx(int rc, const char *fmt, ...)
778 {
779 va_list ap;
780 ui_end();
781
782 va_start(ap, fmt);
783 fprintf(stderr, "%s: ", program_invocation_short_name);
784 vfprintf(stderr, fmt, ap);
785 fputc('\n', stderr);
786 va_end(ap);
787
788 exit(rc);
789 }
790
791 static void __attribute__((__format__ (__printf__, 1, 2)))
792 ui_info(const char *fmt, ...)
793 {
794 va_list ap;
795 va_start(ap, fmt);
796 if (ui_enabled)
797 ui_vprint_center(INFO_LINE,
798 colors_wanted() ? COLOR_PAIR(CFDISK_CL_INFO) : 0,
799 fmt, ap);
800 else {
801 vfprintf(stdout, fmt, ap);
802 fputc('\n', stdout);
803 }
804 va_end(ap);
805 }
806
807 static void ui_clean_info(void)
808 {
809 move(INFO_LINE, 0);
810 clrtoeol();
811 }
812
813 static void __attribute__((__format__ (__printf__, 1, 2)))
814 ui_hint(const char *fmt, ...)
815 {
816 va_list ap;
817 va_start(ap, fmt);
818 if (ui_enabled)
819 ui_vprint_center(HINT_LINE, A_BOLD, fmt, ap);
820 else {
821 vfprintf(stdout, fmt, ap);
822 fputc('\n', stdout);
823 }
824 va_end(ap);
825 }
826
827 static void ui_clean_hint(void)
828 {
829 move(HINT_LINE, 0);
830 clrtoeol();
831 }
832
833
834 static void sig_handler_die(int dummy __attribute__((__unused__)))
835 {
836 sig_die = 1;
837 }
838
839 static void sig_handler_resize(int dummy __attribute__((__unused__)))
840 {
841 sig_resize = 1;
842 }
843
844 static void menu_refresh_size(struct cfdisk *cf)
845 {
846 if (cf->menu && cf->menu->nitems)
847 cf->menu->page_sz = (cf->menu->nitems / (ui_lines - 4)) ? ui_lines - 4 : 0;
848 }
849
850 static void menu_update_ignore(struct cfdisk *cf)
851 {
852 char ignore[128] = { 0 };
853 int i = 0;
854 struct cfdisk_menu *m;
855 struct cfdisk_menuitem *d, *org = NULL;
856 size_t idx;
857
858 assert(cf);
859 assert(cf->menu);
860 assert(cf->menu->ignore_cb);
861
862 m = cf->menu;
863 DBG(MENU, ul_debug("update menu ignored keys"));
864
865 i = m->ignore_cb(cf, ignore, sizeof(ignore));
866 ignore[i] = '\0';
867
868 /* return if no change */
869 if ((!m->ignore && !*ignore)
870 || (m->ignore && *ignore && strcmp(m->ignore, ignore) == 0)) {
871 return;
872 }
873
874 if (!m->prefkey)
875 org = menu_get_menuitem(cf, m->idx);
876
877 free(m->ignore);
878 m->ignore = xstrdup(ignore);
879 m->nitems = 0;
880
881 for (d = m->items; d->name; d++) {
882 if (m->ignore && strchr(m->ignore, d->key))
883 continue;
884 m->nitems++;
885 }
886
887 DBG(MENU, ul_debug("update menu preferred keys"));
888
889 /* refresh menu index to be at the same menuitem or go to the first */
890 if (org && menu_get_menuitem_by_key(cf, org->key, &idx))
891 m->idx = idx;
892 else if (m->prefkey && menu_get_menuitem_by_key(cf, m->prefkey, &idx))
893 m->idx = idx;
894 else
895 m->idx = 0;
896
897 menu_refresh_size(cf);
898 }
899
900 static struct cfdisk_menu *menu_push(
901 struct cfdisk *cf,
902 struct cfdisk_menuitem *items)
903 {
904 struct cfdisk_menu *m = xcalloc(1, sizeof(*m));
905 struct cfdisk_menuitem *d;
906
907 assert(cf);
908
909 DBG(MENU, ul_debug("new menu"));
910
911 m->prev = cf->menu;
912 m->items = items;
913
914 for (d = m->items; d->name; d++) {
915 const char *name = _(d->name);
916 size_t len = mbs_safe_width(name);
917 if (len > m->width)
918 m->width = len;
919 m->nitems++;
920 }
921
922 cf->menu = m;
923
924 menu_refresh_size(cf);
925 return m;
926 }
927
928 static struct cfdisk_menu *menu_pop(struct cfdisk *cf)
929 {
930 struct cfdisk_menu *m = NULL;
931
932 assert(cf);
933
934 DBG(MENU, ul_debug("pop menu"));
935
936 if (cf->menu) {
937 m = cf->menu->prev;
938 free(cf->menu->ignore);
939 free(cf->menu->title);
940 free(cf->menu);
941 }
942 cf->menu = m;
943 return cf->menu;
944 }
945
946 static void menu_set_title(struct cfdisk_menu *m, const char *title)
947 {
948 char *str = NULL;
949
950 if (title) {
951 size_t len = mbs_safe_width(title);
952 if (len + MENU_TITLE_PADDING > m->width)
953 m->width = len + MENU_TITLE_PADDING;
954 str = xstrdup(title);
955 }
956 m->title = str;
957 }
958
959
960 static int ui_init(struct cfdisk *cf __attribute__((__unused__)))
961 {
962 struct sigaction sa;
963
964 DBG(UI, ul_debug("init"));
965
966 /* setup SIGCHLD handler */
967 sigemptyset(&sa.sa_mask);
968 sa.sa_flags = 0;
969 sa.sa_handler = sig_handler_die;
970 sigaction(SIGINT, &sa, NULL);
971 sigaction(SIGTERM, &sa, NULL);
972
973 sa.sa_handler = sig_handler_resize;
974 sigaction(SIGWINCH, &sa, NULL);
975
976 ui_enabled = 1;
977 initscr();
978
979 #ifdef HAVE_USE_DEFAULT_COLORS
980 if (colors_wanted() && has_colors()) {
981 size_t i;
982
983 start_color();
984 use_default_colors();
985 for (i = 1; i < ARRAY_SIZE(color_pairs); i++) /* yeah, start from 1! */
986 init_pair(i, color_pairs[i][0], color_pairs[i][1]);
987 }
988 #else
989 colors_off();
990 #endif
991
992 cbreak();
993 noecho();
994 nonl();
995 curs_set(0);
996 keypad(stdscr, TRUE);
997
998 return 0;
999 }
1000
1001 /* "[ string ]" */
1002 #define MENU_H_ITEMWIDTH(m) ( MENU_H_PRESTR_SZ \
1003 + MENU_H_SPADDING \
1004 + (m)->width \
1005 + MENU_H_SPADDING \
1006 + MENU_H_POSTSTR_SZ)
1007
1008 #define MENU_V_ITEMWIDTH(m) (MENU_V_SPADDING + (m)->width + MENU_V_SPADDING)
1009
1010
1011 static size_t menuitem_get_line(struct cfdisk *cf, size_t idx)
1012 {
1013 struct cfdisk_menu *m = cf->menu;
1014
1015 if (m->vertical) {
1016 if (!m->page_sz) /* small menu */
1017 return (ui_lines - (cf->menu->nitems + 1)) / 2 + idx;
1018 return (idx % m->page_sz) + 1;
1019 }
1020
1021 {
1022 size_t len = MENU_H_ITEMWIDTH(m) + MENU_H_BETWEEN; /** item width */
1023 size_t items = ui_cols / len; /* items per line */
1024
1025 if (items == 0)
1026 return 0;
1027 return MENU_START_LINE + ((idx / items));
1028 }
1029 }
1030
1031 static int menuitem_get_column(struct cfdisk *cf, size_t idx)
1032 {
1033 if (cf->menu->vertical) {
1034 size_t nc = MENU_V_ITEMWIDTH(cf->menu);
1035 if ((size_t) ui_cols <= nc)
1036 return 0;
1037 return (ui_cols - nc) / 2;
1038 }
1039
1040 {
1041 size_t len = MENU_H_ITEMWIDTH(cf->menu) + MENU_H_BETWEEN; /* item width */
1042 size_t items = ui_cols / len; /* items per line */
1043 size_t extra = items < cf->menu->nitems ? /* extra space on line */
1044 ui_cols % len : /* - multi-line menu */
1045 ui_cols - (cf->menu->nitems * len); /* - one line menu */
1046
1047 if (items == 0)
1048 return 0; /* hmm... no space */
1049
1050 extra += MENU_H_BETWEEN; /* add padding after last item to extra */
1051
1052 if (idx < items)
1053 return (idx * len) + (extra / 2);
1054 return ((idx % items) * len) + (extra / 2);
1055 }
1056 }
1057
1058 static int menuitem_on_page(struct cfdisk *cf, size_t idx)
1059 {
1060 struct cfdisk_menu *m = cf->menu;
1061
1062 if (m->page_sz == 0 ||
1063 m->idx / m->page_sz == idx / m->page_sz)
1064 return 1;
1065 return 0;
1066 }
1067
1068 static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx)
1069 {
1070 struct cfdisk_menuitem *d;
1071 size_t i;
1072
1073 for (i = 0, d = cf->menu->items; d->name; d++) {
1074 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
1075 continue;
1076 if (i++ == idx)
1077 return d;
1078 }
1079
1080 return NULL;
1081 }
1082
1083 static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf,
1084 int key, size_t *idx)
1085 {
1086 struct cfdisk_menuitem *d;
1087
1088 for (*idx = 0, d = cf->menu->items; d->name; d++) {
1089 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
1090 continue;
1091 if (key == d->key)
1092 return d;
1093 (*idx)++;
1094 }
1095
1096 return NULL;
1097 }
1098
1099 static void ui_draw_menuitem(struct cfdisk *cf,
1100 struct cfdisk_menuitem *d,
1101 size_t idx)
1102 {
1103 char *buf, *ptr;
1104 const char *name;
1105 size_t width;
1106 const size_t buf_sz = 80 * MB_CUR_MAX;
1107 int ln, cl, vert = cf->menu->vertical;
1108
1109 if (!menuitem_on_page(cf, idx))
1110 return; /* no visible item */
1111 ln = menuitem_get_line(cf, idx);
1112 cl = menuitem_get_column(cf, idx);
1113
1114 ptr = buf = xmalloc(buf_sz);
1115 /* string width */
1116 if (vert) {
1117 width = cf->menu->width + MENU_V_SPADDING;
1118 memset(ptr, ' ', MENU_V_SPADDING);
1119 ptr += MENU_V_SPADDING;
1120 } else
1121 width = MENU_H_SPADDING + cf->menu->width + MENU_H_SPADDING;
1122
1123 name = _(d->name);
1124 mbsalign(name, ptr, buf_sz, &width,
1125 vert ? MBS_ALIGN_LEFT : MBS_ALIGN_CENTER,
1126 0);
1127
1128 DBG(MENU, ul_debug("menuitem: cl=%d, ln=%d, item='%s'",
1129 cl, ln, buf));
1130
1131 if (vert) {
1132 mvaddch(ln, cl - 1, ACS_VLINE);
1133 mvaddch(ln, cl + MENU_V_ITEMWIDTH(cf->menu), ACS_VLINE);
1134 }
1135
1136 if (cf->menu->idx == idx)
1137 standout();
1138
1139 if (vert)
1140 mvprintw(ln, cl, "%s", buf);
1141 else
1142 mvprintw(ln, cl, "%s%s%s", MENU_H_PRESTR, buf, MENU_H_POSTSTR);
1143 free(buf);
1144
1145 if (cf->menu->idx == idx) {
1146 standend();
1147 if (d->desc)
1148 ui_hint("%s", _(d->desc));
1149 }
1150 }
1151
1152 static void ui_clean_menu(struct cfdisk *cf)
1153 {
1154 size_t i;
1155 size_t lastline;
1156 struct cfdisk_menu *m = cf->menu;
1157 size_t ln = menuitem_get_line(cf, 0);
1158
1159 if (m->vertical)
1160 lastline = ln + (m->page_sz ? m->page_sz : m->nitems);
1161 else
1162 lastline = menuitem_get_line(cf, m->nitems);
1163
1164 for (i = ln; i <= lastline; i++) {
1165 move(i, 0);
1166 clrtoeol();
1167 DBG(MENU, ul_debug("clean_menu: line %zu", i));
1168 }
1169 if (m->vertical) {
1170 move(ln - 1, 0);
1171 clrtoeol();
1172 }
1173 ui_clean_hint();
1174 }
1175
1176 static void ui_draw_menu(struct cfdisk *cf)
1177 {
1178 struct cfdisk_menuitem *d;
1179 struct cfdisk_menu *m;
1180 size_t i = 0;
1181 size_t ln = menuitem_get_line(cf, 0);
1182 size_t nlines;
1183
1184 assert(cf);
1185 assert(cf->menu);
1186
1187 DBG(MENU, ul_debug("draw start"));
1188
1189 ui_clean_menu(cf);
1190 m = cf->menu;
1191
1192 if (m->vertical)
1193 nlines = m->page_sz ? m->page_sz : m->nitems;
1194 else
1195 nlines = menuitem_get_line(cf, m->nitems);
1196
1197 if (m->ignore_cb)
1198 menu_update_ignore(cf);
1199 i = 0;
1200 while ((d = menu_get_menuitem(cf, i)))
1201 ui_draw_menuitem(cf, d, i++);
1202
1203 if (m->vertical) {
1204 size_t cl = menuitem_get_column(cf, 0);
1205 size_t curpg = m->page_sz ? m->idx / m->page_sz : 0;
1206
1207 /* corners and horizontal lines */
1208 mvaddch(ln - 1, cl - 1, ACS_ULCORNER);
1209 mvaddch(ln + nlines, cl - 1, ACS_LLCORNER);
1210
1211 for (i = 0; i < MENU_V_ITEMWIDTH(m); i++) {
1212 mvaddch(ln - 1, cl + i, ACS_HLINE);
1213 mvaddch(ln + nlines, cl + i, ACS_HLINE);
1214 }
1215
1216 mvaddch(ln - 1, cl + i, ACS_URCORNER);
1217 mvaddch(ln + nlines, cl + i, ACS_LRCORNER);
1218
1219 /* draw also lines around empty lines on last page */
1220 if (m->page_sz &&
1221 m->nitems / m->page_sz == m->idx / m->page_sz) {
1222 for (i = m->nitems % m->page_sz + 1; i <= m->page_sz; i++) {
1223 mvaddch(i, cl - 1, ACS_VLINE);
1224 mvaddch(i, cl + MENU_V_ITEMWIDTH(m), ACS_VLINE);
1225 }
1226 }
1227 if (m->title) {
1228 attron(A_BOLD);
1229 mvprintw(ln - 1, cl, " %s ", m->title);
1230 attroff(A_BOLD);
1231 }
1232 if (curpg != 0)
1233 mvaddch(ln - 1, cl + MENU_V_ITEMWIDTH(m) - 2, ACS_UARROW);
1234 if (m->page_sz && curpg < m->nitems / m->page_sz)
1235 mvaddch(ln + nlines, cl + MENU_V_ITEMWIDTH(m) - 2, ACS_DARROW);
1236 }
1237
1238 DBG(MENU, ul_debug("draw end."));
1239 }
1240
1241 inline static int extra_insert_pair(struct cfdisk_line *l, const char *name, const char *data)
1242 {
1243 struct libscols_line *lsl;
1244 int rc;
1245
1246 assert(l);
1247 assert(l->extra);
1248
1249 if (!data || !*data)
1250 return 0;
1251
1252 lsl = scols_table_new_line(l->extra, NULL);
1253 if (!lsl)
1254 return -ENOMEM;
1255
1256 rc = scols_line_set_data(lsl, 0, name);
1257 if (!rc)
1258 rc = scols_line_set_data(lsl, 1, data);
1259
1260 return rc;
1261 }
1262
1263 #ifndef HAVE_LIBMOUNT
1264 static char *get_mountpoint( struct cfdisk *cf __attribute__((unused)),
1265 const char *tagname __attribute__((unused)),
1266 const char *tagdata __attribute__((unused)))
1267 {
1268 return NULL;
1269 }
1270 #else
1271 static char *get_mountpoint(struct cfdisk *cf, const char *tagname, const char *tagdata)
1272 {
1273 struct libmnt_fs *fs = NULL;
1274 char *target = NULL;
1275 int mounted = 0;
1276
1277 assert(tagname);
1278 assert(tagdata);
1279
1280 DBG(UI, ul_debug("asking for mountpoint [%s=%s]", tagname, tagdata));
1281
1282 if (!cf->mntcache)
1283 cf->mntcache = mnt_new_cache();
1284
1285 /* 1st try between mounted filesystems */
1286 if (!cf->mtab) {
1287 cf->mtab = mnt_new_table();
1288 if (cf->mtab) {
1289 mnt_table_set_cache(cf->mtab, cf->mntcache);
1290 mnt_table_parse_mtab(cf->mtab, NULL);
1291 }
1292 }
1293
1294 if (cf->mtab)
1295 fs = mnt_table_find_tag(cf->mtab, tagname, tagdata, MNT_ITER_FORWARD);
1296
1297 /* 2nd try fstab */
1298 if (!fs) {
1299 if (!cf->fstab) {
1300 cf->fstab = mnt_new_table();
1301 if (cf->fstab) {
1302 mnt_table_set_cache(cf->fstab, cf->mntcache);
1303 if (mnt_table_parse_fstab(cf->fstab, NULL) != 0) {
1304 mnt_unref_table(cf->fstab);
1305 cf->fstab = NULL;
1306 }
1307 }
1308 }
1309 if (cf->fstab)
1310 fs = mnt_table_find_tag(cf->fstab, tagname, tagdata, MNT_ITER_FORWARD);
1311 } else
1312 mounted = 1;
1313
1314 if (fs) {
1315 if (mounted)
1316 xasprintf(&target, _("%s (mounted)"), mnt_fs_get_target(fs));
1317 else
1318 target = xstrdup(mnt_fs_get_target(fs));
1319 }
1320
1321 return target;
1322 }
1323 #endif /* HAVE_LIBMOUNT */
1324
1325 static inline int iszero(const char *str)
1326 {
1327 const char *p;
1328
1329 for (p = str; p && *p == '0'; p++);
1330
1331 return !p || *p == '\0';
1332 }
1333
1334 static int has_uuid(struct fdisk_table *tb, const char *uuid)
1335 {
1336 struct fdisk_partition *pa;
1337 struct fdisk_iter *itr;
1338 int rc = 0;
1339
1340 if (!tb || !uuid || fdisk_table_is_empty(tb))
1341 return 0;
1342
1343 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
1344 while (rc == 0 && fdisk_table_next_partition(tb, itr, &pa) == 0) {
1345 const char *x = fdisk_partition_get_uuid(pa);
1346 if (x)
1347 rc = strcmp(x, uuid) == 0;
1348 }
1349 fdisk_free_iter(itr);
1350 return rc;
1351 }
1352
1353 static void extra_prepare_data(struct cfdisk *cf)
1354 {
1355 struct fdisk_partition *pa = get_current_partition(cf);
1356 struct cfdisk_line *l = &cf->lines[cf->lines_idx];
1357 char *data = NULL;
1358 char *mountpoint = NULL;
1359
1360 DBG(UI, ul_debug("preparing extra data"));
1361
1362 /* string data should not equal an empty string */
1363 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_NAME, &data) && data) {
1364 extra_insert_pair(l, _("Partition name:"), data);
1365 if (!mountpoint)
1366 mountpoint = get_mountpoint(cf, "PARTLABEL", data);
1367 free(data);
1368 }
1369
1370 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_UUID, &data) && data) {
1371 extra_insert_pair(l, _("Partition UUID:"), data);
1372
1373 /* Search for mountpoint by PARTUUID= means that we need to
1374 * check fstab and convert PARTUUID to the device name. This is
1375 * unnecessary and overkill for newly created partitions. Let's
1376 * check if the UUID already exist in the old layout, otherwise
1377 * ignore it.
1378 */
1379 if (!mountpoint && has_uuid(cf->original_layout, data))
1380 mountpoint = get_mountpoint(cf, "PARTUUID", data);
1381 free(data);
1382 }
1383
1384 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_TYPE, &data) && data) {
1385 char *code = NULL, *type = NULL;
1386
1387 fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_TYPEID, &code);
1388 xasprintf(&type, "%s (%s)", data, code);
1389
1390 extra_insert_pair(l, _("Partition type:"), type);
1391 free(data);
1392 free(code);
1393 free(type);
1394 }
1395
1396 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_ATTR, &data) && data) {
1397 extra_insert_pair(l, _("Attributes:"), data);
1398 free(data);
1399 }
1400
1401 /* for numeric data, only show non-zero rows */
1402 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_BSIZE, &data) && data) {
1403 if (!iszero(data))
1404 extra_insert_pair(l, "BSIZE:", data);
1405 free(data);
1406 }
1407
1408 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_CPG, &data) && data) {
1409 if (!iszero(data))
1410 extra_insert_pair(l, "CPG:", data);
1411 free(data);
1412 }
1413
1414 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSIZE, &data) && data) {
1415 if (!iszero(data))
1416 extra_insert_pair(l, "FSIZE:", data);
1417 free(data);
1418 }
1419
1420 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSUUID, &data) && data) {
1421 extra_insert_pair(l, _("Filesystem UUID:"), data);
1422 if (!mountpoint)
1423 mountpoint = get_mountpoint(cf, "UUID", data);
1424 free(data);
1425 }
1426
1427 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSLABEL, &data) && data) {
1428 extra_insert_pair(l, _("Filesystem LABEL:"), data);
1429 if (!mountpoint)
1430 mountpoint = get_mountpoint(cf, "LABEL", data);
1431 free(data);
1432 }
1433 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSTYPE, &data) && data) {
1434 extra_insert_pair(l, _("Filesystem:"), data);
1435 free(data);
1436 }
1437
1438 if (mountpoint) {
1439 extra_insert_pair(l, _("Mountpoint:"), mountpoint);
1440 free(mountpoint);
1441 }
1442 }
1443
1444 static int ui_draw_extra(struct cfdisk *cf)
1445 {
1446 WINDOW *win_ex;
1447 int wline = 1;
1448 struct cfdisk_line *ln = &cf->lines[cf->lines_idx];
1449 char *tbstr = NULL, *end;
1450 int win_ex_start_line, win_height, tblen;
1451 int ndatalines;
1452
1453 if (!cf->show_extra)
1454 return 0;
1455
1456 DBG(UI, ul_debug("draw extra"));
1457
1458 assert(ln->extra);
1459
1460 if (cf->act_win) {
1461 wclear(cf->act_win);
1462 touchwin(stdscr);
1463 }
1464
1465 if (scols_table_is_empty(ln->extra)) {
1466 extra_prepare_data(cf);
1467 if (scols_table_is_empty(ln->extra))
1468 return 0;
1469 }
1470
1471 ndatalines = fdisk_table_get_nents(cf->table) + 1;
1472
1473 /* nents + header + one free line */
1474 win_ex_start_line = TABLE_START_LINE + ndatalines;
1475 win_height = MENU_START_LINE - win_ex_start_line;
1476 tblen = scols_table_get_nlines(ln->extra);
1477
1478 /* we can't get a single line of data under the partlist*/
1479 if (win_height < 3)
1480 return 1;
1481
1482 /* number of data lines + 2 for top/bottom lines */
1483 win_height = win_height < tblen + 2 ? win_height : tblen + 2;
1484
1485 if ((size_t) win_ex_start_line + win_height + 1 < MENU_START_LINE)
1486 win_ex_start_line = MENU_START_LINE - win_height;
1487
1488 win_ex = subwin(stdscr, win_height, ui_cols - 2, win_ex_start_line, 1);
1489
1490 scols_table_reduce_termwidth(ln->extra, 4);
1491 scols_print_table_to_string(ln->extra, &tbstr);
1492
1493 end = tbstr;
1494 while ((end = strchr(end, '\n')))
1495 *end++ = '\0';
1496
1497 box(win_ex, 0, 0);
1498
1499 end = tbstr;
1500 while (--win_height > 1) {
1501 mvwaddstr(win_ex, wline++, 1 /* window column*/, tbstr);
1502 tbstr += strlen(tbstr) + 1;
1503 }
1504 free(end);
1505
1506 if (ln->w)
1507 delwin(ln->w);
1508
1509 DBG(UI, ul_debug("draw window: %p", win_ex));
1510 touchwin(stdscr);
1511 wrefresh(win_ex);
1512
1513 cf->act_win = ln->w = win_ex;
1514 return 0;
1515 }
1516
1517 static void ui_menu_goto(struct cfdisk *cf, int where)
1518 {
1519 struct cfdisk_menuitem *d;
1520 size_t old;
1521
1522 /* stop and begin/end for vertical menus */
1523 if (cf->menu->vertical) {
1524 if (where < 0)
1525 where = 0;
1526 else if (where > (int) cf->menu->nitems - 1)
1527 where = cf->menu->nitems - 1;
1528 } else {
1529 /* continue from begin/end */
1530 if (where < 0)
1531 where = cf->menu->nitems - 1;
1532 else if ((size_t) where > cf->menu->nitems - 1)
1533 where = 0;
1534 }
1535 if ((size_t) where == cf->menu->idx)
1536 return;
1537
1538 ui_clean_info();
1539
1540 old = cf->menu->idx;
1541 cf->menu->idx = where;
1542
1543 if (!menuitem_on_page(cf, old)) {
1544 ui_draw_menu(cf);
1545 return;
1546 }
1547
1548 d = menu_get_menuitem(cf, old);
1549 ui_draw_menuitem(cf, d, old);
1550
1551 d = menu_get_menuitem(cf, where);
1552 ui_draw_menuitem(cf, d, where);
1553
1554 }
1555
1556 static int ui_menu_move(struct cfdisk *cf, int key)
1557 {
1558 struct cfdisk_menu *m;
1559
1560 assert(cf);
1561 assert(cf->menu);
1562
1563 if (key == (int) ERR)
1564 return 0; /* ignore errors */
1565
1566 m = cf->menu;
1567
1568 DBG(MENU, ul_debug("menu move key >%c<.", key));
1569
1570 if (m->vertical)
1571 {
1572 switch (key) {
1573 case KEY_DOWN:
1574 case '\016': /* ^N */
1575 case 'j': /* Vi-like alternative */
1576 ui_menu_goto(cf, m->idx + 1);
1577 return 0;
1578 case KEY_UP:
1579 case '\020': /* ^P */
1580 case 'k': /* Vi-like alternative */
1581 ui_menu_goto(cf, (int) m->idx - 1);
1582 return 0;
1583 case KEY_PPAGE:
1584 if (m->page_sz) {
1585 ui_menu_goto(cf, (int) m->idx - m->page_sz);
1586 return 0;
1587 }
1588 /* fallthrough */
1589 case KEY_HOME:
1590 ui_menu_goto(cf, 0);
1591 return 0;
1592 case KEY_NPAGE:
1593 if (m->page_sz) {
1594 ui_menu_goto(cf, m->idx + m->page_sz);
1595 return 0;
1596 }
1597 /* fallthrough */
1598 case KEY_END:
1599 ui_menu_goto(cf, m->nitems);
1600 return 0;
1601 }
1602 } else {
1603 switch (key) {
1604 case KEY_RIGHT:
1605 case '\t':
1606 ui_menu_goto(cf, m->idx + 1);
1607 return 0;
1608 case KEY_LEFT:
1609 #ifdef KEY_BTAB
1610 case KEY_BTAB:
1611 #endif
1612 ui_menu_goto(cf, (int) m->idx - 1);
1613 return 0;
1614 }
1615 }
1616
1617 if (key == '\014') { /* ^L refresh */
1618 ui_menu_resize(cf);
1619 return 0;
1620 }
1621
1622 DBG(MENU, ul_debug(" no menu move key"));
1623 return 1;
1624 }
1625
1626 /* but don't call me from ui_run(), this is for pop-up menus only */
1627 static void ui_menu_resize(struct cfdisk *cf)
1628 {
1629 DBG(MENU, ul_debug("menu resize/refresh"));
1630 resize();
1631 ui_clean_menu(cf);
1632 menu_refresh_size(cf);
1633 ui_draw_menu(cf);
1634 refresh();
1635 }
1636
1637 static int partition_on_page(struct cfdisk *cf, size_t i)
1638 {
1639 if (cf->page_sz == 0 ||
1640 cf->lines_idx / cf->page_sz == i / cf->page_sz)
1641 return 1;
1642 return 0;
1643 }
1644
1645 static void ui_draw_partition(struct cfdisk *cf, size_t i)
1646 {
1647 int ln = TABLE_START_LINE + 1 + i; /* skip table header */
1648 int cl = ARROW_CURSOR_WIDTH; /* we need extra space for cursor */
1649 int cur = cf->lines_idx == i;
1650 size_t curpg = 0;
1651
1652 if (cf->page_sz) {
1653 if (!partition_on_page(cf, i))
1654 return;
1655 ln = TABLE_START_LINE + (i % cf->page_sz) + 1;
1656 curpg = cf->lines_idx / cf->page_sz;
1657 }
1658
1659 DBG(UI, ul_debug(
1660 "draw partition %zu [page_sz=%zu, "
1661 "line=%d, idx=%zu]",
1662 i, cf->page_sz, ln, cf->lines_idx));
1663
1664 if (cur) {
1665 attron(A_REVERSE);
1666 mvaddstr(ln, 0, ARROW_CURSOR_STRING);
1667 mvaddstr(ln, cl, cf->lines[i + 1].data);
1668 attroff(A_REVERSE);
1669 } else {
1670 int at = 0;
1671
1672 if (colors_wanted() && is_freespace(cf, i)) {
1673 attron(COLOR_PAIR(CFDISK_CL_FREESPACE));
1674 at = 1;
1675 }
1676 mvaddstr(ln, 0, ARROW_CURSOR_DUMMY);
1677 mvaddstr(ln, cl, cf->lines[i + 1].data);
1678 if (at)
1679 attroff(COLOR_PAIR(CFDISK_CL_FREESPACE));
1680 }
1681
1682 if ((size_t) ln == MENU_START_LINE - 1 &&
1683 cf->page_sz && curpg < cf->nlines / cf->page_sz) {
1684 if (cur)
1685 attron(A_REVERSE);
1686 mvaddch(ln, ui_cols - 1, ACS_DARROW);
1687 mvaddch(ln, 0, ACS_DARROW);
1688 if (cur)
1689 attroff(A_REVERSE);
1690 }
1691
1692 }
1693
1694 static int ui_draw_table(struct cfdisk *cf)
1695 {
1696 int cl = ARROW_CURSOR_WIDTH;
1697 size_t i, nparts = fdisk_table_get_nents(cf->table);
1698 size_t curpg = cf->page_sz ? cf->lines_idx / cf->page_sz : 0;
1699
1700 DBG(UI, ul_debug("draw table"));
1701
1702 for (i = TABLE_START_LINE; i <= TABLE_START_LINE + cf->page_sz; i++) {
1703 move(i, 0);
1704 clrtoeol();
1705 }
1706
1707 if (nparts == 0 || (size_t) cf->lines_idx > nparts - 1)
1708 cf->lines_idx = nparts ? nparts - 1 : 0;
1709
1710 /* print header */
1711 attron(A_BOLD);
1712 mvaddstr(TABLE_START_LINE, cl, cf->lines[0].data);
1713 attroff(A_BOLD);
1714
1715 /* print partitions */
1716 for (i = 0; i < nparts; i++)
1717 ui_draw_partition(cf, i);
1718
1719 if (curpg != 0) {
1720 mvaddch(TABLE_START_LINE, ui_cols - 1, ACS_UARROW);
1721 mvaddch(TABLE_START_LINE, 0, ACS_UARROW);
1722 }
1723 if (cf->page_sz && curpg < cf->nlines / cf->page_sz) {
1724 mvaddch(MENU_START_LINE - 1, ui_cols - 1, ACS_DARROW);
1725 mvaddch(MENU_START_LINE - 1, 0, ACS_DARROW);
1726 }
1727 return 0;
1728 }
1729
1730 static int ui_table_goto(struct cfdisk *cf, int where)
1731 {
1732 size_t old;
1733 size_t nparts = fdisk_table_get_nents(cf->table);
1734
1735 DBG(UI, ul_debug("goto table %d", where));
1736
1737 if (where < 0)
1738 where = 0;
1739 else if ((size_t) where > nparts - 1)
1740 where = nparts - 1;
1741
1742 if ((size_t) where == cf->lines_idx)
1743 return 0;
1744
1745 old = cf->lines_idx;
1746 cf->lines_idx = where;
1747
1748 if (!partition_on_page(cf, old) ||!partition_on_page(cf, where))
1749 ui_draw_table(cf);
1750 else {
1751 ui_draw_partition(cf, old); /* cleanup old */
1752 ui_draw_partition(cf, where); /* draw new */
1753 }
1754 ui_clean_info();
1755 ui_draw_menu(cf);
1756 ui_draw_extra(cf);
1757 refresh();
1758
1759 return 0;
1760 }
1761
1762 static int ui_refresh(struct cfdisk *cf)
1763 {
1764 struct fdisk_label *lb;
1765 char *id = NULL;
1766 uint64_t bytes = fdisk_get_nsectors(cf->cxt) * fdisk_get_sector_size(cf->cxt);
1767 char *strsz;
1768
1769 if (!ui_enabled)
1770 return -EINVAL;
1771
1772 strsz = size_to_human_string(SIZE_DECIMAL_2DIGITS
1773 | SIZE_SUFFIX_SPACE
1774 | SIZE_SUFFIX_3LETTER, bytes);
1775
1776 lb = fdisk_get_label(cf->cxt, NULL);
1777 assert(lb);
1778
1779 clear();
1780
1781 /* header */
1782 attron(A_BOLD);
1783 ui_center(0, _("Disk: %s"), fdisk_get_devname(cf->cxt));
1784 attroff(A_BOLD);
1785 ui_center(1, _("Size: %s, %"PRIu64" bytes, %ju sectors"),
1786 strsz, bytes, (uintmax_t) fdisk_get_nsectors(cf->cxt));
1787 if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id)
1788 ui_center(2, _("Label: %s, identifier: %s"),
1789 fdisk_label_get_name(lb), id);
1790 else
1791 ui_center(2, _("Label: %s"), fdisk_label_get_name(lb));
1792 free(strsz);
1793 free(id);
1794
1795 ui_draw_table(cf);
1796 ui_draw_menu(cf);
1797 refresh();
1798 return 0;
1799 }
1800
1801 static ssize_t ui_get_string(const char *prompt,
1802 const char *hint, char *buf, size_t len)
1803 {
1804 int ln = MENU_START_LINE, cl = 1;
1805 ssize_t rc = -1;
1806 struct mbs_editor *edit;
1807
1808 DBG(UI, ul_debug("ui get string"));
1809
1810 assert(buf);
1811 assert(len);
1812
1813 move(ln, 0);
1814 clrtoeol();
1815
1816 move(ln + 1, 0);
1817 clrtoeol();
1818
1819 if (prompt) {
1820 mvaddstr(ln, cl, prompt);
1821 cl += mbs_safe_width(prompt);
1822 }
1823
1824 edit = mbs_new_edit(buf, len, ui_cols - cl);
1825 if (!edit)
1826 goto done;
1827
1828 mbs_edit_goto(edit, MBS_EDIT_END);
1829
1830 if (hint)
1831 ui_hint("%s", hint);
1832 else
1833 ui_clean_hint();
1834
1835 curs_set(1);
1836
1837 while (!sig_die) {
1838 wint_t c; /* we have fallback in widechar.h */
1839
1840 move(ln, cl);
1841 clrtoeol();
1842 mvaddstr(ln, cl, edit->buf);
1843 move(ln, cl + edit->cursor_cells);
1844 refresh();
1845
1846 #if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
1847 defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1848 if (get_wch(&c) == ERR) {
1849 #else
1850 if ((c = getch()) == (wint_t) ERR) {
1851 #endif
1852 if (sig_die)
1853 break;
1854 if (sig_resize) {
1855 resize();
1856 continue;
1857 }
1858 if (!isatty(STDIN_FILENO))
1859 exit(2);
1860 else
1861 goto done;
1862 }
1863
1864 DBG(UI, ul_debug("ui get string: key=%lc", c));
1865
1866 if (c == '\r' || c == '\n' || c == KEY_ENTER)
1867 break;
1868
1869 rc = 1;
1870
1871 switch (c) {
1872 case KEY_ESC:
1873 rc = -CFDISK_ERR_ESC;
1874 goto done;
1875 case KEY_LEFT:
1876 rc = mbs_edit_goto(edit, MBS_EDIT_LEFT);
1877 break;
1878 case KEY_RIGHT:
1879 rc = mbs_edit_goto(edit, MBS_EDIT_RIGHT);
1880 break;
1881 case KEY_END:
1882 rc = mbs_edit_goto(edit, MBS_EDIT_END);
1883 break;
1884 case KEY_HOME:
1885 rc = mbs_edit_goto(edit, MBS_EDIT_HOME);
1886 break;
1887 case KEY_UP:
1888 case KEY_DOWN:
1889 break;
1890 case KEY_DC:
1891 rc = mbs_edit_delete(edit);
1892 break;
1893 case '\b':
1894 case KEY_DELETE:
1895 case KEY_BACKSPACE:
1896 rc = mbs_edit_backspace(edit);
1897 break;
1898 default:
1899 rc = mbs_edit_insert(edit, c);
1900 break;
1901 }
1902 if (rc == 1)
1903 beep();
1904 }
1905
1906 if (sig_die)
1907 die_on_signal();
1908
1909 rc = strlen(edit->buf); /* success */
1910 done:
1911 move(ln, 0);
1912 clrtoeol();
1913 curs_set(0);
1914 refresh();
1915 mbs_free_edit(edit);
1916
1917 return rc;
1918 }
1919
1920 static int ui_get_size(struct cfdisk *cf, /* context */
1921 const char *prompt, /* UI dialog string */
1922 uint64_t *res, /* result in bytes */
1923 uint64_t low, /* minimal size */
1924 uint64_t up, /* maximal size */
1925 int *expsize) /* explicitly specified size */
1926 {
1927 char buf[128];
1928 uint64_t user = 0;
1929 ssize_t rc;
1930 char *dflt = size_to_human_string(0, *res);
1931
1932 DBG(UI, ul_debug("get_size (default=%"PRIu64")", *res));
1933
1934 ui_clean_info();
1935
1936 snprintf(buf, sizeof(buf), "%s", dflt);
1937
1938 do {
1939 int pwr = 0, insec = 0;
1940
1941 rc = ui_get_string(prompt,
1942 _("May be followed by M for MiB, G for GiB, "
1943 "T for TiB, or S for sectors."),
1944 buf, sizeof(buf));
1945 ui_clean_warn();
1946
1947 if (rc == 0) {
1948 ui_warnx(_("Please, specify size."));
1949 continue; /* nothing specified */
1950 } if (rc == -CFDISK_ERR_ESC)
1951 break; /* cancel dialog */
1952
1953 if (strcmp(buf, dflt) == 0)
1954 user = *res, rc = 0; /* no change, use default */
1955 else {
1956 size_t len = strlen(buf);
1957 if (buf[len - 1] == 'S' || buf[len - 1] == 's') {
1958 insec = 1;
1959 buf[len - 1] = '\0';
1960 }
1961 rc = parse_size(buf, (uintmax_t *)&user, &pwr); /* parse */
1962 }
1963
1964 if (rc == 0) {
1965 DBG(UI, ul_debug("get_size user=%"PRIu64", power=%d, in-sectors=%s",
1966 user, pwr, insec ? "yes" : "no"));
1967 if (insec)
1968 user *= fdisk_get_sector_size(cf->cxt);
1969 if (user < low) {
1970 ui_warnx(_("Minimum size is %"PRIu64" bytes."), low);
1971 rc = -ERANGE;
1972 }
1973 if (user > up && pwr && user < up + (1ULL << pwr * 10))
1974 /* ignore when the user specified size overflow
1975 * with in range specified by suffix (e.g. MiB) */
1976 user = up;
1977
1978 if (user > up) {
1979 ui_warnx(_("Maximum size is %"PRIu64" bytes."), up);
1980 rc = -ERANGE;
1981 }
1982 if (rc == 0 && insec && expsize)
1983 *expsize = 1;
1984
1985 } else
1986 ui_warnx(_("Failed to parse size."));
1987 } while (rc != 0);
1988
1989 if (rc == 0)
1990 *res = user;
1991 free(dflt);
1992
1993 DBG(UI, ul_debug("get_size (result=%"PRIu64", rc=%zd)", *res, rc));
1994 return rc;
1995 }
1996
1997 static struct fdisk_parttype *ui_get_parttype(struct cfdisk *cf,
1998 struct fdisk_parttype *cur)
1999 {
2000 struct cfdisk_menuitem *d, *cm;
2001 size_t i = 0, nitems, idx = 0;
2002 struct fdisk_parttype *t = NULL;
2003 struct fdisk_label *lb;
2004 int codetypes = 0;
2005
2006 DBG(UI, ul_debug("asking for parttype."));
2007
2008 lb = fdisk_get_label(cf->cxt, NULL);
2009
2010 /* create cfdisk menu according to label types, note that the
2011 * last cm[] item has to be empty -- so nitems + 1 */
2012 nitems = fdisk_label_get_nparttypes(lb);
2013 if (!nitems)
2014 return NULL;
2015
2016 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
2017 if (!cm)
2018 return NULL;
2019
2020 codetypes = fdisk_label_has_code_parttypes(lb);
2021
2022 for (i = 0; i < nitems; i++) {
2023 const struct fdisk_parttype *x = fdisk_label_get_parttype(lb, i);
2024 char *name;
2025
2026 cm[i].userdata = (void *) x;
2027 if (codetypes)
2028 xasprintf(&name, "%2x %s",
2029 fdisk_parttype_get_code(x),
2030 _(fdisk_parttype_get_name(x)));
2031 else {
2032 name = (char *) _(fdisk_parttype_get_name(x));
2033 cm[i].desc = fdisk_parttype_get_string(x);
2034 }
2035 cm[i].name = name;
2036 if (x == cur)
2037 idx = i;
2038 }
2039
2040 /* make the new menu active */
2041 menu_push(cf, cm);
2042 cf->menu->vertical = 1;
2043 cf->menu->idx = idx;
2044 menu_set_title(cf->menu, _("Select partition type"));
2045 ui_draw_menu(cf);
2046 refresh();
2047
2048 while (!sig_die) {
2049 int key = getch();
2050
2051 if (sig_die)
2052 break;
2053 if (sig_resize)
2054 ui_menu_resize(cf);
2055 if (ui_menu_move(cf, key) == 0)
2056 continue;
2057
2058 switch (key) {
2059 case KEY_ENTER:
2060 case '\n':
2061 case '\r':
2062 d = menu_get_menuitem(cf, cf->menu->idx);
2063 if (d)
2064 t = (struct fdisk_parttype *) d->userdata;
2065 goto done;
2066 case KEY_ESC:
2067 case 'q':
2068 case 'Q':
2069 goto done;
2070 }
2071 }
2072
2073 if (sig_die)
2074 die_on_signal();
2075 done:
2076 menu_pop(cf);
2077 if (codetypes) {
2078 for (i = 0; i < nitems; i++)
2079 free((char *) cm[i].name);
2080 }
2081 free(cm);
2082 DBG(UI, ul_debug("get parrtype done [type=%s] ", t ?
2083 fdisk_parttype_get_name(t) : NULL));
2084 return t;
2085 }
2086
2087 static int ui_script_read(struct cfdisk *cf)
2088 {
2089 struct fdisk_script *sc = NULL;
2090 char buf[PATH_MAX] = { 0 };
2091 int rc;
2092
2093 erase();
2094 rc = ui_get_string( _("Enter script file name: "),
2095 _("The script file will be applied to in-memory partition table."),
2096 buf, sizeof(buf));
2097 if (rc <= 0)
2098 return rc;
2099
2100 rc = -1;
2101 errno = 0;
2102 sc = fdisk_new_script_from_file(cf->cxt, buf);
2103 if (!sc && errno)
2104 ui_warn(_("Cannot open %s"), buf);
2105 else if (!sc)
2106 ui_warnx(_("Failed to parse script file %s"), buf);
2107 else if (fdisk_apply_script(cf->cxt, sc) != 0)
2108 ui_warnx(_("Failed to apply script %s"), buf);
2109 else
2110 rc = 0;
2111
2112 ui_clean_hint();
2113 fdisk_unref_script(sc);
2114 return rc;
2115 }
2116
2117 static int ui_script_write(struct cfdisk *cf)
2118 {
2119 struct fdisk_script *sc = NULL;
2120 char buf[PATH_MAX] = { 0 };
2121 FILE *f = NULL;
2122 int rc;
2123
2124 rc = ui_get_string( _("Enter script file name: "),
2125 _("The current in-memory partition table will be dumped to the file."),
2126 buf, sizeof(buf));
2127 if (rc <= 0)
2128 return rc;
2129
2130 rc = 0;
2131 sc = fdisk_new_script(cf->cxt);
2132 if (!sc) {
2133 ui_warn(_("Failed to allocate script handler"));
2134 goto done;
2135 }
2136
2137 rc = fdisk_script_read_context(sc, NULL);
2138 if (rc) {
2139 ui_warnx(_("Failed to read disk layout into script."));
2140 goto done;
2141 }
2142
2143 DBG(UI, ul_debug("writing dump into: '%s'", buf));
2144 f = fopen(buf, "w");
2145 if (!f) {
2146 ui_warn(_("Cannot open %s"), buf);
2147 rc = -errno;
2148 goto done;
2149 }
2150
2151 rc = fdisk_script_write_file(sc, f);
2152 if (!rc)
2153 ui_info(_("Disk layout successfully dumped."));
2154 done:
2155 if (rc)
2156 ui_warn(_("Failed to write script %s"), buf);
2157 if (f)
2158 fclose(f);
2159 fdisk_unref_script(sc);
2160 return rc;
2161 }
2162
2163 /* prints menu with libfdisk labels and waits for users response */
2164 static int ui_create_label(struct cfdisk *cf)
2165 {
2166 struct cfdisk_menuitem *d, *cm;
2167 int rc = 1, refresh_menu = 1;
2168 size_t i = 0, nitems;
2169 struct fdisk_label *lb = NULL;
2170
2171 assert(cf);
2172
2173 DBG(UI, ul_debug("asking for new disklabe."));
2174
2175 /* create cfdisk menu according to libfdisk labels, note that the
2176 * last cm[] item has to be empty -- so nitems + 1 */
2177 nitems = fdisk_get_nlabels(cf->cxt);
2178 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
2179
2180 while (fdisk_next_label(cf->cxt, &lb) == 0) {
2181 if (fdisk_label_is_disabled(lb) ||
2182 fdisk_label_get_type(lb) == FDISK_DISKLABEL_BSD)
2183 continue;
2184 cm[i++].name = fdisk_label_get_name(lb);
2185 }
2186
2187 erase();
2188
2189 /* make the new menu active */
2190 menu_push(cf, cm);
2191 cf->menu->vertical = 1;
2192 menu_set_title(cf->menu, _("Select label type"));
2193
2194 if (!cf->zero_start)
2195 ui_info(_("Device does not contain a recognized partition table."));
2196
2197
2198 while (!sig_die) {
2199 int key;
2200
2201 if (refresh_menu) {
2202 ui_draw_menu(cf);
2203 ui_hint(_("Select a type to create a new label, press 'L' to load script file, 'Q' quits."));
2204 refresh();
2205 refresh_menu = 0;
2206 }
2207
2208 key = getch();
2209
2210 if (sig_die)
2211 break;
2212 if (sig_resize)
2213 ui_menu_resize(cf);
2214 if (ui_menu_move(cf, key) == 0)
2215 continue;
2216 switch (key) {
2217 case KEY_ENTER:
2218 case '\n':
2219 case '\r':
2220 d = menu_get_menuitem(cf, cf->menu->idx);
2221 if (d)
2222 rc = fdisk_create_disklabel(cf->cxt, d->name);
2223 goto done;
2224 case KEY_ESC:
2225 case 'q':
2226 case 'Q':
2227 goto done;
2228 case 'l':
2229 case 'L':
2230 rc = ui_script_read(cf);
2231 if (rc == 0)
2232 goto done;
2233 refresh_menu = 1;
2234 break;
2235 }
2236 }
2237
2238 if (sig_die)
2239 die_on_signal();
2240 done:
2241 menu_pop(cf);
2242 free(cm);
2243 DBG(UI, ul_debug("create label done [rc=%d] ", rc));
2244 return rc;
2245 }
2246
2247
2248 static int ui_help(void)
2249 {
2250 size_t i;
2251 static const char *help[] = {
2252 N_("This is cfdisk, a curses-based disk partitioning program."),
2253 N_("It lets you create, delete, and modify partitions on a block device."),
2254 " ",
2255 N_("Command Meaning"),
2256 N_("------- -------"),
2257 N_(" b Toggle bootable flag of the current partition"),
2258 N_(" d Delete the current partition"),
2259 N_(" h Print this screen"),
2260 N_(" n Create new partition from free space"),
2261 N_(" q Quit program without writing partition table"),
2262 N_(" r Reduce or enlarge the current partition"),
2263 N_(" s Fix partitions order (only when in disarray)"),
2264 N_(" t Change the partition type"),
2265 N_(" u Dump disk layout to sfdisk compatible script file"),
2266 N_(" W Write partition table to disk (you must enter uppercase W);"),
2267 N_(" since this might destroy data on the disk, you must either"),
2268 N_(" confirm or deny the write by entering 'yes' or 'no'"),
2269 N_(" x Display/hide extra information about a partition"),
2270 N_("Up Arrow Move cursor to the previous partition"),
2271 N_("Down Arrow Move cursor to the next partition"),
2272 N_("Left Arrow Move cursor to the previous menu item"),
2273 N_("Right Arrow Move cursor to the next menu item"),
2274 " ",
2275 N_("Note: All of the commands can be entered with either upper or lower"),
2276 N_("case letters (except for Write)."),
2277 " ",
2278 N_("Use lsblk(8) or partx(8) to see more details about the device."),
2279 " ",
2280 " ",
2281 "Copyright (C) 2014-2017 Karel Zak <kzak@redhat.com>"
2282 };
2283
2284 erase();
2285 for (i = 0; i < ARRAY_SIZE(help); i++)
2286 mvaddstr(i, 1, _(help[i]));
2287
2288 ui_info(_("Press a key to continue."));
2289
2290 getch();
2291
2292 if (sig_die)
2293 die_on_signal();
2294 return 0;
2295 }
2296
2297 /* TODO: use @sz, now 128bytes */
2298 static int main_menu_ignore_keys(struct cfdisk *cf, char *ignore,
2299 size_t sz __attribute__((__unused__)))
2300 {
2301 struct fdisk_partition *pa = get_current_partition(cf);
2302 size_t i = 0;
2303
2304 if (!pa)
2305 return 0;
2306 if (fdisk_partition_is_freespace(pa)) {
2307 ignore[i++] = 'd'; /* delete */
2308 ignore[i++] = 't'; /* set type */
2309 ignore[i++] = 'b'; /* set bootable */
2310 ignore[i++] = 'r'; /* resize */
2311 cf->menu->prefkey = 'n';
2312 } else {
2313 cf->menu->prefkey = 'q';
2314 ignore[i++] = 'n';
2315 if (!fdisk_is_label(cf->cxt, DOS) &&
2316 !fdisk_is_label(cf->cxt, SGI))
2317 ignore[i++] = 'b';
2318 }
2319
2320 if (!cf->wrong_order)
2321 ignore[i++] = 's';
2322
2323 if (fdisk_is_readonly(cf->cxt))
2324 ignore[i++] = 'W';
2325
2326 return i;
2327 }
2328
2329
2330 /* returns: error: < 0, success: 0, quit: 1 */
2331 static int main_menu_action(struct cfdisk *cf, int key)
2332 {
2333 size_t n;
2334 int ref = 0, rc, org_order = cf->wrong_order;
2335 const char *info = NULL, *warn = NULL;
2336 struct fdisk_partition *pa;
2337
2338 assert(cf);
2339 assert(cf->cxt);
2340 assert(cf->menu);
2341
2342 if (key == 0) {
2343 struct cfdisk_menuitem *d = menu_get_menuitem(cf, cf->menu->idx);
2344 if (!d)
2345 return 0;
2346 key = d->key;
2347
2348 } else if (key != 'w' && key != 'W')
2349 key = tolower(key); /* case insensitive except 'W'rite */
2350
2351 DBG(MENU, ul_debug("main menu action: key=%c", key));
2352
2353 if (cf->menu->ignore && strchr(cf->menu->ignore, key)) {
2354 DBG(MENU, ul_debug(" ignore '%c'", key));
2355 return 0;
2356 }
2357
2358 pa = get_current_partition(cf);
2359 if (!pa)
2360 return -EINVAL;
2361 n = fdisk_partition_get_partno(pa);
2362
2363 DBG(MENU, ul_debug("menu action on %p", pa));
2364 ui_clean_hint();
2365 ui_clean_info();
2366
2367 switch (key) {
2368 case 'b': /* Bootable flag */
2369 {
2370 int fl = fdisk_is_label(cf->cxt, DOS) ? DOS_FLAG_ACTIVE :
2371 fdisk_is_label(cf->cxt, SGI) ? SGI_FLAG_BOOT : 0;
2372
2373 if (fl && fdisk_toggle_partition_flag(cf->cxt, n, fl))
2374 warn = _("Could not toggle the flag.");
2375 else if (fl)
2376 ref = 1;
2377 break;
2378 }
2379 #ifdef KEY_DC
2380 case KEY_DC:
2381 #endif
2382 case 'd': /* Delete */
2383 if (fdisk_delete_partition(cf->cxt, n) != 0)
2384 warn = _("Could not delete partition %zu.");
2385 else
2386 info = _("Partition %zu has been deleted.");
2387 ref = 1;
2388 break;
2389 case 'h': /* Help */
2390 case '?':
2391 ui_help();
2392 ref = 1;
2393 break;
2394 case 'n': /* New */
2395 {
2396 uint64_t start, size, dflt_size, secs, max_size;
2397 struct fdisk_partition *npa; /* the new partition */
2398 int expsize = 0; /* size specified explicitly in sectors */
2399
2400 if (!fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
2401 return -EINVAL;
2402
2403 /* free space range */
2404 start = fdisk_partition_get_start(pa);
2405 size = max_size = dflt_size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cf->cxt);
2406
2407 if (ui_get_size(cf, _("Partition size: "), &size,
2408 fdisk_get_sector_size(cf->cxt),
2409 max_size, &expsize) == -CFDISK_ERR_ESC)
2410 break;
2411
2412 secs = size / fdisk_get_sector_size(cf->cxt);
2413
2414 npa = fdisk_new_partition();
2415 if (!npa)
2416 return -ENOMEM;
2417
2418 if (dflt_size == size) /* default is to fillin all free space */
2419 fdisk_partition_end_follow_default(npa, 1);
2420 else
2421 fdisk_partition_set_size(npa, secs);
2422
2423 if (expsize)
2424 fdisk_partition_size_explicit(pa, 1);
2425
2426 fdisk_partition_set_start(npa, start);
2427 fdisk_partition_partno_follow_default(npa, 1);
2428 /* add to disk label -- libfdisk will ask for missing details */
2429 rc = fdisk_add_partition(cf->cxt, npa, NULL);
2430 fdisk_unref_partition(npa);
2431 if (rc == 0)
2432 ref = 1;
2433 break;
2434 }
2435 case 'q': /* Quit */
2436 return 1;
2437 case 't': /* Type */
2438 {
2439 struct fdisk_parttype *t;
2440
2441 if (fdisk_partition_is_freespace(pa))
2442 return -EINVAL;
2443 t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
2444 t = ui_get_parttype(cf, t);
2445 ref = 1;
2446
2447 if (t && fdisk_set_partition_type(cf->cxt, n, t) == 0)
2448 info = _("Changed type of partition %zu.");
2449 else
2450 info = _("The type of partition %zu is unchanged.");
2451 break;
2452 }
2453 case 'r': /* resize */
2454 {
2455 struct fdisk_partition *npa, *next;
2456 uint64_t size, max_size, secs;
2457
2458 if (fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
2459 return -EINVAL;
2460
2461 size = fdisk_partition_get_size(pa);
2462
2463 /* is the next freespace? */
2464 next = fdisk_table_get_partition(cf->table, cf->lines_idx + 1);
2465 if (next && fdisk_partition_is_freespace(next))
2466 size += fdisk_partition_get_size(next);
2467
2468 size *= fdisk_get_sector_size(cf->cxt);
2469 max_size = size;
2470
2471 if (ui_get_size(cf, _("New size: "), &size,
2472 fdisk_get_sector_size(cf->cxt),
2473 max_size, NULL) == -CFDISK_ERR_ESC)
2474 break;
2475 secs = size / fdisk_get_sector_size(cf->cxt);
2476 npa = fdisk_new_partition();
2477 if (!npa)
2478 return -ENOMEM;
2479
2480 fdisk_partition_set_size(npa, secs);
2481
2482 rc = fdisk_set_partition(cf->cxt, n, npa);
2483 fdisk_unref_partition(npa);
2484 if (rc == 0) {
2485 ref = 1;
2486 info = _("Partition %zu resized.");
2487 }
2488 break;
2489 }
2490 case 's': /* Sort */
2491 if (cf->wrong_order) {
2492 fdisk_reorder_partitions(cf->cxt);
2493 ref = 1;
2494 }
2495 break;
2496 case 'u': /* dUmp */
2497 ui_script_write(cf);
2498 break;
2499 case 'W': /* Write */
2500 {
2501 char buf[64] = { 0 };
2502
2503 if (fdisk_is_readonly(cf->cxt)) {
2504 warn = _("Device is open in read-only mode.");
2505 break;
2506 }
2507
2508 rc = ui_get_string(
2509 _("Are you sure you want to write the partition "
2510 "table to disk? "),
2511 _("Type \"yes\" or \"no\", or press ESC to leave this dialog."),
2512 buf, sizeof(buf));
2513
2514 ref = 1;
2515 if (rc <= 0 || (strcasecmp(buf, "yes") != 0 &&
2516 strcasecmp(buf, _("yes")) != 0)) {
2517 info = _("Did not write partition table to disk.");
2518 break;
2519 }
2520 rc = fdisk_write_disklabel(cf->cxt);
2521 if (rc)
2522 warn = _("Failed to write disklabel.");
2523 else {
2524 if (cf->device_is_used)
2525 fdisk_reread_changes(cf->cxt, cf->original_layout);
2526 else
2527 fdisk_reread_partition_table(cf->cxt);
2528 info = _("The partition table has been altered.");
2529 }
2530 cf->nwrites++;
2531 break;
2532 }
2533 default:
2534 break;
2535 }
2536
2537 if (ref) {
2538 lines_refresh(cf);
2539 ui_refresh(cf);
2540 ui_draw_extra(cf);
2541 } else
2542 ui_draw_menu(cf);
2543
2544 ui_clean_hint();
2545
2546 if (warn)
2547 ui_warnx(warn, n + 1);
2548 else if (info)
2549 ui_info(info, n + 1);
2550 else if (key == 'n' && cf->wrong_order && org_order == 0)
2551 ui_info(_("Note that partition table entries are not in disk order now."));
2552
2553 return 0;
2554 }
2555
2556 static void ui_resize_refresh(struct cfdisk *cf)
2557 {
2558 DBG(UI, ul_debug("ui resize/refresh"));
2559 resize();
2560 menu_refresh_size(cf);
2561 lines_refresh(cf);
2562 ui_refresh(cf);
2563 ui_draw_extra(cf);
2564 }
2565
2566 static void toggle_show_extra(struct cfdisk *cf)
2567 {
2568 if (cf->show_extra && cf->act_win) {
2569 wclear(cf->act_win);
2570 touchwin(stdscr);
2571 }
2572 cf->show_extra = cf->show_extra ? 0 : 1;
2573
2574 if (cf->show_extra)
2575 ui_draw_extra(cf);
2576 DBG(MENU, ul_debug("extra: %s", cf->show_extra ? "ENABLED" : "DISABLED" ));
2577 }
2578
2579 static int ui_run(struct cfdisk *cf)
2580 {
2581 int rc = 0;
2582
2583 ui_lines = LINES;
2584 ui_cols = COLS;
2585 DBG(UI, ul_debug("start cols=%zu, lines=%zu", ui_cols, ui_lines));
2586
2587 if (fdisk_get_collision(cf->cxt)) {
2588 ui_warnx(_("Device already contains a %s signature; it will be removed by a write command."),
2589 fdisk_get_collision(cf->cxt));
2590 fdisk_enable_wipe(cf->cxt, 1);
2591 ui_hint(_("Press a key to continue."));
2592 getch();
2593 }
2594
2595 if (!fdisk_has_label(cf->cxt) || cf->zero_start) {
2596 rc = ui_create_label(cf);
2597 if (rc < 0) {
2598 errno = -rc;
2599 ui_err(EXIT_FAILURE,
2600 _("failed to create a new disklabel"));
2601 }
2602 if (rc)
2603 return rc;
2604 }
2605
2606 cols_init(cf);
2607 rc = lines_refresh(cf);
2608 if (rc)
2609 ui_errx(EXIT_FAILURE, _("failed to read partitions"));
2610
2611 menu_push(cf, main_menuitems);
2612 cf->menu->ignore_cb = main_menu_ignore_keys;
2613
2614 rc = ui_refresh(cf);
2615 if (rc)
2616 return rc;
2617
2618 cf->show_extra = 1;
2619 ui_draw_extra(cf);
2620
2621 if (fdisk_is_readonly(cf->cxt))
2622 ui_warnx(_("Device is open in read-only mode. Changes will remain in memory only."));
2623 else if (cf->device_is_used)
2624 ui_warnx(_("Device is currently in use, repartitioning is probably a bad idea."));
2625 else if (cf->wrong_order)
2626 ui_info(_("Note that partition table entries are not in disk order now."));
2627
2628 while (!sig_die) {
2629 int key = getch();
2630
2631 rc = 0;
2632
2633 if (sig_die)
2634 break;
2635 if (sig_resize)
2636 /* Note that ncurses getch() returns ERR when interrupted
2637 * by signal, but SLang does not interrupt at all. */
2638 ui_resize_refresh(cf);
2639 if (key == ERR)
2640 continue;
2641 if (key == '\014') { /* ^L refresh */
2642 ui_resize_refresh(cf);
2643 continue;
2644 }
2645 if (ui_menu_move(cf, key) == 0)
2646 continue;
2647
2648 DBG(UI, ul_debug("main action key >%1$c< [\\0%1$o].", key));
2649
2650 switch (key) {
2651 case KEY_DOWN:
2652 case '\016': /* ^N */
2653 case 'j': /* Vi-like alternative */
2654 ui_table_goto(cf, cf->lines_idx + 1);
2655 break;
2656 case KEY_UP:
2657 case '\020': /* ^P */
2658 case 'k': /* Vi-like alternative */
2659 ui_table_goto(cf, (int) cf->lines_idx - 1);
2660 break;
2661 case KEY_PPAGE:
2662 if (cf->page_sz) {
2663 ui_table_goto(cf, (int) cf->lines_idx - cf->page_sz);
2664 break;
2665 }
2666 /* fallthrough */
2667 case KEY_HOME:
2668 ui_table_goto(cf, 0);
2669 break;
2670 case KEY_NPAGE:
2671 if (cf->page_sz) {
2672 ui_table_goto(cf, cf->lines_idx + cf->page_sz);
2673 break;
2674 }
2675 /* fallthrough */
2676 case KEY_END:
2677 ui_table_goto(cf, (int) cf->nlines - 1);
2678 break;
2679 case KEY_ENTER:
2680 case '\n':
2681 case '\r':
2682 rc = main_menu_action(cf, 0);
2683 break;
2684 case 'X':
2685 case 'x': /* Extra */
2686 toggle_show_extra(cf);
2687 break;
2688 default:
2689 rc = main_menu_action(cf, key);
2690 if (rc < 0)
2691 beep();
2692 break;
2693 }
2694
2695 if (rc == 1)
2696 break; /* quit */
2697 }
2698
2699 menu_pop(cf);
2700
2701 DBG(UI, ul_debug("end"));
2702 return 0;
2703 }
2704
2705 static void __attribute__((__noreturn__)) usage(void)
2706 {
2707 FILE *out = stdout;
2708 fputs(USAGE_HEADER, out);
2709 fprintf(out,
2710 _(" %1$s [options] <disk>\n"), program_invocation_short_name);
2711
2712 fputs(USAGE_SEPARATOR, out);
2713 fputs(_("Display or manipulate a disk partition table.\n"), out);
2714
2715 fputs(USAGE_OPTIONS, out);
2716 fprintf(out,
2717 _(" -L, --color[=<when>] colorize output (%s, %s or %s)\n"), "auto", "always", "never");
2718 fprintf(out,
2719 " %s\n", USAGE_COLORS_DEFAULT);
2720 fputs(_(" -z, --zero start with zeroed partition table\n"), out);
2721 fprintf(out,
2722 _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
2723 fputs(_(" -r, --read-only forced open cfdisk in read-only mode\n"), out);
2724
2725 fputs(USAGE_SEPARATOR, out);
2726 printf(USAGE_HELP_OPTIONS(26));
2727
2728 printf(USAGE_MAN_TAIL("cfdisk(8)"));
2729 exit(EXIT_SUCCESS);
2730 }
2731
2732 int main(int argc, char *argv[])
2733 {
2734 const char *diskpath = NULL, *lockmode = NULL;
2735 int rc, c, colormode = UL_COLORMODE_UNDEF;
2736 int read_only = 0;
2737 struct cfdisk _cf = { .lines_idx = 0 },
2738 *cf = &_cf;
2739 enum {
2740 OPT_LOCK = CHAR_MAX + 1
2741 };
2742 static const struct option longopts[] = {
2743 { "color", optional_argument, NULL, 'L' },
2744 { "lock", optional_argument, NULL, OPT_LOCK },
2745 { "help", no_argument, NULL, 'h' },
2746 { "version", no_argument, NULL, 'V' },
2747 { "zero", no_argument, NULL, 'z' },
2748 { "read-only", no_argument, NULL, 'r' },
2749 { NULL, 0, NULL, 0 },
2750 };
2751
2752 setlocale(LC_ALL, "");
2753 bindtextdomain(PACKAGE, LOCALEDIR);
2754 textdomain(PACKAGE);
2755 close_stdout_atexit();
2756
2757 while((c = getopt_long(argc, argv, "L::hVzr", longopts, NULL)) != -1) {
2758 switch(c) {
2759 case 'h':
2760 usage();
2761 break;
2762 case 'L':
2763 colormode = UL_COLORMODE_AUTO;
2764 if (optarg)
2765 colormode = colormode_or_err(optarg,
2766 _("unsupported color mode"));
2767 break;
2768 case 'r':
2769 read_only = 1;
2770 break;
2771 case 'V':
2772 print_version(EXIT_SUCCESS);
2773 case 'z':
2774 cf->zero_start = 1;
2775 break;
2776 case OPT_LOCK:
2777 lockmode = "1";
2778 if (optarg) {
2779 if (*optarg == '=')
2780 optarg++;
2781 lockmode = optarg;
2782 }
2783 break;
2784 default:
2785 errtryhelp(EXIT_FAILURE);
2786 }
2787 }
2788
2789 colors_init(colormode, "cfdisk");
2790
2791 fdisk_init_debug(0);
2792 scols_init_debug(0);
2793 cfdisk_init_debug();
2794 cf->cxt = fdisk_new_context();
2795 if (!cf->cxt)
2796 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
2797
2798 fdisk_set_ask(cf->cxt, ask_callback, (void *) cf);
2799
2800 if (optind == argc) {
2801 size_t i;
2802
2803 for (i = 0; i < ARRAY_SIZE(default_disks); i++) {
2804 if (access(default_disks[i], F_OK) == 0) {
2805 diskpath = default_disks[i];
2806 break;
2807 }
2808 }
2809 if (!diskpath)
2810 diskpath = default_disks[0]; /* default, used for "cannot open" */
2811 } else
2812 diskpath = argv[optind];
2813
2814 rc = fdisk_assign_device(cf->cxt, diskpath, read_only);
2815 if (rc == -EACCES && read_only == 0)
2816 rc = fdisk_assign_device(cf->cxt, diskpath, 1);
2817 if (rc != 0)
2818 err(EXIT_FAILURE, _("cannot open %s"), diskpath);
2819
2820 if (!fdisk_is_readonly(cf->cxt)) {
2821 if (blkdev_lock(fdisk_get_devfd(cf->cxt), diskpath, lockmode) != 0)
2822 return EXIT_FAILURE;
2823
2824 cf->device_is_used = fdisk_device_is_used(cf->cxt);
2825 fdisk_get_partitions(cf->cxt, &cf->original_layout);
2826 }
2827
2828 /* Don't use err(), warn() from this point */
2829 ui_init(cf);
2830 ui_run(cf);
2831 ui_end();
2832
2833 cfdisk_free_lines(cf);
2834 free(cf->linesbuf);
2835 free(cf->fields);
2836
2837 fdisk_unref_table(cf->table);
2838 #ifdef HAVE_LIBMOUNT
2839 mnt_unref_table(cf->fstab);
2840 mnt_unref_table(cf->mtab);
2841 mnt_unref_cache(cf->mntcache);
2842 #endif
2843 rc = fdisk_deassign_device(cf->cxt, cf->nwrites == 0);
2844 fdisk_unref_context(cf->cxt);
2845 DBG(MISC, ul_debug("bye! [rc=%d]", rc));
2846 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
2847 }