Raw
1 #include "git-compat-util.h"
2 #include "gettext.h"
3 #include "hex.h"
4 #include "object.h"
5 #include "pkt-line.h"
6 #include "connect.h"
7 #include "oid-array.h"
8 #include "odb.h"
9 #include "fetch-object-info.h"
10 #include "string-list.h"
11
12 /* Sends object-info command and its arguments into the request buffer. */
13 static void send_object_info_request(const int fd_out,
14 const struct string_list *server_options,
15 struct oid_array *oids,
16 unsigned ask_size,
17 unsigned ask_type)
18 {
19 struct strbuf req_buf = STRBUF_INIT;
20
21 write_command_and_capabilities(&req_buf, "object-info", server_options);
22
23 if (ask_size)
24 packet_buf_write(&req_buf, "size");
25
26 if (ask_type)
27 packet_buf_write(&req_buf, "type");
28
29 if (oids)
30 for (size_t i = 0; i < oids->nr; i++)
31 packet_buf_write(&req_buf, "oid %s",
32 oid_to_hex(&oids->oid[i]));
33
34 packet_buf_flush(&req_buf);
35 if (write_in_full(fd_out, req_buf.buf, req_buf.len) < 0)
36 die_errno(_("unable to write request to remote"));
37
38 strbuf_release(&req_buf);
39 }
40
41 static int parse_object_size(const char *s, size_t *res)
42 {
43 uintmax_t uim;
44
45 if (!s[0] || s[strspn(s, "0123456789")])
46 return -1;
47 errno = 0;
48 uim = strtoumax(s, NULL, 10);
49 if (errno || uim > SIZE_MAX)
50 return -1;
51 *res = uim;
52 return 0;
53 }
54
55 int fetch_object_info(const enum protocol_version version,
56 const struct string_list *server_options,
57 struct oid_array *oids,
58 struct packet_reader *reader,
59 struct fetch_object_info_results *results,
60 const int stateless_rpc,
61 const int fd_out)
62 {
63 unsigned ask_size = 0;
64 unsigned ask_type = 0;
65 int size_index = -1;
66 int type_index = -1;
67 size_t wanted;
68 size_t i;
69
70 results->nr = oids->nr;
71 CALLOC_ARRAY(results->unrecognized, results->nr);
72
73 switch (version) {
74 case protocol_v2:
75 if (!server_supports_v2("object-info"))
76 die(_("object-info capability is not enabled on the server"));
77
78 if (results->wants_size &&
79 server_supports_feature("object-info", "size", 0))
80 ask_size = 1;
81
82 if (results->wants_type &&
83 server_supports_feature("object-info", "type", 0))
84 ask_type = 1;
85
86 /*
87 * Even if no options are left, we still send the oid so we get
88 * at least an existence check.
89 */
90 send_object_info_request(fd_out, server_options, oids, ask_size,
91 ask_type);
92 break;
93 case protocol_v1:
94 case protocol_v0:
95 die(_("object-info requires protocol v2"));
96 case protocol_unknown_version:
97 BUG("unknown protocol version");
98 }
99 wanted = ask_size + ask_type;
100
101 for (i = 0; i < wanted; i++) {
102 if (packet_reader_read(reader) != PACKET_READ_NORMAL) {
103 check_stateless_delimiter(stateless_rpc, reader,
104 "stateless delimiter expected");
105 return -1;
106 }
107
108 if (!strcmp(reader->line, "size")) {
109 if (!ask_size)
110 die(_("object-info: unrequested 'size' attribute"));
111 if (results->sizes)
112 die(_("object-info: duplicate 'size' attribute"));
113 size_index = (int)i;
114 CALLOC_ARRAY(results->sizes, results->nr);
115 } else if (!strcmp(reader->line, "type")) {
116 if (!ask_type)
117 die(_("object-info: unrequested 'type' attribute"));
118 if (results->types)
119 die(_("object-info: duplicate 'type' attribute"));
120 type_index = (int)i;
121 CALLOC_ARRAY(results->types, results->nr);
122 } else {
123 BUG("unexpected object-info option: %s", reader->line);
124 }
125 }
126
127 for (i = 0;
128 packet_reader_read(reader) == PACKET_READ_NORMAL &&
129 i < oids->nr;
130 i++) {
131 struct string_list object_info_values = STRING_LIST_INIT_DUP;
132
133 string_list_split(&object_info_values, reader->line, " ", -1);
134
135 if (strcmp(object_info_values.items[0].string,
136 oid_to_hex(&oids->oid[i])))
137 die(_("object-info: expected OID: %s, got %s"),
138 oid_to_hex(&oids->oid[i]),
139 object_info_values.items[0].string);
140
141 /*
142 * If the response is two elements but the second one is an
143 * empty string, that means that the OID is unrecognized by the
144 * server.
145 */
146 if (object_info_values.nr >= 2 &&
147 !strcmp(object_info_values.items[1].string, "")) {
148 results->unrecognized[i] = 1;
149 string_list_clear(&object_info_values, 0);
150 continue;
151 }
152
153 /*
154 * Because we only ask for attributes the server said it
155 * supports, we expect the answer to have one value per
156 * requested attribute, plus the OID.
157 */
158 if (wanted + 1 != object_info_values.nr)
159 die("object-info: unexpected number of attributes: %s",
160 reader->line);
161
162 if (results->sizes &&
163 parse_object_size(object_info_values.items[size_index + 1].string,
164 &results->sizes[i]))
165 die("object-info: object %s has invalid size %s",
166 object_info_values.items[0].string,
167 object_info_values.items[size_index + 1].string);
168
169 if (results->types) {
170 const char *type_str =
171 object_info_values.items[type_index + 1].string;
172 int type = type_from_string_gently(type_str, -1, 1);
173
174 if (type < 0)
175 die(_("object-info: object %s has invalid type '%s'"),
176 object_info_values.items[0].string, type_str);
177
178 results->types[i] = type;
179 }
180
181 string_list_clear(&object_info_values, 0);
182 }
183
184 if (i != oids->nr)
185 die(_("object-info: expected %" PRIuMAX " objects, got %" PRIuMAX),
186 (uintmax_t)oids->nr, (uintmax_t)i);
187
188 check_stateless_delimiter(stateless_rpc, reader, "stateless delimiter expected");
189
190 return 0;
191 }
192
193 void free_fetch_object_info_results(struct fetch_object_info_results *results)
194 {
195 free(results->sizes);
196 free(results->types);
197 free(results->unrecognized);
198 memset(results, 0, sizeof(*results));
199 }