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