comparison toys/other/losetup.c @ 778:14aabcd31fd9

Add losetup. (Who knows, it might even work.)
author Rob Landley <rob@landley.net>
date Sun, 30 Dec 2012 21:35:01 -0600
parents
children 6cc69be43c42
comparison
equal deleted inserted replaced
777:85e5097c49c1 778:14aabcd31fd9
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
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 /*
49 todo: basic /dev file association
50 associate DEV FILE
51 #-a
52 cdfjosS
53 allocate new loop device:
54 /dev/loop-control
55 https://lkml.org/lkml/2011/7/26/148
56 */
57
58 // -f: *device is NULL
59
60 // Perform requested operation on one device. Returns 1 if handled, 0 if error
61 static void loopback_setup(char *device, char *file)
62 {
63 struct loop_info64 *loop = (void *)(toybuf+32);
64 int rc = 0, lfd = -1, ffd;
65 unsigned flags = toys.optflags;
66
67 // Open file (ffd) and loop device (lfd)
68
69 if (file) ffd = xopen(file, TT.openflags);
70 if (!device) {
71 int i, cfd = open("/dev/loop-control", O_RDWR);
72
73 // We assume /dev is devtmpfs so device creation has no lag. Otherwise
74 // just preallocate loop devices and stay within them.
75
76 // mount -o loop depends on found device being at the start of toybuf.
77 if (cfd != -1) {
78 if (0 <= (i = ioctl(cfd, LOOP_CTL_GET_FREE)))
79 sprintf(device = toybuf, "/dev/loop%d", i);
80 close(cfd);
81 }
82 }
83
84 if (device) lfd = open(device, TT.openflags);
85
86 // Stat the loop device to see if there's a current association.
87 memset(loop, 0, sizeof(struct loop_info64));
88 if (-1 == lfd || ioctl(lfd, LOOP_GET_STATUS64, loop)) {
89 if (errno == ENXIO && (flags & (FLAG_a|FLAG_j))) return;
90 if (errno != ENXIO || !file) {
91 perror_msg("%s", device ? device : "-f");
92 rc = 1;
93 goto done;
94 }
95 }
96
97 // Skip -j filtered devices
98 if (TT.jfile && (loop->lo_device != TT.jdev || loop->lo_inode != TT.jino))
99 goto done;
100
101 // Check size of file or delete existing association
102 if (flags & (FLAG_c|FLAG_d)) {
103 if (ioctl(lfd, (flags & FLAG_c) ? LOOP_SET_CAPACITY : LOOP_CLR_FD, 0)) {
104 perror_msg("%s", device);
105 rc = 1;
106 goto done;
107 }
108 // Associate file with this device?
109 } else if (file) {
110 char *s = xrealpath(file);
111
112 if (ioctl(lfd, LOOP_SET_FD, ffd)) perror_exit("%s=%s", device, file);
113 loop->lo_offset = TT.offset;
114 loop->lo_sizelimit = TT.size;
115 strncpy((char *)loop->lo_file_name, s, LO_NAME_SIZE);
116 s[LO_NAME_SIZE-1] = 0;
117 if (ioctl(lfd, LOOP_SET_STATUS64, loop)) perror_exit("%s=%s", device, file);
118 if (flags & FLAG_s) printf("%s", device);
119 free(s);
120 } else if (flags & FLAG_f) printf("%s", device);
121 else {
122 xprintf("%s: [%04llx]:%llu (%s)", device, loop->lo_device, loop->lo_inode,
123 loop->lo_file_name);
124 if (loop->lo_offset) xprintf(", offset %llu", loop->lo_offset);
125 if (loop->lo_sizelimit) xprintf(", sizelimit %llu", loop->lo_sizelimit);
126 xputc('\n');
127 }
128
129 done:
130 if (file) close(ffd);
131 if (lfd != -1) close(lfd);
132 toys.exitval |= rc;
133 }
134
135 // Perform an action on all currently existing loop devices
136 static int dash_a(struct dirtree *node)
137 {
138 char *s = node->name;
139
140 // Initial /dev node needs to recurse down one level, then only loop[0-9]*
141 if (*s == '/') return DIRTREE_RECURSE;
142 if (strncmp(s, "loop", 4) || !isdigit(s[4])) return 0;
143
144 s = dirtree_path(node, 0);
145 loopback_setup(s, 0);
146 free(s);
147
148 return 0;
149 }
150
151 void losetup_main(void)
152 {
153 char **s;
154
155 TT.openflags = (toys.optflags & FLAG_r) ? O_RDONLY : O_RDWR;
156
157 if (TT.jfile) {
158 struct stat st;
159
160 xstat(TT.jfile, &st);
161 TT.jdev = st.st_dev;
162 TT.jino = st.st_ino;
163 }
164
165 // With just device, display current association
166 // -a, -f substitute for device
167 // -j substitute for device
168
169 // new association: S size o offset rs - need a file
170 // existing association: cd
171
172 // -f(dc FILE)
173
174 if (toys.optflags & FLAG_f) {
175 if (toys.optc > 1) perror_exit("max 1 arg");
176 loopback_setup(NULL, *toys.optargs);
177 } else if (toys.optflags & (FLAG_a|FLAG_j)) {
178 if (toys.optc) error_exit("bad args");
179 dirtree_read("/dev", dash_a);
180 // Do we need one DEVICE argument?
181 } else {
182 char *file = (toys.optflags & (FLAG_d|FLAG_c)) ? NULL : toys.optargs[1];
183
184 if (!toys.optc || (file && toys.optc>1)) {
185 if (CFG_HELP) toys.exithelp++;
186 perror_exit("needs 1 arg");
187 }
188 for (s = toys.optargs; *s; s++) loopback_setup(*s, file);
189 }
190 }