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