]>
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 | */ | |
896352d3 | 7 | #include <assert.h> |
6dbe3af9 KZ |
8 | #include <stdio.h> |
9 | #include <string.h> | |
fd6b7a7f | 10 | #include <errno.h> |
6dbe3af9 KZ |
11 | #include <stdlib.h> |
12 | #include <unistd.h> | |
13 | #include <sys/ioctl.h> | |
fd6b7a7f | 14 | #include <sys/stat.h> |
7711bc17 | 15 | #include <inttypes.h> |
65329058 | 16 | #include <getopt.h> |
6dbe3af9 | 17 | |
7e86cd54 OO |
18 | #include <libsmartcols.h> |
19 | ||
934df30d | 20 | #include "c.h" |
7eda085c | 21 | #include "nls.h" |
934df30d | 22 | #include "strutils.h" |
de4acb05 | 23 | #include "loopdev.h" |
efb8854f | 24 | #include "closestream.h" |
96801c48 | 25 | #include "optutils.h" |
896352d3 | 26 | #include "xalloc.h" |
114ade3d | 27 | #include "canonicalize.h" |
362f5d20 | 28 | #include "pathnames.h" |
96801c48 | 29 | |
39fde137 | 30 | enum { |
c7e0925d | 31 | A_CREATE = 1, /* setup a new device */ |
c654c4f0 KZ |
32 | A_DELETE, /* delete given device(s) */ |
33 | A_DELETE_ALL, /* delete all devices */ | |
39fde137 | 34 | A_SHOW, /* list devices */ |
09ec0c0a | 35 | A_SHOW_ONE, /* print info about one device */ |
39fde137 KZ |
36 | A_FIND_FREE, /* find first unused */ |
37 | A_SET_CAPACITY, /* set device capacity */ | |
64c3bb3c | 38 | A_SET_DIRECT_IO, /* set accessing backing file by direct io */ |
a1a41597 | 39 | A_SET_BLOCKSIZE, /* set logical block size of the loop device */ |
39fde137 KZ |
40 | }; |
41 | ||
896352d3 OO |
42 | enum { |
43 | COL_NAME = 0, | |
44 | COL_AUTOCLR, | |
45 | COL_BACK_FILE, | |
46 | COL_BACK_INO, | |
47 | COL_BACK_MAJMIN, | |
48 | COL_MAJMIN, | |
49 | COL_OFFSET, | |
50 | COL_PARTSCAN, | |
51 | COL_RO, | |
4ad996d7 | 52 | COL_SIZELIMIT, |
faeef4d2 | 53 | COL_DIO, |
76493ceb | 54 | COL_LOGSEC, |
896352d3 OO |
55 | }; |
56 | ||
7e86cd54 OO |
57 | /* basic output flags */ |
58 | static int no_headings; | |
59 | static int raw; | |
4827093d | 60 | static int json; |
896352d3 OO |
61 | |
62 | struct colinfo { | |
63 | const char *name; | |
64 | double whint; | |
65 | int flags; | |
66 | const char *help; | |
67 | }; | |
68 | ||
69 | static struct colinfo infos[] = { | |
7e86cd54 | 70 | [COL_AUTOCLR] = { "AUTOCLEAR", 1, SCOLS_FL_RIGHT, N_("autoclear flag set")}, |
896352d3 | 71 | [COL_BACK_FILE] = { "BACK-FILE", 0.3, 0, N_("device backing file")}, |
7e86cd54 | 72 | [COL_BACK_INO] = { "BACK-INO", 4, SCOLS_FL_RIGHT, N_("backing file inode number")}, |
896352d3 OO |
73 | [COL_BACK_MAJMIN] = { "BACK-MAJ:MIN", 6, 0, N_("backing file major:minor device number")}, |
74 | [COL_NAME] = { "NAME", 0.25, 0, N_("loop device name")}, | |
7e86cd54 OO |
75 | [COL_OFFSET] = { "OFFSET", 5, SCOLS_FL_RIGHT, N_("offset from the beginning")}, |
76 | [COL_PARTSCAN] = { "PARTSCAN", 1, SCOLS_FL_RIGHT, N_("partscan flag set")}, | |
77 | [COL_RO] = { "RO", 1, SCOLS_FL_RIGHT, N_("read-only device")}, | |
78 | [COL_SIZELIMIT] = { "SIZELIMIT", 5, SCOLS_FL_RIGHT, N_("size limit of the file in bytes")}, | |
896352d3 | 79 | [COL_MAJMIN] = { "MAJ:MIN", 3, 0, N_("loop device major:minor number")}, |
faeef4d2 | 80 | [COL_DIO] = { "DIO", 1, SCOLS_FL_RIGHT, N_("access backing file with direct-io")}, |
76493ceb | 81 | [COL_LOGSEC] = { "LOG-SEC", 4, SCOLS_FL_RIGHT, N_("logical sector size in bytes")}, |
896352d3 OO |
82 | }; |
83 | ||
d78deddc | 84 | static int columns[ARRAY_SIZE(infos) * 2] = {-1}; |
40b17508 | 85 | static size_t ncolumns; |
6dbe3af9 | 86 | |
896352d3 OO |
87 | static int get_column_id(int num) |
88 | { | |
40b17508 KZ |
89 | assert(num >= 0); |
90 | assert((size_t) num < ncolumns); | |
d78deddc | 91 | assert(columns[num] < (int) ARRAY_SIZE(infos)); |
896352d3 OO |
92 | return columns[num]; |
93 | } | |
94 | ||
95 | static struct colinfo *get_column_info(int num) | |
96 | { | |
97 | return &infos[ get_column_id(num) ]; | |
98 | } | |
99 | ||
100 | static int column_name_to_id(const char *name, size_t namesz) | |
101 | { | |
102 | size_t i; | |
103 | ||
d78deddc | 104 | for (i = 0; i < ARRAY_SIZE(infos); i++) { |
896352d3 OO |
105 | const char *cn = infos[i].name; |
106 | ||
107 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) | |
108 | return i; | |
109 | } | |
110 | warnx(_("unknown column: %s"), name); | |
111 | return -1; | |
112 | } | |
d03dd608 | 113 | |
39fde137 KZ |
114 | static int printf_loopdev(struct loopdev_cxt *lc) |
115 | { | |
116 | uint64_t x; | |
117 | dev_t dev = 0; | |
118 | ino_t ino = 0; | |
1b504263 | 119 | char *fname; |
a3b421df | 120 | uint32_t type; |
39fde137 KZ |
121 | |
122 | fname = loopcxt_get_backing_file(lc); | |
123 | if (!fname) | |
124 | return -EINVAL; | |
125 | ||
126 | if (loopcxt_get_backing_devno(lc, &dev) == 0) | |
127 | loopcxt_get_backing_inode(lc, &ino); | |
128 | ||
129 | if (!dev && !ino) { | |
130 | /* | |
131 | * Probably non-root user (no permissions to | |
132 | * call LOOP_GET_STATUS ioctls). | |
133 | */ | |
134 | printf("%s: []: (%s)", | |
135 | loopcxt_get_device(lc), fname); | |
136 | ||
137 | if (loopcxt_get_offset(lc, &x) == 0 && x) | |
138 | printf(_(", offset %ju"), x); | |
139 | ||
140 | if (loopcxt_get_sizelimit(lc, &x) == 0 && x) | |
141 | printf(_(", sizelimit %ju"), x); | |
142 | printf("\n"); | |
143 | return 0; | |
144 | } | |
145 | ||
146 | printf("%s: [%04d]:%" PRIu64 " (%s)", | |
a3b421df | 147 | loopcxt_get_device(lc), (int) dev, ino, fname); |
39fde137 KZ |
148 | |
149 | if (loopcxt_get_offset(lc, &x) == 0 && x) | |
150 | printf(_(", offset %ju"), x); | |
151 | ||
152 | if (loopcxt_get_sizelimit(lc, &x) == 0 && x) | |
153 | printf(_(", sizelimit %ju"), x); | |
154 | ||
155 | if (loopcxt_get_encrypt_type(lc, &type) == 0) { | |
156 | const char *e = loopcxt_get_crypt_name(lc); | |
157 | ||
158 | if ((!e || !*e) && type == 1) | |
159 | e = "XOR"; | |
160 | if (e && *e) | |
a3b421df | 161 | printf(_(", encryption %s (type %u)"), e, type); |
39fde137 KZ |
162 | } |
163 | printf("\n"); | |
164 | return 0; | |
165 | } | |
166 | ||
bc0ac075 KZ |
167 | static int show_all_loops(struct loopdev_cxt *lc, const char *file, |
168 | uint64_t offset, int flags) | |
39fde137 | 169 | { |
bc0ac075 | 170 | struct stat sbuf, *st = &sbuf; |
6d62bc0f | 171 | char *cn_file = NULL; |
bc0ac075 | 172 | |
39fde137 | 173 | if (loopcxt_init_iterator(lc, LOOPITER_FL_USED)) |
6e90a44c | 174 | return -1; |
39fde137 | 175 | |
bc0ac075 KZ |
176 | if (!file || stat(file, st)) |
177 | st = NULL; | |
178 | ||
179 | while (loopcxt_next(lc) == 0) { | |
6d62bc0f KZ |
180 | if (file) { |
181 | int used; | |
182 | const char *bf = cn_file ? cn_file : file; | |
183 | ||
74a4705a | 184 | used = loopcxt_is_used(lc, st, bf, offset, 0, flags); |
6d62bc0f KZ |
185 | if (!used && !cn_file) { |
186 | bf = cn_file = canonicalize_path(file); | |
74a4705a | 187 | used = loopcxt_is_used(lc, st, bf, offset, 0, flags); |
6d62bc0f KZ |
188 | } |
189 | if (!used) | |
114ade3d SK |
190 | continue; |
191 | } | |
bc0ac075 KZ |
192 | printf_loopdev(lc); |
193 | } | |
c654c4f0 | 194 | loopcxt_deinit_iterator(lc); |
f614b73c | 195 | free(cn_file); |
6e90a44c KZ |
196 | return 0; |
197 | } | |
198 | ||
c654c4f0 KZ |
199 | static int delete_loop(struct loopdev_cxt *lc) |
200 | { | |
201 | if (loopcxt_delete_device(lc)) | |
202 | warn(_("%s: detach failed"), loopcxt_get_device(lc)); | |
203 | else | |
204 | return 0; | |
205 | ||
206 | return -1; | |
207 | } | |
208 | ||
209 | static int delete_all_loops(struct loopdev_cxt *lc) | |
210 | { | |
211 | int res = 0; | |
212 | ||
213 | if (loopcxt_init_iterator(lc, LOOPITER_FL_USED)) | |
214 | return -1; | |
215 | ||
216 | while (loopcxt_next(lc) == 0) | |
217 | res += delete_loop(lc); | |
218 | ||
219 | loopcxt_deinit_iterator(lc); | |
220 | return res; | |
39fde137 KZ |
221 | } |
222 | ||
7e86cd54 | 223 | static int set_scols_data(struct loopdev_cxt *lc, struct libscols_line *ln) |
896352d3 | 224 | { |
40b17508 | 225 | size_t i; |
896352d3 OO |
226 | |
227 | for (i = 0; i < ncolumns; i++) { | |
48f1f38b KZ |
228 | const char *p = NULL; /* external data */ |
229 | char *np = NULL; /* allocated here */ | |
896352d3 | 230 | uint64_t x = 0; |
a9e3ec19 | 231 | int rc = 0; |
896352d3 OO |
232 | |
233 | switch(get_column_id(i)) { | |
234 | case COL_NAME: | |
235 | p = loopcxt_get_device(lc); | |
896352d3 OO |
236 | break; |
237 | case COL_BACK_FILE: | |
238 | p = loopcxt_get_backing_file(lc); | |
896352d3 OO |
239 | break; |
240 | case COL_OFFSET: | |
241 | if (loopcxt_get_offset(lc, &x) == 0) | |
242 | xasprintf(&np, "%jd", x); | |
896352d3 | 243 | break; |
4ad996d7 | 244 | case COL_SIZELIMIT: |
896352d3 OO |
245 | if (loopcxt_get_sizelimit(lc, &x) == 0) |
246 | xasprintf(&np, "%jd", x); | |
896352d3 OO |
247 | break; |
248 | case COL_BACK_MAJMIN: | |
249 | { | |
250 | dev_t dev = 0; | |
251 | if (loopcxt_get_backing_devno(lc, &dev) == 0 && dev) | |
252 | xasprintf(&np, "%8u:%-3u", major(dev), minor(dev)); | |
896352d3 OO |
253 | break; |
254 | } | |
255 | case COL_MAJMIN: | |
256 | { | |
257 | struct stat st; | |
258 | ||
259 | if (loopcxt_get_device(lc) | |
260 | && stat(loopcxt_get_device(lc), &st) == 0 | |
261 | && S_ISBLK(st.st_mode) | |
262 | && major(st.st_rdev) == LOOPDEV_MAJOR) | |
263 | xasprintf(&np, "%3u:%-3u", major(st.st_rdev), | |
264 | minor(st.st_rdev)); | |
896352d3 OO |
265 | break; |
266 | } | |
267 | case COL_BACK_INO: | |
268 | { | |
269 | ino_t ino = 0; | |
270 | if (loopcxt_get_backing_inode(lc, &ino) == 0 && ino) | |
271 | xasprintf(&np, "%ju", ino); | |
896352d3 OO |
272 | break; |
273 | } | |
274 | case COL_AUTOCLR: | |
48f1f38b | 275 | p = loopcxt_is_autoclear(lc) ? "1" : "0"; |
896352d3 OO |
276 | break; |
277 | case COL_RO: | |
48f1f38b | 278 | p = loopcxt_is_readonly(lc) ? "1" : "0"; |
896352d3 | 279 | break; |
faeef4d2 ML |
280 | case COL_DIO: |
281 | p = loopcxt_is_dio(lc) ? "1" : "0"; | |
282 | break; | |
896352d3 | 283 | case COL_PARTSCAN: |
48f1f38b | 284 | p = loopcxt_is_partscan(lc) ? "1" : "0"; |
896352d3 | 285 | break; |
76493ceb | 286 | case COL_LOGSEC: |
a1a41597 SB |
287 | if (loopcxt_get_blocksize(lc, &x) == 0) |
288 | xasprintf(&np, "%jd", x); | |
289 | break; | |
896352d3 OO |
290 | default: |
291 | return -EINVAL; | |
292 | } | |
48f1f38b KZ |
293 | |
294 | ||
295 | if (p) | |
780ce22c | 296 | rc = scols_line_set_data(ln, i, p); /* calls strdup() */ |
48f1f38b | 297 | else if (np) |
780ce22c KZ |
298 | rc = scols_line_refer_data(ln, i, np); /* only refers */ |
299 | ||
300 | if (rc) | |
301 | err(EXIT_FAILURE, _("failed to add output data")); | |
896352d3 | 302 | } |
48f1f38b | 303 | |
896352d3 OO |
304 | return 0; |
305 | } | |
306 | ||
48f1f38b | 307 | static int show_table(struct loopdev_cxt *lc, |
9f56106d KZ |
308 | const char *file, |
309 | uint64_t offset, | |
7e86cd54 | 310 | int flags) |
896352d3 OO |
311 | { |
312 | struct stat sbuf, *st = &sbuf; | |
48f1f38b | 313 | struct libscols_table *tb; |
7e86cd54 | 314 | struct libscols_line *ln; |
40b17508 KZ |
315 | int rc = 0; |
316 | size_t i; | |
896352d3 | 317 | |
710ed55d KZ |
318 | scols_init_debug(0); |
319 | ||
0925a9dd | 320 | if (!(tb = scols_new_table())) |
780ce22c | 321 | err(EXIT_FAILURE, _("failed to allocate output table")); |
0925a9dd | 322 | scols_table_enable_raw(tb, raw); |
4827093d | 323 | scols_table_enable_json(tb, json); |
0925a9dd | 324 | scols_table_enable_noheadings(tb, no_headings); |
896352d3 | 325 | |
4827093d KZ |
326 | if (json) |
327 | scols_table_set_name(tb, "loopdevices"); | |
328 | ||
e0b06769 | 329 | for (i = 0; i < ncolumns; i++) { |
896352d3 OO |
330 | struct colinfo *ci = get_column_info(i); |
331 | ||
48f1f38b | 332 | if (!scols_table_new_column(tb, ci->name, ci->whint, ci->flags)) |
780ce22c | 333 | err(EXIT_FAILURE, _("failed to allocate output column")); |
896352d3 OO |
334 | } |
335 | ||
9f56106d | 336 | /* only one loopdev requested (already assigned to loopdev_cxt) */ |
896352d3 | 337 | if (loopcxt_get_device(lc)) { |
48f1f38b | 338 | ln = scols_table_new_line(tb, NULL); |
7e86cd54 | 339 | if (!ln) |
780ce22c | 340 | err(EXIT_FAILURE, _("failed to allocate output line")); |
48f1f38b | 341 | rc = set_scols_data(lc, ln); |
896352d3 | 342 | |
9f56106d | 343 | /* list all loopdevs */ |
48f1f38b KZ |
344 | } else { |
345 | char *cn_file = NULL; | |
896352d3 | 346 | |
48f1f38b KZ |
347 | rc = loopcxt_init_iterator(lc, LOOPITER_FL_USED); |
348 | if (rc) | |
349 | goto done; | |
350 | if (!file || stat(file, st)) | |
351 | st = NULL; | |
352 | ||
353 | while (loopcxt_next(lc) == 0) { | |
354 | if (file) { | |
355 | int used; | |
356 | const char *bf = cn_file ? cn_file : file; | |
6d62bc0f | 357 | |
74a4705a | 358 | used = loopcxt_is_used(lc, st, bf, offset, 0, flags); |
48f1f38b KZ |
359 | if (!used && !cn_file) { |
360 | bf = cn_file = canonicalize_path(file); | |
74a4705a | 361 | used = loopcxt_is_used(lc, st, bf, offset, 0, flags); |
48f1f38b KZ |
362 | } |
363 | if (!used) | |
364 | continue; | |
6d62bc0f | 365 | } |
48f1f38b KZ |
366 | |
367 | ln = scols_table_new_line(tb, NULL); | |
368 | if (!ln) | |
780ce22c | 369 | err(EXIT_FAILURE, _("failed to allocate output line")); |
48f1f38b KZ |
370 | rc = set_scols_data(lc, ln); |
371 | if (rc) | |
372 | break; | |
6d62bc0f | 373 | } |
896352d3 | 374 | |
48f1f38b KZ |
375 | loopcxt_deinit_iterator(lc); |
376 | free(cn_file); | |
896352d3 | 377 | } |
48f1f38b KZ |
378 | done: |
379 | if (rc == 0) | |
380 | rc = scols_print_table(tb); | |
381 | scols_unref_table(tb); | |
382 | return rc; | |
896352d3 OO |
383 | } |
384 | ||
6e1eda6f | 385 | static void __attribute__((__noreturn__)) usage(void) |
aadb9303 | 386 | { |
6e1eda6f | 387 | FILE *out = stdout; |
e0b06769 SK |
388 | size_t i; |
389 | ||
aadb9303 KZ |
390 | fputs(USAGE_HEADER, out); |
391 | ||
392 | fprintf(out, | |
393 | _(" %1$s [options] [<loopdev>]\n" | |
394 | " %1$s [options] -f | <loopdev> <file>\n"), | |
395 | program_invocation_short_name); | |
396 | ||
451dbcfa BS |
397 | fputs(USAGE_SEPARATOR, out); |
398 | fputs(_("Set up and control loop devices.\n"), out); | |
399 | ||
ec3624aa | 400 | /* commands */ |
aadb9303 | 401 | fputs(USAGE_OPTIONS, out); |
9c47f25e | 402 | fputs(_(" -a, --all list all used devices\n"), out); |
aa06617f | 403 | fputs(_(" -d, --detach <loopdev>... detach one or more devices\n"), out); |
9c47f25e BS |
404 | fputs(_(" -D, --detach-all detach all used devices\n"), out); |
405 | fputs(_(" -f, --find find first unused device\n"), out); | |
aa06617f | 406 | fputs(_(" -c, --set-capacity <loopdev> resize the device\n"), out); |
9c47f25e | 407 | fputs(_(" -j, --associated <file> list all devices associated with <file>\n"), out); |
9a94b634 | 408 | fputs(_(" -L, --nooverlap avoid possible conflict between devices\n"), out); |
9c47f25e | 409 | |
ec3624aa | 410 | /* commands options */ |
aadb9303 | 411 | fputs(USAGE_SEPARATOR, out); |
59a4ed11 | 412 | fputs(_(" -o, --offset <num> start at offset <num> into file\n"), out); |
aa06617f | 413 | fputs(_(" --sizelimit <num> device is limited to <num> bytes of the file\n"), out); |
76493ceb | 414 | fputs(_(" -b --sector-size <num> set the logical sector size to <num>\n"), out); |
aa06617f BS |
415 | fputs(_(" -P, --partscan create a partitioned loop device\n"), out); |
416 | fputs(_(" -r, --read-only set up a read-only loop device\n"), out); | |
f98d9641 | 417 | fputs(_(" --direct-io[=<on|off>] open backing file with O_DIRECT\n"), out); |
59a4ed11 SK |
418 | fputs(_(" --show print device name after setup (with -f)\n"), out); |
419 | fputs(_(" -v, --verbose verbose mode\n"), out); | |
aadb9303 | 420 | |
ec3624aa | 421 | /* output options */ |
9f56106d | 422 | fputs(USAGE_SEPARATOR, out); |
ec3624aa | 423 | fputs(_(" -J, --json use JSON --list output format\n"), out); |
14576644 | 424 | fputs(_(" -l, --list list info about all or specified (default)\n"), out); |
0d0d12ad | 425 | fputs(_(" -n, --noheadings don't print headings for --list output\n"), out); |
ec3624aa | 426 | fputs(_(" -O, --output <cols> specify columns to output for --list\n"), out); |
9f56106d KZ |
427 | fputs(_(" --raw use raw --list output format\n"), out); |
428 | ||
aadb9303 | 429 | fputs(USAGE_SEPARATOR, out); |
f45f3ec3 | 430 | printf(USAGE_HELP_OPTIONS(31)); |
aadb9303 | 431 | |
c3a4cfc5 | 432 | fputs(USAGE_COLUMNS, out); |
d78deddc | 433 | for (i = 0; i < ARRAY_SIZE(infos); i++) |
896352d3 OO |
434 | fprintf(out, " %12s %s\n", infos[i].name, _(infos[i].help)); |
435 | ||
f45f3ec3 | 436 | printf(USAGE_MAN_TAIL("losetup(8)")); |
6997468e | 437 | |
6e1eda6f | 438 | exit(EXIT_SUCCESS); |
aadb9303 | 439 | } |
22853e4a | 440 | |
35545456 KZ |
441 | static void warn_size(const char *filename, uint64_t size) |
442 | { | |
443 | struct stat st; | |
444 | ||
445 | if (!size) { | |
b048b8af | 446 | if (stat(filename, &st) || S_ISBLK(st.st_mode)) |
35545456 KZ |
447 | return; |
448 | size = st.st_size; | |
449 | } | |
450 | ||
451 | if (size < 512) | |
97b820bf BS |
452 | warnx(_("%s: Warning: file is smaller than 512 bytes; the loop device " |
453 | "may be useless or invisible for system tools."), | |
35545456 KZ |
454 | filename); |
455 | else if (size % 512) | |
97b820bf | 456 | warnx(_("%s: Warning: file does not fit into a 512-byte sector; " |
35545456 KZ |
457 | "the end of the file will be ignored."), |
458 | filename); | |
459 | } | |
460 | ||
9a94b634 KZ |
461 | static int create_loop(struct loopdev_cxt *lc, |
462 | int nooverlap, int lo_flags, int flags, | |
463 | const char *file, uint64_t offset, uint64_t sizelimit) | |
464 | { | |
465 | int hasdev = loopcxt_has_device(lc); | |
466 | int rc = 0; | |
467 | ||
d8ba61fc | 468 | /* losetup --find --noverlap file.img */ |
9a94b634 KZ |
469 | if (!hasdev && nooverlap) { |
470 | rc = loopcxt_find_overlap(lc, file, offset, sizelimit); | |
471 | switch (rc) { | |
472 | case 0: /* not found */ | |
473 | break; | |
474 | ||
475 | case 1: /* overlap */ | |
476 | loopcxt_deinit(lc); | |
477 | errx(EXIT_FAILURE, _("%s: overlapping loop device exists"), file); | |
478 | ||
479 | case 2: /* overlap -- full size and offset match (reuse) */ | |
480 | { | |
481 | uint32_t lc_encrypt_type; | |
482 | ||
483 | /* Once a loop is initialized RO, there is no | |
484 | * way to change its parameters. */ | |
485 | if (loopcxt_is_readonly(lc) | |
486 | && !(lo_flags & LO_FLAGS_READ_ONLY)) { | |
487 | loopcxt_deinit(lc); | |
488 | errx(EXIT_FAILURE, _("%s: overlapping read-only loop device exists"), file); | |
489 | } | |
490 | ||
491 | /* This is no more supported, but check to be safe. */ | |
492 | if (loopcxt_get_encrypt_type(lc, &lc_encrypt_type) == 0 | |
493 | && lc_encrypt_type != LO_CRYPT_NONE) { | |
494 | loopcxt_deinit(lc); | |
495 | errx(EXIT_FAILURE, _("%s: overlapping encrypted loop device exists"), file); | |
496 | } | |
f27d989c SB |
497 | |
498 | lc->info.lo_flags &= !LO_FLAGS_AUTOCLEAR; | |
499 | if (loopcxt_set_status(lc)) { | |
500 | loopcxt_deinit(lc); | |
501 | errx(EXIT_FAILURE, _("%s: failed to re-use loop device"), file); | |
502 | } | |
9a94b634 KZ |
503 | return 0; /* success, re-use */ |
504 | } | |
505 | default: /* error */ | |
506 | loopcxt_deinit(lc); | |
507 | errx(EXIT_FAILURE, _("failed to inspect loop devices")); | |
508 | return -errno; | |
509 | } | |
510 | } | |
511 | ||
512 | if (hasdev && !is_loopdev(loopcxt_get_device(lc))) | |
513 | loopcxt_add_device(lc); | |
514 | ||
d8ba61fc KZ |
515 | /* losetup --noverlap /dev/loopN file.img */ |
516 | if (hasdev && nooverlap) { | |
517 | struct loopdev_cxt lc2; | |
518 | ||
519 | if (loopcxt_init(&lc2, 0)) { | |
520 | loopcxt_deinit(lc); | |
521 | err(EXIT_FAILURE, _("failed to initialize loopcxt")); | |
522 | } | |
523 | rc = loopcxt_find_overlap(&lc2, file, offset, sizelimit); | |
524 | loopcxt_deinit(&lc2); | |
525 | ||
526 | if (rc) { | |
527 | loopcxt_deinit(lc); | |
528 | if (rc > 0) | |
529 | errx(EXIT_FAILURE, _("%s: overlapping loop device exists"), file); | |
530 | err(EXIT_FAILURE, _("%s: failed to check for conflicting loop devices"), file); | |
531 | } | |
532 | } | |
533 | ||
9a94b634 KZ |
534 | /* Create a new device */ |
535 | do { | |
536 | const char *errpre; | |
537 | ||
538 | /* Note that loopcxt_{find_unused,set_device}() resets | |
539 | * loopcxt struct. | |
540 | */ | |
541 | if (!hasdev && (rc = loopcxt_find_unused(lc))) { | |
542 | warnx(_("cannot find an unused loop device")); | |
543 | break; | |
544 | } | |
545 | if (flags & LOOPDEV_FL_OFFSET) | |
546 | loopcxt_set_offset(lc, offset); | |
547 | if (flags & LOOPDEV_FL_SIZELIMIT) | |
548 | loopcxt_set_sizelimit(lc, sizelimit); | |
549 | if (lo_flags) | |
550 | loopcxt_set_flags(lc, lo_flags); | |
551 | if ((rc = loopcxt_set_backing_file(lc, file))) { | |
552 | warn(_("%s: failed to use backing file"), file); | |
553 | break; | |
554 | } | |
555 | errno = 0; | |
556 | rc = loopcxt_setup_device(lc); | |
557 | if (rc == 0) | |
558 | break; /* success */ | |
559 | if (errno == EBUSY && !hasdev) | |
560 | continue; | |
561 | ||
562 | /* errors */ | |
563 | errpre = hasdev && loopcxt_get_fd(lc) < 0 ? | |
564 | loopcxt_get_device(lc) : file; | |
565 | warn(_("%s: failed to set up loop device"), errpre); | |
566 | break; | |
567 | } while (hasdev == 0); | |
568 | ||
569 | return rc; | |
570 | } | |
571 | ||
39fde137 KZ |
572 | int main(int argc, char **argv) |
573 | { | |
574 | struct loopdev_cxt lc; | |
9a94b634 | 575 | int act = 0, flags = 0, no_overlap = 0, c; |
5cf05c71 | 576 | char *file = NULL; |
a1a41597 | 577 | uint64_t offset = 0, sizelimit = 0, blocksize = 0; |
7e86cd54 | 578 | int res = 0, showdev = 0, lo_flags = 0; |
896352d3 OO |
579 | char *outarg = NULL; |
580 | int list = 0; | |
a1a41597 | 581 | unsigned long use_dio = 0, set_dio = 0, set_blocksize = 0; |
6c7d5ae9 | 582 | |
aadb9303 KZ |
583 | enum { |
584 | OPT_SIZELIMIT = CHAR_MAX + 1, | |
9f56106d | 585 | OPT_SHOW, |
64c3bb3c ML |
586 | OPT_RAW, |
587 | OPT_DIO | |
aadb9303 KZ |
588 | }; |
589 | static const struct option longopts[] = { | |
87918040 SK |
590 | { "all", no_argument, NULL, 'a' }, |
591 | { "set-capacity", required_argument, NULL, 'c' }, | |
592 | { "detach", required_argument, NULL, 'd' }, | |
593 | { "detach-all", no_argument, NULL, 'D' }, | |
594 | { "find", no_argument, NULL, 'f' }, | |
595 | { "nooverlap", no_argument, NULL, 'L' }, | |
596 | { "help", no_argument, NULL, 'h' }, | |
597 | { "associated", required_argument, NULL, 'j' }, | |
598 | { "json", no_argument, NULL, 'J' }, | |
599 | { "list", no_argument, NULL, 'l' }, | |
76493ceb | 600 | { "sector-size", required_argument, NULL, 'b' }, |
87918040 SK |
601 | { "noheadings", no_argument, NULL, 'n' }, |
602 | { "offset", required_argument, NULL, 'o' }, | |
603 | { "output", required_argument, NULL, 'O' }, | |
604 | { "sizelimit", required_argument, NULL, OPT_SIZELIMIT }, | |
605 | { "partscan", no_argument, NULL, 'P' }, | |
606 | { "read-only", no_argument, NULL, 'r' }, | |
607 | { "direct-io", optional_argument, NULL, OPT_DIO }, | |
608 | { "raw", no_argument, NULL, OPT_RAW }, | |
609 | { "show", no_argument, NULL, OPT_SHOW }, | |
610 | { "verbose", no_argument, NULL, 'v' }, | |
611 | { "version", no_argument, NULL, 'V' }, | |
612 | { NULL, 0, NULL, 0 } | |
d99f0140 | 613 | }; |
22853e4a | 614 | |
c1ac3144 KZ |
615 | static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ |
616 | { 'D','a','c','d','f','j' }, | |
896352d3 | 617 | { 'D','c','d','f','l' }, |
65178cb3 | 618 | { 'D','c','d','f','O' }, |
4827093d | 619 | { 'J',OPT_RAW }, |
c1ac3144 KZ |
620 | { 0 } |
621 | }; | |
622 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
623 | ||
22853e4a KZ |
624 | setlocale(LC_ALL, ""); |
625 | bindtextdomain(PACKAGE, LOCALEDIR); | |
626 | textdomain(PACKAGE); | |
efb8854f | 627 | atexit(close_stdout); |
22853e4a | 628 | |
defa0710 KZ |
629 | if (loopcxt_init(&lc, 0)) |
630 | err(EXIT_FAILURE, _("failed to initialize loopcxt")); | |
631 | ||
a1a41597 | 632 | while ((c = getopt_long(argc, argv, "ab:c:d:Dfhj:JlLno:O:PrvV", |
d99f0140 | 633 | longopts, NULL)) != -1) { |
c1ac3144 KZ |
634 | |
635 | err_exclusive_options(c, longopts, excl, excl_st); | |
636 | ||
22853e4a | 637 | switch (c) { |
8b125fae | 638 | case 'a': |
39fde137 | 639 | act = A_SHOW; |
8b125fae | 640 | break; |
a1a41597 SB |
641 | case 'b': |
642 | set_blocksize = 1; | |
643 | blocksize = strtosize_or_err(optarg, _("failed to parse logical block size")); | |
644 | break; | |
d34ac93a | 645 | case 'c': |
6e90a44c | 646 | act = A_SET_CAPACITY; |
21ce3f3a KZ |
647 | if (!is_loopdev(optarg) || |
648 | loopcxt_set_device(&lc, optarg)) | |
defa0710 KZ |
649 | err(EXIT_FAILURE, _("%s: failed to use device"), |
650 | optarg); | |
d34ac93a | 651 | break; |
faf142b6 | 652 | case 'r': |
c7e0925d | 653 | lo_flags |= LO_FLAGS_READ_ONLY; |
faf142b6 | 654 | break; |
22853e4a | 655 | case 'd': |
c654c4f0 | 656 | act = A_DELETE; |
21ce3f3a KZ |
657 | if (!is_loopdev(optarg) || |
658 | loopcxt_set_device(&lc, optarg)) | |
defa0710 KZ |
659 | err(EXIT_FAILURE, _("%s: failed to use device"), |
660 | optarg); | |
22853e4a | 661 | break; |
34f9b684 | 662 | case 'D': |
c654c4f0 | 663 | act = A_DELETE_ALL; |
34f9b684 | 664 | break; |
d162fcb5 | 665 | case 'f': |
bcdbdc72 | 666 | act = A_FIND_FREE; |
d162fcb5 | 667 | break; |
108591d2 | 668 | case 'h': |
6e1eda6f | 669 | usage(); |
108591d2 | 670 | break; |
4827093d KZ |
671 | case 'J': |
672 | json = 1; | |
673 | break; | |
259fcc57 | 674 | case 'j': |
bc0ac075 KZ |
675 | act = A_SHOW; |
676 | file = optarg; | |
259fcc57 | 677 | break; |
896352d3 OO |
678 | case 'l': |
679 | list = 1; | |
680 | break; | |
9a94b634 KZ |
681 | case 'L': |
682 | no_overlap = 1; | |
683 | break; | |
9f56106d | 684 | case 'n': |
7e86cd54 | 685 | no_headings = 1; |
9f56106d KZ |
686 | break; |
687 | case OPT_RAW: | |
7e86cd54 | 688 | raw = 1; |
9f56106d | 689 | break; |
22853e4a | 690 | case 'o': |
e9e426eb | 691 | offset = strtosize_or_err(optarg, _("failed to parse offset")); |
bc0ac075 | 692 | flags |= LOOPDEV_FL_OFFSET; |
22853e4a | 693 | break; |
896352d3 OO |
694 | case 'O': |
695 | outarg = optarg; | |
65178cb3 | 696 | list = 1; |
896352d3 | 697 | break; |
916bf85e KZ |
698 | case 'P': |
699 | lo_flags |= LO_FLAGS_PARTSCAN; | |
700 | break; | |
aadb9303 | 701 | case OPT_SHOW: |
ba3809b0 KZ |
702 | showdev = 1; |
703 | break; | |
64c3bb3c | 704 | case OPT_DIO: |
f98d9641 KZ |
705 | use_dio = set_dio = 1; |
706 | if (optarg) | |
707 | use_dio = parse_switch(optarg, _("argument error"), "on", "off", NULL); | |
64c3bb3c | 708 | break; |
22853e4a | 709 | case 'v': |
22853e4a | 710 | break; |
aadb9303 KZ |
711 | case 'V': |
712 | printf(UTIL_LINUX_VERSION); | |
713 | return EXIT_SUCCESS; | |
714 | case OPT_SIZELIMIT: /* --sizelimit */ | |
e9e426eb | 715 | sizelimit = strtosize_or_err(optarg, _("failed to parse size")); |
c7e0925d | 716 | flags |= LOOPDEV_FL_SIZELIMIT; |
7bcefc7f | 717 | break; |
22853e4a | 718 | default: |
677ec86c | 719 | errtryhelp(EXIT_FAILURE); |
22853e4a KZ |
720 | } |
721 | } | |
d162fcb5 | 722 | |
896352d3 OO |
723 | /* default is --list --all */ |
724 | if (argc == 1) { | |
725 | act = A_SHOW; | |
726 | list = 1; | |
727 | } | |
728 | ||
4827093d KZ |
729 | if (!act && argc == 2 && (raw || json)) { |
730 | act = A_SHOW; | |
731 | list = 1; | |
732 | } | |
733 | ||
896352d3 OO |
734 | /* default --list output columns */ |
735 | if (list && !ncolumns) { | |
736 | columns[ncolumns++] = COL_NAME; | |
4ad996d7 | 737 | columns[ncolumns++] = COL_SIZELIMIT; |
896352d3 OO |
738 | columns[ncolumns++] = COL_OFFSET; |
739 | columns[ncolumns++] = COL_AUTOCLR; | |
740 | columns[ncolumns++] = COL_RO; | |
741 | columns[ncolumns++] = COL_BACK_FILE; | |
faeef4d2 | 742 | columns[ncolumns++] = COL_DIO; |
76493ceb | 743 | columns[ncolumns++] = COL_LOGSEC; |
896352d3 | 744 | } |
39fde137 | 745 | |
c7e0925d KZ |
746 | if (act == A_FIND_FREE && optind < argc) { |
747 | /* | |
748 | * losetup -f <backing_file> | |
749 | */ | |
750 | act = A_CREATE; | |
751 | file = argv[optind++]; | |
752 | } | |
896352d3 OO |
753 | |
754 | if (list && !act && optind == argc) | |
755 | /* | |
756 | * losetup --list defaults to --all | |
757 | */ | |
758 | act = A_SHOW; | |
759 | ||
09ec0c0a KZ |
760 | if (!act && optind + 1 == argc) { |
761 | /* | |
896352d3 | 762 | * losetup [--list] <device> |
64c3bb3c | 763 | * OR |
a1a41597 | 764 | * losetup {--direct-io[=off]|--logical-blocksize=size}... <device> |
09ec0c0a | 765 | */ |
a1a41597 | 766 | if (!(set_dio || set_blocksize)) |
64c3bb3c | 767 | act = A_SHOW_ONE; |
a1a41597 | 768 | if (set_dio) |
64c3bb3c | 769 | act = A_SET_DIRECT_IO; |
a1a41597 SB |
770 | if (set_blocksize) |
771 | act = A_SET_BLOCKSIZE; | |
21ce3f3a KZ |
772 | if (!is_loopdev(argv[optind]) || |
773 | loopcxt_set_device(&lc, argv[optind])) | |
defa0710 KZ |
774 | err(EXIT_FAILURE, _("%s: failed to use device"), |
775 | argv[optind]); | |
776 | optind++; | |
09ec0c0a | 777 | } |
c7e0925d KZ |
778 | if (!act) { |
779 | /* | |
780 | * losetup <loopdev> <backing_file> | |
781 | */ | |
782 | act = A_CREATE; | |
783 | ||
784 | if (optind >= argc) | |
785 | errx(EXIT_FAILURE, _("no loop device specified")); | |
21ce3f3a | 786 | /* don't use is_loopdev() here, the device does not have exist yet */ |
defa0710 | 787 | if (loopcxt_set_device(&lc, argv[optind])) |
e9e7698e | 788 | err(EXIT_FAILURE, _("%s: failed to use device"), |
defa0710 KZ |
789 | argv[optind]); |
790 | optind++; | |
c7e0925d KZ |
791 | |
792 | if (optind >= argc) | |
793 | errx(EXIT_FAILURE, _("no file specified")); | |
794 | file = argv[optind++]; | |
795 | } | |
796 | ||
797 | if (act != A_CREATE && | |
5cf05c71 | 798 | (sizelimit || lo_flags || showdev)) |
c7e0925d | 799 | errx(EXIT_FAILURE, |
136b23ef | 800 | _("the options %s are allowed during loop device setup only"), |
5cf05c71 | 801 | "--{sizelimit,read-only,show}"); |
c7e0925d | 802 | |
934df30d KZ |
803 | if ((flags & LOOPDEV_FL_OFFSET) && |
804 | act != A_CREATE && (act != A_SHOW || !file)) | |
136b23ef | 805 | errx(EXIT_FAILURE, _("the option --offset is not allowed in this context")); |
c654c4f0 | 806 | |
896352d3 OO |
807 | if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns), |
808 | &ncolumns, column_name_to_id) < 0) | |
809 | return EXIT_FAILURE; | |
810 | ||
39fde137 | 811 | switch (act) { |
c7e0925d | 812 | case A_CREATE: |
9a94b634 | 813 | res = create_loop(&lc, no_overlap, lo_flags, flags, file, offset, sizelimit); |
35545456 KZ |
814 | if (res == 0) { |
815 | if (showdev) | |
816 | printf("%s\n", loopcxt_get_device(&lc)); | |
817 | warn_size(file, sizelimit); | |
a1a41597 SB |
818 | if (set_dio || set_blocksize) |
819 | goto lo_set_post; | |
35545456 | 820 | } |
c7e0925d | 821 | break; |
c654c4f0 KZ |
822 | case A_DELETE: |
823 | res = delete_loop(&lc); | |
824 | while (optind < argc) { | |
21ce3f3a KZ |
825 | if (!is_loopdev(argv[optind]) || |
826 | loopcxt_set_device(&lc, argv[optind])) | |
defa0710 KZ |
827 | warn(_("%s: failed to use device"), |
828 | argv[optind]); | |
829 | optind++; | |
c654c4f0 KZ |
830 | res += delete_loop(&lc); |
831 | } | |
832 | break; | |
833 | case A_DELETE_ALL: | |
834 | res = delete_all_loops(&lc); | |
835 | break; | |
bcdbdc72 | 836 | case A_FIND_FREE: |
3e55659f | 837 | res = loopcxt_find_unused(&lc); |
362f5d20 KZ |
838 | if (res) { |
839 | int errsv = errno; | |
840 | ||
841 | if (access(_PATH_DEV_LOOPCTL, F_OK) == 0 && | |
842 | access(_PATH_DEV_LOOPCTL, W_OK) != 0) | |
843 | ; | |
844 | else | |
845 | errno = errsv; | |
846 | ||
136b23ef | 847 | warn(_("cannot find an unused loop device")); |
362f5d20 | 848 | } else |
bcdbdc72 KZ |
849 | printf("%s\n", loopcxt_get_device(&lc)); |
850 | break; | |
39fde137 | 851 | case A_SHOW: |
896352d3 | 852 | if (list) |
48f1f38b | 853 | res = show_table(&lc, file, offset, flags); |
896352d3 OO |
854 | else |
855 | res = show_all_loops(&lc, file, offset, flags); | |
bc0ac075 | 856 | break; |
09ec0c0a | 857 | case A_SHOW_ONE: |
896352d3 | 858 | if (list) |
48f1f38b | 859 | res = show_table(&lc, NULL, 0, 0); |
896352d3 OO |
860 | else |
861 | res = printf_loopdev(&lc); | |
09ec0c0a | 862 | if (res) |
136b23ef | 863 | warn("%s", loopcxt_get_device(&lc)); |
09ec0c0a | 864 | break; |
6e90a44c | 865 | case A_SET_CAPACITY: |
293714c0 JM |
866 | res = loopcxt_set_capacity(&lc); |
867 | if (res) | |
868 | warn(_("%s: set capacity failed"), | |
869 | loopcxt_get_device(&lc)); | |
6e90a44c | 870 | break; |
64c3bb3c | 871 | case A_SET_DIRECT_IO: |
a1a41597 SB |
872 | case A_SET_BLOCKSIZE: |
873 | lo_set_post: | |
874 | if (set_dio) { | |
875 | res = loopcxt_set_dio(&lc, use_dio); | |
876 | if (res) | |
877 | warn(_("%s: set direct io failed"), | |
878 | loopcxt_get_device(&lc)); | |
879 | } | |
880 | if (set_blocksize) { | |
881 | res = loopcxt_set_blocksize(&lc, blocksize); | |
882 | if (res) | |
883 | warn(_("%s: set logical block size failed"), | |
884 | loopcxt_get_device(&lc)); | |
885 | } | |
64c3bb3c | 886 | break; |
39fde137 | 887 | default: |
6e1eda6f RM |
888 | warnx(_("bad usage")); |
889 | errtryhelp(EXIT_FAILURE); | |
39fde137 KZ |
890 | break; |
891 | } | |
892 | ||
893 | loopcxt_deinit(&lc); | |
c7e0925d | 894 | return res ? EXIT_FAILURE : EXIT_SUCCESS; |
22853e4a KZ |
895 | } |
896 |