1
+#include "git-compat-util.h"
2
+#include "gettext.h"
3
+#include "hex.h"
4
+#include "pkt-line.h"
5
+#include "connect.h"
6
+#include "oid-array.h"
7
+#include "odb.h"
8
+#include "fetch-object-info.h"
9
+#include "string-list.h"
10
+
11
+/* Sends object-info command and its arguments into the request buffer. */
12
+static void send_object_info_request(const int fd_out, struct object_info_args *args)
13
+{
14
+ struct strbuf req_buf = STRBUF_INIT;
15
+
16
+ write_command_and_capabilities(&req_buf, "object-info", args->server_options);
17
+
18
+ if (unsorted_string_list_has_string(args->object_info_options, "size"))
19
+ packet_buf_write(&req_buf, "size");
20
+ else if (args->object_info_options->nr)
21
+ BUG("only size should be in object_info_options");
22
+
23
+ if (args->oids)
24
+ for (size_t i = 0; i < args->oids->nr; i++)
25
+ packet_buf_write(&req_buf, "oid %s", oid_to_hex(&args->oids->oid[i]));
26
+
27
+ packet_buf_flush(&req_buf);
28
+ if (write_in_full(fd_out, req_buf.buf, req_buf.len) < 0)
29
+ die_errno(_("unable to write request to remote"));
30
+
31
+ strbuf_release(&req_buf);
32
+}
33
+
34
+static int parse_object_size(const char *s, size_t *res)
35
+{
36
+ uintmax_t uim;
37
+
38
+ if (!s[0] || s[strspn(s, "0123456789")])
39
+ return -1;
40
+ errno = 0;
41
+ uim = strtoumax(s, NULL, 10);
42
+ if (errno || uim > SIZE_MAX)
43
+ return -1;
44
+ *res = uim;
45
+ return 0;
46
+}
47
+
48
+int fetch_object_info(const enum protocol_version version, struct object_info_args *args,
49
+ struct packet_reader *reader, struct object_info *object_info_data,
50
+ const int stateless_rpc, const int fd_out)
51
+{
52
+ int size_index = -1;
53
+
54
+ switch (version) {
55
+ case protocol_v2:
56
+ if (!server_supports_v2("object-info"))
57
+ die(_("object-info capability is not enabled on the server"));
58
+ send_object_info_request(fd_out, args);
59
+ break;
60
+ case protocol_v1:
61
+ case protocol_v0:
62
+ die(_("object-info requires protocol v2"));
63
+ case protocol_unknown_version:
64
+ BUG("unknown protocol version");
65
+ }
66
+
67
+ for (size_t i = 0; i < args->object_info_options->nr; i++) {
68
+ if (packet_reader_read(reader) != PACKET_READ_NORMAL) {
69
+ check_stateless_delimiter(stateless_rpc, reader,
70
+ "stateless delimiter expected");
71
+ return -1;
72
+ }
73
+
74
+ if (!string_list_has_string(args->object_info_options, reader->line))
75
+ return -1;
76
+
77
+ if (!strcmp(reader->line, "size")) {
78
+ /*
79
+ * i is the number of supported options which currently
80
+ * is only size. No risk of overflow.
81
+ */
82
+ size_index = (int)i;
83
+ for (size_t j = 0; j < args->oids->nr; j++)
84
+ object_info_data[j].sizep =
85
+ xcalloc(1, sizeof(*object_info_data[j].sizep));
86
+ } else {
87
+ BUG("only size is supported");
88
+ }
89
+ }
90
+
91
+ for (size_t i = 0;
92
+ packet_reader_read(reader) == PACKET_READ_NORMAL &&
93
+ i < args->oids->nr;
94
+ i++) {
95
+ struct string_list object_info_values = STRING_LIST_INIT_DUP;
96
+
97
+ string_list_split(&object_info_values, reader->line, " ", -1);
98
+
99
+ if (strcmp(object_info_values.items[0].string,
100
+ oid_to_hex(&args->oids->oid[i])))
101
+ die(_("object-info: expected OID: %s, got %s"),
102
+ oid_to_hex(&args->oids->oid[i]),
103
+ object_info_values.items[0].string);
104
+
105
+ /*
106
+ * If the response is two elements but the second one is an
107
+ * empty string, that means that the OID is unrecognized by the
108
+ * server.
109
+ */
110
+ if (object_info_values.nr >= 2 &&
111
+ !strcmp(object_info_values.items[1].string, "")) {
112
+ object_info_data[i].unrecognized = 1;
113
+ string_list_clear(&object_info_values, 0);
114
+ continue;
115
+ }
116
+
117
+ /*
118
+ * Because we filter the options to be only the supported by
119
+ * the server we expect the server to answer with the same
120
+ * number of attributes requested.
121
+ */
122
+ if (args->object_info_options->nr + 1 != object_info_values.nr)
123
+ die("object-info: unexpected number of attributes: %s",
124
+ reader->line);
125
+
126
+ if (size_index >= 0 &&
127
+ parse_object_size(object_info_values.items[size_index + 1].string,
128
+ object_info_data[i].sizep))
129
+ die("object-info: ref %s has invalid size %s",
130
+ object_info_values.items[0].string,
131
+ object_info_values.items[size_index + 1].string);
132
+
133
+ string_list_clear(&object_info_values, 0);
134
+ }
135
+ check_stateless_delimiter(stateless_rpc, reader, "stateless delimiter expected");
136
+
137
+ return 0;
138
+}