Skip to content

Commit a24fb9b

Browse files
authored
Merge pull request systemd#18472 from poettering/conservative-rename-fix
fix conservative_renameat()
2 parents 7bac23e + 06fed30 commit a24fb9b

File tree

2 files changed

+43
-13
lines changed

2 files changed

+43
-13
lines changed

src/basic/fs-util.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,23 +1656,37 @@ int conservative_renameat(
16561656
goto do_rename;
16571657

16581658
for (;;) {
1659-
char buf1[16*1024];
1660-
char buf2[sizeof(buf1) + 1];
1659+
uint8_t buf1[16*1024];
1660+
uint8_t buf2[sizeof(buf1)];
16611661
ssize_t l1, l2;
16621662

16631663
l1 = read(old_fd, buf1, sizeof(buf1));
16641664
if (l1 < 0)
16651665
goto do_rename;
16661666

1667-
l2 = read(new_fd, buf2, l1 + 1);
1668-
if (l1 != l2)
1669-
goto do_rename;
1667+
if (l1 == sizeof(buf1))
1668+
/* Read the full block, hence read a full block in the other file too */
16701669

1671-
if (l1 == 0) /* EOF on both! And everything's the same so far, yay! */
1672-
break;
1670+
l2 = read(new_fd, buf2, l1);
1671+
else {
1672+
assert((size_t) l1 < sizeof(buf1));
1673+
1674+
/* Short read. This hence was the last block in the first file, and then came
1675+
* EOF. Read one byte more in the second file, so that we can verify we hit EOF there
1676+
* too. */
1677+
1678+
assert((size_t) (l1 + 1) <= sizeof(buf2));
1679+
l2 = read(new_fd, buf2, l1 + 1);
1680+
}
1681+
if (l2 != l1)
1682+
goto do_rename;
16731683

16741684
if (memcmp(buf1, buf2, l1) != 0)
16751685
goto do_rename;
1686+
1687+
if ((size_t) l1 < sizeof(buf1)) /* We hit EOF on the first file, and the second file too, hence exit
1688+
* now. */
1689+
break;
16761690
}
16771691

16781692
is_same:

src/test/test-fs-util.c

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "macro.h"
1212
#include "mkdir.h"
1313
#include "path-util.h"
14+
#include "random-util.h"
1415
#include "rm-rf.h"
1516
#include "stdio-util.h"
1617
#include "string-util.h"
@@ -836,12 +837,24 @@ static void test_path_is_encrypted(void) {
836837
test_path_is_encrypted_one("/dev", booted > 0 ? false : -1);
837838
}
838839

840+
static void create_binary_file(const char *p, const void *data, size_t l) {
841+
_cleanup_close_ int fd = -1;
842+
843+
fd = open(p, O_CREAT|O_WRONLY|O_EXCL|O_CLOEXEC, 0600);
844+
assert_se(fd >= 0);
845+
assert_se(write(fd, data, l) == (ssize_t) l);
846+
}
847+
839848
static void test_conservative_rename(void) {
840849
_cleanup_(unlink_and_freep) char *p = NULL;
841850
_cleanup_free_ char *q = NULL;
851+
size_t l = 16*1024 + random_u64() % (32 * 1024); /* some randomly sized buffer 16k…48k */
852+
uint8_t buffer[l+1];
853+
854+
random_bytes(buffer, l);
842855

843856
assert_se(tempfn_random_child(NULL, NULL, &p) >= 0);
844-
assert_se(write_string_file(p, "this is a test", WRITE_STRING_FILE_CREATE) >= 0);
857+
create_binary_file(p, buffer, l);
845858

846859
assert_se(tempfn_random_child(NULL, NULL, &q) >= 0);
847860

@@ -856,27 +869,30 @@ static void test_conservative_rename(void) {
856869
assert_se(access(q, F_OK) < 0 && errno == ENOENT);
857870

858871
/* Check that a manual new writeout is also detected */
859-
assert_se(write_string_file(q, "this is a test", WRITE_STRING_FILE_CREATE) >= 0);
872+
create_binary_file(q, buffer, l);
860873
assert_se(conservative_renameat(AT_FDCWD, q, AT_FDCWD, p) == 0);
861874
assert_se(access(q, F_OK) < 0 && errno == ENOENT);
862875

863876
/* Check that a minimally changed version is detected */
864-
assert_se(write_string_file(q, "this is a_test", WRITE_STRING_FILE_CREATE) >= 0);
877+
buffer[47] = ~buffer[47];
878+
create_binary_file(q, buffer, l);
865879
assert_se(conservative_renameat(AT_FDCWD, q, AT_FDCWD, p) > 0);
866880
assert_se(access(q, F_OK) < 0 && errno == ENOENT);
867881

868882
/* Check that this really is new updated version */
869-
assert_se(write_string_file(q, "this is a_test", WRITE_STRING_FILE_CREATE) >= 0);
883+
create_binary_file(q, buffer, l);
870884
assert_se(conservative_renameat(AT_FDCWD, q, AT_FDCWD, p) == 0);
871885
assert_se(access(q, F_OK) < 0 && errno == ENOENT);
872886

873887
/* Make sure we detect extended files */
874-
assert_se(write_string_file(q, "this is a_testx", WRITE_STRING_FILE_CREATE) >= 0);
888+
buffer[l++] = 47;
889+
create_binary_file(q, buffer, l);
875890
assert_se(conservative_renameat(AT_FDCWD, q, AT_FDCWD, p) > 0);
876891
assert_se(access(q, F_OK) < 0 && errno == ENOENT);
877892

878893
/* Make sure we detect truncated files */
879-
assert_se(write_string_file(q, "this is a_test", WRITE_STRING_FILE_CREATE) >= 0);
894+
l--;
895+
create_binary_file(q, buffer, l);
880896
assert_se(conservative_renameat(AT_FDCWD, q, AT_FDCWD, p) > 0);
881897
assert_se(access(q, F_OK) < 0 && errno == ENOENT);
882898
}

0 commit comments

Comments
 (0)