Raw
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 /*
59 * When removing an element from the list it gets swapped by the
60 * last element, iterate backwards to prevent elements skipping
61 * evaluation.
62 *
63 * object_info_options->nr can be safely casted without overflow
64 * because the number of options is a small known number (the
65 * supported placeholders which currently are size and type).
66 */
67 for (int i = (int)args->object_info_options->nr - 1; i >= 0; i--)
68 if (!server_supports_feature("object-info",
69 args->object_info_options->items[i].string, 0))
70 unsorted_string_list_delete_item(args->object_info_options, i, 0);
71
72 /*
73 * Even if no options are left, we still send the oid so we get
74 * at least an existence check.
75 */
76 send_object_info_request(fd_out, args);
77 break;
78 case protocol_v1:
79 case protocol_v0:
80 die(_("object-info requires protocol v2"));
81 case protocol_unknown_version:
82 BUG("unknown protocol version");
83 }
84
85 for (size_t i = 0; i < args->object_info_options->nr; i++) {
86 if (packet_reader_read(reader) != PACKET_READ_NORMAL) {
87 check_stateless_delimiter(stateless_rpc, reader,
88 "stateless delimiter expected");
89 return -1;
90 }
91
92 if (!unsorted_string_list_has_string(args->object_info_options, reader->line))
93 return -1;
94
95 if (!strcmp(reader->line, "size")) {
96 /*
97 * i is the number of supported options which currently
98 * is only size. No risk of overflow.
99 */
100 size_index = (int)i;
101 for (size_t j = 0; j < args->oids->nr; j++)
102 object_info_data[j].sizep =
103 xcalloc(1, sizeof(*object_info_data[j].sizep));
104 } else {
105 BUG("only size is supported");
106 }
107 }
108
109 for (size_t i = 0;
110 packet_reader_read(reader) == PACKET_READ_NORMAL &&
111 i < args->oids->nr;
112 i++) {
113 struct string_list object_info_values = STRING_LIST_INIT_DUP;
114
115 string_list_split(&object_info_values, reader->line, " ", -1);
116
117 if (strcmp(object_info_values.items[0].string,
118 oid_to_hex(&args->oids->oid[i])))
119 die(_("object-info: expected OID: %s, got %s"),
120 oid_to_hex(&args->oids->oid[i]),
121 object_info_values.items[0].string);
122
123 /*
124 * If the response is two elements but the second one is an
125 * empty string, that means that the OID is unrecognized by the
126 * server.
127 */
128 if (object_info_values.nr >= 2 &&
129 !strcmp(object_info_values.items[1].string, "")) {
130 object_info_data[i].unrecognized = 1;
131 string_list_clear(&object_info_values, 0);
132 continue;
133 }
134
135 /*
136 * Because we filter the options to be only the supported by
137 * the server we expect the server to answer with the same
138 * number of attributes requested.
139 */
140 if (args->object_info_options->nr + 1 != object_info_values.nr)
141 die("object-info: unexpected number of attributes: %s",
142 reader->line);
143
144 if (size_index >= 0 &&
145 parse_object_size(object_info_values.items[size_index + 1].string,
146 object_info_data[i].sizep))
147 die("object-info: ref %s has invalid size %s",
148 object_info_values.items[0].string,
149 object_info_values.items[size_index + 1].string);
150
151 string_list_clear(&object_info_values, 0);
152 }
153 check_stateless_delimiter(stateless_rpc, reader, "stateless delimiter expected");
154
155 return 0;
156 }