]>
Commit | Line | Data |
---|---|---|
2c37ca7c | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
7f8b2bf3 | 2 | /* |
2c37ca7c | 3 | * This file is part of libmount from util-linux project. |
7f8b2bf3 | 4 | * |
2c37ca7c KZ |
5 | * Copyright (C) 2011-2018 Karel Zak <kzak@redhat.com> |
6 | * | |
7 | * libmount is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU Lesser General Public License as published by | |
9 | * the Free Software Foundation; either version 2.1 of the License, or | |
10 | * (at your option) any later version. | |
7f8b2bf3 KZ |
11 | */ |
12 | ||
13 | /* | |
14 | * DOCS: - "lo@" prefix for fstype is unsupported | |
7f8b2bf3 KZ |
15 | */ |
16 | ||
17 | #include <blkid.h> | |
7b46647d | 18 | #include <stdbool.h> |
7f8b2bf3 KZ |
19 | |
20 | #include "mountP.h" | |
21 | #include "loopdev.h" | |
22 | #include "linux_version.h" | |
23 | ||
24 | ||
25 | int mnt_context_is_loopdev(struct libmnt_context *cxt) | |
26 | { | |
27 | const char *type, *src; | |
7f8b2bf3 KZ |
28 | |
29 | assert(cxt); | |
c8512236 | 30 | |
7f8b2bf3 KZ |
31 | /* The mount flags have to be merged, otherwise we have to use |
32 | * expensive mnt_context_get_user_mflags() instead of cxt->user_mountflags. */ | |
33 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); | |
34 | ||
35 | if (!cxt->fs) | |
36 | return 0; | |
37 | src = mnt_fs_get_srcpath(cxt->fs); | |
38 | if (!src) | |
39 | return 0; /* backing file not set */ | |
40 | ||
41 | if (cxt->user_mountflags & (MNT_MS_LOOP | | |
42 | MNT_MS_OFFSET | | |
5cf05c71 | 43 | MNT_MS_SIZELIMIT)) { |
c8512236 | 44 | |
7bbde59c | 45 | DBG(LOOP, ul_debugobj(cxt, "loopdev specific options detected")); |
7f8b2bf3 | 46 | return 1; |
c8512236 | 47 | } |
7f8b2bf3 | 48 | |
6498ece0 KZ |
49 | if ((cxt->mountflags & (MS_BIND | MS_MOVE)) |
50 | || mnt_context_propagation_only(cxt)) | |
7f8b2bf3 KZ |
51 | return 0; |
52 | ||
c8512236 KZ |
53 | /* Automatically create a loop device from a regular file if a |
54 | * filesystem is not specified or the filesystem is known for libblkid | |
55 | * (these filesystems work with block devices only). The file size | |
d58b3157 OO |
56 | * should be at least 1KiB, otherwise we will create an empty loopdev with |
57 | * no mountable filesystem... | |
7f8b2bf3 | 58 | * |
d58b3157 | 59 | * Note that there is no restriction (on kernel side) that would prevent a regular |
7f8b2bf3 KZ |
60 | * file as a mount(2) source argument. A filesystem that is able to mount |
61 | * regular files could be implemented. | |
62 | */ | |
63 | type = mnt_fs_get_fstype(cxt->fs); | |
7f8b2bf3 | 64 | |
ac8697d6 | 65 | if (mnt_fs_is_regularfs(cxt->fs) && |
7f8b2bf3 KZ |
66 | (!type || strcmp(type, "auto") == 0 || blkid_known_fstype(type))) { |
67 | struct stat st; | |
68 | ||
c8512236 | 69 | if (stat(src, &st) == 0 && S_ISREG(st.st_mode) && |
e8cd1819 | 70 | st.st_size > 1024) { |
7bbde59c | 71 | DBG(LOOP, ul_debugobj(cxt, "automatically enabling loop= option")); |
e8cd1819 KZ |
72 | cxt->user_mountflags |= MNT_MS_LOOP; |
73 | mnt_optstr_append_option(&cxt->fs->user_optstr, "loop", NULL); | |
c8512236 | 74 | return 1; |
e8cd1819 | 75 | } |
7f8b2bf3 KZ |
76 | } |
77 | ||
c8512236 | 78 | return 0; |
7f8b2bf3 KZ |
79 | } |
80 | ||
8b470b20 | 81 | |
d58b3157 | 82 | /* Check if there already exists a mounted loop device on the mountpoint node |
8b470b20 KZ |
83 | * with the same parameters. |
84 | */ | |
ba2bdf41 KZ |
85 | static int __attribute__((nonnull)) |
86 | is_mounted_same_loopfile(struct libmnt_context *cxt, | |
8b470b20 KZ |
87 | const char *target, |
88 | const char *backing_file, | |
89 | uint64_t offset) | |
90 | { | |
91 | struct libmnt_table *tb; | |
92 | struct libmnt_iter itr; | |
93 | struct libmnt_fs *fs; | |
94 | struct libmnt_cache *cache; | |
68c41a5f KZ |
95 | const char *bf; |
96 | int rc = 0; | |
cddd2eaa | 97 | struct libmnt_ns *ns_old; |
8b470b20 KZ |
98 | |
99 | assert(cxt); | |
100 | assert(cxt->fs); | |
101 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); | |
102 | ||
372d410d | 103 | if (mnt_context_get_mtab(cxt, &tb)) |
8b470b20 KZ |
104 | return 0; |
105 | ||
cddd2eaa VD |
106 | ns_old = mnt_context_switch_target_ns(cxt); |
107 | if (!ns_old) | |
108 | return -MNT_ERR_NAMESPACE; | |
109 | ||
7bbde59c | 110 | DBG(LOOP, ul_debugobj(cxt, "checking if %s mounted on %s", |
8b470b20 KZ |
111 | backing_file, target)); |
112 | ||
113 | cache = mnt_context_get_cache(cxt); | |
114 | mnt_reset_iter(&itr, MNT_ITER_BACKWARD); | |
115 | ||
68c41a5f KZ |
116 | bf = cache ? mnt_resolve_path(backing_file, cache) : backing_file; |
117 | ||
d58b3157 | 118 | /* Search for a mountpoint node in mtab, proceed if any of these have the |
8b470b20 KZ |
119 | * loop option set or the device is a loop device |
120 | */ | |
68c41a5f | 121 | while (rc == 0 && mnt_table_next_fs(tb, &itr, &fs) == 0) { |
8b470b20 KZ |
122 | const char *src = mnt_fs_get_source(fs); |
123 | const char *opts = mnt_fs_get_user_options(fs); | |
124 | char *val; | |
125 | size_t len; | |
8b470b20 KZ |
126 | |
127 | if (!src || !mnt_fs_match_target(fs, target, cache)) | |
128 | continue; | |
129 | ||
68c41a5f KZ |
130 | rc = 0; |
131 | ||
8b470b20 | 132 | if (strncmp(src, "/dev/loop", 9) == 0) { |
74a4705a | 133 | rc = loopdev_is_used((char *) src, bf, offset, 0, LOOPDEV_FL_OFFSET); |
8b470b20 KZ |
134 | |
135 | } else if (opts && (cxt->user_mountflags & MNT_MS_LOOP) && | |
136 | mnt_optstr_get_option(opts, "loop", &val, &len) == 0 && val) { | |
137 | ||
138 | val = strndup(val, len); | |
74a4705a | 139 | rc = loopdev_is_used((char *) val, bf, offset, 0, LOOPDEV_FL_OFFSET); |
8b470b20 KZ |
140 | free(val); |
141 | } | |
8b470b20 | 142 | } |
68c41a5f | 143 | if (rc) |
7bbde59c | 144 | DBG(LOOP, ul_debugobj(cxt, "%s already mounted", backing_file)); |
cddd2eaa VD |
145 | |
146 | if (!mnt_context_switch_ns(cxt, ns_old)) | |
147 | return -MNT_ERR_NAMESPACE; | |
68c41a5f | 148 | return rc; |
8b470b20 KZ |
149 | } |
150 | ||
7f8b2bf3 KZ |
151 | int mnt_context_setup_loopdev(struct libmnt_context *cxt) |
152 | { | |
f0d3ff0a | 153 | const char *backing_file, *optstr, *loopdev = NULL; |
6e258026 | 154 | char *val = NULL, *loopval = NULL; |
7f8b2bf3 KZ |
155 | size_t len; |
156 | struct loopdev_cxt lc; | |
b9fd3340 KZ |
157 | int rc = 0, lo_flags = 0; |
158 | uint64_t offset = 0, sizelimit = 0; | |
7b46647d | 159 | bool reuse = FALSE; |
7f8b2bf3 | 160 | |
98d391c0 | 161 | assert(cxt); |
7f8b2bf3 KZ |
162 | assert(cxt->fs); |
163 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); | |
164 | ||
165 | backing_file = mnt_fs_get_srcpath(cxt->fs); | |
166 | if (!backing_file) | |
167 | return -EINVAL; | |
168 | ||
7bbde59c | 169 | DBG(LOOP, ul_debugobj(cxt, "trying to setup device for %s", backing_file)); |
7f8b2bf3 KZ |
170 | |
171 | if (cxt->mountflags & MS_RDONLY) { | |
7bbde59c | 172 | DBG(LOOP, ul_debugobj(cxt, "enabling READ-ONLY flag")); |
7f8b2bf3 KZ |
173 | lo_flags |= LO_FLAGS_READ_ONLY; |
174 | } | |
365e5a7c | 175 | |
b9fd3340 | 176 | optstr = mnt_fs_get_user_options(cxt->fs); |
7f8b2bf3 | 177 | |
b9fd3340 KZ |
178 | /* |
179 | * loop= | |
180 | */ | |
181 | if (rc == 0 && (cxt->user_mountflags & MNT_MS_LOOP) && | |
182 | mnt_optstr_get_option(optstr, "loop", &val, &len) == 0 && val) { | |
6e258026 SB |
183 | loopval = strndup(val, len); |
184 | rc = loopval ? 0 : -ENOMEM; | |
b9fd3340 KZ |
185 | } |
186 | ||
187 | /* | |
188 | * offset= | |
189 | */ | |
190 | if (rc == 0 && (cxt->user_mountflags & MNT_MS_OFFSET) && | |
191 | mnt_optstr_get_option(optstr, "offset", &val, &len) == 0) { | |
192 | rc = mnt_parse_offset(val, len, &offset); | |
61f5ff6c | 193 | if (rc) { |
7bbde59c | 194 | DBG(LOOP, ul_debugobj(cxt, "failed to parse offset=")); |
61f5ff6c KZ |
195 | rc = -MNT_ERR_MOUNTOPT; |
196 | } | |
7f8b2bf3 KZ |
197 | } |
198 | ||
b9fd3340 KZ |
199 | /* |
200 | * sizelimit= | |
201 | */ | |
202 | if (rc == 0 && (cxt->user_mountflags & MNT_MS_SIZELIMIT) && | |
203 | mnt_optstr_get_option(optstr, "sizelimit", &val, &len) == 0) { | |
204 | rc = mnt_parse_offset(val, len, &sizelimit); | |
61f5ff6c | 205 | if (rc) { |
7bbde59c | 206 | DBG(LOOP, ul_debugobj(cxt, "failed to parse sizelimit=")); |
61f5ff6c KZ |
207 | rc = -MNT_ERR_MOUNTOPT; |
208 | } | |
b9fd3340 KZ |
209 | } |
210 | ||
1a7a421e KZ |
211 | /* |
212 | * encryption= | |
213 | */ | |
214 | if (rc == 0 && (cxt->user_mountflags & MNT_MS_ENCRYPTION) && | |
215 | mnt_optstr_get_option(optstr, "encryption", &val, &len) == 0) { | |
7bbde59c | 216 | DBG(LOOP, ul_debugobj(cxt, "encryption no longer supported")); |
5cf05c71 | 217 | rc = -MNT_ERR_MOUNTOPT; |
1a7a421e KZ |
218 | } |
219 | ||
8b470b20 KZ |
220 | if (rc == 0 && is_mounted_same_loopfile(cxt, |
221 | mnt_context_get_target(cxt), | |
222 | backing_file, offset)) | |
223 | rc = -EBUSY; | |
224 | ||
6e258026 SB |
225 | if (rc) |
226 | goto done_no_deinit; | |
227 | ||
bdf46c4d SB |
228 | /* It is possible to mount the same file more times. If we set more |
229 | * than one loop device referring to the same file, kernel has no | |
230 | * mechanism to detect it. To prevent data corruption, the same loop | |
231 | * device has to be recycled. | |
232 | */ | |
8efad715 SB |
233 | if (backing_file) { |
234 | rc = loopcxt_init(&lc, 0); | |
235 | if (rc) | |
236 | goto done_no_deinit; | |
237 | ||
dff7e160 KZ |
238 | rc = loopcxt_find_overlap(&lc, backing_file, offset, sizelimit); |
239 | switch (rc) { | |
240 | case 0: /* not found */ | |
73afd3f8 | 241 | DBG(LOOP, ul_debugobj(cxt, "not found overlapping loopdev")); |
dff7e160 KZ |
242 | loopcxt_deinit(&lc); |
243 | break; | |
244 | ||
245 | case 1: /* overlap */ | |
73afd3f8 | 246 | DBG(LOOP, ul_debugobj(cxt, "overlapping %s detected", |
dff7e160 KZ |
247 | loopcxt_get_device(&lc))); |
248 | rc = -MNT_ERR_LOOPOVERLAP; | |
8efad715 | 249 | goto done; |
dff7e160 KZ |
250 | |
251 | case 2: /* overlap -- full size and offset match (reuse) */ | |
252 | { | |
22a64b02 | 253 | uint32_t lc_encrypt_type = 0; |
8efad715 | 254 | |
dff7e160 | 255 | DBG(LOOP, ul_debugobj(cxt, "re-using existing loop device %s", |
8efad715 SB |
256 | loopcxt_get_device(&lc))); |
257 | ||
3e1fc3bb JK |
258 | /* Open loop device to block device autoclear... */ |
259 | if (loopcxt_get_fd(&lc) < 0) { | |
260 | DBG(LOOP, ul_debugobj(cxt, "failed to get loopdev FD")); | |
261 | rc = -errno; | |
262 | goto done; | |
263 | } | |
264 | ||
265 | /* | |
266 | * Now that we certainly have the loop device open, | |
267 | * verify the loop device was not autocleared in the | |
268 | * mean time. | |
269 | */ | |
270 | if (!loopcxt_get_info(&lc)) { | |
271 | DBG(LOOP, ul_debugobj(cxt, "lost race with %s teardown", | |
272 | loopcxt_get_device(&lc))); | |
273 | loopcxt_deinit(&lc); | |
274 | break; | |
275 | } | |
276 | ||
8efad715 | 277 | /* Once a loop is initialized RO, there is no |
dff7e160 KZ |
278 | * way to change its parameters. */ |
279 | if (loopcxt_is_readonly(&lc) | |
280 | && !(lo_flags & LO_FLAGS_READ_ONLY)) { | |
281 | DBG(LOOP, ul_debugobj(cxt, "%s is read-only", | |
282 | loopcxt_get_device(&lc))); | |
8efad715 SB |
283 | rc = -EROFS; |
284 | goto done; | |
285 | } | |
286 | ||
dff7e160 KZ |
287 | /* This is no more supported, but check to be safe. */ |
288 | if (loopcxt_get_encrypt_type(&lc, &lc_encrypt_type) == 0 | |
289 | && lc_encrypt_type != LO_CRYPT_NONE) { | |
8efad715 SB |
290 | DBG(LOOP, ul_debugobj(cxt, "encryption no longer supported for device %s", |
291 | loopcxt_get_device(&lc))); | |
292 | rc = -MNT_ERR_LOOPOVERLAP; | |
293 | goto done; | |
294 | } | |
dff7e160 | 295 | rc = 0; |
3789806d SB |
296 | /* loop= used with argument. Conflict will occur. */ |
297 | if (loopval) { | |
298 | rc = -MNT_ERR_LOOPOVERLAP; | |
299 | goto done; | |
7b46647d SB |
300 | } else { |
301 | reuse = TRUE; | |
3789806d | 302 | goto success; |
7b46647d | 303 | } |
8efad715 | 304 | } |
dff7e160 | 305 | default: /* error */ |
bdf46c4d SB |
306 | goto done; |
307 | } | |
bdf46c4d | 308 | } |
bdf46c4d | 309 | |
dff7e160 | 310 | DBG(LOOP, ul_debugobj(cxt, "not found; create a new loop device")); |
6e258026 | 311 | rc = loopcxt_init(&lc, 0); |
441cdba9 SB |
312 | if (rc) |
313 | goto done_no_deinit; | |
314 | if (loopval) { | |
6e258026 SB |
315 | rc = loopcxt_set_device(&lc, loopval); |
316 | if (rc == 0) | |
317 | loopdev = loopcxt_get_device(&lc); | |
318 | } | |
b9fd3340 KZ |
319 | if (rc) |
320 | goto done; | |
321 | ||
7f8b2bf3 KZ |
322 | /* since 2.6.37 we don't have to store backing filename to mtab |
323 | * because kernel provides the name in /sys. | |
324 | */ | |
325 | if (get_linux_version() >= KERNEL_VERSION(2, 6, 37) || | |
150e696d | 326 | !mnt_context_mtab_writable(cxt)) { |
7bbde59c | 327 | DBG(LOOP, ul_debugobj(cxt, "enabling AUTOCLEAR flag")); |
7f8b2bf3 KZ |
328 | lo_flags |= LO_FLAGS_AUTOCLEAR; |
329 | } | |
330 | ||
331 | do { | |
332 | /* found free device */ | |
333 | if (!loopdev) { | |
334 | rc = loopcxt_find_unused(&lc); | |
335 | if (rc) | |
336 | goto done; | |
7bbde59c | 337 | DBG(LOOP, ul_debugobj(cxt, "trying to use %s", |
7f8b2bf3 KZ |
338 | loopcxt_get_device(&lc))); |
339 | } | |
340 | ||
b9fd3340 KZ |
341 | /* set device attributes |
342 | * -- note that loopcxt_find_unused() resets "lc" | |
343 | */ | |
7f8b2bf3 | 344 | rc = loopcxt_set_backing_file(&lc, backing_file); |
7f8b2bf3 | 345 | |
b9fd3340 KZ |
346 | if (!rc && offset) |
347 | rc = loopcxt_set_offset(&lc, offset); | |
348 | if (!rc && sizelimit) | |
349 | rc = loopcxt_set_sizelimit(&lc, sizelimit); | |
350 | if (!rc) | |
351 | loopcxt_set_flags(&lc, lo_flags); | |
352 | if (rc) { | |
7bbde59c | 353 | DBG(LOOP, ul_debugobj(cxt, "failed to set loop attributes")); |
b9fd3340 KZ |
354 | goto done; |
355 | } | |
7f8b2bf3 KZ |
356 | |
357 | /* setup the device */ | |
358 | rc = loopcxt_setup_device(&lc); | |
359 | if (!rc) | |
360 | break; /* success */ | |
361 | ||
362 | if (loopdev || rc != -EBUSY) { | |
7bbde59c | 363 | DBG(LOOP, ul_debugobj(cxt, "failed to setup device")); |
82756a74 | 364 | rc = -MNT_ERR_LOOPDEV; |
b9fd3340 | 365 | goto done; |
7f8b2bf3 | 366 | } |
7bbde59c | 367 | DBG(LOOP, ul_debugobj(cxt, "device stolen...trying again")); |
7f8b2bf3 KZ |
368 | } while (1); |
369 | ||
bdf46c4d | 370 | success: |
7f8b2bf3 KZ |
371 | if (!rc) |
372 | rc = mnt_fs_set_source(cxt->fs, loopcxt_get_device(&lc)); | |
373 | ||
374 | if (!rc) { | |
375 | /* success */ | |
376 | cxt->flags |= MNT_FL_LOOPDEV_READY; | |
377 | ||
7b46647d SB |
378 | if (reuse || ( (cxt->user_mountflags & MNT_MS_LOOP) && |
379 | loopcxt_is_autoclear(&lc))) { | |
7f8b2bf3 | 380 | /* |
d58b3157 | 381 | * autoclear flag accepted by the kernel, don't store |
7f8b2bf3 KZ |
382 | * the "loop=" option to mtab. |
383 | */ | |
7bbde59c | 384 | DBG(LOOP, ul_debugobj(cxt, "removing unnecessary loop= from mtab")); |
7f8b2bf3 | 385 | cxt->user_mountflags &= ~MNT_MS_LOOP; |
f0d3ff0a KZ |
386 | mnt_optstr_remove_option(&cxt->fs->user_optstr, "loop"); |
387 | } | |
7f8b2bf3 KZ |
388 | |
389 | if (!(cxt->mountflags & MS_RDONLY) && | |
390 | loopcxt_is_readonly(&lc)) | |
391 | /* | |
392 | * mount planned read-write, but loopdev is read-only, | |
393 | * let's fix mount options... | |
394 | */ | |
f9906424 | 395 | mnt_context_set_mflags(cxt, cxt->mountflags | MS_RDONLY); |
7f8b2bf3 KZ |
396 | |
397 | /* we have to keep the device open until mount(1), | |
d58b3157 | 398 | * otherwise it will be auto-cleared by kernel |
7f8b2bf3 KZ |
399 | */ |
400 | cxt->loopdev_fd = loopcxt_get_fd(&lc); | |
dff7e160 KZ |
401 | if (cxt->loopdev_fd < 0) { |
402 | DBG(LOOP, ul_debugobj(cxt, "failed to get loopdev FD")); | |
403 | rc = -errno; | |
404 | } else | |
405 | loopcxt_set_fd(&lc, -1, 0); | |
7f8b2bf3 KZ |
406 | } |
407 | done: | |
408 | loopcxt_deinit(&lc); | |
6e258026 SB |
409 | done_no_deinit: |
410 | free(loopval); | |
7f8b2bf3 KZ |
411 | return rc; |
412 | } | |
413 | ||
414 | /* | |
415 | * Deletes loop device | |
416 | */ | |
417 | int mnt_context_delete_loopdev(struct libmnt_context *cxt) | |
418 | { | |
419 | const char *src; | |
420 | int rc; | |
421 | ||
422 | assert(cxt); | |
423 | assert(cxt->fs); | |
424 | ||
425 | src = mnt_fs_get_srcpath(cxt->fs); | |
426 | if (!src) | |
427 | return -EINVAL; | |
428 | ||
429 | if (cxt->loopdev_fd > -1) | |
430 | close(cxt->loopdev_fd); | |
431 | ||
432 | rc = loopdev_delete(src); | |
433 | cxt->flags &= ~MNT_FL_LOOPDEV_READY; | |
434 | cxt->loopdev_fd = -1; | |
435 | ||
7bbde59c | 436 | DBG(LOOP, ul_debugobj(cxt, "deleted [rc=%d]", rc)); |
7f8b2bf3 KZ |
437 | return rc; |
438 | } | |
439 | ||
440 | /* | |
441 | * Clears loopdev stuff in context, should be called after | |
442 | * failed or successful mount(2). | |
443 | */ | |
444 | int mnt_context_clear_loopdev(struct libmnt_context *cxt) | |
445 | { | |
446 | assert(cxt); | |
447 | ||
448 | if (mnt_context_get_status(cxt) == 0 && | |
449 | (cxt->flags & MNT_FL_LOOPDEV_READY)) { | |
450 | /* | |
451 | * mount(2) failed, delete loopdev | |
452 | */ | |
453 | mnt_context_delete_loopdev(cxt); | |
454 | ||
455 | } else if (cxt->loopdev_fd > -1) { | |
456 | /* | |
457 | * mount(2) success, close the device | |
458 | */ | |
7bbde59c | 459 | DBG(LOOP, ul_debugobj(cxt, "closing FD")); |
7f8b2bf3 KZ |
460 | close(cxt->loopdev_fd); |
461 | } | |
462 | cxt->loopdev_fd = -1; | |
463 | return 0; | |
464 | } | |
465 |