]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/partx.c
Merge branch 'master' of https://github.com/pali/util-linux
[thirdparty/util-linux.git] / disk-utils / partx.c
1 /*
2 * partx: tell the kernel about your disk's partitions
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 *
7 * aeb, 2000-03-21 -- sah is 42 now
8 *
9 * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
10 * Rewritten to use libblkid for util-linux
11 * based on ideas from Karel Zak <kzak@redhat.com>
12 */
13
14 #include <stdio.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <ctype.h>
18 #include <getopt.h>
19 #include <unistd.h>
20 #include <assert.h>
21 #include <dirent.h>
22
23 #include <blkid.h>
24 #include <libsmartcols.h>
25
26 #include "c.h"
27 #include "pathnames.h"
28 #include "nls.h"
29 #include "blkdev.h"
30 #include "strutils.h"
31 #include "xalloc.h"
32 #include "partx.h"
33 #include "sysfs.h"
34 #include "loopdev.h"
35 #include "at.h"
36 #include "closestream.h"
37 #include "optutils.h"
38
39 /* this is the default upper limit, could be modified by --nr */
40 #define SLICES_MAX 256
41
42 /* basic table settings */
43 enum {
44 PARTX_RAW = (1 << 0),
45 PARTX_NOHEADINGS = (1 << 1),
46 PARTX_EXPORT = (1 << 2),
47 };
48
49 /* all the columns (-o option) */
50 enum {
51 COL_PARTNO,
52 COL_START,
53 COL_END,
54 COL_SECTORS,
55 COL_SIZE,
56 COL_NAME,
57 COL_UUID,
58 COL_TYPE,
59 COL_FLAGS,
60 COL_SCHEME,
61 };
62
63 #define ACT_ERROR "--{add,delete,show,list,raw,pairs}"
64 enum {
65 ACT_NONE,
66 ACT_LIST,
67 ACT_SHOW,
68 ACT_ADD,
69 ACT_UPD,
70 ACT_DELETE
71 };
72
73 enum {
74 FL_BYTES = (1 << 1)
75 };
76
77 /* column names */
78 struct colinfo {
79 const char *name; /* header */
80 double whint; /* width hint (N < 1 is in percent of termwidth) */
81 int flags; /* SCOLS_FL_* */
82 const char *help;
83 };
84
85 /* columns descriptions */
86 struct colinfo infos[] = {
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") },
93 [COL_UUID] = { "UUID", 36, 0, N_("partition UUID")},
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)")},
97 };
98
99 #define NCOLS ARRAY_SIZE(infos)
100
101 /* array with IDs of enabled columns */
102 static int columns[NCOLS], ncolumns;
103
104 static int verbose;
105 static int partx_flags;
106 static struct loopdev_cxt lc;
107 static int loopdev;
108
109 static void assoc_loopdev(const char *fname)
110 {
111 int rc;
112
113 if (loopcxt_init(&lc, 0))
114 err(EXIT_FAILURE, _("failed to initialize loopcxt"));
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)
131 err(EXIT_FAILURE, _("%s: failed to set up loop device"), fname);
132
133 loopdev = 1;
134 }
135
136 static inline int get_column_id(int num)
137 {
138 assert(ARRAY_SIZE(columns) == NCOLS);
139 assert(num < ncolumns);
140 assert(columns[num] < (int) NCOLS);
141 return columns[num];
142 }
143
144 static inline struct colinfo *get_column_info(int num)
145 {
146 return &infos[ get_column_id(num) ];
147 }
148
149 static int column_name_to_id(const char *name, size_t namesz)
150 {
151 size_t i;
152
153 assert(name);
154
155 for (i = 0; i < NCOLS; i++) {
156 const char *cn = infos[i].name;
157
158 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
159 return i;
160 }
161 warnx(_("unknown column: %s"), name);
162 return -1;
163 }
164
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).
170 */
171 static 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) {
180 struct sysfs_cxt cxt;
181 int rc;
182
183 if (sysfs_init(&cxt, devno, NULL))
184 goto err;
185
186 rc = sysfs_read_int(&cxt, "partition", &partno);
187 sysfs_deinit(&cxt);
188
189 if (rc == 0)
190 return partno;
191 }
192
193 sz = strlen(partition);
194 p = partition + sz - 1;
195
196 if (!isdigit((unsigned int) *p))
197 goto err;
198
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;
207 err:
208 errx(EXIT_FAILURE, _("%s: failed to get partition number"), partition);
209 }
210
211 static int get_max_partno(const char *disk, dev_t devno)
212 {
213 char path[PATH_MAX], *parent, *dirname = NULL;
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
235 dirname = xstrdup(path);
236
237 while ((d = readdir(dir))) {
238 int fd;
239
240 if (!strcmp(d->d_name, ".") ||
241 !strcmp(d->d_name, ".."))
242 continue;
243 #ifdef _DIRENT_HAVE_D_TYPE
244 if (d->d_type != DT_DIR && d->d_type != DT_UNKNOWN)
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
251 fd = open_at(dirfd(dir), dirname, path, O_RDONLY);
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);
259 }
260 }
261 }
262
263 free(dirname);
264 closedir(dir);
265 return partno;
266 dflt:
267 return SLICES_MAX;
268 }
269
270 static 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
279 static 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) {
299 warnx(_("specified range <%d:%d> "
300 "does not make sense"), lower, upper);
301 return -1;
302 }
303
304 for (i = lower; i <= upper; i++) {
305 rc = partx_del_partition(fd, i);
306 if (rc == 0) {
307 if (verbose)
308 printf(_("%s: partition #%d removed\n"), device, i);
309 continue;
310 } else if (errno == ENXIO) {
311 if (verbose)
312 printf(_("%s: partition #%d already doesn't exist\n"), device, i);
313 continue;
314 }
315 rc = -1;
316 if (verbose)
317 warn(_("%s: deleting partition #%d failed"), device, i);
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;
325 }
326 }
327
328 if (errfirst)
329 del_parts_warnx(device, errfirst, errlast);
330 return rc;
331 }
332
333
334 static 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
343 static int add_parts(int fd, const char *device,
344 blkid_partlist ls, int lower, int upper)
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 /*
369 * Let's follow the Linux kernel and reduce
370 * DOS extended partition to 1 or 2 sectors.
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)
381 warn(_("%s: adding partition #%d failed"), device, n);
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 }
390 }
391
392 if (errfirst)
393 add_parts_warnx(device, errfirst, errlast);
394
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)) {
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
410 return rc;
411 }
412
413 static 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
422 static int upd_parts(int fd, const char *device, dev_t devno,
423 blkid_partlist ls, int lower, int upper)
424 {
425 int n, nparts, rc = 0, errfirst = 0, errlast = 0, err;
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
451 for (n = lower; n <= upper; n++) {
452 par = blkid_partlist_get_partition_by_partno(ls, n);
453 if (!par) {
454 if (verbose)
455 warn(_("%s: no partition #%d"), device, n);
456 continue;
457 }
458
459 start = blkid_partition_get_start(par);
460 size = blkid_partition_get_size(par);
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 */
471 if (err == -1 && errno == EBUSY)
472 {
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)
478 continue;
479 }
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
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
506 static 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
527 printf(P_("#%2d: %9ju-%9ju (%9ju sector, %6ju MB)\n",
528 "#%2d: %9ju-%9ju (%9ju sectors, %6ju MB)\n",
529 size),
530 n, start, start + size -1,
531 size, (size << 9) / 1000000);
532 }
533 return 0;
534 }
535
536 static int add_scols_line(struct libscols_table *table, blkid_partition par)
537 {
538 struct libscols_line *line;
539 int i, rc = 0;
540
541 assert(table);
542 assert(par);
543
544 line = scols_table_new_line(table, NULL);
545 if (!line) {
546 warn(_("failed to add line to output"));
547 return -ENOMEM;
548 }
549
550 for (i = 0; i < ncolumns; i++) {
551 char *str = NULL; /* allocated string */
552 const char *cstr = NULL; /* foreign string */
553
554 switch (get_column_id(i)) {
555 case COL_PARTNO:
556 xasprintf(&str, "%d", blkid_partition_get_partno(par));
557 break;
558 case COL_START:
559 xasprintf(&str, "%ju", blkid_partition_get_start(par));
560 break;
561 case COL_END:
562 xasprintf(&str, "%ju",
563 blkid_partition_get_start(par) +
564 blkid_partition_get_size(par) - 1);
565 break;
566 case COL_SECTORS:
567 xasprintf(&str, "%ju", blkid_partition_get_size(par));
568 break;
569 case COL_SIZE:
570 if (partx_flags & FL_BYTES)
571 xasprintf(&str, "%ju", (uintmax_t)
572 blkid_partition_get_size(par) << 9);
573 else
574 str = size_to_human_string(SIZE_SUFFIX_1LETTER,
575 blkid_partition_get_size(par) << 9);
576 break;
577 case COL_NAME:
578 cstr = blkid_partition_get_name(par);
579 break;
580 case COL_UUID:
581 cstr = blkid_partition_get_uuid(par);
582 break;
583 case COL_TYPE:
584 if (blkid_partition_get_type_string(par))
585 cstr = blkid_partition_get_type_string(par);
586 else
587 xasprintf(&str, "0x%x",
588 blkid_partition_get_type(par));
589 break;
590 case COL_FLAGS:
591 xasprintf(&str, "0x%llx", blkid_partition_get_flags(par));
592 break;
593 case COL_SCHEME:
594 {
595 blkid_parttable tab = blkid_partition_get_table(par);
596 if (tab)
597 cstr = blkid_parttable_get_type(tab);
598 break;
599 }
600 default:
601 break;
602 }
603
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 }
612 }
613
614 return rc;
615 }
616
617 static int show_parts(blkid_partlist ls, int scols_flags, int lower, int upper)
618 {
619 int i, rc = -1;
620 struct libscols_table *table;
621 int nparts;
622
623 assert(ls);
624
625 nparts = blkid_partlist_numof_partitions(ls);
626 if (!nparts)
627 return 0;
628
629 scols_init_debug(0);
630 table = scols_new_table();
631 if (!table) {
632 warn(_("failed to initialize output table"));
633 return -1;
634 }
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));
638
639 for (i = 0; i < ncolumns; i++) {
640 struct colinfo *col = get_column_info(i);
641
642 if (!scols_table_new_column(table, col->name, col->whint, col->flags)) {
643 warnx(_("failed to initialize output column"));
644 goto done;
645 }
646 }
647
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);
651
652 if (lower && n < lower)
653 continue;
654 if (upper && n > upper)
655 continue;
656
657 rc = add_scols_line(table, par);
658 if (rc)
659 break;
660 }
661
662 rc = 0;
663 scols_print_table(table);
664 done:
665 scols_unref_table(table);
666 return rc;
667 }
668
669 static 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 "
684 "filter for '%s'"), type);
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);
696 if (verbose && tab) {
697 printf(_("%s: partition table type '%s' detected\n"),
698 device, blkid_parttable_get_type(tab));
699
700 if (!blkid_partlist_numof_partitions(ls))
701 printf(_("%s: partition table with no partitions"), device);
702 }
703
704 return ls;
705 }
706
707 static void __attribute__((__noreturn__)) usage(FILE *out)
708 {
709 size_t i;
710
711 fputs(USAGE_HEADER, out);
712 fprintf(out,
713 _(" %s [-a|-d|-s|-u] [--nr <n:m> | <partition>] <disk>\n"),
714 program_invocation_short_name);
715
716 fputs(USAGE_SEPARATOR, out);
717 fputs(_("Tell the kernel about the presence and numbering of partitions.\n"), out);
718
719 fputs(USAGE_OPTIONS, out);
720 fputs(_(" -a, --add add specified partitions or all of them\n"), out);
721 fputs(_(" -d, --delete delete specified partitions or all of them\n"), out);
722 fputs(_(" -u, --update update specified partitions or all of them\n"), out);
723 fputs(_(" -s, --show list partitions\n\n"), out);
724 fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out);
725 fputs(_(" -g, --noheadings don't print headings for --show\n"), out);
726 fputs(_(" -n, --nr <n:m> specify the range of partitions (e.g. --nr 2:4)\n"), out);
727 fputs(_(" -o, --output <list> define which output columns to use\n"), out);
728 fputs(_(" -P, --pairs use key=\"value\" output format\n"), out);
729 fputs(_(" -r, --raw use raw output format\n"), out);
730 fputs(_(" -t, --type <type> specify the partition type (dos, bsd, solaris, etc.)\n"), out);
731 fputs(_(" -v, --verbose verbose mode\n"), out);
732
733 fputs(USAGE_SEPARATOR, out);
734 fputs(USAGE_HELP, out);
735 fputs(USAGE_VERSION, out);
736
737 fputs(_("\nAvailable columns (for --show, --raw or --pairs):\n"), out);
738
739 for (i = 0; i < NCOLS; i++)
740 fprintf(out, " %10s %s\n", infos[i].name, _(infos[i].help));
741
742 fprintf(out, USAGE_MAN_TAIL("partx(8)"));
743
744 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
745 }
746
747 int main(int argc, char **argv)
748 {
749 int fd, c, what = ACT_NONE, lower = 0, upper = 0, rc = 0;
750 int scols_flags = 0;
751 char *type = NULL;
752 char *device = NULL; /* pointer to argv[], ie: /dev/sda1 */
753 char *wholedisk = NULL; /* allocated, ie: /dev/sda */
754 char *outarg = NULL;
755 dev_t disk_devno = 0, part_devno = 0;
756
757 static const struct option long_opts[] = {
758 { "bytes", no_argument, NULL, 'b' },
759 { "noheadings", no_argument, NULL, 'g' },
760 { "raw", no_argument, NULL, 'r' },
761 { "list", no_argument, NULL, 'l' },
762 { "show", no_argument, NULL, 's' },
763 { "add", no_argument, NULL, 'a' },
764 { "delete", no_argument, NULL, 'd' },
765 { "update", no_argument, NULL, 'u' },
766 { "type", required_argument, NULL, 't' },
767 { "nr", required_argument, NULL, 'n' },
768 { "output", required_argument, NULL, 'o' },
769 { "pairs", no_argument, NULL, 'P' },
770 { "help", no_argument, NULL, 'h' },
771 { "version", no_argument, NULL, 'V' },
772 { "verbose", no_argument, NULL, 'v' },
773 { NULL, 0, NULL, 0 }
774 };
775
776 static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
777 { 'P','a','d','l','r','s' },
778 { 0 }
779 };
780 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
781
782 setlocale(LC_ALL, "");
783 bindtextdomain(PACKAGE, LOCALEDIR);
784 textdomain(PACKAGE);
785 atexit(close_stdout);
786
787 while ((c = getopt_long(argc, argv,
788 "abdglrsuvn:t:o:PhV", long_opts, NULL)) != -1) {
789
790 err_exclusive_options(c, long_opts, excl, excl_st);
791
792 switch(c) {
793 case 'a':
794 what = ACT_ADD;
795 break;
796 case 'b':
797 partx_flags |= FL_BYTES;
798 break;
799 case 'd':
800 what = ACT_DELETE;
801 break;
802 case 'g':
803 scols_flags |= PARTX_NOHEADINGS;
804 break;
805 case 'l':
806 what = ACT_LIST;
807 break;
808 case 'n':
809 if (parse_range(optarg, &lower, &upper, 0))
810 errx(EXIT_FAILURE, _("failed to parse --nr <M-N> range"));
811 break;
812 case 'o':
813 outarg = optarg;
814 break;
815 case 'P':
816 scols_flags |= PARTX_EXPORT;
817 what = ACT_SHOW;
818 break;
819 case 'r':
820 scols_flags |= PARTX_RAW;
821 what = ACT_SHOW;
822 break;
823 case 's':
824 what = ACT_SHOW;
825 break;
826 case 't':
827 type = optarg;
828 break;
829 case 'u':
830 what = ACT_UPD;
831 break;
832 case 'v':
833 verbose = 1;
834 break;
835 case 'h':
836 usage(stdout);
837 case 'V':
838 printf(UTIL_LINUX_VERSION);
839 return EXIT_SUCCESS;
840 case '?':
841 default:
842 usage(stderr);
843 }
844 }
845
846 if (what == ACT_NONE)
847 what = ACT_SHOW;
848
849 /* --show default, could by modified by -o */
850 if (what == ACT_SHOW && !ncolumns) {
851 columns[ncolumns++] = COL_PARTNO;
852 columns[ncolumns++] = COL_START;
853 columns[ncolumns++] = COL_END;
854 columns[ncolumns++] = COL_SECTORS;
855 columns[ncolumns++] = COL_SIZE;
856 columns[ncolumns++] = COL_NAME;
857 columns[ncolumns++] = COL_UUID;
858 }
859
860 if (what == ACT_SHOW && outarg &&
861 string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
862 &ncolumns, column_name_to_id) < 0)
863 return EXIT_FAILURE;
864
865 /*
866 * Note that 'partx /dev/sda1' == 'partx /dev/sda1 /dev/sda'
867 * so assume that the device and/or disk are always the last
868 * arguments to be passed to partx.
869 */
870 if (optind == argc - 2) {
871 /* passed 2 arguments:
872 * /dev/sda1 /dev/sda : partition + whole-disk
873 * -- /dev/sda1 : partition that should be used as a whole-disk
874 */
875 device = argv[optind];
876
877 if (strcmp(device, "-") == 0) {
878 device = NULL;
879 wholedisk = xstrdup(argv[optind + 1]);
880 } else {
881 device = argv[optind];
882 wholedisk = xstrdup(argv[optind + 1]);
883 }
884 } else if (optind == argc - 1) {
885 /* passed only one arg (ie: /dev/sda3 or /dev/sda) */
886 struct stat sb;
887
888 device = argv[optind];
889
890 if (stat(device, &sb))
891 err(EXIT_FAILURE, _("stat failed %s"), device);
892
893 part_devno = sb.st_rdev;
894
895 if (blkid_devno_to_wholedisk(part_devno,
896 NULL, 0, &disk_devno) == 0 &&
897 part_devno != disk_devno)
898 wholedisk = blkid_devno_to_devname(disk_devno);
899
900 if (!wholedisk) {
901 wholedisk = xstrdup(device);
902 disk_devno = part_devno;
903 device = NULL;
904 part_devno = 0;
905 }
906 } else
907 usage(stderr);
908
909 if (device && (upper || lower))
910 errx(EXIT_FAILURE, _("--nr and <partition> are mutually exclusive"));
911
912 assert(wholedisk);
913
914 if (device) {
915 /* use partno from given partition instead of --nr range, e.g:
916 * partx -d /dev/sda3
917 * is the same like:
918 * partx -d --nr 3 /dev/sda
919 */
920 struct stat sb;
921
922 if (!part_devno && !stat(device, &sb))
923 part_devno = sb.st_rdev;
924
925 lower = upper = get_partno_from_device(device, part_devno);
926 }
927
928 if (verbose)
929 printf(_("partition: %s, disk: %s, lower: %d, upper: %d\n"),
930 device ? device : "none", wholedisk, lower, upper);
931
932 if (what == ACT_ADD || what == ACT_DELETE) {
933 struct stat x;
934
935 if (stat(wholedisk, &x))
936 errx(EXIT_FAILURE, "%s", wholedisk);
937
938 if (S_ISREG(x.st_mode)) {
939 /* not a blkdev, try to associate it to a loop device */
940 if (what == ACT_DELETE)
941 errx(EXIT_FAILURE, _("%s: cannot delete partitions"),
942 wholedisk);
943 if (!loopmod_supports_partscan())
944 errx(EXIT_FAILURE, _("%s: partitioned loop devices unsupported"),
945 wholedisk);
946 assoc_loopdev(wholedisk);
947 wholedisk = xstrdup(lc.device);
948 } else if (!S_ISBLK(x.st_mode))
949 errx(EXIT_FAILURE, _("%s: not a block device"), wholedisk);
950 }
951 if ((fd = open(wholedisk, O_RDONLY)) == -1)
952 err(EXIT_FAILURE, _("cannot open %s"), wholedisk);
953
954 if (what == ACT_DELETE)
955 rc = del_parts(fd, wholedisk, disk_devno, lower, upper);
956 else {
957 blkid_probe pr = blkid_new_probe();
958 blkid_partlist ls = NULL;
959
960 if (!pr || blkid_probe_set_device(pr, fd, 0, 0))
961 warnx(_("%s: failed to initialize blkid prober"),
962 wholedisk);
963 else
964 ls = get_partlist(pr, wholedisk, type);
965
966 if (ls) {
967 int n = blkid_partlist_numof_partitions(ls);
968
969 if (lower < 0)
970 lower = n + lower + 1;
971 if (upper < 0)
972 upper = n + upper + 1;
973 if (lower > upper) {
974 warnx(_("specified range <%d:%d> "
975 "does not make sense"), lower, upper);
976 rc = -1, what = ACT_NONE;
977 }
978
979 switch (what) {
980 case ACT_SHOW:
981 rc = show_parts(ls, scols_flags, lower, upper);
982 break;
983 case ACT_LIST:
984 rc = list_parts(ls, lower, upper);
985 break;
986 case ACT_ADD:
987 rc = add_parts(fd, wholedisk, ls, lower, upper);
988 break;
989 case ACT_UPD:
990 rc = upd_parts(fd, wholedisk, disk_devno, ls, lower, upper);
991 case ACT_NONE:
992 break;
993 default:
994 abort();
995 }
996 }
997 blkid_free_probe(pr);
998 }
999
1000 if (loopdev)
1001 loopcxt_deinit(&lc);
1002
1003 if (close_fd(fd) != 0)
1004 err(EXIT_FAILURE, _("write failed"));
1005
1006 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
1007 }