summaryrefslogtreecommitdiff
path: root/sys-utils/losetup.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys-utils/losetup.c')
-rw-r--r--sys-utils/losetup.c465
1 files changed, 465 insertions, 0 deletions
diff --git a/sys-utils/losetup.c b/sys-utils/losetup.c
new file mode 100644
index 0000000..fdcc0be
--- /dev/null
+++ b/sys-utils/losetup.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ * Originally from Ted's losetup.c
+ *
+ * losetup.c - setup and control loop devices
+ */
+#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 "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "loopdev.h"
+#include "xgetpass.h"
+#include "closestream.h"
+#include "optutils.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 */
+};
+
+static int verbose;
+
+
+static int printf_loopdev(struct loopdev_cxt *lc)
+{
+ uint64_t x;
+ dev_t dev = 0;
+ ino_t ino = 0;
+ char *fname = NULL;
+ 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;
+
+ if (loopcxt_init_iterator(lc, LOOPITER_FL_USED))
+ return -1;
+
+ if (!file || stat(file, st))
+ st = NULL;
+
+ while (loopcxt_next(lc) == 0) {
+
+ if (file && !loopcxt_is_used(lc, st, file, offset, flags))
+ continue;
+
+ printf_loopdev(lc);
+ }
+ loopcxt_deinit_iterator(lc);
+ return 0;
+}
+
+static int set_capacity(struct loopdev_cxt *lc)
+{
+ int fd = loopcxt_get_fd(lc);
+
+ if (fd < 0)
+ warn(_("cannot open %s"), loopcxt_get_device(lc));
+ else if (ioctl(fd, LOOP_SET_CAPACITY) != 0)
+ warnx(_("%s: set capacity failed"), loopcxt_get_device(lc));
+ else
+ return 0;
+
+ return -1;
+}
+
+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 void usage(FILE *out)
+{
+ fputs(USAGE_HEADER, out);
+
+ fprintf(out,
+ _(" %1$s [options] [<loopdev>]\n"
+ " %1$s [options] -f | <loopdev> <file>\n"),
+ program_invocation_short_name);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -a, --all list all used devices\n"
+ " -d, --detach <loopdev> [...] detach one or more devices\n"
+ " -D, --detach-all detach all used devices\n"
+ " -f, --find find first unused device\n"
+ " -c, --set-capacity <loopdev> resize device\n"
+ " -j, --associated <file> list all devices associated with <file>\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+
+ fputs(_(" -e, --encryption <type> enable encryption with specified <name/num>\n"
+ " -o, --offset <num> start at offset <num> into file\n"
+ " --sizelimit <num> device limited to <num> bytes of the file\n"
+ " -p, --pass-fd <num> read passphrase from file descriptor <num>\n"
+ " -P, --partscan create partitioned loop device\n"
+ " -r, --read-only setup read-only loop device\n"
+ " --show print device name after setup (with -f)\n"
+ " -v, --verbose verbose mode\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+
+ fprintf(out, USAGE_MAN_TAIL("losetup(8)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+static void warn_size(const char *filename, uint64_t size)
+{
+ struct stat st;
+
+ if (!size) {
+ if (stat(filename, &st))
+ return;
+ size = st.st_size;
+ }
+
+ if (size < 512)
+ warnx(_("%s: warning: file smaller than 512 bytes, the loop device "
+ "maybe 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);
+}
+
+int main(int argc, char **argv)
+{
+ struct loopdev_cxt lc;
+ int act = 0, flags = 0, passfd = -1, c;
+ char *file = NULL, *encryption = NULL;
+ uint64_t offset = 0, sizelimit = 0;
+ int res = 0, showdev = 0, lo_flags = 0;
+
+ enum {
+ OPT_SIZELIMIT = CHAR_MAX + 1,
+ OPT_SHOW
+ };
+ static const struct option longopts[] = {
+ { "all", 0, 0, 'a' },
+ { "set-capacity", 1, 0, 'c' },
+ { "detach", 1, 0, 'd' },
+ { "detach-all", 0, 0, 'D' },
+ { "encryption", 1, 0, 'e' },
+ { "find", 0, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "associated", 1, 0, 'j' },
+ { "offset", 1, 0, 'o' },
+ { "sizelimit", 1, 0, OPT_SIZELIMIT },
+ { "pass-fd", 1, 0, 'p' },
+ { "partscan", 0, 0, 'P' },
+ { "read-only", 0, 0, 'r' },
+ { "show", 0, 0, OPT_SHOW },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { NULL, 0, 0, 0 }
+ };
+
+ static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
+ { 'D','a','c','d','f','j' },
+ { 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"));
+
+ loopcxt_enable_debug(&lc, getenv("LOOPDEV_DEBUG") ? TRUE : FALSE);
+
+ while ((c = getopt_long(argc, argv, "ac:d:De:E:fhj:o:p:PrvV",
+ longopts, NULL)) != -1) {
+
+ err_exclusive_options(c, longopts, excl, excl_st);
+
+ switch (c) {
+ case 'a':
+ act = A_SHOW;
+ break;
+ case 'c':
+ act = A_SET_CAPACITY;
+ if (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 (loopcxt_set_device(&lc, optarg))
+ err(EXIT_FAILURE, _("%s: failed to use device"),
+ optarg);
+ break;
+ case 'D':
+ act = A_DELETE_ALL;
+ break;
+ case 'E':
+ case 'e':
+ encryption = optarg;
+ break;
+ case 'f':
+ act = A_FIND_FREE;
+ break;
+ case 'h':
+ usage(stdout);
+ break;
+ case 'j':
+ act = A_SHOW;
+ file = optarg;
+ break;
+ case 'o':
+ offset = strtosize_or_err(optarg, _("failed to parse offset"));
+ flags |= LOOPDEV_FL_OFFSET;
+ break;
+ case 'p':
+ passfd = strtou32_or_err(optarg,
+ _("invalid passphrase file descriptor"));
+ break;
+ case 'P':
+ lo_flags |= LO_FLAGS_PARTSCAN;
+ break;
+ case OPT_SHOW:
+ showdev = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ 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:
+ usage(stderr);
+ }
+ }
+
+ if (argc == 1)
+ usage(stderr);
+
+ if (act == A_FIND_FREE && optind < argc) {
+ /*
+ * losetup -f <backing_file>
+ */
+ act = A_CREATE;
+ file = argv[optind++];
+ }
+ if (!act && optind + 1 == argc) {
+ /*
+ * losetup <device>
+ */
+ act = A_SHOW_ONE;
+ if (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"));
+ 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 &&
+ (encryption || sizelimit || passfd != -1 || lo_flags || showdev))
+ errx(EXIT_FAILURE,
+ _("the options %s are allowed to loop device setup only"),
+ "--{encryption,sizelimit,pass-fd,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."));
+
+ switch (act) {
+ case A_CREATE:
+ {
+ char *pass = NULL;
+ int hasdev = loopcxt_has_device(&lc);
+
+ if (encryption) {
+#ifdef MCL_FUTURE
+ if(mlockall(MCL_CURRENT | MCL_FUTURE))
+ err(EXIT_FAILURE, _("couldn't lock into memory"));
+#endif
+ pass = xgetpass(passfd, _("Password: "));
+ }
+ do {
+ /* Note that loopcxt_{find_unused,set_device}() resets
+ * loopcxt struct.
+ */
+ if (!hasdev && (res = loopcxt_find_unused(&lc))) {
+ warnx(_("not found unused device"));
+ break;
+ }
+ if (encryption && pass)
+ loopcxt_set_encryption(&lc, encryption, pass);
+ 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 ((res = loopcxt_set_backing_file(&lc, file))) {
+ warn(_("%s: failed to use backing file"), file);
+ break;
+ }
+ errno = 0;
+ res = loopcxt_setup_device(&lc);
+ if (res == 0)
+ break; /* success */
+ if (errno != EBUSY) {
+ warn(_("%s: failed to setup loop device"),
+ hasdev && loopcxt_get_fd(&lc) < 0 ?
+ loopcxt_get_device(&lc) : file);
+ break;
+ }
+ } while (hasdev == 0);
+
+ free(pass);
+
+ if (res == 0) {
+ if (showdev)
+ printf("%s\n", loopcxt_get_device(&lc));
+ warn_size(file, sizelimit);
+ }
+ break;
+ }
+ case A_DELETE:
+ res = delete_loop(&lc);
+ while (optind < argc) {
+ if (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:
+ if (loopcxt_find_unused(&lc))
+ warn(_("find unused loop device failed"));
+ else
+ printf("%s\n", loopcxt_get_device(&lc));
+ break;
+ case A_SHOW:
+ res = show_all_loops(&lc, file, offset, flags);
+ break;
+ case A_SHOW_ONE:
+ res = printf_loopdev(&lc);
+ if (res)
+ warn(_("%s"), loopcxt_get_device(&lc));
+ break;
+ case A_SET_CAPACITY:
+ res = set_capacity(&lc);
+ break;
+ default:
+ usage(stderr);
+ break;
+ }
+
+ loopcxt_deinit(&lc);
+ return res ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+