1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2010 ProFUSION embedded systems
6 #include <linux/loop.h>
10 #if HAVE_VALGRIND_MEMCHECK_H
11 #include <valgrind/memcheck.h>
14 #include "sd-device.h"
16 #include "alloc-util.h"
17 #include "blockdev-util.h"
18 #include "detach-loopback.h"
19 #include "device-util.h"
22 typedef struct LoopbackDevice
{
25 LIST_FIELDS(struct LoopbackDevice
, loopback_device
);
28 static void loopback_device_free(LoopbackDevice
**head
, LoopbackDevice
*m
) {
32 LIST_REMOVE(loopback_device
, *head
, m
);
38 static void loopback_device_list_free(LoopbackDevice
**head
) {
42 loopback_device_free(head
, *head
);
45 static int loopback_list_get(LoopbackDevice
**head
) {
46 _cleanup_(sd_device_enumerator_unrefp
) sd_device_enumerator
*e
= NULL
;
52 r
= sd_device_enumerator_new(&e
);
56 r
= sd_device_enumerator_allow_uninitialized(e
);
60 r
= sd_device_enumerator_add_match_subsystem(e
, "block", true);
64 r
= sd_device_enumerator_add_match_sysname(e
, "loop*");
68 r
= sd_device_enumerator_add_match_sysattr(e
, "loop/backing_file", NULL
, true);
72 FOREACH_DEVICE(e
, d
) {
73 _cleanup_free_
char *p
= NULL
;
78 if (sd_device_get_devnum(d
, &devnum
) < 0 ||
79 sd_device_get_devname(d
, &dn
) < 0)
86 lb
= new(LoopbackDevice
, 1);
90 *lb
= (LoopbackDevice
) {
95 LIST_PREPEND(loopback_device
, *head
, lb
);
101 static int delete_loopback(const char *device
) {
102 _cleanup_close_
int fd
= -EBADF
;
103 struct loop_info64 info
;
107 fd
= open(device
, O_RDONLY
|O_CLOEXEC
);
109 log_debug_errno(errno
, "Failed to open loopback device %s: %m", device
);
110 return errno
== ENOENT
? 0 : -errno
;
113 /* Loopback block devices don't sync in-flight blocks when we clear the fd, hence sync explicitly
116 log_debug_errno(errno
, "Failed to sync loop block device %s, ignoring: %m", device
);
118 if (ioctl(fd
, LOOP_CLR_FD
, 0) < 0) {
119 if (errno
== ENXIO
) /* Nothing bound, didn't do anything */
123 return log_debug_errno(errno
, "Failed to clear loopback device %s: %m", device
);
125 if (ioctl(fd
, LOOP_GET_STATUS64
, &info
) < 0) {
126 if (errno
== ENXIO
) /* What? Suddenly detached after all? That's fine by us then. */
129 log_debug_errno(errno
, "Failed to invoke LOOP_GET_STATUS64 on loopback device %s, ignoring: %m", device
);
130 return -EBUSY
; /* propagate original error */
133 #if HAVE_VALGRIND_MEMCHECK_H
134 VALGRIND_MAKE_MEM_DEFINED(&info
, sizeof(info
));
137 if (FLAGS_SET(info
.lo_flags
, LO_FLAGS_AUTOCLEAR
)) /* someone else already set LO_FLAGS_AUTOCLEAR for us? fine by us */
138 return -EBUSY
; /* propagate original error */
140 info
.lo_flags
|= LO_FLAGS_AUTOCLEAR
;
141 if (ioctl(fd
, LOOP_SET_STATUS64
, &info
) < 0) {
142 if (errno
== ENXIO
) /* Suddenly detached after all? Fine by us */
145 log_debug_errno(errno
, "Failed to set LO_FLAGS_AUTOCLEAR flag for loop device %s, ignoring: %m", device
);
147 log_debug("Successfully set LO_FLAGS_AUTOCLEAR flag for loop device %s.", device
);
152 if (ioctl(fd
, LOOP_GET_STATUS64
, &info
) < 0) {
153 /* If the LOOP_CLR_FD above succeeded we'll see ENXIO here. */
155 log_debug("Successfully detached loopback device %s.", device
);
157 log_debug_errno(errno
, "Failed to invoke LOOP_GET_STATUS64 on loopback device %s, ignoring: %m", device
); /* the LOOP_CLR_FD at least worked, let's hope for the best */
162 #if HAVE_VALGRIND_MEMCHECK_H
163 VALGRIND_MAKE_MEM_DEFINED(&info
, sizeof(info
));
166 /* Linux makes LOOP_CLR_FD succeed whenever LO_FLAGS_AUTOCLEAR is set without actually doing
167 * anything. Very confusing. Let's hence not claim we did anything in this case. */
168 if (FLAGS_SET(info
.lo_flags
, LO_FLAGS_AUTOCLEAR
))
169 log_debug("Successfully called LOOP_CLR_FD on a loopback device %s with autoclear set, which is a NOP.", device
);
171 log_debug("Weird, LOOP_CLR_FD succeeded but the device is still attached on %s.", device
);
173 return -EBUSY
; /* Nothing changed, the device is still attached, hence it apparently is still busy */
176 static int loopback_points_list_detach(LoopbackDevice
**head
, bool *changed
, bool last_try
) {
178 dev_t rootdev
= 0, usrdev
= 0;
183 (void) get_block_device_harder("/", &rootdev
);
184 (void) block_get_whole_disk(rootdev
, &rootdev
);
186 (void) get_block_device_harder("/usr", &usrdev
);
187 (void) block_get_whole_disk(usrdev
, &usrdev
);
189 LIST_FOREACH(loopback_device
, m
, *head
) {
190 if ((major(rootdev
) != 0 && rootdev
== m
->devnum
) ||
191 (major(usrdev
) != 0 && usrdev
== m
->devnum
)) {
192 log_debug("Not detaching loopback device %s that backs the OS itself, skipping.", m
->path
);
197 log_info("Detaching loopback %s.", m
->path
);
198 r
= delete_loopback(m
->path
);
200 log_full_errno(last_try
? LOG_ERR
: LOG_INFO
, r
, "Could not detach loopback %s: %m", m
->path
);
207 loopback_device_free(head
, m
);
213 int loopback_detach_all(bool *changed
, bool last_try
) {
214 _cleanup_(loopback_device_list_free
) LIST_HEAD(LoopbackDevice
, loopback_list_head
);
219 LIST_HEAD_INIT(loopback_list_head
);
221 r
= loopback_list_get(&loopback_list_head
);
225 return loopback_points_list_detach(&loopback_list_head
, changed
, last_try
);