/* Taken from Ted's losetup.c - Mitch */ /* Added vfs mount options - aeb - 960223 */ /* Removed lomount - aeb - 960224 */ /* 1999-02-22 Arkadiusz Miƛkiewicz * - added Native Language Support * Sun Mar 21 1999 - Arnaldo Carvalho de Melo * - fixed strerr(errno) in gettext calls */ #define PROC_DEVICES "/proc/devices" /* * losetup.c - setup and control loop devices */ #include "kpartx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lopart.h" #include "xstrncpy.h" #ifndef LOOP_CTL_GET_FREE #define LOOP_CTL_GET_FREE 0x4C82 #endif static char * xstrdup (const char *s) { char *t; if (s == NULL) return NULL; t = strdup (s); if (t == NULL) { fprintf(stderr, "not enough memory"); exit(1); } return t; } #define SIZE(a) (sizeof(a)/sizeof(a[0])) char *find_loop_by_file(const char *filename) { DIR *dir; struct dirent *dent; char dev[64], *found = NULL, *p; int fd, bytes_read; struct stat statbuf; struct loop_info loopinfo; const char VIRT_BLOCK[] = "/sys/devices/virtual/block"; char path[PATH_MAX]; char bf_path[PATH_MAX]; char backing_file[PATH_MAX]; dir = opendir(VIRT_BLOCK); if (!dir) return NULL; while ((dent = readdir(dir)) != NULL) { if (strncmp(dent->d_name,"loop",4)) continue; if (snprintf(path, PATH_MAX, "%s/%s/dev", VIRT_BLOCK, dent->d_name) >= PATH_MAX) continue; fd = open(path, O_RDONLY); if (fd < 0) continue; bytes_read = read(fd, dev, sizeof(dev) - 1); if (bytes_read <= 0) { close(fd); continue; } close(fd); dev[bytes_read] = '\0'; p = strchr(dev, '\n'); if (p != NULL) *p = '\0'; if (snprintf(path, PATH_MAX, "/dev/block/%s", dev) >= PATH_MAX) continue; fd = open (path, O_RDONLY); if (fd < 0) continue; if (fstat (fd, &statbuf) != 0 || !S_ISBLK(statbuf.st_mode)) { close (fd); continue; } if (ioctl (fd, LOOP_GET_STATUS, &loopinfo) != 0) { close (fd); continue; } close (fd); if (0 == strcmp(filename, loopinfo.lo_name)) { found = realpath(path, NULL); break; } /* * filename is a realpath, while loopinfo.lo_name may hold just the * basename. If that's the case, try to match filename against the * backing_file entry for this loop entry */ if (snprintf(bf_path, PATH_MAX, "%s/%s/loop/backing_file", VIRT_BLOCK, dent->d_name) >= PATH_MAX) continue; fd = open(bf_path, O_RDONLY); if (fd < 0) continue; bytes_read = read(fd, backing_file, sizeof(backing_file) - 1); if (bytes_read <= 0) { close(fd); continue; } close(fd); backing_file[bytes_read-1] = '\0'; if (0 == strcmp(filename, backing_file)) { found = realpath(path, NULL); break; } } closedir(dir); return found; } char *find_unused_loop_device(void) { char dev[20], *next_loop_dev = NULL; int fd, next_loop = 0, somedev = 0, someloop = 0, loop_known = 0; struct stat statbuf; struct loop_info loopinfo; FILE *procdev; while (next_loop_dev == NULL) { if (stat("/dev/loop-control", &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) { int next_loop_fd; next_loop_fd = open("/dev/loop-control", O_RDWR); if (next_loop_fd < 0) return NULL; next_loop = ioctl(next_loop_fd, LOOP_CTL_GET_FREE); close(next_loop_fd); if (next_loop < 0) return NULL; } sprintf(dev, "/dev/loop%d", next_loop); fd = open (dev, O_RDONLY); if (fd >= 0) { if (fstat (fd, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) { somedev++; if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0) someloop++; /* in use */ else if (errno == ENXIO) next_loop_dev = xstrdup(dev); } close (fd); /* continue trying as long as devices exist */ continue; } break; } if (next_loop_dev) return next_loop_dev; /* Nothing found. Why not? */ if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) { char line[100]; while (fgets (line, sizeof(line), procdev)) if (strstr (line, " loop\n")) { loop_known = 1; break; } fclose(procdev); if (!loop_known) loop_known = -1; } if (!somedev) fprintf(stderr, "mount: could not find any device /dev/loop#"); else if (!someloop) { if (loop_known == 1) fprintf(stderr, "mount: Could not find any loop device.\n" " Maybe /dev/loop# has a wrong major number?"); else if (loop_known == -1) fprintf(stderr, "mount: Could not find any loop device, and, according to %s,\n" " this kernel does not know about the loop device.\n" " (If so, then recompile or `modprobe loop'.)", PROC_DEVICES); else fprintf(stderr, "mount: Could not find any loop device. Maybe this kernel does not know\n" " about the loop device (then recompile or `modprobe loop'), or\n" " maybe /dev/loop# has the wrong major number?"); } else fprintf(stderr, "mount: could not find any free loop device"); return NULL; } int set_loop(const char *device, const char *file, int offset, int *loopro) { struct loop_info loopinfo; int fd, ffd, mode; mode = (*loopro ? O_RDONLY : O_RDWR); if ((ffd = open (file, mode)) < 0) { if (!*loopro && (errno == EROFS || errno == EACCES)) ffd = open (file, mode = O_RDONLY); if (ffd < 0) { perror (file); return 1; } } if ((fd = open (device, mode)) < 0) { close(ffd); perror (device); return 1; } *loopro = (mode == O_RDONLY); memset (&loopinfo, 0, sizeof (loopinfo)); xstrncpy (loopinfo.lo_name, file, LO_NAME_SIZE); loopinfo.lo_offset = offset; loopinfo.lo_encrypt_type = LO_CRYPT_NONE; loopinfo.lo_encrypt_key_size = 0; if (ioctl(fd, LOOP_SET_FD, (void*)(uintptr_t)(ffd)) < 0) { perror ("ioctl: LOOP_SET_FD"); close (fd); close (ffd); return 1; } if (ioctl (fd, LOOP_SET_STATUS, &loopinfo) < 0) { (void) ioctl (fd, LOOP_CLR_FD, 0); perror ("ioctl: LOOP_SET_STATUS"); close (fd); close (ffd); return 1; } close (fd); close (ffd); return 0; } int del_loop(const char *device) { int retries = 5; int fd; if ((fd = open (device, O_RDONLY)) < 0) { int errsv = errno; fprintf(stderr, "loop: can't delete device %s: %s\n", device, strerror (errsv)); return 1; } while (ioctl (fd, LOOP_CLR_FD, 0) < 0) { if (errno != EBUSY || retries-- <= 0) { perror ("ioctl: LOOP_CLR_FD"); close (fd); return 1; } fprintf(stderr, "loop: device %s still in use, retrying delete\n", device); sleep(1); continue; } close (fd); return 0; }