File: | build-scan/../src/coredump/coredump-vacuum.c |
Warning: | line 204, column 48 Potential leak of memory pointed to by 'n' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
2 | ||||
3 | #include <sys/statvfs.h> | |||
4 | ||||
5 | #include "alloc-util.h" | |||
6 | #include "coredump-vacuum.h" | |||
7 | #include "dirent-util.h" | |||
8 | #include "fd-util.h" | |||
9 | #include "fs-util.h" | |||
10 | #include "hashmap.h" | |||
11 | #include "macro.h" | |||
12 | #include "string-util.h" | |||
13 | #include "time-util.h" | |||
14 | #include "user-util.h" | |||
15 | #include "util.h" | |||
16 | ||||
17 | #define DEFAULT_MAX_USE_LOWER(uint64_t) (1ULL*1024ULL*1024ULL) (uint64_t) (1ULL*1024ULL*1024ULL) /* 1 MiB */ | |||
18 | #define DEFAULT_MAX_USE_UPPER(uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ | |||
19 | #define DEFAULT_KEEP_FREE_UPPER(uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ | |||
20 | #define DEFAULT_KEEP_FREE(uint64_t) (1024ULL*1024ULL) (uint64_t) (1024ULL*1024ULL) /* 1 MB */ | |||
21 | ||||
22 | struct vacuum_candidate { | |||
23 | unsigned n_files; | |||
24 | char *oldest_file; | |||
25 | usec_t oldest_mtime; | |||
26 | }; | |||
27 | ||||
28 | static void vacuum_candidate_free(struct vacuum_candidate *c) { | |||
29 | if (!c) | |||
30 | return; | |||
31 | ||||
32 | free(c->oldest_file); | |||
33 | free(c); | |||
34 | } | |||
35 | ||||
36 | DEFINE_TRIVIAL_CLEANUP_FUNC(struct vacuum_candidate*, vacuum_candidate_free)static inline void vacuum_candidate_freep(struct vacuum_candidate * *p) { if (*p) vacuum_candidate_free(*p); }; | |||
37 | ||||
38 | static void vacuum_candidate_hashmap_free(Hashmap *h) { | |||
39 | hashmap_free_with_destructor(h, vacuum_candidate_free)({ ({ void *_item; while ((_item = hashmap_steal_first(h))) vacuum_candidate_free (_item); }); hashmap_free(h); }); | |||
40 | } | |||
41 | ||||
42 | DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, vacuum_candidate_hashmap_free)static inline void vacuum_candidate_hashmap_freep(Hashmap* *p ) { if (*p) vacuum_candidate_hashmap_free(*p); }; | |||
43 | ||||
44 | static int uid_from_file_name(const char *filename, uid_t *uid) { | |||
45 | const char *p, *e, *u; | |||
46 | ||||
47 | p = startswith(filename, "core."); | |||
48 | if (!p) | |||
49 | return -EINVAL22; | |||
50 | ||||
51 | /* Skip the comm field */ | |||
52 | p = strchr(p, '.'); | |||
53 | if (!p) | |||
54 | return -EINVAL22; | |||
55 | p++; | |||
56 | ||||
57 | /* Find end up UID */ | |||
58 | e = strchr(p, '.'); | |||
59 | if (!e) | |||
60 | return -EINVAL22; | |||
61 | ||||
62 | u = strndupa(p, e-p)(__extension__ ({ const char *__old = (p); size_t __len = strnlen (__old, (e-p)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old, __len ); })); | |||
63 | return parse_uid(u, uid); | |||
64 | } | |||
65 | ||||
66 | static bool_Bool vacuum_necessary(int fd, uint64_t sum, uint64_t keep_free, uint64_t max_use) { | |||
67 | uint64_t fs_size = 0, fs_free = (uint64_t) -1; | |||
68 | struct statvfs sv; | |||
69 | ||||
70 | assert(fd >= 0)do { if ((__builtin_expect(!!(!(fd >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fd >= 0"), "../src/coredump/coredump-vacuum.c" , 70, __PRETTY_FUNCTION__); } while (0); | |||
71 | ||||
72 | if (fstatvfs(fd, &sv) >= 0) { | |||
73 | fs_size = sv.f_frsize * sv.f_blocks; | |||
74 | fs_free = sv.f_frsize * sv.f_bfree; | |||
75 | } | |||
76 | ||||
77 | if (max_use == (uint64_t) -1) { | |||
78 | ||||
79 | if (fs_size > 0) { | |||
80 | max_use = PAGE_ALIGN(fs_size / 10)ALIGN_TO((fs_size / 10), page_size()); /* 10% */ | |||
81 | ||||
82 | if (max_use > DEFAULT_MAX_USE_UPPER(uint64_t) (4ULL*1024ULL*1024ULL*1024ULL)) | |||
83 | max_use = DEFAULT_MAX_USE_UPPER(uint64_t) (4ULL*1024ULL*1024ULL*1024ULL); | |||
84 | ||||
85 | if (max_use < DEFAULT_MAX_USE_LOWER(uint64_t) (1ULL*1024ULL*1024ULL)) | |||
86 | max_use = DEFAULT_MAX_USE_LOWER(uint64_t) (1ULL*1024ULL*1024ULL); | |||
87 | } else | |||
88 | max_use = DEFAULT_MAX_USE_LOWER(uint64_t) (1ULL*1024ULL*1024ULL); | |||
89 | } else | |||
90 | max_use = PAGE_ALIGN(max_use)ALIGN_TO((max_use), page_size()); | |||
91 | ||||
92 | if (max_use > 0 && sum > max_use) | |||
93 | return true1; | |||
94 | ||||
95 | if (keep_free == (uint64_t) -1) { | |||
96 | ||||
97 | if (fs_size > 0) { | |||
98 | keep_free = PAGE_ALIGN((fs_size * 3) / 20)ALIGN_TO(((fs_size * 3) / 20), page_size()); /* 15% */ | |||
99 | ||||
100 | if (keep_free > DEFAULT_KEEP_FREE_UPPER(uint64_t) (4ULL*1024ULL*1024ULL*1024ULL)) | |||
101 | keep_free = DEFAULT_KEEP_FREE_UPPER(uint64_t) (4ULL*1024ULL*1024ULL*1024ULL); | |||
102 | } else | |||
103 | keep_free = DEFAULT_KEEP_FREE(uint64_t) (1024ULL*1024ULL); | |||
104 | } else | |||
105 | keep_free = PAGE_ALIGN(keep_free)ALIGN_TO((keep_free), page_size()); | |||
106 | ||||
107 | if (keep_free > 0 && fs_free < keep_free) | |||
108 | return true1; | |||
109 | ||||
110 | return false0; | |||
111 | } | |||
112 | ||||
113 | int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) { | |||
114 | _cleanup_closedir___attribute__((cleanup(closedirp))) DIR *d = NULL((void*)0); | |||
115 | struct stat exclude_st; | |||
116 | int r; | |||
117 | ||||
118 | if (keep_free == 0 && max_use == 0) | |||
| ||||
119 | return 0; | |||
120 | ||||
121 | if (exclude_fd >= 0) { | |||
122 | if (fstat(exclude_fd, &exclude_st) < 0) | |||
123 | return log_error_errno(errno, "Failed to fstat(): %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/coredump/coredump-vacuum.c", 123 , __func__, "Failed to fstat(): %m") : -abs(_e); }); | |||
124 | } | |||
125 | ||||
126 | /* This algorithm will keep deleting the oldest file of the | |||
127 | * user with the most coredumps until we are back in the size | |||
128 | * limits. Note that vacuuming for journal files is different, | |||
129 | * because we rely on rate-limiting of the messages there, | |||
130 | * to avoid being flooded. */ | |||
131 | ||||
132 | d = opendir("/var/lib/systemd/coredump"); | |||
133 | if (!d) { | |||
134 | if (errno(*__errno_location ()) == ENOENT2) | |||
135 | return 0; | |||
136 | ||||
137 | return log_error_errno(errno, "Can't open coredump directory: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/coredump/coredump-vacuum.c", 137 , __func__, "Can't open coredump directory: %m") : -abs(_e); } ); | |||
138 | } | |||
139 | ||||
140 | for (;;) { | |||
141 | _cleanup_(vacuum_candidate_hashmap_freep)__attribute__((cleanup(vacuum_candidate_hashmap_freep))) Hashmap *h = NULL((void*)0); | |||
142 | struct vacuum_candidate *worst = NULL((void*)0); | |||
143 | struct dirent *de; | |||
144 | uint64_t sum = 0; | |||
145 | ||||
146 | rewinddir(d); | |||
147 | ||||
148 | FOREACH_DIRENT(de, d, goto fail)for ((*__errno_location ()) = 0, de = readdir(d);; (*__errno_location ()) = 0, de = readdir(d)) if (!de) { if ((*__errno_location ( )) > 0) { goto fail; } break; } else if (hidden_or_backup_file ((de)->d_name)) continue; else { | |||
149 | struct vacuum_candidate *c; | |||
150 | struct stat st; | |||
151 | uid_t uid; | |||
152 | usec_t t; | |||
153 | ||||
154 | r = uid_from_file_name(de->d_name, &uid); | |||
155 | if (r < 0) | |||
156 | continue; | |||
157 | ||||
158 | if (fstatat(dirfd(d), de->d_name, &st, AT_NO_AUTOMOUNT0x800|AT_SYMLINK_NOFOLLOW0x100) < 0) { | |||
159 | if (errno(*__errno_location ()) == ENOENT2) | |||
160 | continue; | |||
161 | ||||
162 | log_warning_errno(errno, "Failed to stat /var/lib/systemd/coredump/%s: %m", de->d_name)({ 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/coredump/coredump-vacuum.c", 162 , __func__, "Failed to stat /var/lib/systemd/coredump/%s: %m" , de->d_name) : -abs(_e); }); | |||
163 | continue; | |||
164 | } | |||
165 | ||||
166 | if (!S_ISREG(st.st_mode)((((st.st_mode)) & 0170000) == (0100000))) | |||
167 | continue; | |||
168 | ||||
169 | if (exclude_fd
| |||
170 | exclude_st.st_dev == st.st_dev && | |||
171 | exclude_st.st_ino == st.st_ino) | |||
172 | continue; | |||
173 | ||||
174 | r = hashmap_ensure_allocated(&h, NULL)internal_hashmap_ensure_allocated(&h, ((void*)0) ); | |||
175 | if (r < 0) | |||
176 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/coredump/coredump-vacuum.c" , 176, __func__); | |||
177 | ||||
178 | t = timespec_load(&st.st_mtim); | |||
179 | ||||
180 | c = hashmap_get(h, UID_TO_PTR(uid)((void*) (((uintptr_t) (uid))+1))); | |||
181 | if (c) { | |||
182 | ||||
183 | if (t < c->oldest_mtime) { | |||
184 | char *n; | |||
185 | ||||
186 | n = strdup(de->d_name); | |||
187 | if (!n) | |||
188 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/coredump/coredump-vacuum.c" , 188, __func__); | |||
189 | ||||
190 | free(c->oldest_file); | |||
191 | c->oldest_file = n; | |||
192 | c->oldest_mtime = t; | |||
193 | } | |||
194 | ||||
195 | } else { | |||
196 | _cleanup_(vacuum_candidate_freep)__attribute__((cleanup(vacuum_candidate_freep))) struct vacuum_candidate *n = NULL((void*)0); | |||
197 | ||||
198 | n = new0(struct vacuum_candidate, 1)((struct vacuum_candidate*) calloc((1), sizeof(struct vacuum_candidate ))); | |||
199 | if (!n) | |||
200 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/coredump/coredump-vacuum.c" , 200, __func__); | |||
201 | ||||
202 | n->oldest_file = strdup(de->d_name); | |||
203 | if (!n->oldest_file) | |||
204 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/coredump/coredump-vacuum.c" , 204, __func__); | |||
| ||||
205 | ||||
206 | n->oldest_mtime = t; | |||
207 | ||||
208 | r = hashmap_put(h, UID_TO_PTR(uid)((void*) (((uintptr_t) (uid))+1)), n); | |||
209 | if (r < 0) | |||
210 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/coredump/coredump-vacuum.c" , 210, __func__); | |||
211 | ||||
212 | c = TAKE_PTR(n)({ typeof(n) _ptr_ = (n); (n) = ((void*)0); _ptr_; }); | |||
213 | } | |||
214 | ||||
215 | c->n_files++; | |||
216 | ||||
217 | if (!worst || | |||
218 | worst->n_files < c->n_files || | |||
219 | (worst->n_files == c->n_files && c->oldest_mtime < worst->oldest_mtime)) | |||
220 | worst = c; | |||
221 | ||||
222 | sum += st.st_blocks * 512; | |||
223 | } | |||
224 | ||||
225 | if (!worst) | |||
226 | break; | |||
227 | ||||
228 | r = vacuum_necessary(dirfd(d), sum, keep_free, max_use); | |||
229 | if (r <= 0) | |||
230 | return r; | |||
231 | ||||
232 | r = unlinkat_deallocate(dirfd(d), worst->oldest_file, 0); | |||
233 | if (r == -ENOENT2) | |||
234 | continue; | |||
235 | if (r < 0) | |||
236 | return log_error_errno(r, "Failed to remove file %s: %m", worst->oldest_file)({ 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/coredump/coredump-vacuum.c", 236, __func__, "Failed to remove file %s: %m" , worst->oldest_file) : -abs(_e); }); | |||
237 | ||||
238 | log_info("Removed old coredump %s.", worst->oldest_file)({ 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/coredump/coredump-vacuum.c", 238, __func__, "Removed old coredump %s." , worst->oldest_file) : -abs(_e); }); | |||
239 | } | |||
240 | ||||
241 | return 0; | |||
242 | ||||
243 | fail: | |||
244 | return log_error_errno(errno, "Failed to read directory: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/coredump/coredump-vacuum.c", 244 , __func__, "Failed to read directory: %m") : -abs(_e); }); | |||
245 | } |