You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
5.2 KiB
197 lines
5.2 KiB
/* losetup.c - Loopback setup
|
|
*
|
|
* Copyright 2012 Rob Landley <rob@landley.net>
|
|
*
|
|
* No standard. (Sigh.)
|
|
|
|
USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdcaD[!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 FILE Iterate through all loopback devices associated with FILE
|
|
|
|
existing:
|
|
-c Check capacity (file size changed)
|
|
-d DEV Detach loopback device
|
|
-D Detach all loopback devices
|
|
|
|
new:
|
|
-s Show device name (alias --show)
|
|
-o OFF Start association at offset OFF into FILE
|
|
-r Read only
|
|
-S SIZE Limit SIZE of loopback association (alias --sizelimit)
|
|
*/
|
|
|
|
#define FOR_losetup
|
|
#include "toys.h"
|
|
#include <linux/loop.h>
|
|
|
|
GLOBALS(
|
|
char *j;
|
|
long o, S;
|
|
|
|
int openflags;
|
|
dev_t jdev;
|
|
ino_t jino;
|
|
char *dir;
|
|
)
|
|
|
|
// -f: *device is NULL
|
|
|
|
// Perform requested operation on one device. Returns 1 if handled, 0 if error
|
|
static int loopback_setup(char *device, char *file)
|
|
{
|
|
struct loop_info64 *loop = (void *)(toybuf+32);
|
|
int lfd = -1, ffd = -1;
|
|
int racy = !device;
|
|
|
|
// 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, "%s/loop%d", TT.dir, 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 && (FLAG(a) || FLAG(j))) goto done;
|
|
// ENXIO expected if we're just trying to print the first unused device.
|
|
if (errno == ENXIO && FLAG(f) && !file) {
|
|
puts(device);
|
|
goto done;
|
|
}
|
|
if (errno != ENXIO || !file) {
|
|
perror_msg_raw(device ? device : "-f");
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// Skip -j filtered devices
|
|
if (TT.j && (loop->lo_device != TT.jdev || loop->lo_inode != TT.jino))
|
|
goto done;
|
|
|
|
// Check size of file or delete existing association
|
|
if (FLAG(c) || FLAG(d)) {
|
|
// The constant is LOOP_SET_CAPACITY
|
|
if (ioctl(lfd, FLAG(c) ? 0x4C07 : LOOP_CLR_FD, 0)) {
|
|
perror_msg_raw(device);
|
|
goto done;
|
|
}
|
|
// Associate file with this device?
|
|
} else if (file) {
|
|
char *f_path = xabspath(file, 1);
|
|
|
|
if (!f_path) perror_exit("file"); // already opened, but if deleted since...
|
|
if (ioctl(lfd, LOOP_SET_FD, ffd)) {
|
|
free(f_path);
|
|
if (racy && errno == EBUSY) return 1;
|
|
perror_exit("%s=%s", device, file);
|
|
}
|
|
xstrncpy((char *)loop->lo_file_name, f_path, LO_NAME_SIZE);
|
|
free(f_path);
|
|
loop->lo_offset = TT.o;
|
|
loop->lo_sizelimit = TT.S;
|
|
if (ioctl(lfd, LOOP_SET_STATUS64, loop)) perror_exit("%s=%s", device, file);
|
|
if (FLAG(s)) puts(device);
|
|
}
|
|
else {
|
|
xprintf("%s: [%lld]:%llu (%s)", device, (long long)loop->lo_device,
|
|
(long long)loop->lo_inode, loop->lo_file_name);
|
|
if (loop->lo_offset) xprintf(", offset %llu",
|
|
(unsigned long long)loop->lo_offset);
|
|
if (loop->lo_sizelimit) xprintf(", sizelimit %llu",
|
|
(unsigned long long)loop->lo_sizelimit);
|
|
xputc('\n');
|
|
}
|
|
|
|
done:
|
|
xclose(ffd);
|
|
xclose(lfd);
|
|
return 0;
|
|
}
|
|
|
|
// 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 (!node->parent) 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.dir = CFG_TOYBOX_ON_ANDROID ? "/dev/block" : "/dev";
|
|
TT.openflags = FLAG(r) ? O_RDONLY : O_RDWR;
|
|
|
|
if (TT.j) {
|
|
struct stat st;
|
|
|
|
xstat(TT.j, &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 (FLAG(D)) toys.optflags |= FLAG_a | FLAG_d;
|
|
|
|
if (FLAG(f)) {
|
|
if (toys.optc > 1) perror_exit("max 1 arg");
|
|
while (loopback_setup(NULL, *toys.optargs));
|
|
} else if (FLAG(a) || FLAG(j)) {
|
|
if (toys.optc) error_exit("bad args");
|
|
dirtree_read(TT.dir, dash_a);
|
|
// Do we need one DEVICE argument?
|
|
} else {
|
|
char *file = (FLAG(c) || FLAG(d)) ? NULL : toys.optargs[1];
|
|
|
|
if (!toys.optc || (file && toys.optc != 2))
|
|
help_exit("needs %d arg%s", 1+!!file, file ? "s" : "");
|
|
for (s = toys.optargs; *s; s++) {
|
|
loopback_setup(*s, file);
|
|
if (file) break;
|
|
}
|
|
}
|
|
}
|