diff options
author | Davidlohr Bueso | 2010-12-09 21:54:17 +0100 |
---|---|---|
committer | Karel Zak | 2010-12-09 21:54:17 +0100 |
commit | c4ecaf21d59671ac7ec0bd26bd2c346c98c7771c (patch) | |
tree | cd89a4e8048653d693b7d9f4a1507479d1ad7314 /partx/partx.c | |
parent | mount: be more explicit about --move in mount.8 (diff) | |
download | kernel-qcow2-util-linux-c4ecaf21d59671ac7ec0bd26bd2c346c98c7771c.tar.gz kernel-qcow2-util-linux-c4ecaf21d59671ac7ec0bd26bd2c346c98c7771c.tar.xz kernel-qcow2-util-linux-c4ecaf21d59671ac7ec0bd26bd2c346c98c7771c.zip |
partx: complete rewrite
Co-Author: Karel Zak <kzak@redhat.com>
Signed-off-by: Davidlohr Bueso <dave@gnu.org>
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'partx/partx.c')
-rw-r--r-- | partx/partx.c | 1082 |
1 files changed, 756 insertions, 326 deletions
diff --git a/partx/partx.c b/partx/partx.c index aa5a2ad8f..e0f1f2a9b 100644 --- a/partx/partx.c +++ b/partx/partx.c @@ -1,404 +1,834 @@ /* - * Given a block device and a partition table type, - * try to parse the partition table, and list the - * contents. Optionally add or remove partitions. - * + * partx: tell the kernel about your disk's partitions * [This is not an fdisk - adding and removing partitions * is not a change of the disk, but just telling the kernel * about presence and numbering of on-disk partitions.] * - * Call: - * partx [-{l|a|d}] [--type TYPE] [--nr M-N] [partition] wholedisk - * where TYPE is {dos|bsd|solaris|unixware|gpt}. - * - * Read wholedisk and add all partitions: - * partx -a wholedisk - * - * Subdivide a partition into slices (and delete or shrink the partition): - * [Not easy: one needs the partition number of partition - - * that is the last 4 or 6 bits of the minor; it can also be found - * in /proc/partitions; but there is no good direct way.] - * partx -a partition wholedisk - * - * Delete all partitions from wholedisk: - * partx -d wholedisk - * - * Delete partitions M-N from wholedisk: - * partx -d --nr M-N wholedisk - * * aeb, 2000-03-21 -- sah is 42 now + * + * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org> + * Rewritten to use libblkid for util-linux-ng + * based on ideas from Karel Zak <kzak@redhat.com> */ #include <stdio.h> #include <fcntl.h> +#include <err.h> #include <errno.h> #include <stdlib.h> #include <string.h> +#include <ctype.h> #include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> #include <unistd.h> +#include <assert.h> #include <sys/ioctl.h> -#include <linux/hdreg.h> /* HDIO_GETGEO */ -#ifdef HAVE_LINUX_COMPILER_H -#include <linux/compiler.h> -#endif #include <linux/blkpg.h> +#include <dirent.h> + +#include <blkid.h> #include "c.h" +#include "pathnames.h" +#include "nls.h" +#include "tt.h" #include "blkdev.h" - +#include "strutils.h" +#include "xalloc.h" #include "partx.h" -#include "crc32.h" -static void errmerge(int err, int m, char *msg1, char *msg2); -#define MAXTYPES 64 -#define MAXSLICES 256 +/* this is the default upper limit, could be modified by --nr */ +#define SLICES_MAX 256 + +/* all the columns (-o option) */ +enum { + COL_PARTNO, + COL_START, + COL_END, + COL_SECTORS, + COL_SIZE, + COL_NAME, + COL_UUID, + COL_TYPE, + __NCOLUMNS +}; -struct slice slices[MAXSLICES]; +enum { + ACT_LIST = 1, + ACT_SHOW, + ACT_ADD, + ACT_DELETE +}; + +enum { + FL_BYTES = (1 << 1) +}; + +/* column names */ +struct colinfo { + const char *name; /* header */ + double whint; /* width hint (N < 1 is in percent of termwidth) */ + int flags; /* TT_FL_* */ + const char *help; +}; -enum action { LIST, ADD, DELETE }; +/* columns descriptions */ +struct colinfo infos[__NCOLUMNS] = { + [COL_PARTNO] = { "PART", 0.25, TT_FL_RIGHT, N_("partition number") }, + [COL_START] = { "START", 0.30, TT_FL_RIGHT, N_("start of the partition in sectors") }, + [COL_END] = { "END", 0.30, TT_FL_RIGHT, N_("end of the partition in sectors") }, + [COL_SECTORS] = { "SECTORS", 0.30, TT_FL_RIGHT, N_("number of sectors") }, + [COL_SIZE] = { "SIZE", 0.30, TT_FL_RIGHT, N_("human readable size") }, + [COL_NAME] = { "NAME", 0.30, TT_FL_TRUNC, N_("partition name") }, + [COL_UUID] = { "UUID", 36, 0, N_("partition UUID")}, + [COL_TYPE] = { "TYPE", 1, TT_FL_RIGHT, N_("partition type; Extended, Primary or Logical")}, +}; +/* array with IDs of enabled columns */ +static int columns[__NCOLUMNS], ncolumns; + +static int verbose; +static int partx_flags; -struct pt { - char *type; - ptreader *fn; -} pts[MAXTYPES]; -int ptct; -static void -addpts(char *t, ptreader f) +static inline int get_column_id(int num) { - if (ptct >= MAXTYPES) { - fprintf(stderr, "addpts: too many types\n"); - exit(1); - } - pts[ptct].type = t; - pts[ptct].fn = f; - ptct++; + assert(ARRAY_SIZE(columns) == __NCOLUMNS); + assert(num < ncolumns); + assert(columns[num] < __NCOLUMNS); + return columns[num]; } -static void -initpts(void) +static inline struct colinfo *get_column_info(int num) { - addpts("gpt", read_gpt_pt); - addpts("dos", read_dos_pt); - addpts("bsd", read_bsd_pt); - addpts("solaris", read_solaris_pt); - addpts("unixware", read_unixware_pt); - addpts("sun", read_sun_pt); - addpts("mac", read_mac_pt); + return &infos[ get_column_id(num) ]; } -static char short_opts[] = "ladgvn:t:"; -static const struct option long_opts[] = { - { "gpt", no_argument, NULL, 'g' }, - { "type", required_argument, NULL, 't' }, - { "nr", required_argument, NULL, 'n' }, - { NULL, 0, NULL, 0 } -}; +static int column_name_to_id(const char *name, size_t namesz) +{ + int i; -/* Used in gpt.c */ -int force_gpt=0; - -int -main(int argc, char **argv){ - int fd, fd2, c, i, j, k, n; - unsigned long long size; - struct hd_geometry g; - struct slice all; - struct blkpg_ioctl_arg a; - struct blkpg_partition pt; - struct pt *ptp; - enum action what = LIST; - char *p, *type, *diskdevice, *device; - int lower, upper; - int verbose = 0; - int ret = 0; - - initpts(); - init_crc32(); - - lower = upper = 0; - type = device = diskdevice = NULL; - - while ((c = getopt_long (argc, argv, short_opts, long_opts, NULL)) - != -1) switch(c) { - case 'l': - what = LIST; break; - case 'a': - what = ADD; break; - case 'd': - what = DELETE; break; - case 'g': - force_gpt = 1; break; - case 'n': - p = optarg; - lower = atoi(p); - p = strchr(p, '-'); - if (p) - upper = atoi(p+1); - else - upper = lower; - break; - case 't': - type = optarg; - break; - case 'v': - verbose = 1; - break; - case '?': - default: - fprintf(stderr, "unknown option\n"); - exit(1); - } + assert(name); - if (optind == argc-2) { - device = argv[optind]; - diskdevice = argv[optind+1]; - } else if (optind == argc-1) { - diskdevice = device = argv[optind]; - } else { - fprintf(stderr, "call: partx -opts [device] wholedisk\n"); - exit(1); - } + for (i = 0; i < __NCOLUMNS; i++) { + const char *cn = infos[i].name; - fd = open(diskdevice, O_RDONLY); - if (fd == -1) { - perror(diskdevice); - exit(1); + if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) + return i; } + warnx(_("unknown column: %s"), name); + return -1; +} - /* remove the indicated partitions from the kernel partition tables */ - if (what == DELETE) { - if (device != diskdevice) { - fprintf(stderr, - "call: partx -d [--nr M-N] wholedisk\n"); - exit(1); +/* + * Given a partition return the corresponding partition number. + * + * Note that this function tries to use sysfs, otherwise it assumes that the + * last characters are always numeric (sda1, sdc20, etc). + * + * Returns -1 on error. + */ +static int get_partno_from_device(char *partition, dev_t devno) +{ + int partno = 0; + size_t sz; + char *p, *end = NULL; + + assert(partition); + + if (devno) { + /* the device exits, read the partition number from /sys + * TODO: move this to stuff to lib/sysfs.c */ + char path[PATH_MAX]; + FILE *f; + + snprintf(path, sizeof(path), + _PATH_SYS_DEVBLOCK "/%d:%d/partition", + major(devno), minor(devno)); + f = fopen(path, "r"); + if (f) { + if (fscanf(f, "%d", &partno) != 1) + partno = 0; + fclose(f); } + if (partno) + return partno; + } - if (!lower) - lower = 1; + sz = strlen(partition); + p = partition + sz - 1; - while (upper == 0 || lower <= upper) { - int err; + if (!isdigit((unsigned int) *p)) + goto err; - if (lower > MAXSLICES) - break; - pt.pno = lower; - pt.start = 0; - pt.length = 0; - pt.devname[0] = 0; - pt.volname[0] = 0; - a.op = BLKPG_DEL_PARTITION; - a.flags = 0; - a.datalen = sizeof(pt); - a.data = &pt; - if (ioctl(fd, BLKPG, &a) == -1) - err = errno; - else - err = 0; - errmerge(err, lower, - "error deleting partition %d: ", - "error deleting partitions %d-%d: "); - /* expected errors: - EBUSY: mounted or in use as swap - ENXIO: no such nonempty partition - EINVAL: not wholedisk, or bad pno - EACCES/EPERM: permission denied - */ - if (err && err != EBUSY && err != ENXIO) { - ret = 1; - break; + while (isdigit((unsigned int) *(p - 1))) p--; + + errno = 0; + partno = strtol(p, &end, 10); + if (errno || !end || *end || p == end) + goto err; + + return partno; +err: + errx(EXIT_FAILURE, _("%s: failed to get partition number"), partition); +} + +static int get_max_partno(const char *disk, dev_t devno) +{ + char path[PATH_MAX], *parent; + struct stat st; + DIR *dir; + struct dirent *d; + int partno = 0; + + if (!devno && !stat(disk, &st)) + devno = st.st_rdev; + if (!devno) + goto dflt; + parent = strrchr(disk, '/'); + if (!parent) + goto dflt; + parent++; + + snprintf(path, sizeof(path), _PATH_SYS_DEVBLOCK "/%d:%d/", + major(devno), minor(devno)); + + dir = opendir(path); + if (!dir) + goto dflt; + + while ((d = readdir(dir))) { + int fd; + + if (!strcmp(d->d_name, ".") || + !strcmp(d->d_name, "..")) + continue; +#ifdef _DIRENT_HAVE_D_TYPE + if (d->d_type != DT_DIR) + continue; +#endif + if (strncmp(parent, d->d_name, strlen(parent))) + continue; + snprintf(path, sizeof(path), "%s/partition", d->d_name); + + fd = openat(dirfd(dir), path, O_RDONLY); + if (fd) { + int x = 0; + FILE *f = fdopen(fd, "r"); + if (f) { + if (fscanf(f, "%d", &x) == 1 && x > partno) + partno = x; + fclose(f); } - if (err == 0 && verbose) - printf("deleted partition %d\n", lower); - lower++; } - errmerge(0, 0, - "error deleting partition %d: ", - "error deleting partitions %d-%d: "); - return ret; } - if (device != diskdevice) { - fd2 = open(device, O_RDONLY); - if (fd2 == -1) { - perror(device); - exit(1); + closedir(dir); + return partno; +dflt: + return SLICES_MAX; +} + +static void del_parts_warnx(const char *device, int first, int last) +{ + if (first == last) + warnx(_("%s: error deleting partition %d"), device, first); + else + warnx(_("%s: error deleting partitions %d-%d"), + device, first, last); +} + +static int del_parts(int fd, const char *device, dev_t devno, + int lower, int upper) +{ + int rc = 0, i, errfirst = 0, errlast = 0; + + assert(fd >= 0); + assert(device); + + if (!lower) + lower = 1; + if (!upper || lower < 0 || upper < 0) { + int n = get_max_partno(device, devno); + if (!upper) + upper = n; + else if (upper < 0) + upper = n + upper + 1; + if (lower < 0) + lower = n + lower + 1; + } + if (lower > upper) { + warnx(_("defined range <%d:%d> " + "does not make sense"), lower, upper); + return -1; + } + + for (i = lower; i <= upper; i++) { + if (partx_del_partition(fd, i) == 0) { + if (verbose) + printf(_("%s: partition #%d removed\n"), device, i); + continue; + } + rc = -1; + if (verbose) + warn(_("%s: delete partition #%d failed"), device, i); + if (!errfirst) + errlast = errfirst = i; + else if (errlast + 1 == i) + errlast++; + else { + del_parts_warnx(device, errfirst, errlast); + errlast = errfirst = i; } - } else { - fd2 = fd; } - if (ioctl(fd, HDIO_GETGEO, &g)) { - perror("HDIO_GETGEO"); - exit(1); + if (errfirst) + del_parts_warnx(device, errfirst, errlast); + return rc; +} + +static void add_parts_warnx(const char *device, int first, int last) +{ + if (first == last) + warnx(_("%s: error adding partition %d"), device, first); + else + warnx(_("%s: error adding partitions %d-%d"), + device, first, last); +} + +static int add_parts(int fd, const char *device, + blkid_partlist ls, int lower, int upper) +{ + int i, nparts, rc = 0, errfirst = 0, errlast = 0; + + assert(fd >= 0); + assert(device); + assert(ls); + + nparts = blkid_partlist_numof_partitions(ls); + + for (i = 0; i < nparts; i++) { + blkid_partition par = blkid_partlist_get_partition(ls, i); + int n = blkid_partition_get_partno(par); + uintmax_t start, size; + + if (lower && n < lower) + continue; + if (upper && n > upper) + continue; + + start = blkid_partition_get_start(par); + size = blkid_partition_get_size(par); + + if (blkid_partition_is_extended(par)) + /* + * Let's follow Linux kernel and reduce + * DOS extended partition to 1 or 2 sectors + */ + size = min(size, (uintmax_t) 2); + + if (partx_add_partition(fd, n, start, size) == 0) { + if (verbose) + printf(_("%s: partition #%d added\n"), device, n); + continue; + } + rc = -1; + if (verbose) + warn(_("%s: add partition #%d failed"), device, n); + if (!errfirst) + errlast = errfirst = n; + else if (errlast + 1 == n) + errlast++; + else { + add_parts_warnx(device, errfirst, errlast); + errlast = errfirst = n; + } } - if (g.start != 0) { - fprintf(stderr, "last arg is not the whole disk\n"); - fprintf(stderr, "call: partx -opts device wholedisk\n"); - exit(1); + + if (errfirst) + add_parts_warnx(device, errfirst, errlast); + return rc; +} + +static int list_parts(blkid_partlist ls, int lower, int upper) +{ + int i, nparts; + + assert(ls); + + nparts = blkid_partlist_numof_partitions(ls); + + for (i = 0; i < nparts; i++) { + blkid_partition par = blkid_partlist_get_partition(ls, i); + int n = blkid_partition_get_partno(par); + uintmax_t start, size; + + if (lower && n < lower) + continue; + if (upper && n > upper) + continue; + + start = blkid_partition_get_start(par); + size = blkid_partition_get_size(par); + + printf("#%2d: %9ju-%9ju (%9ju sectors, %6ju MB)\n", + n, start, start + size -1, + size, (size << 9) / 1000000); } + return 0; +} + +static void add_tt_line(struct tt *tt, blkid_partition par) +{ + struct tt_line *line; + int i; + + assert(tt); + assert(par); - if (ioctl(fd2, HDIO_GETGEO, &g)) { - perror("HDIO_GETGEO"); - exit(1); + line = tt_add_line(tt, NULL); + if (!line) { + warn(_("failed to add line to output")); + return; } - all.start = g.start; - if (blkdev_get_sectors(fd2, &size) != 0) { - perror("partx"); - exit(1); + for (i = 0; i < ncolumns; i++) { + char *str = NULL; + int rc = 0; + + switch (get_column_id(i)) { + case COL_PARTNO: + rc = asprintf(&str, "%d", + blkid_partition_get_partno(par)); + break; + case COL_START: + rc = asprintf(&str, "%ju", + blkid_partition_get_start(par)); + break; + case COL_END: + rc = asprintf(&str, "%ju", + blkid_partition_get_start(par) + + blkid_partition_get_size(par) - 1); + break; + case COL_SECTORS: + rc = asprintf(&str, "%ju", + blkid_partition_get_size(par)); + break; + case COL_SIZE: + if (partx_flags & FL_BYTES) + rc = asprintf(&str, "%ju", (uintmax_t) + blkid_partition_get_size(par) << 9); + else { + str = size_to_human_string( + blkid_partition_get_size(par) << 9); + if (str) + rc = 1; + } + break; + case COL_NAME: + str = (char *) blkid_partition_get_name(par); + break; + case COL_UUID: + str = (char *) blkid_partition_get_uuid(par); + break; + case COL_TYPE: + if (blkid_partition_is_primary(par)) + str = xstrdup("P"), rc = 1; + else if (blkid_partition_is_logical(par)) + str = xstrdup("L"), rc = 1; + else if (blkid_partition_is_extended(par)) + str = xstrdup("E"), rc = 1; + break; + default: + break; + } + + if (str && rc) + tt_line_set_data(line, i, str); } - all.size = (unsigned int) size; +} - if (verbose) - printf("device %s: start %d size %d\n", - device, all.start, all.size); +static int show_parts(blkid_partlist ls, int tt_flags, int lower, int upper) +{ + int i, rc = -1; + struct tt *tt; + int nparts; - if (all.size == 0) { - fprintf(stderr, "That disk slice has size 0\n"); - exit(0); + assert(ls); + + nparts = blkid_partlist_numof_partitions(ls); + if (!nparts) + return 0; + + tt = tt_new_table(tt_flags); + if (!tt) { + warn(_("failed to initialize output table")); + return -1; } - if (all.size == 2) - all.size = 0; /* probably extended partition */ - /* add the indicated partitions to the kernel partition tables */ - if (!lower) - lower = 1; - for (i = 0; i < ptct; i++) { - ptp = &pts[i]; - if (!type || !strcmp(type, ptp->type)) { - n = ptp->fn(fd, all, slices, ARRAY_SIZE(slices)); - if (n >= 0 && verbose) - printf("%s: %d slices\n", ptp->type, n); - if (n > 0 && (verbose || what == LIST)) { - for (j=0; j<n; j++) - printf("#%2d: %9d-%9d (%9d sectors, %6d MB)\n", - lower+j, - slices[j].start, - slices[j].start+slices[j].size-1, - slices[j].size, - (int)((512 * (long long) slices[j].size) - / 1000000)); - } - if (n > 0 && what == ADD) { - /* test for overlap, as in the case of an - extended partition, and reduce size */ - for (j=0; j<n; j++) { - for (k=j+1; k<n; k++) { - if (slices[k].start > slices[j].start && - slices[k].start < slices[j].start + - slices[j].size) { - slices[j].size = slices[k].start - - slices[j].start; - if (verbose) - printf("reduced size of " - "partition #%d to %d\n", - lower+j, - slices[j].size); - } - } - } - for (j=0; j<n; j++) { - /* skip unused/empty partitions */ - if (slices[j].size == 0) - continue; - pt.pno = lower+j; - pt.start = 512 * (long long) slices[j].start; - pt.length = 512 * (long long) slices[j].size; - pt.devname[0] = 0; - pt.volname[0] = 0; - a.op = BLKPG_ADD_PARTITION; - a.flags = 0; - a.datalen = sizeof(pt); - a.data = &pt; - if (ioctl(fd, BLKPG, &a) == -1) { - perror("BLKPG"); - fprintf(stderr, - "error adding partition %d\n", - lower+j); - } else if (verbose) - printf("added partition %d\n", lower+j); - } - } + for (i = 0; i < ncolumns; i++) { + struct colinfo *col = get_column_info(i); + + if (!tt_define_column(tt, col->name, col->whint, col->flags)) { + warnx(_("failed to initialize output column")); + goto done; } } - return 0; -} + for (i = 0; i < nparts; i++) { + blkid_partition par = blkid_partlist_get_partition(ls, i); + int n = blkid_partition_get_partno(par); -static void * -xmalloc (size_t size) { - void *t; + if (lower && n < lower) + continue; + if (upper && n > upper) + continue; - if (size == 0) - return NULL; - t = malloc (size); - if (t == NULL) { - fprintf(stderr, "Out of memory\n"); - exit(1); + add_tt_line(tt, par); } - return t; + + rc = 0; + tt_print_table(tt); +done: + tt_free_table(tt); + return rc; } -static int -sseek(int fd, unsigned int secnr) { - long long in, out; - in = ((long long) secnr << 9); - out = 1; +static int parse_range(const char *str, int *lower, int *upper) +{ + char *end = NULL; + + if (!str) + return 0; - if ((out = lseek(fd, in, SEEK_SET)) != in) - { - fprintf(stderr, "lseek error\n"); - return -1; + *upper = *lower = 0; + errno = 0; + + if (*str == ':') { /* <:N> */ + str++; + *upper = strtol(str, &end, 10); + if (errno || !end || *end || end == str) + return -1; + } else { + *upper = *lower = strtol(str, &end, 10); + if (errno || !end || end == str) + return -1; + + if (*end == ':' && !*(end + 1)) /* <M:> */ + *upper = 0; + else if (*end == '-' || *end == ':') { /* <M:N> <M-N> */ + str = end + 1; + end = NULL; + errno = 0; + *upper = strtol(str, &end, 10); + + if (errno || !end || *end || end == str) + return -1; + } } return 0; } -static -struct block { - unsigned int secnr; - unsigned char *block; - struct block *next; -} *blockhead; - -unsigned char * -getblock(int fd, unsigned int secnr) { - struct block *bp; - - for (bp = blockhead; bp; bp = bp->next) - if (bp->secnr == secnr) - return bp->block; - if (sseek(fd, secnr)) +static blkid_partlist get_partlist(blkid_probe pr, + const char *device, char *type) +{ + blkid_partlist ls; + blkid_parttable tab; + + assert(pr); + assert(device); + + if (type) { + char *name[] = { type, NULL }; + + if (blkid_probe_filter_partitions_type(pr, + BLKID_FLTR_ONLYIN, name)) { + warnx(_("failed to initialize blkid " + "filter for '%s'"), type); + return NULL; + } + } + + ls = blkid_probe_get_partitions(pr); + if (!ls) { + warnx(_("%s: failed to read partition table"), device); + return NULL; + } + + tab = blkid_partlist_get_table(ls); + if (verbose && tab) + printf(_("%s: partition table '%s' detected\n"), + device, + blkid_parttable_get_type(tab)); + + if (!blkid_partlist_numof_partitions(ls)) { + warnx(_("%s: %s partition table does not contains " + "usable partitions"), device, + blkid_parttable_get_type(tab)); return NULL; - bp = xmalloc(sizeof(struct block)); - bp->secnr = secnr; - bp->next = blockhead; - blockhead = bp; - bp->block = (unsigned char *) xmalloc(1024); - if (read(fd, bp->block, 1024) != 1024) { - fprintf(stderr, "read error, sector %d\n", secnr); - bp->block = NULL; } - return bp->block; + return ls; +} + +static int __attribute__((__noreturn__)) usage(FILE *out) +{ + int i; + + fprintf(out, _( + "\nUsage:\n" + " %s [-a|-d|-s] [--nr <N-M> | <device>] <wholedisk>\n"), + program_invocation_short_name); + + fprintf(out, _( + "\nOptions:\n" + " -a, --add add specified partitions or all of them\n" + " -d, --delete delete specified partitions or all of them\n" + " -l, --list list partitions (DEPRECATED)\n" + " -s, --show list partitions\n\n" + + " -b, --bytes print SIZE in bytes rather than in human readable format\n" + " -g, --noheadings don't print headings for --show\n" + " -r, --raw use raw format output\n" + " -t, --type <TYPE> specify the partition type (dos, bsd, solaris, etc.)\n" + " -n, --nr <M:N> specify the range of partitions (--nr 2:4)\n" + " -o, --output <LIST> output column\n" + " -h, --help print this help\n\n")); + + fprintf(out, _("\nAvailable columns (for --show):\n")); + + for (i = 0; i < __NCOLUMNS; i++) + fprintf(out, " %10s %s\n", infos[i].name, gettext(infos[i].help)); + + fprintf(out, _("\nFor more information see partx(8).\n")); + + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); +} + +static int __attribute__((__noreturn__)) +errx_mutually_exclusive(const char *opts) +{ + errx(EXIT_FAILURE, "%s %s", opts, _("options are mutually exclusive")); } -/* call with errno and integer m and error message */ -/* merge to interval m-n */ -static void -errmerge(int err, int m, char *msg1, char *msg2) { - static int preverr, firstm, prevm; - - if (err != preverr) { - if (preverr) { - if (firstm == prevm) - fprintf(stderr, msg1, firstm); - else - fprintf(stderr, msg2, firstm, prevm); - errno = preverr; - perror("BLKPG"); +int main(int argc, char **argv) +{ + int fd, c, what = 0, lower = 0, upper = 0, rc = 0; + int tt_flags = 0; + char *type = NULL; + char *device = NULL; /* pointer to atgv[], ie: /dev/sda1 */ + char *wholedisk = NULL; /* allocated, ie: /dev/sda */ + dev_t disk_devno = 0, part_devno = 0; + + static const struct option long_opts[] = { + { "bytes", no_argument, NULL, 'b' }, + { "noheadings", no_argument, NULL, 'g' }, + { "raw", no_argument, NULL, 'r' }, + { "list", no_argument, NULL, 'l' }, + { "show", no_argument, NULL, 's' }, + { "add", no_argument, NULL, 'a' }, + { "delete", no_argument, NULL, 'd' }, + { "type", required_argument, NULL, 't' }, + { "nr", required_argument, NULL, 'n' }, + { "output", required_argument, NULL, 'o' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + while ((c = getopt_long(argc, argv, "abdglrsvn:t:o:h", long_opts, NULL)) != -1) { + switch(c) { + case 'a': + if (what) + errx_mutually_exclusive("--{add,delete,show,list,raw}"); + what = ACT_ADD; + break; + case 'b': + partx_flags |= FL_BYTES; + break; + case 'd': + if (what) + errx_mutually_exclusive("--{add,delete,show,list,raw}"); + what = ACT_DELETE; + break; + case 'g': + tt_flags |= TT_FL_NOHEADINGS; + break; + case 'l': + if (what) + errx_mutually_exclusive("--{add,delete,show,list,raw}"); + what = ACT_LIST; + break; + case 'n': + if (parse_range(optarg, &lower, &upper)) + errx(EXIT_FAILURE, _("failed to parse --nr <M-N> range")); + break; + + case 'o': + if (tt_parse_columns_list(optarg, columns, &ncolumns, + column_name_to_id)) + return EXIT_FAILURE; + break; + case 'r': + tt_flags |= TT_FL_RAW; + if (what) + errx_mutually_exclusive("--{add,delete,show,list,raw}"); + what = ACT_SHOW; + break; + + case 's': + if (what) + errx_mutually_exclusive("--{add,delete,show,list,raw}"); + what = ACT_SHOW; + break; + case 't': + type = optarg; + break; + case 'v': + verbose = 1; + break; + case 'h': + usage(stdout); + case '?': + default: + usage(stderr); + } + } + + /* -o <list> enables --show mode by default */ + if (ncolumns && !what) + what = ACT_SHOW; + + /* backwardly compatible default */ + if (!what) + what = ACT_LIST; + + /* --show default, could by modified by -o */ + if (what == ACT_SHOW && !ncolumns) { + columns[ncolumns++] = COL_PARTNO; + columns[ncolumns++] = COL_START; + columns[ncolumns++] = COL_END; + columns[ncolumns++] = COL_SECTORS; + columns[ncolumns++] = COL_SIZE; + columns[ncolumns++] = COL_NAME; + columns[ncolumns++] = COL_UUID; + } + + /* + * Note that 'partx /dev/sda1' == 'partx /dev/sda1 /dev/sda' + * so assume that the device and/or disk are always the last + * arguments to be passed to partx. + */ + if (optind == argc - 2) { + /* passed 2 arguments: + * /dev/sda1 /dev/sda : partition + whole-disk + * -- /dev/sda1 : partition that should be used as a whole-disk + */ + device = argv[optind]; + + if (strcmp(device, "-") == 0) { + device = NULL; + wholedisk = xstrdup(argv[optind + 1]); + } else { + device = argv[optind]; + wholedisk = xstrdup(argv[optind + 1]); + } + } else if (optind == argc - 1) { + /* passed only one arg (ie: /dev/sda3 or /dev/sda) */ + struct stat sb; + + device = argv[optind]; + + if (stat(device, &sb)) + err(EXIT_FAILURE, _("%s: stat failed"), device); + + part_devno = sb.st_rdev; + + if (blkid_devno_to_wholedisk(part_devno, + NULL, 0, &disk_devno) == 0 && + part_devno != disk_devno) + wholedisk = blkid_devno_to_devname(disk_devno); + + if (!wholedisk) { + wholedisk = xstrdup(device); + disk_devno = part_devno; + device = NULL; + part_devno = 0; } - preverr = err; - firstm = prevm = m; } else - prevm = m; + usage(stderr); + + if (device && (upper || lower)) + errx(EXIT_FAILURE, _("--nr and <partition> are mutually exclusive}")); + + assert(wholedisk); + + if (device) { + /* use partno from given partition instead of --nr range, e.g: + * partx -d /dev/sda3 + * is the same like: + * partx -d --nr 3 /dev/sda + */ + struct stat sb; + + if (!part_devno && !stat(device, &sb)) + part_devno = sb.st_rdev; + + lower = upper = get_partno_from_device(device, part_devno); + } + + if (verbose) + printf("device: %s, whole-disk: %s, lower: %d, upper: %d\n", + device, wholedisk, lower, upper); + + if (what == ACT_ADD || what == ACT_DELETE) { + struct stat x; + + if (stat(wholedisk, &x) || !S_ISBLK(x.st_mode)) + errx(EXIT_FAILURE, _("%s: not a block device"), wholedisk); + } + if ((fd = open(wholedisk, O_RDONLY)) == -1) + err(EXIT_FAILURE, _("%s: open failed"), wholedisk); + + if (what == ACT_DELETE) + rc = del_parts(fd, wholedisk, disk_devno, lower, upper); + else { + blkid_probe pr = blkid_new_probe(); + blkid_partlist ls = NULL; + + if (!pr || blkid_probe_set_device(pr, fd, 0, 0)) + warnx(_("%s: failed to initialize blkid prober"), + wholedisk); + else + ls = get_partlist(pr, wholedisk, type); + + if (ls) { + int n = blkid_partlist_numof_partitions(ls); + + if (lower < 0) + lower = n + lower + 1; + if (upper < 0) + upper = n + upper + 1; + if (lower > upper) { + warnx(_("defined range <%d:%d> " + "does not make sense"), lower, upper); + rc = -1, what = 0; + } + + switch (what) { + case ACT_SHOW: + rc = show_parts(ls, tt_flags, lower, upper); + break; + case ACT_LIST: + rc = list_parts(ls, lower, upper); + break; + case ACT_ADD: + rc = add_parts(fd, wholedisk, ls, lower, upper); + break; + } + } + blkid_free_probe(pr); + } + + close(fd); + return rc ? EXIT_FAILURE : EXIT_SUCCESS; } + |