X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fjournal%2Fjournal-vacuum.c;h=692b875fae37299e1acf57a7640ba85d8ec93a76;hb=07630cea1f3a845c09309f197ac7c4f11edd3b62;hp=731f6c770f8938ae9982ea7573eed70d9f00a976;hpb=4fa25d62bd586b56fa6a30009f41ce6dbc5fdd54;p=thirdparty%2Fsystemd.git diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c index 731f6c770f8..692b875fae3 100644 --- a/src/journal/journal-vacuum.c +++ b/src/journal/journal-vacuum.c @@ -19,30 +19,27 @@ along with systemd; If not, see . ***/ -#include #include #include -#include #include -#ifdef HAVE_XATTR -#include -#endif +#include "sd-id128.h" #include "journal-def.h" #include "journal-file.h" -#include "journal-vacuum.h" -#include "sd-id128.h" +#include "string-util.h" #include "util.h" +#include "journal-vacuum.h" + struct vacuum_info { - off_t usage; + uint64_t usage; char *filename; uint64_t realtime; + sd_id128_t seqnum_id; uint64_t seqnum; - bool have_seqnum; }; @@ -73,78 +70,97 @@ static int vacuum_compare(const void *_a, const void *_b) { } static void patch_realtime( - const char *dir, + int fd, const char *fn, const struct stat *st, unsigned long long *realtime) { - usec_t x; - -#ifdef HAVE_XATTR - uint64_t crtime; - _cleanup_free_ const char *path = NULL; -#endif + usec_t x, crtime = 0; /* The timestamp was determined by the file name, but let's * see if the file might actually be older than the file name * suggested... */ - assert(dir); + assert(fd >= 0); assert(fn); assert(st); assert(realtime); x = timespec_load(&st->st_ctim); - if (x > 0 && x != (usec_t) -1 && x < *realtime) + if (x > 0 && x != USEC_INFINITY && x < *realtime) *realtime = x; x = timespec_load(&st->st_atim); - if (x > 0 && x != (usec_t) -1 && x < *realtime) + if (x > 0 && x != USEC_INFINITY && x < *realtime) *realtime = x; x = timespec_load(&st->st_mtim); - if (x > 0 && x != (usec_t) -1 && x < *realtime) + if (x > 0 && x != USEC_INFINITY && x < *realtime) *realtime = x; -#ifdef HAVE_XATTR /* Let's read the original creation time, if possible. Ideally * we'd just query the creation time the FS might provide, but * unfortunately there's currently no sane API to query * it. Hence let's implement this manually... */ - /* Unfortunately there is is not fgetxattrat(), so we need to - * go via path here. :-( */ + if (fd_getcrtime_at(fd, fn, &crtime, 0) >= 0) { + if (crtime < *realtime) + *realtime = crtime; + } +} - path = strjoin(dir, "/", fn, NULL); - if (!path) - return; +static int journal_file_empty(int dir_fd, const char *name) { + _cleanup_close_ int fd; + struct stat st; + le64_t n_entries; + ssize_t n; + + fd = openat(dir_fd, name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK|O_NOATIME); + if (fd < 0) { + /* Maybe failed due to O_NOATIME and lack of privileges? */ + fd = openat(dir_fd, name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); + if (fd < 0) + return -errno; + } - if (getxattr(path, "user.crtime_usec", &crtime, sizeof(crtime)) == sizeof(crtime)) { - crtime = le64toh(crtime); + if (fstat(fd, &st) < 0) + return -errno; - if (crtime > 0 && crtime != (uint64_t) -1 && crtime < *realtime) - *realtime = crtime; - } -#endif + /* If an offline file doesn't even have a header we consider it empty */ + if (st.st_size < (off_t) sizeof(Header)) + return 1; + + /* If the number of entries is empty, we consider it empty, too */ + n = pread(fd, &n_entries, sizeof(n_entries), offsetof(Header, n_entries)); + if (n < 0) + return -errno; + if (n != sizeof(n_entries)) + return -EIO; + + return le64toh(n_entries) <= 0; } int journal_directory_vacuum( const char *directory, uint64_t max_use, - uint64_t min_free, + uint64_t n_max_files, usec_t max_retention_usec, - usec_t *oldest_usec) { + usec_t *oldest_usec, + bool verbose) { - DIR *d; - int r = 0; + _cleanup_closedir_ DIR *d = NULL; struct vacuum_info *list = NULL; - unsigned n_list = 0, n_allocated = 0, i; - uint64_t sum = 0; + unsigned n_list = 0, i, n_active_files = 0; + size_t n_allocated = 0; + uint64_t sum = 0, freed = 0; usec_t retention_limit = 0; + char sbytes[FORMAT_BYTES_MAX]; + struct dirent *de; + int r; assert(directory); - if (max_use <= 0 && min_free <= 0 && max_retention_usec <= 0) + if (max_use <= 0 && max_retention_usec <= 0 && n_max_files <= 0) return 0; if (max_retention_usec > 0) { @@ -159,28 +175,20 @@ int journal_directory_vacuum( if (!d) return -errno; - for (;;) { - int k; - struct dirent *de; - union dirent_storage buf; - size_t q; - struct stat st; - char *p; + FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) { + unsigned long long seqnum = 0, realtime; + _cleanup_free_ char *p = NULL; sd_id128_t seqnum_id; bool have_seqnum; + uint64_t size; + struct stat st; + size_t q; - k = readdir_r(d, &buf.de, &de); - if (k != 0) { - r = -k; - goto finish; - } - - if (!de) - break; - - if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) + if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { + log_debug_errno(errno, "Failed to stat file %s while vacuuming, ignoring: %m", de->d_name); continue; + } if (!S_ISREG(st.st_mode)) continue; @@ -189,15 +197,20 @@ int journal_directory_vacuum( if (endswith(de->d_name, ".journal")) { - /* Vacuum archived files */ + /* Vacuum archived files. Active files are + * left around */ - if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8) + if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8) { + n_active_files++; continue; + } if (de->d_name[q-8-16-1] != '-' || de->d_name[q-8-16-1-16-1] != '-' || - de->d_name[q-8-16-1-16-1-32-1] != '@') + de->d_name[q-8-16-1-16-1-32-1] != '@') { + n_active_files++; continue; + } p = strdup(de->d_name); if (!p) { @@ -207,12 +220,12 @@ int journal_directory_vacuum( de->d_name[q-8-16-1-16-1] = 0; if (sd_id128_from_string(de->d_name + q-8-16-1-16-1-32, &seqnum_id) < 0) { - free(p); + n_active_files++; continue; } if (sscanf(de->d_name + q-8-16-1-16, "%16llx-%16llx.journal", &seqnum, &realtime) != 2) { - free(p); + n_active_files++; continue; } @@ -223,12 +236,16 @@ int journal_directory_vacuum( /* Vacuum corrupted files */ - if (q < 1 + 16 + 1 + 16 + 8 + 1) + if (q < 1 + 16 + 1 + 16 + 8 + 1) { + n_active_files ++; continue; + } if (de->d_name[q-1-8-16-1] != '-' || - de->d_name[q-1-8-16-1-16-1] != '@') + de->d_name[q-1-8-16-1-16-1] != '@') { + n_active_files ++; continue; + } p = strdup(de->d_name); if (!p) { @@ -237,82 +254,94 @@ int journal_directory_vacuum( } if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) { - free(p); + n_active_files ++; continue; } have_seqnum = false; - } else - /* We do not vacuum active files or unknown files! */ + } else { + /* We do not vacuum unknown files! */ + log_debug("Not vacuuming unknown file %s.", de->d_name); continue; + } - patch_realtime(directory, de->d_name, &st, &realtime); + size = 512UL * (uint64_t) st.st_blocks; - if (n_list >= n_allocated) { - struct vacuum_info *j; + r = journal_file_empty(dirfd(d), p); + if (r < 0) { + log_debug_errno(r, "Failed check if %s is empty, ignoring: %m", p); + continue; + } + if (r > 0) { + /* Always vacuum empty non-online files. */ - n_allocated = MAX(n_allocated * 2U, 8U); - j = realloc(list, n_allocated * sizeof(struct vacuum_info)); - if (!j) { - free(p); - r = -ENOMEM; - goto finish; - } + if (unlinkat(dirfd(d), p, 0) >= 0) { + + log_full(verbose ? LOG_INFO : LOG_DEBUG, + "Deleted empty archived journal %s/%s (%s).", directory, p, format_bytes(sbytes, sizeof(sbytes), size)); - list = j; + freed += size; + } else if (errno != ENOENT) + log_warning_errno(errno, "Failed to delete empty archived journal %s/%s: %m", directory, p); + + continue; + } + + patch_realtime(dirfd(d), p, &st, &realtime); + + if (!GREEDY_REALLOC(list, n_allocated, n_list + 1)) { + r = -ENOMEM; + goto finish; } list[n_list].filename = p; - list[n_list].usage = 512UL * (uint64_t) st.st_blocks; + list[n_list].usage = size; list[n_list].seqnum = seqnum; list[n_list].realtime = realtime; list[n_list].seqnum_id = seqnum_id; list[n_list].have_seqnum = have_seqnum; - - sum += list[n_list].usage; - n_list ++; + + p = NULL; + sum += size; } - if (n_list > 0) - qsort(list, n_list, sizeof(struct vacuum_info), vacuum_compare); + qsort_safe(list, n_list, sizeof(struct vacuum_info), vacuum_compare); for (i = 0; i < n_list; i++) { - struct statvfs ss; + unsigned left; - if (fstatvfs(dirfd(d), &ss) < 0) { - r = -errno; - goto finish; - } + left = n_active_files + n_list - i; if ((max_retention_usec <= 0 || list[i].realtime >= retention_limit) && (max_use <= 0 || sum <= max_use) && - (min_free <= 0 || (uint64_t) ss.f_bavail * (uint64_t) ss.f_bsize >= min_free)) + (n_max_files <= 0 || left <= n_max_files)) break; if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) { - log_debug("Deleted archived journal %s/%s.", directory, list[i].filename); + log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted archived journal %s/%s (%s).", directory, list[i].filename, format_bytes(sbytes, sizeof(sbytes), list[i].usage)); + freed += list[i].usage; - if ((uint64_t) list[i].usage > sum) + if (list[i].usage < sum) sum -= list[i].usage; else sum = 0; } else if (errno != ENOENT) - log_warning("Failed to delete %s/%s: %m", directory, list[i].filename); + log_warning_errno(errno, "Failed to delete archived journal %s/%s: %m", directory, list[i].filename); } if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec)) *oldest_usec = list[i].realtime; + r = 0; + finish: for (i = 0; i < n_list; i++) free(list[i].filename); - free(list); - if (d) - closedir(d); + log_full(verbose ? LOG_INFO : LOG_DEBUG, "Vacuuming done, freed %s of archived journals on disk.", format_bytes(sbytes, sizeof(sbytes), freed)); return r; }