1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 * This file is part of libmount from util-linux project.
5 * Copyright (C) 2011-2022 Karel Zak <kzak@redhat.com>
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.
12 * Please, see the comment in libmount/src/hooks.c to understand how hooks work.
20 #include "linux_version.h"
26 /* de-initiallize this module */
27 static int hookset_deinit(struct libmnt_context
*cxt
, const struct libmnt_hookset
*hs
)
31 DBG(HOOK
, ul_debugobj(hs
, "deinit '%s'", hs
->name
));
33 /* remove all our hooks */
34 while (mnt_context_remove_hook(cxt
, hs
, 0, &data
) == 0) {
42 static inline struct hook_data
*new_hook_data(void)
44 struct hook_data
*hd
= calloc(1, sizeof(*hd
));
53 /* Check if there already exists a mounted loop device on the mountpoint node
54 * with the same parameters.
56 static int __attribute__((nonnull
))
57 is_mounted_same_loopfile(struct libmnt_context
*cxt
,
59 const char *backing_file
,
62 struct libmnt_table
*tb
= NULL
;
63 struct libmnt_iter itr
;
65 struct libmnt_cache
*cache
;
68 struct libmnt_ns
*ns_old
;
69 unsigned long flags
= 0;
73 assert((cxt
->flags
& MNT_FL_MOUNTFLAGS_MERGED
));
75 if (mnt_context_get_mountinfo(cxt
, &tb
))
78 ns_old
= mnt_context_switch_target_ns(cxt
);
80 return -MNT_ERR_NAMESPACE
;
82 DBG(LOOP
, ul_debugobj(cxt
, "checking if %s mounted on %s",
83 backing_file
, target
));
85 rc
= mnt_context_get_user_mflags(cxt
, &flags
);
89 cache
= mnt_context_get_cache(cxt
);
90 mnt_reset_iter(&itr
, MNT_ITER_BACKWARD
);
92 bf
= cache
? mnt_resolve_path(backing_file
, cache
) : backing_file
;
94 /* Search for a mountpoint node in mountinfo, proceed if any of these have the
95 * loop option set or the device is a loop device
97 while (rc
== 0 && mnt_table_next_fs(tb
, &itr
, &fs
) == 0) {
98 const char *src
= mnt_fs_get_source(fs
);
99 const char *opts
= mnt_fs_get_user_options(fs
);
103 if (!src
|| !mnt_fs_match_target(fs
, target
, cache
))
108 if (strncmp(src
, "/dev/loop", 9) == 0) {
109 rc
= loopdev_is_used((char *) src
, bf
, offset
, 0, LOOPDEV_FL_OFFSET
);
111 } else if (opts
&& (flags
& MNT_MS_LOOP
) &&
112 mnt_optstr_get_option(opts
, "loop", &val
, &len
) == 0 && val
) {
114 val
= strndup(val
, len
);
115 rc
= loopdev_is_used((char *) val
, bf
, offset
, 0, LOOPDEV_FL_OFFSET
);
120 DBG(LOOP
, ul_debugobj(cxt
, "%s already mounted", backing_file
));
122 if (!mnt_context_switch_ns(cxt
, ns_old
))
123 return -MNT_ERR_NAMESPACE
;
127 static int setup_loopdev(struct libmnt_context
*cxt
,
128 struct libmnt_optlist
*ol
, struct hook_data
*hd
)
130 const char *backing_file
, *loopdev
= NULL
;
131 struct loopdev_cxt lc
;
132 int rc
= 0, lo_flags
= 0;
133 uint64_t offset
= 0, sizelimit
= 0;
135 struct libmnt_opt
*opt
, *loopopt
= NULL
;
137 backing_file
= mnt_fs_get_srcpath(cxt
->fs
);
141 DBG(LOOP
, ul_debugobj(cxt
, "trying to setup device for %s", backing_file
));
143 if (mnt_optlist_is_rdonly(ol
)) {
144 DBG(LOOP
, ul_debugobj(cxt
, "enabling READ-ONLY flag"));
145 lo_flags
|= LO_FLAGS_READ_ONLY
;
152 loopopt
= mnt_optlist_get_opt(ol
, MNT_MS_LOOP
, cxt
->map_userspace
);
157 if (!rc
&& (opt
= mnt_optlist_get_opt(ol
, MNT_MS_OFFSET
, cxt
->map_userspace
))
158 && mnt_opt_has_value(opt
)) {
159 if (strtosize(mnt_opt_get_value(opt
), &offset
)) {
160 DBG(LOOP
, ul_debugobj(cxt
, "failed to parse offset="));
161 rc
= -MNT_ERR_MOUNTOPT
;
168 if (!rc
&& (opt
= mnt_optlist_get_opt(ol
, MNT_MS_SIZELIMIT
, cxt
->map_userspace
))
169 && mnt_opt_has_value(opt
)) {
170 if (strtosize(mnt_opt_get_value(opt
), &sizelimit
)) {
171 DBG(LOOP
, ul_debugobj(cxt
, "failed to parse sizelimit="));
172 rc
= -MNT_ERR_MOUNTOPT
;
179 if (!rc
&& mnt_optlist_get_opt(ol
, MNT_MS_ENCRYPTION
, cxt
->map_userspace
)) {
180 DBG(LOOP
, ul_debugobj(cxt
, "encryption no longer supported"));
181 rc
= -MNT_ERR_MOUNTOPT
;
184 if (!rc
&& is_mounted_same_loopfile(cxt
,
185 mnt_context_get_target(cxt
),
186 backing_file
, offset
))
192 /* It is possible to mount the same file more times. If we set more
193 * than one loop device referring to the same file, kernel has no
194 * mechanism to detect it. To prevent data corruption, the same loop
195 * device has to be recycled.
198 rc
= loopcxt_init(&lc
, 0);
202 rc
= loopcxt_find_overlap(&lc
, backing_file
, offset
, sizelimit
);
204 case 0: /* not found */
205 DBG(LOOP
, ul_debugobj(cxt
, "not found overlapping loopdev"));
209 case 1: /* overlap */
210 DBG(LOOP
, ul_debugobj(cxt
, "overlapping %s detected",
211 loopcxt_get_device(&lc
)));
212 rc
= -MNT_ERR_LOOPOVERLAP
;
215 case 2: /* overlap -- full size and offset match (reuse) */
217 uint32_t lc_encrypt_type
= 0;
219 DBG(LOOP
, ul_debugobj(cxt
, "re-using existing loop device %s",
220 loopcxt_get_device(&lc
)));
222 /* Open loop device to block device autoclear... */
223 if (loopcxt_get_fd(&lc
) < 0) {
224 DBG(LOOP
, ul_debugobj(cxt
, "failed to get loopdev FD"));
230 * Now that we certainly have the loop device open,
231 * verify the loop device was not autocleared in the
234 if (!loopcxt_get_info(&lc
)) {
235 DBG(LOOP
, ul_debugobj(cxt
, "lost race with %s teardown",
236 loopcxt_get_device(&lc
)));
241 /* Once a loop is initialized RO, there is no
242 * way to change its parameters. */
243 if (loopcxt_is_readonly(&lc
)
244 && !(lo_flags
& LO_FLAGS_READ_ONLY
)) {
245 DBG(LOOP
, ul_debugobj(cxt
, "%s is read-only",
246 loopcxt_get_device(&lc
)));
251 /* This is no more supported, but check to be safe. */
252 if (loopcxt_get_encrypt_type(&lc
, &lc_encrypt_type
) == 0
253 && lc_encrypt_type
!= LO_CRYPT_NONE
) {
254 DBG(LOOP
, ul_debugobj(cxt
, "encryption no longer supported for device %s",
255 loopcxt_get_device(&lc
)));
256 rc
= -MNT_ERR_LOOPOVERLAP
;
260 /* loop= used with argument. Conflict will occur. */
261 if (mnt_opt_has_value(loopopt
)) {
262 rc
= -MNT_ERR_LOOPOVERLAP
;
274 DBG(LOOP
, ul_debugobj(cxt
, "not found; create a new loop device"));
275 rc
= loopcxt_init(&lc
, 0);
278 if (mnt_opt_has_value(loopopt
)) {
279 rc
= loopcxt_set_device(&lc
, mnt_opt_get_value(loopopt
));
281 loopdev
= loopcxt_get_device(&lc
);
286 /* since 2.6.37 we don't have to store backing filename to mountinfo
287 * because kernel provides the name in /sys.
289 if (get_linux_version() >= KERNEL_VERSION(2, 6, 37)) {
290 DBG(LOOP
, ul_debugobj(cxt
, "enabling AUTOCLEAR flag"));
291 lo_flags
|= LO_FLAGS_AUTOCLEAR
;
295 /* found free device */
297 rc
= loopcxt_find_unused(&lc
);
300 DBG(LOOP
, ul_debugobj(cxt
, "trying to use %s",
301 loopcxt_get_device(&lc
)));
304 /* set device attributes
305 * -- note that loopcxt_find_unused() resets "lc"
307 rc
= loopcxt_set_backing_file(&lc
, backing_file
);
310 rc
= loopcxt_set_offset(&lc
, offset
);
311 if (!rc
&& sizelimit
)
312 rc
= loopcxt_set_sizelimit(&lc
, sizelimit
);
314 loopcxt_set_flags(&lc
, lo_flags
);
316 DBG(LOOP
, ul_debugobj(cxt
, "failed to set loop attributes"));
320 /* setup the device */
321 rc
= loopcxt_setup_device(&lc
);
325 if (loopdev
|| rc
!= -EBUSY
) {
326 DBG(LOOP
, ul_debugobj(cxt
, "failed to setup device"));
327 rc
= -MNT_ERR_LOOPDEV
;
330 DBG(LOOP
, ul_debugobj(cxt
, "device stolen...trying again"));
335 rc
= mnt_fs_set_source(cxt
->fs
, loopcxt_get_device(&lc
));
338 if (loopopt
&& (reuse
|| loopcxt_is_autoclear(&lc
))) {
340 * autoclear flag accepted by the kernel, don't store
341 * the "loop=" option to utab.
343 DBG(LOOP
, ul_debugobj(cxt
, "removing unnecessary loop= from utab"));
344 mnt_optlist_remove_opt(ol
, loopopt
);
348 if (!mnt_optlist_is_rdonly(ol
) && loopcxt_is_readonly(&lc
))
350 * mount planned read-write, but loopdev is read-only,
351 * let's fix mount options...
353 mnt_optlist_append_flags(ol
, MS_RDONLY
, cxt
->map_linux
);
356 * We have to keep the device open until mount(1), otherwise it
357 * will be auto-cleared by kernel. However we don't want to
358 * keep writeable fd as kernel wants to block all writers to
359 * the device being mounted (in the more hardened
360 * configurations). So grab read-only fd instead.
362 hd
->loopdev_fd
= open(lc
.device
, O_RDONLY
| O_CLOEXEC
);
363 if (hd
->loopdev_fd
< 0) {
365 ul_debugobj(cxt
, "failed to reopen loopdev FD"));
375 static int delete_loopdev(struct libmnt_context
*cxt
, struct hook_data
*hd
)
383 src
= mnt_fs_get_srcpath(cxt
->fs
);
387 if (hd
&& hd
->loopdev_fd
> -1) {
388 close(hd
->loopdev_fd
);
392 rc
= loopdev_delete(src
); /* see lib/loopdev.c */
394 DBG(LOOP
, ul_debugobj(cxt
, "deleted [rc=%d]", rc
));
398 /* Now used by umount until context_umount.c will use hooks toosee */
399 int mnt_context_delete_loopdev(struct libmnt_context
*cxt
)
401 return delete_loopdev(cxt
, NULL
);
404 static int is_loopdev_required(struct libmnt_context
*cxt
, struct libmnt_optlist
*ol
)
406 const char *src
, *type
;
407 unsigned long flags
= 0;
409 if (cxt
->action
!= MNT_ACT_MOUNT
)
413 if (mnt_optlist_is_bind(ol
)
414 || mnt_optlist_is_move(ol
)
415 || mnt_context_propagation_only(cxt
))
418 src
= mnt_fs_get_srcpath(cxt
->fs
);
420 return 0; /* backing file not set */
422 /* userspace flags */
423 if (mnt_context_get_user_mflags(cxt
, &flags
))
426 if (flags
& (MNT_MS_LOOP
| MNT_MS_OFFSET
| MNT_MS_SIZELIMIT
)) {
427 DBG(LOOP
, ul_debugobj(cxt
, "loopdev specific options detected"));
431 /* Automatically create a loop device from a regular file if a
432 * filesystem is not specified or the filesystem is known for libblkid
433 * (these filesystems work with block devices only). The file size
434 * should be at least 1KiB, otherwise we will create an empty loopdev with
435 * no mountable filesystem...
437 * Note that there is no restriction (on kernel side) that would prevent a regular
438 * file as a mount(2) source argument. A filesystem that is able to mount
439 * regular files could be implemented.
441 type
= mnt_fs_get_fstype(cxt
->fs
);
443 if (mnt_fs_is_regularfs(cxt
->fs
) &&
444 (!type
|| strcmp(type
, "auto") == 0 || blkid_known_fstype(type
))) {
447 if (stat(src
, &st
) == 0 && S_ISREG(st
.st_mode
) &&
450 DBG(LOOP
, ul_debugobj(cxt
, "automatically enabling loop= option"));
451 mnt_optlist_append_flags(ol
, MNT_MS_LOOP
, cxt
->map_userspace
);
459 /* call after mount(2) */
460 static int hook_cleanup_loopdev(
461 struct libmnt_context
*cxt
,
462 const struct libmnt_hookset
*hs
__attribute__((__unused__
)),
465 struct hook_data
*hd
= (struct hook_data
*) data
;
467 if (!hd
|| hd
->loopdev_fd
< 0)
470 if (mnt_context_get_status(cxt
) == 0) {
472 * mount(2) failed, delete loopdev
474 delete_loopdev(cxt
, hd
);
478 * mount(2) success, close the device
480 DBG(LOOP
, ul_debugobj(cxt
, "closing FD"));
481 close(hd
->loopdev_fd
);
488 /* call to prepare mount source */
489 static int hook_prepare_loopdev(
490 struct libmnt_context
*cxt
,
491 const struct libmnt_hookset
*hs
,
492 void *data
__attribute__((__unused__
)))
494 struct libmnt_optlist
*ol
;
495 struct hook_data
*hd
;
500 ol
= mnt_context_get_optlist(cxt
);
503 if (!is_loopdev_required(cxt
, ol
))
505 hd
= new_hook_data();
509 rc
= setup_loopdev(cxt
, ol
, hd
);
511 rc
= mnt_context_append_hook(cxt
, hs
,
512 MNT_STAGE_MOUNT_POST
,
513 hd
, hook_cleanup_loopdev
);
515 delete_loopdev(cxt
, hd
);
522 const struct libmnt_hookset hookset_loopdev
=
526 .firststage
= MNT_STAGE_PREP_SOURCE
,
527 .firstcall
= hook_prepare_loopdev
,
529 .deinit
= hookset_deinit