]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fs-util: make sure conservative_renameat() properly detects identity of longer files
authorLennart Poettering <lennart@poettering.net>
Thu, 4 Feb 2021 17:06:54 +0000 (18:06 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 4 Feb 2021 17:25:25 +0000 (18:25 +0100)
The old code got confused with files with a size >16K. Let's fix that.

Noticed by @benjarobin

Replaces: #18442

src/basic/fs-util.c

index 9290fd8784d11df3fb9ef144c0608e85cae43a1d..f46287d272859c30f82dde9cdaa4a19be2454b10 100644 (file)
@@ -1656,23 +1656,37 @@ int conservative_renameat(
                 goto do_rename;
 
         for (;;) {
-                char buf1[16*1024];
-                char buf2[sizeof(buf1) + 1];
+                uint8_t buf1[16*1024];
+                uint8_t buf2[sizeof(buf1)];
                 ssize_t l1, l2;
 
                 l1 = read(old_fd, buf1, sizeof(buf1));
                 if (l1 < 0)
                         goto do_rename;
 
-                l2 = read(new_fd, buf2, l1 + 1);
-                if (l1 != l2)
-                        goto do_rename;
+                if (l1 == sizeof(buf1))
+                        /* Read the full block, hence read a full block in the other file too */
 
-                if (l1 == 0) /* EOF on both! And everything's the same so far, yay! */
-                        break;
+                        l2 = read(new_fd, buf2, l1);
+                else {
+                        assert((size_t) l1 < sizeof(buf1));
+
+                        /* Short read. This hence was the last block in the first file, and then came
+                         * EOF. Read one byte more in the second file, so that we can verify we hit EOF there
+                         * too. */
+
+                        assert((size_t) (l1 + 1) <= sizeof(buf2));
+                        l2 = read(new_fd, buf2, l1 + 1);
+                }
+                if (l2 != l1)
+                        goto do_rename;
 
                 if (memcmp(buf1, buf2, l1) != 0)
                         goto do_rename;
+
+                if ((size_t) l1 < sizeof(buf1)) /* We hit EOF on the first file, and the second file too, hence exit
+                                                 * now. */
+                        break;
         }
 
 is_same: