Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2019 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions
8 : : * are met:
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer
11 : : * in this position and unchanged.
12 : : * 2. Redistributions in binary form must reproduce the above copyright
13 : : * notice, this list of conditions and the following disclaimer in the
14 : : * documentation and/or other materials provided with the distribution.
15 : : *
16 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 : : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : : */
27 : :
28 : : #include <ucl.h>
29 : :
30 : : #include "pkg.h"
31 : : #include "private/event.h"
32 : : #include "private/pkg.h"
33 : :
34 : : /* Default to repo v1 for now */
35 : : #define DEFAULT_META_VERSION 2
36 : :
37 : : static ucl_object_t *repo_meta_schema_v1 = NULL;
38 : : static ucl_object_t *repo_meta_schema_v2 = NULL;
39 : :
40 : : static void
41 : 4927 : pkg_repo_meta_set_default(struct pkg_repo_meta *meta)
42 : : {
43 : 4927 : meta->digest_format = PKG_HASH_TYPE_SHA256_BASE32;
44 : 4927 : meta->packing_format = DEFAULT_COMPRESSION;
45 : :
46 : : /* Not use conflicts for now */
47 : 4927 : meta->conflicts = NULL;
48 : 4927 : meta->conflicts_archive = NULL;
49 : 4927 : meta->manifests = xstrdup("packagesite.yaml");
50 : 4927 : meta->manifests_archive = xstrdup("packagesite");
51 : 4927 : meta->filesite = xstrdup("filesite.yaml");
52 : 4927 : meta->filesite_archive = xstrdup("filesite");
53 : : /* Not using fulldb */
54 : 4927 : meta->fulldb = NULL;
55 : 4927 : meta->fulldb_archive = NULL;
56 : :
57 : : /*
58 : : * digest is only used on legacy v1 repository
59 : : * but pkg_repo_meta_is_special_file depend on the
60 : : * information in the pkg_repo_meta.
61 : : * Leave digests here so pkg will not complain that
62 : : * repodir/digest.txz isn't a valid package when switching
63 : : * from version 1 to version 2
64 : : */
65 : 4927 : meta->digests = xstrdup("digests");
66 : 4927 : meta->digests_archive = xstrdup("digests");
67 : 4927 : }
68 : :
69 : : void
70 : 4148 : pkg_repo_meta_free(struct pkg_repo_meta *meta)
71 : : {
72 : : struct pkg_repo_meta_key *k;
73 : : pkghash_it it;
74 : :
75 : : /*
76 : : * It is safe to free NULL pointer by standard
77 : : */
78 [ + - ]: 4148 : if (meta != NULL) {
79 : 4148 : free(meta->conflicts);
80 : 4148 : free(meta->manifests);
81 : 4148 : free(meta->digests);
82 : 4148 : free(meta->fulldb);
83 : 4148 : free(meta->filesite);
84 : 4148 : free(meta->conflicts_archive);
85 : 4148 : free(meta->manifests_archive);
86 : 4148 : free(meta->digests_archive);
87 : 4148 : free(meta->fulldb_archive);
88 : 4148 : free(meta->filesite_archive);
89 : 4148 : free(meta->maintainer);
90 : 4148 : free(meta->source);
91 : 4148 : free(meta->source_identifier);
92 : 4148 : it = pkghash_iterator(meta->keys);
93 [ - + ]: 4148 : while (pkghash_next(&it)) {
94 : 0 : k = (struct pkg_repo_meta_key *)it.value;
95 : 0 : free(k->name);
96 : 0 : free(k->pubkey);
97 : 0 : free(k->pubkey_type);
98 : 0 : free(k);
99 : : }
100 : 4148 : pkghash_destroy(meta->keys);
101 : 4148 : free(meta);
102 : 4148 : }
103 : 4148 : }
104 : :
105 : : static ucl_object_t*
106 : 16 : pkg_repo_meta_open_schema_v1()
107 : : {
108 : : struct ucl_parser *parser;
109 : : static const char meta_schema_str_v1[] = ""
110 : : "{"
111 : : "type = object;"
112 : : "properties {"
113 : : "version = {type = integer};\n"
114 : : "maintainer = {type = string};\n"
115 : : "source = {type = string};\n"
116 : : "packing_format = {enum = [tzst, txz, tbz, tgz, tar]};\n"
117 : : "digest_format = {enum = [sha256_base32, sha256_hex, blake2_base32, blake2s_base32]};\n"
118 : : "digests = {type = string};\n"
119 : : "manifests = {type = string};\n"
120 : : "conflicts = {type = string};\n"
121 : : "fulldb = {type = string};\n"
122 : : "filesite = {type = string};\n"
123 : : "digests_archive = {type = string};\n"
124 : : "manifests_archive = {type = string};\n"
125 : : "conflicts_archive = {type = string};\n"
126 : : "fulldb_archive = {type = string};\n"
127 : : "filesite_archive = {type = string};\n"
128 : : "source_identifier = {type = string};\n"
129 : : "revision = {type = integer};\n"
130 : : "eol = {type = integer};\n"
131 : : "cert = {"
132 : : " type = object;\n"
133 : : " properties {"
134 : : " type = {enum = [rsa]};\n"
135 : : " data = {type = string};\n"
136 : : " name = {type = string};\n"
137 : : " }"
138 : : " required = [type, data, name];\n"
139 : : "};\n"
140 : :
141 : : "}\n"
142 : : "required = [version]\n"
143 : : "}";
144 : :
145 [ + + ]: 16 : if (repo_meta_schema_v1 != NULL)
146 : 8 : return (repo_meta_schema_v1);
147 : :
148 : 8 : parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS);
149 [ + - ]: 8 : if (!ucl_parser_add_chunk(parser, meta_schema_str_v1,
150 : : sizeof(meta_schema_str_v1) - 1)) {
151 : 0 : pkg_emit_error("cannot parse schema for repo meta: %s",
152 : 0 : ucl_parser_get_error(parser));
153 : 0 : ucl_parser_free(parser);
154 : 0 : return (NULL);
155 : : }
156 : :
157 : 8 : repo_meta_schema_v1 = ucl_parser_get_object(parser);
158 : 8 : ucl_parser_free(parser);
159 : :
160 : 8 : return (repo_meta_schema_v1);
161 : 16 : }
162 : :
163 : : static ucl_object_t*
164 : 1300 : pkg_repo_meta_open_schema_v2()
165 : : {
166 : : struct ucl_parser *parser;
167 : : static const char meta_schema_str_v2[] = ""
168 : : "{"
169 : : "type = object;"
170 : : "properties {"
171 : : "version = {type = integer};\n"
172 : : "maintainer = {type = string};\n"
173 : : "source = {type = string};\n"
174 : : "packing_format = {enum = [tzst, txz, tbz, tgz, tar]};\n"
175 : : "manifests = {type = string};\n"
176 : : "conflicts = {type = string};\n"
177 : : "fulldb = {type = string};\n"
178 : : "filesite = {type = string};\n"
179 : : "manifests_archive = {type = string};\n"
180 : : "conflicts_archive = {type = string};\n"
181 : : "fulldb_archive = {type = string};\n"
182 : : "filesite_archive = {type = string};\n"
183 : : "source_identifier = {type = string};\n"
184 : : "revision = {type = integer};\n"
185 : : "eol = {type = integer};\n"
186 : : "cert = {"
187 : : " type = object;\n"
188 : : " properties {"
189 : : " type = {enum = [rsa]};\n"
190 : : " data = {type = string};\n"
191 : : " name = {type = string};\n"
192 : : " }"
193 : : " required = [type, data, name];\n"
194 : : "};\n"
195 : :
196 : : "}\n"
197 : : "required = [version]\n"
198 : : "}";
199 : :
200 [ + + ]: 1300 : if (repo_meta_schema_v2 != NULL)
201 : 659 : return (repo_meta_schema_v2);
202 : :
203 : 641 : parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS);
204 [ + - ]: 641 : if (!ucl_parser_add_chunk(parser, meta_schema_str_v2,
205 : : sizeof(meta_schema_str_v2) - 1)) {
206 : 0 : pkg_emit_error("cannot parse schema for repo meta: %s",
207 : 0 : ucl_parser_get_error(parser));
208 : 0 : ucl_parser_free(parser);
209 : 0 : return (NULL);
210 : : }
211 : :
212 : 641 : repo_meta_schema_v2 = ucl_parser_get_object(parser);
213 : 641 : ucl_parser_free(parser);
214 : :
215 : 641 : return (repo_meta_schema_v2);
216 : 1300 : }
217 : :
218 : : static struct pkg_repo_meta_key*
219 : 0 : pkg_repo_meta_parse_cert(const ucl_object_t *obj)
220 : : {
221 : : struct pkg_repo_meta_key *key;
222 : :
223 : 0 : key = xcalloc(1, sizeof(*key));
224 : :
225 : : /*
226 : : * It is already validated so just use it as is
227 : : */
228 : 0 : key->name = xstrdup(ucl_object_tostring(ucl_object_find_key(obj, "name")));
229 : 0 : key->pubkey = xstrdup(ucl_object_tostring(ucl_object_find_key(obj, "data")));
230 : 0 : key->pubkey_type = xstrdup(ucl_object_tostring(ucl_object_find_key(obj, "type")));
231 : :
232 : 0 : return (key);
233 : : }
234 : :
235 : : #define META_EXTRACT_STRING(field) do { \
236 : : obj = ucl_object_find_key(top, (#field)); \
237 : : if (obj != NULL && obj->type == UCL_STRING) { \
238 : : free(meta->field); \
239 : : meta->field = xstrdup(ucl_object_tostring(obj)); \
240 : : } \
241 : : } while (0)
242 : :
243 : : static int
244 : 1316 : pkg_repo_meta_parse(ucl_object_t *top, struct pkg_repo_meta **target, int version)
245 : : {
246 : : const ucl_object_t *obj, *cur;
247 : 1316 : ucl_object_iter_t iter = NULL;
248 : : struct pkg_repo_meta *meta;
249 : : struct pkg_repo_meta_key *cert;
250 : :
251 : 1316 : meta = xcalloc(1, sizeof(*meta));
252 : :
253 : 1316 : pkg_repo_meta_set_default(meta);
254 : 1316 : meta->version = version;
255 : :
256 [ - + # # ]: 1316 : META_EXTRACT_STRING(maintainer);
257 [ - + # # ]: 1316 : META_EXTRACT_STRING(source);
258 : :
259 [ - + # # ]: 1316 : META_EXTRACT_STRING(conflicts);
260 [ + + - + ]: 1316 : META_EXTRACT_STRING(digests);
261 [ + + - + ]: 1316 : META_EXTRACT_STRING(manifests);
262 [ - + # # ]: 1316 : META_EXTRACT_STRING(fulldb);
263 [ + + - + ]: 1316 : META_EXTRACT_STRING(filesite);
264 [ - + # # ]: 1316 : META_EXTRACT_STRING(conflicts_archive);
265 [ + + - + ]: 1316 : META_EXTRACT_STRING(digests_archive);
266 [ + + - + ]: 1316 : META_EXTRACT_STRING(manifests_archive);
267 [ - + # # ]: 1316 : META_EXTRACT_STRING(fulldb_archive);
268 [ + + - + ]: 1316 : META_EXTRACT_STRING(filesite_archive);
269 : :
270 [ - + # # ]: 1316 : META_EXTRACT_STRING(source_identifier);
271 : :
272 : 1316 : obj = ucl_object_find_key(top, "eol");
273 [ - + # # ]: 1316 : if (obj != NULL && obj->type == UCL_INT) {
274 : 0 : meta->eol = ucl_object_toint(obj);
275 : 0 : }
276 : :
277 : 1316 : obj = ucl_object_find_key(top, "revision");
278 [ - + # # ]: 1316 : if (obj != NULL && obj->type == UCL_INT) {
279 : 0 : meta->revision = ucl_object_toint(obj);
280 : 0 : }
281 : :
282 : 1316 : obj = ucl_object_find_key(top, "packing_format");
283 [ + + - + ]: 1316 : if (obj != NULL && obj->type == UCL_STRING) {
284 : 1300 : meta->packing_format = packing_format_from_string(ucl_object_tostring(obj));
285 : 1300 : }
286 : :
287 : 1316 : obj = ucl_object_find_key(top, "digest_format");
288 [ + + - + ]: 1316 : if (obj != NULL && obj->type == UCL_STRING) {
289 : 8 : meta->digest_format = pkg_checksum_type_from_string(ucl_object_tostring(obj));
290 : 8 : }
291 : :
292 : 1316 : obj = ucl_object_find_key(top, "cert");
293 [ - + ]: 1316 : while ((cur = ucl_iterate_object(obj, &iter, false)) != NULL) {
294 : 0 : cert = pkg_repo_meta_parse_cert(cur);
295 [ # # ]: 0 : if (cert != NULL)
296 [ # # # # ]: 0 : pkghash_safe_add(meta->keys, cert->name, cert, NULL);
297 : : }
298 : :
299 : 1316 : *target = meta;
300 : :
301 : 1316 : return (EPKG_OK);
302 : : }
303 : :
304 : : #undef META_EXTRACT_STRING
305 : :
306 : : static int
307 : 1316 : pkg_repo_meta_version(ucl_object_t *top)
308 : : {
309 : : const ucl_object_t *obj;
310 : :
311 [ - + ]: 1316 : if ((obj = ucl_object_find_key(top, "version")) != NULL) {
312 [ + - ]: 1316 : if (obj->type == UCL_INT) {
313 : 1316 : return (ucl_object_toint(obj));
314 : : }
315 : 0 : }
316 : :
317 : 0 : return (-1);
318 : 1316 : }
319 : :
320 : : int
321 : 244 : pkg_repo_meta_dump_fd(struct pkg_repo_meta *meta, const int fd)
322 : : {
323 : : FILE *f;
324 : :
325 : 244 : f = fdopen(dup(fd), "w+");
326 [ - + ]: 244 : if (f == NULL) {
327 : 0 : pkg_emit_error("Cannot dump file");
328 : 0 : return (EPKG_FATAL);
329 : : }
330 : 244 : ucl_object_emit_file(pkg_repo_meta_to_ucl(meta), UCL_EMIT_JSON_COMPACT, f);
331 : 244 : fclose(f);
332 : 244 : return (EPKG_OK);
333 : 244 : }
334 : :
335 : : int
336 : 1316 : pkg_repo_meta_load(const int fd, struct pkg_repo_meta **target)
337 : : {
338 : : struct ucl_parser *parser;
339 : : ucl_object_t *top, *schema;
340 : : struct ucl_schema_error err;
341 : : int version;
342 : :
343 : 1316 : parser = ucl_parser_new(UCL_PARSER_KEY_LOWERCASE);
344 : :
345 [ - + ]: 1316 : if (!ucl_parser_add_fd(parser, fd)) {
346 : 0 : pkg_emit_error("cannot parse repository meta: %s",
347 : 0 : ucl_parser_get_error(parser));
348 : 0 : ucl_parser_free(parser);
349 : 0 : return (EPKG_FATAL);
350 : : }
351 : :
352 : 1316 : top = ucl_parser_get_object(parser);
353 : 1316 : ucl_parser_free(parser);
354 : :
355 : 1316 : version = pkg_repo_meta_version(top);
356 [ + - ]: 1316 : if (version == -1) {
357 : 0 : pkg_emit_error("repository meta has wrong version or wrong format");
358 : 0 : ucl_object_unref(top);
359 : 0 : return (EPKG_FATAL);
360 : : }
361 : :
362 : : /* Now we support only v1 and v2 meta */
363 [ + + ]: 1316 : if (version == 1) {
364 : 16 : schema = pkg_repo_meta_open_schema_v1();
365 : 16 : printf("WARNING: Meta v1 support will be removed in the next version\n");
366 : 16 : }
367 [ + - ]: 1300 : else if (version == 2)
368 : 1300 : schema = pkg_repo_meta_open_schema_v2();
369 : : else {
370 : 0 : pkg_emit_error("repository meta has wrong version %d", version);
371 : 0 : ucl_object_unref(top);
372 : 0 : return (EPKG_FATAL);
373 : : }
374 [ - + ]: 1316 : if (schema != NULL) {
375 [ - + ]: 1316 : if (!ucl_object_validate(schema, top, &err)) {
376 : 0 : printf("repository meta cannot be validated: %s\n", err.msg);
377 : 0 : ucl_object_unref(top);
378 : 0 : return (EPKG_FATAL);
379 : : }
380 : 1316 : }
381 : :
382 : 1316 : return (pkg_repo_meta_parse(top, target, version));
383 : 1316 : }
384 : :
385 : : struct pkg_repo_meta *
386 : 3611 : pkg_repo_meta_default(void)
387 : : {
388 : : struct pkg_repo_meta *meta;
389 : :
390 : 3611 : meta = xcalloc(1, sizeof(*meta));
391 : 3611 : meta->version = DEFAULT_META_VERSION;
392 : 3611 : pkg_repo_meta_set_default(meta);
393 : :
394 : 3611 : return (meta);
395 : : }
396 : :
397 : : #define META_EXPORT_FIELD(result, meta, field, type) do { \
398 : : if (meta->field != 0) \
399 : : ucl_object_insert_key((result), ucl_object_from ## type (meta->field), \
400 : : #field, 0, false); \
401 : : } while(0)
402 : :
403 : : #define META_EXPORT_FIELD_FUNC(result, meta, field, type, func) do { \
404 : : if (func(meta->field) != 0) \
405 : : ucl_object_insert_key((result), ucl_object_from ## type (func(meta->field)), \
406 : : #field, 0, false); \
407 : : } while(0)
408 : :
409 : :
410 : : ucl_object_t *
411 : 521 : pkg_repo_meta_to_ucl(struct pkg_repo_meta *meta)
412 : : {
413 : 521 : ucl_object_t *result = ucl_object_typed_new(UCL_OBJECT);
414 : :
415 [ + - ]: 521 : META_EXPORT_FIELD(result, meta, version, int);
416 [ - + ]: 521 : META_EXPORT_FIELD(result, meta, maintainer, string);
417 [ - + ]: 521 : META_EXPORT_FIELD(result, meta, source, string);
418 : :
419 [ + - ]: 521 : META_EXPORT_FIELD_FUNC(result, meta, packing_format, string,
420 : : packing_format_to_string);
421 : :
422 [ + + ]: 521 : if (meta->version == 1) {
423 [ + - ]: 8 : META_EXPORT_FIELD_FUNC(result, meta, digest_format, string,
424 : : pkg_checksum_type_to_string);
425 [ + - ]: 8 : META_EXPORT_FIELD(result, meta, digests, string);
426 [ + - ]: 8 : META_EXPORT_FIELD(result, meta, digests_archive, string);
427 : 8 : }
428 [ + - ]: 521 : META_EXPORT_FIELD(result, meta, manifests, string);
429 [ - + ]: 521 : META_EXPORT_FIELD(result, meta, conflicts, string);
430 [ - + ]: 521 : META_EXPORT_FIELD(result, meta, fulldb, string);
431 [ + - ]: 521 : META_EXPORT_FIELD(result, meta, filesite, string);
432 [ + - ]: 521 : META_EXPORT_FIELD(result, meta, manifests_archive, string);
433 [ - + ]: 521 : META_EXPORT_FIELD(result, meta, conflicts_archive, string);
434 [ - + ]: 521 : META_EXPORT_FIELD(result, meta, fulldb_archive, string);
435 [ + - ]: 521 : META_EXPORT_FIELD(result, meta, filesite_archive, string);
436 : :
437 [ - + ]: 521 : META_EXPORT_FIELD(result, meta, source_identifier, string);
438 [ - + ]: 521 : META_EXPORT_FIELD(result, meta, revision, int);
439 [ - + ]: 521 : META_EXPORT_FIELD(result, meta, eol, int);
440 : :
441 : : /* TODO: export keys */
442 : :
443 : 521 : return (result);
444 : : }
445 : :
446 : : #undef META_EXPORT_FIELD
447 : : #undef META_EXPORT_FIELD_FUNC
448 : :
449 : : #define META_SPECIAL_FILE(file, meta, field) \
450 : : special || (meta->field == NULL ? false : (strcmp(file, meta->field) == 0))
451 : :
452 : : bool
453 : 558 : pkg_repo_meta_is_special_file(const char *file, struct pkg_repo_meta *meta)
454 : : {
455 : 558 : bool special = false;
456 : :
457 [ - + - + ]: 558 : special = META_SPECIAL_FILE(file, meta, digests_archive);
458 [ + + - + ]: 558 : special = META_SPECIAL_FILE(file, meta, manifests_archive);
459 [ + + - + ]: 558 : special = META_SPECIAL_FILE(file, meta, filesite_archive);
460 [ + + + - ]: 558 : special = META_SPECIAL_FILE(file, meta, conflicts_archive);
461 [ + + + - ]: 558 : special = META_SPECIAL_FILE(file, meta, fulldb_archive);
462 : :
463 : 558 : return (special);
464 : : }
465 : :
466 : : bool
467 : 582 : pkg_repo_meta_is_old_file(const char *file, struct pkg_repo_meta *meta)
468 : : {
469 : 582 : bool special = false;
470 : :
471 [ + + ]: 582 : if (meta->version != 1)
472 [ - + - + ]: 558 : special = META_SPECIAL_FILE(file, meta, digests_archive);
473 : :
474 : 582 : return (special);
475 : : }
|