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