Re: [PATCH] mount: add fallback for versionsort()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, Jul 26, 2008 at 11:04:31PM +0200, Karel Zak wrote:
> On Fri, Jul 25, 2008 at 09:49:10AM +0100, James Youngman wrote:
>
> > Yes, I know that Linux systems running glibc will be using the glibc
> > version of the file, and that having an old version of this file for
> > the benefit of other systems is better than nothing, but I am
> > interested in how we can solve the whole problem.
> 
>  I thought about it in last days and I'll remove scandir() and
>  versionsort() from the code. We needn't it at all.

 I've committed a patch (see below) that removes scandir() stuff and
 uses readdir() only. This solution seems faster too.

    Karel


>From edb68d0ca36c54fa5996bfd917eb236780f0ff80 Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@xxxxxxxxxx>
Date: Fri, 1 Aug 2008 12:18:03 +0200
Subject: [PATCH] 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@xxxxxxxxxx>
---
 include/pathnames.h |    1 +
 mount/lomount.c     |  302 ++++++++++++++++++++++++++-------------------------
 2 files changed, 154 insertions(+), 149 deletions(-)

diff --git a/include/pathnames.h b/include/pathnames.h
index e171d93..ad35bf9 100644
--- a/include/pathnames.h
+++ b/include/pathnames.h
@@ -73,6 +73,7 @@
 #define _PATH_PROC_SWAPS	"/proc/swaps"
 #define _PATH_PROC_FILESYSTEMS	"/proc/filesystems"
 #define _PATH_PROC_MOUNTS	"/proc/mounts"
+#define _PATH_PROC_PARTITIONS	"/proc/partitions"
 
 #ifndef _PATH_MOUNTED
 # ifdef MOUNTED					/* deprecated */
diff --git a/mount/lomount.c b/mount/lomount.c
index de28a26..5cf50f8 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
-- 
1.5.5.1


--
To unsubscribe from this list: send the line "unsubscribe util-linux-ng" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux