diff --git a/src/server/receiver.c b/src/server/receiver.c index c811b16..bd7aaf6 100644 --- a/src/server/receiver.c +++ b/src/server/receiver.c @@ -66,9 +66,11 @@ static bool receiver_process_batch(Config* config, int file_descriptor) { struct stat st; bool has_old = file_stat_secure(full_path, &st); long long old_mtime_nsec = 0; + if (has_old) { #ifdef __linux__ - old_mtime_nsec = st.st_mtim.tv_nsec; + old_mtime_nsec = st.st_mtim.tv_nsec; #endif + } bool match = has_old && (unsigned long long)st.st_size == check_size && metadata_mtime_matches(st.st_mtime, old_mtime_nsec, (time_t)check_mtime, (long)check_mtime_nsec, config->modify_window); diff --git a/src/server/server.c b/src/server/server.c index aa1879f..536a08d 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -183,9 +183,11 @@ int receive_files(Config* config, int fd) { } bool has_old = full_path && file_stat_secure(full_path, &st); long long old_mtime_nsec = 0; + if (has_old) { #ifdef __linux__ - old_mtime_nsec = st.st_mtim.tv_nsec; + old_mtime_nsec = st.st_mtim.tv_nsec; #endif + } bool match = has_old && (unsigned long long)st.st_size == check_size && metadata_mtime_matches(st.st_mtime, old_mtime_nsec, (time_t)check_mtime, (long)check_mtime_nsec, config->modify_window); diff --git a/src/shared/metadata.c b/src/shared/metadata.c index ed1f9eb..0ea4139 100644 --- a/src/shared/metadata.c +++ b/src/shared/metadata.c @@ -38,6 +38,10 @@ bool metadata_mtime_matches(time_t left_sec, long left_nsec, time_t right_sec, l seconds = right - left; nanoseconds = (int64_t)right_nsec - (int64_t)left_nsec; } + if (nanoseconds < 0) { + seconds--; + nanoseconds += 1000000000LL; + } return seconds < modify_window || (seconds == modify_window && nanoseconds == 0); } diff --git a/tests/test_metadata.c b/tests/test_metadata.c index 02fee7f..1c8f5df 100644 --- a/tests/test_metadata.c +++ b/tests/test_metadata.c @@ -126,6 +126,10 @@ static void test_metadata_mtime_window() { EXPECT_TRUE(metadata_mtime_matches(100, 100000000, 101, 600000000, 2)); EXPECT_FALSE(metadata_mtime_matches(100, 100000000, 102, 600000000, 2)); EXPECT_TRUE(metadata_mtime_matches(100, 100000000, 102, 100000000, 2)); + EXPECT_TRUE(metadata_mtime_matches(100, 900000000, 102, 100000000, 2)); + EXPECT_FALSE(metadata_mtime_matches(100, 100000000, 102, 900000000, 2)); + EXPECT_TRUE(metadata_mtime_matches(100, 900000000, 102, 900000000, 2)); + EXPECT_FALSE(metadata_mtime_matches(100, 900000000, 101, 100000001, 0)); EXPECT_FALSE(metadata_mtime_matches(100, 100000000, 100, 100000001, 0)); EXPECT_TRUE(metadata_mtime_matches(100, 100000000, 100, 100000000, 0)); } diff --git a/tests/test_server.c b/tests/test_server.c index 8e86795..d17137c 100644 --- a/tests/test_server.c +++ b/tests/test_server.c @@ -182,11 +182,40 @@ static void test_receive_manifest_rejects_traversal() { config_delete(cfg); } +static void test_receive_incremental_check_rejects_invalid_nanoseconds() { + Config* cfg = config_create(); + EXPECT_NOT_NULL(cfg); + cfg->receive_root_directory = str_dup("/tmp/dst"); + + int p[2]; + EXPECT_EQ_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, p), 0); + io_set_fds(p[0], p[1]); + EXPECT_TRUE(send_str(p[1], "file.txt")); + unsigned long long size = 0; + long long mtime = 100; + long long mtime_nsec = 1000000000LL; + EXPECT_TRUE(send_n_data(p[1], &size, sizeof(size))); + EXPECT_TRUE(send_n_data(p[1], &mtime, sizeof(mtime))); + EXPECT_TRUE(send_n_data(p[1], &mtime_nsec, sizeof(mtime_nsec))); + + bool skipped = false; + EXPECT_NULL(receive_incremental_check(p[0], cfg, &skipped)); + Status status; + EXPECT_TRUE(receive_status(p[1], &status)); + EXPECT_EQ_INT(status, STATUS_ERROR); + EXPECT_FALSE(skipped); + + close(p[0]); + close(p[1]); + config_delete(cfg); +} + void test_server() { if (!is_running_under_valgrind()) { test_receive_files_finished(); test_receive_files_single_file(); test_receive_files_abort(); test_receive_manifest_rejects_traversal(); + test_receive_incremental_check_rejects_invalid_nanoseconds(); } }