1005 lines
24 KiB
C
1005 lines
24 KiB
C
/*
|
|
* 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.]
|
|
*
|
|
* aeb, 2000-03-21 -- sah is 42 now
|
|
*
|
|
* Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
|
|
* Rewritten to use libblkid for util-linux
|
|
* based on ideas from Karel Zak <kzak@redhat.com>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <getopt.h>
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
#include <dirent.h>
|
|
|
|
#include <blkid.h>
|
|
#include <libsmartcols.h>
|
|
|
|
#include "c.h"
|
|
#include "pathnames.h"
|
|
#include "nls.h"
|
|
#include "blkdev.h"
|
|
#include "strutils.h"
|
|
#include "xalloc.h"
|
|
#include "partx.h"
|
|
#include "sysfs.h"
|
|
#include "loopdev.h"
|
|
#include "at.h"
|
|
#include "closestream.h"
|
|
#include "optutils.h"
|
|
|
|
/* this is the default upper limit, could be modified by --nr */
|
|
#define SLICES_MAX 256
|
|
|
|
/* basic table settings */
|
|
enum {
|
|
PARTX_RAW = (1 << 0),
|
|
PARTX_NOHEADINGS = (1 << 1),
|
|
PARTX_EXPORT = (1 << 2),
|
|
};
|
|
|
|
/* all the columns (-o option) */
|
|
enum {
|
|
COL_PARTNO,
|
|
COL_START,
|
|
COL_END,
|
|
COL_SECTORS,
|
|
COL_SIZE,
|
|
COL_NAME,
|
|
COL_UUID,
|
|
COL_TYPE,
|
|
COL_FLAGS,
|
|
COL_SCHEME,
|
|
};
|
|
|
|
#define ACT_ERROR "--{add,delete,show,list,raw,pairs}"
|
|
enum {
|
|
ACT_NONE,
|
|
ACT_LIST,
|
|
ACT_SHOW,
|
|
ACT_ADD,
|
|
ACT_UPD,
|
|
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; /* SCOLS_FL_* */
|
|
const char *help;
|
|
};
|
|
|
|
/* columns descriptions */
|
|
struct colinfo infos[] = {
|
|
[COL_PARTNO] = { "NR", 0.25, SCOLS_FL_RIGHT, N_("partition number") },
|
|
[COL_START] = { "START", 0.30, SCOLS_FL_RIGHT, N_("start of the partition in sectors") },
|
|
[COL_END] = { "END", 0.30, SCOLS_FL_RIGHT, N_("end of the partition in sectors") },
|
|
[COL_SECTORS] = { "SECTORS", 0.30, SCOLS_FL_RIGHT, N_("number of sectors") },
|
|
[COL_SIZE] = { "SIZE", 0.30, SCOLS_FL_RIGHT, N_("human readable size") },
|
|
[COL_NAME] = { "NAME", 0.30, SCOLS_FL_TRUNC, N_("partition name") },
|
|
[COL_UUID] = { "UUID", 36, 0, N_("partition UUID")},
|
|
[COL_SCHEME] = { "SCHEME", 0.1, SCOLS_FL_TRUNC, N_("partition table type (dos, gpt, ...)")},
|
|
[COL_FLAGS] = { "FLAGS", 0.1, SCOLS_FL_TRUNC, N_("partition flags")},
|
|
[COL_TYPE] = { "TYPE", 1, SCOLS_FL_RIGHT, N_("partition type (a string, a UUID, or hex)")},
|
|
};
|
|
|
|
#define NCOLS ARRAY_SIZE(infos)
|
|
|
|
/* array with IDs of enabled columns */
|
|
static int columns[NCOLS], ncolumns;
|
|
|
|
static int verbose;
|
|
static int partx_flags;
|
|
static struct loopdev_cxt lc;
|
|
static int loopdev;
|
|
|
|
static void assoc_loopdev(const char *fname)
|
|
{
|
|
int rc;
|
|
|
|
if (loopcxt_init(&lc, 0))
|
|
err(EXIT_FAILURE, _("failed to initialize loopcxt"));
|
|
|
|
rc = loopcxt_find_unused(&lc);
|
|
if (rc)
|
|
err(EXIT_FAILURE, _("%s: failed to find unused loop device"),
|
|
fname);
|
|
|
|
if (verbose)
|
|
printf(_("Trying to use '%s' for the loop device\n"),
|
|
loopcxt_get_device(&lc));
|
|
|
|
if (loopcxt_set_backing_file(&lc, fname))
|
|
err(EXIT_FAILURE, _("%s: failed to set backing file"), fname);
|
|
|
|
rc = loopcxt_setup_device(&lc);
|
|
|
|
if (rc == -EBUSY)
|
|
err(EXIT_FAILURE, _("%s: failed to set up loop device"), fname);
|
|
|
|
loopdev = 1;
|
|
}
|
|
|
|
static inline int get_column_id(int num)
|
|
{
|
|
assert(ARRAY_SIZE(columns) == NCOLS);
|
|
assert(num < ncolumns);
|
|
assert(columns[num] < (int) NCOLS);
|
|
return columns[num];
|
|
}
|
|
|
|
static inline struct colinfo *get_column_info(int num)
|
|
{
|
|
return &infos[ get_column_id(num) ];
|
|
}
|
|
|
|
static int column_name_to_id(const char *name, size_t namesz)
|
|
{
|
|
size_t i;
|
|
|
|
assert(name);
|
|
|
|
for (i = 0; i < NCOLS; i++) {
|
|
const char *cn = infos[i].name;
|
|
|
|
if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
|
|
return i;
|
|
}
|
|
warnx(_("unknown column: %s"), name);
|
|
return -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).
|
|
*/
|
|
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) {
|
|
struct sysfs_cxt cxt;
|
|
int rc;
|
|
|
|
if (sysfs_init(&cxt, devno, NULL))
|
|
goto err;
|
|
|
|
rc = sysfs_read_int(&cxt, "partition", &partno);
|
|
sysfs_deinit(&cxt);
|
|
|
|
if (rc == 0)
|
|
return partno;
|
|
}
|
|
|
|
sz = strlen(partition);
|
|
p = partition + sz - 1;
|
|
|
|
if (!isdigit((unsigned int) *p))
|
|
goto err;
|
|
|
|
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, *dirname = NULL;
|
|
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;
|
|
|
|
dirname = xstrdup(path);
|
|
|
|
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 && d->d_type != DT_UNKNOWN)
|
|
continue;
|
|
#endif
|
|
if (strncmp(parent, d->d_name, strlen(parent)))
|
|
continue;
|
|
snprintf(path, sizeof(path), "%s/partition", d->d_name);
|
|
|
|
fd = open_at(dirfd(dir), dirname, 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
free(dirname);
|
|
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(_("specified range <%d:%d> "
|
|
"does not make sense"), lower, upper);
|
|
return -1;
|
|
}
|
|
|
|
for (i = lower; i <= upper; i++) {
|
|
rc = partx_del_partition(fd, i);
|
|
if (rc == 0) {
|
|
if (verbose)
|
|
printf(_("%s: partition #%d removed\n"), device, i);
|
|
continue;
|
|
} else if (errno == ENXIO) {
|
|
if (verbose)
|
|
printf(_("%s: partition #%d already doesn't exist\n"), device, i);
|
|
continue;
|
|
}
|
|
rc = -1;
|
|
if (verbose)
|
|
warn(_("%s: deleting 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;
|
|
}
|
|
}
|
|
|
|
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 the 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: adding 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 (errfirst)
|
|
add_parts_warnx(device, errfirst, errlast);
|
|
|
|
/*
|
|
* The kernel with enabled partitions scanner for loop devices add *all*
|
|
* partitions, so we should delete any extra, unwanted ones, when the -n
|
|
* option is passed.
|
|
*/
|
|
if (loopdev && loopcxt_is_partscan(&lc) && (lower || upper)) {
|
|
for (i = 0; i < nparts; i++) {
|
|
blkid_partition par = blkid_partlist_get_partition(ls, i);
|
|
int n = blkid_partition_get_partno(par);
|
|
|
|
if (n < lower || n > upper)
|
|
partx_del_partition(fd, n);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void upd_parts_warnx(const char *device, int first, int last)
|
|
{
|
|
if (first == last)
|
|
warnx(_("%s: error updating partition %d"), device, first);
|
|
else
|
|
warnx(_("%s: error updating partitions %d-%d"),
|
|
device, first, last);
|
|
}
|
|
|
|
static int upd_parts(int fd, const char *device, dev_t devno,
|
|
blkid_partlist ls, int lower, int upper)
|
|
{
|
|
int n, nparts, rc = 0, errfirst = 0, errlast = 0, err;
|
|
blkid_partition par;
|
|
uintmax_t start, size;
|
|
|
|
assert(fd >= 0);
|
|
assert(device);
|
|
assert(ls);
|
|
|
|
nparts = blkid_partlist_numof_partitions(ls);
|
|
if (!lower)
|
|
lower = 1;
|
|
if (!upper || lower < 0 || upper < 0) {
|
|
n = get_max_partno(device, devno);
|
|
if (!upper)
|
|
upper = n > nparts ? n : nparts;
|
|
else if (upper < 0)
|
|
upper = n + upper + 1;
|
|
if (lower < 0)
|
|
lower = n + lower + 1;
|
|
}
|
|
if (lower > upper) {
|
|
warnx(_("specified range <%d:%d> "
|
|
"does not make sense"), lower, upper);
|
|
return -1;
|
|
}
|
|
|
|
for (n = lower; n <= upper; n++) {
|
|
par = blkid_partlist_get_partition_by_partno(ls, n);
|
|
if (!par) {
|
|
if (verbose)
|
|
warn(_("%s: no partition #%d"), device, n);
|
|
continue;
|
|
}
|
|
|
|
start = blkid_partition_get_start(par);
|
|
size = blkid_partition_get_size(par);
|
|
if (blkid_partition_is_extended(par))
|
|
/*
|
|
* Let's follow the Linux kernel and reduce
|
|
* DOS extended partition to 1 or 2 sectors.
|
|
*/
|
|
size = min(size, (uintmax_t) 2);
|
|
|
|
err = partx_del_partition(fd, n);
|
|
if (err == -1 && errno == ENXIO)
|
|
err = 0; /* good, it already doesn't exist */
|
|
if (err == -1 && errno == EBUSY)
|
|
{
|
|
/* try to resize */
|
|
err = partx_resize_partition(fd, n, start, size);
|
|
if (verbose)
|
|
printf(_("%s: partition #%d resized\n"), device, n);
|
|
if (err == 0)
|
|
continue;
|
|
}
|
|
if (err == 0 && partx_add_partition(fd, n, start, size) == 0) {
|
|
if (verbose)
|
|
printf(_("%s: partition #%d added\n"), device, n);
|
|
continue;
|
|
}
|
|
|
|
if (err == 0)
|
|
continue;
|
|
rc = -1;
|
|
if (verbose)
|
|
warn(_("%s: updating partition #%d failed"), device, n);
|
|
if (!errfirst)
|
|
errlast = errfirst = n;
|
|
else if (errlast + 1 == n)
|
|
errlast++;
|
|
else {
|
|
upd_parts_warnx(device, errfirst, errlast);
|
|
errlast = errfirst = n;
|
|
}
|
|
}
|
|
|
|
if (errfirst)
|
|
upd_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(P_("#%2d: %9ju-%9ju (%9ju sector, %6ju MB)\n",
|
|
"#%2d: %9ju-%9ju (%9ju sectors, %6ju MB)\n",
|
|
size),
|
|
n, start, start + size -1,
|
|
size, (size << 9) / 1000000);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int add_scols_line(struct libscols_table *table, blkid_partition par)
|
|
{
|
|
struct libscols_line *line;
|
|
int i, rc = 0;
|
|
|
|
assert(table);
|
|
assert(par);
|
|
|
|
line = scols_table_new_line(table, NULL);
|
|
if (!line) {
|
|
warn(_("failed to add line to output"));
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (i = 0; i < ncolumns; i++) {
|
|
char *str = NULL; /* allocated string */
|
|
const char *cstr = NULL; /* foreign string */
|
|
|
|
switch (get_column_id(i)) {
|
|
case COL_PARTNO:
|
|
xasprintf(&str, "%d", blkid_partition_get_partno(par));
|
|
break;
|
|
case COL_START:
|
|
xasprintf(&str, "%ju", blkid_partition_get_start(par));
|
|
break;
|
|
case COL_END:
|
|
xasprintf(&str, "%ju",
|
|
blkid_partition_get_start(par) +
|
|
blkid_partition_get_size(par) - 1);
|
|
break;
|
|
case COL_SECTORS:
|
|
xasprintf(&str, "%ju", blkid_partition_get_size(par));
|
|
break;
|
|
case COL_SIZE:
|
|
if (partx_flags & FL_BYTES)
|
|
xasprintf(&str, "%ju", (uintmax_t)
|
|
blkid_partition_get_size(par) << 9);
|
|
else
|
|
str = size_to_human_string(SIZE_SUFFIX_1LETTER,
|
|
blkid_partition_get_size(par) << 9);
|
|
break;
|
|
case COL_NAME:
|
|
cstr = blkid_partition_get_name(par);
|
|
break;
|
|
case COL_UUID:
|
|
cstr = blkid_partition_get_uuid(par);
|
|
break;
|
|
case COL_TYPE:
|
|
if (blkid_partition_get_type_string(par))
|
|
cstr = blkid_partition_get_type_string(par);
|
|
else
|
|
xasprintf(&str, "0x%x",
|
|
blkid_partition_get_type(par));
|
|
break;
|
|
case COL_FLAGS:
|
|
xasprintf(&str, "0x%llx", blkid_partition_get_flags(par));
|
|
break;
|
|
case COL_SCHEME:
|
|
{
|
|
blkid_parttable tab = blkid_partition_get_table(par);
|
|
if (tab)
|
|
cstr = blkid_parttable_get_type(tab);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (cstr)
|
|
rc = scols_line_set_data(line, i, cstr);
|
|
else if (str)
|
|
rc = scols_line_refer_data(line, i, str);
|
|
if (rc) {
|
|
warn(_("failed to add data to output table"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int show_parts(blkid_partlist ls, int scols_flags, int lower, int upper)
|
|
{
|
|
int i, rc = -1;
|
|
struct libscols_table *table;
|
|
int nparts;
|
|
|
|
assert(ls);
|
|
|
|
nparts = blkid_partlist_numof_partitions(ls);
|
|
if (!nparts)
|
|
return 0;
|
|
|
|
scols_init_debug(0);
|
|
table = scols_new_table();
|
|
if (!table) {
|
|
warn(_("failed to initialize output table"));
|
|
return -1;
|
|
}
|
|
scols_table_enable_raw(table, !!(scols_flags & PARTX_RAW));
|
|
scols_table_enable_export(table, !!(scols_flags & PARTX_EXPORT));
|
|
scols_table_enable_noheadings(table, !!(scols_flags & PARTX_NOHEADINGS));
|
|
|
|
for (i = 0; i < ncolumns; i++) {
|
|
struct colinfo *col = get_column_info(i);
|
|
|
|
if (!scols_table_new_column(table, col->name, col->whint, col->flags)) {
|
|
warnx(_("failed to initialize output column"));
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < nparts; i++) {
|
|
blkid_partition par = blkid_partlist_get_partition(ls, i);
|
|
int n = blkid_partition_get_partno(par);
|
|
|
|
if (lower && n < lower)
|
|
continue;
|
|
if (upper && n > upper)
|
|
continue;
|
|
|
|
rc = add_scols_line(table, par);
|
|
if (rc)
|
|
break;
|
|
}
|
|
|
|
rc = 0;
|
|
scols_print_table(table);
|
|
done:
|
|
scols_unref_table(table);
|
|
return rc;
|
|
}
|
|
|
|
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 type '%s' detected\n"),
|
|
device, blkid_parttable_get_type(tab));
|
|
|
|
if (!blkid_partlist_numof_partitions(ls))
|
|
printf(_("%s: partition table with no partitions"), device);
|
|
}
|
|
|
|
return ls;
|
|
}
|
|
|
|
static void __attribute__((__noreturn__)) usage(FILE *out)
|
|
{
|
|
size_t i;
|
|
|
|
fputs(USAGE_HEADER, out);
|
|
fprintf(out,
|
|
_(" %s [-a|-d|-s|-u] [--nr <n:m> | <partition>] <disk>\n"),
|
|
program_invocation_short_name);
|
|
|
|
fputs(USAGE_OPTIONS, out);
|
|
fputs(_(" -a, --add add specified partitions or all of them\n"), out);
|
|
fputs(_(" -d, --delete delete specified partitions or all of them\n"), out);
|
|
fputs(_(" -u, --update update specified partitions or all of them\n"), out);
|
|
fputs(_(" -s, --show list partitions\n\n"), out);
|
|
fputs(_(" -b, --bytes print SIZE in bytes rather than in human readable format\n"), out);
|
|
fputs(_(" -g, --noheadings don't print headings for --show\n"), out);
|
|
fputs(_(" -n, --nr <n:m> specify the range of partitions (e.g. --nr 2:4)\n"), out);
|
|
fputs(_(" -o, --output <list> define which output columns to use\n"), out);
|
|
fputs(_(" -P, --pairs use key=\"value\" output format\n"), out);
|
|
fputs(_(" -r, --raw use raw output format\n"), out);
|
|
fputs(_(" -t, --type <type> specify the partition type (dos, bsd, solaris, etc.)\n"), out);
|
|
fputs(_(" -v, --verbose verbose mode\n"), out);
|
|
|
|
fputs(USAGE_SEPARATOR, out);
|
|
fputs(USAGE_HELP, out);
|
|
fputs(USAGE_VERSION, out);
|
|
|
|
fputs(_("\nAvailable columns (for --show, --raw or --pairs):\n"), out);
|
|
|
|
for (i = 0; i < NCOLS; i++)
|
|
fprintf(out, " %10s %s\n", infos[i].name, _(infos[i].help));
|
|
|
|
fprintf(out, USAGE_MAN_TAIL("partx(8)"));
|
|
|
|
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int fd, c, what = ACT_NONE, lower = 0, upper = 0, rc = 0;
|
|
int scols_flags = 0;
|
|
char *type = NULL;
|
|
char *device = NULL; /* pointer to argv[], ie: /dev/sda1 */
|
|
char *wholedisk = NULL; /* allocated, ie: /dev/sda */
|
|
char *outarg = NULL;
|
|
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' },
|
|
{ "update", no_argument, NULL, 'u' },
|
|
{ "type", required_argument, NULL, 't' },
|
|
{ "nr", required_argument, NULL, 'n' },
|
|
{ "output", required_argument, NULL, 'o' },
|
|
{ "pairs", no_argument, NULL, 'P' },
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "version", no_argument, NULL, 'V' },
|
|
{ "verbose", no_argument, NULL, 'v' },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
|
|
static const ul_excl_t excl[] = { /* rows and cols in in ASCII order */
|
|
{ 'P','a','d','l','r','s' },
|
|
{ 0 }
|
|
};
|
|
int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
|
|
|
|
setlocale(LC_ALL, "");
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
textdomain(PACKAGE);
|
|
atexit(close_stdout);
|
|
|
|
while ((c = getopt_long(argc, argv,
|
|
"abdglrsuvn:t:o:PhV", long_opts, NULL)) != -1) {
|
|
|
|
err_exclusive_options(c, long_opts, excl, excl_st);
|
|
|
|
switch(c) {
|
|
case 'a':
|
|
what = ACT_ADD;
|
|
break;
|
|
case 'b':
|
|
partx_flags |= FL_BYTES;
|
|
break;
|
|
case 'd':
|
|
what = ACT_DELETE;
|
|
break;
|
|
case 'g':
|
|
scols_flags |= PARTX_NOHEADINGS;
|
|
break;
|
|
case 'l':
|
|
what = ACT_LIST;
|
|
break;
|
|
case 'n':
|
|
if (parse_range(optarg, &lower, &upper, 0))
|
|
errx(EXIT_FAILURE, _("failed to parse --nr <M-N> range"));
|
|
break;
|
|
case 'o':
|
|
outarg = optarg;
|
|
break;
|
|
case 'P':
|
|
scols_flags |= PARTX_EXPORT;
|
|
what = ACT_SHOW;
|
|
break;
|
|
case 'r':
|
|
scols_flags |= PARTX_RAW;
|
|
what = ACT_SHOW;
|
|
break;
|
|
case 's':
|
|
what = ACT_SHOW;
|
|
break;
|
|
case 't':
|
|
type = optarg;
|
|
break;
|
|
case 'u':
|
|
what = ACT_UPD;
|
|
break;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case 'h':
|
|
usage(stdout);
|
|
case 'V':
|
|
printf(UTIL_LINUX_VERSION);
|
|
return EXIT_SUCCESS;
|
|
case '?':
|
|
default:
|
|
usage(stderr);
|
|
}
|
|
}
|
|
|
|
if (what == ACT_NONE)
|
|
what = ACT_SHOW;
|
|
|
|
/* --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;
|
|
}
|
|
|
|
if (what == ACT_SHOW && outarg &&
|
|
string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
|
|
&ncolumns, column_name_to_id) < 0)
|
|
return EXIT_FAILURE;
|
|
|
|
/*
|
|
* 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, _("stat failed %s"), 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;
|
|
}
|
|
} else
|
|
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(_("partition: %s, disk: %s, lower: %d, upper: %d\n"),
|
|
device ? device : "none", wholedisk, lower, upper);
|
|
|
|
if (what == ACT_ADD || what == ACT_DELETE) {
|
|
struct stat x;
|
|
|
|
if (stat(wholedisk, &x))
|
|
errx(EXIT_FAILURE, "%s", wholedisk);
|
|
|
|
if (S_ISREG(x.st_mode)) {
|
|
/* not a blkdev, try to associate it to a loop device */
|
|
if (what == ACT_DELETE)
|
|
errx(EXIT_FAILURE, _("%s: cannot delete partitions"),
|
|
wholedisk);
|
|
if (!loopmod_supports_partscan())
|
|
errx(EXIT_FAILURE, _("%s: partitioned loop devices unsupported"),
|
|
wholedisk);
|
|
assoc_loopdev(wholedisk);
|
|
wholedisk = xstrdup(lc.device);
|
|
} else if (!S_ISBLK(x.st_mode))
|
|
errx(EXIT_FAILURE, _("%s: not a block device"), wholedisk);
|
|
}
|
|
if ((fd = open(wholedisk, O_RDONLY)) == -1)
|
|
err(EXIT_FAILURE, _("cannot open %s"), 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(_("specified range <%d:%d> "
|
|
"does not make sense"), lower, upper);
|
|
rc = -1, what = ACT_NONE;
|
|
}
|
|
|
|
switch (what) {
|
|
case ACT_SHOW:
|
|
rc = show_parts(ls, scols_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;
|
|
case ACT_UPD:
|
|
rc = upd_parts(fd, wholedisk, disk_devno, ls, lower, upper);
|
|
case ACT_NONE:
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
blkid_free_probe(pr);
|
|
}
|
|
|
|
if (loopdev)
|
|
loopcxt_deinit(&lc);
|
|
|
|
if (close_fd(fd) != 0)
|
|
err(EXIT_FAILURE, _("write failed"));
|
|
|
|
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
}
|