21255d
From 29c5b8dd6228c4401f034ca0aa85f99ac42cf8dd Mon Sep 17 00:00:00 2001
21255d
From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= <msekleta@redhat.com>
21255d
Date: Thu, 5 Nov 2020 17:55:25 +0100
21255d
Subject: [PATCH] basic/stat-util: make mtime check stricter and use entire
21255d
 timestamp
21255d
21255d
Note that st_mtime member of struct stat is defined as follows,
21255d
21255d
 #define st_mtime st_mtim.tv_sec
21255d
21255d
Hence we omitted checking nanosecond part of the timestamp (struct
21255d
timespec) and possibly would miss modifications that happened within the
21255d
same second.
21255d
21255d
(cherry picked from commit a59b0a9f768f6e27b25f4f1bab6de08842e78d74)
21255d
21255d
Related: #1642728
21255d
---
21255d
 src/basic/stat-util.c | 22 ++++++++++++++++++++++
21255d
 src/basic/stat-util.h |  2 ++
21255d
 2 files changed, 24 insertions(+)
21255d
21255d
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
21255d
index 26aee9bad6..c61c4c0517 100644
21255d
--- a/src/basic/stat-util.c
21255d
+++ b/src/basic/stat-util.c
21255d
@@ -287,3 +287,25 @@ int fd_verify_regular(int fd) {
21255d
 
21255d
         return stat_verify_regular(&st);
21255d
 }
21255d
+
21255d
+bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
21255d
+
21255d
+        /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to
21255d
+         * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file
21255d
+         * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file
21255d
+         * size, backing device, inode type and if this refers to a device not the major/minor.
21255d
+         *
21255d
+         * Note that we don't care if file attributes such as ownership or access mode change, this here is
21255d
+         * about contents of the file. The purpose here is to detect file contents changes, and nothing
21255d
+         * else. */
21255d
+
21255d
+        return a && b &&
21255d
+                (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */
21255d
+                ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 &&  /* same inode type */
21255d
+                a->st_mtim.tv_sec == b->st_mtim.tv_sec &&
21255d
+                a->st_mtim.tv_nsec == b->st_mtim.tv_nsec &&
21255d
+                (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */
21255d
+                a->st_dev == b->st_dev &&
21255d
+                a->st_ino == b->st_ino &&
21255d
+                (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */
21255d
+}
21255d
\ No newline at end of file
21255d
diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h
21255d
index f8014ed30b..9e1a2b70da 100644
21255d
--- a/src/basic/stat-util.h
21255d
+++ b/src/basic/stat-util.h
21255d
@@ -58,3 +58,5 @@ int path_is_temporary_fs(const char *path);
21255d
 
21255d
 int stat_verify_regular(const struct stat *st);
21255d
 int fd_verify_regular(int fd);
21255d
+
21255d
+bool stat_inode_unmodified(const struct stat *a, const struct stat *b);