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