]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shutdown/detach-loopback.c
seccomp: add arm_fadvise64_64 to system-service group
[thirdparty/systemd.git] / src / shutdown / detach-loopback.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2010 ProFUSION embedded systems
4 ***/
5
6 #include <linux/loop.h>
7 #include <sys/ioctl.h>
8 #include <unistd.h>
9
10 #if HAVE_VALGRIND_MEMCHECK_H
11 #include <valgrind/memcheck.h>
12 #endif
13
14 #include "sd-device.h"
15
16 #include "alloc-util.h"
17 #include "blockdev-util.h"
18 #include "detach-loopback.h"
19 #include "device-util.h"
20 #include "fd-util.h"
21
22 typedef struct LoopbackDevice {
23 char *path;
24 dev_t devnum;
25 LIST_FIELDS(struct LoopbackDevice, loopback_device);
26 } LoopbackDevice;
27
28 static void loopback_device_free(LoopbackDevice **head, LoopbackDevice *m) {
29 assert(head);
30 assert(m);
31
32 LIST_REMOVE(loopback_device, *head, m);
33
34 free(m->path);
35 free(m);
36 }
37
38 static void loopback_device_list_free(LoopbackDevice **head) {
39 assert(head);
40
41 while (*head)
42 loopback_device_free(head, *head);
43 }
44
45 static int loopback_list_get(LoopbackDevice **head) {
46 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
47 sd_device *d;
48 int r;
49
50 assert(head);
51
52 r = sd_device_enumerator_new(&e);
53 if (r < 0)
54 return r;
55
56 r = sd_device_enumerator_allow_uninitialized(e);
57 if (r < 0)
58 return r;
59
60 r = sd_device_enumerator_add_match_subsystem(e, "block", true);
61 if (r < 0)
62 return r;
63
64 r = sd_device_enumerator_add_match_sysname(e, "loop*");
65 if (r < 0)
66 return r;
67
68 r = sd_device_enumerator_add_match_sysattr(e, "loop/backing_file", NULL, true);
69 if (r < 0)
70 return r;
71
72 FOREACH_DEVICE(e, d) {
73 _cleanup_free_ char *p = NULL;
74 const char *dn;
75 LoopbackDevice *lb;
76 dev_t devnum;
77
78 if (sd_device_get_devnum(d, &devnum) < 0 ||
79 sd_device_get_devname(d, &dn) < 0)
80 continue;
81
82 p = strdup(dn);
83 if (!p)
84 return -ENOMEM;
85
86 lb = new(LoopbackDevice, 1);
87 if (!lb)
88 return -ENOMEM;
89
90 *lb = (LoopbackDevice) {
91 .path = TAKE_PTR(p),
92 .devnum = devnum,
93 };
94
95 LIST_PREPEND(loopback_device, *head, lb);
96 }
97
98 return 0;
99 }
100
101 static int delete_loopback(const char *device) {
102 _cleanup_close_ int fd = -EBADF;
103 struct loop_info64 info;
104
105 assert(device);
106
107 fd = open(device, O_RDONLY|O_CLOEXEC);
108 if (fd < 0) {
109 log_debug_errno(errno, "Failed to open loopback device %s: %m", device);
110 return errno == ENOENT ? 0 : -errno;
111 }
112
113 /* Loopback block devices don't sync in-flight blocks when we clear the fd, hence sync explicitly
114 * first */
115 if (fsync(fd) < 0)
116 log_debug_errno(errno, "Failed to sync loop block device %s, ignoring: %m", device);
117
118 if (ioctl(fd, LOOP_CLR_FD, 0) < 0) {
119 if (errno == ENXIO) /* Nothing bound, didn't do anything */
120 return 0;
121
122 if (errno != EBUSY)
123 return log_debug_errno(errno, "Failed to clear loopback device %s: %m", device);
124
125 if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) {
126 if (errno == ENXIO) /* What? Suddenly detached after all? That's fine by us then. */
127 return 1;
128
129 log_debug_errno(errno, "Failed to invoke LOOP_GET_STATUS64 on loopback device %s, ignoring: %m", device);
130 return -EBUSY; /* propagate original error */
131 }
132
133 #if HAVE_VALGRIND_MEMCHECK_H
134 VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
135 #endif
136
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 */
139
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 */
143 return 1;
144
145 log_debug_errno(errno, "Failed to set LO_FLAGS_AUTOCLEAR flag for loop device %s, ignoring: %m", device);
146 } else
147 log_debug("Successfully set LO_FLAGS_AUTOCLEAR flag for loop device %s.", device);
148
149 return -EBUSY;
150 }
151
152 if (ioctl(fd, LOOP_GET_STATUS64, &info) < 0) {
153 /* If the LOOP_CLR_FD above succeeded we'll see ENXIO here. */
154 if (errno == ENXIO)
155 log_debug("Successfully detached loopback device %s.", device);
156 else
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 */
158
159 return 1;
160 }
161
162 #if HAVE_VALGRIND_MEMCHECK_H
163 VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
164 #endif
165
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);
170 else
171 log_debug("Weird, LOOP_CLR_FD succeeded but the device is still attached on %s.", device);
172
173 return -EBUSY; /* Nothing changed, the device is still attached, hence it apparently is still busy */
174 }
175
176 static int loopback_points_list_detach(LoopbackDevice **head, bool *changed, bool last_try) {
177 int n_failed = 0, r;
178 dev_t rootdev = 0, usrdev = 0;
179
180 assert(head);
181 assert(changed);
182
183 (void) get_block_device_harder("/", &rootdev);
184 (void) block_get_whole_disk(rootdev, &rootdev);
185
186 (void) get_block_device_harder("/usr", &usrdev);
187 (void) block_get_whole_disk(usrdev, &usrdev);
188
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);
193 n_failed++;
194 continue;
195 }
196
197 log_info("Detaching loopback %s.", m->path);
198 r = delete_loopback(m->path);
199 if (r < 0) {
200 log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not detach loopback %s: %m", m->path);
201 n_failed++;
202 continue;
203 }
204 if (r > 0)
205 *changed = true;
206
207 loopback_device_free(head, m);
208 }
209
210 return n_failed;
211 }
212
213 int loopback_detach_all(bool *changed, bool last_try) {
214 _cleanup_(loopback_device_list_free) LIST_HEAD(LoopbackDevice, loopback_list_head);
215 int r;
216
217 assert(changed);
218
219 LIST_HEAD_INIT(loopback_list_head);
220
221 r = loopback_list_get(&loopback_list_head);
222 if (r < 0)
223 return r;
224
225 return loopback_points_list_detach(&loopback_list_head, changed, last_try);
226 }