| 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 | } |