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