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