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> - 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