summaryrefslogtreecommitdiffstats
path: root/mount/lomount.c
diff options
context:
space:
mode:
authorKarel Zak2008-08-01 12:18:03 +0200
committerKarel Zak2008-08-04 13:54:27 +0200
commitedb68d0ca36c54fa5996bfd917eb236780f0ff80 (patch)
tree3bdadcc76666480f4422b68c3ab880b4210cd18a /mount/lomount.c
parentuse getpagesize() (diff)
downloadkernel-qcow2-util-linux-edb68d0ca36c54fa5996bfd917eb236780f0ff80.tar.gz
kernel-qcow2-util-linux-edb68d0ca36c54fa5996bfd917eb236780f0ff80.tar.xz
kernel-qcow2-util-linux-edb68d0ca36c54fa5996bfd917eb236780f0ff80.zip
losetup: looplist_* refactoring, remove scandir()
This patch replaces scandir-based implementation with readdir(). The readdir(3) is less expensive and more portable (to non-glibc environment). The patch also replaces sysfs-based solution with simpler /proc/partitions parsing. The /proc/partitions includes all used loop devices on all systems (include 2.4). This solution seems faster than scandir(/sys/block/) too. Summary, the losetup (with this patch) uses three methods to found a loop device: a) parse /proc/partitions to found already used loop devices (for loserup -a) b) stat(2) for all loop[0-7] devices (default number of loop devices). This is classic method from util-linux <= 2.13. This method is good enough for standard Linux machines with default number of loop devices. c) scan all /dev or /dev/loop/ for loop devices. This is useful for crazy people who need more than 8 loop devices. Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'mount/lomount.c')
-rw-r--r--mount/lomount.c302
1 files changed, 153 insertions, 149 deletions
diff --git a/mount/lomount.c b/mount/lomount.c
index de28a2653..5cf50f884 100644
--- a/mount/lomount.c
+++ b/mount/lomount.c
@@ -2,7 +2,6 @@
/*
* losetup.c - setup and control loop devices
*/
-
#include <stdio.h>
#include <string.h>
#include <ctype.h>
@@ -24,6 +23,7 @@
#include "sundries.h"
#include "xmalloc.h"
#include "realpath.h"
+#include "pathnames.h"
#define SIZE(a) (sizeof(a)/sizeof(a[0]))
@@ -63,17 +63,16 @@ loop_info64_to_old(const struct loop_info64 *info64, struct loop_info *info)
#define DEV_LOOP_PATH "/dev/loop"
#define DEV_PATH "/dev"
-#define SYSFS_BLOCK_PATH "/sys/block"
#define LOOPMAJOR 7
#define NLOOPS_DEFAULT 8 /* /dev/loop[0-7] */
struct looplist {
int flag; /* scanning options */
- int ndef; /* number of tested default devices */
- struct dirent **names; /* scandir-like list of loop devices */
- int nnames; /* number of items in names */
- int ncur; /* current possition in direcotry */
- char name[32]; /* device name */
+ FILE *proc; /* /proc/partitions */
+ int ncur; /* current possition */
+ 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 */
@@ -82,7 +81,7 @@ struct looplist {
#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_SYSFS (1 << 4) /* try to use /sys/block */
+#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 */
@@ -132,50 +131,6 @@ is_loop_autoclear(const char *device)
return rc;
}
-static char *
-looplist_mk_devname(struct looplist *ll, int num)
-{
- if (ll->flag & LLFLG_SUBDIR)
- snprintf(ll->name, sizeof(ll->name),
- DEV_LOOP_PATH "/%d", num);
- else
- snprintf(ll->name, sizeof(ll->name),
- DEV_PATH "/loop%d", num);
-
- return is_loop_device(ll->name) ? ll->name : NULL;
-}
-
-/* ignores all non-loop devices, default loop devices */
-static int
-filter_loop(const struct dirent *d)
-{
- return strncmp(d->d_name, "loop", 4) == 0;
-}
-
-/* all loops exclude default loops */
-static int
-filter_loop_ndflt(const struct dirent *d)
-{
- int mn;
-
- if (strncmp(d->d_name, "loop", 4) == 0 &&
- sscanf(d->d_name, "loop%d", &mn) == 1 &&
- mn >= NLOOPS_DEFAULT)
- return 1;
- return 0;
-}
-
-static int
-filter_loop_num(const struct dirent *d)
-{
- char *end = NULL;
- int mn = strtol(d->d_name, &end, 10);
-
- if (mn >= NLOOPS_DEFAULT && end && *end == '\0')
- return 1;
- return 0;
-}
-
static int
looplist_open(struct looplist *ll, int flag)
{
@@ -183,7 +138,6 @@ looplist_open(struct looplist *ll, int flag)
memset(ll, 0, sizeof(*ll));
ll->flag = flag;
- ll->ndef = -1;
ll->ncur = -1;
if (stat(DEV_PATH, &st) == -1 || (!S_ISDIR(st.st_mode)))
@@ -193,9 +147,8 @@ looplist_open(struct looplist *ll, int flag)
ll->flag |= LLFLG_SUBDIR; /* /dev/loop/ exists */
if ((ll->flag & LLFLG_USEDONLY) &&
- stat(SYSFS_BLOCK_PATH, &st) == 0 &&
- S_ISDIR(st.st_mode))
- ll->flag |= LLFLG_SYSFS; /* try to use /sys/block/loopN */
+ stat(_PATH_PROC_PARTITIONS, &st) == 0)
+ ll->flag |= LLFLG_PROCFS; /* try /proc/partitions */
ll->flag |= LLFLG_DFLT; /* required! */
return 0;
@@ -204,135 +157,184 @@ looplist_open(struct looplist *ll, int flag)
static void
looplist_close(struct looplist *ll)
{
- if (ll->names) {
- for(++ll->ncur; ll->ncur < ll->nnames; ll->ncur++)
- free(ll->names[ll->ncur]);
-
- free(ll->names);
- ll->names = NULL;
- ll->nnames = 0;
- }
+ if (ll->minors)
+ 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_is_wanted(struct looplist *ll, int fd)
+looplist_open_dev(struct looplist *ll, int lnum)
{
- int ret;
+ struct stat st;
+ int used;
+ int fd;
+
+ /* create a full device path */
+ snprintf(ll->name, sizeof(ll->name),
+ ll->flag & LLFLG_SUBDIR ?
+ DEV_LOOP_PATH "/%d" :
+ DEV_PATH "/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 1;
- ret = is_loop_used(fd);
+ return fd;
- if ((ll->flag & LLFLG_USEDONLY) && ret == 0)
- return 0;
- if ((ll->flag & LLFLG_FREEONLY) && ret == 1)
- return 0;
+ used = is_loop_used(fd);
- return 1;
+ 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);
+}
+
+/*
+ * The classic scandir() is more expensive and less portable.
+ * We needn't full loop device names -- minor numers (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)
+ continue;
+ n = name2minor(hasprefix, d->d_name);
+ if (n == -1 || n < NLOOPS_DEFAULT)
+ continue;
+ if (count + 1 > arylen) {
+ arylen += 1;
+ *ary = *ary ? realloc(*ary, arylen * sizeof(int)) :
+ malloc(arylen * sizeof(int));
+ if (!*ary)
+ return -1;
+ }
+ (*ary)[count++] = n;
+ }
+ if (count)
+ qsort(*ary, count, sizeof(int), cmpnum);
+
+ closedir(dir);
+ return count;
}
static int
looplist_next(struct looplist *ll)
{
- int fd;
- int ret;
- char *dirname, *dev;
+ int fd, n;
if (ll->flag & LLFLG_DONE)
return -1;
- /* A) try to use /sys/block/loopN devices (for losetup -a only)
+ /* A) Look for used loop devices in /proc/partitions ("losetup -a" only)
*/
- if (ll->flag & LLFLG_SYSFS) {
- int mn;
+ if (ll->flag & LLFLG_PROCFS) {
+ char buf[BUFSIZ];
- if (!ll->nnames) {
- ll->nnames = scandir(SYSFS_BLOCK_PATH, &ll->names,
- filter_loop, versionsort);
- ll->ncur = -1;
- }
- for(++ll->ncur; ll->ncur < ll->nnames; ll->ncur++) {
- ret = sscanf(ll->names[ll->ncur]->d_name, "loop%d", &mn);
- free(ll->names[ll->ncur]);
- if (ret != 1)
+ 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;
- dev = looplist_mk_devname(ll, mn);
- if (dev) {
- ll->ct_succ++;
- if ((fd = open(dev, O_RDONLY)) > -1) {
- if (looplist_is_wanted(ll, fd))
- return fd;
- close(fd);
- } else if (errno == EACCES)
- ll->ct_perm++;
- }
+ if (m != LOOPMAJOR)
+ continue;
+ fd = looplist_open_dev(ll, n);
+ if (fd != -1)
+ return fd;
}
- if (ll->nnames)
- free(ll->names);
- ll->names = NULL;
- ll->ncur = -1;
- ll->nnames = 0;
- ll->flag &= ~LLFLG_SYSFS;
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++) {
- dev = looplist_mk_devname(ll, ll->ncur);
- if (dev) {
- ll->ct_succ++;
- if ((fd = open(dev, O_RDONLY)) > -1) {
- if (looplist_is_wanted(ll, fd))
- return fd;
- close(fd);
- } else if (errno == EACCES)
- ll->ct_perm++;
- }
+ fd = looplist_open_dev(ll, ll->ncur);
+ if (fd != -1)
+ return fd;
}
ll->flag &= ~LLFLG_DFLT;
- ll->ncur = -1;
}
-
/* C) the worst posibility, scan all /dev or /dev/loop
*/
- dirname = ll->flag & LLFLG_SUBDIR ? DEV_LOOP_PATH : DEV_PATH;
-
- if (!ll->nnames) {
- ll->nnames = scandir(dirname, &ll->names,
- ll->flag & LLFLG_SUBDIR ?
- filter_loop_num : filter_loop_ndflt,
- versionsort);
+ if (!ll->minors) {
+ ll->nminors = (ll->flag & LLFLG_SUBDIR) ?
+ loop_scandir(DEV_LOOP_PATH, &ll->minors, 0) :
+ loop_scandir(DEV_PATH, &ll->minors, 1);
ll->ncur = -1;
}
-
- for(++ll->ncur; ll->ncur < ll->nnames; ll->ncur++) {
- struct stat st;
-
- snprintf(ll->name, sizeof(ll->name),
- "%s/%s", dirname, ll->names[ll->ncur]->d_name);
- free(ll->names[ll->ncur]);
- ret = stat(ll->name, &st);
-
- if (ret == 0 && S_ISBLK(st.st_mode) &&
- major(st.st_rdev) == LOOPMAJOR &&
- minor(st.st_rdev) >= NLOOPS_DEFAULT) {
- ll->ct_succ++;
- fd = open(ll->name, O_RDONLY);
-
- if (fd != -1) {
- if (looplist_is_wanted(ll, fd))
- return fd;
- close(fd);
- } else if (errno == EACCES)
- ll->ct_perm++;
- }
+ 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;
@@ -429,8 +431,9 @@ show_used_loop_devices (void) {
}
looplist_close(&ll);
- if (ll.ct_succ && ll.ct_perm) {
- error(_("%s: no permission to look at /dev/loop#"), progname);
+ if (!ll.ct_succ && ll.ct_perm) {
+ error(_("%s: no permission to look at /dev/loop%s<N>"), progname,
+ (ll.flag & LLFLG_SUBDIR) ? "/" : "");
return 1;
}
return 0;
@@ -576,8 +579,9 @@ find_unused_loop_device (void) {
if (devname)
return devname;
- if (ll.ct_succ && ll.ct_perm)
- error(_("%s: no permission to look at /dev/loop#"), progname);
+ if (!ll.ct_succ && ll.ct_perm)
+ error(_("%s: no permission to look at /dev/loop%s<N>"), progname,
+ (ll.flag & LLFLG_SUBDIR) ? "/" : "");
else if (ll.ct_succ)
error(_("%s: could not find any free loop device"), progname);
else