]>
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 | * |
3611b10c | 5 | * Copyright (C) 2011-2022 Karel Zak <kzak@redhat.com> |
2c37ca7c KZ |
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. | |
7f014eda KZ |
11 | * |
12 | * Please, see the comment in libmount/src/hooks.c to understand how hooks work. | |
7f8b2bf3 | 13 | */ |
7f8b2bf3 | 14 | #include <blkid.h> |
7b46647d | 15 | #include <stdbool.h> |
7f8b2bf3 KZ |
16 | |
17 | #include "mountP.h" | |
18 | #include "loopdev.h" | |
93c68844 | 19 | #include "strutils.h" |
7f8b2bf3 KZ |
20 | #include "linux_version.h" |
21 | ||
3611b10c KZ |
22 | struct hook_data { |
23 | int loopdev_fd; | |
24 | }; | |
7f8b2bf3 | 25 | |
3611b10c KZ |
26 | /* de-initiallize this module */ |
27 | static int hookset_deinit(struct libmnt_context *cxt, const struct libmnt_hookset *hs) | |
28 | { | |
29 | void *data; | |
93c68844 | 30 | |
3611b10c | 31 | DBG(HOOK, ul_debugobj(hs, "deinit '%s'", hs->name)); |
7f8b2bf3 | 32 | |
3611b10c KZ |
33 | /* remove all our hooks */ |
34 | while (mnt_context_remove_hook(cxt, hs, 0, &data) == 0) { | |
35 | free(data); | |
36 | data = NULL; | |
c8512236 | 37 | } |
7f8b2bf3 | 38 | |
3611b10c KZ |
39 | return 0; |
40 | } | |
7f8b2bf3 | 41 | |
3611b10c KZ |
42 | static inline struct hook_data *new_hook_data(void) |
43 | { | |
44 | struct hook_data *hd = calloc(1, sizeof(*hd)); | |
93c68844 | 45 | |
3611b10c KZ |
46 | if (!hd) |
47 | return NULL; | |
7f8b2bf3 | 48 | |
3611b10c KZ |
49 | hd->loopdev_fd = -1; |
50 | return hd; | |
7f8b2bf3 KZ |
51 | } |
52 | ||
d58b3157 | 53 | /* Check if there already exists a mounted loop device on the mountpoint node |
8b470b20 KZ |
54 | * with the same parameters. |
55 | */ | |
ba2bdf41 KZ |
56 | static int __attribute__((nonnull)) |
57 | is_mounted_same_loopfile(struct libmnt_context *cxt, | |
8b470b20 KZ |
58 | const char *target, |
59 | const char *backing_file, | |
60 | uint64_t offset) | |
61 | { | |
dabfab0e | 62 | struct libmnt_table *tb = NULL; |
8b470b20 KZ |
63 | struct libmnt_iter itr; |
64 | struct libmnt_fs *fs; | |
65 | struct libmnt_cache *cache; | |
68c41a5f KZ |
66 | const char *bf; |
67 | int rc = 0; | |
cddd2eaa | 68 | struct libmnt_ns *ns_old; |
93c68844 | 69 | unsigned long flags = 0; |
8b470b20 KZ |
70 | |
71 | assert(cxt); | |
72 | assert(cxt->fs); | |
73 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); | |
74 | ||
e9d52e6e | 75 | if (mnt_context_get_mountinfo(cxt, &tb)) |
8b470b20 KZ |
76 | return 0; |
77 | ||
cddd2eaa VD |
78 | ns_old = mnt_context_switch_target_ns(cxt); |
79 | if (!ns_old) | |
80 | return -MNT_ERR_NAMESPACE; | |
81 | ||
7bbde59c | 82 | DBG(LOOP, ul_debugobj(cxt, "checking if %s mounted on %s", |
8b470b20 KZ |
83 | backing_file, target)); |
84 | ||
93c68844 KZ |
85 | rc = mnt_context_get_user_mflags(cxt, &flags); |
86 | if (rc) | |
87 | return 0; | |
88 | ||
8b470b20 KZ |
89 | cache = mnt_context_get_cache(cxt); |
90 | mnt_reset_iter(&itr, MNT_ITER_BACKWARD); | |
91 | ||
68c41a5f KZ |
92 | bf = cache ? mnt_resolve_path(backing_file, cache) : backing_file; |
93 | ||
e9d52e6e | 94 | /* Search for a mountpoint node in mountinfo, proceed if any of these have the |
8b470b20 KZ |
95 | * loop option set or the device is a loop device |
96 | */ | |
68c41a5f | 97 | while (rc == 0 && mnt_table_next_fs(tb, &itr, &fs) == 0) { |
8b470b20 KZ |
98 | const char *src = mnt_fs_get_source(fs); |
99 | const char *opts = mnt_fs_get_user_options(fs); | |
100 | char *val; | |
101 | size_t len; | |
8b470b20 KZ |
102 | |
103 | if (!src || !mnt_fs_match_target(fs, target, cache)) | |
104 | continue; | |
105 | ||
68c41a5f KZ |
106 | rc = 0; |
107 | ||
8b470b20 | 108 | if (strncmp(src, "/dev/loop", 9) == 0) { |
74a4705a | 109 | rc = loopdev_is_used((char *) src, bf, offset, 0, LOOPDEV_FL_OFFSET); |
8b470b20 | 110 | |
93c68844 | 111 | } else if (opts && (flags & MNT_MS_LOOP) && |
8b470b20 KZ |
112 | mnt_optstr_get_option(opts, "loop", &val, &len) == 0 && val) { |
113 | ||
114 | val = strndup(val, len); | |
74a4705a | 115 | rc = loopdev_is_used((char *) val, bf, offset, 0, LOOPDEV_FL_OFFSET); |
8b470b20 KZ |
116 | free(val); |
117 | } | |
8b470b20 | 118 | } |
68c41a5f | 119 | if (rc) |
7bbde59c | 120 | DBG(LOOP, ul_debugobj(cxt, "%s already mounted", backing_file)); |
cddd2eaa VD |
121 | |
122 | if (!mnt_context_switch_ns(cxt, ns_old)) | |
123 | return -MNT_ERR_NAMESPACE; | |
68c41a5f | 124 | return rc; |
8b470b20 KZ |
125 | } |
126 | ||
3611b10c KZ |
127 | static int setup_loopdev(struct libmnt_context *cxt, |
128 | struct libmnt_optlist *ol, struct hook_data *hd) | |
7f8b2bf3 | 129 | { |
93c68844 | 130 | const char *backing_file, *loopdev = NULL; |
7f8b2bf3 | 131 | struct loopdev_cxt lc; |
b9fd3340 KZ |
132 | int rc = 0, lo_flags = 0; |
133 | uint64_t offset = 0, sizelimit = 0; | |
7b46647d | 134 | bool reuse = FALSE; |
93c68844 | 135 | struct libmnt_opt *opt, *loopopt = NULL; |
7f8b2bf3 | 136 | |
7f8b2bf3 KZ |
137 | backing_file = mnt_fs_get_srcpath(cxt->fs); |
138 | if (!backing_file) | |
139 | return -EINVAL; | |
140 | ||
7bbde59c | 141 | DBG(LOOP, ul_debugobj(cxt, "trying to setup device for %s", backing_file)); |
7f8b2bf3 | 142 | |
93c68844 | 143 | if (mnt_optlist_is_rdonly(ol)) { |
7bbde59c | 144 | DBG(LOOP, ul_debugobj(cxt, "enabling READ-ONLY flag")); |
7f8b2bf3 KZ |
145 | lo_flags |= LO_FLAGS_READ_ONLY; |
146 | } | |
365e5a7c | 147 | |
b9fd3340 KZ |
148 | /* |
149 | * loop= | |
150 | */ | |
93c68844 KZ |
151 | if (!rc) |
152 | loopopt = mnt_optlist_get_opt(ol, MNT_MS_LOOP, cxt->map_userspace); | |
b9fd3340 KZ |
153 | |
154 | /* | |
155 | * offset= | |
156 | */ | |
93c68844 KZ |
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)) { | |
7bbde59c | 160 | DBG(LOOP, ul_debugobj(cxt, "failed to parse offset=")); |
61f5ff6c KZ |
161 | rc = -MNT_ERR_MOUNTOPT; |
162 | } | |
7f8b2bf3 KZ |
163 | } |
164 | ||
b9fd3340 KZ |
165 | /* |
166 | * sizelimit= | |
167 | */ | |
93c68844 KZ |
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)) { | |
7bbde59c | 171 | DBG(LOOP, ul_debugobj(cxt, "failed to parse sizelimit=")); |
61f5ff6c KZ |
172 | rc = -MNT_ERR_MOUNTOPT; |
173 | } | |
b9fd3340 KZ |
174 | } |
175 | ||
1a7a421e KZ |
176 | /* |
177 | * encryption= | |
178 | */ | |
93c68844 | 179 | if (!rc && mnt_optlist_get_opt(ol, MNT_MS_ENCRYPTION, cxt->map_userspace)) { |
7bbde59c | 180 | DBG(LOOP, ul_debugobj(cxt, "encryption no longer supported")); |
5cf05c71 | 181 | rc = -MNT_ERR_MOUNTOPT; |
1a7a421e KZ |
182 | } |
183 | ||
93c68844 | 184 | if (!rc && is_mounted_same_loopfile(cxt, |
8b470b20 KZ |
185 | mnt_context_get_target(cxt), |
186 | backing_file, offset)) | |
187 | rc = -EBUSY; | |
188 | ||
6e258026 SB |
189 | if (rc) |
190 | goto done_no_deinit; | |
191 | ||
bdf46c4d SB |
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. | |
196 | */ | |
8efad715 SB |
197 | if (backing_file) { |
198 | rc = loopcxt_init(&lc, 0); | |
199 | if (rc) | |
200 | goto done_no_deinit; | |
201 | ||
dff7e160 KZ |
202 | rc = loopcxt_find_overlap(&lc, backing_file, offset, sizelimit); |
203 | switch (rc) { | |
204 | case 0: /* not found */ | |
73afd3f8 | 205 | DBG(LOOP, ul_debugobj(cxt, "not found overlapping loopdev")); |
dff7e160 KZ |
206 | loopcxt_deinit(&lc); |
207 | break; | |
208 | ||
209 | case 1: /* overlap */ | |
73afd3f8 | 210 | DBG(LOOP, ul_debugobj(cxt, "overlapping %s detected", |
dff7e160 KZ |
211 | loopcxt_get_device(&lc))); |
212 | rc = -MNT_ERR_LOOPOVERLAP; | |
8efad715 | 213 | goto done; |
dff7e160 KZ |
214 | |
215 | case 2: /* overlap -- full size and offset match (reuse) */ | |
216 | { | |
22a64b02 | 217 | uint32_t lc_encrypt_type = 0; |
8efad715 | 218 | |
dff7e160 | 219 | DBG(LOOP, ul_debugobj(cxt, "re-using existing loop device %s", |
8efad715 SB |
220 | loopcxt_get_device(&lc))); |
221 | ||
3e1fc3bb JK |
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")); | |
225 | rc = -errno; | |
226 | goto done; | |
227 | } | |
228 | ||
229 | /* | |
230 | * Now that we certainly have the loop device open, | |
231 | * verify the loop device was not autocleared in the | |
232 | * mean time. | |
233 | */ | |
234 | if (!loopcxt_get_info(&lc)) { | |
235 | DBG(LOOP, ul_debugobj(cxt, "lost race with %s teardown", | |
236 | loopcxt_get_device(&lc))); | |
237 | loopcxt_deinit(&lc); | |
238 | break; | |
239 | } | |
240 | ||
8efad715 | 241 | /* Once a loop is initialized RO, there is no |
dff7e160 KZ |
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))); | |
8efad715 SB |
247 | rc = -EROFS; |
248 | goto done; | |
249 | } | |
250 | ||
dff7e160 KZ |
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) { | |
8efad715 SB |
254 | DBG(LOOP, ul_debugobj(cxt, "encryption no longer supported for device %s", |
255 | loopcxt_get_device(&lc))); | |
256 | rc = -MNT_ERR_LOOPOVERLAP; | |
257 | goto done; | |
258 | } | |
dff7e160 | 259 | rc = 0; |
3789806d | 260 | /* loop= used with argument. Conflict will occur. */ |
93c68844 | 261 | if (mnt_opt_has_value(loopopt)) { |
3789806d SB |
262 | rc = -MNT_ERR_LOOPOVERLAP; |
263 | goto done; | |
7b46647d SB |
264 | } else { |
265 | reuse = TRUE; | |
3789806d | 266 | goto success; |
7b46647d | 267 | } |
8efad715 | 268 | } |
dff7e160 | 269 | default: /* error */ |
bdf46c4d SB |
270 | goto done; |
271 | } | |
bdf46c4d | 272 | } |
bdf46c4d | 273 | |
dff7e160 | 274 | DBG(LOOP, ul_debugobj(cxt, "not found; create a new loop device")); |
6e258026 | 275 | rc = loopcxt_init(&lc, 0); |
441cdba9 SB |
276 | if (rc) |
277 | goto done_no_deinit; | |
93c68844 KZ |
278 | if (mnt_opt_has_value(loopopt)) { |
279 | rc = loopcxt_set_device(&lc, mnt_opt_get_value(loopopt)); | |
6e258026 SB |
280 | if (rc == 0) |
281 | loopdev = loopcxt_get_device(&lc); | |
282 | } | |
b9fd3340 KZ |
283 | if (rc) |
284 | goto done; | |
285 | ||
e9d52e6e | 286 | /* since 2.6.37 we don't have to store backing filename to mountinfo |
7f8b2bf3 KZ |
287 | * because kernel provides the name in /sys. |
288 | */ | |
e9d52e6e | 289 | if (get_linux_version() >= KERNEL_VERSION(2, 6, 37)) { |
7bbde59c | 290 | DBG(LOOP, ul_debugobj(cxt, "enabling AUTOCLEAR flag")); |
7f8b2bf3 KZ |
291 | lo_flags |= LO_FLAGS_AUTOCLEAR; |
292 | } | |
293 | ||
294 | do { | |
295 | /* found free device */ | |
296 | if (!loopdev) { | |
297 | rc = loopcxt_find_unused(&lc); | |
298 | if (rc) | |
299 | goto done; | |
7bbde59c | 300 | DBG(LOOP, ul_debugobj(cxt, "trying to use %s", |
7f8b2bf3 KZ |
301 | loopcxt_get_device(&lc))); |
302 | } | |
303 | ||
b9fd3340 KZ |
304 | /* set device attributes |
305 | * -- note that loopcxt_find_unused() resets "lc" | |
306 | */ | |
7f8b2bf3 | 307 | rc = loopcxt_set_backing_file(&lc, backing_file); |
7f8b2bf3 | 308 | |
b9fd3340 KZ |
309 | if (!rc && offset) |
310 | rc = loopcxt_set_offset(&lc, offset); | |
311 | if (!rc && sizelimit) | |
312 | rc = loopcxt_set_sizelimit(&lc, sizelimit); | |
313 | if (!rc) | |
314 | loopcxt_set_flags(&lc, lo_flags); | |
315 | if (rc) { | |
7bbde59c | 316 | DBG(LOOP, ul_debugobj(cxt, "failed to set loop attributes")); |
b9fd3340 KZ |
317 | goto done; |
318 | } | |
7f8b2bf3 KZ |
319 | |
320 | /* setup the device */ | |
321 | rc = loopcxt_setup_device(&lc); | |
322 | if (!rc) | |
323 | break; /* success */ | |
324 | ||
325 | if (loopdev || rc != -EBUSY) { | |
7bbde59c | 326 | DBG(LOOP, ul_debugobj(cxt, "failed to setup device")); |
82756a74 | 327 | rc = -MNT_ERR_LOOPDEV; |
b9fd3340 | 328 | goto done; |
7f8b2bf3 | 329 | } |
7bbde59c | 330 | DBG(LOOP, ul_debugobj(cxt, "device stolen...trying again")); |
7f8b2bf3 KZ |
331 | } while (1); |
332 | ||
bdf46c4d | 333 | success: |
7f8b2bf3 KZ |
334 | if (!rc) |
335 | rc = mnt_fs_set_source(cxt->fs, loopcxt_get_device(&lc)); | |
336 | ||
337 | if (!rc) { | |
93c68844 | 338 | if (loopopt && (reuse || loopcxt_is_autoclear(&lc))) { |
7f8b2bf3 | 339 | /* |
d58b3157 | 340 | * autoclear flag accepted by the kernel, don't store |
e9d52e6e | 341 | * the "loop=" option to utab. |
7f8b2bf3 | 342 | */ |
e9d52e6e | 343 | DBG(LOOP, ul_debugobj(cxt, "removing unnecessary loop= from utab")); |
93c68844 KZ |
344 | mnt_optlist_remove_opt(ol, loopopt); |
345 | loopopt = NULL; | |
f0d3ff0a | 346 | } |
7f8b2bf3 | 347 | |
93c68844 | 348 | if (!mnt_optlist_is_rdonly(ol) && loopcxt_is_readonly(&lc)) |
7f8b2bf3 KZ |
349 | /* |
350 | * mount planned read-write, but loopdev is read-only, | |
351 | * let's fix mount options... | |
352 | */ | |
93c68844 | 353 | mnt_optlist_append_flags(ol, MS_RDONLY, cxt->map_linux); |
7f8b2bf3 | 354 | |
1cde32f3 JK |
355 | /* |
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. | |
7f8b2bf3 | 361 | */ |
1cde32f3 | 362 | hd->loopdev_fd = open(lc.device, O_RDONLY | O_CLOEXEC); |
3611b10c | 363 | if (hd->loopdev_fd < 0) { |
1cde32f3 JK |
364 | DBG(LOOP, |
365 | ul_debugobj(cxt, "failed to reopen loopdev FD")); | |
dff7e160 | 366 | rc = -errno; |
1cde32f3 | 367 | } |
7f8b2bf3 KZ |
368 | } |
369 | done: | |
370 | loopcxt_deinit(&lc); | |
6e258026 | 371 | done_no_deinit: |
7f8b2bf3 KZ |
372 | return rc; |
373 | } | |
374 | ||
3611b10c | 375 | static int delete_loopdev(struct libmnt_context *cxt, struct hook_data *hd) |
7f8b2bf3 KZ |
376 | { |
377 | const char *src; | |
378 | int rc; | |
379 | ||
380 | assert(cxt); | |
381 | assert(cxt->fs); | |
382 | ||
383 | src = mnt_fs_get_srcpath(cxt->fs); | |
384 | if (!src) | |
385 | return -EINVAL; | |
386 | ||
3611b10c KZ |
387 | if (hd && hd->loopdev_fd > -1) { |
388 | close(hd->loopdev_fd); | |
389 | hd->loopdev_fd = -1; | |
390 | } | |
7f8b2bf3 | 391 | |
3611b10c | 392 | rc = loopdev_delete(src); /* see lib/loopdev.c */ |
7f8b2bf3 | 393 | |
7bbde59c | 394 | DBG(LOOP, ul_debugobj(cxt, "deleted [rc=%d]", rc)); |
7f8b2bf3 KZ |
395 | return rc; |
396 | } | |
397 | ||
3611b10c KZ |
398 | /* Now used by umount until context_umount.c will use hooks toosee */ |
399 | int mnt_context_delete_loopdev(struct libmnt_context *cxt) | |
7f8b2bf3 | 400 | { |
3611b10c KZ |
401 | return delete_loopdev(cxt, NULL); |
402 | } | |
403 | ||
404 | static int is_loopdev_required(struct libmnt_context *cxt, struct libmnt_optlist *ol) | |
405 | { | |
406 | const char *src, *type; | |
407 | unsigned long flags = 0; | |
408 | ||
409 | if (cxt->action != MNT_ACT_MOUNT) | |
410 | return 0; | |
411 | if (!cxt->fs) | |
412 | return 0; | |
413 | if (mnt_optlist_is_bind(ol) | |
414 | || mnt_optlist_is_move(ol) | |
415 | || mnt_context_propagation_only(cxt)) | |
416 | return 0; | |
417 | ||
418 | src = mnt_fs_get_srcpath(cxt->fs); | |
419 | if (!src) | |
420 | return 0; /* backing file not set */ | |
421 | ||
422 | /* userspace flags */ | |
423 | if (mnt_context_get_user_mflags(cxt, &flags)) | |
424 | return 0; | |
425 | ||
426 | if (flags & (MNT_MS_LOOP | MNT_MS_OFFSET | MNT_MS_SIZELIMIT)) { | |
427 | DBG(LOOP, ul_debugobj(cxt, "loopdev specific options detected")); | |
428 | return 1; | |
429 | } | |
430 | ||
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... | |
436 | * | |
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. | |
440 | */ | |
441 | type = mnt_fs_get_fstype(cxt->fs); | |
442 | ||
443 | if (mnt_fs_is_regularfs(cxt->fs) && | |
444 | (!type || strcmp(type, "auto") == 0 || blkid_known_fstype(type))) { | |
445 | struct stat st; | |
446 | ||
447 | if (stat(src, &st) == 0 && S_ISREG(st.st_mode) && | |
448 | st.st_size > 1024) { | |
449 | ||
450 | DBG(LOOP, ul_debugobj(cxt, "automatically enabling loop= option")); | |
451 | mnt_optlist_append_flags(ol, MNT_MS_LOOP, cxt->map_userspace); | |
452 | return 1; | |
453 | } | |
454 | } | |
7f8b2bf3 | 455 | |
3611b10c KZ |
456 | return 0; |
457 | } | |
458 | ||
459 | /* call after mount(2) */ | |
460 | static int hook_cleanup_loopdev( | |
461 | struct libmnt_context *cxt, | |
462 | const struct libmnt_hookset *hs __attribute__((__unused__)), | |
463 | void *data) | |
464 | { | |
465 | struct hook_data *hd = (struct hook_data *) data; | |
466 | ||
467 | if (!hd || hd->loopdev_fd < 0) | |
468 | return 0; | |
469 | ||
470 | if (mnt_context_get_status(cxt) == 0) { | |
7f8b2bf3 KZ |
471 | /* |
472 | * mount(2) failed, delete loopdev | |
473 | */ | |
3611b10c | 474 | delete_loopdev(cxt, hd); |
7f8b2bf3 | 475 | |
3611b10c | 476 | } else { |
7f8b2bf3 KZ |
477 | /* |
478 | * mount(2) success, close the device | |
479 | */ | |
7bbde59c | 480 | DBG(LOOP, ul_debugobj(cxt, "closing FD")); |
3611b10c KZ |
481 | close(hd->loopdev_fd); |
482 | hd->loopdev_fd = -1; | |
7f8b2bf3 | 483 | } |
3611b10c | 484 | |
7f8b2bf3 KZ |
485 | return 0; |
486 | } | |
487 | ||
3611b10c KZ |
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__))) | |
493 | { | |
494 | struct libmnt_optlist *ol; | |
495 | struct hook_data *hd; | |
496 | int rc; | |
497 | ||
498 | assert(cxt); | |
499 | ||
500 | ol = mnt_context_get_optlist(cxt); | |
501 | if (!ol) | |
502 | return -ENOMEM; | |
503 | if (!is_loopdev_required(cxt, ol)) | |
504 | return 0; | |
505 | hd = new_hook_data(); | |
506 | if (!hd) | |
507 | return -ENOMEM; | |
508 | ||
509 | rc = setup_loopdev(cxt, ol, hd); | |
510 | if (!rc) | |
511 | rc = mnt_context_append_hook(cxt, hs, | |
512 | MNT_STAGE_MOUNT_POST, | |
513 | hd, hook_cleanup_loopdev); | |
514 | if (rc) { | |
515 | delete_loopdev(cxt, hd); | |
516 | free(hd); | |
517 | } | |
518 | return rc; | |
519 | } | |
520 | ||
521 | ||
522 | const struct libmnt_hookset hookset_loopdev = | |
523 | { | |
524 | .name = "__loopdev", | |
525 | ||
526 | .firststage = MNT_STAGE_PREP_SOURCE, | |
527 | .firstcall = hook_prepare_loopdev, | |
528 | ||
529 | .deinit = hookset_deinit | |
530 | }; |