diff --git a/gvfs-helper.c b/gvfs-helper.c index ca37be28e4..c7ab63263c 100644 --- a/gvfs-helper.c +++ b/gvfs-helper.c @@ -1882,6 +1882,7 @@ cleanup: static void my_finalize_packfile(struct gh__request_params *params, struct gh__response_status *status, + int b_keep, const struct strbuf *temp_path_pack, const struct strbuf *temp_path_idx, struct strbuf *final_path_pack, @@ -1901,6 +1902,21 @@ static void my_finalize_packfile(struct gh__request_params *params, return; } + if (b_keep) { + struct strbuf keep = STRBUF_INIT; + int fd_keep; + + strbuf_addbuf(&keep, final_path_pack); + strbuf_strip_suffix(&keep, ".pack"); + strbuf_addstr(&keep, ".keep"); + + fd_keep = xopen(keep.buf, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd_keep >= 0) + close(fd_keep); + + strbuf_release(&keep); + } + if (params->result_list) { struct strbuf result_msg = STRBUF_INIT; @@ -1953,7 +1969,7 @@ static void install_packfile(struct gh__request_params *params, create_final_packfile_pathnames("vfs", packfile_checksum.buf, NULL, &final_path_pack, &final_path_idx, &final_filename); - my_finalize_packfile(params, status, + my_finalize_packfile(params, status, 0, &temp_path_pack, &temp_path_idx, &final_path_pack, &final_path_idx, &final_filename); @@ -2049,6 +2065,12 @@ struct ph { /* * Extract the next packfile from the multipack. + * Install {.pack, .idx, .keep} set. + * + * Mark each successfully installed prefetch pack as .keep it as installed + * in case we have errors decoding/indexing later packs within the received + * multipart file. (A later pass can delete the unnecessary .keep files + * from this and any previous invocations.) */ static void extract_packfile_from_multipack( struct gh__request_params *params, @@ -2145,7 +2167,7 @@ static void extract_packfile_from_multipack( } else { /* - * Server send the .idx immediately after the .pack in the + * Server sent the .idx immediately after the .pack in the * data stream. I'm tempted to verify it, but that defeats * the purpose of having it cached... */ @@ -2167,7 +2189,7 @@ static void extract_packfile_from_multipack( &final_path_pack, &final_path_idx, &final_filename); - my_finalize_packfile(params, status, + my_finalize_packfile(params, status, 1, &temp_path_pack, &temp_path_idx, &final_path_pack, &final_path_idx, &final_filename); @@ -2182,6 +2204,56 @@ done: strbuf_release(&final_filename); } +struct keep_files_data { + timestamp_t max_timestamp; + int pos_of_max; + struct string_list *keep_files; +}; + +static void cb_keep_files(const char *full_path, size_t full_path_len, + const char *file_path, void *void_data) +{ + struct keep_files_data *data = void_data; + const char *val; + timestamp_t t; + + /* + * We expect prefetch packfiles named like: + * + * prefetch--.keep + */ + if (!skip_prefix(file_path, "prefetch-", &val)) + return; + if (!ends_with(val, ".keep")) + return; + + t = strtol(val, NULL, 10); + if (t > data->max_timestamp) { + data->pos_of_max = data->keep_files->nr; + data->max_timestamp = t; + } + + string_list_append(data->keep_files, full_path); +} + +static void delete_stale_keep_files( + struct gh__request_params *params, + struct gh__response_status *status) +{ + struct string_list keep_files = STRING_LIST_INIT_DUP; + struct keep_files_data data = { 0, 0, &keep_files }; + int k; + + for_each_file_in_pack_dir(gh__global.buf_odb_path.buf, + cb_keep_files, &data); + for (k = 0; k < keep_files.nr; k++) { + if (k != data.pos_of_max) + unlink(keep_files.items[k].string); + } + + string_list_clear(&keep_files, 0); +} + /* * Cut apart the received multipart response into individual packfiles * and install each one. @@ -2200,6 +2272,7 @@ static void install_prefetch(struct gh__request_params *params, unsigned short np; unsigned short k; int fd = -1; + int nr_installed = 0; struct strbuf temp_path_mp = STRBUF_INIT; @@ -2240,8 +2313,12 @@ static void install_prefetch(struct gh__request_params *params, extract_packfile_from_multipack(params, status, fd, k); if (status->ec != GH__ERROR_CODE__OK) break; + nr_installed++; } + if (nr_installed) + delete_stale_keep_files(params, status); + cleanup: if (fd != -1) close(fd); diff --git a/t/t5799-gvfs-helper.sh b/t/t5799-gvfs-helper.sh index ffb4eea957..aae93931ad 100755 --- a/t/t5799-gvfs-helper.sh +++ b/t/t5799-gvfs-helper.sh @@ -390,6 +390,30 @@ verify_received_packfile_count () { return 0 } +# Verify that we have exactly 1 prefetch .keep file. +# Optionally, verify that it has the given timestamp. +# +verify_prefetch_keeps () { + count=$(( $(ls -1 "$SHARED_CACHE_T1"/pack/prefetch-*.keep | wc -l) )) + if test $count -ne 1 + then + echo "verify_prefetch_keep_file_count: found $count, expected 1." + return 1 + fi + + if test $# -eq 1 + then + count=$(( $(ls -1 "$SHARED_CACHE_T1"/pack/prefetch-$1-*.keep | wc -l) )) + if test $count -ne 1 + then + echo "verify_prefetch_keep_file_count: did not find expected keep file." + return 1 + fi + fi + + return 0 +} + per_test_cleanup () { stop_gvfs_protocol_server @@ -643,6 +667,7 @@ test_expect_success 'basic: PREFETCH w/o arg gets all' ' # packfile. # verify_received_packfile_count 3 && + verify_prefetch_keeps 1200000000 && stop_gvfs_protocol_server && verify_connection_count 1 @@ -664,6 +689,7 @@ test_expect_success 'basic: PREFETCH w/ arg' ' # packfile. # verify_received_packfile_count 2 && + verify_prefetch_keeps 1200000000 && stop_gvfs_protocol_server && verify_connection_count 1 @@ -686,6 +712,7 @@ test_expect_success 'basic: PREFETCH mayhem no_prefetch_idx' ' # packfile. # verify_received_packfile_count 2 && + verify_prefetch_keeps 1200000000 && stop_gvfs_protocol_server && verify_connection_count 1 @@ -707,6 +734,7 @@ test_expect_success 'basic: PREFETCH up-to-date' ' # packfile. # verify_received_packfile_count 2 && + verify_prefetch_keeps 1200000000 && # Ask again for any packfiles newer than what we have cached locally. # @@ -720,6 +748,7 @@ test_expect_success 'basic: PREFETCH up-to-date' ' # packfile. # verify_received_packfile_count 0 && + verify_prefetch_keeps 1200000000 && stop_gvfs_protocol_server && verify_connection_count 2