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 : 2649 : pkg_repo_meta_set_default(struct pkg_repo_meta *meta)
42 : : {
43 : 2649 : meta->digest_format = PKG_HASH_TYPE_SHA256_BASE32;
44 : 2649 : meta->packing_format = DEFAULT_COMPRESSION;
45 : :
46 : : /* Not use conflicts for now */
47 : 2649 : meta->conflicts = NULL;
48 : 2649 : meta->conflicts_archive = NULL;
49 : 2649 : meta->data = xstrdup("data");
50 : 2649 : meta->data_archive = xstrdup("data");
51 : 2649 : meta->manifests = xstrdup("packagesite.yaml");
52 : 2649 : meta->manifests_archive = xstrdup("packagesite");
53 : 2649 : meta->filesite = xstrdup("filesite.yaml");
54 : 2649 : meta->filesite_archive = xstrdup("filesite");
55 : : /* Not using fulldb */
56 : 2649 : meta->fulldb = NULL;
57 : 2649 : meta->fulldb_archive = NULL;
58 : :
59 : : /*
60 : : * digest is only used on legacy v1 repository
61 : : * but pkg_repo_meta_is_special_file depend on the
62 : : * information in the pkg_repo_meta.
63 : : * Leave digests here so pkg will not complain that
64 : : * repodir/digest.txz isn't a valid package when switching
65 : : * from version 1 to version 2
66 : : */
67 : 2649 : meta->digests = xstrdup("digests");
68 : 2649 : meta->digests_archive = xstrdup("digests");
69 : 2649 : }
70 : :
71 : : void
72 : 2412 : pkg_repo_meta_free(struct pkg_repo_meta *meta)
73 : : {
74 : : struct pkg_repo_meta_key *k;
75 : : pkghash_it it;
76 : :
77 : : /*
78 : : * It is safe to free NULL pointer by standard
79 : : */
80 [ + - ]: 2412 : if (meta != NULL) {
81 : 2412 : free(meta->conflicts);
82 : 2412 : free(meta->manifests);
83 : 2412 : free(meta->digests);
84 : 2412 : free(meta->data);
85 : 2412 : free(meta->fulldb);
86 : 2412 : free(meta->filesite);
87 : 2412 : free(meta->conflicts_archive);
88 : 2412 : free(meta->data_archive);
89 : 2412 : free(meta->manifests_archive);
90 : 2412 : free(meta->digests_archive);
91 : 2412 : free(meta->fulldb_archive);
92 : 2412 : free(meta->filesite_archive);
93 : 2412 : free(meta->maintainer);
94 : 2412 : free(meta->source);
95 : 2412 : free(meta->source_identifier);
96 : 2412 : it = pkghash_iterator(meta->keys);
97 [ - + ]: 2412 : while (pkghash_next(&it)) {
98 : 0 : k = (struct pkg_repo_meta_key *)it.value;
99 : 0 : free(k->name);
100 : 0 : free(k->pubkey);
101 : 0 : free(k->pubkey_type);
102 : 0 : free(k);
103 : : }
104 : 2412 : pkghash_destroy(meta->keys);
105 : 2412 : free(meta);
106 : 2412 : }
107 : 2412 : }
108 : :
109 : : static ucl_object_t*
110 : 0 : pkg_repo_meta_open_schema_v1()
111 : : {
112 : : struct ucl_parser *parser;
113 : : static const char meta_schema_str_v1[] = ""
114 : : "{"
115 : : "type = object;"
116 : : "properties {"
117 : : "version = {type = integer};\n"
118 : : "maintainer = {type = string};\n"
119 : : "source = {type = string};\n"
120 : : "packing_format = {enum = [tzst, txz, tbz, tgz, tar]};\n"
121 : : "digest_format = {enum = [sha256_base32, sha256_hex, blake2_base32, blake2s_base32]};\n"
122 : : "digests = {type = string};\n"
123 : : "manifests = {type = string};\n"
124 : : "conflicts = {type = string};\n"
125 : : "fulldb = {type = string};\n"
126 : : "filesite = {type = string};\n"
127 : : "digests_archive = {type = string};\n"
128 : : "manifests_archive = {type = string};\n"
129 : : "conflicts_archive = {type = string};\n"
130 : : "fulldb_archive = {type = string};\n"
131 : : "filesite_archive = {type = string};\n"
132 : : "source_identifier = {type = string};\n"
133 : : "revision = {type = integer};\n"
134 : : "eol = {type = integer};\n"
135 : : "cert = {"
136 : : " type = object;\n"
137 : : " properties {"
138 : : " type = {enum = [rsa]};\n"
139 : : " data = {type = string};\n"
140 : : " name = {type = string};\n"
141 : : " }"
142 : : " required = [type, data, name];\n"
143 : : "};\n"
144 : :
145 : : "}\n"
146 : : "required = [version]\n"
147 : : "}";
148 : :
149 [ # # ]: 0 : if (repo_meta_schema_v1 != NULL)
150 : 0 : return (repo_meta_schema_v1);
151 : :
152 : 0 : parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS);
153 [ # # ]: 0 : if (!ucl_parser_add_chunk(parser, meta_schema_str_v1,
154 : : sizeof(meta_schema_str_v1) - 1)) {
155 : 0 : pkg_emit_error("cannot parse schema for repo meta: %s",
156 : 0 : ucl_parser_get_error(parser));
157 : 0 : ucl_parser_free(parser);
158 : 0 : return (NULL);
159 : : }
160 : :
161 : 0 : repo_meta_schema_v1 = ucl_parser_get_object(parser);
162 : 0 : ucl_parser_free(parser);
163 : :
164 : 0 : return (repo_meta_schema_v1);
165 : 0 : }
166 : :
167 : : static ucl_object_t*
168 : 311 : pkg_repo_meta_open_schema_v2()
169 : : {
170 : : struct ucl_parser *parser;
171 : : static const char meta_schema_str_v2[] = ""
172 : : "{"
173 : : "type = object;"
174 : : "properties {"
175 : : "version = {type = integer};\n"
176 : : "maintainer = {type = string};\n"
177 : : "source = {type = string};\n"
178 : : "packing_format = {enum = [tzst, txz, tbz, tgz, tar]};\n"
179 : : "manifests = {type = string};\n"
180 : : "data = { type = string };\n"
181 : : "conflicts = {type = string};\n"
182 : : "fulldb = {type = string};\n"
183 : : "filesite = {type = string};\n"
184 : : "data_archive = { type = string};\n"
185 : : "manifests_archive = {type = string};\n"
186 : : "conflicts_archive = {type = string};\n"
187 : : "fulldb_archive = {type = string};\n"
188 : : "filesite_archive = {type = string};\n"
189 : : "source_identifier = {type = string};\n"
190 : : "revision = {type = integer};\n"
191 : : "eol = {type = integer};\n"
192 : : "cert = {"
193 : : " type = object;\n"
194 : : " properties {"
195 : : " type = {enum = [rsa]};\n"
196 : : " data = {type = string};\n"
197 : : " name = {type = string};\n"
198 : : " }"
199 : : " required = [type, data, name];\n"
200 : : "};\n"
201 : :
202 : : "}\n"
203 : : "required = [version]\n"
204 : : "}";
205 : :
206 [ + + ]: 311 : if (repo_meta_schema_v2 != NULL)
207 : 188 : return (repo_meta_schema_v2);
208 : :
209 : 123 : parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS);
210 [ + - ]: 123 : if (!ucl_parser_add_chunk(parser, meta_schema_str_v2,
211 : : sizeof(meta_schema_str_v2) - 1)) {
212 : 0 : pkg_emit_error("cannot parse schema for repo meta: %s",
213 : 0 : ucl_parser_get_error(parser));
214 : 0 : ucl_parser_free(parser);
215 : 0 : return (NULL);
216 : : }
217 : :
218 : 123 : repo_meta_schema_v2 = ucl_parser_get_object(parser);
219 : 123 : ucl_parser_free(parser);
220 : :
221 : 123 : return (repo_meta_schema_v2);
222 : 311 : }
223 : :
224 : : static struct pkg_repo_meta_key*
225 : 0 : pkg_repo_meta_parse_cert(const ucl_object_t *obj)
226 : : {
227 : : struct pkg_repo_meta_key *key;
228 : :
229 : 0 : key = xcalloc(1, sizeof(*key));
230 : :
231 : : /*
232 : : * It is already validated so just use it as is
233 : : */
234 : 0 : key->name = xstrdup(ucl_object_tostring(ucl_object_find_key(obj, "name")));
235 : 0 : key->pubkey = xstrdup(ucl_object_tostring(ucl_object_find_key(obj, "data")));
236 : 0 : key->pubkey_type = xstrdup(ucl_object_tostring(ucl_object_find_key(obj, "type")));
237 : :
238 : 0 : return (key);
239 : : }
240 : :
241 : : #define META_EXTRACT_STRING(field) do { \
242 : : obj = ucl_object_find_key(top, (#field)); \
243 : : if (obj != NULL && obj->type == UCL_STRING) { \
244 : : free(meta->field); \
245 : : meta->field = xstrdup(ucl_object_tostring(obj)); \
246 : : } \
247 : : } while (0)
248 : :
249 : : static int
250 : 311 : pkg_repo_meta_parse(ucl_object_t *top, struct pkg_repo_meta **target, int version)
251 : : {
252 : : const ucl_object_t *obj, *cur;
253 : 311 : ucl_object_iter_t iter = NULL;
254 : : struct pkg_repo_meta *meta;
255 : : struct pkg_repo_meta_key *cert;
256 : :
257 : 311 : meta = xcalloc(1, sizeof(*meta));
258 : :
259 : 311 : pkg_repo_meta_set_default(meta);
260 : 311 : meta->version = version;
261 : :
262 [ - + # # ]: 311 : META_EXTRACT_STRING(maintainer);
263 [ - + # # ]: 311 : META_EXTRACT_STRING(source);
264 : :
265 [ - + # # ]: 311 : META_EXTRACT_STRING(conflicts);
266 [ + + - + ]: 311 : META_EXTRACT_STRING(data);
267 [ - + # # ]: 311 : META_EXTRACT_STRING(digests);
268 [ + + - + ]: 311 : META_EXTRACT_STRING(manifests);
269 [ - + # # ]: 311 : META_EXTRACT_STRING(fulldb);
270 [ + + - + ]: 311 : META_EXTRACT_STRING(filesite);
271 [ - + # # ]: 311 : META_EXTRACT_STRING(conflicts_archive);
272 [ - + # # ]: 311 : META_EXTRACT_STRING(digests_archive);
273 [ + + - + ]: 311 : META_EXTRACT_STRING(manifests_archive);
274 [ - + # # ]: 311 : META_EXTRACT_STRING(fulldb_archive);
275 [ + + - + ]: 311 : META_EXTRACT_STRING(filesite_archive);
276 : :
277 [ - + # # ]: 311 : META_EXTRACT_STRING(source_identifier);
278 : :
279 : 311 : obj = ucl_object_find_key(top, "eol");
280 [ - + # # ]: 311 : if (obj != NULL && obj->type == UCL_INT) {
281 : 0 : meta->eol = ucl_object_toint(obj);
282 : 0 : }
283 : :
284 : 311 : obj = ucl_object_find_key(top, "revision");
285 [ - + # # ]: 311 : if (obj != NULL && obj->type == UCL_INT) {
286 : 0 : meta->revision = ucl_object_toint(obj);
287 : 0 : }
288 : :
289 : 311 : obj = ucl_object_find_key(top, "packing_format");
290 [ + + - + ]: 311 : if (obj != NULL && obj->type == UCL_STRING) {
291 : 309 : meta->packing_format = packing_format_from_string(ucl_object_tostring(obj));
292 : 309 : }
293 : :
294 : 311 : obj = ucl_object_find_key(top, "digest_format");
295 [ - + # # ]: 311 : if (obj != NULL && obj->type == UCL_STRING) {
296 : 0 : meta->digest_format = pkg_checksum_type_from_string(ucl_object_tostring(obj));
297 : 0 : }
298 : :
299 : 311 : obj = ucl_object_find_key(top, "cert");
300 [ - + ]: 311 : while ((cur = ucl_iterate_object(obj, &iter, false)) != NULL) {
301 : 0 : cert = pkg_repo_meta_parse_cert(cur);
302 [ # # ]: 0 : if (cert != NULL)
303 [ # # # # ]: 0 : pkghash_safe_add(meta->keys, cert->name, cert, NULL);
304 : : }
305 : :
306 : 311 : *target = meta;
307 : :
308 : 311 : return (EPKG_OK);
309 : : }
310 : :
311 : : #undef META_EXTRACT_STRING
312 : :
313 : : static int
314 : 317 : pkg_repo_meta_version(ucl_object_t *top)
315 : : {
316 : : const ucl_object_t *obj;
317 : :
318 [ + + ]: 317 : if ((obj = ucl_object_find_key(top, "version")) != NULL) {
319 [ + - ]: 311 : if (obj->type == UCL_INT) {
320 : 311 : return (ucl_object_toint(obj));
321 : : }
322 : 0 : }
323 : :
324 : 6 : return (-1);
325 : 317 : }
326 : :
327 : : int
328 : 72 : pkg_repo_meta_dump_fd(struct pkg_repo_meta *meta, const int fd)
329 : : {
330 : : FILE *f;
331 : :
332 : 72 : f = fdopen(dup(fd), "w+");
333 [ + - ]: 72 : if (f == NULL) {
334 : 0 : pkg_emit_error("Cannot dump file");
335 : 0 : return (EPKG_FATAL);
336 : : }
337 : 72 : ucl_object_emit_file(pkg_repo_meta_to_ucl(meta), UCL_EMIT_JSON_COMPACT, f);
338 : 72 : fclose(f);
339 : 72 : return (EPKG_OK);
340 : 72 : }
341 : :
342 : : int
343 : 317 : pkg_repo_meta_load(const int fd, struct pkg_repo_meta **target)
344 : : {
345 : : struct ucl_parser *parser;
346 : : ucl_object_t *top, *schema;
347 : : struct ucl_schema_error err;
348 : : int version;
349 : :
350 : 317 : parser = ucl_parser_new(UCL_PARSER_KEY_LOWERCASE);
351 : :
352 [ - + ]: 317 : if (!ucl_parser_add_fd(parser, fd)) {
353 : 0 : pkg_emit_error("cannot parse repository meta: %s",
354 : 0 : ucl_parser_get_error(parser));
355 : 0 : ucl_parser_free(parser);
356 : 0 : return (EPKG_FATAL);
357 : : }
358 : :
359 : 317 : top = ucl_parser_get_object(parser);
360 : 317 : ucl_parser_free(parser);
361 : :
362 : 317 : version = pkg_repo_meta_version(top);
363 [ + + ]: 317 : if (version == -1) {
364 : 6 : pkg_emit_error("repository meta has wrong version or wrong format");
365 : 6 : ucl_object_unref(top);
366 : 6 : return (EPKG_FATAL);
367 : : }
368 : :
369 : : /* Now we support only v1 and v2 meta */
370 [ - + ]: 311 : if (version == 1) {
371 : 0 : schema = pkg_repo_meta_open_schema_v1();
372 : 0 : printf("WARNING: Meta v1 support will be removed in the next version\n");
373 : 0 : }
374 [ + - ]: 311 : else if (version == 2)
375 : 311 : schema = pkg_repo_meta_open_schema_v2();
376 : : else {
377 : 0 : pkg_emit_error("repository meta has wrong version %d", version);
378 : 0 : ucl_object_unref(top);
379 : 0 : return (EPKG_FATAL);
380 : : }
381 [ - + ]: 311 : if (schema != NULL) {
382 [ + - ]: 311 : if (!ucl_object_validate(schema, top, &err)) {
383 : 0 : printf("repository meta cannot be validated: %s\n", err.msg);
384 : 0 : ucl_object_unref(top);
385 : 0 : return (EPKG_FATAL);
386 : : }
387 : 311 : }
388 : :
389 : 311 : return (pkg_repo_meta_parse(top, target, version));
390 : 317 : }
391 : :
392 : : struct pkg_repo_meta *
393 : 2338 : pkg_repo_meta_default(void)
394 : : {
395 : : struct pkg_repo_meta *meta;
396 : :
397 : 2338 : meta = xcalloc(1, sizeof(*meta));
398 : 2338 : meta->version = DEFAULT_META_VERSION;
399 : 2338 : pkg_repo_meta_set_default(meta);
400 : :
401 : 2338 : return (meta);
402 : : }
403 : :
404 : : #define META_EXPORT_FIELD(result, meta, field, type) do { \
405 : : if (meta->field != 0) \
406 : : ucl_object_insert_key((result), ucl_object_from ## type (meta->field), \
407 : : #field, 0, false); \
408 : : } while(0)
409 : :
410 : : #define META_EXPORT_FIELD_FUNC(result, meta, field, type, func) do { \
411 : : if (func(meta->field) != 0) \
412 : : ucl_object_insert_key((result), ucl_object_from ## type (func(meta->field)), \
413 : : #field, 0, false); \
414 : : } while(0)
415 : :
416 : :
417 : : ucl_object_t *
418 : 147 : pkg_repo_meta_to_ucl(struct pkg_repo_meta *meta)
419 : : {
420 : 147 : ucl_object_t *result = ucl_object_typed_new(UCL_OBJECT);
421 : :
422 [ - + ]: 147 : META_EXPORT_FIELD(result, meta, version, int);
423 [ + - ]: 147 : META_EXPORT_FIELD(result, meta, maintainer, string);
424 [ + - ]: 147 : META_EXPORT_FIELD(result, meta, source, string);
425 : :
426 [ - + ]: 147 : META_EXPORT_FIELD_FUNC(result, meta, packing_format, string,
427 : : packing_format_to_string);
428 : :
429 [ + - ]: 147 : if (meta->version == 1) {
430 [ # # ]: 0 : META_EXPORT_FIELD_FUNC(result, meta, digest_format, string,
431 : : pkg_checksum_type_to_string);
432 [ # # ]: 0 : META_EXPORT_FIELD(result, meta, digests, string);
433 [ # # ]: 0 : META_EXPORT_FIELD(result, meta, digests_archive, string);
434 : 0 : }
435 [ - + ]: 147 : META_EXPORT_FIELD(result, meta, manifests, string);
436 [ - + ]: 147 : META_EXPORT_FIELD(result, meta, data, string);
437 [ + - ]: 147 : META_EXPORT_FIELD(result, meta, conflicts, string);
438 [ + - ]: 147 : META_EXPORT_FIELD(result, meta, fulldb, string);
439 [ - + ]: 147 : META_EXPORT_FIELD(result, meta, filesite, string);
440 [ - + ]: 147 : META_EXPORT_FIELD(result, meta, manifests_archive, string);
441 [ + - ]: 147 : META_EXPORT_FIELD(result, meta, conflicts_archive, string);
442 [ + - ]: 147 : META_EXPORT_FIELD(result, meta, fulldb_archive, string);
443 [ - + ]: 147 : META_EXPORT_FIELD(result, meta, filesite_archive, string);
444 : :
445 [ + - ]: 147 : META_EXPORT_FIELD(result, meta, source_identifier, string);
446 [ + - ]: 147 : META_EXPORT_FIELD(result, meta, revision, int);
447 [ + - ]: 147 : META_EXPORT_FIELD(result, meta, eol, int);
448 : :
449 : : /* TODO: export keys */
450 : :
451 : 147 : return (result);
452 : : }
453 : :
454 : : #undef META_EXPORT_FIELD
455 : : #undef META_EXPORT_FIELD_FUNC
456 : :
457 : : #define META_SPECIAL_FILE(file, meta, field) \
458 : : special || (meta->field == NULL ? false : STREQ(file, meta->field))
459 : :
460 : : bool
461 : 161 : pkg_repo_meta_is_special_file(const char *file, struct pkg_repo_meta *meta)
462 : : {
463 : 161 : bool special = false;
464 : :
465 [ - + + - : 161 : special = META_SPECIAL_FILE(file, meta, digests_archive);
- + ]
466 [ - + + - : 161 : special = META_SPECIAL_FILE(file, meta, manifests_archive);
+ + ]
467 [ + + + - : 161 : special = META_SPECIAL_FILE(file, meta, filesite_archive);
- + ]
468 [ + + - + : 161 : special = META_SPECIAL_FILE(file, meta, conflicts_archive);
- + ]
469 [ + + - + : 161 : special = META_SPECIAL_FILE(file, meta, fulldb_archive);
- + ]
470 [ + + + - : 161 : special = META_SPECIAL_FILE(file, meta, data_archive);
+ + ]
471 : :
472 : 161 : return (special);
473 : : }
474 : :
475 : : bool
476 : 161 : pkg_repo_meta_is_old_file(const char *file, struct pkg_repo_meta *meta)
477 : : {
478 : 161 : bool special = false;
479 : :
480 [ - + ]: 161 : if (meta->version != 1)
481 [ - + + - : 161 : special = META_SPECIAL_FILE(file, meta, digests_archive);
- + ]
482 : :
483 : 161 : return (special);
484 : : }
|