From: Darrick J. Wong <djwong@xxxxxxxxxx> In iomap mode, the kernel is responsible for maintaining timestamps because file writes don't upcall to fuse2fs. The kernel's predicate for deciding if [cm]time should be updated bases its decisions off [cm]time being an exact match for the coarse clock (instead of checking that [cm]time < coarse_clock) which means that fuse2fs setting a fine-grained timestamp that is slightly ahead of the coarse clock can result in timestamps appearing to go backwards. generic/423 doesn't like seeing btime > ctime from statx, so we'll use the coarse clock in iomap mode. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- misc/fuse2fs.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c index ddc647f32c5df6..54f501b36d808b 100644 --- a/misc/fuse2fs.c +++ b/misc/fuse2fs.c @@ -575,8 +575,24 @@ static inline void fuse2fs_dump_extents(struct fuse2fs *ff, ext2_ino_t ino, ext2fs_extent_free(extents); } -static void get_now(struct timespec *now) +static void fuse2fs_get_now(struct fuse2fs *ff, struct timespec *now) { +#ifdef CLOCK_REALTIME_COARSE + /* + * In iomap mode, the kernel is responsible for maintaining timestamps + * because file writes don't upcall to fuse2fs. The kernel's predicate + * for deciding if [cm]time should be updated bases its decisions off + * [cm]time being an exact match for the coarse clock (instead of + * checking that [cm]time < coarse_clock) which means that fuse2fs + * setting a fine-grained timestamp that is slightly ahead of the + * coarse clock can result in timestamps appearing to go backwards. + * generic/423 doesn't like seeing btime > ctime from statx, so we'll + * use the coarse clock in iomap mode. + */ + if (fuse2fs_iomap_does_fileio(ff) && + !clock_gettime(CLOCK_REALTIME_COARSE, now)) + return; +#endif #ifdef CLOCK_REALTIME if (!clock_gettime(CLOCK_REALTIME, now)) return; @@ -604,7 +620,7 @@ static void fuse2fs_init_timestamps(struct fuse2fs *ff, ext2_ino_t ino, { struct timespec now; - get_now(&now); + fuse2fs_get_now(ff, &now); EXT4_INODE_SET_XTIME(i_atime, &now, inode); EXT4_INODE_SET_XTIME(i_ctime, &now, inode); EXT4_INODE_SET_XTIME(i_mtime, &now, inode); @@ -623,7 +639,7 @@ static int fuse2fs_update_ctime(struct fuse2fs *ff, ext2_ino_t ino, struct timespec now; struct ext2_inode_large inode; - get_now(&now); + fuse2fs_get_now(ff, &now); /* If user already has a inode buffer, just update that */ if (pinode) { @@ -669,7 +685,7 @@ static int fuse2fs_update_atime(struct fuse2fs *ff, ext2_ino_t ino) pinode = &inode; EXT4_INODE_GET_XTIME(i_atime, &atime, pinode); EXT4_INODE_GET_XTIME(i_mtime, &mtime, pinode); - get_now(&now); + fuse2fs_get_now(ff, &now); datime = atime.tv_sec + ((double)atime.tv_nsec / NSEC_PER_SEC); dmtime = mtime.tv_sec + ((double)mtime.tv_nsec / NSEC_PER_SEC); @@ -704,7 +720,7 @@ static int fuse2fs_update_mtime(struct fuse2fs *ff, ext2_ino_t ino, struct timespec now; if (pinode) { - get_now(&now); + fuse2fs_get_now(ff, &now); EXT4_INODE_SET_XTIME(i_mtime, &now, pinode); EXT4_INODE_SET_XTIME(i_ctime, &now, pinode); increment_version(pinode); @@ -719,7 +735,7 @@ static int fuse2fs_update_mtime(struct fuse2fs *ff, ext2_ino_t ino, if (err) return translate_error(fs, ino, err); - get_now(&now); + fuse2fs_get_now(ff, &now); EXT4_INODE_SET_XTIME(i_mtime, &now, &inode); EXT4_INODE_SET_XTIME(i_ctime, &now, &inode); increment_version(&inode); @@ -4380,9 +4396,9 @@ static int op_utimens(const char *path, const struct timespec ctv[2] tv[1] = ctv[1]; #ifdef UTIME_NOW if (tv[0].tv_nsec == UTIME_NOW) - get_now(tv); + fuse2fs_get_now(ff, tv); if (tv[1].tv_nsec == UTIME_NOW) - get_now(tv + 1); + fuse2fs_get_now(ff, tv + 1); #endif /* UTIME_NOW */ #ifdef UTIME_OMIT if (tv[0].tv_nsec != UTIME_OMIT) @@ -6917,7 +6933,7 @@ static int __translate_error(ext2_filsys fs, ext2_ino_t ino, errcode_t err, error_message(err), func, line); /* Make a note in the error log */ - get_now(&now); + fuse2fs_get_now(ff, &now); ext2fs_set_tstamp(fs->super, s_last_error_time, now.tv_sec); fs->super->s_last_error_ino = ino; fs->super->s_last_error_line = line;