#include <unistd.h>
#include "chattr-util.h"
+#include "copy.h"
#include "fd-util.h"
#include "format-util.h"
#include "journal-authenticate.h"
#include "path-util.h"
#include "random-util.h"
#include "set.h"
+#include "stat-util.h"
#include "sync-util.h"
static int journald_file_truncate(JournalFile *f) {
* As a result we use atomic operations on f->offline_state for inter-thread communications with
* journal_file_set_offline() and journal_file_set_online(). */
static void journald_file_set_offline_internal(JournaldFile *f) {
+ int r;
+
assert(f);
assert(f->file->fd >= 0);
assert(f->file->header);
f->file->header->state = f->file->archive ? STATE_ARCHIVED : STATE_OFFLINE;
(void) fsync(f->file->fd);
+
+ /* If we've archived the journal file, first try to re-enable COW on the file. If the
+ * FS_NOCOW_FL flag was never set or we succesfully removed it, continue. If we fail
+ * to remove the flag on the archived file, rewrite the file without the NOCOW flag.
+ * We need this fallback because on some filesystems (BTRFS), the NOCOW flag cannot
+ * be removed after data has been written to a file. The only way to remove it is to
+ * copy all data to a new file without the NOCOW flag set. */
+
+ if (f->file->archive) {
+ r = chattr_fd(f->file->fd, 0, FS_NOCOW_FL, NULL);
+ if (r >= 0)
+ continue;
+
+ log_debug_errno(r, "Failed to re-enable copy-on-write for %s: %m, rewriting file", f->file->path);
+
+ r = copy_file_atomic(f->file->path, f->file->path, f->file->mode, 0, FS_NOCOW_FL, COPY_REPLACE | COPY_FSYNC);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to rewrite %s: %m", f->file->path);
+ continue;
+ }
+ }
+
break;
case OFFLINE_OFFLINING:
if (f->mmap && f->cache_fd)
mmap_cache_fd_free(f->cache_fd);
- if (f->fd >= 0 && f->defrag_on_close) {
-
- /* Be friendly to btrfs: turn COW back on again now,
- * and defragment the file. We won't write to the file
- * ever again, hence remove all fragmentation, and
- * reenable all the good bits COW usually provides
- * (such as data checksumming). */
-
- (void) chattr_fd(f->fd, 0, FS_NOCOW_FL, NULL);
- (void) btrfs_defrag_fd(f->fd);
- }
-
if (f->close_fd)
safe_close(f->fd);
free(f->path);
* occurs. */
f->archive = true;
- /* Currently, btrfs is not very good with out write patterns and fragments heavily. Let's defrag our journal
- * files when we archive them */
- f->defrag_on_close = true;
-
return 0;
}
int journal_file_dispose(int dir_fd, const char *fname) {
_cleanup_free_ char *p = NULL;
- _cleanup_close_ int fd = -1;
assert(fname);
if (renameat(dir_fd, fname, dir_fd, p) < 0)
return -errno;
- /* btrfs doesn't cope well with our write pattern and fragments heavily. Let's defrag all files we rotate */
- fd = openat(dir_fd, p, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fd < 0)
- log_debug_errno(errno, "Failed to open file for defragmentation/FS_NOCOW_FL, ignoring: %m");
- else {
- (void) chattr_fd(fd, 0, FS_NOCOW_FL, NULL);
- (void) btrfs_defrag_fd(fd);
- }
-
return 0;
}