10f66f451Sopenharmony_ci/* losetup.c - Loopback setup 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2012 Rob Landley <rob@landley.net> 40f66f451Sopenharmony_ci * 50f66f451Sopenharmony_ci * No standard. (Sigh.) 60f66f451Sopenharmony_ci 70f66f451Sopenharmony_ciUSE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdcaD[!afj]", TOYFLAG_SBIN)) 80f66f451Sopenharmony_ci 90f66f451Sopenharmony_ciconfig LOSETUP 100f66f451Sopenharmony_ci bool "losetup" 110f66f451Sopenharmony_ci default y 120f66f451Sopenharmony_ci help 130f66f451Sopenharmony_ci usage: losetup [-cdrs] [-o OFFSET] [-S SIZE] {-d DEVICE...|-j FILE|-af|{DEVICE FILE}} 140f66f451Sopenharmony_ci 150f66f451Sopenharmony_ci Associate a loopback device with a file, or show current file (if any) 160f66f451Sopenharmony_ci associated with a loop device. 170f66f451Sopenharmony_ci 180f66f451Sopenharmony_ci Instead of a device: 190f66f451Sopenharmony_ci -a Iterate through all loopback devices 200f66f451Sopenharmony_ci -f Find first unused loop device (may create one) 210f66f451Sopenharmony_ci -j FILE Iterate through all loopback devices associated with FILE 220f66f451Sopenharmony_ci 230f66f451Sopenharmony_ci existing: 240f66f451Sopenharmony_ci -c Check capacity (file size changed) 250f66f451Sopenharmony_ci -d DEV Detach loopback device 260f66f451Sopenharmony_ci -D Detach all loopback devices 270f66f451Sopenharmony_ci 280f66f451Sopenharmony_ci new: 290f66f451Sopenharmony_ci -s Show device name (alias --show) 300f66f451Sopenharmony_ci -o OFF Start association at offset OFF into FILE 310f66f451Sopenharmony_ci -r Read only 320f66f451Sopenharmony_ci -S SIZE Limit SIZE of loopback association (alias --sizelimit) 330f66f451Sopenharmony_ci*/ 340f66f451Sopenharmony_ci 350f66f451Sopenharmony_ci#define FOR_losetup 360f66f451Sopenharmony_ci#include "toys.h" 370f66f451Sopenharmony_ci#include <linux/loop.h> 380f66f451Sopenharmony_ci 390f66f451Sopenharmony_ciGLOBALS( 400f66f451Sopenharmony_ci char *j; 410f66f451Sopenharmony_ci long o, S; 420f66f451Sopenharmony_ci 430f66f451Sopenharmony_ci int openflags; 440f66f451Sopenharmony_ci dev_t jdev; 450f66f451Sopenharmony_ci ino_t jino; 460f66f451Sopenharmony_ci char *dir; 470f66f451Sopenharmony_ci) 480f66f451Sopenharmony_ci 490f66f451Sopenharmony_ci// -f: *device is NULL 500f66f451Sopenharmony_ci 510f66f451Sopenharmony_ci// Perform requested operation on one device. Returns 1 if handled, 0 if error 520f66f451Sopenharmony_cistatic int loopback_setup(char *device, char *file) 530f66f451Sopenharmony_ci{ 540f66f451Sopenharmony_ci struct loop_info64 *loop = (void *)(toybuf+32); 550f66f451Sopenharmony_ci int lfd = -1, ffd = -1; 560f66f451Sopenharmony_ci int racy = !device; 570f66f451Sopenharmony_ci 580f66f451Sopenharmony_ci // Open file (ffd) and loop device (lfd) 590f66f451Sopenharmony_ci 600f66f451Sopenharmony_ci if (file) ffd = xopen(file, TT.openflags); 610f66f451Sopenharmony_ci if (!device) { 620f66f451Sopenharmony_ci int i, cfd = open("/dev/loop-control", O_RDWR); 630f66f451Sopenharmony_ci 640f66f451Sopenharmony_ci // We assume /dev is devtmpfs so device creation has no lag. Otherwise 650f66f451Sopenharmony_ci // just preallocate loop devices and stay within them. 660f66f451Sopenharmony_ci 670f66f451Sopenharmony_ci // mount -o loop depends on found device being at the start of toybuf. 680f66f451Sopenharmony_ci if (cfd != -1) { 690f66f451Sopenharmony_ci if (0 <= (i = ioctl(cfd, LOOP_CTL_GET_FREE))) { 700f66f451Sopenharmony_ci sprintf(device = toybuf, "%s/loop%d", TT.dir, i); 710f66f451Sopenharmony_ci } 720f66f451Sopenharmony_ci close(cfd); 730f66f451Sopenharmony_ci } 740f66f451Sopenharmony_ci } 750f66f451Sopenharmony_ci 760f66f451Sopenharmony_ci if (device) lfd = open(device, TT.openflags); 770f66f451Sopenharmony_ci 780f66f451Sopenharmony_ci // Stat the loop device to see if there's a current association. 790f66f451Sopenharmony_ci memset(loop, 0, sizeof(struct loop_info64)); 800f66f451Sopenharmony_ci if (-1 == lfd || ioctl(lfd, LOOP_GET_STATUS64, loop)) { 810f66f451Sopenharmony_ci if (errno == ENXIO && (FLAG(a) || FLAG(j))) goto done; 820f66f451Sopenharmony_ci // ENXIO expected if we're just trying to print the first unused device. 830f66f451Sopenharmony_ci if (errno == ENXIO && FLAG(f) && !file) { 840f66f451Sopenharmony_ci puts(device); 850f66f451Sopenharmony_ci goto done; 860f66f451Sopenharmony_ci } 870f66f451Sopenharmony_ci if (errno != ENXIO || !file) { 880f66f451Sopenharmony_ci perror_msg_raw(device ? device : "-f"); 890f66f451Sopenharmony_ci goto done; 900f66f451Sopenharmony_ci } 910f66f451Sopenharmony_ci } 920f66f451Sopenharmony_ci 930f66f451Sopenharmony_ci // Skip -j filtered devices 940f66f451Sopenharmony_ci if (TT.j && (loop->lo_device != TT.jdev || loop->lo_inode != TT.jino)) 950f66f451Sopenharmony_ci goto done; 960f66f451Sopenharmony_ci 970f66f451Sopenharmony_ci // Check size of file or delete existing association 980f66f451Sopenharmony_ci if (FLAG(c) || FLAG(d)) { 990f66f451Sopenharmony_ci // The constant is LOOP_SET_CAPACITY 1000f66f451Sopenharmony_ci if (ioctl(lfd, FLAG(c) ? 0x4C07 : LOOP_CLR_FD, 0)) { 1010f66f451Sopenharmony_ci perror_msg_raw(device); 1020f66f451Sopenharmony_ci goto done; 1030f66f451Sopenharmony_ci } 1040f66f451Sopenharmony_ci // Associate file with this device? 1050f66f451Sopenharmony_ci } else if (file) { 1060f66f451Sopenharmony_ci char *f_path = xabspath(file, ABS_PATH); 1070f66f451Sopenharmony_ci 1080f66f451Sopenharmony_ci if (!f_path) perror_exit("%s", file); // already opened but if deleted since 1090f66f451Sopenharmony_ci if (ioctl(lfd, LOOP_SET_FD, ffd)) { 1100f66f451Sopenharmony_ci free(f_path); 1110f66f451Sopenharmony_ci if (racy && errno == EBUSY) return 1; 1120f66f451Sopenharmony_ci perror_exit("%s=%s", device, file); 1130f66f451Sopenharmony_ci } 1140f66f451Sopenharmony_ci xstrncpy((char *)loop->lo_file_name, f_path, LO_NAME_SIZE); 1150f66f451Sopenharmony_ci free(f_path); 1160f66f451Sopenharmony_ci loop->lo_offset = TT.o; 1170f66f451Sopenharmony_ci loop->lo_sizelimit = TT.S; 1180f66f451Sopenharmony_ci if (ioctl(lfd, LOOP_SET_STATUS64, loop)) perror_exit("%s=%s", device, file); 1190f66f451Sopenharmony_ci if (FLAG(s)) puts(device); 1200f66f451Sopenharmony_ci } 1210f66f451Sopenharmony_ci else { 1220f66f451Sopenharmony_ci xprintf("%s: [%lld]:%llu (%s)", device, (long long)loop->lo_device, 1230f66f451Sopenharmony_ci (long long)loop->lo_inode, loop->lo_file_name); 1240f66f451Sopenharmony_ci if (loop->lo_offset) xprintf(", offset %llu", 1250f66f451Sopenharmony_ci (unsigned long long)loop->lo_offset); 1260f66f451Sopenharmony_ci if (loop->lo_sizelimit) xprintf(", sizelimit %llu", 1270f66f451Sopenharmony_ci (unsigned long long)loop->lo_sizelimit); 1280f66f451Sopenharmony_ci xputc('\n'); 1290f66f451Sopenharmony_ci } 1300f66f451Sopenharmony_ci 1310f66f451Sopenharmony_cidone: 1320f66f451Sopenharmony_ci xclose(ffd); 1330f66f451Sopenharmony_ci xclose(lfd); 1340f66f451Sopenharmony_ci return 0; 1350f66f451Sopenharmony_ci} 1360f66f451Sopenharmony_ci 1370f66f451Sopenharmony_ci// Perform an action on all currently existing loop devices 1380f66f451Sopenharmony_cistatic int dash_a(struct dirtree *node) 1390f66f451Sopenharmony_ci{ 1400f66f451Sopenharmony_ci char *s = node->name; 1410f66f451Sopenharmony_ci 1420f66f451Sopenharmony_ci // Initial /dev node needs to recurse down one level, then only loop[0-9]* 1430f66f451Sopenharmony_ci if (!node->parent) return DIRTREE_RECURSE; 1440f66f451Sopenharmony_ci if (strncmp(s, "loop", 4) || !isdigit(s[4])) return 0; 1450f66f451Sopenharmony_ci 1460f66f451Sopenharmony_ci s = dirtree_path(node, 0); 1470f66f451Sopenharmony_ci loopback_setup(s, 0); 1480f66f451Sopenharmony_ci free(s); 1490f66f451Sopenharmony_ci 1500f66f451Sopenharmony_ci return 0; 1510f66f451Sopenharmony_ci} 1520f66f451Sopenharmony_ci 1530f66f451Sopenharmony_civoid losetup_main(void) 1540f66f451Sopenharmony_ci{ 1550f66f451Sopenharmony_ci char **s; 1560f66f451Sopenharmony_ci 1570f66f451Sopenharmony_ci TT.dir = CFG_TOYBOX_ON_ANDROID ? "/dev/block" : "/dev"; 1580f66f451Sopenharmony_ci TT.openflags = FLAG(r) ? O_RDONLY : O_RDWR; 1590f66f451Sopenharmony_ci 1600f66f451Sopenharmony_ci if (TT.j) { 1610f66f451Sopenharmony_ci struct stat st; 1620f66f451Sopenharmony_ci 1630f66f451Sopenharmony_ci xstat(TT.j, &st); 1640f66f451Sopenharmony_ci TT.jdev = st.st_dev; 1650f66f451Sopenharmony_ci TT.jino = st.st_ino; 1660f66f451Sopenharmony_ci } 1670f66f451Sopenharmony_ci 1680f66f451Sopenharmony_ci // With just device, display current association 1690f66f451Sopenharmony_ci // -a, -f substitute for device 1700f66f451Sopenharmony_ci // -j substitute for device 1710f66f451Sopenharmony_ci 1720f66f451Sopenharmony_ci // new association: S size o offset rs - need a file 1730f66f451Sopenharmony_ci // existing association: cd 1740f66f451Sopenharmony_ci 1750f66f451Sopenharmony_ci // -f(dc FILE) 1760f66f451Sopenharmony_ci 1770f66f451Sopenharmony_ci if (FLAG(D)) toys.optflags |= FLAG_a | FLAG_d; 1780f66f451Sopenharmony_ci 1790f66f451Sopenharmony_ci if (FLAG(f)) { 1800f66f451Sopenharmony_ci if (toys.optc > 1) perror_exit("max 1 arg"); 1810f66f451Sopenharmony_ci while (loopback_setup(NULL, *toys.optargs)); 1820f66f451Sopenharmony_ci } else if (FLAG(a) || FLAG(j)) { 1830f66f451Sopenharmony_ci if (toys.optc) error_exit("bad args"); 1840f66f451Sopenharmony_ci dirtree_read(TT.dir, dash_a); 1850f66f451Sopenharmony_ci // Do we need one DEVICE argument? 1860f66f451Sopenharmony_ci } else { 1870f66f451Sopenharmony_ci char *file = (FLAG(c) || FLAG(d)) ? NULL : toys.optargs[1]; 1880f66f451Sopenharmony_ci 1890f66f451Sopenharmony_ci if (!toys.optc || (file && toys.optc != 2)) 1900f66f451Sopenharmony_ci help_exit("needs %d arg%s", 1+!!file, file ? "s" : ""); 1910f66f451Sopenharmony_ci for (s = toys.optargs; *s; s++) { 1920f66f451Sopenharmony_ci loopback_setup(*s, file); 1930f66f451Sopenharmony_ci if (file) break; 1940f66f451Sopenharmony_ci } 1950f66f451Sopenharmony_ci } 1960f66f451Sopenharmony_ci} 197