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.]
7 * aeb, 2000-03-21 -- sah is 42 now
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>
26 #include "pathnames.h"
36 #include "closestream.h"
39 /* this is the default upper limit, could be modified by --nr */
40 #define SLICES_MAX 256
42 /* all the columns (-o option) */
56 #define ACT_ERROR "--{add,delete,show,list,raw,pairs}"
72 const char *name
; /* header */
73 double whint
; /* width hint (N < 1 is in percent of termwidth) */
74 int flags
; /* TT_FL_* */
78 /* columns descriptions */
79 struct colinfo infos
[] = {
80 [COL_PARTNO
] = { "NR", 0.25, TT_FL_RIGHT
, N_("partition number") },
81 [COL_START
] = { "START", 0.30, TT_FL_RIGHT
, N_("start of the partition in sectors") },
82 [COL_END
] = { "END", 0.30, TT_FL_RIGHT
, N_("end of the partition in sectors") },
83 [COL_SECTORS
] = { "SECTORS", 0.30, TT_FL_RIGHT
, N_("number of sectors") },
84 [COL_SIZE
] = { "SIZE", 0.30, TT_FL_RIGHT
, N_("human readable size") },
85 [COL_NAME
] = { "NAME", 0.30, TT_FL_TRUNC
, N_("partition name") },
86 [COL_UUID
] = { "UUID", 36, 0, N_("partition UUID")},
87 [COL_SCHEME
] = { "SCHEME", 0.1, TT_FL_TRUNC
, N_("partition table type (dos, gpt, ...)")},
88 [COL_FLAGS
] = { "FLAGS", 0.1, TT_FL_TRUNC
, N_("partition flags")},
89 [COL_TYPE
] = { "TYPE", 1, TT_FL_RIGHT
, N_("partition type (a string, a UUID, or hex)")},
92 #define NCOLS ARRAY_SIZE(infos)
94 /* array with IDs of enabled columns */
95 static int columns
[NCOLS
], ncolumns
;
98 static int partx_flags
;
99 static struct loopdev_cxt lc
;
102 static void assoc_loopdev(const char *fname
)
106 if (loopcxt_init(&lc
, 0))
107 err(EXIT_FAILURE
, _("failed to initialize loopcxt"));
109 rc
= loopcxt_find_unused(&lc
);
111 err(EXIT_FAILURE
, _("%s: failed to find unused loop device"),
115 printf(_("Trying to use '%s' for the loop device\n"),
116 loopcxt_get_device(&lc
));
118 if (loopcxt_set_backing_file(&lc
, fname
))
119 err(EXIT_FAILURE
, _("%s: failed to set backing file"), fname
);
121 rc
= loopcxt_setup_device(&lc
);
124 err(EXIT_FAILURE
, _("%s: failed to set up loop device"), fname
);
129 static inline int get_column_id(int num
)
131 assert(ARRAY_SIZE(columns
) == NCOLS
);
132 assert(num
< ncolumns
);
133 assert(columns
[num
] < (int) NCOLS
);
137 static inline struct colinfo
*get_column_info(int num
)
139 return &infos
[ get_column_id(num
) ];
142 static int column_name_to_id(const char *name
, size_t namesz
)
148 for (i
= 0; i
< NCOLS
; i
++) {
149 const char *cn
= infos
[i
].name
;
151 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
154 warnx(_("unknown column: %s"), name
);
159 * Given a partition return the corresponding partition number.
161 * Note that this function tries to use sysfs, otherwise it assumes that the
162 * last characters are always numeric (sda1, sdc20, etc).
164 static int get_partno_from_device(char *partition
, dev_t devno
)
168 char *p
, *end
= NULL
;
173 struct sysfs_cxt cxt
;
176 if (sysfs_init(&cxt
, devno
, NULL
))
179 rc
= sysfs_read_int(&cxt
, "partition", &partno
);
186 sz
= strlen(partition
);
187 p
= partition
+ sz
- 1;
189 if (!isdigit((unsigned int) *p
))
192 while (isdigit((unsigned int) *(p
- 1))) p
--;
195 partno
= strtol(p
, &end
, 10);
196 if (errno
|| !end
|| *end
|| p
== end
)
201 errx(EXIT_FAILURE
, _("%s: failed to get partition number"), partition
);
204 static int get_max_partno(const char *disk
, dev_t devno
)
206 char path
[PATH_MAX
], *parent
, *dirname
= NULL
;
212 if (!devno
&& !stat(disk
, &st
))
216 parent
= strrchr(disk
, '/');
221 snprintf(path
, sizeof(path
), _PATH_SYS_DEVBLOCK
"/%d:%d/",
222 major(devno
), minor(devno
));
228 dirname
= xstrdup(path
);
230 while ((d
= readdir(dir
))) {
233 if (!strcmp(d
->d_name
, ".") ||
234 !strcmp(d
->d_name
, ".."))
236 #ifdef _DIRENT_HAVE_D_TYPE
237 if (d
->d_type
!= DT_DIR
&& d
->d_type
!= DT_UNKNOWN
)
240 if (strncmp(parent
, d
->d_name
, strlen(parent
)))
242 snprintf(path
, sizeof(path
), "%s/partition", d
->d_name
);
244 fd
= open_at(dirfd(dir
), dirname
, path
, O_RDONLY
);
247 FILE *f
= fdopen(fd
, "r");
249 if (fscanf(f
, "%d", &x
) == 1 && x
> partno
)
263 static void del_parts_warnx(const char *device
, int first
, int last
)
266 warnx(_("%s: error deleting partition %d"), device
, first
);
268 warnx(_("%s: error deleting partitions %d-%d"),
269 device
, first
, last
);
272 static int del_parts(int fd
, const char *device
, dev_t devno
,
273 int lower
, int upper
)
275 int rc
= 0, i
, errfirst
= 0, errlast
= 0;
282 if (!upper
|| lower
< 0 || upper
< 0) {
283 int n
= get_max_partno(device
, devno
);
287 upper
= n
+ upper
+ 1;
289 lower
= n
+ lower
+ 1;
292 warnx(_("specified range <%d:%d> "
293 "does not make sense"), lower
, upper
);
297 for (i
= lower
; i
<= upper
; i
++) {
298 rc
= partx_del_partition(fd
, i
);
301 printf(_("%s: partition #%d removed\n"), device
, i
);
303 } else if (errno
== ENXIO
) {
305 printf(_("%s: partition #%d already doesn't exist\n"), device
, i
);
310 warn(_("%s: deleting partition #%d failed"), device
, i
);
312 errlast
= errfirst
= i
;
313 else if (errlast
+ 1 == i
)
316 del_parts_warnx(device
, errfirst
, errlast
);
317 errlast
= errfirst
= i
;
322 del_parts_warnx(device
, errfirst
, errlast
);
327 static void add_parts_warnx(const char *device
, int first
, int last
)
330 warnx(_("%s: error adding partition %d"), device
, first
);
332 warnx(_("%s: error adding partitions %d-%d"),
333 device
, first
, last
);
336 static int add_parts(int fd
, const char *device
,
337 blkid_partlist ls
, int lower
, int upper
)
339 int i
, nparts
, rc
= 0, errfirst
= 0, errlast
= 0;
345 nparts
= blkid_partlist_numof_partitions(ls
);
347 for (i
= 0; i
< nparts
; i
++) {
348 blkid_partition par
= blkid_partlist_get_partition(ls
, i
);
349 int n
= blkid_partition_get_partno(par
);
350 uintmax_t start
, size
;
352 if (lower
&& n
< lower
)
354 if (upper
&& n
> upper
)
357 start
= blkid_partition_get_start(par
);
358 size
= blkid_partition_get_size(par
);
360 if (blkid_partition_is_extended(par
))
362 * Let's follow the Linux kernel and reduce
363 * DOS extended partition to 1 or 2 sectors.
365 size
= min(size
, (uintmax_t) 2);
367 if (partx_add_partition(fd
, n
, start
, size
) == 0) {
369 printf(_("%s: partition #%d added\n"), device
, n
);
374 warn(_("%s: adding partition #%d failed"), device
, n
);
376 errlast
= errfirst
= n
;
377 else if (errlast
+ 1 == n
)
380 add_parts_warnx(device
, errfirst
, errlast
);
381 errlast
= errfirst
= n
;
386 add_parts_warnx(device
, errfirst
, errlast
);
389 * The kernel with enabled partitions scanner for loop devices add *all*
390 * partitions, so we should delete any extra, unwanted ones, when the -n
393 if (loopdev
&& loopcxt_is_partscan(&lc
) && (lower
|| upper
)) {
394 for (i
= 0; i
< nparts
; i
++) {
395 blkid_partition par
= blkid_partlist_get_partition(ls
, i
);
396 int n
= blkid_partition_get_partno(par
);
398 if (n
< lower
|| n
> upper
)
399 partx_del_partition(fd
, n
);
406 static void upd_parts_warnx(const char *device
, int first
, int last
)
409 warnx(_("%s: error updating partition %d"), device
, first
);
411 warnx(_("%s: error updating partitions %d-%d"),
412 device
, first
, last
);
415 static int upd_parts(int fd
, const char *device
, dev_t devno
,
416 blkid_partlist ls
, int lower
, int upper
)
418 int n
, nparts
, rc
= 0, errfirst
= 0, errlast
= 0, err
;
420 uintmax_t start
, size
;
426 nparts
= blkid_partlist_numof_partitions(ls
);
429 if (!upper
|| lower
< 0 || upper
< 0) {
430 n
= get_max_partno(device
, devno
);
432 upper
= n
> nparts
? n
: nparts
;
434 upper
= n
+ upper
+ 1;
436 lower
= n
+ lower
+ 1;
439 warnx(_("specified range <%d:%d> "
440 "does not make sense"), lower
, upper
);
444 for (n
= lower
; n
<= upper
; n
++) {
445 par
= blkid_partlist_get_partition_by_partno(ls
, n
);
448 warn(_("%s: no partition #%d"), device
, n
);
452 start
= blkid_partition_get_start(par
);
453 size
= blkid_partition_get_size(par
);
454 if (blkid_partition_is_extended(par
))
456 * Let's follow the Linux kernel and reduce
457 * DOS extended partition to 1 or 2 sectors.
459 size
= min(size
, (uintmax_t) 2);
461 err
= partx_del_partition(fd
, n
);
462 if (err
== -1 && errno
== ENXIO
)
463 err
= 0; /* good, it already doesn't exist */
464 if (err
== -1 && errno
== EBUSY
)
467 err
= partx_resize_partition(fd
, n
, start
, size
);
469 printf(_("%s: partition #%d resized\n"), device
, n
);
473 if (err
== 0 && partx_add_partition(fd
, n
, start
, size
) == 0) {
475 printf(_("%s: partition #%d added\n"), device
, n
);
483 warn(_("%s: updating partition #%d failed"), device
, n
);
485 errlast
= errfirst
= n
;
486 else if (errlast
+ 1 == n
)
489 upd_parts_warnx(device
, errfirst
, errlast
);
490 errlast
= errfirst
= n
;
495 upd_parts_warnx(device
, errfirst
, errlast
);
499 static int list_parts(blkid_partlist ls
, int lower
, int upper
)
505 nparts
= blkid_partlist_numof_partitions(ls
);
507 for (i
= 0; i
< nparts
; i
++) {
508 blkid_partition par
= blkid_partlist_get_partition(ls
, i
);
509 int n
= blkid_partition_get_partno(par
);
510 uintmax_t start
, size
;
512 if (lower
&& n
< lower
)
514 if (upper
&& n
> upper
)
517 start
= blkid_partition_get_start(par
);
518 size
= blkid_partition_get_size(par
);
520 printf(P_("#%2d: %9ju-%9ju (%9ju sector, %6ju MB)\n",
521 "#%2d: %9ju-%9ju (%9ju sectors, %6ju MB)\n",
523 n
, start
, start
+ size
-1,
524 size
, (size
<< 9) / 1000000);
529 static void add_tt_line(struct tt
*tt
, blkid_partition par
)
531 struct tt_line
*line
;
537 line
= tt_add_line(tt
, NULL
);
539 warn(_("failed to add line to output"));
543 for (i
= 0; i
< ncolumns
; i
++) {
546 switch (get_column_id(i
)) {
548 xasprintf(&str
, "%d", blkid_partition_get_partno(par
));
551 xasprintf(&str
, "%ju", blkid_partition_get_start(par
));
554 xasprintf(&str
, "%ju",
555 blkid_partition_get_start(par
) +
556 blkid_partition_get_size(par
) - 1);
559 xasprintf(&str
, "%ju", blkid_partition_get_size(par
));
562 if (partx_flags
& FL_BYTES
)
563 xasprintf(&str
, "%ju", (uintmax_t)
564 blkid_partition_get_size(par
) << 9);
566 str
= size_to_human_string(SIZE_SUFFIX_1LETTER
,
567 blkid_partition_get_size(par
) << 9);
570 str
= xstrdup(blkid_partition_get_name(par
));
573 str
= xstrdup(blkid_partition_get_uuid(par
));
576 if (blkid_partition_get_type_string(par
))
577 str
= xstrdup(blkid_partition_get_type_string(par
));
579 xasprintf(&str
, "0x%x",
580 blkid_partition_get_type(par
));
583 xasprintf(&str
, "0x%llx", blkid_partition_get_flags(par
));
587 blkid_parttable tab
= blkid_partition_get_table(par
);
589 str
= xstrdup(blkid_parttable_get_type(tab
));
597 tt_line_set_data(line
, i
, str
);
601 static int show_parts(blkid_partlist ls
, int tt_flags
, int lower
, int upper
)
609 nparts
= blkid_partlist_numof_partitions(ls
);
613 tt
= tt_new_table(tt_flags
| TT_FL_FREEDATA
);
615 warn(_("failed to initialize output table"));
619 for (i
= 0; i
< ncolumns
; i
++) {
620 struct colinfo
*col
= get_column_info(i
);
622 if (!tt_define_column(tt
, col
->name
, col
->whint
, col
->flags
)) {
623 warnx(_("failed to initialize output column"));
628 for (i
= 0; i
< nparts
; i
++) {
629 blkid_partition par
= blkid_partlist_get_partition(ls
, i
);
630 int n
= blkid_partition_get_partno(par
);
632 if (lower
&& n
< lower
)
634 if (upper
&& n
> upper
)
637 add_tt_line(tt
, par
);
647 static blkid_partlist
get_partlist(blkid_probe pr
,
648 const char *device
, char *type
)
657 char *name
[] = { type
, NULL
};
659 if (blkid_probe_filter_partitions_type(pr
,
660 BLKID_FLTR_ONLYIN
, name
)) {
661 warnx(_("failed to initialize blkid "
662 "filter for '%s'"), type
);
667 ls
= blkid_probe_get_partitions(pr
);
669 warnx(_("%s: failed to read partition table"), device
);
673 tab
= blkid_partlist_get_table(ls
);
674 if (verbose
&& tab
) {
675 printf(_("%s: partition table type '%s' detected\n"),
676 device
, blkid_parttable_get_type(tab
));
678 if (!blkid_partlist_numof_partitions(ls
))
679 printf(_("%s: partition table with no partitions"), device
);
685 static void __attribute__((__noreturn__
)) usage(FILE *out
)
689 fputs(USAGE_HEADER
, out
);
691 _(" %s [-a|-d|-s|-u] [--nr <n:m> | <partition>] <disk>\n"),
692 program_invocation_short_name
);
694 fputs(USAGE_OPTIONS
, out
);
695 fputs(_(" -a, --add add specified partitions or all of them\n"), out
);
696 fputs(_(" -d, --delete delete specified partitions or all of them\n"), out
);
697 fputs(_(" -u, --update update specified partitions or all of them\n"), out
);
698 fputs(_(" -s, --show list partitions\n\n"), out
);
699 fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out
);
700 fputs(_(" -g, --noheadings don't print headings for --show\n"), out
);
701 fputs(_(" -n, --nr <n:m> specify the range of partitions (e.g. --nr 2:4)\n"), out
);
702 fputs(_(" -o, --output <list> define which output columns to use\n"), out
);
703 fputs(_(" -P, --pairs use key=\"value\" output format\n"), out
);
704 fputs(_(" -r, --raw use raw output format\n"), out
);
705 fputs(_(" -t, --type <type> specify the partition type (dos, bsd, solaris, etc.)\n"), out
);
706 fputs(_(" -v, --verbose verbose mode\n"), out
);
708 fputs(USAGE_SEPARATOR
, out
);
709 fputs(USAGE_HELP
, out
);
710 fputs(USAGE_VERSION
, out
);
712 fputs(_("\nAvailable columns (for --show, --raw or --pairs):\n"), out
);
714 for (i
= 0; i
< NCOLS
; i
++)
715 fprintf(out
, " %10s %s\n", infos
[i
].name
, _(infos
[i
].help
));
717 fprintf(out
, USAGE_MAN_TAIL("partx(8)"));
719 exit(out
== stderr
? EXIT_FAILURE
: EXIT_SUCCESS
);
722 int main(int argc
, char **argv
)
724 int fd
, c
, what
= ACT_NONE
, lower
= 0, upper
= 0, rc
= 0;
727 char *device
= NULL
; /* pointer to argv[], ie: /dev/sda1 */
728 char *wholedisk
= NULL
; /* allocated, ie: /dev/sda */
730 dev_t disk_devno
= 0, part_devno
= 0;
732 static const struct option long_opts
[] = {
733 { "bytes", no_argument
, NULL
, 'b' },
734 { "noheadings", no_argument
, NULL
, 'g' },
735 { "raw", no_argument
, NULL
, 'r' },
736 { "list", no_argument
, NULL
, 'l' },
737 { "show", no_argument
, NULL
, 's' },
738 { "add", no_argument
, NULL
, 'a' },
739 { "delete", no_argument
, NULL
, 'd' },
740 { "update", no_argument
, NULL
, 'u' },
741 { "type", required_argument
, NULL
, 't' },
742 { "nr", required_argument
, NULL
, 'n' },
743 { "output", required_argument
, NULL
, 'o' },
744 { "pairs", no_argument
, NULL
, 'P' },
745 { "help", no_argument
, NULL
, 'h' },
746 { "version", no_argument
, NULL
, 'V' },
747 { "verbose", no_argument
, NULL
, 'v' },
751 static const ul_excl_t excl
[] = { /* rows and cols in in ASCII order */
752 { 'P','a','d','l','r','s' },
755 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
757 setlocale(LC_ALL
, "");
758 bindtextdomain(PACKAGE
, LOCALEDIR
);
760 atexit(close_stdout
);
762 while ((c
= getopt_long(argc
, argv
,
763 "abdglrsuvn:t:o:PhV", long_opts
, NULL
)) != -1) {
765 err_exclusive_options(c
, long_opts
, excl
, excl_st
);
772 partx_flags
|= FL_BYTES
;
778 tt_flags
|= TT_FL_NOHEADINGS
;
784 if (parse_range(optarg
, &lower
, &upper
, 0))
785 errx(EXIT_FAILURE
, _("failed to parse --nr <M-N> range"));
791 tt_flags
|= TT_FL_EXPORT
;
795 tt_flags
|= TT_FL_RAW
;
813 printf(UTIL_LINUX_VERSION
);
821 if (what
== ACT_NONE
)
824 /* --show default, could by modified by -o */
825 if (what
== ACT_SHOW
&& !ncolumns
) {
826 columns
[ncolumns
++] = COL_PARTNO
;
827 columns
[ncolumns
++] = COL_START
;
828 columns
[ncolumns
++] = COL_END
;
829 columns
[ncolumns
++] = COL_SECTORS
;
830 columns
[ncolumns
++] = COL_SIZE
;
831 columns
[ncolumns
++] = COL_NAME
;
832 columns
[ncolumns
++] = COL_UUID
;
835 if (what
== ACT_SHOW
&& outarg
&&
836 string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
837 &ncolumns
, column_name_to_id
) < 0)
841 * Note that 'partx /dev/sda1' == 'partx /dev/sda1 /dev/sda'
842 * so assume that the device and/or disk are always the last
843 * arguments to be passed to partx.
845 if (optind
== argc
- 2) {
846 /* passed 2 arguments:
847 * /dev/sda1 /dev/sda : partition + whole-disk
848 * -- /dev/sda1 : partition that should be used as a whole-disk
850 device
= argv
[optind
];
852 if (strcmp(device
, "-") == 0) {
854 wholedisk
= xstrdup(argv
[optind
+ 1]);
856 device
= argv
[optind
];
857 wholedisk
= xstrdup(argv
[optind
+ 1]);
859 } else if (optind
== argc
- 1) {
860 /* passed only one arg (ie: /dev/sda3 or /dev/sda) */
863 device
= argv
[optind
];
865 if (stat(device
, &sb
))
866 err(EXIT_FAILURE
, _("stat failed %s"), device
);
868 part_devno
= sb
.st_rdev
;
870 if (blkid_devno_to_wholedisk(part_devno
,
871 NULL
, 0, &disk_devno
) == 0 &&
872 part_devno
!= disk_devno
)
873 wholedisk
= blkid_devno_to_devname(disk_devno
);
876 wholedisk
= xstrdup(device
);
877 disk_devno
= part_devno
;
884 if (device
&& (upper
|| lower
))
885 errx(EXIT_FAILURE
, _("--nr and <partition> are mutually exclusive"));
890 /* use partno from given partition instead of --nr range, e.g:
893 * partx -d --nr 3 /dev/sda
897 if (!part_devno
&& !stat(device
, &sb
))
898 part_devno
= sb
.st_rdev
;
900 lower
= upper
= get_partno_from_device(device
, part_devno
);
904 printf(_("partition: %s, disk: %s, lower: %d, upper: %d\n"),
905 device
? device
: "none", wholedisk
, lower
, upper
);
907 if (what
== ACT_ADD
|| what
== ACT_DELETE
) {
910 if (stat(wholedisk
, &x
))
911 errx(EXIT_FAILURE
, "%s", wholedisk
);
913 if (S_ISREG(x
.st_mode
)) {
914 /* not a blkdev, try to associate it to a loop device */
915 if (what
== ACT_DELETE
)
916 errx(EXIT_FAILURE
, _("%s: cannot delete partitions"),
918 if (!loopmod_supports_partscan())
919 errx(EXIT_FAILURE
, _("%s: partitioned loop devices unsupported"),
921 assoc_loopdev(wholedisk
);
922 wholedisk
= xstrdup(lc
.device
);
923 } else if (!S_ISBLK(x
.st_mode
))
924 errx(EXIT_FAILURE
, _("%s: not a block device"), wholedisk
);
926 if ((fd
= open(wholedisk
, O_RDONLY
)) == -1)
927 err(EXIT_FAILURE
, _("cannot open %s"), wholedisk
);
929 if (what
== ACT_DELETE
)
930 rc
= del_parts(fd
, wholedisk
, disk_devno
, lower
, upper
);
932 blkid_probe pr
= blkid_new_probe();
933 blkid_partlist ls
= NULL
;
935 if (!pr
|| blkid_probe_set_device(pr
, fd
, 0, 0))
936 warnx(_("%s: failed to initialize blkid prober"),
939 ls
= get_partlist(pr
, wholedisk
, type
);
942 int n
= blkid_partlist_numof_partitions(ls
);
945 lower
= n
+ lower
+ 1;
947 upper
= n
+ upper
+ 1;
949 warnx(_("specified range <%d:%d> "
950 "does not make sense"), lower
, upper
);
951 rc
= -1, what
= ACT_NONE
;
956 rc
= show_parts(ls
, tt_flags
, lower
, upper
);
959 rc
= list_parts(ls
, lower
, upper
);
962 rc
= add_parts(fd
, wholedisk
, ls
, lower
, upper
);
965 rc
= upd_parts(fd
, wholedisk
, disk_devno
, ls
, lower
, upper
);
972 blkid_free_probe(pr
);
978 if (close_fd(fd
) != 0)
979 err(EXIT_FAILURE
, _("write failed"));
981 return rc
? EXIT_FAILURE
: EXIT_SUCCESS
;