]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/losetup.c
Merge branch 'su-v1' of https://github.com/lnussel/util-linux
[thirdparty/util-linux.git] / sys-utils / losetup.c
1 /*
2 * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
3 * Originally from Ted's losetup.c
4 *
5 * losetup.c - setup and control loop devices
6 */
7 #include <stdio.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <sys/ioctl.h>
13 #include <sys/stat.h>
14 #include <inttypes.h>
15 #include <getopt.h>
16
17 #include "c.h"
18 #include "nls.h"
19 #include "strutils.h"
20 #include "loopdev.h"
21 #include "xgetpass.h"
22 #include "closestream.h"
23
24 enum {
25 A_CREATE = 1, /* setup a new device */
26 A_DELETE, /* delete given device(s) */
27 A_DELETE_ALL, /* delete all devices */
28 A_SHOW, /* list devices */
29 A_SHOW_ONE, /* print info about one device */
30 A_FIND_FREE, /* find first unused */
31 A_SET_CAPACITY, /* set device capacity */
32 };
33
34 static int verbose;
35
36
37 static int printf_loopdev(struct loopdev_cxt *lc)
38 {
39 uint64_t x;
40 dev_t dev = 0;
41 ino_t ino = 0;
42 char *fname = NULL;
43 uint32_t type;
44
45 fname = loopcxt_get_backing_file(lc);
46 if (!fname)
47 return -EINVAL;
48
49 if (loopcxt_get_backing_devno(lc, &dev) == 0)
50 loopcxt_get_backing_inode(lc, &ino);
51
52 if (!dev && !ino) {
53 /*
54 * Probably non-root user (no permissions to
55 * call LOOP_GET_STATUS ioctls).
56 */
57 printf("%s: []: (%s)",
58 loopcxt_get_device(lc), fname);
59
60 if (loopcxt_get_offset(lc, &x) == 0 && x)
61 printf(_(", offset %ju"), x);
62
63 if (loopcxt_get_sizelimit(lc, &x) == 0 && x)
64 printf(_(", sizelimit %ju"), x);
65 printf("\n");
66 return 0;
67 }
68
69 printf("%s: [%04d]:%" PRIu64 " (%s)",
70 loopcxt_get_device(lc), (int) dev, ino, fname);
71
72 if (loopcxt_get_offset(lc, &x) == 0 && x)
73 printf(_(", offset %ju"), x);
74
75 if (loopcxt_get_sizelimit(lc, &x) == 0 && x)
76 printf(_(", sizelimit %ju"), x);
77
78 if (loopcxt_get_encrypt_type(lc, &type) == 0) {
79 const char *e = loopcxt_get_crypt_name(lc);
80
81 if ((!e || !*e) && type == 1)
82 e = "XOR";
83 if (e && *e)
84 printf(_(", encryption %s (type %u)"), e, type);
85 }
86 printf("\n");
87 return 0;
88 }
89
90 static int show_all_loops(struct loopdev_cxt *lc, const char *file,
91 uint64_t offset, int flags)
92 {
93 struct stat sbuf, *st = &sbuf;
94
95 if (loopcxt_init_iterator(lc, LOOPITER_FL_USED))
96 return -1;
97
98 if (!file || stat(file, st))
99 st = NULL;
100
101 while (loopcxt_next(lc) == 0) {
102
103 if (file && !loopcxt_is_used(lc, st, file, offset, flags))
104 continue;
105
106 printf_loopdev(lc);
107 }
108 loopcxt_deinit_iterator(lc);
109 return 0;
110 }
111
112 static int set_capacity(struct loopdev_cxt *lc)
113 {
114 int fd = loopcxt_get_fd(lc);
115
116 if (fd < 0)
117 warn(_("%s: open failed"), loopcxt_get_device(lc));
118 else if (ioctl(fd, LOOP_SET_CAPACITY) != 0)
119 warnx(_("%s: set capacity failed"), loopcxt_get_device(lc));
120 else
121 return 0;
122
123 return -1;
124 }
125
126 static int delete_loop(struct loopdev_cxt *lc)
127 {
128 if (loopcxt_delete_device(lc))
129 warn(_("%s: detach failed"), loopcxt_get_device(lc));
130 else
131 return 0;
132
133 return -1;
134 }
135
136 static int delete_all_loops(struct loopdev_cxt *lc)
137 {
138 int res = 0;
139
140 if (loopcxt_init_iterator(lc, LOOPITER_FL_USED))
141 return -1;
142
143 while (loopcxt_next(lc) == 0)
144 res += delete_loop(lc);
145
146 loopcxt_deinit_iterator(lc);
147 return res;
148 }
149
150 static void usage(FILE *out)
151 {
152 fputs(USAGE_HEADER, out);
153
154 fprintf(out,
155 _(" %1$s [options] [<loopdev>]\n"
156 " %1$s [options] -f | <loopdev> <file>\n"),
157 program_invocation_short_name);
158
159 fputs(USAGE_OPTIONS, out);
160 fputs(_(" -a, --all list all used devices\n"
161 " -d, --detach <loopdev> [...] detach one or more devices\n"
162 " -D, --detach-all detach all used devices\n"
163 " -f, --find find first unused device\n"
164 " -c, --set-capacity <loopdev> resize device\n"
165 " -j, --associated <file> list all devices associated with <file>\n"), out);
166 fputs(USAGE_SEPARATOR, out);
167
168 fputs(_(" -e, --encryption <type> enable encryption with specified <name/num>\n"
169 " -o, --offset <num> start at offset <num> into file\n"
170 " --sizelimit <num> device limited to <num> bytes of the file\n"
171 " -p, --pass-fd <num> read passphrase from file descriptor <num>\n"
172 " -P, --partscan create partitioned loop device\n"
173 " -r, --read-only setup read-only loop device\n"
174 " --show print device name after setup (with -f)\n"
175 " -v, --verbose verbose mode\n"), out);
176
177 fputs(USAGE_SEPARATOR, out);
178 fputs(USAGE_HELP, out);
179 fputs(USAGE_VERSION, out);
180
181 fprintf(out, USAGE_MAN_TAIL("losetup(8)"));
182
183 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
184 }
185
186 int main(int argc, char **argv)
187 {
188 struct loopdev_cxt lc;
189 int act = 0, flags = 0, passfd = -1, c;
190 char *file = NULL, *encryption = NULL;
191 uint64_t offset = 0, sizelimit = 0;
192 int res = 0, showdev = 0, lo_flags = 0;
193
194 enum {
195 OPT_SIZELIMIT = CHAR_MAX + 1,
196 OPT_SHOW
197 };
198 static const struct option longopts[] = {
199 { "all", 0, 0, 'a' },
200 { "set-capacity", 1, 0, 'c' },
201 { "detach", 1, 0, 'd' },
202 { "detach-all", 0, 0, 'D' },
203 { "encryption", 1, 0, 'e' },
204 { "find", 0, 0, 'f' },
205 { "help", 0, 0, 'h' },
206 { "associated", 1, 0, 'j' },
207 { "offset", 1, 0, 'o' },
208 { "sizelimit", 1, 0, OPT_SIZELIMIT },
209 { "pass-fd", 1, 0, 'p' },
210 { "partscan", 0, 0, 'P' },
211 { "read-only", 0, 0, 'r' },
212 { "show", 0, 0, OPT_SHOW },
213 { "verbose", 0, 0, 'v' },
214 { "version", 0, 0, 'V' },
215 { NULL, 0, 0, 0 }
216 };
217
218 setlocale(LC_ALL, "");
219 bindtextdomain(PACKAGE, LOCALEDIR);
220 textdomain(PACKAGE);
221 atexit(close_stdout);
222
223 loopcxt_init(&lc, 0);
224 loopcxt_enable_debug(&lc, getenv("LOOPDEV_DEBUG") ? TRUE : FALSE);
225
226 while ((c = getopt_long(argc, argv, "ac:d:De:E:fhj:o:p:PrvV",
227 longopts, NULL)) != -1) {
228
229 if (act && strchr("acdDfj", c))
230 errx(EXIT_FAILURE,
231 _("the options %s are mutually exclusive"),
232 "--{all,associated,set-capacity,detach,detach-all,find}");
233
234 switch (c) {
235 case 'a':
236 act = A_SHOW;
237 break;
238 case 'c':
239 act = A_SET_CAPACITY;
240 loopcxt_set_device(&lc, optarg);
241 break;
242 case 'r':
243 lo_flags |= LO_FLAGS_READ_ONLY;
244 break;
245 case 'd':
246 act = A_DELETE;
247 loopcxt_set_device(&lc, optarg);
248 break;
249 case 'D':
250 act = A_DELETE_ALL;
251 break;
252 case 'E':
253 case 'e':
254 encryption = optarg;
255 break;
256 case 'f':
257 act = A_FIND_FREE;
258 break;
259 case 'h':
260 usage(stdout);
261 break;
262 case 'j':
263 act = A_SHOW;
264 file = optarg;
265 break;
266 case 'o':
267 offset = strtosize_or_err(optarg, _("failed to parse offset"));
268 flags |= LOOPDEV_FL_OFFSET;
269 break;
270 case 'p':
271 passfd = strtou32_or_err(optarg,
272 _("invalid passphrase file descriptor"));
273 break;
274 case 'P':
275 lo_flags |= LO_FLAGS_PARTSCAN;
276 break;
277 case OPT_SHOW:
278 showdev = 1;
279 break;
280 case 'v':
281 verbose = 1;
282 break;
283 case 'V':
284 printf(UTIL_LINUX_VERSION);
285 return EXIT_SUCCESS;
286 case OPT_SIZELIMIT: /* --sizelimit */
287 sizelimit = strtosize_or_err(optarg, _("failed to parse size"));
288 flags |= LOOPDEV_FL_SIZELIMIT;
289 break;
290 default:
291 usage(stderr);
292 }
293 }
294
295 if (argc == 1)
296 usage(stderr);
297
298 if (act == A_FIND_FREE && optind < argc) {
299 /*
300 * losetup -f <backing_file>
301 */
302 act = A_CREATE;
303 file = argv[optind++];
304 }
305 if (!act && optind + 1 == argc) {
306 /*
307 * losetup <device>
308 */
309 act = A_SHOW_ONE;
310 loopcxt_set_device(&lc, argv[optind++]);
311 }
312 if (!act) {
313 /*
314 * losetup <loopdev> <backing_file>
315 */
316 act = A_CREATE;
317
318 if (optind >= argc)
319 errx(EXIT_FAILURE, _("no loop device specified"));
320 loopcxt_set_device(&lc, argv[optind++]);
321
322 if (optind >= argc)
323 errx(EXIT_FAILURE, _("no file specified"));
324 file = argv[optind++];
325 }
326
327 if (act != A_CREATE &&
328 (encryption || sizelimit || passfd != -1 || lo_flags || showdev))
329 errx(EXIT_FAILURE,
330 _("the options %s are allowed to loop device setup only"),
331 "--{encryption,sizelimit,pass-fd,read-only,show}");
332
333 if ((flags & LOOPDEV_FL_OFFSET) &&
334 act != A_CREATE && (act != A_SHOW || !file))
335 errx(EXIT_FAILURE, _("the option --offset is not allowed in this context."));
336
337 switch (act) {
338 case A_CREATE:
339 {
340 char *pass = NULL;
341 int hasdev = loopcxt_has_device(&lc);
342
343 if (encryption) {
344 #ifdef MCL_FUTURE
345 if(mlockall(MCL_CURRENT | MCL_FUTURE))
346 err(EXIT_FAILURE, _("couldn't lock into memory"));
347 #endif
348 pass = xgetpass(passfd, _("Password: "));
349 }
350 do {
351 /* Note that loopcxt_{find_unused,set_device}() resets
352 * loopcxt struct.
353 */
354 if (!hasdev && (res = loopcxt_find_unused(&lc))) {
355 warnx(_("not found unused device"));
356 break;
357 }
358 if (encryption && pass)
359 loopcxt_set_encryption(&lc, encryption, pass);
360 if (flags & LOOPDEV_FL_OFFSET)
361 loopcxt_set_offset(&lc, offset);
362 if (flags & LOOPDEV_FL_SIZELIMIT)
363 loopcxt_set_sizelimit(&lc, sizelimit);
364 if (lo_flags)
365 loopcxt_set_flags(&lc, lo_flags);
366 if ((res = loopcxt_set_backing_file(&lc, file))) {
367 warn(_("%s: failed to use backing file"), file);
368 break;
369 }
370 errno = 0;
371 res = loopcxt_setup_device(&lc);
372 if (res == 0)
373 break; /* success */
374 if (errno != EBUSY) {
375 warn(_("%s: failed to setup loop device"),
376 hasdev && loopcxt_get_fd(&lc) < 0 ?
377 loopcxt_get_device(&lc) : file);
378 break;
379 }
380 } while (hasdev == 0);
381
382 free(pass);
383
384 if (showdev && res == 0)
385 printf("%s\n", loopcxt_get_device(&lc));
386 break;
387 }
388 case A_DELETE:
389 res = delete_loop(&lc);
390 while (optind < argc) {
391 loopcxt_set_device(&lc, argv[optind++]);
392 res += delete_loop(&lc);
393 }
394 break;
395 case A_DELETE_ALL:
396 res = delete_all_loops(&lc);
397 break;
398 case A_FIND_FREE:
399 if (loopcxt_find_unused(&lc))
400 warn(_("find unused loop device failed"));
401 else
402 printf("%s\n", loopcxt_get_device(&lc));
403 break;
404 case A_SHOW:
405 res = show_all_loops(&lc, file, offset, flags);
406 break;
407 case A_SHOW_ONE:
408 res = printf_loopdev(&lc);
409 if (res)
410 warn(_("%s"), loopcxt_get_device(&lc));
411 break;
412 case A_SET_CAPACITY:
413 res = set_capacity(&lc);
414 break;
415 default:
416 usage(stderr);
417 break;
418 }
419
420 loopcxt_deinit(&lc);
421 return res ? EXIT_FAILURE : EXIT_SUCCESS;
422 }
423