Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/pkg_repo_meta.c
2065 views
1
/*-
2
* Copyright (c) 2019 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2014 Vsevolod Stakhov <[email protected]>
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
pkg_repo_meta_set_default(struct pkg_repo_meta *meta)
42
{
43
meta->digest_format = PKG_HASH_TYPE_SHA256_BASE32;
44
meta->packing_format = DEFAULT_COMPRESSION;
45
46
/* Not use conflicts for now */
47
meta->conflicts = NULL;
48
meta->conflicts_archive = NULL;
49
meta->data = xstrdup("data");
50
meta->data_archive = xstrdup("data");
51
meta->manifests = xstrdup("packagesite.yaml");
52
meta->manifests_archive = xstrdup("packagesite");
53
meta->filesite = xstrdup("filesite.yaml");
54
meta->filesite_archive = xstrdup("filesite");
55
/* Not using fulldb */
56
meta->fulldb = NULL;
57
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
meta->digests = xstrdup("digests");
68
meta->digests_archive = xstrdup("digests");
69
}
70
71
void
72
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
if (meta != NULL) {
81
free(meta->conflicts);
82
free(meta->manifests);
83
free(meta->digests);
84
free(meta->data);
85
free(meta->fulldb);
86
free(meta->filesite);
87
free(meta->conflicts_archive);
88
free(meta->data_archive);
89
free(meta->manifests_archive);
90
free(meta->digests_archive);
91
free(meta->fulldb_archive);
92
free(meta->filesite_archive);
93
free(meta->maintainer);
94
free(meta->source);
95
free(meta->source_identifier);
96
it = pkghash_iterator(meta->keys);
97
while (pkghash_next(&it)) {
98
k = (struct pkg_repo_meta_key *)it.value;
99
free(k->name);
100
free(k->pubkey);
101
free(k->pubkey_type);
102
free(k);
103
}
104
pkghash_destroy(meta->keys);
105
free(meta);
106
}
107
}
108
109
static ucl_object_t*
110
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
if (repo_meta_schema_v1 != NULL)
150
return (repo_meta_schema_v1);
151
152
parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS);
153
if (!ucl_parser_add_chunk(parser, meta_schema_str_v1,
154
sizeof(meta_schema_str_v1) - 1)) {
155
pkg_emit_error("cannot parse schema for repo meta: %s",
156
ucl_parser_get_error(parser));
157
ucl_parser_free(parser);
158
return (NULL);
159
}
160
161
repo_meta_schema_v1 = ucl_parser_get_object(parser);
162
ucl_parser_free(parser);
163
164
return (repo_meta_schema_v1);
165
}
166
167
static ucl_object_t*
168
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
if (repo_meta_schema_v2 != NULL)
207
return (repo_meta_schema_v2);
208
209
parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS);
210
if (!ucl_parser_add_chunk(parser, meta_schema_str_v2,
211
sizeof(meta_schema_str_v2) - 1)) {
212
pkg_emit_error("cannot parse schema for repo meta: %s",
213
ucl_parser_get_error(parser));
214
ucl_parser_free(parser);
215
return (NULL);
216
}
217
218
repo_meta_schema_v2 = ucl_parser_get_object(parser);
219
ucl_parser_free(parser);
220
221
return (repo_meta_schema_v2);
222
}
223
224
static struct pkg_repo_meta_key*
225
pkg_repo_meta_parse_cert(const ucl_object_t *obj)
226
{
227
struct pkg_repo_meta_key *key;
228
229
key = xcalloc(1, sizeof(*key));
230
231
/*
232
* It is already validated so just use it as is
233
*/
234
key->name = xstrdup(ucl_object_tostring(ucl_object_find_key(obj, "name")));
235
key->pubkey = xstrdup(ucl_object_tostring(ucl_object_find_key(obj, "data")));
236
key->pubkey_type = xstrdup(ucl_object_tostring(ucl_object_find_key(obj, "type")));
237
238
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
pkg_repo_meta_parse(ucl_object_t *top, struct pkg_repo_meta **target, int version)
251
{
252
const ucl_object_t *obj, *cur;
253
ucl_object_iter_t iter = NULL;
254
struct pkg_repo_meta *meta;
255
struct pkg_repo_meta_key *cert;
256
257
meta = xcalloc(1, sizeof(*meta));
258
259
pkg_repo_meta_set_default(meta);
260
meta->version = version;
261
262
META_EXTRACT_STRING(maintainer);
263
META_EXTRACT_STRING(source);
264
265
META_EXTRACT_STRING(conflicts);
266
META_EXTRACT_STRING(data);
267
META_EXTRACT_STRING(digests);
268
META_EXTRACT_STRING(manifests);
269
META_EXTRACT_STRING(fulldb);
270
META_EXTRACT_STRING(filesite);
271
META_EXTRACT_STRING(conflicts_archive);
272
META_EXTRACT_STRING(digests_archive);
273
META_EXTRACT_STRING(manifests_archive);
274
META_EXTRACT_STRING(fulldb_archive);
275
META_EXTRACT_STRING(filesite_archive);
276
277
META_EXTRACT_STRING(source_identifier);
278
279
obj = ucl_object_find_key(top, "eol");
280
if (obj != NULL && obj->type == UCL_INT) {
281
meta->eol = ucl_object_toint(obj);
282
}
283
284
obj = ucl_object_find_key(top, "revision");
285
if (obj != NULL && obj->type == UCL_INT) {
286
meta->revision = ucl_object_toint(obj);
287
}
288
289
obj = ucl_object_find_key(top, "packing_format");
290
if (obj != NULL && obj->type == UCL_STRING) {
291
meta->packing_format = packing_format_from_string(ucl_object_tostring(obj));
292
}
293
294
obj = ucl_object_find_key(top, "digest_format");
295
if (obj != NULL && obj->type == UCL_STRING) {
296
meta->digest_format = pkg_checksum_type_from_string(ucl_object_tostring(obj));
297
}
298
299
obj = ucl_object_find_key(top, "cert");
300
while ((cur = ucl_iterate_object(obj, &iter, false)) != NULL) {
301
cert = pkg_repo_meta_parse_cert(cur);
302
if (cert != NULL)
303
pkghash_safe_add(meta->keys, cert->name, cert, NULL);
304
}
305
306
*target = meta;
307
308
return (EPKG_OK);
309
}
310
311
#undef META_EXTRACT_STRING
312
313
static int
314
pkg_repo_meta_version(ucl_object_t *top)
315
{
316
const ucl_object_t *obj;
317
318
if ((obj = ucl_object_find_key(top, "version")) != NULL) {
319
if (obj->type == UCL_INT) {
320
return (ucl_object_toint(obj));
321
}
322
}
323
324
return (-1);
325
}
326
327
int
328
pkg_repo_meta_dump_fd(struct pkg_repo_meta *meta, const int fd)
329
{
330
FILE *f;
331
332
f = fdopen(dup(fd), "w+");
333
if (f == NULL) {
334
pkg_emit_error("Cannot dump file");
335
return (EPKG_FATAL);
336
}
337
ucl_object_emit_file(pkg_repo_meta_to_ucl(meta), UCL_EMIT_JSON_COMPACT, f);
338
fclose(f);
339
return (EPKG_OK);
340
}
341
342
int
343
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
parser = ucl_parser_new(UCL_PARSER_KEY_LOWERCASE);
351
352
if (!ucl_parser_add_fd(parser, fd)) {
353
pkg_emit_error("cannot parse repository meta: %s",
354
ucl_parser_get_error(parser));
355
ucl_parser_free(parser);
356
return (EPKG_FATAL);
357
}
358
359
top = ucl_parser_get_object(parser);
360
ucl_parser_free(parser);
361
362
version = pkg_repo_meta_version(top);
363
if (version == -1) {
364
pkg_emit_error("repository meta has wrong version or wrong format");
365
ucl_object_unref(top);
366
return (EPKG_FATAL);
367
}
368
369
/* Now we support only v1 and v2 meta */
370
if (version == 1) {
371
schema = pkg_repo_meta_open_schema_v1();
372
printf("WARNING: Meta v1 support will be removed in the next version\n");
373
}
374
else if (version == 2)
375
schema = pkg_repo_meta_open_schema_v2();
376
else {
377
pkg_emit_error("repository meta has wrong version %d", version);
378
ucl_object_unref(top);
379
return (EPKG_FATAL);
380
}
381
if (schema != NULL) {
382
if (!ucl_object_validate(schema, top, &err)) {
383
printf("repository meta cannot be validated: %s\n", err.msg);
384
ucl_object_unref(top);
385
return (EPKG_FATAL);
386
}
387
}
388
389
return (pkg_repo_meta_parse(top, target, version));
390
}
391
392
struct pkg_repo_meta *
393
pkg_repo_meta_default(void)
394
{
395
struct pkg_repo_meta *meta;
396
397
meta = xcalloc(1, sizeof(*meta));
398
meta->version = DEFAULT_META_VERSION;
399
pkg_repo_meta_set_default(meta);
400
401
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
pkg_repo_meta_to_ucl(struct pkg_repo_meta *meta)
419
{
420
ucl_object_t *result = ucl_object_typed_new(UCL_OBJECT);
421
422
META_EXPORT_FIELD(result, meta, version, int);
423
META_EXPORT_FIELD(result, meta, maintainer, string);
424
META_EXPORT_FIELD(result, meta, source, string);
425
426
META_EXPORT_FIELD_FUNC(result, meta, packing_format, string,
427
packing_format_to_string);
428
429
if (meta->version == 1) {
430
META_EXPORT_FIELD_FUNC(result, meta, digest_format, string,
431
pkg_checksum_type_to_string);
432
META_EXPORT_FIELD(result, meta, digests, string);
433
META_EXPORT_FIELD(result, meta, digests_archive, string);
434
}
435
META_EXPORT_FIELD(result, meta, manifests, string);
436
META_EXPORT_FIELD(result, meta, data, string);
437
META_EXPORT_FIELD(result, meta, conflicts, string);
438
META_EXPORT_FIELD(result, meta, fulldb, string);
439
META_EXPORT_FIELD(result, meta, filesite, string);
440
META_EXPORT_FIELD(result, meta, manifests_archive, string);
441
META_EXPORT_FIELD(result, meta, conflicts_archive, string);
442
META_EXPORT_FIELD(result, meta, fulldb_archive, string);
443
META_EXPORT_FIELD(result, meta, filesite_archive, string);
444
445
META_EXPORT_FIELD(result, meta, source_identifier, string);
446
META_EXPORT_FIELD(result, meta, revision, int);
447
META_EXPORT_FIELD(result, meta, eol, int);
448
449
/* TODO: export keys */
450
451
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
pkg_repo_meta_is_special_file(const char *file, struct pkg_repo_meta *meta)
462
{
463
bool special = false;
464
465
special = META_SPECIAL_FILE(file, meta, digests_archive);
466
special = META_SPECIAL_FILE(file, meta, manifests_archive);
467
special = META_SPECIAL_FILE(file, meta, filesite_archive);
468
special = META_SPECIAL_FILE(file, meta, conflicts_archive);
469
special = META_SPECIAL_FILE(file, meta, fulldb_archive);
470
special = META_SPECIAL_FILE(file, meta, data_archive);
471
472
return (special);
473
}
474
475
bool
476
pkg_repo_meta_is_old_file(const char *file, struct pkg_repo_meta *meta)
477
{
478
bool special = false;
479
480
if (meta->version != 1)
481
special = META_SPECIAL_FILE(file, meta, digests_archive);
482
483
return (special);
484
}
485
486