]> git.ipfire.org Git - thirdparty/util-linux.git/blame - disk-utils/partx.c
chsh: keep struct options in .rodata
[thirdparty/util-linux.git] / disk-utils / partx.c
CommitLineData
22853e4a 1/*
c4ecaf21 2 * partx: tell the kernel about your disk's partitions
22853e4a
KZ
3 * [This is not an fdisk - adding and removing partitions
4 * is not a change of the disk, but just telling the kernel
5 * about presence and numbering of on-disk partitions.]
6 *
22853e4a 7 * aeb, 2000-03-21 -- sah is 42 now
c4ecaf21
DB
8 *
9 * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
a9485396 10 * Rewritten to use libblkid for util-linux
c4ecaf21 11 * based on ideas from Karel Zak <kzak@redhat.com>
22853e4a
KZ
12 */
13
14#include <stdio.h>
22853e4a
KZ
15#include <errno.h>
16#include <stdlib.h>
c4ecaf21 17#include <ctype.h>
22853e4a 18#include <getopt.h>
1d4ad1de 19#include <unistd.h>
c4ecaf21 20#include <assert.h>
c4ecaf21
DB
21#include <dirent.h>
22
23#include <blkid.h>
016aa0ac 24#include <libsmartcols.h>
0d518f34 25
1aff9b62 26#include "c.h"
c4ecaf21
DB
27#include "pathnames.h"
28#include "nls.h"
0d518f34 29#include "blkdev.h"
c4ecaf21
DB
30#include "strutils.h"
31#include "xalloc.h"
22853e4a 32#include "partx.h"
a88268b8 33#include "sysfs.h"
0e938113 34#include "loopdev.h"
741a5b10 35#include "at.h"
757bbfad 36#include "closestream.h"
6320c5aa 37#include "optutils.h"
22853e4a 38
c4ecaf21
DB
39/* this is the default upper limit, could be modified by --nr */
40#define SLICES_MAX 256
41
016aa0ac
OO
42/* basic table settings */
43enum {
44 PARTX_RAW = (1 << 0),
45 PARTX_NOHEADINGS = (1 << 1),
46 PARTX_EXPORT = (1 << 2),
47};
48
c4ecaf21
DB
49/* all the columns (-o option) */
50enum {
51 COL_PARTNO,
52 COL_START,
53 COL_END,
54 COL_SECTORS,
55 COL_SIZE,
56 COL_NAME,
57 COL_UUID,
58 COL_TYPE,
5de966b3
KZ
59 COL_FLAGS,
60 COL_SCHEME,
c4ecaf21 61};
22853e4a 62
6320c5aa 63#define ACT_ERROR "--{add,delete,show,list,raw,pairs}"
c4ecaf21 64enum {
6320c5aa
SK
65 ACT_NONE,
66 ACT_LIST,
c4ecaf21
DB
67 ACT_SHOW,
68 ACT_ADD,
3b905b79 69 ACT_UPD,
c4ecaf21
DB
70 ACT_DELETE
71};
72
73enum {
74 FL_BYTES = (1 << 1)
75};
76
77/* column names */
78struct colinfo {
79 const char *name; /* header */
80 double whint; /* width hint (N < 1 is in percent of termwidth) */
016aa0ac 81 int flags; /* SCOLS_FL_* */
c4ecaf21
DB
82 const char *help;
83};
22853e4a 84
c4ecaf21 85/* columns descriptions */
10ebfeea 86struct colinfo infos[] = {
016aa0ac
OO
87 [COL_PARTNO] = { "NR", 0.25, SCOLS_FL_RIGHT, N_("partition number") },
88 [COL_START] = { "START", 0.30, SCOLS_FL_RIGHT, N_("start of the partition in sectors") },
89 [COL_END] = { "END", 0.30, SCOLS_FL_RIGHT, N_("end of the partition in sectors") },
90 [COL_SECTORS] = { "SECTORS", 0.30, SCOLS_FL_RIGHT, N_("number of sectors") },
91 [COL_SIZE] = { "SIZE", 0.30, SCOLS_FL_RIGHT, N_("human readable size") },
92 [COL_NAME] = { "NAME", 0.30, SCOLS_FL_TRUNC, N_("partition name") },
c4ecaf21 93 [COL_UUID] = { "UUID", 36, 0, N_("partition UUID")},
016aa0ac
OO
94 [COL_SCHEME] = { "SCHEME", 0.1, SCOLS_FL_TRUNC, N_("partition table type (dos, gpt, ...)")},
95 [COL_FLAGS] = { "FLAGS", 0.1, SCOLS_FL_TRUNC, N_("partition flags")},
96 [COL_TYPE] = { "TYPE", 1, SCOLS_FL_RIGHT, N_("partition type (a string, a UUID, or hex)")},
c4ecaf21 97};
10ebfeea
DB
98
99#define NCOLS ARRAY_SIZE(infos)
100
c4ecaf21 101/* array with IDs of enabled columns */
10ebfeea 102static int columns[NCOLS], ncolumns;
c4ecaf21
DB
103
104static int verbose;
105static int partx_flags;
0e938113
DB
106static struct loopdev_cxt lc;
107static int loopdev;
22853e4a 108
0e938113
DB
109static void assoc_loopdev(const char *fname)
110{
111 int rc;
112
defa0710
KZ
113 if (loopcxt_init(&lc, 0))
114 err(EXIT_FAILURE, _("failed to initialize loopcxt"));
0e938113
DB
115
116 rc = loopcxt_find_unused(&lc);
117 if (rc)
118 err(EXIT_FAILURE, _("%s: failed to find unused loop device"),
119 fname);
120
121 if (verbose)
122 printf(_("Trying to use '%s' for the loop device\n"),
123 loopcxt_get_device(&lc));
124
125 if (loopcxt_set_backing_file(&lc, fname))
126 err(EXIT_FAILURE, _("%s: failed to set backing file"), fname);
127
128 rc = loopcxt_setup_device(&lc);
129
130 if (rc == -EBUSY)
dbfff473 131 err(EXIT_FAILURE, _("%s: failed to set up loop device"), fname);
0e938113
DB
132
133 loopdev = 1;
134}
22853e4a 135
c4ecaf21 136static inline int get_column_id(int num)
1d4ad1de 137{
10ebfeea 138 assert(ARRAY_SIZE(columns) == NCOLS);
c4ecaf21 139 assert(num < ncolumns);
10ebfeea 140 assert(columns[num] < (int) NCOLS);
c4ecaf21 141 return columns[num];
22853e4a
KZ
142}
143
c4ecaf21 144static inline struct colinfo *get_column_info(int num)
1d4ad1de 145{
c4ecaf21 146 return &infos[ get_column_id(num) ];
22853e4a
KZ
147}
148
c4ecaf21
DB
149static int column_name_to_id(const char *name, size_t namesz)
150{
10ebfeea 151 size_t i;
22853e4a 152
c4ecaf21 153 assert(name);
22853e4a 154
10ebfeea 155 for (i = 0; i < NCOLS; i++) {
c4ecaf21 156 const char *cn = infos[i].name;
22853e4a 157
c4ecaf21
DB
158 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
159 return i;
22853e4a 160 }
c4ecaf21
DB
161 warnx(_("unknown column: %s"), name);
162 return -1;
163}
22853e4a 164
c4ecaf21
DB
165/*
166 * Given a partition return the corresponding partition number.
167 *
168 * Note that this function tries to use sysfs, otherwise it assumes that the
169 * last characters are always numeric (sda1, sdc20, etc).
c4ecaf21
DB
170 */
171static int get_partno_from_device(char *partition, dev_t devno)
172{
173 int partno = 0;
174 size_t sz;
175 char *p, *end = NULL;
176
177 assert(partition);
178
179 if (devno) {
a88268b8 180 struct sysfs_cxt cxt;
43e3f054 181 int rc;
a88268b8 182
5bfbbc78
KZ
183 if (sysfs_init(&cxt, devno, NULL))
184 goto err;
185
43e3f054
KZ
186 rc = sysfs_read_int(&cxt, "partition", &partno);
187 sysfs_deinit(&cxt);
188
189 if (rc == 0)
c4ecaf21
DB
190 return partno;
191 }
22853e4a 192
c4ecaf21
DB
193 sz = strlen(partition);
194 p = partition + sz - 1;
22853e4a 195
c4ecaf21
DB
196 if (!isdigit((unsigned int) *p))
197 goto err;
22853e4a 198
c4ecaf21
DB
199 while (isdigit((unsigned int) *(p - 1))) p--;
200
201 errno = 0;
202 partno = strtol(p, &end, 10);
203 if (errno || !end || *end || p == end)
204 goto err;
205
206 return partno;
207err:
208 errx(EXIT_FAILURE, _("%s: failed to get partition number"), partition);
209}
210
211static int get_max_partno(const char *disk, dev_t devno)
212{
741a5b10 213 char path[PATH_MAX], *parent, *dirname = NULL;
c4ecaf21
DB
214 struct stat st;
215 DIR *dir;
216 struct dirent *d;
217 int partno = 0;
218
219 if (!devno && !stat(disk, &st))
220 devno = st.st_rdev;
221 if (!devno)
222 goto dflt;
223 parent = strrchr(disk, '/');
224 if (!parent)
225 goto dflt;
226 parent++;
227
228 snprintf(path, sizeof(path), _PATH_SYS_DEVBLOCK "/%d:%d/",
229 major(devno), minor(devno));
230
231 dir = opendir(path);
232 if (!dir)
233 goto dflt;
234
741a5b10
KZ
235 dirname = xstrdup(path);
236
237 while ((d = readdir(dir))) {
c4ecaf21
DB
238 int fd;
239
240 if (!strcmp(d->d_name, ".") ||
241 !strcmp(d->d_name, ".."))
242 continue;
243#ifdef _DIRENT_HAVE_D_TYPE
a7c60528 244 if (d->d_type != DT_DIR && d->d_type != DT_UNKNOWN)
c4ecaf21
DB
245 continue;
246#endif
247 if (strncmp(parent, d->d_name, strlen(parent)))
248 continue;
249 snprintf(path, sizeof(path), "%s/partition", d->d_name);
250
741a5b10 251 fd = open_at(dirfd(dir), dirname, path, O_RDONLY);
c4ecaf21
DB
252 if (fd) {
253 int x = 0;
254 FILE *f = fdopen(fd, "r");
255 if (f) {
256 if (fscanf(f, "%d", &x) == 1 && x > partno)
257 partno = x;
258 fclose(f);
22853e4a 259 }
22853e4a 260 }
22853e4a
KZ
261 }
262
741a5b10 263 free(dirname);
c4ecaf21
DB
264 closedir(dir);
265 return partno;
266dflt:
267 return SLICES_MAX;
268}
269
270static void del_parts_warnx(const char *device, int first, int last)
271{
272 if (first == last)
273 warnx(_("%s: error deleting partition %d"), device, first);
274 else
275 warnx(_("%s: error deleting partitions %d-%d"),
276 device, first, last);
277}
278
279static int del_parts(int fd, const char *device, dev_t devno,
280 int lower, int upper)
281{
282 int rc = 0, i, errfirst = 0, errlast = 0;
283
284 assert(fd >= 0);
285 assert(device);
286
287 if (!lower)
288 lower = 1;
289 if (!upper || lower < 0 || upper < 0) {
290 int n = get_max_partno(device, devno);
291 if (!upper)
292 upper = n;
293 else if (upper < 0)
294 upper = n + upper + 1;
295 if (lower < 0)
296 lower = n + lower + 1;
297 }
298 if (lower > upper) {
144df9a2 299 warnx(_("specified range <%d:%d> "
c4ecaf21
DB
300 "does not make sense"), lower, upper);
301 return -1;
302 }
303
304 for (i = lower; i <= upper; i++) {
ab025087
PS
305 rc = partx_del_partition(fd, i);
306 if (rc == 0) {
c4ecaf21
DB
307 if (verbose)
308 printf(_("%s: partition #%d removed\n"), device, i);
309 continue;
ab025087
PS
310 } else if (errno == ENXIO) {
311 if (verbose)
312 printf(_("%s: partition #%d already doesn't exist\n"), device, i);
313 continue;
c4ecaf21
DB
314 }
315 rc = -1;
316 if (verbose)
144df9a2 317 warn(_("%s: deleting partition #%d failed"), device, i);
c4ecaf21
DB
318 if (!errfirst)
319 errlast = errfirst = i;
320 else if (errlast + 1 == i)
321 errlast++;
322 else {
323 del_parts_warnx(device, errfirst, errlast);
324 errlast = errfirst = i;
22853e4a 325 }
22853e4a
KZ
326 }
327
c4ecaf21
DB
328 if (errfirst)
329 del_parts_warnx(device, errfirst, errlast);
330 return rc;
331}
332
0e938113 333
c4ecaf21
DB
334static void add_parts_warnx(const char *device, int first, int last)
335{
336 if (first == last)
337 warnx(_("%s: error adding partition %d"), device, first);
338 else
339 warnx(_("%s: error adding partitions %d-%d"),
340 device, first, last);
341}
342
343static int add_parts(int fd, const char *device,
0e938113 344 blkid_partlist ls, int lower, int upper)
c4ecaf21
DB
345{
346 int i, nparts, rc = 0, errfirst = 0, errlast = 0;
347
348 assert(fd >= 0);
349 assert(device);
350 assert(ls);
351
352 nparts = blkid_partlist_numof_partitions(ls);
353
354 for (i = 0; i < nparts; i++) {
355 blkid_partition par = blkid_partlist_get_partition(ls, i);
356 int n = blkid_partition_get_partno(par);
357 uintmax_t start, size;
358
359 if (lower && n < lower)
360 continue;
361 if (upper && n > upper)
362 continue;
363
364 start = blkid_partition_get_start(par);
365 size = blkid_partition_get_size(par);
366
367 if (blkid_partition_is_extended(par))
368 /*
144df9a2
BS
369 * Let's follow the Linux kernel and reduce
370 * DOS extended partition to 1 or 2 sectors.
c4ecaf21
DB
371 */
372 size = min(size, (uintmax_t) 2);
373
374 if (partx_add_partition(fd, n, start, size) == 0) {
375 if (verbose)
376 printf(_("%s: partition #%d added\n"), device, n);
377 continue;
378 }
379 rc = -1;
380 if (verbose)
144df9a2 381 warn(_("%s: adding partition #%d failed"), device, n);
c4ecaf21
DB
382 if (!errfirst)
383 errlast = errfirst = n;
384 else if (errlast + 1 == n)
385 errlast++;
386 else {
387 add_parts_warnx(device, errfirst, errlast);
388 errlast = errfirst = n;
389 }
22853e4a 390 }
c4ecaf21
DB
391
392 if (errfirst)
393 add_parts_warnx(device, errfirst, errlast);
0e938113 394
59d749c3
KZ
395 /*
396 * The kernel with enabled partitions scanner for loop devices add *all*
397 * partitions, so we should delete any extra, unwanted ones, when the -n
398 * option is passed.
399 */
400 if (loopdev && loopcxt_is_partscan(&lc) && (lower || upper)) {
0e938113
DB
401 for (i = 0; i < nparts; i++) {
402 blkid_partition par = blkid_partlist_get_partition(ls, i);
403 int n = blkid_partition_get_partno(par);
404
405 if (n < lower || n > upper)
406 partx_del_partition(fd, n);
407 }
408 }
409
c4ecaf21
DB
410 return rc;
411}
412
3b905b79
PS
413static void upd_parts_warnx(const char *device, int first, int last)
414{
415 if (first == last)
416 warnx(_("%s: error updating partition %d"), device, first);
417 else
418 warnx(_("%s: error updating partitions %d-%d"),
419 device, first, last);
420}
421
422static int upd_parts(int fd, const char *device, dev_t devno,
423 blkid_partlist ls, int lower, int upper)
424{
84ac3112 425 int n, nparts, rc = 0, errfirst = 0, errlast = 0, err;
3b905b79
PS
426 blkid_partition par;
427 uintmax_t start, size;
428
429 assert(fd >= 0);
430 assert(device);
431 assert(ls);
432
433 nparts = blkid_partlist_numof_partitions(ls);
434 if (!lower)
435 lower = 1;
436 if (!upper || lower < 0 || upper < 0) {
437 n = get_max_partno(device, devno);
438 if (!upper)
439 upper = n > nparts ? n : nparts;
440 else if (upper < 0)
441 upper = n + upper + 1;
442 if (lower < 0)
443 lower = n + lower + 1;
444 }
445 if (lower > upper) {
446 warnx(_("specified range <%d:%d> "
447 "does not make sense"), lower, upper);
448 return -1;
449 }
450
84ac3112 451 for (n = lower; n <= upper; n++) {
c62f164c 452 par = blkid_partlist_get_partition_by_partno(ls, n);
84ac3112
SM
453 if (!par) {
454 if (verbose)
455 warn(_("%s: no partition #%d"), device, n);
3b905b79 456 continue;
84ac3112 457 }
3b905b79
PS
458
459 start = blkid_partition_get_start(par);
460 size = blkid_partition_get_size(par);
3b905b79
PS
461 if (blkid_partition_is_extended(par))
462 /*
463 * Let's follow the Linux kernel and reduce
464 * DOS extended partition to 1 or 2 sectors.
465 */
466 size = min(size, (uintmax_t) 2);
467
468 err = partx_del_partition(fd, n);
469 if (err == -1 && errno == ENXIO)
470 err = 0; /* good, it already doesn't exist */
84ac3112 471 if (err == -1 && errno == EBUSY)
3b905b79 472 {
84ac3112
SM
473 /* try to resize */
474 err = partx_resize_partition(fd, n, start, size);
475 if (verbose)
476 printf(_("%s: partition #%d resized\n"), device, n);
477 if (err == 0)
3b905b79 478 continue;
3b905b79 479 }
84ac3112
SM
480 if (err == 0 && partx_add_partition(fd, n, start, size) == 0) {
481 if (verbose)
482 printf(_("%s: partition #%d added\n"), device, n);
483 continue;
484 }
485
3b905b79
PS
486 if (err == 0)
487 continue;
488 rc = -1;
489 if (verbose)
490 warn(_("%s: updating partition #%d failed"), device, n);
491 if (!errfirst)
492 errlast = errfirst = n;
493 else if (errlast + 1 == n)
494 errlast++;
495 else {
496 upd_parts_warnx(device, errfirst, errlast);
497 errlast = errfirst = n;
498 }
499 }
500
501 if (errfirst)
502 upd_parts_warnx(device, errfirst, errlast);
503 return rc;
504}
505
c4ecaf21
DB
506static int list_parts(blkid_partlist ls, int lower, int upper)
507{
508 int i, nparts;
509
510 assert(ls);
511
512 nparts = blkid_partlist_numof_partitions(ls);
513
514 for (i = 0; i < nparts; i++) {
515 blkid_partition par = blkid_partlist_get_partition(ls, i);
516 int n = blkid_partition_get_partno(par);
517 uintmax_t start, size;
518
519 if (lower && n < lower)
520 continue;
521 if (upper && n > upper)
522 continue;
523
524 start = blkid_partition_get_start(par);
525 size = blkid_partition_get_size(par);
526
0df61bea
BS
527 printf(P_("#%2d: %9ju-%9ju (%9ju sector, %6ju MB)\n",
528 "#%2d: %9ju-%9ju (%9ju sectors, %6ju MB)\n",
529 size),
c4ecaf21
DB
530 n, start, start + size -1,
531 size, (size << 9) / 1000000);
22853e4a 532 }
c4ecaf21
DB
533 return 0;
534}
535
3b80b370 536static int add_scols_line(struct libscols_table *table, blkid_partition par)
c4ecaf21 537{
016aa0ac 538 struct libscols_line *line;
3b80b370 539 int i, rc = 0;
c4ecaf21 540
016aa0ac 541 assert(table);
c4ecaf21 542 assert(par);
22853e4a 543
016aa0ac 544 line = scols_table_new_line(table, NULL);
c4ecaf21
DB
545 if (!line) {
546 warn(_("failed to add line to output"));
3b80b370 547 return -ENOMEM;
22853e4a 548 }
22853e4a 549
c4ecaf21 550 for (i = 0; i < ncolumns; i++) {
3b80b370
KZ
551 char *str = NULL; /* allocated string */
552 const char *cstr = NULL; /* foreign string */
c4ecaf21
DB
553
554 switch (get_column_id(i)) {
555 case COL_PARTNO:
8acf2fb6 556 xasprintf(&str, "%d", blkid_partition_get_partno(par));
c4ecaf21
DB
557 break;
558 case COL_START:
8acf2fb6 559 xasprintf(&str, "%ju", blkid_partition_get_start(par));
c4ecaf21
DB
560 break;
561 case COL_END:
8acf2fb6 562 xasprintf(&str, "%ju",
c4ecaf21
DB
563 blkid_partition_get_start(par) +
564 blkid_partition_get_size(par) - 1);
565 break;
566 case COL_SECTORS:
8acf2fb6 567 xasprintf(&str, "%ju", blkid_partition_get_size(par));
c4ecaf21
DB
568 break;
569 case COL_SIZE:
570 if (partx_flags & FL_BYTES)
8acf2fb6 571 xasprintf(&str, "%ju", (uintmax_t)
c4ecaf21 572 blkid_partition_get_size(par) << 9);
5de966b3 573 else
5d2a9849 574 str = size_to_human_string(SIZE_SUFFIX_1LETTER,
c4ecaf21 575 blkid_partition_get_size(par) << 9);
c4ecaf21
DB
576 break;
577 case COL_NAME:
3b80b370 578 cstr = blkid_partition_get_name(par);
c4ecaf21
DB
579 break;
580 case COL_UUID:
3b80b370 581 cstr = blkid_partition_get_uuid(par);
c4ecaf21
DB
582 break;
583 case COL_TYPE:
6df8dcfb 584 if (blkid_partition_get_type_string(par))
3b80b370 585 cstr = blkid_partition_get_type_string(par);
5de966b3 586 else
8acf2fb6 587 xasprintf(&str, "0x%x",
5de966b3 588 blkid_partition_get_type(par));
c4ecaf21 589 break;
5de966b3 590 case COL_FLAGS:
8acf2fb6 591 xasprintf(&str, "0x%llx", blkid_partition_get_flags(par));
5de966b3
KZ
592 break;
593 case COL_SCHEME:
594 {
595 blkid_parttable tab = blkid_partition_get_table(par);
6df8dcfb 596 if (tab)
3b80b370 597 cstr = blkid_parttable_get_type(tab);
5de966b3
KZ
598 break;
599 }
c4ecaf21
DB
600 default:
601 break;
602 }
603
3b80b370
KZ
604 if (cstr)
605 rc = scols_line_set_data(line, i, cstr);
606 else if (str)
607 rc = scols_line_refer_data(line, i, str);
608 if (rc) {
609 warn(_("failed to add data to output table"));
610 break;
611 }
22853e4a 612 }
3b80b370
KZ
613
614 return rc;
c4ecaf21 615}
22853e4a 616
016aa0ac 617static int show_parts(blkid_partlist ls, int scols_flags, int lower, int upper)
c4ecaf21
DB
618{
619 int i, rc = -1;
016aa0ac 620 struct libscols_table *table;
c4ecaf21 621 int nparts;
22853e4a 622
c4ecaf21
DB
623 assert(ls);
624
625 nparts = blkid_partlist_numof_partitions(ls);
626 if (!nparts)
627 return 0;
628
710ed55d 629 scols_init_debug(0);
0925a9dd 630 table = scols_new_table();
016aa0ac 631 if (!table) {
c4ecaf21
DB
632 warn(_("failed to initialize output table"));
633 return -1;
22853e4a 634 }
0925a9dd
KZ
635 scols_table_enable_raw(table, !!(scols_flags & PARTX_RAW));
636 scols_table_enable_export(table, !!(scols_flags & PARTX_EXPORT));
637 scols_table_enable_noheadings(table, !!(scols_flags & PARTX_NOHEADINGS));
22853e4a 638
c4ecaf21
DB
639 for (i = 0; i < ncolumns; i++) {
640 struct colinfo *col = get_column_info(i);
641
016aa0ac 642 if (!scols_table_new_column(table, col->name, col->whint, col->flags)) {
c4ecaf21
DB
643 warnx(_("failed to initialize output column"));
644 goto done;
22853e4a
KZ
645 }
646 }
647
c4ecaf21
DB
648 for (i = 0; i < nparts; i++) {
649 blkid_partition par = blkid_partlist_get_partition(ls, i);
650 int n = blkid_partition_get_partno(par);
22853e4a 651
c4ecaf21
DB
652 if (lower && n < lower)
653 continue;
654 if (upper && n > upper)
655 continue;
22853e4a 656
3b80b370
KZ
657 rc = add_scols_line(table, par);
658 if (rc)
659 break;
22853e4a 660 }
c4ecaf21
DB
661
662 rc = 0;
016aa0ac 663 scols_print_table(table);
c4ecaf21 664done:
016aa0ac 665 scols_unref_table(table);
c4ecaf21 666 return rc;
22853e4a
KZ
667}
668
c4ecaf21
DB
669static blkid_partlist get_partlist(blkid_probe pr,
670 const char *device, char *type)
671{
672 blkid_partlist ls;
673 blkid_parttable tab;
674
675 assert(pr);
676 assert(device);
677
678 if (type) {
679 char *name[] = { type, NULL };
680
681 if (blkid_probe_filter_partitions_type(pr,
682 BLKID_FLTR_ONLYIN, name)) {
683 warnx(_("failed to initialize blkid "
144df9a2 684 "filter for '%s'"), type);
c4ecaf21
DB
685 return NULL;
686 }
687 }
688
689 ls = blkid_probe_get_partitions(pr);
690 if (!ls) {
691 warnx(_("%s: failed to read partition table"), device);
692 return NULL;
693 }
694
695 tab = blkid_partlist_get_table(ls);
83ae4c24 696 if (verbose && tab) {
144df9a2 697 printf(_("%s: partition table type '%s' detected\n"),
83ae4c24 698 device, blkid_parttable_get_type(tab));
c4ecaf21 699
83ae4c24
KZ
700 if (!blkid_partlist_numof_partitions(ls))
701 printf(_("%s: partition table with no partitions"), device);
22853e4a 702 }
83ae4c24 703
c4ecaf21
DB
704 return ls;
705}
706
abafd686 707static void __attribute__((__noreturn__)) usage(FILE *out)
c4ecaf21 708{
10ebfeea 709 size_t i;
c4ecaf21 710
4ded3823 711 fputs(USAGE_HEADER, out);
e96fd176 712 fprintf(out,
3b905b79 713 _(" %s [-a|-d|-s|-u] [--nr <n:m> | <partition>] <disk>\n"),
c4ecaf21
DB
714 program_invocation_short_name);
715
8275b732 716 fputs(USAGE_OPTIONS, out);
b7e785ed
SK
717 fputs(_(" -a, --add add specified partitions or all of them\n"), out);
718 fputs(_(" -d, --delete delete specified partitions or all of them\n"), out);
b7e785ed 719 fputs(_(" -u, --update update specified partitions or all of them\n"), out);
dbfff473 720 fputs(_(" -s, --show list partitions\n\n"), out);
b7e785ed
SK
721 fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out);
722 fputs(_(" -g, --noheadings don't print headings for --show\n"), out);
723 fputs(_(" -n, --nr <n:m> specify the range of partitions (e.g. --nr 2:4)\n"), out);
dbfff473 724 fputs(_(" -o, --output <list> define which output columns to use\n"), out);
b7e785ed
SK
725 fputs(_(" -P, --pairs use key=\"value\" output format\n"), out);
726 fputs(_(" -r, --raw use raw output format\n"), out);
727 fputs(_(" -t, --type <type> specify the partition type (dos, bsd, solaris, etc.)\n"), out);
728 fputs(_(" -v, --verbose verbose mode\n"), out);
8275b732
KZ
729
730 fputs(USAGE_SEPARATOR, out);
731 fputs(USAGE_HELP, out);
732 fputs(USAGE_VERSION, out);
c4ecaf21 733
e96fd176 734 fputs(_("\nAvailable columns (for --show, --raw or --pairs):\n"), out);
c4ecaf21 735
10ebfeea 736 for (i = 0; i < NCOLS; i++)
d015794e 737 fprintf(out, " %10s %s\n", infos[i].name, _(infos[i].help));
c4ecaf21 738
4ded3823 739 fprintf(out, USAGE_MAN_TAIL("partx(8)"));
c4ecaf21
DB
740
741 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
742}
743
c4ecaf21
DB
744int main(int argc, char **argv)
745{
6320c5aa 746 int fd, c, what = ACT_NONE, lower = 0, upper = 0, rc = 0;
016aa0ac 747 int scols_flags = 0;
c4ecaf21 748 char *type = NULL;
1d6a5daa 749 char *device = NULL; /* pointer to argv[], ie: /dev/sda1 */
c4ecaf21 750 char *wholedisk = NULL; /* allocated, ie: /dev/sda */
4e395c55 751 char *outarg = NULL;
c4ecaf21
DB
752 dev_t disk_devno = 0, part_devno = 0;
753
754 static const struct option long_opts[] = {
755 { "bytes", no_argument, NULL, 'b' },
756 { "noheadings", no_argument, NULL, 'g' },
757 { "raw", no_argument, NULL, 'r' },
758 { "list", no_argument, NULL, 'l' },
759 { "show", no_argument, NULL, 's' },
760 { "add", no_argument, NULL, 'a' },
761 { "delete", no_argument, NULL, 'd' },
3b905b79 762 { "update", no_argument, NULL, 'u' },
c4ecaf21
DB
763 { "type", required_argument, NULL, 't' },
764 { "nr", required_argument, NULL, 'n' },
765 { "output", required_argument, NULL, 'o' },
8ed48785 766 { "pairs", no_argument, NULL, 'P' },
c4ecaf21 767 { "help", no_argument, NULL, 'h' },
72bb1cc9 768 { "version", no_argument, NULL, 'V' },
8275b732 769 { "verbose", no_argument, NULL, 'v' },
c4ecaf21
DB
770 { NULL, 0, NULL, 0 }
771 };
772
a29627cb
KZ
773 static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
774 { 'P','a','d','l','r','s' },
775 { 0 }
776 };
777 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
778
5c87aa1a
BS
779 setlocale(LC_ALL, "");
780 bindtextdomain(PACKAGE, LOCALEDIR);
781 textdomain(PACKAGE);
757bbfad 782 atexit(close_stdout);
5c87aa1a 783
8ed48785 784 while ((c = getopt_long(argc, argv,
3b905b79 785 "abdglrsuvn:t:o:PhV", long_opts, NULL)) != -1) {
a29627cb
KZ
786
787 err_exclusive_options(c, long_opts, excl, excl_st);
788
c4ecaf21
DB
789 switch(c) {
790 case 'a':
a29627cb 791 what = ACT_ADD;
c4ecaf21
DB
792 break;
793 case 'b':
794 partx_flags |= FL_BYTES;
795 break;
796 case 'd':
a29627cb 797 what = ACT_DELETE;
c4ecaf21
DB
798 break;
799 case 'g':
016aa0ac 800 scols_flags |= PARTX_NOHEADINGS;
c4ecaf21
DB
801 break;
802 case 'l':
a29627cb 803 what = ACT_LIST;
c4ecaf21
DB
804 break;
805 case 'n':
af7df9ee 806 if (parse_range(optarg, &lower, &upper, 0))
c4ecaf21
DB
807 errx(EXIT_FAILURE, _("failed to parse --nr <M-N> range"));
808 break;
c4ecaf21 809 case 'o':
4e395c55 810 outarg = optarg;
c4ecaf21 811 break;
8ed48785 812 case 'P':
016aa0ac 813 scols_flags |= PARTX_EXPORT;
a29627cb 814 what = ACT_SHOW;
8ed48785 815 break;
c4ecaf21 816 case 'r':
016aa0ac 817 scols_flags |= PARTX_RAW;
a29627cb 818 what = ACT_SHOW;
c4ecaf21 819 break;
c4ecaf21 820 case 's':
a29627cb 821 what = ACT_SHOW;
c4ecaf21
DB
822 break;
823 case 't':
824 type = optarg;
825 break;
3b905b79
PS
826 case 'u':
827 what = ACT_UPD;
828 break;
c4ecaf21
DB
829 case 'v':
830 verbose = 1;
831 break;
832 case 'h':
833 usage(stdout);
72bb1cc9
SK
834 case 'V':
835 printf(UTIL_LINUX_VERSION);
836 return EXIT_SUCCESS;
c4ecaf21
DB
837 case '?':
838 default:
839 usage(stderr);
840 }
841 }
842
6320c5aa 843 if (what == ACT_NONE)
7cfd1b26 844 what = ACT_SHOW;
c4ecaf21
DB
845
846 /* --show default, could by modified by -o */
847 if (what == ACT_SHOW && !ncolumns) {
848 columns[ncolumns++] = COL_PARTNO;
849 columns[ncolumns++] = COL_START;
850 columns[ncolumns++] = COL_END;
851 columns[ncolumns++] = COL_SECTORS;
852 columns[ncolumns++] = COL_SIZE;
853 columns[ncolumns++] = COL_NAME;
854 columns[ncolumns++] = COL_UUID;
855 }
856
4e395c55
MB
857 if (what == ACT_SHOW && outarg &&
858 string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
859 &ncolumns, column_name_to_id) < 0)
860 return EXIT_FAILURE;
861
c4ecaf21
DB
862 /*
863 * Note that 'partx /dev/sda1' == 'partx /dev/sda1 /dev/sda'
864 * so assume that the device and/or disk are always the last
865 * arguments to be passed to partx.
866 */
867 if (optind == argc - 2) {
868 /* passed 2 arguments:
869 * /dev/sda1 /dev/sda : partition + whole-disk
870 * -- /dev/sda1 : partition that should be used as a whole-disk
871 */
872 device = argv[optind];
873
874 if (strcmp(device, "-") == 0) {
875 device = NULL;
876 wholedisk = xstrdup(argv[optind + 1]);
877 } else {
878 device = argv[optind];
879 wholedisk = xstrdup(argv[optind + 1]);
880 }
881 } else if (optind == argc - 1) {
882 /* passed only one arg (ie: /dev/sda3 or /dev/sda) */
883 struct stat sb;
884
885 device = argv[optind];
886
887 if (stat(device, &sb))
add1b8af 888 err(EXIT_FAILURE, _("stat failed %s"), device);
c4ecaf21
DB
889
890 part_devno = sb.st_rdev;
891
892 if (blkid_devno_to_wholedisk(part_devno,
893 NULL, 0, &disk_devno) == 0 &&
894 part_devno != disk_devno)
895 wholedisk = blkid_devno_to_devname(disk_devno);
896
897 if (!wholedisk) {
898 wholedisk = xstrdup(device);
899 disk_devno = part_devno;
900 device = NULL;
901 part_devno = 0;
22853e4a 902 }
22853e4a 903 } else
c4ecaf21
DB
904 usage(stderr);
905
906 if (device && (upper || lower))
e22d8b95 907 errx(EXIT_FAILURE, _("--nr and <partition> are mutually exclusive"));
c4ecaf21
DB
908
909 assert(wholedisk);
910
911 if (device) {
912 /* use partno from given partition instead of --nr range, e.g:
913 * partx -d /dev/sda3
914 * is the same like:
915 * partx -d --nr 3 /dev/sda
916 */
917 struct stat sb;
918
919 if (!part_devno && !stat(device, &sb))
920 part_devno = sb.st_rdev;
921
922 lower = upper = get_partno_from_device(device, part_devno);
923 }
924
925 if (verbose)
144df9a2 926 printf(_("partition: %s, disk: %s, lower: %d, upper: %d\n"),
2e2b6bb1 927 device ? device : "none", wholedisk, lower, upper);
c4ecaf21
DB
928
929 if (what == ACT_ADD || what == ACT_DELETE) {
930 struct stat x;
931
0e938113
DB
932 if (stat(wholedisk, &x))
933 errx(EXIT_FAILURE, "%s", wholedisk);
934
935 if (S_ISREG(x.st_mode)) {
936 /* not a blkdev, try to associate it to a loop device */
937 if (what == ACT_DELETE)
938 errx(EXIT_FAILURE, _("%s: cannot delete partitions"),
939 wholedisk);
59d749c3 940 if (!loopmod_supports_partscan())
0e938113
DB
941 errx(EXIT_FAILURE, _("%s: partitioned loop devices unsupported"),
942 wholedisk);
943 assoc_loopdev(wholedisk);
944 wholedisk = xstrdup(lc.device);
945 } else if (!S_ISBLK(x.st_mode))
c4ecaf21
DB
946 errx(EXIT_FAILURE, _("%s: not a block device"), wholedisk);
947 }
948 if ((fd = open(wholedisk, O_RDONLY)) == -1)
289dcc90 949 err(EXIT_FAILURE, _("cannot open %s"), wholedisk);
c4ecaf21
DB
950
951 if (what == ACT_DELETE)
952 rc = del_parts(fd, wholedisk, disk_devno, lower, upper);
953 else {
954 blkid_probe pr = blkid_new_probe();
955 blkid_partlist ls = NULL;
956
957 if (!pr || blkid_probe_set_device(pr, fd, 0, 0))
958 warnx(_("%s: failed to initialize blkid prober"),
959 wholedisk);
960 else
961 ls = get_partlist(pr, wholedisk, type);
962
963 if (ls) {
964 int n = blkid_partlist_numof_partitions(ls);
965
966 if (lower < 0)
967 lower = n + lower + 1;
968 if (upper < 0)
969 upper = n + upper + 1;
970 if (lower > upper) {
144df9a2 971 warnx(_("specified range <%d:%d> "
c4ecaf21 972 "does not make sense"), lower, upper);
6320c5aa 973 rc = -1, what = ACT_NONE;
c4ecaf21
DB
974 }
975
976 switch (what) {
977 case ACT_SHOW:
016aa0ac 978 rc = show_parts(ls, scols_flags, lower, upper);
c4ecaf21
DB
979 break;
980 case ACT_LIST:
981 rc = list_parts(ls, lower, upper);
982 break;
983 case ACT_ADD:
984 rc = add_parts(fd, wholedisk, ls, lower, upper);
985 break;
3b905b79
PS
986 case ACT_UPD:
987 rc = upd_parts(fd, wholedisk, disk_devno, ls, lower, upper);
6320c5aa
SK
988 case ACT_NONE:
989 break;
990 default:
991 abort();
c4ecaf21
DB
992 }
993 }
994 blkid_free_probe(pr);
995 }
996
0e938113
DB
997 if (loopdev)
998 loopcxt_deinit(&lc);
999
83874290
SK
1000 if (close_fd(fd) != 0)
1001 err(EXIT_FAILURE, _("write failed"));
1002
c4ecaf21 1003 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
22853e4a 1004}