1 /* losetup.c - Loopback setup 2 * 3 * Copyright 2012 Rob Landley <rob (at) landley.net> 4 * 5 * No standard. (Sigh.) 6 7 USE_LOSETUP(NEWTOY(losetup, ">2S(sizelimit)#s(show)ro#j:fdca[!afj]", TOYFLAG_SBIN)) 8 9 config LOSETUP 10 bool "losetup" 11 default y 12 help 13 usage: losetup [-cdrs] [-o OFFSET] [-S SIZE] {-d DEVICE...|-j FILE|-af|{DEVICE FILE}} 14 15 Associate a loopback device with a file, or show current file (if any) 16 associated with a loop device. 17 18 Instead of a device: 19 -a Iterate through all loopback devices 20 -f Find first unused loop device (may create one) 21 -j Iterate through all loopback devices associated with FILE 22 23 existing: 24 -c Check capacity (file size changed) 25 -d Detach loopback device 26 27 new: 28 -s Show device name (alias --show) 29 -o Start association at OFFSET into FILE 30 -r Read only 31 -S Limit SIZE of loopback association (alias --sizelimit) 32 */ 33 34 #define FOR_losetup 35 #include "toys.h" 36 #include <linux/loop.h> 37 38 GLOBALS( 39 char *j; 40 long o, S; 41 42 int openflags; 43 dev_t jdev; 44 ino_t jino; 45 ) 46 47 // -f: *device is NULL 48 49 // Perform requested operation on one device. Returns 1 if handled, 0 if error 50 static void loopback_setup(char *device, char *file) 51 { 52 struct loop_info64 *loop = (void *)(toybuf+32); 53 int lfd = -1, ffd = ffd; 54 unsigned flags = toys.optflags; 55 56 // Open file (ffd) and loop device (lfd) 57 58 if (file) ffd = xopen(file, TT.openflags); 59 if (!device) { 60 int i, cfd = open("/dev/loop-control", O_RDWR); 61 62 // We assume /dev is devtmpfs so device creation has no lag. Otherwise 63 // just preallocate loop devices and stay within them. 64 65 // mount -o loop depends on found device being at the start of toybuf. 66 if (cfd != -1) { 67 if (0 <= (i = ioctl(cfd, 0x4C82))) { // LOOP_CTL_GET_FREE 68 sprintf(device = toybuf, "/dev/loop%d", i); 69 // Fallback for Android 70 if (access(toybuf, F_OK)) sprintf(toybuf, "/dev/block/loop%d", i); 71 } 72 close(cfd); 73 } 74 } 75 76 if (device) lfd = open(device, TT.openflags); 77 78 // Stat the loop device to see if there's a current association. 79 memset(loop, 0, sizeof(struct loop_info64)); 80 if (-1 == lfd || ioctl(lfd, LOOP_GET_STATUS64, loop)) { 81 if (errno == ENXIO && (flags & (FLAG_a|FLAG_j))) goto done; 82 if (errno != ENXIO || !file) { 83 perror_msg_raw(device ? device : "-f"); 84 goto done; 85 } 86 } 87 88 // Skip -j filtered devices 89 if (TT.j && (loop->lo_device != TT.jdev || loop->lo_inode != TT.jino)) 90 goto done; 91 92 // Check size of file or delete existing association 93 if (flags & (FLAG_c|FLAG_d)) { 94 // The constant is LOOP_SET_CAPACITY 95 if (ioctl(lfd, (flags & FLAG_c) ? 0x4C07 : LOOP_CLR_FD, 0)) { 96 perror_msg_raw(device); 97 goto done; 98 } 99 // Associate file with this device? 100 } else if (file) { 101 char *s = xabspath(file, 1); 102 103 if (!s) perror_exit("file"); // already opened, but if deleted since... 104 if (ioctl(lfd, LOOP_SET_FD, ffd)) perror_exit("%s=%s", device, file); 105 loop->lo_offset = TT.o; 106 loop->lo_sizelimit = TT.S; 107 xstrncpy((char *)loop->lo_file_name, s, LO_NAME_SIZE); 108 s[LO_NAME_SIZE-1] = 0; 109 if (ioctl(lfd, LOOP_SET_STATUS64, loop)) perror_exit("%s=%s", device, file); 110 if (flags & FLAG_s) printf("%s", device); 111 free(s); 112 } else if (flags & FLAG_f) printf("%s", device); 113 else { 114 xprintf("%s: [%04llx]:%llu (%s)", device, (long long)loop->lo_device, 115 (long long)loop->lo_inode, loop->lo_file_name); 116 if (loop->lo_offset) xprintf(", offset %llu", 117 (unsigned long long)loop->lo_offset); 118 if (loop->lo_sizelimit) xprintf(", sizelimit %llu", 119 (unsigned long long)loop->lo_sizelimit); 120 xputc('\n'); 121 } 122 123 done: 124 if (file) close(ffd); 125 if (lfd != -1) close(lfd); 126 } 127 128 // Perform an action on all currently existing loop devices 129 static int dash_a(struct dirtree *node) 130 { 131 char *s = node->name; 132 133 // Initial /dev node needs to recurse down one level, then only loop[0-9]* 134 if (!node->parent) return DIRTREE_RECURSE; 135 if (strncmp(s, "loop", 4) || !isdigit(s[4])) return 0; 136 137 s = dirtree_path(node, 0); 138 loopback_setup(s, 0); 139 free(s); 140 141 return 0; 142 } 143 144 void losetup_main(void) 145 { 146 char **s; 147 148 TT.openflags = (toys.optflags & FLAG_r) ? O_RDONLY : O_RDWR; 149 150 if (TT.j) { 151 struct stat st; 152 153 xstat(TT.j, &st); 154 TT.jdev = st.st_dev; 155 TT.jino = st.st_ino; 156 } 157 158 // With just device, display current association 159 // -a, -f substitute for device 160 // -j substitute for device 161 162 // new association: S size o offset rs - need a file 163 // existing association: cd 164 165 // -f(dc FILE) 166 167 if (toys.optflags & FLAG_f) { 168 if (toys.optc > 1) perror_exit("max 1 arg"); 169 loopback_setup(NULL, *toys.optargs); 170 } else if (toys.optflags & (FLAG_a|FLAG_j)) { 171 if (toys.optc) error_exit("bad args"); 172 dirtree_read("/dev", dash_a); 173 // Do we need one DEVICE argument? 174 } else { 175 char *file = (toys.optflags & (FLAG_d|FLAG_c)) ? NULL : toys.optargs[1]; 176 177 if (!toys.optc || (file && toys.optc != 2)) 178 help_exit("needs %d arg%s", 1+!!file, file ? "s" : ""); 179 for (s = toys.optargs; *s; s++) { 180 loopback_setup(*s, file); 181 if (file) break; 182 } 183 } 184 } 185