| /* |
| * Copyright (C) 2011 Karel Zak <kzak@redhat.com> |
| * Originally from Ted's losetup.c |
| * |
| * losetup.c - setup and control loop devices |
| */ |
| #include <assert.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <inttypes.h> |
| #include <getopt.h> |
| |
| #include <libsmartcols.h> |
| |
| #include "c.h" |
| #include "nls.h" |
| #include "strutils.h" |
| #include "loopdev.h" |
| #include "closestream.h" |
| #include "optutils.h" |
| #include "xalloc.h" |
| #include "canonicalize.h" |
| #include "pathnames.h" |
| |
| enum { |
| A_CREATE = 1, /* setup a new device */ |
| A_DELETE, /* delete given device(s) */ |
| A_DELETE_ALL, /* delete all devices */ |
| A_SHOW, /* list devices */ |
| A_SHOW_ONE, /* print info about one device */ |
| A_FIND_FREE, /* find first unused */ |
| A_SET_CAPACITY, /* set device capacity */ |
| A_SET_DIRECT_IO, /* set accessing backing file by direct io */ |
| A_SET_BLOCKSIZE, /* set logical block size of the loop device */ |
| }; |
| |
| enum { |
| COL_NAME = 0, |
| COL_AUTOCLR, |
| COL_BACK_FILE, |
| COL_BACK_INO, |
| COL_BACK_MAJMIN, |
| COL_MAJMIN, |
| COL_OFFSET, |
| COL_PARTSCAN, |
| COL_RO, |
| COL_SIZELIMIT, |
| COL_DIO, |
| COL_LOGSEC, |
| }; |
| |
| /* basic output flags */ |
| static int no_headings; |
| static int raw; |
| static int json; |
| |
| struct colinfo { |
| const char *name; |
| double whint; |
| int flags; |
| const char *help; |
| |
| int json_type; /* default is string */ |
| }; |
| |
| static struct colinfo infos[] = { |
| [COL_AUTOCLR] = { "AUTOCLEAR", 1, SCOLS_FL_RIGHT, N_("autoclear flag set"), SCOLS_JSON_BOOLEAN}, |
| [COL_BACK_FILE] = { "BACK-FILE", 0.3, 0, N_("device backing file")}, |
| [COL_BACK_INO] = { "BACK-INO", 4, SCOLS_FL_RIGHT, N_("backing file inode number"), SCOLS_JSON_NUMBER}, |
| [COL_BACK_MAJMIN] = { "BACK-MAJ:MIN", 6, 0, N_("backing file major:minor device number")}, |
| [COL_NAME] = { "NAME", 0.25, 0, N_("loop device name")}, |
| [COL_OFFSET] = { "OFFSET", 5, SCOLS_FL_RIGHT, N_("offset from the beginning"), SCOLS_JSON_NUMBER}, |
| [COL_PARTSCAN] = { "PARTSCAN", 1, SCOLS_FL_RIGHT, N_("partscan flag set"), SCOLS_JSON_BOOLEAN}, |
| [COL_RO] = { "RO", 1, SCOLS_FL_RIGHT, N_("read-only device"), SCOLS_JSON_BOOLEAN}, |
| [COL_SIZELIMIT] = { "SIZELIMIT", 5, SCOLS_FL_RIGHT, N_("size limit of the file in bytes"), SCOLS_JSON_NUMBER}, |
| [COL_MAJMIN] = { "MAJ:MIN", 3, 0, N_("loop device major:minor number")}, |
| [COL_DIO] = { "DIO", 1, SCOLS_FL_RIGHT, N_("access backing file with direct-io"), SCOLS_JSON_BOOLEAN}, |
| [COL_LOGSEC] = { "LOG-SEC", 4, SCOLS_FL_RIGHT, N_("logical sector size in bytes"), SCOLS_JSON_NUMBER}, |
| }; |
| |
| static int columns[ARRAY_SIZE(infos) * 2] = {-1}; |
| static size_t ncolumns; |
| |
| static int get_column_id(int num) |
| { |
| assert(num >= 0); |
| assert((size_t) num < ncolumns); |
| assert(columns[num] < (int) ARRAY_SIZE(infos)); |
| return columns[num]; |
| } |
| |
| static 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; |
| |
| for (i = 0; i < ARRAY_SIZE(infos); i++) { |
| const char *cn = infos[i].name; |
| |
| if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) |
| return i; |
| } |
| warnx(_("unknown column: %s"), name); |
| return -1; |
| } |
| |
| static int printf_loopdev(struct loopdev_cxt *lc) |
| { |
| uint64_t x; |
| dev_t dev = 0; |
| ino_t ino = 0; |
| char *fname; |
| uint32_t type; |
| |
| fname = loopcxt_get_backing_file(lc); |
| if (!fname) |
| return -EINVAL; |
| |
| if (loopcxt_get_backing_devno(lc, &dev) == 0) |
| loopcxt_get_backing_inode(lc, &ino); |
| |
| if (!dev && !ino) { |
| /* |
| * Probably non-root user (no permissions to |
| * call LOOP_GET_STATUS ioctls). |
| */ |
| printf("%s: []: (%s)", |
| loopcxt_get_device(lc), fname); |
| |
| if (loopcxt_get_offset(lc, &x) == 0 && x) |
| printf(_(", offset %ju"), x); |
| |
| if (loopcxt_get_sizelimit(lc, &x) == 0 && x) |
| printf(_(", sizelimit %ju"), x); |
| printf("\n"); |
| return 0; |
| } |
| |
| printf("%s: [%04d]:%" PRIu64 " (%s)", |
| loopcxt_get_device(lc), (int) dev, ino, fname); |
| |
| if (loopcxt_get_offset(lc, &x) == 0 && x) |
| printf(_(", offset %ju"), x); |
| |
| if (loopcxt_get_sizelimit(lc, &x) == 0 && x) |
| printf(_(", sizelimit %ju"), x); |
| |
| if (loopcxt_get_encrypt_type(lc, &type) == 0) { |
| const char *e = loopcxt_get_crypt_name(lc); |
| |
| if ((!e || !*e) && type == 1) |
| e = "XOR"; |
| if (e && *e) |
| printf(_(", encryption %s (type %u)"), e, type); |
| } |
| printf("\n"); |
| return 0; |
| } |
| |
| static int show_all_loops(struct loopdev_cxt *lc, const char *file, |
| uint64_t offset, int flags) |
| { |
| struct stat sbuf, *st = &sbuf; |
| char *cn_file = NULL; |
| |
| if (loopcxt_init_iterator(lc, LOOPITER_FL_USED)) |
| return -1; |
| |
| if (!file || stat(file, st)) |
| st = NULL; |
| |
| while (loopcxt_next(lc) == 0) { |
| if (file) { |
| int used; |
| const char *bf = cn_file ? cn_file : file; |
| |
| used = loopcxt_is_used(lc, st, bf, offset, 0, flags); |
| if (!used && !cn_file) { |
| bf = cn_file = canonicalize_path(file); |
| used = loopcxt_is_used(lc, st, bf, offset, 0, flags); |
| } |
| if (!used) |
| continue; |
| } |
| printf_loopdev(lc); |
| } |
| loopcxt_deinit_iterator(lc); |
| free(cn_file); |
| return 0; |
| } |
| |
| static int delete_loop(struct loopdev_cxt *lc) |
| { |
| if (loopcxt_delete_device(lc)) |
| warn(_("%s: detach failed"), loopcxt_get_device(lc)); |
| else |
| return 0; |
| |
| return -1; |
| } |
| |
| static int delete_all_loops(struct loopdev_cxt *lc) |
| { |
| int res = 0; |
| |
| if (loopcxt_init_iterator(lc, LOOPITER_FL_USED)) |
| return -1; |
| |
| while (loopcxt_next(lc) == 0) |
| res += delete_loop(lc); |
| |
| loopcxt_deinit_iterator(lc); |
| return res; |
| } |
| |
| static int set_scols_data(struct loopdev_cxt *lc, struct libscols_line *ln) |
| { |
| size_t i; |
| |
| for (i = 0; i < ncolumns; i++) { |
| const char *p = NULL; /* external data */ |
| char *np = NULL; /* allocated here */ |
| uint64_t x = 0; |
| int rc = 0; |
| |
| switch(get_column_id(i)) { |
| case COL_NAME: |
| p = loopcxt_get_device(lc); |
| break; |
| case COL_BACK_FILE: |
| p = loopcxt_get_backing_file(lc); |
| break; |
| case COL_OFFSET: |
| if (loopcxt_get_offset(lc, &x) == 0) |
| xasprintf(&np, "%jd", x); |
| break; |
| case COL_SIZELIMIT: |
| if (loopcxt_get_sizelimit(lc, &x) == 0) |
| xasprintf(&np, "%jd", x); |
| break; |
| case COL_BACK_MAJMIN: |
| { |
| dev_t dev = 0; |
| if (loopcxt_get_backing_devno(lc, &dev) == 0 && dev) |
| xasprintf(&np, "%8u:%-3u", major(dev), minor(dev)); |
| break; |
| } |
| case COL_MAJMIN: |
| { |
| struct stat st; |
| |
| if (loopcxt_get_device(lc) |
| && stat(loopcxt_get_device(lc), &st) == 0 |
| && S_ISBLK(st.st_mode) |
| && major(st.st_rdev) == LOOPDEV_MAJOR) |
| xasprintf(&np, "%3u:%-3u", major(st.st_rdev), |
| minor(st.st_rdev)); |
| break; |
| } |
| case COL_BACK_INO: |
| { |
| ino_t ino = 0; |
| if (loopcxt_get_backing_inode(lc, &ino) == 0 && ino) |
| xasprintf(&np, "%ju", ino); |
| break; |
| } |
| case COL_AUTOCLR: |
| p = loopcxt_is_autoclear(lc) ? "1" : "0"; |
| break; |
| case COL_RO: |
| p = loopcxt_is_readonly(lc) ? "1" : "0"; |
| break; |
| case COL_DIO: |
| p = loopcxt_is_dio(lc) ? "1" : "0"; |
| break; |
| case COL_PARTSCAN: |
| p = loopcxt_is_partscan(lc) ? "1" : "0"; |
| break; |
| case COL_LOGSEC: |
| if (loopcxt_get_blocksize(lc, &x) == 0) |
| xasprintf(&np, "%jd", x); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| |
| if (p) |
| rc = scols_line_set_data(ln, i, p); /* calls strdup() */ |
| else if (np) |
| rc = scols_line_refer_data(ln, i, np); /* only refers */ |
| |
| if (rc) |
| err(EXIT_FAILURE, _("failed to add output data")); |
| } |
| |
| return 0; |
| } |
| |
| static int show_table(struct loopdev_cxt *lc, |
| const char *file, |
| uint64_t offset, |
| int flags) |
| { |
| struct stat sbuf, *st = &sbuf; |
| struct libscols_table *tb; |
| struct libscols_line *ln; |
| int rc = 0; |
| size_t i; |
| |
| scols_init_debug(0); |
| |
| if (!(tb = scols_new_table())) |
| err(EXIT_FAILURE, _("failed to allocate output table")); |
| scols_table_enable_raw(tb, raw); |
| scols_table_enable_json(tb, json); |
| scols_table_enable_noheadings(tb, no_headings); |
| |
| if (json) |
| scols_table_set_name(tb, "loopdevices"); |
| |
| for (i = 0; i < ncolumns; i++) { |
| struct colinfo *ci = get_column_info(i); |
| struct libscols_column *cl; |
| |
| cl = scols_table_new_column(tb, ci->name, ci->whint, ci->flags); |
| if (!cl) |
| err(EXIT_FAILURE, _("failed to allocate output column")); |
| if (json) |
| scols_column_set_json_type(cl, ci->json_type); |
| } |
| |
| /* only one loopdev requested (already assigned to loopdev_cxt) */ |
| if (loopcxt_get_device(lc)) { |
| ln = scols_table_new_line(tb, NULL); |
| if (!ln) |
| err(EXIT_FAILURE, _("failed to allocate output line")); |
| rc = set_scols_data(lc, ln); |
| |
| /* list all loopdevs */ |
| } else { |
| char *cn_file = NULL; |
| |
| rc = loopcxt_init_iterator(lc, LOOPITER_FL_USED); |
| if (rc) |
| goto done; |
| if (!file || stat(file, st)) |
| st = NULL; |
| |
| while (loopcxt_next(lc) == 0) { |
| if (file) { |
| int used; |
| const char *bf = cn_file ? cn_file : file; |
| |
| used = loopcxt_is_used(lc, st, bf, offset, 0, flags); |
| if (!used && !cn_file) { |
| bf = cn_file = canonicalize_path(file); |
| used = loopcxt_is_used(lc, st, bf, offset, 0, flags); |
| } |
| if (!used) |
| continue; |
| } |
| |
| ln = scols_table_new_line(tb, NULL); |
| if (!ln) |
| err(EXIT_FAILURE, _("failed to allocate output line")); |
| rc = set_scols_data(lc, ln); |
| if (rc) |
| break; |
| } |
| |
| loopcxt_deinit_iterator(lc); |
| free(cn_file); |
| } |
| done: |
| if (rc == 0) |
| rc = scols_print_table(tb); |
| scols_unref_table(tb); |
| return rc; |
| } |
| |
| static void __attribute__((__noreturn__)) usage(void) |
| { |
| FILE *out = stdout; |
| size_t i; |
| |
| fputs(USAGE_HEADER, out); |
| |
| fprintf(out, |
| _(" %1$s [options] [<loopdev>]\n" |
| " %1$s [options] -f | <loopdev> <file>\n"), |
| program_invocation_short_name); |
| |
| fputs(USAGE_SEPARATOR, out); |
| fputs(_("Set up and control loop devices.\n"), out); |
| |
| /* commands */ |
| fputs(USAGE_OPTIONS, out); |
| fputs(_(" -a, --all list all used devices\n"), out); |
| fputs(_(" -d, --detach <loopdev>... detach one or more devices\n"), out); |
| fputs(_(" -D, --detach-all detach all used devices\n"), out); |
| fputs(_(" -f, --find find first unused device\n"), out); |
| fputs(_(" -c, --set-capacity <loopdev> resize the device\n"), out); |
| fputs(_(" -j, --associated <file> list all devices associated with <file>\n"), out); |
| fputs(_(" -L, --nooverlap avoid possible conflict between devices\n"), out); |
| |
| /* commands options */ |
| fputs(USAGE_SEPARATOR, out); |
| fputs(_(" -o, --offset <num> start at offset <num> into file\n"), out); |
| fputs(_(" --sizelimit <num> device is limited to <num> bytes of the file\n"), out); |
| fputs(_(" -b --sector-size <num> set the logical sector size to <num>\n"), out); |
| fputs(_(" -P, --partscan create a partitioned loop device\n"), out); |
| fputs(_(" -r, --read-only set up a read-only loop device\n"), out); |
| fputs(_(" --direct-io[=<on|off>] open backing file with O_DIRECT\n"), out); |
| fputs(_(" --show print device name after setup (with -f)\n"), out); |
| fputs(_(" -v, --verbose verbose mode\n"), out); |
| |
| /* output options */ |
| fputs(USAGE_SEPARATOR, out); |
| fputs(_(" -J, --json use JSON --list output format\n"), out); |
| fputs(_(" -l, --list list info about all or specified (default)\n"), out); |
| fputs(_(" -n, --noheadings don't print headings for --list output\n"), out); |
| fputs(_(" -O, --output <cols> specify columns to output for --list\n"), out); |
| fputs(_(" --output-all output all columns\n"), out); |
| fputs(_(" --raw use raw --list output format\n"), out); |
| |
| fputs(USAGE_SEPARATOR, out); |
| printf(USAGE_HELP_OPTIONS(31)); |
| |
| fputs(USAGE_COLUMNS, out); |
| for (i = 0; i < ARRAY_SIZE(infos); i++) |
| fprintf(out, " %12s %s\n", infos[i].name, _(infos[i].help)); |
| |
| printf(USAGE_MAN_TAIL("losetup(8)")); |
| |
| exit(EXIT_SUCCESS); |
| } |
| |
| static void warn_size(const char *filename, uint64_t size) |
| { |
| struct stat st; |
| |
| if (!size) { |
| if (stat(filename, &st) || S_ISBLK(st.st_mode)) |
| return; |
| size = st.st_size; |
| } |
| |
| if (size < 512) |
| warnx(_("%s: Warning: file is smaller than 512 bytes; the loop device " |
| "may be useless or invisible for system tools."), |
| filename); |
| else if (size % 512) |
| warnx(_("%s: Warning: file does not fit into a 512-byte sector; " |
| "the end of the file will be ignored."), |
| filename); |
| } |
| |
| static int create_loop(struct loopdev_cxt *lc, |
| int nooverlap, int lo_flags, int flags, |
| const char *file, uint64_t offset, uint64_t sizelimit) |
| { |
| int hasdev = loopcxt_has_device(lc); |
| int rc = 0; |
| |
| /* losetup --find --noverlap file.img */ |
| if (!hasdev && nooverlap) { |
| rc = loopcxt_find_overlap(lc, file, offset, sizelimit); |
| switch (rc) { |
| case 0: /* not found */ |
| break; |
| |
| case 1: /* overlap */ |
| loopcxt_deinit(lc); |
| errx(EXIT_FAILURE, _("%s: overlapping loop device exists"), file); |
| |
| case 2: /* overlap -- full size and offset match (reuse) */ |
| { |
| uint32_t lc_encrypt_type; |
| |
| /* Once a loop is initialized RO, there is no |
| * way to change its parameters. */ |
| if (loopcxt_is_readonly(lc) |
| && !(lo_flags & LO_FLAGS_READ_ONLY)) { |
| loopcxt_deinit(lc); |
| errx(EXIT_FAILURE, _("%s: overlapping read-only loop device exists"), file); |
| } |
| |
| /* This is no more supported, but check to be safe. */ |
| if (loopcxt_get_encrypt_type(lc, &lc_encrypt_type) == 0 |
| && lc_encrypt_type != LO_CRYPT_NONE) { |
| loopcxt_deinit(lc); |
| errx(EXIT_FAILURE, _("%s: overlapping encrypted loop device exists"), file); |
| } |
| |
| lc->info.lo_flags &= ~LO_FLAGS_AUTOCLEAR; |
| if (loopcxt_set_status(lc)) { |
| loopcxt_deinit(lc); |
| errx(EXIT_FAILURE, _("%s: failed to re-use loop device"), file); |
| } |
| return 0; /* success, re-use */ |
| } |
| default: /* error */ |
| loopcxt_deinit(lc); |
| errx(EXIT_FAILURE, _("failed to inspect loop devices")); |
| return -errno; |
| } |
| } |
| |
| if (hasdev && !is_loopdev(loopcxt_get_device(lc))) |
| loopcxt_add_device(lc); |
| |
| /* losetup --noverlap /dev/loopN file.img */ |
| if (hasdev && nooverlap) { |
| struct loopdev_cxt lc2; |
| |
| if (loopcxt_init(&lc2, 0)) { |
| loopcxt_deinit(lc); |
| err(EXIT_FAILURE, _("failed to initialize loopcxt")); |
| } |
| rc = loopcxt_find_overlap(&lc2, file, offset, sizelimit); |
| loopcxt_deinit(&lc2); |
| |
| if (rc) { |
| loopcxt_deinit(lc); |
| if (rc > 0) |
| errx(EXIT_FAILURE, _("%s: overlapping loop device exists"), file); |
| err(EXIT_FAILURE, _("%s: failed to check for conflicting loop devices"), file); |
| } |
| } |
| |
| /* Create a new device */ |
| do { |
| const char *errpre; |
| |
| /* Note that loopcxt_{find_unused,set_device}() resets |
| * loopcxt struct. |
| */ |
| if (!hasdev && (rc = loopcxt_find_unused(lc))) { |
| warnx(_("cannot find an unused loop device")); |
| break; |
| } |
| if (flags & LOOPDEV_FL_OFFSET) |
| loopcxt_set_offset(lc, offset); |
| if (flags & LOOPDEV_FL_SIZELIMIT) |
| loopcxt_set_sizelimit(lc, sizelimit); |
| if (lo_flags) |
| loopcxt_set_flags(lc, lo_flags); |
| if ((rc = loopcxt_set_backing_file(lc, file))) { |
| warn(_("%s: failed to use backing file"), file); |
| break; |
| } |
| errno = 0; |
| rc = loopcxt_setup_device(lc); |
| if (rc == 0) |
| break; /* success */ |
| if (errno == EBUSY && !hasdev) |
| continue; |
| |
| /* errors */ |
| errpre = hasdev && loopcxt_get_fd(lc) < 0 ? |
| loopcxt_get_device(lc) : file; |
| warn(_("%s: failed to set up loop device"), errpre); |
| break; |
| } while (hasdev == 0); |
| |
| return rc; |
| } |
| |
| int main(int argc, char **argv) |
| { |
| struct loopdev_cxt lc; |
| int act = 0, flags = 0, no_overlap = 0, c; |
| char *file = NULL; |
| uint64_t offset = 0, sizelimit = 0, blocksize = 0; |
| int res = 0, showdev = 0, lo_flags = 0; |
| char *outarg = NULL; |
| int list = 0; |
| unsigned long use_dio = 0, set_dio = 0, set_blocksize = 0; |
| |
| enum { |
| OPT_SIZELIMIT = CHAR_MAX + 1, |
| OPT_SHOW, |
| OPT_RAW, |
| OPT_DIO, |
| OPT_OUTPUT_ALL |
| }; |
| static const struct option longopts[] = { |
| { "all", no_argument, NULL, 'a' }, |
| { "set-capacity", required_argument, NULL, 'c' }, |
| { "detach", required_argument, NULL, 'd' }, |
| { "detach-all", no_argument, NULL, 'D' }, |
| { "find", no_argument, NULL, 'f' }, |
| { "nooverlap", no_argument, NULL, 'L' }, |
| { "help", no_argument, NULL, 'h' }, |
| { "associated", required_argument, NULL, 'j' }, |
| { "json", no_argument, NULL, 'J' }, |
| { "list", no_argument, NULL, 'l' }, |
| { "sector-size", required_argument, NULL, 'b' }, |
| { "noheadings", no_argument, NULL, 'n' }, |
| { "offset", required_argument, NULL, 'o' }, |
| { "output", required_argument, NULL, 'O' }, |
| { "output-all", no_argument, NULL, OPT_OUTPUT_ALL }, |
| { "sizelimit", required_argument, NULL, OPT_SIZELIMIT }, |
| { "partscan", no_argument, NULL, 'P' }, |
| { "read-only", no_argument, NULL, 'r' }, |
| { "direct-io", optional_argument, NULL, OPT_DIO }, |
| { "raw", no_argument, NULL, OPT_RAW }, |
| { "show", no_argument, NULL, OPT_SHOW }, |
| { "verbose", no_argument, NULL, 'v' }, |
| { "version", no_argument, NULL, 'V' }, |
| { NULL, 0, NULL, 0 } |
| }; |
| |
| static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ |
| { 'D','a','c','d','f','j' }, |
| { 'D','c','d','f','l' }, |
| { 'D','c','d','f','O' }, |
| { 'J',OPT_RAW }, |
| { 0 } |
| }; |
| int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; |
| |
| setlocale(LC_ALL, ""); |
| bindtextdomain(PACKAGE, LOCALEDIR); |
| textdomain(PACKAGE); |
| atexit(close_stdout); |
| |
| if (loopcxt_init(&lc, 0)) |
| err(EXIT_FAILURE, _("failed to initialize loopcxt")); |
| |
| while ((c = getopt_long(argc, argv, "ab:c:d:Dfhj:JlLno:O:PrvV", |
| longopts, NULL)) != -1) { |
| |
| err_exclusive_options(c, longopts, excl, excl_st); |
| |
| switch (c) { |
| case 'a': |
| act = A_SHOW; |
| break; |
| case 'b': |
| set_blocksize = 1; |
| blocksize = strtosize_or_err(optarg, _("failed to parse logical block size")); |
| break; |
| case 'c': |
| act = A_SET_CAPACITY; |
| if (!is_loopdev(optarg) || |
| loopcxt_set_device(&lc, optarg)) |
| err(EXIT_FAILURE, _("%s: failed to use device"), |
| optarg); |
| break; |
| case 'r': |
| lo_flags |= LO_FLAGS_READ_ONLY; |
| break; |
| case 'd': |
| act = A_DELETE; |
| if (!is_loopdev(optarg) || |
| loopcxt_set_device(&lc, optarg)) |
| err(EXIT_FAILURE, _("%s: failed to use device"), |
| optarg); |
| break; |
| case 'D': |
| act = A_DELETE_ALL; |
| break; |
| case 'f': |
| act = A_FIND_FREE; |
| break; |
| case 'h': |
| usage(); |
| break; |
| case 'J': |
| json = 1; |
| break; |
| case 'j': |
| act = A_SHOW; |
| file = optarg; |
| break; |
| case 'l': |
| list = 1; |
| break; |
| case 'L': |
| no_overlap = 1; |
| break; |
| case 'n': |
| no_headings = 1; |
| break; |
| case OPT_RAW: |
| raw = 1; |
| break; |
| case 'o': |
| offset = strtosize_or_err(optarg, _("failed to parse offset")); |
| flags |= LOOPDEV_FL_OFFSET; |
| break; |
| case 'O': |
| outarg = optarg; |
| list = 1; |
| break; |
| case OPT_OUTPUT_ALL: |
| for (ncolumns = 0; ncolumns < ARRAY_SIZE(infos); ncolumns++) |
| columns[ncolumns] = ncolumns; |
| break; |
| case 'P': |
| lo_flags |= LO_FLAGS_PARTSCAN; |
| break; |
| case OPT_SHOW: |
| showdev = 1; |
| break; |
| case OPT_DIO: |
| use_dio = set_dio = 1; |
| if (optarg) |
| use_dio = parse_switch(optarg, _("argument error"), "on", "off", NULL); |
| break; |
| case 'v': |
| break; |
| case 'V': |
| printf(UTIL_LINUX_VERSION); |
| return EXIT_SUCCESS; |
| case OPT_SIZELIMIT: /* --sizelimit */ |
| sizelimit = strtosize_or_err(optarg, _("failed to parse size")); |
| flags |= LOOPDEV_FL_SIZELIMIT; |
| break; |
| default: |
| errtryhelp(EXIT_FAILURE); |
| } |
| } |
| |
| ul_path_init_debug(); |
| ul_sysfs_init_debug(); |
| |
| /* default is --list --all */ |
| if (argc == 1) { |
| act = A_SHOW; |
| list = 1; |
| } |
| |
| if (!act && argc == 2 && (raw || json)) { |
| act = A_SHOW; |
| list = 1; |
| } |
| |
| /* default --list output columns */ |
| if (list && !ncolumns) { |
| columns[ncolumns++] = COL_NAME; |
| columns[ncolumns++] = COL_SIZELIMIT; |
| columns[ncolumns++] = COL_OFFSET; |
| columns[ncolumns++] = COL_AUTOCLR; |
| columns[ncolumns++] = COL_RO; |
| columns[ncolumns++] = COL_BACK_FILE; |
| columns[ncolumns++] = COL_DIO; |
| columns[ncolumns++] = COL_LOGSEC; |
| } |
| |
| if (act == A_FIND_FREE && optind < argc) { |
| /* |
| * losetup -f <backing_file> |
| */ |
| act = A_CREATE; |
| file = argv[optind++]; |
| |
| if (optind < argc) |
| errx(EXIT_FAILURE, _("unexpected arguments")); |
| } |
| |
| if (list && !act && optind == argc) |
| /* |
| * losetup --list defaults to --all |
| */ |
| act = A_SHOW; |
| |
| if (!act && optind + 1 == argc) { |
| /* |
| * losetup [--list] <device> |
| * OR |
| * losetup {--direct-io[=off]|--logical-blocksize=size}... <device> |
| */ |
| if (!(set_dio || set_blocksize)) |
| act = A_SHOW_ONE; |
| if (set_dio) |
| act = A_SET_DIRECT_IO; |
| if (set_blocksize) |
| act = A_SET_BLOCKSIZE; |
| if (!is_loopdev(argv[optind]) || |
| loopcxt_set_device(&lc, argv[optind])) |
| err(EXIT_FAILURE, _("%s: failed to use device"), |
| argv[optind]); |
| optind++; |
| } |
| if (!act) { |
| /* |
| * losetup <loopdev> <backing_file> |
| */ |
| act = A_CREATE; |
| |
| if (optind >= argc) |
| errx(EXIT_FAILURE, _("no loop device specified")); |
| /* don't use is_loopdev() here, the device does not have exist yet */ |
| if (loopcxt_set_device(&lc, argv[optind])) |
| err(EXIT_FAILURE, _("%s: failed to use device"), |
| argv[optind]); |
| optind++; |
| |
| if (optind >= argc) |
| errx(EXIT_FAILURE, _("no file specified")); |
| file = argv[optind++]; |
| } |
| |
| if (act != A_CREATE && |
| (sizelimit || lo_flags || showdev)) |
| errx(EXIT_FAILURE, |
| _("the options %s are allowed during loop device setup only"), |
| "--{sizelimit,read-only,show}"); |
| |
| if ((flags & LOOPDEV_FL_OFFSET) && |
| act != A_CREATE && (act != A_SHOW || !file)) |
| errx(EXIT_FAILURE, _("the option --offset is not allowed in this context")); |
| |
| if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), |
| &ncolumns, column_name_to_id) < 0) |
| return EXIT_FAILURE; |
| |
| switch (act) { |
| case A_CREATE: |
| res = create_loop(&lc, no_overlap, lo_flags, flags, file, offset, sizelimit); |
| if (res == 0) { |
| if (showdev) |
| printf("%s\n", loopcxt_get_device(&lc)); |
| warn_size(file, sizelimit); |
| if (set_dio || set_blocksize) |
| goto lo_set_post; |
| } |
| break; |
| case A_DELETE: |
| res = delete_loop(&lc); |
| while (optind < argc) { |
| if (!is_loopdev(argv[optind]) || |
| loopcxt_set_device(&lc, argv[optind])) |
| warn(_("%s: failed to use device"), |
| argv[optind]); |
| optind++; |
| res += delete_loop(&lc); |
| } |
| break; |
| case A_DELETE_ALL: |
| res = delete_all_loops(&lc); |
| break; |
| case A_FIND_FREE: |
| res = loopcxt_find_unused(&lc); |
| if (res) { |
| int errsv = errno; |
| |
| if (access(_PATH_DEV_LOOPCTL, F_OK) == 0 && |
| access(_PATH_DEV_LOOPCTL, W_OK) != 0) |
| ; |
| else |
| errno = errsv; |
| |
| warn(_("cannot find an unused loop device")); |
| } else |
| printf("%s\n", loopcxt_get_device(&lc)); |
| break; |
| case A_SHOW: |
| if (list) |
| res = show_table(&lc, file, offset, flags); |
| else |
| res = show_all_loops(&lc, file, offset, flags); |
| break; |
| case A_SHOW_ONE: |
| if (list) |
| res = show_table(&lc, NULL, 0, 0); |
| else |
| res = printf_loopdev(&lc); |
| if (res) |
| warn("%s", loopcxt_get_device(&lc)); |
| break; |
| case A_SET_CAPACITY: |
| res = loopcxt_set_capacity(&lc); |
| if (res) |
| warn(_("%s: set capacity failed"), |
| loopcxt_get_device(&lc)); |
| break; |
| case A_SET_DIRECT_IO: |
| case A_SET_BLOCKSIZE: |
| lo_set_post: |
| if (set_dio) { |
| res = loopcxt_set_dio(&lc, use_dio); |
| if (res) |
| warn(_("%s: set direct io failed"), |
| loopcxt_get_device(&lc)); |
| } |
| if (set_blocksize) { |
| res = loopcxt_set_blocksize(&lc, blocksize); |
| if (res) |
| warn(_("%s: set logical block size failed"), |
| loopcxt_get_device(&lc)); |
| } |
| break; |
| default: |
| warnx(_("bad usage")); |
| errtryhelp(EXIT_FAILURE); |
| break; |
| } |
| |
| loopcxt_deinit(&lc); |
| return res ? EXIT_FAILURE : EXIT_SUCCESS; |
| } |
| |