]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
stat-util: introduce inode_unmodified_hash_ops
authorLennart Poettering <lennart@amutable.com>
Tue, 24 Mar 2026 16:06:09 +0000 (17:06 +0100)
committerLennart Poettering <lennart@amutable.com>
Tue, 24 Mar 2026 20:24:47 +0000 (21:24 +0100)
This is almost the same as inode_hash_ops, but also hashes + compares
all attributes that could affect the contents of a file. It ignores
"superficial"/"external" attributes such as ownership or access mode
however.

src/basic/stat-util.c
src/basic/stat-util.h

index 98dfa8c5a97374d5d29110ac1df2eeef73f79125..ac218d155201735035d20a2e56f849aa286cbb7b 100644 (file)
@@ -731,6 +731,59 @@ int inode_compare_func(const struct stat *a, const struct stat *b) {
 
 DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free);
 
+void inode_unmodified_hash_func(const struct stat *q, struct siphash *state) {
+        inode_hash_func(q, state);
+
+        siphash24_compress_typesafe(q->st_mtim.tv_sec, state);
+        siphash24_compress_typesafe(q->st_mtim.tv_nsec, state);
+
+        if (S_ISREG(q->st_mode))
+                siphash24_compress_typesafe(q->st_size, state);
+        else {
+                uint64_t invalid = UINT64_MAX;
+                siphash24_compress_typesafe(invalid, state);
+        }
+
+        if (S_ISCHR(q->st_mode) || S_ISBLK(q->st_mode))
+                siphash24_compress_typesafe(q->st_rdev, state);
+        else {
+                dev_t invalid = (dev_t) -1;
+                siphash24_compress_typesafe(invalid, state);
+        }
+}
+
+int inode_unmodified_compare_func(const struct stat *a, const struct stat *b) {
+        int r;
+
+        r = inode_compare_func(a, b);
+        if (r != 0)
+                return r;
+
+        r = CMP(a->st_mtim.tv_sec, b->st_mtim.tv_sec);
+        if (r != 0)
+                return r;
+
+        r = CMP(a->st_mtim.tv_nsec, b->st_mtim.tv_nsec);
+        if (r != 0)
+                return r;
+
+        if (S_ISREG(a->st_mode)) {
+                r = CMP(a->st_size, b->st_size);
+                if (r != 0)
+                        return r;
+        }
+
+        if (S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) {
+                r = CMP(a->st_rdev, b->st_rdev);
+                if (r != 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_unmodified_hash_ops, struct stat, inode_unmodified_hash_func, inode_unmodified_compare_func, free);
+
 const char* inode_type_to_string(mode_t m) {
 
         /* Returns a short string for the inode type. We use the same name as the underlying macros for each
index 939a0fc398db4bb25bd0060bd794177aa6a96e63..2b50c9c55888d41a98b76b490dc470d4a193eb91 100644 (file)
@@ -127,6 +127,12 @@ void inode_hash_func(const struct stat *q, struct siphash *state);
 int inode_compare_func(const struct stat *a, const struct stat *b);
 extern const struct hash_ops inode_hash_ops;
 
+/* This is a more thorough version of the above, and also checks the mtimes, the size, and the rdev. It does
+ * not check "external" attributes such as access mode or ownership. */
+void inode_unmodified_hash_func(const struct stat *q, struct siphash *state);
+int inode_unmodified_compare_func(const struct stat *a, const struct stat *b);
+extern const struct hash_ops inode_unmodified_hash_ops;
+
 DECLARE_STRING_TABLE_LOOKUP(inode_type, mode_t);
 
 /* Macros that check whether the stat/statx structures have been initialized already. For "struct stat" we