Skip to content

Commit 1188a9b

Browse files
derrickstoleemjcheetham
authored andcommittedJul 23, 2024
Merge gvfs-helper prefetch feature
Includes these pull requests: git-for-windows#227 git-for-windows#228 git-for-windows#229 git-for-windows#231 git-for-windows#240 Signed-off-by: Derrick Stolee <[email protected]>
2 parents c2db5a3 + e75a40f commit 1188a9b

10 files changed

+1786
-335
lines changed
 

‎Documentation/config/core.txt

+4
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,10 @@ core.gvfs::
791791
is first accessed and brought down to the client. Git.exe can't
792792
currently tell the first access vs subsequent accesses so this
793793
flag just blocks them from occurring at all.
794+
GVFS_PREFETCH_DURING_FETCH::
795+
Bit value 128
796+
While performing a `git fetch` command, use the gvfs-helper to
797+
perform a "prefetch" of commits and trees.
794798
--
795799

796800
core.useGvfsHelper::

‎Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -2863,7 +2863,7 @@ gettext.sp gettext.s gettext.o: GIT-PREFIX
28632863
gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \
28642864
-DGIT_LOCALE_PATH='"$(localedir_relative_SQ)"'
28652865

2866-
http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp: SP_EXTRA_FLAGS += \
2866+
http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp gvfs-helper.sp: SP_EXTRA_FLAGS += \
28672867
-DCURL_DISABLE_TYPECHECK
28682868

28692869
pack-revindex.sp: SP_EXTRA_FLAGS += -Wno-memcpy-max-count

‎builtin/fetch.c

+13
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
#include "string-list.h"
1919
#include "remote.h"
2020
#include "transport.h"
21+
#include "gvfs.h"
22+
#include "gvfs-helper-client.h"
23+
#include "packfile.h"
2124
#include "run-command.h"
2225
#include "parse-options.h"
2326
#include "sigchain.h"
@@ -1160,6 +1163,13 @@ static int store_updated_refs(struct display_state *display_state,
11601163

11611164
opt.exclude_hidden_refs_section = "fetch";
11621165
rm = ref_map;
1166+
1167+
/*
1168+
* Before checking connectivity, be really sure we have the
1169+
* latest pack-files loaded into memory.
1170+
*/
1171+
reprepare_packed_git(the_repository);
1172+
11631173
if (check_connected(iterate_ref_map, &rm, &opt)) {
11641174
rc = error(_("%s did not send all necessary objects\n"),
11651175
display_state->url);
@@ -2402,6 +2412,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
24022412
}
24032413
string_list_remove_duplicates(&list, 0);
24042414

2415+
if (core_gvfs & GVFS_PREFETCH_DURING_FETCH)
2416+
gh_client__prefetch(0, NULL);
2417+
24052418
if (negotiate_only) {
24062419
struct oidset acked_commits = OIDSET_INIT;
24072420
struct oidset_iter iter;

‎gvfs-helper-client.c

+116-13
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@ static struct hashmap gh_server__subprocess_map;
2828
static struct object_directory *gh_client__chosen_odb;
2929

3030
/*
31-
* The "objects" capability has 2 verbs: "get" and "post".
31+
* The "objects" capability has verbs: "get" and "post" and "prefetch".
3232
*/
3333
#define CAP_OBJECTS (1u<<1)
3434
#define CAP_OBJECTS_NAME "objects"
3535

3636
#define CAP_OBJECTS__VERB_GET1_NAME "get"
3737
#define CAP_OBJECTS__VERB_POST_NAME "post"
38+
#define CAP_OBJECTS__VERB_PREFETCH_NAME "prefetch"
3839

3940
static int gh_client__start_fn(struct subprocess_entry *subprocess)
4041
{
@@ -133,6 +134,44 @@ static int gh_client__send__objects_get(struct child_process *process,
133134
return 0;
134135
}
135136

137+
/*
138+
* Send a request to gvfs-helper to prefetch packfiles from either the
139+
* cache-server or the main Git server using "/gvfs/prefetch".
140+
*
141+
* objects.prefetch LF
142+
* [<seconds-since_epoch> LF]
143+
* <flush>
144+
*/
145+
static int gh_client__send__objects_prefetch(struct child_process *process,
146+
timestamp_t seconds_since_epoch)
147+
{
148+
int err;
149+
150+
/*
151+
* We assume that all of the packet_ routines call error()
152+
* so that we don't have to.
153+
*/
154+
155+
err = packet_write_fmt_gently(
156+
process->in,
157+
(CAP_OBJECTS_NAME "." CAP_OBJECTS__VERB_PREFETCH_NAME "\n"));
158+
if (err)
159+
return err;
160+
161+
if (seconds_since_epoch) {
162+
err = packet_write_fmt_gently(process->in, "%" PRItime "\n",
163+
seconds_since_epoch);
164+
if (err)
165+
return err;
166+
}
167+
168+
err = packet_flush_gently(process->in);
169+
if (err)
170+
return err;
171+
172+
return 0;
173+
}
174+
136175
/*
137176
* Update the loose object cache to include the newly created
138177
* object.
@@ -180,7 +219,7 @@ static void gh_client__update_packed_git(const char *line)
180219
}
181220

182221
/*
183-
* Both CAP_OBJECTS verbs return the same format response:
222+
* CAP_OBJECTS verbs return the same format response:
184223
*
185224
* <odb>
186225
* <data>*
@@ -220,6 +259,8 @@ static int gh_client__objects__receive_response(
220259
const char *v1;
221260
char *line;
222261
int len;
262+
int nr_loose = 0;
263+
int nr_packfile = 0;
223264
int err = 0;
224265

225266
while (1) {
@@ -238,13 +279,13 @@ static int gh_client__objects__receive_response(
238279
else if (starts_with(line, "packfile")) {
239280
gh_client__update_packed_git(line);
240281
ghc |= GHC__CREATED__PACKFILE;
241-
*p_nr_packfile += 1;
282+
nr_packfile++;
242283
}
243284

244285
else if (starts_with(line, "loose")) {
245286
gh_client__update_loose_cache(line);
246287
ghc |= GHC__CREATED__LOOSE;
247-
*p_nr_loose += 1;
288+
nr_loose++;
248289
}
249290

250291
else if (starts_with(line, "ok"))
@@ -258,6 +299,8 @@ static int gh_client__objects__receive_response(
258299
}
259300

260301
*p_ghc = ghc;
302+
*p_nr_loose = nr_loose;
303+
*p_nr_packfile = nr_packfile;
261304

262305
return err;
263306
}
@@ -314,7 +357,7 @@ static struct gh_server__process *gh_client__find_long_running_process(
314357
/*
315358
* Find an existing long-running process with the above command
316359
* line -or- create a new long-running process for this and
317-
* subsequent 'get' requests.
360+
* subsequent requests.
318361
*/
319362
if (!gh_server__subprocess_map_initialized) {
320363
gh_server__subprocess_map_initialized = 1;
@@ -351,10 +394,14 @@ static struct gh_server__process *gh_client__find_long_running_process(
351394

352395
void gh_client__queue_oid(const struct object_id *oid)
353396
{
354-
// TODO consider removing this trace2. it is useful for interactive
355-
// TODO debugging, but may generate way too much noise for a data
356-
// TODO event.
357-
trace2_printf("gh_client__queue_oid: %s", oid_to_hex(oid));
397+
/*
398+
* Keep this trace as a printf only, so that it goes to the
399+
* perf log, but not the event log. It is useful for interactive
400+
* debugging, but generates way too much (unuseful) noise for the
401+
* database.
402+
*/
403+
if (trace2_is_enabled())
404+
trace2_printf("gh_client__queue_oid: %s", oid_to_hex(oid));
358405

359406
if (!oidset_insert(&gh_client__oidset_queued, oid))
360407
gh_client__oidset_count++;
@@ -435,10 +482,14 @@ int gh_client__get_immediate(const struct object_id *oid,
435482
int nr_packfile = 0;
436483
int err = 0;
437484

438-
// TODO consider removing this trace2. it is useful for interactive
439-
// TODO debugging, but may generate way too much noise for a data
440-
// TODO event.
441-
trace2_printf("gh_client__get_immediate: %s", oid_to_hex(oid));
485+
/*
486+
* Keep this trace as a printf only, so that it goes to the
487+
* perf log, but not the event log. It is useful for interactive
488+
* debugging, but generates way too much (unuseful) noise for the
489+
* database.
490+
*/
491+
if (trace2_is_enabled())
492+
trace2_printf("gh_client__get_immediate: %s", oid_to_hex(oid));
442493

443494
entry = gh_client__find_long_running_process(CAP_OBJECTS);
444495
if (!entry)
@@ -467,3 +518,55 @@ int gh_client__get_immediate(const struct object_id *oid,
467518

468519
return err;
469520
}
521+
522+
/*
523+
* Ask gvfs-helper to prefetch commits-and-trees packfiles since a
524+
* given timestamp.
525+
*
526+
* If seconds_since_epoch is zero, gvfs-helper will scan the ODB for
527+
* the last received prefetch and ask for ones newer than that.
528+
*/
529+
int gh_client__prefetch(timestamp_t seconds_since_epoch,
530+
int *nr_packfiles_received)
531+
{
532+
struct gh_server__process *entry;
533+
struct child_process *process;
534+
enum gh_client__created ghc;
535+
int nr_loose = 0;
536+
int nr_packfile = 0;
537+
int err = 0;
538+
539+
entry = gh_client__find_long_running_process(CAP_OBJECTS);
540+
if (!entry)
541+
return -1;
542+
543+
trace2_region_enter("gh-client", "objects/prefetch", the_repository);
544+
trace2_data_intmax("gh-client", the_repository, "prefetch/since",
545+
seconds_since_epoch);
546+
547+
process = &entry->subprocess.process;
548+
549+
sigchain_push(SIGPIPE, SIG_IGN);
550+
551+
err = gh_client__send__objects_prefetch(process, seconds_since_epoch);
552+
if (!err)
553+
err = gh_client__objects__receive_response(
554+
process, &ghc, &nr_loose, &nr_packfile);
555+
556+
sigchain_pop(SIGPIPE);
557+
558+
if (err) {
559+
subprocess_stop(&gh_server__subprocess_map,
560+
(struct subprocess_entry *)entry);
561+
FREE_AND_NULL(entry);
562+
}
563+
564+
trace2_data_intmax("gh-client", the_repository,
565+
"prefetch/packfile_count", nr_packfile);
566+
trace2_region_leave("gh-client", "objects/prefetch", the_repository);
567+
568+
if (nr_packfiles_received)
569+
*nr_packfiles_received = nr_packfile;
570+
571+
return err;
572+
}

‎gvfs-helper-client.h

+18
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,22 @@ void gh_client__queue_oid_array(const struct object_id *oids, int oid_nr);
6666
*/
6767
int gh_client__drain_queue(enum gh_client__created *p_ghc);
6868

69+
/*
70+
* Ask `gvfs-helper server` to fetch any "prefetch packs"
71+
* available on the server more recent than the requested time.
72+
*
73+
* If seconds_since_epoch is zero, gvfs-helper will scan the ODB for
74+
* the last received prefetch and ask for ones newer than that.
75+
*
76+
* A long-running background process is used to subsequent requests
77+
* (either prefetch or regular immediate/queued requests) more efficient.
78+
*
79+
* One or more packfiles will be created in the shared-cache ODB.
80+
*
81+
* Returns 0 on success, -1 on error. Optionally also returns the
82+
* number of prefetch packs received.
83+
*/
84+
int gh_client__prefetch(timestamp_t seconds_since_epoch,
85+
int *nr_packfiles_received);
86+
6987
#endif /* GVFS_HELPER_CLIENT_H */

‎gvfs-helper.c

+1,023-267
Large diffs are not rendered by default.

‎gvfs.h

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#define GVFS_FETCH_SKIP_REACHABILITY_AND_UPLOADPACK (1 << 4)
3030
#define GVFS_BLOCK_FILTERS_AND_EOL_CONVERSIONS (1 << 6)
31+
#define GVFS_PREFETCH_DURING_FETCH (1 << 7)
3132

3233
void gvfs_load_config_value(const char *value);
3334
int gvfs_config_is_set(int mask);

‎remote-curl.c

+3
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,9 @@ static int fetch_git(struct discovery *heads,
11951195
struct strvec args = STRVEC_INIT;
11961196
struct strbuf rpc_result = STRBUF_INIT;
11971197

1198+
if (core_use_gvfs_helper)
1199+
return 0;
1200+
11981201
strvec_pushl(&args, "fetch-pack", "--stateless-rpc",
11991202
"--stdin", "--lock-pack", NULL);
12001203
if (options.followtags)

‎t/helper/test-gvfs-protocol.c

+314-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
#include "strbuf.h"
1212
#include "string-list.h"
1313
#include "trace2.h"
14+
#include "copy.h"
1415
#include "object.h"
16+
#include "object-file.h"
1517
#include "object-store.h"
1618
#include "replace-object.h"
1719
#include "repository.h"
@@ -22,6 +24,7 @@
2224
#include "date.h"
2325
#include "wrapper.h"
2426
#include "git-zlib.h"
27+
#include "packfile.h"
2528

2629
#define TR2_CAT "test-gvfs-protocol"
2730

@@ -551,9 +554,6 @@ static enum worker_result send_loose_object(const struct object_id *oid,
551554
return send_http_error(1, 404, "Not Found", -1, WR_MAYHEM);
552555
}
553556

554-
trace2_printf("%s: OBJECT type=%d len=%ld '%.40s'", TR2_CAT,
555-
type, size, (const char *)content);
556-
557557
/*
558558
* We are blending several somewhat independent concepts here:
559559
*
@@ -872,7 +872,6 @@ static enum worker_result get_packfile_from_oids(
872872
goto done;
873873
}
874874

875-
trace2_printf("%s: pack-objects returned %d bytes", TR2_CAT, buf_packfile->len);
876875
wr = WR_OK;
877876

878877
done:
@@ -1020,6 +1019,305 @@ static enum worker_result do__gvfs_objects__post(struct req *req)
10201019
return wr;
10211020
}
10221021

1022+
/*
1023+
* bswap.h only defines big endian functions.
1024+
* The GVFS Protocol defines fields in little endian.
1025+
*/
1026+
static inline uint64_t my_get_le64(uint64_t le_val)
1027+
{
1028+
#if GIT_BYTE_ORDER == GIT_LITTLE_ENDIAN
1029+
return le_val;
1030+
#else
1031+
return default_bswap64(le_val);
1032+
#endif
1033+
}
1034+
1035+
static inline uint16_t my_get_le16(uint16_t le_val)
1036+
{
1037+
#if GIT_BYTE_ORDER == GIT_LITTLE_ENDIAN
1038+
return le_val;
1039+
#else
1040+
return default_bswap16(le_val);
1041+
#endif
1042+
}
1043+
1044+
/*
1045+
* GVFS Protocol headers for the multipack format
1046+
* All integer values are little-endian on the wire.
1047+
*
1048+
* Note: technically, the protocol defines the `ph` fields as signed, but
1049+
* that makes a mess of the bswap routines and we're not going to overflow
1050+
* them for a very long time.
1051+
*/
1052+
1053+
static unsigned char v1_h[6] = { 'G', 'P', 'R', 'E', ' ', 0x01 };
1054+
1055+
struct ph {
1056+
uint64_t timestamp;
1057+
uint64_t len_pack;
1058+
uint64_t len_idx;
1059+
};
1060+
1061+
/*
1062+
* Accumulate a list of commits-and-trees packfiles we have in the local ODB.
1063+
* The test script should have pre-created a set of "ct-<epoch>.pack" and .idx
1064+
* files for us. We serve these as is and DO NOT try to dynamically create
1065+
* new commits/trees packfiles (like the cache-server does). We are only
1066+
* testing if/whether gvfs-helper.exe can receive one or more packfiles and
1067+
* idx files over the protocol.
1068+
*/
1069+
struct ct_pack_item {
1070+
struct ph ph;
1071+
struct strbuf path_pack;
1072+
struct strbuf path_idx;
1073+
};
1074+
1075+
static void ct_pack_item__free(struct ct_pack_item *item)
1076+
{
1077+
if (!item)
1078+
return;
1079+
strbuf_release(&item->path_pack);
1080+
strbuf_release(&item->path_idx);
1081+
free(item);
1082+
}
1083+
1084+
struct ct_pack_data {
1085+
struct ct_pack_item **items;
1086+
size_t nr, alloc;
1087+
};
1088+
1089+
static void ct_pack_data__release(struct ct_pack_data *data)
1090+
{
1091+
int k;
1092+
1093+
if (!data)
1094+
return;
1095+
1096+
for (k = 0; k < data->nr; k++)
1097+
ct_pack_item__free(data->items[k]);
1098+
1099+
FREE_AND_NULL(data->items);
1100+
data->nr = 0;
1101+
data->alloc = 0;
1102+
}
1103+
1104+
static void cb_ct_pack(const char *full_path, size_t full_path_len,
1105+
const char *file_path, void *void_data)
1106+
{
1107+
struct ct_pack_data *data = void_data;
1108+
struct ct_pack_item *item = NULL;
1109+
struct stat st;
1110+
const char *v;
1111+
1112+
/*
1113+
* We only want "ct-<epoch>.pack" files. The test script creates
1114+
* cached commits-and-trees packfiles with this prefix to avoid
1115+
* confusion with prefetch packfiles received by gvfs-helper.
1116+
*/
1117+
if (!ends_with(file_path, ".pack"))
1118+
return;
1119+
if (!skip_prefix(file_path, "ct-", &v))
1120+
return;
1121+
1122+
item = (struct ct_pack_item *)xcalloc(1, sizeof(*item));
1123+
strbuf_init(&item->path_pack, 0);
1124+
strbuf_addstr(&item->path_pack, full_path);
1125+
1126+
strbuf_init(&item->path_idx, 0);
1127+
strbuf_addstr(&item->path_idx, full_path);
1128+
strbuf_strip_suffix(&item->path_idx, ".pack");
1129+
strbuf_addstr(&item->path_idx, ".idx");
1130+
1131+
item->ph.timestamp = (uint64_t)strtoul(v, NULL, 10);
1132+
1133+
lstat(item->path_pack.buf, &st);
1134+
item->ph.len_pack = (uint64_t)st.st_size;
1135+
1136+
if (string_list_has_string(&mayhem_list, "no_prefetch_idx"))
1137+
item->ph.len_idx = maximum_unsigned_value_of_type(uint64_t);
1138+
else if (lstat(item->path_idx.buf, &st) < 0)
1139+
item->ph.len_idx = maximum_unsigned_value_of_type(uint64_t);
1140+
else
1141+
item->ph.len_idx = (uint64_t)st.st_size;
1142+
1143+
ALLOC_GROW(data->items, data->nr + 1, data->alloc);
1144+
data->items[data->nr++] = item;
1145+
}
1146+
1147+
/*
1148+
* Sort by increasing EPOCH time.
1149+
*/
1150+
static int ct_pack_sort_compare(const void *_a, const void *_b)
1151+
{
1152+
const struct ct_pack_item *a = *(const struct ct_pack_item **)_a;
1153+
const struct ct_pack_item *b = *(const struct ct_pack_item **)_b;
1154+
return (a->ph.timestamp < b->ph.timestamp) ? -1 : (a->ph.timestamp != b->ph.timestamp);
1155+
}
1156+
1157+
static enum worker_result send_ct_item(const struct ct_pack_item *item)
1158+
{
1159+
struct ph ph_le;
1160+
int fd_pack = -1;
1161+
int fd_idx = -1;
1162+
enum worker_result wr = WR_OK;
1163+
1164+
/* send per-packfile header. all fields are little-endian on the wire. */
1165+
ph_le.timestamp = my_get_le64(item->ph.timestamp);
1166+
ph_le.len_pack = my_get_le64(item->ph.len_pack);
1167+
ph_le.len_idx = my_get_le64(item->ph.len_idx);
1168+
1169+
if (write_in_full(1, &ph_le, sizeof(ph_le)) < 0) {
1170+
logerror("unable to write ph_le");
1171+
wr = WR_IO_ERROR;
1172+
goto done;
1173+
}
1174+
1175+
trace2_printf("%s: sending prefetch pack '%s'", TR2_CAT, item->path_pack.buf);
1176+
1177+
fd_pack = git_open_cloexec(item->path_pack.buf, O_RDONLY);
1178+
if (fd_pack == -1 || copy_fd(fd_pack, 1)) {
1179+
logerror("could not send packfile");
1180+
wr = WR_IO_ERROR;
1181+
goto done;
1182+
}
1183+
1184+
if (item->ph.len_idx != maximum_unsigned_value_of_type(uint64_t)) {
1185+
trace2_printf("%s: sending prefetch idx '%s'", TR2_CAT, item->path_idx.buf);
1186+
1187+
fd_idx = git_open_cloexec(item->path_idx.buf, O_RDONLY);
1188+
if (fd_idx == -1 || copy_fd(fd_idx, 1)) {
1189+
logerror("could not send idx");
1190+
wr = WR_IO_ERROR;
1191+
goto done;
1192+
}
1193+
}
1194+
1195+
done:
1196+
if (fd_pack != -1)
1197+
close(fd_pack);
1198+
if (fd_idx != -1)
1199+
close(fd_idx);
1200+
return wr;
1201+
}
1202+
1203+
/*
1204+
* The GVFS Protocol defines the lastTimeStamp parameter as the value
1205+
* of the last prefetch pack that the client has. Therefore, we only
1206+
* want to send newer ones.
1207+
*/
1208+
static int want_ct_pack(const struct ct_pack_item *item, timestamp_t last_timestamp)
1209+
{
1210+
return item->ph.timestamp > last_timestamp;
1211+
}
1212+
1213+
static enum worker_result send_multipack(struct ct_pack_data *data,
1214+
timestamp_t last_timestamp)
1215+
{
1216+
struct strbuf response_header = STRBUF_INIT;
1217+
struct strbuf uuid = STRBUF_INIT;
1218+
enum worker_result wr;
1219+
size_t content_len = 0;
1220+
unsigned short np = 0;
1221+
unsigned short np_le;
1222+
int k;
1223+
1224+
/*
1225+
* Precompute the content-length so that we don't have to deal with
1226+
* chunking it.
1227+
*/
1228+
content_len += sizeof(v1_h) + sizeof(np);
1229+
for (k = 0; k < data->nr; k++) {
1230+
struct ct_pack_item *item = data->items[k];
1231+
1232+
if (!want_ct_pack(item, last_timestamp))
1233+
continue;
1234+
1235+
np++;
1236+
content_len += sizeof(struct ph);
1237+
content_len += item->ph.len_pack;
1238+
if (item->ph.len_idx != maximum_unsigned_value_of_type(uint64_t))
1239+
content_len += item->ph.len_idx;
1240+
}
1241+
1242+
strbuf_addstr(&response_header, "HTTP/1.1 200 OK\r\n");
1243+
strbuf_addstr(&response_header, "Cache-Control: private\r\n");
1244+
strbuf_addstr(&response_header,
1245+
"Content-Type: application/x-gvfs-timestamped-packfiles-indexes\r\n");
1246+
strbuf_addf( &response_header, "Content-Length: %d\r\n", (int)content_len);
1247+
strbuf_addf( &response_header, "Server: test-gvfs-protocol/%s\r\n", git_version_string);
1248+
strbuf_addf( &response_header, "Date: %s\r\n", show_date(time(NULL), 0, DATE_MODE(RFC2822)));
1249+
gen_fake_uuid(&uuid);
1250+
strbuf_addf( &response_header, "X-VSS-E2EID: %s\r\n", uuid.buf);
1251+
strbuf_addstr(&response_header, "\r\n");
1252+
1253+
if (write_in_full(1, response_header.buf, response_header.len) < 0) {
1254+
logerror("unable to write response header");
1255+
wr = WR_IO_ERROR;
1256+
goto done;
1257+
}
1258+
1259+
/* send protocol version header */
1260+
if (write_in_full(1, v1_h, sizeof(v1_h)) < 0) {
1261+
logerror("unabled to write v1_h");
1262+
wr = WR_IO_ERROR;
1263+
goto done;
1264+
}
1265+
1266+
/* send number of packfiles */
1267+
np_le = my_get_le16(np);
1268+
if (write_in_full(1, &np_le, sizeof(np_le)) < 0) {
1269+
logerror("unable to write np");
1270+
wr = WR_IO_ERROR;
1271+
goto done;
1272+
}
1273+
1274+
for (k = 0; k < data->nr; k++) {
1275+
if (!want_ct_pack(data->items[k], last_timestamp))
1276+
continue;
1277+
1278+
wr = send_ct_item(data->items[k]);
1279+
if (wr != WR_OK)
1280+
goto done;
1281+
}
1282+
1283+
wr = WR_OK;
1284+
1285+
done:
1286+
strbuf_release(&uuid);
1287+
strbuf_release(&response_header);
1288+
1289+
return wr;
1290+
}
1291+
1292+
static enum worker_result do__gvfs_prefetch__get(struct req *req)
1293+
{
1294+
struct ct_pack_data data;
1295+
timestamp_t last_timestamp = 0;
1296+
enum worker_result wr;
1297+
1298+
memset(&data, 0, sizeof(data));
1299+
1300+
if (req->quest_args.len) {
1301+
const char *key = strstr(req->quest_args.buf, "lastPackTimestamp=");
1302+
if (key) {
1303+
const char *val;
1304+
if (skip_prefix(key, "lastPackTimestamp=", &val)) {
1305+
last_timestamp = strtol(val, NULL, 10);
1306+
}
1307+
}
1308+
}
1309+
trace2_printf("%s: prefetch/since %"PRItime, TR2_CAT, last_timestamp);
1310+
1311+
for_each_file_in_pack_dir(get_object_directory(), cb_ct_pack, &data);
1312+
QSORT(data.items, data.nr, ct_pack_sort_compare);
1313+
1314+
wr = send_multipack(&data, last_timestamp);
1315+
1316+
ct_pack_data__release(&data);
1317+
1318+
return wr;
1319+
}
1320+
10231321
/*
10241322
* Read the HTTP request up to the start of the optional message-body.
10251323
* We do this byte-by-byte because we have keep-alive turned on and
@@ -1174,6 +1472,11 @@ static enum worker_result req__read(struct req *req, int fd)
11741472
* We let our caller read/chunk it in as appropriate.
11751473
*/
11761474
done:
1475+
1476+
#if 0
1477+
/*
1478+
* This is useful for debugging the request, but very noisy.
1479+
*/
11771480
if (trace2_is_enabled()) {
11781481
struct string_list_item *item;
11791482
trace2_printf("%s: %s", TR2_CAT, req->start_line.buf);
@@ -1188,6 +1491,7 @@ static enum worker_result req__read(struct req *req, int fd)
11881491
for_each_string_list_item(item, &req->header_list)
11891492
trace2_printf("%s: Hdrs: %s", TR2_CAT, item->string);
11901493
}
1494+
#endif
11911495

11921496
strbuf_release(&h);
11931497

@@ -1238,6 +1542,12 @@ static enum worker_result dispatch(struct req *req)
12381542
return do__gvfs_config__get(req);
12391543
}
12401544

1545+
if (!strcmp(req->gvfs_api.buf, "gvfs/prefetch")) {
1546+
1547+
if (!strcmp(method, "GET"))
1548+
return do__gvfs_prefetch__get(req);
1549+
}
1550+
12411551
return send_http_error(1, 501, "Not Implemented", -1,
12421552
WR_OK | WR_HANGUP);
12431553
}

‎t/t5799-gvfs-helper.sh

+293-50
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.