Raw
1 #include "git-compat-util.h"
2 #include "protocol-caps.h"
3 #include "gettext.h"
4 #include "hex.h"
5 #include "pkt-line.h"
6 #include "hash.h"
7 #include "object.h"
8 #include "odb.h"
9 #include "repository.h"
10 #include "string-list.h"
11 #include "strbuf.h"
12
13 struct requested_info {
14 unsigned size : 1;
15 };
16
17 /*
18 * Parses oids from the given line and collects them in the given
19 * oid_str_list. Returns 1 if parsing was successful and 0 otherwise.
20 */
21 static int parse_oid(const char *line, struct string_list *oid_str_list)
22 {
23 const char *arg;
24
25 if (!skip_prefix(line, "oid ", &arg))
26 return 0;
27
28 string_list_append(oid_str_list, arg);
29
30 return 1;
31 }
32
33 /*
34 * odb_read_object_info_extended() wrapper. Similar to odb_read_object_info()
35 * but uses the flags:
36 *
37 * - OBJECT_INFO_SKIP_FETCH_OBJECT so a server won't fetch an object when a
38 * object-info request asks for an OID that it doesn't have.
39 *
40 * - OBJECT_INFO_QUICK to avoid re-scanning packs when the object is not found.
41 */
42 static enum object_type get_object_info(struct object_database *odb,
43 const struct object_id *oid,
44 size_t *sizep)
45 {
46 enum object_type type;
47 struct object_info oi = OBJECT_INFO_INIT;
48
49 oi.typep = &type;
50 oi.sizep = sizep;
51 if (odb_read_object_info_extended(odb, oid, &oi,
52 OBJECT_INFO_LOOKUP_REPLACE |
53 OBJECT_INFO_SKIP_FETCH_OBJECT |
54 OBJECT_INFO_QUICK) < 0)
55 return OBJ_BAD;
56 return type;
57 }
58
59 /*
60 * Validates and send requested info back to the client. Any errors detected
61 * are returned as they are detected.
62 */
63 static void send_info(struct repository *r, struct packet_writer *writer,
64 struct string_list *oid_str_list,
65 struct requested_info *info)
66 {
67 struct string_list_item *item;
68 struct strbuf send_buffer = STRBUF_INIT;
69
70 if (!oid_str_list->nr)
71 return;
72
73 if (info->size)
74 packet_writer_write(writer, "size");
75
76 for_each_string_list_item (item, oid_str_list) {
77 const char *oid_str = item->string;
78 struct object_id oid;
79 size_t object_size;
80
81 if (get_oid_hex_algop(oid_str, &oid, r->hash_algo) < 0) {
82 packet_writer_error(
83 writer,
84 "object-info: protocol error, expected to get oid, not '%s'",
85 oid_str);
86 continue;
87 }
88
89 strbuf_addstr(&send_buffer, oid_str);
90
91 /*
92 * Check the existence of the object first.
93 * If an object is not recognized by the server append SP to
94 * the response.
95 */
96 if (get_object_info(r->objects, &oid, &object_size) <= OBJ_NONE) {
97 strbuf_addstr(&send_buffer, " ");
98 goto write;
99 }
100
101 if (info->size) {
102 strbuf_addf(&send_buffer, " %"PRIuMAX,
103 (uintmax_t)object_size);
104 }
105
106 write:
107 packet_writer_write(writer, "%s", send_buffer.buf);
108 strbuf_reset(&send_buffer);
109 }
110 strbuf_release(&send_buffer);
111 }
112
113 int cap_object_info(struct repository *r, struct packet_reader *request)
114 {
115 struct requested_info info = { 0 };
116 struct packet_writer writer;
117 struct string_list oid_str_list = STRING_LIST_INIT_DUP;
118
119 packet_writer_init(&writer, 1);
120
121 while (packet_reader_read(request) == PACKET_READ_NORMAL) {
122 if (!strcmp("size", request->line)) {
123 info.size = 1;
124 continue;
125 }
126
127 if (parse_oid(request->line, &oid_str_list))
128 continue;
129
130 packet_writer_error(&writer,
131 "object-info: unexpected line: '%s'",
132 request->line);
133 }
134
135 if (request->status != PACKET_READ_FLUSH) {
136 packet_writer_error(
137 &writer, "object-info: expected flush after arguments");
138 die(_("object-info: expected flush after arguments"));
139 }
140
141 send_info(r, &writer, &oid_str_list, &info);
142
143 string_list_clear(&oid_str_list, 1);
144
145 packet_flush(1);
146
147 return 0;
148 }