summaryrefslogtreecommitdiffstats
path: root/sys-utils/losetup.c
diff options
context:
space:
mode:
authorKarel Zak2011-12-21 19:24:58 +0100
committerKarel Zak2011-12-22 12:19:38 +0100
commitc7e0925defe178ac31a524463a3488e4d72b6e4b (patch)
treed77b6d68a543aceb11bc13baa9647333346c110f /sys-utils/losetup.c
parentlosetup: rewrite -f (diff)
downloadkernel-qcow2-util-linux-c7e0925defe178ac31a524463a3488e4d72b6e4b.tar.gz
kernel-qcow2-util-linux-c7e0925defe178ac31a524463a3488e4d72b6e4b.tar.xz
kernel-qcow2-util-linux-c7e0925defe178ac31a524463a3488e4d72b6e4b.zip
losetup: rewrite loop setup
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'sys-utils/losetup.c')
-rw-r--r--sys-utils/losetup.c927
1 files changed, 94 insertions, 833 deletions
diff --git a/sys-utils/losetup.c b/sys-utils/losetup.c
index 0d8bd858d..ab45dff1d 100644
--- a/sys-utils/losetup.c
+++ b/sys-utils/losetup.c
@@ -28,7 +28,7 @@
#include "canonicalize.h"
enum {
- A_CREATE, /* setup a new device */
+ A_CREATE = 1, /* setup a new device */
A_DELETE, /* delete given device(s) */
A_DELETE_ALL, /* delete all devices */
A_SHOW, /* list devices */
@@ -38,603 +38,12 @@ enum {
static int verbose;
-static int is_associated(int dev, struct stat *file, unsigned long long offset, int isoff);
-
-#define LOOPMAJOR 7
-#define NLOOPS_DEFAULT 8 /* /dev/loop[0-7] */
-
-struct looplist {
- int flag; /* scanning options */
- FILE *proc; /* /proc/partitions */
- int ncur; /* current position */
- int *minors; /* ary of minor numbers (when scan whole /dev) */
- int nminors; /* number of items in *minors */
- char name[128]; /* device name */
- int ct_perm; /* count permission problems */
- int ct_succ; /* count number of successfully
- detected devices */
-};
-
-#define LLFLG_USEDONLY (1 << 1) /* return used devices only */
-#define LLFLG_FREEONLY (1 << 2) /* return non-used devices */
-#define LLFLG_DONE (1 << 3) /* all is done */
-#define LLFLG_PROCFS (1 << 4) /* try to found used devices in /proc/partitions */
-#define LLFLG_SUBDIR (1 << 5) /* /dev/loop/N */
-#define LLFLG_DFLT (1 << 6) /* directly try to check default loops */
-
-#define SETLOOP_RDONLY (1<<0) /* Open loop read-only */
-#define SETLOOP_AUTOCLEAR (1<<1) /* Automatically detach loop on close (2.6.25?) */
-
-/* TODO: move to lib/sysfs.c */
-static char *loopfile_from_sysfs(const char *device)
-{
- FILE *f;
- struct stat st;
- char buf[PATH_MAX], *res = NULL;
-
- if (stat(device, &st) || !S_ISBLK(st.st_mode))
- return NULL;
-
- snprintf(buf, sizeof(buf), _PATH_SYS_DEVBLOCK "/%d:%d/loop/backing_file",
- major(st.st_rdev), minor(st.st_rdev));
-
- f = fopen(buf, "r");
- if (!f)
- return NULL;
-
- if (fgets(buf, sizeof(buf), f)) {
- size_t sz = strlen(buf);
- if (sz) {
- buf[sz - 1] = '\0';
- res = xstrdup(buf);
- }
- }
-
- fclose(f);
- return res;
-}
-
-char *loopdev_get_loopfile(const char *device)
-{
- char *res = loopfile_from_sysfs(device);
-
- if (!res) {
- struct loop_info64 lo64;
- int fd;
-
- if ((fd = open(device, O_RDONLY)) < 0)
- return NULL;
-
- if (ioctl(fd, LOOP_GET_STATUS64, &lo64) == 0) {
- lo64.lo_file_name[LO_NAME_SIZE-2] = '*';
- lo64.lo_file_name[LO_NAME_SIZE-1] = 0;
- res = xstrdup((char *) lo64.lo_file_name);
-
- }
- close(fd);
- }
- return res;
-}
-
-int
-is_loop_device (const char *device) {
- struct stat st;
-
- return (stat(device, &st) == 0 &&
- S_ISBLK(st.st_mode) &&
- major(st.st_rdev) == LOOPMAJOR);
-}
-
-static int
-is_loop_used(int fd)
-{
- struct loop_info64 li;
-
- errno = 0;
- if (ioctl (fd, LOOP_GET_STATUS64, &li) < 0 && errno == ENXIO)
- return 0;
- return 1;
-}
-
-static int
-is_loopfd_autoclear(int fd)
-{
- struct loop_info64 lo64;
-
- if (ioctl(fd, LOOP_GET_STATUS64, &lo64) == 0) {
- if (lo64.lo_flags & LO_FLAGS_AUTOCLEAR)
- return 1;
-
- }
- return 0;
-}
-
-int
-is_loop_autoclear(const char *device)
-{
- int fd, rc;
-
- if ((fd = open(device, O_RDONLY)) < 0)
- return 0;
- rc = is_loopfd_autoclear(fd);
-
- close(fd);
- return rc;
-}
-
-static int
-looplist_open(struct looplist *ll, int flag)
-{
- struct stat st;
-
- memset(ll, 0, sizeof(*ll));
- ll->flag = flag;
- ll->ncur = -1;
-
- if (stat(_PATH_DEV, &st) == -1 || (!S_ISDIR(st.st_mode)))
- return -1; /* /dev doesn't exist */
-
- if (stat(_PATH_DEV_LOOP, &st) == 0 && S_ISDIR(st.st_mode))
- ll->flag |= LLFLG_SUBDIR; /* /dev/loop/ exists */
-
- if ((ll->flag & LLFLG_USEDONLY) &&
- stat(_PATH_PROC_PARTITIONS, &st) == 0)
- ll->flag |= LLFLG_PROCFS; /* try /proc/partitions */
-
- ll->flag |= LLFLG_DFLT; /* required! */
- return 0;
-}
-
-static void
-looplist_close(struct looplist *ll)
-{
- free(ll->minors);
- if (ll->proc)
- fclose(ll->proc);
- ll->minors = NULL;
- ll->proc = NULL;
- ll->ncur = -1;
- ll->flag |= LLFLG_DONE;
-}
-
-static int
-looplist_open_dev(struct looplist *ll, int lnum)
-{
- struct stat st;
- int used;
- int fd;
-
- /* create a full device path */
- snprintf(ll->name, sizeof(ll->name),
- ll->flag & LLFLG_SUBDIR ?
- _PATH_DEV_LOOP "/%d" :
- _PATH_DEV "loop%d",
- lnum);
-
- fd = open(ll->name, O_RDONLY);
- if (fd == -1) {
- if (errno == EACCES)
- ll->ct_perm++;
- return -1;
- }
- if (fstat(fd, &st) == -1)
- goto error;
- if (!S_ISBLK(st.st_mode) || major(st.st_rdev) != LOOPMAJOR)
- goto error;
-
- ll->ct_succ++;
-
- /* check if the device is wanted */
- if (!(ll->flag & (LLFLG_USEDONLY | LLFLG_FREEONLY)))
- return fd;
-
- used = is_loop_used(fd);
-
- if ((ll->flag & LLFLG_USEDONLY) && used)
- return fd;
- if ((ll->flag & LLFLG_FREEONLY) && !used)
- return fd;
-error:
- close(fd);
- return -1;
-}
-
-/* returns <N> from "loop<N>" */
-static int
-name2minor(int hasprefix, const char *name)
-{
- int n;
- char *end;
-
- if (hasprefix) {
- if (strncmp(name, "loop", 4))
- return -1;
- name += 4;
- }
- n = strtol(name, &end, 10);
- if (end && end != name && *end == '\0' && n >= 0)
- return n;
- return -1;
-}
-
-static int
-cmpnum(const void *p1, const void *p2)
-{
- return (*(int *) p1 > *(int *) p2) - (*(int *) p1 < *(int *) p2);
-}
-
-/*
- * The classic scandir() is more expensive and less portable.
- * We needn't full loop device names -- minor numbers (loop<N>)
- * are enough.
- */
-static int
-loop_scandir(const char *dirname, int **ary, int hasprefix)
-{
- DIR *dir;
- struct dirent *d;
- int n, count = 0, arylen = 0;
-
- if (!dirname || !ary)
- return -1;
- dir = opendir(dirname);
- if (!dir)
- return -1;
-
- *ary = NULL;
-
- while((d = readdir(dir))) {
- if (d->d_type != DT_BLK && d->d_type != DT_UNKNOWN && d->d_type != DT_LNK)
- continue;
- n = name2minor(hasprefix, d->d_name);
- if (n == -1 || n < NLOOPS_DEFAULT)
- continue;
- if (count + 1 > arylen) {
- int *tmp;
-
- arylen += 1;
-
- tmp = realloc(*ary, arylen * sizeof(int));
- if (!tmp) {
- free(*ary);
- return -1;
- }
- *ary = tmp;
- }
- (*ary)[count++] = n;
- }
- if (count)
- qsort(*ary, count, sizeof(int), cmpnum);
-
- closedir(dir);
- return count;
-}
-
-static int
-looplist_next(struct looplist *ll)
-{
- int fd, n;
-
- if (ll->flag & LLFLG_DONE)
- return -1;
-
- /* A) Look for used loop devices in /proc/partitions ("losetup -a" only)
- */
- if (ll->flag & LLFLG_PROCFS) {
- char buf[BUFSIZ];
-
- if (!ll->proc)
- ll->proc = fopen(_PATH_PROC_PARTITIONS, "r");
-
- while (ll->proc && fgets(buf, sizeof(buf), ll->proc)) {
- int m;
- unsigned long long sz;
- char name[128];
-
- if (sscanf(buf, " %d %d %llu %128[^\n ]",
- &m, &n, &sz, name) != 4)
- continue;
- if (m != LOOPMAJOR)
- continue;
- /* unfortunately, real minor numbers needn't to match
- * loop<N> device name. We have to follow device name.
- */
- n = name2minor(1, name);
- fd = looplist_open_dev(ll, n);
- if (fd != -1)
- return fd;
- }
- goto done;
- }
-
-
- /* B) Classic way, try first eight loop devices (default number
- * of loop devices). This is enough for 99% of all cases.
- */
- if (ll->flag & LLFLG_DFLT) {
- for (++ll->ncur; ll->ncur < NLOOPS_DEFAULT; ll->ncur++) {
- fd = looplist_open_dev(ll, ll->ncur);
- if (fd != -1)
- return fd;
- }
- ll->flag &= ~LLFLG_DFLT;
- }
-
- /* C) the worst possibility, scan all /dev or /dev/loop
- */
- if (!ll->minors) {
- ll->nminors = (ll->flag & LLFLG_SUBDIR) ?
- loop_scandir(_PATH_DEV_LOOP, &ll->minors, 0) :
- loop_scandir(_PATH_DEV, &ll->minors, 1);
- ll->ncur = -1;
- }
- for (++ll->ncur; ll->ncur < ll->nminors; ll->ncur++) {
- fd = looplist_open_dev(ll, ll->minors[ll->ncur]);
- if (fd != -1)
- return fd;
- }
-
-done:
- looplist_close(ll);
- return -1;
-}
-
-/* Find loop device associated with given @filename. Used for unmounting loop
- * device specified by associated backing file.
- *
- * returns: 1 no such device/error
- * 2 more than one loop device associated with @filename
- * 0 exactly one loop device associated with @filename
- * (@loopdev points to string containing full device name)
- */
-int
-find_loopdev_by_backing_file(const char *filename, char **loopdev)
-{
- struct looplist ll;
- struct stat filestat;
- int fd;
- int devs_n = 0; /* number of loop devices found */
- char* devname = NULL;
-
- if (stat(filename, &filestat) == -1) {
- perror(filename);
- return 1;
- }
-
- if (looplist_open(&ll, LLFLG_USEDONLY) == -1) {
- warnx(_("/dev directory does not exist."));
- return 1;
- }
-
- while((devs_n < 2) && (fd = looplist_next(&ll)) != -1) {
- if (is_associated(fd, &filestat, 0, 0) == 1) {
- if (!devname)
- devname = xstrdup(ll.name);
- devs_n++;
- }
- close(fd);
- }
- looplist_close(&ll);
-
- if (devs_n == 1) {
- *loopdev = devname;
- return 0; /* exactly one loopdev */
- }
- free(devname);
- return devs_n ? 2 : 1; /* more loopdevs or error */
-}
-
-
-static int
-show_loop_fd(int fd, char *device) {
- struct loop_info64 loopinfo64;
- int errsv;
-
- if (ioctl(fd, LOOP_GET_STATUS64, &loopinfo64) == 0) {
-
- char *lofile = NULL;
-
- loopinfo64.lo_file_name[LO_NAME_SIZE-2] = '*';
- loopinfo64.lo_file_name[LO_NAME_SIZE-1] = 0;
- loopinfo64.lo_crypt_name[LO_NAME_SIZE-1] = 0;
-
- /* ioctl has limited buffer for backing file name, since
- * kernel 2.6.37 the filename is available in sysfs too
- */
- if (strlen((char *) loopinfo64.lo_file_name) == LO_NAME_SIZE - 1)
- lofile = loopfile_from_sysfs(device);
- if (!lofile)
- lofile = (char *) loopinfo64.lo_file_name;
-
- printf("%s: [%04" PRIx64 "]:%" PRIu64 " (%s)",
- device, loopinfo64.lo_device, loopinfo64.lo_inode,
- lofile);
-
- if (lofile != (char *) loopinfo64.lo_file_name)
- free(lofile);
-
- if (loopinfo64.lo_offset)
- printf(_(", offset %" PRIu64 ), loopinfo64.lo_offset);
-
- if (loopinfo64.lo_sizelimit)
- printf(_(", sizelimit %" PRIu64 ), loopinfo64.lo_sizelimit);
-
- if (loopinfo64.lo_encrypt_type ||
- loopinfo64.lo_crypt_name[0]) {
- char *e = (char *)loopinfo64.lo_crypt_name;
-
- if (*e == 0 && loopinfo64.lo_encrypt_type == 1)
- e = "XOR";
- printf(_(", encryption %s (type %" PRIu32 ")"),
- e, loopinfo64.lo_encrypt_type);
- }
- printf("\n");
- return 0;
- }
-
- errsv = errno;
- fprintf(stderr, _("loop: can't get info on device %s: %s\n"),
- device, strerror (errsv));
- return 1;
-}
-
-static int
-show_loop(char *device) {
- int ret, fd;
-
- if ((fd = open(device, O_RDONLY)) < 0) {
- int errsv = errno;
- fprintf(stderr, _("loop: can't open device %s: %s\n"),
- device, strerror (errsv));
- return 2;
- }
- ret = show_loop_fd(fd, device);
- close(fd);
- return ret;
-}
-
-
-static int
-show_used_loop_devices (void) {
- struct looplist ll;
- int fd;
-
- if (looplist_open(&ll, LLFLG_USEDONLY) == -1) {
- warnx(_("/dev directory does not exist."));
- return 1;
- }
-
- while((fd = looplist_next(&ll)) != -1) {
- show_loop_fd(fd, ll.name);
- close(fd);
- }
- looplist_close(&ll);
-
- if (!ll.ct_succ && ll.ct_perm) {
- warnx(_("no permission to look at /dev/loop%s<N>"),
- (ll.flag & LLFLG_SUBDIR) ? "/" : "");
- return 1;
- }
- return 0;
-}
-
-/* check if the loopfile is already associated with the same given
- * parameters.
- *
- * returns: 0 unused / error
- * 1 loop device already used
- */
-static int
-is_associated(int dev, struct stat *file, unsigned long long offset, int isoff)
-{
- struct loop_info64 linfo64;
- int ret = 0;
-
- if (ioctl(dev, LOOP_GET_STATUS64, &linfo64) == 0) {
- if (file->st_dev == linfo64.lo_device &&
- file->st_ino == linfo64.lo_inode &&
- (isoff == 0 || offset == linfo64.lo_offset))
- ret = 1;
-
- }
-
- return ret;
-}
-
-/* check if the loop file is already used with the same given
- * parameters. We check for device no, inode and offset.
- * returns: associated devname or NULL
- */
-char *
-loopfile_used (const char *filename, unsigned long long offset) {
- struct looplist ll;
- char *devname = NULL;
- struct stat filestat;
- int fd;
-
- if (stat(filename, &filestat) == -1) {
- perror(filename);
- return NULL;
- }
-
- if (looplist_open(&ll, LLFLG_USEDONLY) == -1) {
- warnx(_("/dev directory does not exist."));
- return NULL;
- }
-
- while((fd = looplist_next(&ll)) != -1) {
- int res = is_associated(fd, &filestat, offset, 1);
- close(fd);
- if (res == 1) {
- devname = xstrdup(ll.name);
- break;
- }
- }
- looplist_close(&ll);
-
- return devname;
-}
-
-int
-loopfile_used_with(char *devname, const char *filename, unsigned long long offset)
-{
- struct stat statbuf;
- int fd, ret;
-
- if (!is_loop_device(devname))
- return 0;
-
- if (stat(filename, &statbuf) == -1)
- return 0;
-
- fd = open(devname, O_RDONLY);
- if (fd == -1)
- return 0;
-
- ret = is_associated(fd, &statbuf, offset, 1);
- close(fd);
- return ret;
-}
-
-char *
-find_unused_loop_device (void) {
- struct looplist ll;
- char *devname = NULL;
- int fd;
-
- if (looplist_open(&ll, LLFLG_FREEONLY) == -1) {
- warnx(_("/dev directory does not exist."));
- return NULL;
- }
-
- if ((fd = looplist_next(&ll)) != -1) {
- close(fd);
- devname = xstrdup(ll.name);
- }
- looplist_close(&ll);
- if (devname)
- return devname;
-
- if (!ll.ct_succ && ll.ct_perm)
- warnx(_("no permission to look at /dev/loop%s<N>"),
- (ll.flag & LLFLG_SUBDIR) ? "/" : "");
- else if (ll.ct_succ)
- warnx(_("could not find any free loop device"));
- else
- warnx(_(
- "Could not find any loop device. Maybe this kernel "
- "does not know\n"
- " about the loop device? (If so, recompile or "
- "`modprobe loop'.)"));
- return NULL;
-}
-
/*
* A function to read the passphrase either from the terminal or from
* an open file descriptor.
*/
-static char *
-xgetpass(int pfd, const char *prompt) {
+static char *xgetpass(int pfd, const char *prompt)
+{
char *pass;
int buflen, i;
@@ -669,160 +78,6 @@ xgetpass(int pfd, const char *prompt) {
return pass;
}
-static int
-digits_only(const char *s) {
- while (*s)
- if (!isdigit(*s++))
- return 0;
- return 1;
-}
-
-/*
- * return codes:
- * 0 - success
- * 1 - error
- * 2 - error (EBUSY)
- */
-int
-set_loop(const char *device, const char *file, unsigned long long offset,
- unsigned long long sizelimit, const char *encryption, int pfd, int *options) {
- struct loop_info64 loopinfo64;
- int fd, ffd, mode, i;
- char *pass;
- char *filename;
-
- if (verbose) {
- char *xdev = loopfile_used(file, offset);
-
- if (xdev) {
- printf(_("warning: %s is already associated with %s\n"),
- file, xdev);
- free(xdev);
- }
- }
-
- mode = (*options & SETLOOP_RDONLY) ? O_RDONLY : O_RDWR;
- if ((ffd = open(file, mode)) < 0) {
- if (!(*options & SETLOOP_RDONLY) &&
- (errno == EROFS || errno == EACCES))
- ffd = open(file, mode = O_RDONLY);
- if (ffd < 0) {
- perror(file);
- return 1;
- }
- if (verbose)
- printf(_("warning: %s: is write-protected, using read-only.\n"),
- file);
- *options |= SETLOOP_RDONLY;
- }
- if ((fd = open(device, mode)) < 0) {
- perror (device);
- close(ffd);
- return 1;
- }
- memset(&loopinfo64, 0, sizeof(loopinfo64));
-
- if (!(filename = canonicalize_path(file)))
- filename = (char *) file;
- xstrncpy((char *)loopinfo64.lo_file_name, filename, LO_NAME_SIZE);
-
- if (encryption && *encryption) {
- if (digits_only(encryption)) {
- loopinfo64.lo_encrypt_type = atoi(encryption);
- } else {
- loopinfo64.lo_encrypt_type = LO_CRYPT_CRYPTOAPI;
- snprintf((char *)loopinfo64.lo_crypt_name, LO_NAME_SIZE,
- "%s", encryption);
- }
- }
-
- loopinfo64.lo_offset = offset;
- loopinfo64.lo_sizelimit = sizelimit;
-
-#ifdef MCL_FUTURE
- /*
- * Oh-oh, sensitive data coming up. Better lock into memory to prevent
- * passwd etc being swapped out and left somewhere on disk.
- */
- if (loopinfo64.lo_encrypt_type != LO_CRYPT_NONE) {
- if(mlockall(MCL_CURRENT | MCL_FUTURE)) {
- perror("memlock");
- fprintf(stderr, _("Couldn't lock into memory, exiting.\n"));
- exit(1);
- }
- }
-#endif
-
- switch (loopinfo64.lo_encrypt_type) {
- case LO_CRYPT_NONE:
- loopinfo64.lo_encrypt_key_size = 0;
- break;
- case LO_CRYPT_XOR:
- pass = getpass(_("Password: "));
- goto gotpass;
- default:
- pass = xgetpass(pfd, _("Password: "));
- gotpass:
- memset(loopinfo64.lo_encrypt_key, 0, LO_KEY_SIZE);
- xstrncpy((char *)loopinfo64.lo_encrypt_key, pass, LO_KEY_SIZE);
- memset(pass, 0, strlen(pass));
- loopinfo64.lo_encrypt_key_size = LO_KEY_SIZE;
- }
-
- if (ioctl(fd, LOOP_SET_FD, ffd) < 0) {
- int rc = 1;
-
- if (errno == EBUSY) {
- if (verbose)
- printf(_("ioctl LOOP_SET_FD failed: %m\n"));
- rc = 2;
- } else
- perror("ioctl: LOOP_SET_FD");
-
- close(fd);
- close(ffd);
- if (file != filename)
- free(filename);
- return rc;
- }
- close (ffd);
-
- if (*options & SETLOOP_AUTOCLEAR)
- loopinfo64.lo_flags = LO_FLAGS_AUTOCLEAR;
-
- i = ioctl(fd, LOOP_SET_STATUS64, &loopinfo64);
- if (i)
- perror("ioctl: LOOP_SET_STATUS64");
-
- if ((*options & SETLOOP_AUTOCLEAR) && !is_loopfd_autoclear(fd))
- /* kernel doesn't support loop auto-destruction */
- *options &= ~SETLOOP_AUTOCLEAR;
-
- memset(&loopinfo64, 0, sizeof(loopinfo64));
-
- if (i) {
- ioctl (fd, LOOP_CLR_FD, 0);
- close (fd);
- if (file != filename)
- free(filename);
- return 1;
- }
-
- /*
- * HACK: here we're leaking a file descriptor,
- * but mount is a short-lived process anyway.
- */
- if (!(*options & SETLOOP_AUTOCLEAR))
- close (fd);
-
- if (verbose)
- printf(_("set_loop(%s,%s,%llu,%llu): success\n"),
- device, filename, offset, sizelimit);
- if (file != filename)
- free(filename);
- return 0;
-}
-
static int printf_loopdev(struct loopdev_cxt *lc)
{
uint64_t x;
@@ -967,17 +222,10 @@ usage(FILE *out) {
int main(int argc, char **argv)
{
struct loopdev_cxt lc;
- int act = 0, flags = 0;
- char *file = NULL;
- uint64_t offset = 0;
-
- char *p, *sizelimit, *encryption, *passfd, *device;
- int find, c;
- int res = 0;
- int showdev = 0;
- int ro = 0;
- int pfd = -1;
- uintmax_t slimit = 0;
+ 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;
loopcxt_init(&lc, 0);
/*loopcxt_enable_debug(&lc, TRUE);*/
@@ -1004,9 +252,6 @@ int main(int argc, char **argv)
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
- find = 0;
- sizelimit = encryption = passfd = NULL;
-
while ((c = getopt_long(argc, argv, "ac:d:De:E:fhj:o:p:rsv",
longopts, NULL)) != -1) {
@@ -1024,7 +269,7 @@ int main(int argc, char **argv)
loopcxt_set_device(&lc, optarg);
break;
case 'r':
- ro = 1;
+ lo_flags |= LO_FLAGS_READ_ONLY;
break;
case 'd':
act = A_DELETE;
@@ -1039,7 +284,6 @@ int main(int argc, char **argv)
break;
case 'f':
act = A_FIND_FREE;
- find = 1;
break;
case 'h':
usage(stdout);
@@ -1055,7 +299,8 @@ int main(int argc, char **argv)
flags |= LOOPDEV_FL_OFFSET;
break;
case 'p':
- passfd = optarg;
+ passfd = strtol_or_err(optarg,
+ _("invalid passphrase file descriptor"));
break;
case 's':
showdev = 1;
@@ -1063,11 +308,12 @@ int main(int argc, char **argv)
case 'v':
verbose = 1;
break;
-
case 128: /* --sizelimit */
- sizelimit = optarg;
+ if (strtosize(optarg, &sizelimit))
+ errx(EXIT_FAILURE,
+ _("invalid size '%s' specified"), optarg);
+ flags |= LOOPDEV_FL_SIZELIMIT;
break;
-
default:
usage(stderr);
}
@@ -1076,8 +322,86 @@ int main(int argc, char **argv)
if (argc == 1)
usage(stderr);
+ if (act == A_FIND_FREE && optind < argc) {
+ /*
+ * losetup -f <backing_file>
+ */
+ act = A_CREATE;
+ file = argv[optind++];
+ }
+ if (!act) {
+ /*
+ * losetup <loopdev> <backing_file>
+ */
+ 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) {
@@ -1101,74 +425,11 @@ int main(int argc, char **argv)
res = set_capacity(&lc);
break;
default:
+ usage(stderr);
break;
}
loopcxt_deinit(&lc);
-
- if (act)
- return res ? EXIT_FAILURE : EXIT_SUCCESS;
-
- if (find) {
- if ( argc < optind || argc > optind+1)
- usage(stderr);
- } else {
- if (argc < optind+1 || argc > optind+2)
- usage(stderr);
- }
-
- if (sizelimit && strtosize(sizelimit, &slimit)) {
- warnx(_("invalid sizelimit '%s' specified"), sizelimit);
- usage(stderr);
- }
-
- if (find) {
- device = find_unused_loop_device();
- if (device == NULL)
- return -1;
- if (argc == optind) {
- if (verbose)
- printf(_("Loop device is %s\n"), device);
- printf("%s\n", device);
- return 0;
- }
- file = argv[optind];
- } else {
- device = argv[optind];
- if (argc == optind+1)
- file = NULL;
- else
- file = argv[optind+1];
- }
-
- if (file == NULL)
- res = show_loop(device);
- else {
- if (passfd && sscanf(passfd, "%d", &pfd) != 1)
- usage(stderr);
- do {
- res = set_loop(device, file, offset, slimit, encryption, pfd, &ro);
- if (res == 2 && find) {
- if (verbose)
- printf(_("stolen loop=%s...trying again\n"),
- device);
- free(device);
- if (!(device = find_unused_loop_device()))
- return -1;
- }
- } while (find && res == 2);
-
- if (device) {
- if (res == 2)
- warnx(_("%s: device is busy"), device);
- else if (res == 0) {
- if (verbose)
- printf(_("Loop device is %s\n"), device);
- if (showdev && find)
- printf("%s\n", device);
- }
- }
- }
- return res;
+ return res ? EXIT_FAILURE : EXIT_SUCCESS;
}