]>
Commit | Line | Data |
---|---|---|
6dbe3af9 | 1 | /* |
39fde137 KZ |
2 | * Copyright (C) 2011 Karel Zak <kzak@redhat.com> |
3 | * Originally from Ted's losetup.c | |
4 | * | |
6dbe3af9 KZ |
5 | * losetup.c - setup and control loop devices |
6 | */ | |
6dbe3af9 KZ |
7 | #include <stdio.h> |
8 | #include <string.h> | |
fd6b7a7f | 9 | #include <errno.h> |
6dbe3af9 KZ |
10 | #include <stdlib.h> |
11 | #include <unistd.h> | |
12 | #include <sys/ioctl.h> | |
fd6b7a7f | 13 | #include <sys/stat.h> |
7711bc17 | 14 | #include <inttypes.h> |
65329058 | 15 | #include <getopt.h> |
6dbe3af9 | 16 | |
934df30d | 17 | #include "c.h" |
7eda085c | 18 | #include "nls.h" |
934df30d | 19 | #include "strutils.h" |
de4acb05 | 20 | #include "loopdev.h" |
6c503d59 | 21 | #include "xgetpass.h" |
efb8854f | 22 | #include "closestream.h" |
0a719a7c | 23 | |
39fde137 | 24 | enum { |
c7e0925d | 25 | A_CREATE = 1, /* setup a new device */ |
c654c4f0 KZ |
26 | A_DELETE, /* delete given device(s) */ |
27 | A_DELETE_ALL, /* delete all devices */ | |
39fde137 | 28 | A_SHOW, /* list devices */ |
09ec0c0a | 29 | A_SHOW_ONE, /* print info about one device */ |
39fde137 KZ |
30 | A_FIND_FREE, /* find first unused */ |
31 | A_SET_CAPACITY, /* set device capacity */ | |
32 | }; | |
33 | ||
0a719a7c | 34 | static int verbose; |
6dbe3af9 | 35 | |
d03dd608 | 36 | |
39fde137 KZ |
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; | |
a3b421df | 43 | uint32_t type; |
39fde137 KZ |
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)", | |
a3b421df | 70 | loopcxt_get_device(lc), (int) dev, ino, fname); |
39fde137 KZ |
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) | |
a3b421df | 84 | printf(_(", encryption %s (type %u)"), e, type); |
39fde137 KZ |
85 | } |
86 | printf("\n"); | |
87 | return 0; | |
88 | } | |
89 | ||
bc0ac075 KZ |
90 | static int show_all_loops(struct loopdev_cxt *lc, const char *file, |
91 | uint64_t offset, int flags) | |
39fde137 | 92 | { |
bc0ac075 KZ |
93 | struct stat sbuf, *st = &sbuf; |
94 | ||
39fde137 | 95 | if (loopcxt_init_iterator(lc, LOOPITER_FL_USED)) |
6e90a44c | 96 | return -1; |
39fde137 | 97 | |
bc0ac075 KZ |
98 | if (!file || stat(file, st)) |
99 | st = NULL; | |
100 | ||
101 | while (loopcxt_next(lc) == 0) { | |
39fde137 | 102 | |
bc0ac075 KZ |
103 | if (file && !loopcxt_is_used(lc, st, file, offset, flags)) |
104 | continue; | |
105 | ||
106 | printf_loopdev(lc); | |
107 | } | |
c654c4f0 | 108 | loopcxt_deinit_iterator(lc); |
6e90a44c KZ |
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 | ||
c654c4f0 KZ |
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; | |
39fde137 KZ |
148 | } |
149 | ||
aadb9303 KZ |
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" | |
916bf85e | 172 | " -P, --partscan create partitioned loop device\n" |
aadb9303 KZ |
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)")); | |
6997468e KZ |
182 | |
183 | exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); | |
aadb9303 | 184 | } |
22853e4a | 185 | |
39fde137 KZ |
186 | int main(int argc, char **argv) |
187 | { | |
188 | struct loopdev_cxt lc; | |
c7e0925d KZ |
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; | |
6c7d5ae9 | 193 | |
aadb9303 KZ |
194 | enum { |
195 | OPT_SIZELIMIT = CHAR_MAX + 1, | |
196 | OPT_SHOW | |
197 | }; | |
198 | static const struct option longopts[] = { | |
d99f0140 | 199 | { "all", 0, 0, 'a' }, |
6e90a44c | 200 | { "set-capacity", 1, 0, 'c' }, |
c654c4f0 | 201 | { "detach", 1, 0, 'd' }, |
34f9b684 | 202 | { "detach-all", 0, 0, 'D' }, |
d99f0140 KZ |
203 | { "encryption", 1, 0, 'e' }, |
204 | { "find", 0, 0, 'f' }, | |
205 | { "help", 0, 0, 'h' }, | |
259fcc57 | 206 | { "associated", 1, 0, 'j' }, |
d99f0140 | 207 | { "offset", 1, 0, 'o' }, |
aadb9303 | 208 | { "sizelimit", 1, 0, OPT_SIZELIMIT }, |
d99f0140 | 209 | { "pass-fd", 1, 0, 'p' }, |
916bf85e | 210 | { "partscan", 0, 0, 'P' }, |
d99f0140 | 211 | { "read-only", 0, 0, 'r' }, |
aadb9303 | 212 | { "show", 0, 0, OPT_SHOW }, |
d99f0140 | 213 | { "verbose", 0, 0, 'v' }, |
aadb9303 | 214 | { "version", 0, 0, 'V' }, |
d99f0140 KZ |
215 | { NULL, 0, 0, 0 } |
216 | }; | |
22853e4a KZ |
217 | |
218 | setlocale(LC_ALL, ""); | |
219 | bindtextdomain(PACKAGE, LOCALEDIR); | |
220 | textdomain(PACKAGE); | |
efb8854f | 221 | atexit(close_stdout); |
22853e4a | 222 | |
6da69b83 KZ |
223 | loopcxt_init(&lc, 0); |
224 | loopcxt_enable_debug(&lc, getenv("LOOPDEV_DEBUG") ? TRUE : FALSE); | |
225 | ||
916bf85e | 226 | while ((c = getopt_long(argc, argv, "ac:d:De:E:fhj:o:p:PrvV", |
d99f0140 | 227 | longopts, NULL)) != -1) { |
39fde137 | 228 | |
bc0ac075 | 229 | if (act && strchr("acdDfj", c)) |
39fde137 KZ |
230 | errx(EXIT_FAILURE, |
231 | _("the options %s are mutually exclusive"), | |
bc0ac075 | 232 | "--{all,associated,set-capacity,detach,detach-all,find}"); |
39fde137 | 233 | |
22853e4a | 234 | switch (c) { |
8b125fae | 235 | case 'a': |
39fde137 | 236 | act = A_SHOW; |
8b125fae | 237 | break; |
d34ac93a | 238 | case 'c': |
6e90a44c KZ |
239 | act = A_SET_CAPACITY; |
240 | loopcxt_set_device(&lc, optarg); | |
d34ac93a | 241 | break; |
faf142b6 | 242 | case 'r': |
c7e0925d | 243 | lo_flags |= LO_FLAGS_READ_ONLY; |
faf142b6 | 244 | break; |
22853e4a | 245 | case 'd': |
c654c4f0 KZ |
246 | act = A_DELETE; |
247 | loopcxt_set_device(&lc, optarg); | |
22853e4a | 248 | break; |
34f9b684 | 249 | case 'D': |
c654c4f0 | 250 | act = A_DELETE_ALL; |
34f9b684 | 251 | break; |
d03dd608 | 252 | case 'E': |
22853e4a KZ |
253 | case 'e': |
254 | encryption = optarg; | |
255 | break; | |
d162fcb5 | 256 | case 'f': |
bcdbdc72 | 257 | act = A_FIND_FREE; |
d162fcb5 | 258 | break; |
108591d2 KZ |
259 | case 'h': |
260 | usage(stdout); | |
261 | break; | |
259fcc57 | 262 | case 'j': |
bc0ac075 KZ |
263 | act = A_SHOW; |
264 | file = optarg; | |
259fcc57 | 265 | break; |
22853e4a | 266 | case 'o': |
e9e426eb | 267 | offset = strtosize_or_err(optarg, _("failed to parse offset")); |
bc0ac075 | 268 | flags |= LOOPDEV_FL_OFFSET; |
22853e4a | 269 | break; |
d03dd608 | 270 | case 'p': |
c7e0925d KZ |
271 | passfd = strtol_or_err(optarg, |
272 | _("invalid passphrase file descriptor")); | |
d03dd608 | 273 | break; |
916bf85e KZ |
274 | case 'P': |
275 | lo_flags |= LO_FLAGS_PARTSCAN; | |
276 | break; | |
aadb9303 | 277 | case OPT_SHOW: |
ba3809b0 KZ |
278 | showdev = 1; |
279 | break; | |
22853e4a KZ |
280 | case 'v': |
281 | verbose = 1; | |
282 | break; | |
aadb9303 KZ |
283 | case 'V': |
284 | printf(UTIL_LINUX_VERSION); | |
285 | return EXIT_SUCCESS; | |
286 | case OPT_SIZELIMIT: /* --sizelimit */ | |
e9e426eb | 287 | sizelimit = strtosize_or_err(optarg, _("failed to parse size")); |
c7e0925d | 288 | flags |= LOOPDEV_FL_SIZELIMIT; |
7bcefc7f | 289 | break; |
22853e4a | 290 | default: |
108591d2 | 291 | usage(stderr); |
22853e4a KZ |
292 | } |
293 | } | |
d162fcb5 | 294 | |
39fde137 | 295 | if (argc == 1) |
108591d2 | 296 | usage(stderr); |
39fde137 | 297 | |
c7e0925d KZ |
298 | if (act == A_FIND_FREE && optind < argc) { |
299 | /* | |
300 | * losetup -f <backing_file> | |
301 | */ | |
302 | act = A_CREATE; | |
303 | file = argv[optind++]; | |
304 | } | |
09ec0c0a KZ |
305 | if (!act && optind + 1 == argc) { |
306 | /* | |
307 | * losetup <device> | |
308 | */ | |
309 | act = A_SHOW_ONE; | |
310 | loopcxt_set_device(&lc, argv[optind++]); | |
311 | } | |
c7e0925d KZ |
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 | ||
934df30d KZ |
333 | if ((flags & LOOPDEV_FL_OFFSET) && |
334 | act != A_CREATE && (act != A_SHOW || !file)) | |
c7e0925d | 335 | errx(EXIT_FAILURE, _("the option --offset is not allowed in this context.")); |
c654c4f0 | 336 | |
39fde137 | 337 | switch (act) { |
c7e0925d KZ |
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) | |
ce65b29a | 363 | loopcxt_set_sizelimit(&lc, sizelimit); |
c7e0925d KZ |
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(_("failed to setup loop device")); | |
376 | break; | |
377 | } | |
378 | } while (hasdev == 0); | |
379 | ||
380 | free(pass); | |
381 | ||
382 | if (showdev && res == 0) | |
383 | printf("%s\n", loopcxt_get_device(&lc)); | |
384 | break; | |
385 | } | |
c654c4f0 KZ |
386 | case A_DELETE: |
387 | res = delete_loop(&lc); | |
388 | while (optind < argc) { | |
389 | loopcxt_set_device(&lc, argv[optind++]); | |
390 | res += delete_loop(&lc); | |
391 | } | |
392 | break; | |
393 | case A_DELETE_ALL: | |
394 | res = delete_all_loops(&lc); | |
395 | break; | |
bcdbdc72 KZ |
396 | case A_FIND_FREE: |
397 | if (loopcxt_find_unused(&lc)) | |
398 | warn(_("find unused loop device failed")); | |
399 | else | |
400 | printf("%s\n", loopcxt_get_device(&lc)); | |
401 | break; | |
39fde137 | 402 | case A_SHOW: |
bc0ac075 KZ |
403 | res = show_all_loops(&lc, file, offset, flags); |
404 | break; | |
09ec0c0a KZ |
405 | case A_SHOW_ONE: |
406 | res = printf_loopdev(&lc); | |
407 | if (res) | |
408 | warn(_("%s"), loopcxt_get_device(&lc)); | |
409 | break; | |
6e90a44c KZ |
410 | case A_SET_CAPACITY: |
411 | res = set_capacity(&lc); | |
412 | break; | |
39fde137 | 413 | default: |
c7e0925d | 414 | usage(stderr); |
39fde137 KZ |
415 | break; |
416 | } | |
417 | ||
418 | loopcxt_deinit(&lc); | |
c7e0925d | 419 | return res ? EXIT_FAILURE : EXIT_SUCCESS; |
22853e4a KZ |
420 | } |
421 |