11
11
#include "strbuf.h"
12
12
#include "string-list.h"
13
13
#include "trace2.h"
14
+ #include "copy.h"
14
15
#include "object.h"
16
+ #include "object-file.h"
15
17
#include "object-store.h"
16
18
#include "replace-object.h"
17
19
#include "repository.h"
22
24
#include "date.h"
23
25
#include "wrapper.h"
24
26
#include "git-zlib.h"
27
+ #include "packfile.h"
25
28
26
29
#define TR2_CAT "test-gvfs-protocol"
27
30
@@ -551,9 +554,6 @@ static enum worker_result send_loose_object(const struct object_id *oid,
551
554
return send_http_error (1 , 404 , "Not Found" , -1 , WR_MAYHEM );
552
555
}
553
556
554
- trace2_printf ("%s: OBJECT type=%d len=%ld '%.40s'" , TR2_CAT ,
555
- type , size , (const char * )content );
556
-
557
557
/*
558
558
* We are blending several somewhat independent concepts here:
559
559
*
@@ -872,7 +872,6 @@ static enum worker_result get_packfile_from_oids(
872
872
goto done ;
873
873
}
874
874
875
- trace2_printf ("%s: pack-objects returned %d bytes" , TR2_CAT , buf_packfile -> len );
876
875
wr = WR_OK ;
877
876
878
877
done :
@@ -1020,6 +1019,305 @@ static enum worker_result do__gvfs_objects__post(struct req *req)
1020
1019
return wr ;
1021
1020
}
1022
1021
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
+
1023
1321
/*
1024
1322
* Read the HTTP request up to the start of the optional message-body.
1025
1323
* 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)
1174
1472
* We let our caller read/chunk it in as appropriate.
1175
1473
*/
1176
1474
done :
1475
+
1476
+ #if 0
1477
+ /*
1478
+ * This is useful for debugging the request, but very noisy.
1479
+ */
1177
1480
if (trace2_is_enabled ()) {
1178
1481
struct string_list_item * item ;
1179
1482
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)
1188
1491
for_each_string_list_item (item , & req -> header_list )
1189
1492
trace2_printf ("%s: Hdrs: %s" , TR2_CAT , item -> string );
1190
1493
}
1494
+ #endif
1191
1495
1192
1496
strbuf_release (& h );
1193
1497
@@ -1238,6 +1542,12 @@ static enum worker_result dispatch(struct req *req)
1238
1542
return do__gvfs_config__get (req );
1239
1543
}
1240
1544
1545
+ if (!strcmp (req -> gvfs_api .buf , "gvfs/prefetch" )) {
1546
+
1547
+ if (!strcmp (method , "GET" ))
1548
+ return do__gvfs_prefetch__get (req );
1549
+ }
1550
+
1241
1551
return send_http_error (1 , 501 , "Not Implemented" , -1 ,
1242
1552
WR_OK | WR_HANGUP );
1243
1553
}
0 commit comments