diff options
-rw-r--r-- | toys/other/losetup.c | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/toys/other/losetup.c b/toys/other/losetup.c new file mode 100644 index 00000000..aa62222c --- /dev/null +++ b/toys/other/losetup.c @@ -0,0 +1,190 @@ +/* losetup.c - Loopback setup + * + * Copyright 2012 Rob Landley <rob@landley.net> + * + * No standard. (Sigh.) + +USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdca[!afj]", TOYFLAG_SBIN)) + +config LOSETUP + bool "losetup" + default y + help + usage: losetup [-cdrs] [-o OFFSET] [-S SIZE] {-d DEVICE...|-j FILE|-af|{DEVICE FILE}} + + Associate a loopback device with a file, or show current file (if any) + associated with a loop device. + + Instead of a device: + -a Iterate through all loopback devices + -f Find first unused loop device (may create one) + -j Iterate through all loopback devices associated with FILE + + existing: + -c Check capacity (file size changed) + -d Detach loopback device + + new: + -s Show device name (alias --show) + -o Start assocation at OFFSET into FILE + -r Read only + -S Limit SIZE of loopback association (alias --sizelimit) +*/ + +#define FOR_losetup +#include "toys.h" +#include <linux/loop.h> + +GLOBALS( + char *jfile; + long offset; + long size; + + int openflags; + dev_t jdev; + ino_t jino; +) + +/* +todo: basic /dev file association + associate DEV FILE + #-a + cdfjosS + allocate new loop device: + /dev/loop-control + https://lkml.org/lkml/2011/7/26/148 +*/ + +// -f: *device is NULL + +// Perform requested operation on one device. Returns 1 if handled, 0 if error +static void loopback_setup(char *device, char *file) +{ + struct loop_info64 *loop = (void *)(toybuf+32); + int rc = 0, lfd = -1, ffd; + unsigned flags = toys.optflags; + + // Open file (ffd) and loop device (lfd) + + if (file) ffd = xopen(file, TT.openflags); + if (!device) { + int i, cfd = open("/dev/loop-control", O_RDWR); + + // We assume /dev is devtmpfs so device creation has no lag. Otherwise + // just preallocate loop devices and stay within them. + + // mount -o loop depends on found device being at the start of toybuf. + if (cfd != -1) { + if (0 <= (i = ioctl(cfd, LOOP_CTL_GET_FREE))) + sprintf(device = toybuf, "/dev/loop%d", i); + close(cfd); + } + } + + if (device) lfd = open(device, TT.openflags); + + // Stat the loop device to see if there's a current association. + memset(loop, 0, sizeof(struct loop_info64)); + if (-1 == lfd || ioctl(lfd, LOOP_GET_STATUS64, loop)) { + if (errno == ENXIO && (flags & (FLAG_a|FLAG_j))) return; + if (errno != ENXIO || !file) { + perror_msg("%s", device ? device : "-f"); + rc = 1; + goto done; + } + } + + // Skip -j filtered devices + if (TT.jfile && (loop->lo_device != TT.jdev || loop->lo_inode != TT.jino)) + goto done; + + // Check size of file or delete existing association + if (flags & (FLAG_c|FLAG_d)) { + if (ioctl(lfd, (flags & FLAG_c) ? LOOP_SET_CAPACITY : LOOP_CLR_FD, 0)) { + perror_msg("%s", device); + rc = 1; + goto done; + } + // Associate file with this device? + } else if (file) { + char *s = xrealpath(file); + + if (ioctl(lfd, LOOP_SET_FD, ffd)) perror_exit("%s=%s", device, file); + loop->lo_offset = TT.offset; + loop->lo_sizelimit = TT.size; + strncpy((char *)loop->lo_file_name, s, LO_NAME_SIZE); + s[LO_NAME_SIZE-1] = 0; + if (ioctl(lfd, LOOP_SET_STATUS64, loop)) perror_exit("%s=%s", device, file); + if (flags & FLAG_s) printf("%s", device); + free(s); + } else if (flags & FLAG_f) printf("%s", device); + else { + xprintf("%s: [%04llx]:%llu (%s)", device, loop->lo_device, loop->lo_inode, + loop->lo_file_name); + if (loop->lo_offset) xprintf(", offset %llu", loop->lo_offset); + if (loop->lo_sizelimit) xprintf(", sizelimit %llu", loop->lo_sizelimit); + xputc('\n'); + } + +done: + if (file) close(ffd); + if (lfd != -1) close(lfd); + toys.exitval |= rc; +} + +// Perform an action on all currently existing loop devices +static int dash_a(struct dirtree *node) +{ + char *s = node->name; + + // Initial /dev node needs to recurse down one level, then only loop[0-9]* + if (*s == '/') return DIRTREE_RECURSE; + if (strncmp(s, "loop", 4) || !isdigit(s[4])) return 0; + + s = dirtree_path(node, 0); + loopback_setup(s, 0); + free(s); + + return 0; +} + +void losetup_main(void) +{ + char **s; + + TT.openflags = (toys.optflags & FLAG_r) ? O_RDONLY : O_RDWR; + + if (TT.jfile) { + struct stat st; + + xstat(TT.jfile, &st); + TT.jdev = st.st_dev; + TT.jino = st.st_ino; + } + + // With just device, display current association + // -a, -f substitute for device + // -j substitute for device + + // new association: S size o offset rs - need a file + // existing association: cd + + // -f(dc FILE) + + if (toys.optflags & FLAG_f) { + if (toys.optc > 1) perror_exit("max 1 arg"); + loopback_setup(NULL, *toys.optargs); + } else if (toys.optflags & (FLAG_a|FLAG_j)) { + if (toys.optc) error_exit("bad args"); + dirtree_read("/dev", dash_a); + // Do we need one DEVICE argument? + } else { + char *file = (toys.optflags & (FLAG_d|FLAG_c)) ? NULL : toys.optargs[1]; + + if (!toys.optc || (file && toys.optc>1)) { + if (CFG_HELP) toys.exithelp++; + perror_exit("needs 1 arg"); + } + for (s = toys.optargs; *s; s++) loopback_setup(*s, file); + } +} |