| From 8ea1f4267bae6b37a4a4d474aec2692a4d211621 Mon Sep 17 00:00:00 2001 |
| From: Fedora systemd team <systemd-maint@redhat.com> |
| Date: Wed, 7 Jan 2015 13:34:02 +0100 |
| Subject: [PATCH] journald: when we detect the journal file we are about to |
| write to has been deleted, rotate |
| |
| https://bugzilla.redhat.com/show_bug.cgi?id=1171719 |
| |
| (cherry-picked from 2678031a179a9b91fc799f8ef951a548c66c4b49) |
| --- |
| src/journal/journal-file.c | 65 ++++++++++++++++++++++++++++++---------- |
| src/journal/journal-file.h | 1 + |
| src/journal/journald-server.c | 6 +++- |
| src/journal/test-journal-flush.c | 3 +- |
| 4 files changed, 57 insertions(+), 18 deletions(-) |
| |
| diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c |
| index c5d2d19..a99265b 100644 |
| --- a/src/journal/journal-file.c |
| +++ b/src/journal/journal-file.c |
| @@ -68,6 +68,9 @@ |
| /* How much to increase the journal file size at once each time we allocate something new. */ |
| #define FILE_SIZE_INCREASE (8ULL*1024ULL*1024ULL) /* 8MB */ |
| |
| +/* Reread fstat() of the file for detecting deletions at least this often */ |
| +#define LAST_STAT_REFRESH_USEC (5*USEC_PER_SEC) |
| + |
| static int journal_file_set_online(JournalFile *f) { |
| assert(f); |
| |
| @@ -312,6 +315,22 @@ static int journal_file_verify_header(JournalFile *f) { |
| return 0; |
| } |
| |
| +static int journal_file_fstat(JournalFile *f) { |
| + assert(f); |
| + assert(f->fd >= 0); |
| + |
| + if (fstat(f->fd, &f->last_stat) < 0) |
| + return -errno; |
| + |
| + f->last_stat_usec = now(CLOCK_MONOTONIC); |
| + |
| + /* Refuse appending to files that are already deleted */ |
| + if (f->last_stat.st_nlink <= 0) |
| + return -EIDRM; |
| + |
| + return 0; |
| +} |
| + |
| static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) { |
| uint64_t old_size, new_size; |
| int r; |
| @@ -330,8 +349,21 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) |
| if (new_size < le64toh(f->header->header_size)) |
| new_size = le64toh(f->header->header_size); |
| |
| - if (new_size <= old_size) |
| - return 0; |
| + if (new_size <= old_size) { |
| + |
| + /* We already pre-allocated enough space, but before |
| + * we write to it, let's check with fstat() if the |
| + * file got deleted, in order make sure we don't throw |
| + * away the data immediately. Don't check fstat() for |
| + * all writes though, but only once ever 10s. */ |
| + |
| + if (f->last_stat_usec + LAST_STAT_REFRESH_USEC > now(CLOCK_MONOTONIC)) |
| + return 0; |
| + |
| + return journal_file_fstat(f); |
| + } |
| + |
| + /* Allocate more space. */ |
| |
| if (f->metrics.max_size > 0 && new_size > f->metrics.max_size) |
| return -E2BIG; |
| @@ -366,15 +398,14 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) |
| if (r != 0) |
| return -r; |
| |
| - if (fstat(f->fd, &f->last_stat) < 0) |
| - return -errno; |
| - |
| f->header->arena_size = htole64(new_size - le64toh(f->header->header_size)); |
| |
| - return 0; |
| + return journal_file_fstat(f); |
| } |
| |
| static int journal_file_move_to(JournalFile *f, int context, bool keep_always, uint64_t offset, uint64_t size, void **ret) { |
| + int r; |
| + |
| assert(f); |
| assert(ret); |
| |
| @@ -386,8 +417,11 @@ static int journal_file_move_to(JournalFile *f, int context, bool keep_always, u |
| /* Hmm, out of range? Let's refresh the fstat() data |
| * first, before we trust that check. */ |
| |
| - if (fstat(f->fd, &f->last_stat) < 0 || |
| - offset + size > (uint64_t) f->last_stat.st_size) |
| + r = journal_file_fstat(f); |
| + if (r < 0) |
| + return r; |
| + |
| + if (offset + size > (uint64_t) f->last_stat.st_size) |
| return -EADDRNOTAVAIL; |
| } |
| |
| @@ -2511,10 +2545,9 @@ int journal_file_open( |
| goto fail; |
| } |
| |
| - if (fstat(f->fd, &f->last_stat) < 0) { |
| - r = -errno; |
| + r = journal_file_fstat(f); |
| + if (r < 0) |
| goto fail; |
| - } |
| |
| if (f->last_stat.st_size == 0 && f->writable) { |
| uint64_t crtime; |
| @@ -2546,10 +2579,9 @@ int journal_file_open( |
| if (r < 0) |
| goto fail; |
| |
| - if (fstat(f->fd, &f->last_stat) < 0) { |
| - r = -errno; |
| + r = journal_file_fstat(f); |
| + if (r < 0) |
| goto fail; |
| - } |
| |
| newly_created = true; |
| } |
| @@ -2657,8 +2689,11 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal) { |
| if (r < 0) |
| return -ENOMEM; |
| |
| + /* Try to rename the file to the archived version. If the file |
| + * already was deleted, we'll get ENOENT, let's ignore that |
| + * case. */ |
| r = rename(old_file->path, p); |
| - if (r < 0) |
| + if (r < 0 && errno != ENOENT) |
| return -errno; |
| |
| old_file->header->state = STATE_ARCHIVED; |
| diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h |
| index 211e121..15f1301 100644 |
| --- a/src/journal/journal-file.h |
| +++ b/src/journal/journal-file.h |
| @@ -66,6 +66,7 @@ typedef struct JournalFile { |
| |
| char *path; |
| struct stat last_stat; |
| + usec_t last_stat_usec; |
| |
| Header *header; |
| HashItem *data_hash_table; |
| diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c |
| index 80c9736..24c4d3c 100644 |
| --- a/src/journal/journald-server.c |
| +++ b/src/journal/journald-server.c |
| @@ -315,6 +315,7 @@ static int do_rotate(Server *s, JournalFile **f, const char* name, |
| name); |
| else |
| server_fix_perms(s, *f, uid); |
| + |
| return r; |
| } |
| |
| @@ -457,7 +458,8 @@ bool shall_try_append_again(JournalFile *f, int r) { |
| -EPROTONOSUPPORT Unsupported feature |
| -EBADMSG Corrupted |
| -ENODATA Truncated |
| - -ESHUTDOWN Already archived */ |
| + -ESHUTDOWN Already archived |
| + -EIDRM Journal file has been deleted */ |
| |
| if (r == -E2BIG || r == -EFBIG || r == -EDQUOT || r == -ENOSPC) |
| log_debug("%s: Allocation limit reached, rotating.", f->path); |
| @@ -469,6 +471,8 @@ bool shall_try_append_again(JournalFile *f, int r) { |
| log_info("%s: Unsupported feature, rotating.", f->path); |
| else if (r == -EBADMSG || r == -ENODATA || r == ESHUTDOWN) |
| log_warning("%s: Journal file corrupted, rotating.", f->path); |
| + else if (r == -EIDRM) |
| + log_warning("%s: Journal file has been deleted, rotating.", f->path); |
| else |
| return false; |
| |
| diff --git a/src/journal/test-journal-flush.c b/src/journal/test-journal-flush.c |
| index 0ca24e0..40ede4a 100644 |
| --- a/src/journal/test-journal-flush.c |
| +++ b/src/journal/test-journal-flush.c |
| @@ -39,8 +39,6 @@ int main(int argc, char *argv[]) { |
| r = journal_file_open(fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, &new_journal); |
| assert_se(r >= 0); |
| |
| - unlink(fn); |
| - |
| r = sd_journal_open(&j, 0); |
| assert_se(r >= 0); |
| |
| @@ -68,6 +66,7 @@ int main(int argc, char *argv[]) { |
| |
| journal_file_close(new_journal); |
| |
| + unlink(fn); |
| assert_se(rmdir(dn) == 0); |
| |
| return 0; |
| -- |
| 2.2.0 |
| |