Bug Summary

File:build-scan/../src/core/umount.c
Warning:line 665, column 24
Potential leak of memory pointed to by 'swap_list_head'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name umount.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I systemd-shutdown.p -I . -I .. -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -I /usr/include/libmount -I /usr/include/blkid -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/core/umount.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2/***
3 Copyright © 2010 ProFUSION embedded systems
4***/
5
6#include <errno(*__errno_location ()).h>
7#include <fcntl.h>
8#include <linux1/loop.h>
9#include <string.h>
10#include <sys/mount.h>
11#include <sys/swap.h>
12
13/* This needs to be after sys/mount.h :( */
14#include <libmount.h>
15
16#include "libudev.h"
17
18#include "alloc-util.h"
19#include "blockdev-util.h"
20#include "def.h"
21#include "escape.h"
22#include "fd-util.h"
23#include "fstab-util.h"
24#include "linux-3.13/dm-ioctl.h"
25#include "mount-setup.h"
26#include "mount-util.h"
27#include "path-util.h"
28#include "process-util.h"
29#include "signal-util.h"
30#include "string-util.h"
31#include "udev-util.h"
32#include "umount.h"
33#include "util.h"
34#include "virt.h"
35
36DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table)static inline void mnt_free_tablep(struct libmnt_table* *p) {
if (*p) mnt_free_table(*p); }
;
37DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter)static inline void mnt_free_iterp(struct libmnt_iter* *p) { if
(*p) mnt_free_iter(*p); }
;
38
39static void mount_point_free(MountPoint **head, MountPoint *m) {
40 assert(head)do { if ((__builtin_expect(!!(!(head)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("head"), "../src/core/umount.c", 40, __PRETTY_FUNCTION__
); } while (0)
;
41 assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("m"), "../src/core/umount.c", 41, __PRETTY_FUNCTION__
); } while (0)
;
42
43 LIST_REMOVE(mount_point, *head, m)do { typeof(*(*head)) **_head = &(*head), *_item = (m); do
{ if ((__builtin_expect(!!(!(_item)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("_item"), "../src/core/umount.c", 43, __PRETTY_FUNCTION__
); } while (0); if (_item->mount_point_next) _item->mount_point_next
->mount_point_prev = _item->mount_point_prev; if (_item
->mount_point_prev) _item->mount_point_prev->mount_point_next
= _item->mount_point_next; else { do { if ((__builtin_expect
(!!(!(*_head == _item)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("*_head == _item"), "../src/core/umount.c", 43, __PRETTY_FUNCTION__
); } while (0); *_head = _item->mount_point_next; } _item->
mount_point_next = _item->mount_point_prev = ((void*)0); }
while (0)
;
44
45 free(m->path);
46 free(m->remount_options);
47 free(m);
48}
49
50void mount_points_list_free(MountPoint **head) {
51 assert(head)do { if ((__builtin_expect(!!(!(head)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("head"), "../src/core/umount.c", 51, __PRETTY_FUNCTION__
); } while (0)
;
52
53 while (*head)
54 mount_point_free(head, *head);
55}
56
57int mount_points_list_get(const char *mountinfo, MountPoint **head) {
58 _cleanup_(mnt_free_tablep)__attribute__((cleanup(mnt_free_tablep))) struct libmnt_table *t = NULL((void*)0);
59 _cleanup_(mnt_free_iterp)__attribute__((cleanup(mnt_free_iterp))) struct libmnt_iter *i = NULL((void*)0);
60 int r;
61
62 assert(head)do { if ((__builtin_expect(!!(!(head)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("head"), "../src/core/umount.c", 62, __PRETTY_FUNCTION__
); } while (0)
;
63
64 t = mnt_new_table();
65 i = mnt_new_iter(MNT_ITER_FORWARD);
66 if (!t || !i)
67 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/core/umount.c", 67
, __func__)
;
68
69 r = mnt_table_parse_mtab(t, mountinfo);
70 if (r < 0)
71 return log_error_errno(r, "Failed to parse %s: %m", mountinfo)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 71, __func__, "Failed to parse %s: %m"
, mountinfo) : -abs(_e); })
;
72
73 for (;;) {
74 struct libmnt_fs *fs;
75 const char *path, *options, *fstype;
76 _cleanup_free___attribute__((cleanup(freep))) char *p = NULL((void*)0);
77 unsigned long remount_flags = 0u;
78 _cleanup_free___attribute__((cleanup(freep))) char *remount_options = NULL((void*)0);
79 bool_Bool try_remount_ro;
80 MountPoint *m;
81
82 r = mnt_table_next_fs(t, i, &fs);
83 if (r == 1)
84 break;
85 if (r < 0)
86 return log_error_errno(r, "Failed to get next entry from %s: %m", mountinfo)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 86, __func__, "Failed to get next entry from %s: %m"
, mountinfo) : -abs(_e); })
;
87
88 path = mnt_fs_get_target(fs);
89 if (!path)
90 continue;
91
92 if (cunescape(path, UNESCAPE_RELAX, &p) < 0)
93 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/core/umount.c", 93
, __func__)
;
94
95 options = mnt_fs_get_options(fs);
96 fstype = mnt_fs_get_fstype(fs);
97
98 /* Ignore mount points we can't unmount because they
99 * are API or because we are keeping them open (like
100 * /dev/console). Also, ignore all mounts below API
101 * file systems, since they are likely virtual too,
102 * and hence not worth spending time on. Also, in
103 * unprivileged containers we might lack the rights to
104 * unmount these things, hence don't bother. */
105 if (mount_point_is_api(p) ||
106 mount_point_ignore(p) ||
107 path_startswith(p, "/dev") ||
108 path_startswith(p, "/sys") ||
109 path_startswith(p, "/proc"))
110 continue;
111
112 /* If we are in a container, don't attempt to
113 * read-only mount anything as that brings no real
114 * benefits, but might confuse the host, as we remount
115 * the superblock here, not the bind mount.
116 *
117 * If the filesystem is a network fs, also skip the
118 * remount. It brings no value (we cannot leave
119 * a "dirty fs") and could hang if the network is down.
120 * Note that umount2() is more careful and will not
121 * hang because of the network being down. */
122 try_remount_ro = detect_container() <= 0 &&
123 !fstype_is_network(fstype) &&
124 !fstype_is_api_vfs(fstype) &&
125 !fstype_is_ro(fstype) &&
126 !fstab_test_yes_no_option(options, "ro\0rw\0");
127
128 if (try_remount_ro) {
129 /* mount(2) states that mount flags and options need to be exactly the same
130 * as they were when the filesystem was mounted, except for the desired
131 * changes. So we reconstruct both here and adjust them for the later
132 * remount call too. */
133
134 r = mnt_fs_get_propagation(fs, &remount_flags);
135 if (r < 0) {
136 log_warning_errno(r, "mnt_fs_get_propagation() failed for %s, ignoring: %m", path)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 136, __func__, "mnt_fs_get_propagation() failed for %s, ignoring: %m"
, path) : -abs(_e); })
;
137 continue;
138 }
139
140 r = mount_option_mangle(options, remount_flags, &remount_flags, &remount_options);
141 if (r < 0) {
142 log_warning_errno(r, "mount_option_mangle failed for %s, ignoring: %m", path)({ int _level = ((4)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 142, __func__, "mount_option_mangle failed for %s, ignoring: %m"
, path) : -abs(_e); })
;
143 continue;
144 }
145
146 /* MS_BIND is special. If it is provided it will only make the mount-point
147 * read-only. If left out, the super block itself is remounted, which we want. */
148 remount_flags = (remount_flags|MS_REMOUNTMS_REMOUNT|MS_RDONLYMS_RDONLY) & ~MS_BINDMS_BIND;
149 }
150
151 m = new0(MountPoint, 1)((MountPoint*) calloc((1), sizeof(MountPoint)));
152 if (!m)
153 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/core/umount.c", 153
, __func__)
;
154
155 free_and_replace(m->path, p)({ free(m->path); (m->path) = (p); (p) = ((void*)0); 0;
})
;
156 free_and_replace(m->remount_options, remount_options)({ free(m->remount_options); (m->remount_options) = (remount_options
); (remount_options) = ((void*)0); 0; })
;
157 m->remount_flags = remount_flags;
158 m->try_remount_ro = try_remount_ro;
159
160 LIST_PREPEND(mount_point, *head, m)do { typeof(*(*head)) **_head = &(*head), *_item = (m); do
{ if ((__builtin_expect(!!(!(_item)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("_item"), "../src/core/umount.c", 160, __PRETTY_FUNCTION__
); } while (0); if ((_item->mount_point_next = *_head)) _item
->mount_point_next->mount_point_prev = _item; _item->
mount_point_prev = ((void*)0); *_head = _item; } while (0)
;
161 }
162
163 return 0;
164}
165
166int swap_list_get(const char *swaps, MountPoint **head) {
167 _cleanup_(mnt_free_tablep)__attribute__((cleanup(mnt_free_tablep))) struct libmnt_table *t = NULL((void*)0);
168 _cleanup_(mnt_free_iterp)__attribute__((cleanup(mnt_free_iterp))) struct libmnt_iter *i = NULL((void*)0);
169 int r;
170
171 assert(head)do { if ((__builtin_expect(!!(!(head)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("head"), "../src/core/umount.c", 171, __PRETTY_FUNCTION__
); } while (0)
;
6
Taking false branch
7
Loop condition is false. Exiting loop
172
173 t = mnt_new_table();
174 i = mnt_new_iter(MNT_ITER_FORWARD);
175 if (!t || !i)
8
Assuming 't' is non-null
9
Assuming 'i' is non-null
10
Taking false branch
176 return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/core/umount.c", 176
, __func__)
;
177
178 r = mnt_table_parse_swaps(t, swaps);
179 if (r < 0)
11
Assuming 'r' is >= 0
12
Taking false branch
180 return log_error_errno(r, "Failed to parse %s: %m", swaps)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 180, __func__, "Failed to parse %s: %m"
, swaps) : -abs(_e); })
;
181
182 for (;;) {
13
Loop condition is true. Entering loop body
30
Loop condition is true. Entering loop body
183 struct libmnt_fs *fs;
184
185 MountPoint *swap;
186 const char *source;
187 _cleanup_free___attribute__((cleanup(freep))) char *d = NULL((void*)0);
188
189 r = mnt_table_next_fs(t, i, &fs);
190 if (r == 1)
14
Assuming 'r' is not equal to 1
15
Taking false branch
31
Assuming 'r' is not equal to 1
32
Taking false branch
191 break;
192 if (r < 0)
16
Assuming 'r' is >= 0
17
Taking false branch
33
Assuming 'r' is < 0
34
Taking true branch
193 return log_error_errno(r, "Failed to get next entry from %s: %m", swaps)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 193, __func__, "Failed to get next entry from %s: %m"
, swaps) : -abs(_e); })
;
35
Assuming the condition is false
36
'?' condition is false
194
195 source = mnt_fs_get_source(fs);
196 if (!source)
18
Assuming 'source' is non-null
19
Taking false branch
197 continue;
198
199 r = cunescape(source, UNESCAPE_RELAX, &d);
200 if (r < 0)
20
Assuming 'r' is >= 0
21
Taking false branch
201 return r;
202
203 swap = new0(MountPoint, 1)((MountPoint*) calloc((1), sizeof(MountPoint)));
22
Memory is allocated
204 if (!swap)
23
Assuming 'swap' is non-null
24
Taking false branch
205 return -ENOMEM12;
206
207 free_and_replace(swap->path, d)({ free(swap->path); (swap->path) = (d); (d) = ((void*)
0); 0; })
;
208 LIST_PREPEND(mount_point, *head, swap)do { typeof(*(*head)) **_head = &(*head), *_item = (swap)
; do { if ((__builtin_expect(!!(!(_item)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("_item"), "../src/core/umount.c", 208, __PRETTY_FUNCTION__
); } while (0); if ((_item->mount_point_next = *_head)) _item
->mount_point_next->mount_point_prev = _item; _item->
mount_point_prev = ((void*)0); *_head = _item; } while (0)
;
25
Taking false branch
26
Loop condition is false. Exiting loop
27
Assuming field 'mount_point_next' is null
28
Taking false branch
29
Loop condition is false. Exiting loop
209 }
210
211 return 0;
212}
213
214static int loopback_list_get(MountPoint **head) {
215 _cleanup_(udev_enumerate_unrefp)__attribute__((cleanup(udev_enumerate_unrefp))) struct udev_enumerate *e = NULL((void*)0);
216 struct udev_list_entry *item = NULL((void*)0), *first = NULL((void*)0);
217 _cleanup_(udev_unrefp)__attribute__((cleanup(udev_unrefp))) struct udev *udev = NULL((void*)0);
218 int r;
219
220 assert(head)do { if ((__builtin_expect(!!(!(head)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("head"), "../src/core/umount.c", 220, __PRETTY_FUNCTION__
); } while (0)
;
221
222 udev = udev_new();
223 if (!udev)
224 return -ENOMEM12;
225
226 e = udev_enumerate_new(udev);
227 if (!e)
228 return -ENOMEM12;
229
230 r = udev_enumerate_add_match_subsystem(e, "block");
231 if (r < 0)
232 return r;
233
234 r = udev_enumerate_add_match_sysname(e, "loop*");
235 if (r < 0)
236 return r;
237
238 r = udev_enumerate_add_match_sysattr(e, "loop/backing_file", NULL((void*)0));
239 if (r < 0)
240 return r;
241
242 r = udev_enumerate_scan_devices(e);
243 if (r < 0)
244 return r;
245
246 first = udev_enumerate_get_list_entry(e);
247 udev_list_entry_foreach(item, first)for (item = first; item != ((void*)0); item = udev_list_entry_get_next
(item))
{
248 _cleanup_(udev_device_unrefp)__attribute__((cleanup(udev_device_unrefp))) struct udev_device *d;
249 const char *dn;
250 _cleanup_free___attribute__((cleanup(freep))) MountPoint *lb = NULL((void*)0);
251
252 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
253 if (!d)
254 return -ENOMEM12;
255
256 dn = udev_device_get_devnode(d);
257 if (!dn)
258 continue;
259
260 lb = new0(MountPoint, 1)((MountPoint*) calloc((1), sizeof(MountPoint)));
261 if (!lb)
262 return -ENOMEM12;
263
264 r = free_and_strdup(&lb->path, dn);
265 if (r < 0)
266 return r;
267
268 LIST_PREPEND(mount_point, *head, lb)do { typeof(*(*head)) **_head = &(*head), *_item = (lb); do
{ if ((__builtin_expect(!!(!(_item)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("_item"), "../src/core/umount.c", 268, __PRETTY_FUNCTION__
); } while (0); if ((_item->mount_point_next = *_head)) _item
->mount_point_next->mount_point_prev = _item; _item->
mount_point_prev = ((void*)0); *_head = _item; } while (0)
;
269 lb = NULL((void*)0);
270 }
271
272 return 0;
273}
274
275static int dm_list_get(MountPoint **head) {
276 _cleanup_(udev_enumerate_unrefp)__attribute__((cleanup(udev_enumerate_unrefp))) struct udev_enumerate *e = NULL((void*)0);
277 struct udev_list_entry *item = NULL((void*)0), *first = NULL((void*)0);
278 _cleanup_(udev_unrefp)__attribute__((cleanup(udev_unrefp))) struct udev *udev = NULL((void*)0);
279 int r;
280
281 assert(head)do { if ((__builtin_expect(!!(!(head)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("head"), "../src/core/umount.c", 281, __PRETTY_FUNCTION__
); } while (0)
;
282
283 udev = udev_new();
284 if (!udev)
285 return -ENOMEM12;
286
287 e = udev_enumerate_new(udev);
288 if (!e)
289 return -ENOMEM12;
290
291 r = udev_enumerate_add_match_subsystem(e, "block");
292 if (r < 0)
293 return r;
294
295 r = udev_enumerate_add_match_sysname(e, "dm-*");
296 if (r < 0)
297 return r;
298
299 r = udev_enumerate_scan_devices(e);
300 if (r < 0)
301 return r;
302
303 first = udev_enumerate_get_list_entry(e);
304 udev_list_entry_foreach(item, first)for (item = first; item != ((void*)0); item = udev_list_entry_get_next
(item))
{
305 _cleanup_(udev_device_unrefp)__attribute__((cleanup(udev_device_unrefp))) struct udev_device *d;
306 dev_t devnum;
307 const char *dn;
308 _cleanup_free___attribute__((cleanup(freep))) MountPoint *m = NULL((void*)0);
309
310 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
311 if (!d)
312 return -ENOMEM12;
313
314 devnum = udev_device_get_devnum(d);
315 dn = udev_device_get_devnode(d);
316 if (major(devnum)gnu_dev_major (devnum) == 0 || !dn)
317 continue;
318
319 m = new0(MountPoint, 1)((MountPoint*) calloc((1), sizeof(MountPoint)));
320 if (!m)
321 return -ENOMEM12;
322
323 m->devnum = devnum;
324 r = free_and_strdup(&m->path, dn);
325 if (r < 0)
326 return r;
327
328 LIST_PREPEND(mount_point, *head, m)do { typeof(*(*head)) **_head = &(*head), *_item = (m); do
{ if ((__builtin_expect(!!(!(_item)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("_item"), "../src/core/umount.c", 328, __PRETTY_FUNCTION__
); } while (0); if ((_item->mount_point_next = *_head)) _item
->mount_point_next->mount_point_prev = _item; _item->
mount_point_prev = ((void*)0); *_head = _item; } while (0)
;
329 m = NULL((void*)0);
330 }
331
332 return 0;
333}
334
335static int delete_loopback(const char *device) {
336 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
337 int r;
338
339 assert(device)do { if ((__builtin_expect(!!(!(device)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("device"), "../src/core/umount.c", 339, __PRETTY_FUNCTION__
); } while (0)
;
340
341 fd = open(device, O_RDONLY00|O_CLOEXEC02000000);
342 if (fd < 0)
343 return errno(*__errno_location ()) == ENOENT2 ? 0 : -errno(*__errno_location ());
344
345 r = ioctl(fd, LOOP_CLR_FD0x4C01, 0);
346 if (r >= 0)
347 return 1;
348
349 /* ENXIO: not bound, so no error */
350 if (errno(*__errno_location ()) == ENXIO6)
351 return 0;
352
353 return -errno(*__errno_location ());
354}
355
356static int delete_dm(dev_t devnum) {
357
358 struct dm_ioctl dm = {
359 .version = {
360 DM_VERSION_MAJOR4,
361 DM_VERSION_MINOR27,
362 DM_VERSION_PATCHLEVEL0
363 },
364 .data_size = sizeof(dm),
365 .dev = devnum,
366 };
367
368 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
369
370 assert(major(devnum) != 0)do { if ((__builtin_expect(!!(!(gnu_dev_major (devnum) != 0))
,0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("major(devnum) != 0"
), "../src/core/umount.c", 370, __PRETTY_FUNCTION__); } while
(0)
;
371
372 fd = open("/dev/mapper/control", O_RDWR02|O_CLOEXEC02000000);
373 if (fd < 0)
374 return -errno(*__errno_location ());
375
376 if (ioctl(fd, DM_DEV_REMOVE(((2U|1U) << (((0 +8)+8)+14)) | (((0xfd)) << (0 +
8)) | (((DM_DEV_REMOVE_CMD)) << 0) | ((((sizeof(struct dm_ioctl
)))) << ((0 +8)+8)))
, &dm) < 0)
377 return -errno(*__errno_location ());
378
379 return 0;
380}
381
382static bool_Bool nonunmountable_path(const char *path) {
383 return path_equal(path, "/")
384#if ! HAVE_SPLIT_USR0
385 || path_equal(path, "/usr")
386#endif
387 || path_startswith(path, "/run/initramfs");
388}
389
390static int remount_with_timeout(MountPoint *m, int umount_log_level) {
391 pid_t pid;
392 int r;
393
394 BLOCK_SIGNALS(SIGCHLD)__attribute__((cleanup(block_signals_reset))) __attribute__ (
(unused)) sigset_t _saved_sigset = ({ sigset_t _t; do { if ((
__builtin_expect(!!(!(sigprocmask_many(0, &_t, 17, -1) >=
0)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("sigprocmask_many(SIG_BLOCK, &_t, 17, -1) >= 0"
), "../src/core/umount.c", 394, __PRETTY_FUNCTION__); } while
(0); _t; })
;
395
396 assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("m"), "../src/core/umount.c", 396, __PRETTY_FUNCTION__
); } while (0)
;
397
398 /* Due to the possiblity of a remount operation hanging, we
399 * fork a child process and set a timeout. If the timeout
400 * lapses, the assumption is that that particular remount
401 * failed. */
402 r = safe_fork("(sd-remount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &pid);
403 if (r < 0)
404 return r;
405 if (r == 0) {
406 log_info("Remounting '%s' read-only in with options '%s'.", m->path, m->remount_options)({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 406, __func__, "Remounting '%s' read-only in with options '%s'."
, m->path, m->remount_options) : -abs(_e); })
;
407
408 /* Start the mount operation here in the child */
409 r = mount(NULL((void*)0), m->path, NULL((void*)0), m->remount_flags, m->remount_options);
410 if (r < 0)
411 log_full_errno(umount_log_level, errno, "Failed to remount '%s' read-only: %m", m->path)({ int _level = ((umount_log_level)), _e = (((*__errno_location
()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm
(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((
_realm) << 10 | (_level)), _e, "../src/core/umount.c", 411
, __func__, "Failed to remount '%s' read-only: %m", m->path
) : -abs(_e); })
;
412
413 _exit(r < 0 ? EXIT_FAILURE1 : EXIT_SUCCESS0);
414 }
415
416 r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC(90*((usec_t) 1000000ULL)));
417 if (r == -ETIMEDOUT110) {
418 log_error_errno(r, "Remounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 418, __func__, "Remounting '%s' timed out, issuing SIGKILL to PID "
"%" "i" ".", m->path, pid) : -abs(_e); })
;
419 (void) kill(pid, SIGKILL9);
420 } else if (r == -EPROTO71)
421 log_debug_errno(r, "Remounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 421, __func__, "Remounting '%s' failed abnormally, child process "
"%" "i" " aborted or exited non-zero.", m->path, pid) : -
abs(_e); })
;
422 else if (r < 0)
423 log_error_errno(r, "Remounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 423, __func__, "Remounting '%s' failed unexpectedly, couldn't wait for child process "
"%" "i" ": %m", m->path, pid) : -abs(_e); })
;
424
425 return r;
426}
427
428static int umount_with_timeout(MountPoint *m, int umount_log_level) {
429 pid_t pid;
430 int r;
431
432 BLOCK_SIGNALS(SIGCHLD)__attribute__((cleanup(block_signals_reset))) __attribute__ (
(unused)) sigset_t _saved_sigset = ({ sigset_t _t; do { if ((
__builtin_expect(!!(!(sigprocmask_many(0, &_t, 17, -1) >=
0)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("sigprocmask_many(SIG_BLOCK, &_t, 17, -1) >= 0"
), "../src/core/umount.c", 432, __PRETTY_FUNCTION__); } while
(0); _t; })
;
433
434 assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("m"), "../src/core/umount.c", 434, __PRETTY_FUNCTION__
); } while (0)
;
435
436 /* Due to the possiblity of a umount operation hanging, we
437 * fork a child process and set a timeout. If the timeout
438 * lapses, the assumption is that that particular umount
439 * failed. */
440 r = safe_fork("(sd-umount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &pid);
441 if (r < 0)
442 return r;
443 if (r == 0) {
444 log_info("Unmounting '%s'.", m->path)({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 444, __func__, "Unmounting '%s'.", m
->path) : -abs(_e); })
;
445
446 /* Start the mount operation here in the child Using MNT_FORCE
447 * causes some filesystems (e.g. FUSE and NFS and other network
448 * filesystems) to abort any pending requests and return -EIO
449 * rather than blocking indefinitely. If the filesysten is
450 * "busy", this may allow processes to die, thus making the
451 * filesystem less busy so the unmount might succeed (rather
452 * then return EBUSY).*/
453 r = umount2(m->path, MNT_FORCEMNT_FORCE);
454 if (r < 0)
455 log_full_errno(umount_log_level, errno, "Failed to unmount %s: %m", m->path)({ int _level = ((umount_log_level)), _e = (((*__errno_location
()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm
(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((
_realm) << 10 | (_level)), _e, "../src/core/umount.c", 455
, __func__, "Failed to unmount %s: %m", m->path) : -abs(_e
); })
;
456
457 _exit(r < 0 ? EXIT_FAILURE1 : EXIT_SUCCESS0);
458 }
459
460 r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC(90*((usec_t) 1000000ULL)));
461 if (r == -ETIMEDOUT110) {
462 log_error_errno(r, "Unmounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 462, __func__, "Unmounting '%s' timed out, issuing SIGKILL to PID "
"%" "i" ".", m->path, pid) : -abs(_e); })
;
463 (void) kill(pid, SIGKILL9);
464 } else if (r == -EPROTO71)
465 log_debug_errno(r, "Unmounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 465, __func__, "Unmounting '%s' failed abnormally, child process "
"%" "i" " aborted or exited non-zero.", m->path, pid) : -
abs(_e); })
;
466 else if (r < 0)
467 log_error_errno(r, "Unmounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 467, __func__, "Unmounting '%s' failed unexpectedly, couldn't wait for child process "
"%" "i" ": %m", m->path, pid) : -abs(_e); })
;
468
469 return r;
470}
471
472/* This includes remounting readonly, which changes the kernel mount options.
473 * Therefore the list passed to this function is invalidated, and should not be reused. */
474static int mount_points_list_umount(MountPoint **head, bool_Bool *changed, int umount_log_level) {
475 MountPoint *m;
476 int n_failed = 0;
477
478 assert(head)do { if ((__builtin_expect(!!(!(head)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("head"), "../src/core/umount.c", 478, __PRETTY_FUNCTION__
); } while (0)
;
479 assert(changed)do { if ((__builtin_expect(!!(!(changed)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("changed"), "../src/core/umount.c", 479,
__PRETTY_FUNCTION__); } while (0)
;
480
481 LIST_FOREACH(mount_point, m, *head)for ((m) = (*head); (m); (m) = (m)->mount_point_next) {
482 if (m->try_remount_ro) {
483 /* We always try to remount directories
484 * read-only first, before we go on and umount
485 * them.
486 *
487 * Mount points can be stacked. If a mount
488 * point is stacked below / or /usr, we
489 * cannot umount or remount it directly,
490 * since there is no way to refer to the
491 * underlying mount. There's nothing we can do
492 * about it for the general case, but we can
493 * do something about it if it is aliased
494 * somehwere else via a bind mount. If we
495 * explicitly remount the super block of that
496 * alias read-only we hence should be
497 * relatively safe regarding keeping a dirty fs
498 * we cannot otherwise see.
499 *
500 * Since the remount can hang in the instance of
501 * remote filesystems, we remount asynchronously
502 * and skip the subsequent umount if it fails. */
503 if (remount_with_timeout(m, umount_log_level) < 0) {
504 /* Remount failed, but try unmounting anyway,
505 * unless this is a mount point we want to skip. */
506 if (nonunmountable_path(m->path)) {
507 n_failed++;
508 continue;
509 }
510 }
511 }
512
513 /* Skip / and /usr since we cannot unmount that
514 * anyway, since we are running from it. They have
515 * already been remounted ro. */
516 if (nonunmountable_path(m->path))
517 continue;
518
519 /* Trying to umount */
520 if (umount_with_timeout(m, umount_log_level) < 0)
521 n_failed++;
522 else
523 *changed = true1;
524 }
525
526 return n_failed;
527}
528
529static int swap_points_list_off(MountPoint **head, bool_Bool *changed) {
530 MountPoint *m, *n;
531 int n_failed = 0;
532
533 assert(head)do { if ((__builtin_expect(!!(!(head)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("head"), "../src/core/umount.c", 533, __PRETTY_FUNCTION__
); } while (0)
;
534 assert(changed)do { if ((__builtin_expect(!!(!(changed)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("changed"), "../src/core/umount.c", 534,
__PRETTY_FUNCTION__); } while (0)
;
535
536 LIST_FOREACH_SAFE(mount_point, m, n, *head)for ((m) = (*head); (m) && (((n) = (m)->mount_point_next
), 1); (m) = (n))
{
537 log_info("Deactivating swap %s.", m->path)({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 537, __func__, "Deactivating swap %s."
, m->path) : -abs(_e); })
;
538 if (swapoff(m->path) == 0) {
539 *changed = true1;
540 mount_point_free(head, m);
541 } else {
542 log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path)({ int _level = ((4)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/core/umount.c", 542, __func__, "Could not deactivate swap %s: %m"
, m->path) : -abs(_e); })
;
543 n_failed++;
544 }
545 }
546
547 return n_failed;
548}
549
550static int loopback_points_list_detach(MountPoint **head, bool_Bool *changed, int umount_log_level) {
551 MountPoint *m, *n;
552 int n_failed = 0, k;
553 struct stat root_st;
554
555 assert(head)do { if ((__builtin_expect(!!(!(head)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("head"), "../src/core/umount.c", 555, __PRETTY_FUNCTION__
); } while (0)
;
556 assert(changed)do { if ((__builtin_expect(!!(!(changed)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("changed"), "../src/core/umount.c", 556,
__PRETTY_FUNCTION__); } while (0)
;
557
558 k = lstat("/", &root_st);
559
560 LIST_FOREACH_SAFE(mount_point, m, n, *head)for ((m) = (*head); (m) && (((n) = (m)->mount_point_next
), 1); (m) = (n))
{
561 int r;
562 struct stat loopback_st;
563
564 if (k >= 0 &&
565 major(root_st.st_dev)gnu_dev_major (root_st.st_dev) != 0 &&
566 lstat(m->path, &loopback_st) >= 0 &&
567 root_st.st_dev == loopback_st.st_rdev) {
568 n_failed++;
569 continue;
570 }
571
572 log_info("Detaching loopback %s.", m->path)({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 572, __func__, "Detaching loopback %s."
, m->path) : -abs(_e); })
;
573 r = delete_loopback(m->path);
574 if (r >= 0) {
575 if (r > 0)
576 *changed = true1;
577
578 mount_point_free(head, m);
579 } else {
580 log_full_errno(umount_log_level, errno, "Could not detach loopback %s: %m", m->path)({ int _level = ((umount_log_level)), _e = (((*__errno_location
()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm
(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((
_realm) << 10 | (_level)), _e, "../src/core/umount.c", 580
, __func__, "Could not detach loopback %s: %m", m->path) :
-abs(_e); })
;
581 n_failed++;
582 }
583 }
584
585 return n_failed;
586}
587
588static int dm_points_list_detach(MountPoint **head, bool_Bool *changed, int umount_log_level) {
589 MountPoint *m, *n;
590 int n_failed = 0, r;
591 dev_t rootdev;
592
593 assert(head)do { if ((__builtin_expect(!!(!(head)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("head"), "../src/core/umount.c", 593, __PRETTY_FUNCTION__
); } while (0)
;
594 assert(changed)do { if ((__builtin_expect(!!(!(changed)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("changed"), "../src/core/umount.c", 594,
__PRETTY_FUNCTION__); } while (0)
;
595
596 r = get_block_device("/", &rootdev);
597 if (r <= 0)
598 rootdev = 0;
599
600 LIST_FOREACH_SAFE(mount_point, m, n, *head)for ((m) = (*head); (m) && (((n) = (m)->mount_point_next
), 1); (m) = (n))
{
601
602 if (major(rootdev)gnu_dev_major (rootdev) != 0 && rootdev == m->devnum) {
603 n_failed ++;
604 continue;
605 }
606
607 log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum))({ int _level = (((6))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/core/umount.c", 607, __func__, "Detaching DM %u:%u."
, gnu_dev_major (m->devnum), gnu_dev_minor (m->devnum))
: -abs(_e); })
;
608 r = delete_dm(m->devnum);
609 if (r >= 0) {
610 *changed = true1;
611 mount_point_free(head, m);
612 } else {
613 log_full_errno(umount_log_level, errno, "Could not detach DM %s: %m", m->path)({ int _level = ((umount_log_level)), _e = (((*__errno_location
()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm
(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((
_realm) << 10 | (_level)), _e, "../src/core/umount.c", 613
, __func__, "Could not detach DM %s: %m", m->path) : -abs(
_e); })
;
614 n_failed++;
615 }
616 }
617
618 return n_failed;
619}
620
621static int umount_all_once(bool_Bool *changed, int umount_log_level) {
622 int r;
623 _cleanup_(mount_points_list_free)__attribute__((cleanup(mount_points_list_free))) LIST_HEAD(MountPoint, mp_list_head)MountPoint *mp_list_head;
624
625 assert(changed)do { if ((__builtin_expect(!!(!(changed)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("changed"), "../src/core/umount.c", 625,
__PRETTY_FUNCTION__); } while (0)
;
626
627 LIST_HEAD_INIT(mp_list_head)do { (mp_list_head) = ((void*)0); } while (0);
628 r = mount_points_list_get(NULL((void*)0), &mp_list_head);
629 if (r < 0)
630 return r;
631
632 return mount_points_list_umount(&mp_list_head, changed, umount_log_level);
633}
634
635int umount_all(bool_Bool *changed, int umount_log_level) {
636 bool_Bool umount_changed;
637 int r;
638
639 assert(changed)do { if ((__builtin_expect(!!(!(changed)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("changed"), "../src/core/umount.c", 639,
__PRETTY_FUNCTION__); } while (0)
;
640
641 /* Retry umount, until nothing can be umounted anymore. Mounts are
642 * processed in order, newest first. The retries are needed when
643 * an old mount has been moved, to a path inside a newer mount. */
644 do {
645 umount_changed = false0;
646
647 r = umount_all_once(&umount_changed, umount_log_level);
648 if (umount_changed)
649 *changed = true1;
650 } while (umount_changed);
651
652 return r;
653}
654
655int swapoff_all(bool_Bool *changed) {
656 _cleanup_(mount_points_list_free)__attribute__((cleanup(mount_points_list_free))) LIST_HEAD(MountPoint, swap_list_head)MountPoint *swap_list_head;
657 int r;
658
659 assert(changed)do { if ((__builtin_expect(!!(!(changed)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("changed"), "../src/core/umount.c", 659,
__PRETTY_FUNCTION__); } while (0)
;
1
Assuming 'changed' is non-null
2
Taking false branch
3
Loop condition is false. Exiting loop
660
661 LIST_HEAD_INIT(swap_list_head)do { (swap_list_head) = ((void*)0); } while (0);
4
Loop condition is false. Exiting loop
662
663 r = swap_list_get(NULL((void*)0), &swap_list_head);
5
Calling 'swap_list_get'
37
Returned allocated memory via 2nd parameter
664 if (r < 0)
38
Assuming 'r' is < 0
39
Taking true branch
665 return r;
40
Potential leak of memory pointed to by 'swap_list_head'
666
667 return swap_points_list_off(&swap_list_head, changed);
668}
669
670int loopback_detach_all(bool_Bool *changed, int umount_log_level) {
671 _cleanup_(mount_points_list_free)__attribute__((cleanup(mount_points_list_free))) LIST_HEAD(MountPoint, loopback_list_head)MountPoint *loopback_list_head;
672 int r;
673
674 assert(changed)do { if ((__builtin_expect(!!(!(changed)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("changed"), "../src/core/umount.c", 674,
__PRETTY_FUNCTION__); } while (0)
;
675
676 LIST_HEAD_INIT(loopback_list_head)do { (loopback_list_head) = ((void*)0); } while (0);
677
678 r = loopback_list_get(&loopback_list_head);
679 if (r < 0)
680 return r;
681
682 return loopback_points_list_detach(&loopback_list_head, changed, umount_log_level);
683}
684
685int dm_detach_all(bool_Bool *changed, int umount_log_level) {
686 _cleanup_(mount_points_list_free)__attribute__((cleanup(mount_points_list_free))) LIST_HEAD(MountPoint, dm_list_head)MountPoint *dm_list_head;
687 int r;
688
689 assert(changed)do { if ((__builtin_expect(!!(!(changed)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("changed"), "../src/core/umount.c", 689,
__PRETTY_FUNCTION__); } while (0)
;
690
691 LIST_HEAD_INIT(dm_list_head)do { (dm_list_head) = ((void*)0); } while (0);
692
693 r = dm_list_get(&dm_list_head);
694 if (r < 0)
695 return r;
696
697 return dm_points_list_detach(&dm_list_head, changed, umount_log_level);
698}