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