/* * Copyright (C) 2011 Karel Zak * Originally from Ted's losetup.c * * losetup.c - setup and control loop devices */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "strutils.h" #include "nls.h" #include "pathnames.h" #include "loopdev.h" #include "xalloc.h" #include "canonicalize.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; /* * A function to read the passphrase either from the terminal or from * an open file descriptor. */ static char *xgetpass(int pfd, const char *prompt) { char *pass; int buflen, i; if (pfd < 0) /* terminal */ return getpass(prompt); pass = NULL; buflen = 0; for (i=0; ; i++) { if (i >= buflen-1) { /* we're running out of space in the buffer. * Make it bigger: */ char *tmppass = pass; buflen += 128; pass = realloc(tmppass, buflen); if (pass == NULL) { /* realloc failed. Stop reading. */ warn(_("Out of memory while reading passphrase")); pass = tmppass; /* the old buffer hasn't changed */ break; } } if (read(pfd, pass+i, 1) != 1 || pass[i] == '\n' || pass[i] == 0) break; } if (pass == NULL) return ""; pass[i] = 0; return pass; } static int printf_loopdev(struct loopdev_cxt *lc) { uint64_t x; dev_t dev = 0; ino_t ino = 0; char *fname = NULL; int 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), 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 %ju)"), 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(_("%s: open failed"), 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(_("\nUsage:\n"), out); fprintf(out, _(" %1$s loop_device give info\n" " %1$s -a | --all list all used\n" " %1$s -d | --detach [ ...] delete\n" " %1$s -D | --detach-all delete all used\n" " %1$s -f | --find find unused\n" " %1$s -c | --set-capacity resize\n" " %1$s -j | --associated [-o ] list all associated with \n" " %1$s [options] {-f|--find|loopdev} setup\n"), program_invocation_short_name); fputs(_("\nOptions:\n"), out); fputs(_(" -e, --encryption enable data encryption with specified \n" " -h, --help this help\n" " -o, --offset start at offset into file\n" " --sizelimit loop limited to only bytes of the file\n" " -p, --pass-fd read passphrase from file descriptor \n" " -r, --read-only setup read-only loop device\n" " --show print device name (with -f )\n" " -v, --verbose verbose mode\n\n"), out); exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); } 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; 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, 128 }, { "pass-fd", 1, 0, 'p' }, { "read-only", 0, 0, 'r' }, { "show", 0, 0, 's' }, { "verbose", 0, 0, 'v' }, { NULL, 0, 0, 0 } }; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); loopcxt_init(&lc, 0); loopcxt_enable_debug(&lc, getenv("LOOPDEV_DEBUG") ? TRUE : FALSE); while ((c = getopt_long(argc, argv, "ac:d:De:E:fhj:o:p:rsv", longopts, NULL)) != -1) { if (act && strchr("acdDfj", c)) errx(EXIT_FAILURE, _("the options %s are mutually exclusive"), "--{all,associated,set-capacity,detach,detach-all,find}"); switch (c) { case 'a': act = A_SHOW; break; case 'c': act = A_SET_CAPACITY; loopcxt_set_device(&lc, optarg); break; case 'r': lo_flags |= LO_FLAGS_READ_ONLY; break; case 'd': act = A_DELETE; loopcxt_set_device(&lc, 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': if (strtosize(optarg, &offset)) errx(EXIT_FAILURE, _("invalid offset '%s' specified"), optarg); flags |= LOOPDEV_FL_OFFSET; break; case 'p': passfd = strtol_or_err(optarg, _("invalid passphrase file descriptor")); break; case 's': showdev = 1; break; case 'v': verbose = 1; break; case 128: /* --sizelimit */ if (strtosize(optarg, &sizelimit)) errx(EXIT_FAILURE, _("invalid size '%s' specified"), optarg); flags |= LOOPDEV_FL_SIZELIMIT; break; default: usage(stderr); } } if (argc == 1) usage(stderr); if (act == A_FIND_FREE && optind < argc) { /* * losetup -f */ act = A_CREATE; file = argv[optind++]; } if (!act && optind + 1 == argc) { /* * losetup */ act = A_SHOW_ONE; loopcxt_set_device(&lc, argv[optind++]); } if (!act) { /* * losetup */ act = A_CREATE; if (optind >= argc) errx(EXIT_FAILURE, _("no loop device specified")); loopcxt_set_device(&lc, argv[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 (act != A_CREATE && act != A_SHOW && (flags & LOOPDEV_FL_OFFSET)) 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_offset(&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(_("failed to setup loop device")); break; } } while (hasdev == 0); free(pass); if (showdev && res == 0) printf("%s\n", loopcxt_get_device(&lc)); break; } case A_DELETE: res = delete_loop(&lc); while (optind < argc) { loopcxt_set_device(&lc, argv[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; }