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