]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
losetup: rewrite loop setup
authorKarel Zak <kzak@redhat.com>
Wed, 21 Dec 2011 18:24:58 +0000 (19:24 +0100)
committerKarel Zak <kzak@redhat.com>
Thu, 22 Dec 2011 11:19:38 +0000 (12:19 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
include/loopdev.h
lib/loopdev.c
sys-utils/losetup.c

index 27f9668ebabbdf6f9ea43b6976f4e820451a7c75..7c7880e1b52785d54ec74d8faebd3eb63e0de9ec 100644 (file)
@@ -114,6 +114,7 @@ enum {
        LOOPDEV_FL_NOIOCTL      = (1 << 6),
        LOOPDEV_FL_DEVSUBDIR    = (1 << 7),
        LOOPDEV_FL_CONTROL      = (1 << 8),     /* system with /dev/loop-control */
+       LOOPDEV_FL_SIZELIMIT    = (1 << 9)
 };
 
 /*
@@ -139,6 +140,7 @@ extern void loopcxt_deinit(struct loopdev_cxt *lc);
 extern void loopcxt_enable_debug(struct loopdev_cxt *lc, int enable);
 
 extern int loopcxt_set_device(struct loopdev_cxt *lc, const char *device);
+extern int loopcxt_has_device(struct loopdev_cxt *lc);
 extern char *loopcxt_strdup_device(struct loopdev_cxt *lc);
 extern const char *loopcxt_get_device(struct loopdev_cxt *lc);
 extern struct sysfs_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc);
index fa23b0e90e9104b726b928cd41ec9f67383d685d..e0467abaa85d83d63e53f2ee19261e1c70ac29e9 100644 (file)
@@ -118,6 +118,11 @@ int loopcxt_set_device(struct loopdev_cxt *lc, const char *device)
        return 0;
 }
 
+int loopcxt_has_device(struct loopdev_cxt *lc)
+{
+       return lc && *lc->device;
+}
+
 /*
  * @lc: context
  * @flags: LOOPDEV_FL_* flags
@@ -948,12 +953,15 @@ int loopcxt_set_encryption(struct loopdev_cxt *lc,
                lc->info.lo_encrypt_key_size = 0;
                break;
        default:
+               DBG(lc, loopdev_debug("setting encryption key"));
                memset(lc->info.lo_encrypt_key, 0, LO_KEY_SIZE);
                strncpy((char *)lc->info.lo_encrypt_key, password, LO_KEY_SIZE);
                lc->info.lo_encrypt_key[LO_KEY_SIZE - 1] = '\0';
                lc->info.lo_encrypt_key_size = LO_KEY_SIZE;
                break;
        }
+
+       DBG(lc, loopdev_debug("encryption successfully set"));
        return 0;
 }
 
index 0d8bd858d0fdc4058d190aa2c456f70ccfc6fcd8..ab45dff1d16c6d89a8df0a47b4977a7de803fe0f 100644 (file)
@@ -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;
 }