Re: [PATCH] losetup: support unlimited number of loops

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

 



>  No feedback.. so applied (we will see ... :-)
didn`t i really comment on that?
mhh - weird!


>  Roland, didn't you ask for more than 256 loop devices?
>  http://www.mail-archive.com/util-linux-ng@xxxxxxxxxxxxxxx/msg00842.html

oh yes, indeed! 
actually i have a system where i use more than 256 loop devices (central cd-rom server) and it`s working very well.
i`m sure i did try the patch once and it seemed to work, but since the cd-rom server was already in production, i was using a less intrusive hack on that to work arond the problem. iirc, the patch didn`t apply to suse util-linux package and i didnŽt want to replace it by the original util-linux package.

will try your patch again very soon and give feedback - thank you for asking!

regards
roland



> 
>     Karel
> 

> -----Ursprüngliche Nachricht-----
> Von: "Karel Zak" <kzak@xxxxxxxxxx>
> Gesendet: 08.02.08 02:17:07
> An: util-linux-ng@xxxxxxxxxxxxxxx
> Betreff: Re: [PATCH] losetup: support unlimited number of loops


> 
> 
>  No feedback.. so applied (we will see ... :-)
> 
>  Roland, didn't you ask for more than 256 loop devices?
>  http://www.mail-archive.com/util-linux-ng@xxxxxxxxxxxxxxx/msg00842.html
> 
>     Karel
> 
> On Mon, Nov 26, 2007 at 12:37:42PM +0100, Karel Zak wrote:
> > Old implementation:
> > 
> >  - supports 256 loop devices only
> >  - doesn't support gaps in list of loop devices
> >    (e.g. loop0, loop1, loop3 -- loop3 is invisible)
> > 
> > Kernel 2.6.21 removes artificial maximum 256 loop device. Now the maximum
> > of loop devices could be really huge (depends on limit of MINOR
> > numbers). It means we need a better way how work with loop devices
> > than blindly call stat(2) for all 0-1048575 devices.
> > 
> > This patch uses three methods:
> > 
> >  a) scan /sys/block/loopN (used for losetup -a only). This method is
> >     probably the fastest way how found used loop device on machine with
> >     huge number of devices in /dev.
> > 
> >  b) classic way, stat(2) for all loop[0-7] devices (default number of
> >     loop devices). This cheap method is sufficient for 99% of all machines.
> > 
> >  c) scan all /dev/loopN or /dev/loop/N
> > 
> > Signed-off-by: Karel Zak <kzak@xxxxxxxxxx>
> > ---
> >  mount/lomount.c |  427 +++++++++++++++++++++++++++++++++++++++++--------------
> >  1 files changed, 318 insertions(+), 109 deletions(-)
> > 
> > diff --git a/mount/lomount.c b/mount/lomount.c
> > index 5bd8954..01bfa14 100644
> > --- a/mount/lomount.c
> > +++ b/mount/lomount.c
> > @@ -1,7 +1,4 @@
> >  /* Originally from Ted's losetup.c */
> > -
> > -#define LOOPMAJOR	7
> > -
> >  /*
> >   * losetup.c - setup and control loop devices
> >   */
> > @@ -17,6 +14,7 @@
> >  #include <sys/stat.h>
> >  #include <sys/mman.h>
> >  #include <sys/sysmacros.h>
> > +#include <dirent.h>
> >  
> >  #include "loop.h"
> >  #include "lomount.h"
> > @@ -60,20 +58,260 @@ loop_info64_to_old(const struct loop_info64 *info64, struct loop_info *info)
> >          return 0;
> >  }
> >  
> > +#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 */
> > +	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_SYSFS	(1 << 4)	/* try to use /sys/block */
> > +#define LLFLG_SUBDIR	(1 << 5)	/* /dev/loop/N */
> > +#define LLFLG_DFLT	(1 << 6)	/* directly try to check default loops */
> > +
> > +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_info li;
> > +	return ioctl (fd, LOOP_GET_STATUS, &li) == 0;
> > +}
> > +
> > +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)
> > +{
> > +	struct stat st;
> > +
> > +	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)))
> > +		return -1;			/* /dev doesn't exist */
> > +
> > +	if (stat(DEV_LOOP_PATH, &st) == 0 && S_ISDIR(st.st_mode))
> > +		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 */
> > +
> > +	ll->flag |= LLFLG_DFLT;			/* required! */
> > +	return 0;
> > +}
> > +
> > +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;
> > +	}
> > +	ll->ncur = -1;
> > +	ll->flag |= LLFLG_DONE;
> > +}
> > +
> > +static int
> > +looplist_is_wanted(struct looplist *ll, int fd)
> > +{
> > +	int ret;
> > +
> > +	if (!(ll->flag & (LLFLG_USEDONLY | LLFLG_FREEONLY)))
> > +		return 1;
> > +	ret = is_loop_used(fd);
> > +
> > +	if ((ll->flag & LLFLG_USEDONLY) && ret == 0)
> > +		return 0;
> > +	if ((ll->flag & LLFLG_FREEONLY) && ret == 1)
> > +		return 0;
> > +
> > +	return 1;
> > +}
> > +
> > +static int
> > +looplist_next(struct looplist *ll)
> > +{
> > +	int fd;
> > +	int ret;
> > +	char *dirname, *dev;
> > +
> > +	if (ll->flag & LLFLG_DONE)
> > +		return -1;
> > +
> > +	/* A) try to use /sys/block/loopN devices (for losetup -a only)
> > +	 */
> > +	if (ll->flag & LLFLG_SYSFS) {
> > +		int mn;
> > +
> > +		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)
> > +				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 (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++;
> > +			}
> > +		}
> > +		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);
> > +		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++;
> > +		}
> > +	}
> > +done:
> > +	looplist_close(ll);
> > +	return -1;
> > +}
> > +
> >  #ifdef MAIN
> >  
> >  static int
> > -show_loop(char *device) {
> > +show_loop_fd(int fd, char *device) {
> >  	struct loop_info loopinfo;
> >  	struct loop_info64 loopinfo64;
> > -	int fd, errsv;
> > -
> > -	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;
> > -	}
> > +	int errsv;
> >  
> >  	if (ioctl(fd, LOOP_GET_STATUS64, &loopinfo64) == 0) {
> >  
> > @@ -101,7 +339,6 @@ show_loop(char *device) {
> >  			       e, loopinfo64.lo_encrypt_type);
> >  		}
> >  		printf("\n");
> > -		close (fd);
> >  		return 0;
> >  	}
> >  
> > @@ -118,52 +355,55 @@ show_loop(char *device) {
> >  			       loopinfo.lo_encrypt_type);
> >  
> >  		printf("\n");
> > -		close (fd);
> >  		return 0;
> >  	}
> >  
> >  	errsv = errno;
> >  	fprintf(stderr, _("loop: can't get info on device %s: %s\n"),
> >  		device, strerror (errsv));
> > -	close (fd);
> >  	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) {
> > -	char dev[20];
> > -	char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
> > -	int i, j, fd, permission = 0, somedev = 0;
> > -	struct stat statbuf;
> > -	struct loop_info loopinfo;
> > +	struct looplist ll;
> > +	int fd;
> >  
> > -	for (j = 0; j < SIZE(loop_formats); j++) {
> > -	    for(i = 0; i < 256; i++) {
> > -		snprintf(dev, sizeof(dev), loop_formats[j], i);
> > -		if (stat (dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
> > -			fd = open (dev, O_RDONLY);
> > -			if (fd >= 0) {
> > -				if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0)
> > -					show_loop(dev);
> > -				close (fd);
> > -				somedev++;
> > -			} else if (errno == EACCES)
> > -				permission++;
> > -			continue; /* continue trying as long as devices exist */
> > -		}
> > -		break;
> > -	    }
> > +	if (looplist_open(&ll, LLFLG_USEDONLY) == -1) {
> > +		error(_("%s: /dev directory does not exist."), progname);
> > +		return 1;
> > +	}
> > +
> > +	while((fd = looplist_next(&ll)) != -1) {
> > +		show_loop_fd(fd, ll.name);
> > +		close(fd);
> >  	}
> > +	looplist_close(&ll);
> >  
> > -	if (somedev==0 && permission) {
> > +	if (ll.ct_succ && ll.ct_perm) {
> >  		error(_("%s: no permission to look at /dev/loop#"), progname);
> >  		return 1;
> >  	}
> >  	return 0;
> >  }
> >  
> > -
> > -#endif
> > +#endif /* MAIN */
> >  
> >  /* check if the loopfile is already associated with the same given
> >   * parameters.
> > @@ -203,37 +443,32 @@ is_associated(int dev, struct stat *file, unsigned long long offset)
> >   */
> >  char *
> >  loopfile_used (const char *filename, unsigned long long offset) {
> > -	char dev[20];
> > -	char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
> > -	int i, j, fd;
> > -	struct stat devstat, filestat;
> > -	struct loop_info loopinfo;
> > +	struct looplist ll;
> > +	char *devname = NULL;
> > +	struct stat filestat;
> > +	int fd;
> >  
> >  	if (stat(filename, &filestat) == -1) {
> >  		perror(filename);
> >  		return NULL;
> >  	}
> >  
> > -	for (j = 0; j < SIZE(loop_formats); j++) {
> > -	    for(i = 0; i < 256; i++) {
> > -		snprintf(dev, sizeof(dev), loop_formats[j], i);
> > -		if (stat (dev, &devstat) == 0 && S_ISBLK(devstat.st_mode)) {
> > -			fd = open (dev, O_RDONLY);
> > -			if (fd >= 0) {
> > -				int res = 0;
> > +	if (looplist_open(&ll, LLFLG_USEDONLY) == -1) {
> > +		error(_("%s: /dev directory does not exist."), progname);
> > +		return NULL;
> > +	}
> >  
> > -				if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0)
> > -					res = is_associated(fd, &filestat, offset);
> > -				close (fd);
> > -				if (res == 1)
> > -					return xstrdup(dev);
> > -			}
> > -			continue; /* continue trying as long as devices exist */
> > +	while((fd = looplist_next(&ll)) != -1) {
> > +		int res = is_associated(fd, &filestat, offset);
> > +		close(fd);
> > +		if (res == 1) {
> > +			devname = xstrdup(ll.name);
> > +			break;
> >  		}
> > -		break;
> > -	    }
> >  	}
> > -	return NULL;
> > +	looplist_close(&ll);
> > +
> > +	return devname;
> >  }
> >  
> >  int
> > @@ -261,62 +496,36 @@ loopfile_used_with(char *devname, const char *filename, unsigned long long offse
> >  	return ret;
> >  }
> >  
> > -int
> > -is_loop_device (const char *device) {
> > -	struct stat statbuf;
> > -
> > -	return (stat(device, &statbuf) == 0 &&
> > -		S_ISBLK(statbuf.st_mode) &&
> > -		major(statbuf.st_rdev) == LOOPMAJOR);
> > -}
> > -
> >  char *
> >  find_unused_loop_device (void) {
> > -	/* Just creating a device, say in /tmp, is probably a bad idea -
> > -	   people might have problems with backup or so.
> > -	   So, we just try /dev/loop[0-7]. */
> > -	char dev[20];
> > -	char *loop_formats[] = { "/dev/loop%d", "/dev/loop/%d" };
> > -	int i, j, fd, somedev = 0, someloop = 0, permission = 0;
> > -	struct stat statbuf;
> > -	struct loop_info loopinfo;
> > +	struct looplist ll;
> > +	char *devname = NULL;
> > +	int fd;
> >  
> > -	for (j = 0; j < SIZE(loop_formats); j++) {
> > -	    for(i = 0; i < 256; i++) {
> > -		sprintf(dev, loop_formats[j], i);
> > -		if (stat (dev, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) {
> > -			somedev++;
> > -			fd = open (dev, O_RDONLY);
> > -			if (fd >= 0) {
> > -				if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0)
> > -					someloop++;		/* in use */
> > -				else if (errno == ENXIO) {
> > -					close (fd);
> > -					return xstrdup(dev);/* probably free */
> > -				}
> > -				close (fd);
> > -			} else if (errno == EACCES)
> > -				permission++;
> > +	if (looplist_open(&ll, LLFLG_FREEONLY) == -1) {
> > +		error(_("%s: /dev directory does not exist."), progname);
> > +		return NULL;
> > +	}
> >  
> > -			continue;/* continue trying as long as devices exist */
> > -		}
> > -		break;
> > -	    }
> > +	if ((fd = looplist_next(&ll)) != -1) {
> > +		close(fd);
> > +		devname = xstrdup(ll.name);
> >  	}
> > +	looplist_close(&ll);
> > +	if (devname)
> > +		return devname;
> >  
> > -	if (!somedev)
> > -		error(_("%s: could not find any device /dev/loop#"), progname);
> > -	else if (!someloop && permission)
> > +	if (ll.ct_succ && ll.ct_perm)
> >  		error(_("%s: no permission to look at /dev/loop#"), progname);
> > -	else if (!someloop)
> > +	else if (ll.ct_succ)
> > +		error(_("%s: could not find any free loop device"), progname);
> > +	else
> >  		error(_(
> >  		    "%s: Could not find any loop device. Maybe this kernel "
> >  		    "does not know\n"
> >  		    "       about the loop device? (If so, recompile or "
> >  		    "`modprobe loop'.)"), progname);
> > -	else
> > -		error(_("%s: could not find any free loop device"), progname);
> > -	return 0;
> > +	return NULL;
> >  }
> >  
> >  /*
> > @@ -529,7 +738,7 @@ mutter(void) {
> >  	fprintf(stderr,
> >  		_("This mount was compiled without loop support. "
> >  		  "Please recompile.\n"));
> > -}  
> > +}
> >  
> >  int
> >  set_loop (const char *device, const char *file, unsigned long long offset,
> > @@ -550,7 +759,7 @@ find_unused_loop_device (void) {
> >  	return 0;
> >  }
> >  
> > -#endif
> > +#endif /* !LOOP_SET_FD */
> >  
> >  #ifdef MAIN
> >  
> > @@ -726,5 +935,5 @@ main(int argc, char **argv) {
> >  		  "Please recompile.\n"));
> >  	return -1;
> >  }
> > -#endif
> > -#endif
> > +#endif /* !LOOP_SET_FD*/
> > +#endif /* MAIN */
> > -- 
> > 1.5.3.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
> > 
> 
> -- 
>  Karel Zak  <kzak@xxxxxxxxxx>
> 


_____________________________________________________________________
Der WEB.DE SmartSurfer hilft bis zu 70% Ihrer Onlinekosten zu sparen!
http://smartsurfer.web.de/?mc=100071&distributionid=000000000066

-
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