diff options
author | Lennart Poettering <lennart@poettering.net> | 2021-02-04 18:06:54 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2021-02-04 18:25:25 +0100 |
commit | eff57d1c2f4db9e578cfe68a1eddb5e7bf45cc2d (patch) | |
tree | c36fca4322d9c5a243e0f7183def04199193dee3 /src/basic/fs-util.c | |
parent | xdg-autostart: Generate autostart services with templated name (diff) | |
download | systemd-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.c | 28 |
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: |