summaryrefslogtreecommitdiffstats
path: root/src/basic/fs-util.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2021-02-04 18:06:54 +0100
committerLennart Poettering <lennart@poettering.net>2021-02-04 18:25:25 +0100
commiteff57d1c2f4db9e578cfe68a1eddb5e7bf45cc2d (patch)
treec36fca4322d9c5a243e0f7183def04199193dee3 /src/basic/fs-util.c
parentxdg-autostart: Generate autostart services with templated name (diff)
downloadsystemd-eff57d1c2f4db9e578cfe68a1eddb5e7bf45cc2d.tar.xz
systemd-eff57d1c2f4db9e578cfe68a1eddb5e7bf45cc2d.zip
fs-util: make sure conservative_renameat() properly detects identity of longer files
The old code got confused with files with a size >16K. Let's fix that. Noticed by @benjarobin Replaces: #18442
Diffstat (limited to 'src/basic/fs-util.c')
-rw-r--r--src/basic/fs-util.c28
1 files changed, 21 insertions, 7 deletions
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 9290fd8784..f46287d272 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -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: