Coverage Report

Created: 2026-10-17 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}