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