Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2014, Vsevolod Stakhov
3 : : * Copyright (c) 2012-2024 Baptiste Daroussin <bapt@FreeBSD.org>
4 : : * Copyright (c) 2012 Julien Laffaye <jlaffaye@FreeBSD.org>
5 : : * Copyright (c) 2024 Serenity Cyber Security, LLC
6 : : * Author: Gleb Popov <arrowd@FreeBSD.org>
7 : : * All rights reserved.
8 : : *
9 : : * Redistribution and use in source and binary forms, with or without
10 : : * modification, are permitted provided that the following conditions are met:
11 : : * * Redistributions of source code must retain the above copyright
12 : : * notice, this list of conditions and the following disclaimer.
13 : : * * Redistributions in binary form must reproduce the above copyright
14 : : * notice, this list of conditions and the following disclaimer in the
15 : : * documentation and/or other materials provided with the distribution.
16 : : *
17 : : * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
18 : : * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 : : * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 : : * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
21 : : * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 : : * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 : : * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 : : * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : : */
28 : :
29 : : #include <sys/stat.h>
30 : : #include <sys/param.h>
31 : : #include <sys/file.h>
32 : : #include <sys/time.h>
33 : :
34 : : #include <stdio.h>
35 : : #include <stdlib.h>
36 : : #include <string.h>
37 : : #include <unistd.h>
38 : : #include <errno.h>
39 : : #include <limits.h>
40 : : #include <fcntl.h>
41 : :
42 : : #include <archive.h>
43 : : #include <archive_entry.h>
44 : :
45 : : #include "pkg.h"
46 : : #include "private/event.h"
47 : : #include "private/utils.h"
48 : : #include "private/pkgdb.h"
49 : : #include "private/pkg.h"
50 : : #include "binary.h"
51 : : #include "binary_private.h"
52 : :
53 : : static int
54 : 72 : pkg_repo_binary_init_update(struct pkg_repo *repo)
55 : : {
56 : : sqlite3 *sqlite;
57 : 72 : const char update_check_sql[] = ""
58 : : "INSERT INTO repo_update VALUES(1);";
59 : 72 : const char update_start_sql[] = ""
60 : : "CREATE TABLE IF NOT EXISTS repo_update (n INT);";
61 : :
62 : : /* [Re]create repo */
63 [ - + ]: 72 : if (repo->ops->create(repo) != EPKG_OK) {
64 : 0 : pkg_emit_notice("Unable to create repository %s", repo->name);
65 : 0 : return (EPKG_FATAL);
66 : : }
67 [ - + ]: 72 : if (repo->ops->open(repo, R_OK|W_OK) != EPKG_OK) {
68 : 0 : pkg_emit_notice("Unable to open created repository %s", repo->name);
69 : 0 : return (EPKG_FATAL);
70 : : }
71 : :
72 : 72 : repo->ops->init(repo);
73 : :
74 [ + - ]: 72 : sqlite = PRIV_GET(repo);
75 : :
76 [ + - ]: 72 : if(sqlite3_exec(sqlite, update_check_sql, NULL, NULL, NULL) == SQLITE_OK) {
77 : 0 : pkg_emit_notice("Previous update has not been finished, restart it");
78 : 0 : return (EPKG_END);
79 : : }
80 : : else {
81 : 72 : sql_exec(sqlite, update_start_sql);
82 : : }
83 : :
84 : 72 : return (EPKG_OK);
85 : 72 : }
86 : :
87 : : static int
88 : 0 : pkg_repo_binary_delete_conflicting(const char *origin, const char *version,
89 : : bool forced)
90 : : {
91 : 0 : int ret = EPKG_FATAL;
92 : : const unsigned char *oversion;
93 : :
94 [ # # ]: 0 : if (pkg_repo_binary_run_prstatement(REPO_VERSION, origin) != SQLITE_ROW) {
95 : 0 : ret = EPKG_FATAL;
96 : 0 : goto cleanup;
97 : : }
98 : 0 : oversion = sqlite3_column_text(pkg_repo_binary_stmt_prstatement(REPO_VERSION), 0);
99 [ # # ]: 0 : if (!forced) {
100 [ # # # # ]: 0 : switch(pkg_version_cmp(oversion, version)) {
101 : : case -1:
102 : 0 : pkg_emit_error("duplicate package origin: replacing older "
103 : : "version %s in repo with package %s",
104 : 0 : oversion, origin);
105 : :
106 [ # # ]: 0 : if (pkg_repo_binary_run_prstatement(DELETE, origin, origin) !=
107 : : SQLITE_DONE)
108 : 0 : ret = EPKG_FATAL;
109 : : else
110 : 0 : ret = EPKG_OK; /* conflict cleared */
111 : :
112 : 0 : break;
113 : : case 0:
114 : : case 1:
115 : 0 : pkg_emit_error("duplicate package origin: package %s is not "
116 : : "newer than version %s already in repo",
117 : 0 : origin, oversion);
118 : 0 : ret = EPKG_END; /* keep what is already in the repo */
119 : 0 : break;
120 : : }
121 : 0 : }
122 : : else {
123 : 0 : ret = EPKG_OK;
124 [ # # ]: 0 : if (pkg_repo_binary_run_prstatement(DELETE, origin, origin) != SQLITE_DONE)
125 : 0 : ret = EPKG_FATAL;
126 : : }
127 : :
128 : : cleanup:
129 : 0 : sqlite3_reset(pkg_repo_binary_stmt_prstatement(REPO_VERSION));
130 : :
131 : 0 : return (ret);
132 : : }
133 : :
134 : : static int
135 : 150 : pkg_repo_binary_add_pkg(struct pkg *pkg, sqlite3 *sqlite, bool forced)
136 : : {
137 : : int ret;
138 : 150 : struct pkg_dep *dep = NULL;
139 : 150 : struct pkg_option *option = NULL;
140 : : struct pkg_kv *kv;
141 : : const char *arch;
142 : : int64_t package_id;
143 : :
144 [ + - ]: 150 : arch = pkg->abi != NULL ? pkg->abi : pkg->altabi;
145 : :
146 : : try_again:
147 [ - + - + ]: 300 : if ((ret = pkg_repo_binary_run_prstatement(PKG,
148 : 150 : pkg->origin, pkg->name, pkg->version, pkg->comment, pkg->desc,
149 : 150 : arch, pkg->maintainer, pkg->www, pkg->prefix, pkg->pkgsize,
150 : 150 : pkg->flatsize, (int64_t)pkg->licenselogic, pkg->sum, pkg->repopath,
151 : 300 : pkg->digest, pkg->old_digest, pkg->vital)) != SQLITE_DONE) {
152 [ # # ]: 0 : if (ret == SQLITE_CONSTRAINT) {
153 : 0 : ERROR_SQLITE(sqlite, "grmbl");
154 [ # # # # : 0 : switch(pkg_repo_binary_delete_conflicting(pkg->origin,
# # ]
155 : 0 : pkg->version, forced)) {
156 : : case EPKG_FATAL: /* sqlite error */
157 : 0 : ERROR_SQLITE(sqlite, pkg_repo_binary_sql_prstatement(PKG));
158 : 0 : return (EPKG_FATAL);
159 : : break;
160 : : case EPKG_END: /* repo already has newer */
161 : 0 : return (EPKG_END);
162 : : break;
163 : : default: /* conflict cleared, try again */
164 : 0 : goto try_again;
165 : : break;
166 : : }
167 : : } else {
168 : 0 : ERROR_SQLITE(sqlite, pkg_repo_binary_sql_prstatement(PKG));
169 : 0 : return (EPKG_FATAL);
170 : : }
171 : : }
172 : 150 : package_id = sqlite3_last_insert_rowid(sqlite);
173 : :
174 : 150 : dep = NULL;
175 [ + + ]: 209 : while (pkg_deps(pkg, &dep) == EPKG_OK) {
176 [ - + - + : 177 : if (pkg_repo_binary_run_prstatement(DEPS, dep->origin,
- + ]
177 : 118 : dep->name, dep->version, package_id) != SQLITE_DONE) {
178 : 0 : ERROR_SQLITE(sqlite, pkg_repo_binary_sql_prstatement(DEPS));
179 : 0 : return (EPKG_FATAL);
180 : : }
181 : : }
182 : :
183 [ + + + + : 298 : tll_foreach(pkg->categories, c) {
- + ]
184 : 148 : ret = pkg_repo_binary_run_prstatement(CAT1, c->item);
185 [ - + ]: 148 : if (ret == SQLITE_DONE)
186 : 296 : ret = pkg_repo_binary_run_prstatement(CAT2, package_id,
187 : 148 : c->item);
188 [ - + ]: 148 : if (ret != SQLITE_DONE) {
189 : 0 : ERROR_SQLITE(sqlite, pkg_repo_binary_sql_prstatement(CAT2));
190 : 0 : return (EPKG_FATAL);
191 : : }
192 : 148 : }
193 : :
194 [ - + - + : 150 : tll_foreach(pkg->licenses, l) {
# # ]
195 : 0 : ret = pkg_repo_binary_run_prstatement(LIC1, l->item);
196 [ # # ]: 0 : if (ret == SQLITE_DONE)
197 : 0 : ret = pkg_repo_binary_run_prstatement(LIC2, package_id,
198 : 0 : l->item);
199 [ # # ]: 0 : if (ret != SQLITE_DONE) {
200 : 0 : ERROR_SQLITE(sqlite, pkg_repo_binary_sql_prstatement(LIC2));
201 : 0 : return (EPKG_FATAL);
202 : : }
203 : 0 : }
204 : :
205 : 150 : option = NULL;
206 [ + + ]: 154 : while (pkg_options(pkg, &option) == EPKG_OK) {
207 : 4 : ret = pkg_repo_binary_run_prstatement(OPT1, option->key);
208 [ - + ]: 4 : if (ret == SQLITE_DONE)
209 : 8 : ret = pkg_repo_binary_run_prstatement(OPT2, option->key,
210 : 4 : option->value, package_id);
211 [ - + ]: 4 : if(ret != SQLITE_DONE) {
212 : 0 : ERROR_SQLITE(sqlite, pkg_repo_binary_sql_prstatement(OPT2));
213 : 0 : return (EPKG_FATAL);
214 : : }
215 : : }
216 : :
217 [ + + + + : 157 : tll_foreach(pkg->shlibs_required, s) {
+ + ]
218 : 7 : ret = pkg_repo_binary_run_prstatement(SHLIB1, s->item);
219 [ - + ]: 7 : if (ret == SQLITE_DONE)
220 : 14 : ret = pkg_repo_binary_run_prstatement(SHLIB_REQD, package_id,
221 : 7 : s->item);
222 [ - + ]: 7 : if (ret != SQLITE_DONE) {
223 : 0 : ERROR_SQLITE(sqlite, pkg_repo_binary_sql_prstatement(SHLIB_REQD));
224 : 0 : return (EPKG_FATAL);
225 : : }
226 : 7 : }
227 : :
228 [ + + + + : 153 : tll_foreach(pkg->shlibs_provided, s) {
+ - ]
229 : 3 : ret = pkg_repo_binary_run_prstatement(SHLIB1, s->item);
230 [ - + ]: 3 : if (ret == SQLITE_DONE)
231 : 6 : ret = pkg_repo_binary_run_prstatement(SHLIB_PROV, package_id,
232 : 3 : s->item);
233 [ - + ]: 3 : if (ret != SQLITE_DONE) {
234 : 0 : ERROR_SQLITE(sqlite, pkg_repo_binary_sql_prstatement(SHLIB_PROV));
235 : 0 : return (EPKG_FATAL);
236 : : }
237 : 3 : }
238 : :
239 [ + + + + : 154 : tll_foreach(pkg->provides, p) {
- + ]
240 : 4 : ret = pkg_repo_binary_run_prstatement(PROVIDE, p->item);
241 [ - + ]: 4 : if (ret == SQLITE_DONE)
242 : 8 : ret = pkg_repo_binary_run_prstatement(PROVIDES, package_id,
243 : 4 : p->item);
244 [ - + ]: 4 : if (ret != SQLITE_DONE) {
245 : 0 : ERROR_SQLITE(sqlite, pkg_repo_binary_sql_prstatement(PROVIDES));
246 : 0 : return (EPKG_FATAL);
247 : : }
248 : 4 : }
249 : :
250 [ + + + + : 157 : tll_foreach(pkg->requires, r) {
- + ]
251 : 7 : ret = pkg_repo_binary_run_prstatement(REQUIRE, r->item);
252 [ - + ]: 7 : if (ret == SQLITE_DONE)
253 : 14 : ret = pkg_repo_binary_run_prstatement(REQUIRES, package_id,
254 : 7 : r->item);
255 [ - + ]: 7 : if (ret != SQLITE_DONE) {
256 : 0 : ERROR_SQLITE(sqlite, pkg_repo_binary_sql_prstatement(REQUIRES));
257 : 0 : return (EPKG_FATAL);
258 : : }
259 : 7 : }
260 : :
261 [ + + + + : 257 : tll_foreach(pkg->annotations, k) {
- + ]
262 : 107 : kv = k->item;
263 : 107 : ret = pkg_repo_binary_run_prstatement(ANNOTATE1, kv->key);
264 [ - + ]: 107 : if (ret == SQLITE_DONE)
265 : 107 : ret = pkg_repo_binary_run_prstatement(ANNOTATE1, kv->value);
266 [ - + ]: 107 : if (ret == SQLITE_DONE)
267 : 214 : ret = pkg_repo_binary_run_prstatement(ANNOTATE2, package_id,
268 : 107 : kv->key, kv->value);
269 [ - + ]: 107 : if (ret != SQLITE_DONE) {
270 : 0 : ERROR_SQLITE(sqlite, pkg_repo_binary_sql_prstatement(ANNOTATE2));
271 : 0 : return (EPKG_FATAL);
272 : : }
273 : 107 : }
274 : :
275 : 150 : return (EPKG_OK);
276 : 150 : }
277 : :
278 : : static int
279 : : pkg_repo_binary_register_conflicts(const char *origin, char **conflicts,
280 : : int conflicts_num, sqlite3 *sqlite)
281 : : {
282 : : const char clean_conflicts_sql[] = ""
283 : : "DELETE FROM pkg_conflicts "
284 : : "WHERE package_id = ?1;";
285 : : const char select_id_sql[] = ""
286 : : "SELECT id FROM packages "
287 : : "WHERE origin = ?1;";
288 : : const char insert_conflict_sql[] = ""
289 : : "INSERT INTO pkg_conflicts "
290 : : "(package_id, conflict_id) "
291 : : "VALUES (?1, ?2);";
292 : : sqlite3_stmt *stmt = NULL;
293 : : int ret, i;
294 : : int64_t origin_id, conflict_id;
295 : :
296 : : pkg_debug(4, "pkgdb_repo_register_conflicts: running '%s'", select_id_sql);
297 : : stmt = prepare_sql(sqlite, select_id_sql);
298 : : if (stmt == NULL)
299 : : return (EPKG_FATAL);
300 : :
301 : : sqlite3_bind_text(stmt, 1, origin, -1, SQLITE_TRANSIENT);
302 : : ret = sqlite3_step(stmt);
303 : :
304 : : if (ret == SQLITE_ROW) {
305 : : origin_id = sqlite3_column_int64(stmt, 0);
306 : : }
307 : : else {
308 : : ERROR_SQLITE(sqlite, select_id_sql);
309 : : return (EPKG_FATAL);
310 : : }
311 : : sqlite3_finalize(stmt);
312 : :
313 : : pkg_debug(4, "pkgdb_repo_register_conflicts: running '%s'", clean_conflicts_sql);
314 : : stmt = prepare_sql(sqlite, clean_conflicts_sql);
315 : : if (stmt == NULL)
316 : : return (EPKG_FATAL);
317 : :
318 : : sqlite3_bind_int64(stmt, 1, origin_id);
319 : : /* Ignore cleanup result */
320 : : (void)sqlite3_step(stmt);
321 : :
322 : : sqlite3_finalize(stmt);
323 : :
324 : : for (i = 0; i < conflicts_num; i ++) {
325 : : /* Select a conflict */
326 : : pkg_debug(4, "pkgdb_repo_register_conflicts: running '%s'", select_id_sql);
327 : : stmt = prepare_sql(sqlite, select_id_sql);
328 : : if (stmt == NULL)
329 : : return (EPKG_FATAL);
330 : :
331 : : sqlite3_bind_text(stmt, 1, conflicts[i], -1, SQLITE_TRANSIENT);
332 : : ret = sqlite3_step(stmt);
333 : :
334 : : if (ret == SQLITE_ROW) {
335 : : conflict_id = sqlite3_column_int64(stmt, 0);
336 : : }
337 : : else {
338 : : ERROR_SQLITE(sqlite, select_id_sql);
339 : : return (EPKG_FATAL);
340 : : }
341 : :
342 : : sqlite3_finalize(stmt);
343 : :
344 : : /* Insert a pair */
345 : : pkg_debug(4, "pkgdb_repo_register_conflicts: running '%s'", insert_conflict_sql);
346 : : stmt = prepare_sql(sqlite, insert_conflict_sql);
347 : : if (stmt == NULL)
348 : : return (EPKG_FATAL);
349 : :
350 : : sqlite3_bind_int64(stmt, 1, origin_id);
351 : : sqlite3_bind_int64(stmt, 2, conflict_id);
352 : : ret = sqlite3_step(stmt);
353 : :
354 : : if (ret != SQLITE_DONE) {
355 : : ERROR_SQLITE(sqlite, insert_conflict_sql);
356 : : return (EPKG_FATAL);
357 : : }
358 : :
359 : : sqlite3_finalize(stmt);
360 : : }
361 : :
362 : : return (EPKG_OK);
363 : : }
364 : :
365 : : static int
366 : 150 : pkg_repo_binary_add_from_ucl(sqlite3 *sqlite, ucl_object_t *o, struct pkg_repo *repo)
367 : : {
368 : 150 : int rc = EPKG_OK;
369 : : struct pkg *pkg;
370 : : const char *abi;
371 : :
372 : 150 : rc = pkg_new(&pkg, PKG_REMOTE);
373 [ - + ]: 150 : if (rc != EPKG_OK)
374 : 0 : return (EPKG_FATAL);
375 : :
376 : 150 : rc = pkg_parse_manifest_ucl(pkg, o);
377 [ - + ]: 150 : if (rc != EPKG_OK) {
378 : 0 : goto cleanup;
379 : : }
380 : :
381 [ - + # # ]: 150 : if (pkg->digest == NULL || !pkg_checksum_is_valid(pkg->digest, strlen(pkg->digest)))
382 : 150 : pkg_checksum_calculate(pkg, NULL, false, true, false);
383 [ + - ]: 150 : abi = pkg->abi != NULL ? pkg->abi : pkg->altabi;
384 [ + - - + ]: 150 : if (abi == NULL || !is_valid_abi(abi, true)) {
385 : 0 : rc = EPKG_FATAL;
386 : 0 : pkg_emit_error("repository %s contains packages with wrong ABI: %s",
387 : 0 : repo->name, abi);
388 : 0 : goto cleanup;
389 : : }
390 [ + - ]: 150 : if (!is_valid_os_version(pkg)) {
391 : 0 : rc = EPKG_FATAL;
392 : 0 : pkg_emit_error("repository %s contains packages for wrong OS "
393 : 0 : "version: %s", repo->name, abi);
394 : 0 : goto cleanup;
395 : : }
396 : :
397 : 150 : free(pkg->reponame);
398 : 150 : pkg->reponame = xstrdup(repo->name);
399 : :
400 : 150 : rc = pkg_repo_binary_add_pkg(pkg, sqlite, true);
401 : :
402 : : cleanup:
403 : 150 : ucl_object_unref(o);
404 : 150 : pkg_free(pkg);
405 : :
406 : 150 : return (rc);
407 : 150 : }
408 : :
409 : : static int
410 : 0 : pkg_repo_binary_add_from_manifest(const char *buf, sqlite3 *sqlite, size_t len,
411 : : struct pkg_repo *repo)
412 : : {
413 : 0 : int rc = EPKG_OK;
414 : : struct pkg *pkg;
415 : : const char *abi;
416 : :
417 : 0 : rc = pkg_new(&pkg, PKG_REMOTE);
418 [ # # ]: 0 : if (rc != EPKG_OK)
419 : 0 : return (EPKG_FATAL);
420 : :
421 : 0 : rc = pkg_parse_manifest(pkg, buf, len);
422 [ # # ]: 0 : if (rc != EPKG_OK) {
423 : 0 : goto cleanup;
424 : : }
425 : :
426 [ # # # # ]: 0 : if (pkg->digest == NULL || !pkg_checksum_is_valid(pkg->digest, strlen(pkg->digest)))
427 : 0 : pkg_checksum_calculate(pkg, NULL, false, true, false);
428 [ # # ]: 0 : abi = pkg->abi != NULL ? pkg->abi : pkg->altabi;
429 [ # # # # ]: 0 : if (abi == NULL || !is_valid_abi(abi, true)) {
430 : 0 : rc = EPKG_FATAL;
431 : 0 : pkg_emit_error("repository %s contains packages with wrong ABI: %s",
432 : 0 : repo->name, abi);
433 : 0 : goto cleanup;
434 : : }
435 [ # # ]: 0 : if (!is_valid_os_version(pkg)) {
436 : 0 : rc = EPKG_FATAL;
437 : 0 : pkg_emit_error("repository %s contains packages for wrong OS "
438 : 0 : "version: %s", repo->name, abi);
439 : 0 : goto cleanup;
440 : : }
441 : :
442 : 0 : free(pkg->reponame);
443 : 0 : pkg->reponame = xstrdup(repo->name);
444 : :
445 : 0 : rc = pkg_repo_binary_add_pkg(pkg, sqlite, true);
446 : :
447 : : cleanup:
448 : 0 : pkg_free(pkg);
449 : :
450 : 0 : return (rc);
451 : 0 : }
452 : :
453 : : static void __unused
454 : : pkg_repo_binary_parse_conflicts(FILE *f, sqlite3 *sqlite)
455 : : {
456 : : size_t linecap = 0;
457 : : ssize_t linelen;
458 : : char *linebuf = NULL, *p, **deps;
459 : : const char *origin, *pdep;
460 : : int ndep, i;
461 : : const char conflicts_clean_sql[] = ""
462 : : "DELETE FROM pkg_conflicts;";
463 : :
464 : : pkg_debug(4, "pkg_parse_conflicts_file: running '%s'", conflicts_clean_sql);
465 : : (void)sql_exec(sqlite, conflicts_clean_sql);
466 : :
467 : : while ((linelen = getline(&linebuf, &linecap, f)) > 0) {
468 : : p = linebuf;
469 : : origin = strsep(&p, ":");
470 : : /* Check dependencies number */
471 : : pdep = p;
472 : : ndep = 1;
473 : : while (*pdep != '\0') {
474 : : if (*pdep == ',')
475 : : ndep ++;
476 : : pdep ++;
477 : : }
478 : : deps = xmalloc(sizeof(char *) * ndep);
479 : : for (i = 0; i < ndep; i ++) {
480 : : deps[i] = strsep(&p, ",\n");
481 : : }
482 : : pkg_repo_binary_register_conflicts(origin, deps, ndep, sqlite);
483 : : free(deps);
484 : : }
485 : :
486 : : free(linebuf);
487 : : }
488 : :
489 : : static void
490 : 0 : rollback_repo(void *data)
491 : : {
492 : 0 : const char *name = (const char *)data;
493 : : char path[MAXPATHLEN];
494 : :
495 : 0 : snprintf(path, sizeof(path), "%s-pkgtemp", name);
496 : 0 : unlink(name);
497 : 0 : rename(path, name);
498 : 0 : snprintf(path, sizeof(path), "%s-journal", name);
499 : 0 : unlink(path);
500 : 0 : }
501 : :
502 : : static void
503 : 144 : save_ucl(struct pkg_repo *repo, ucl_object_t *obj, const char* dst_name)
504 : : {
505 [ + - ]: 144 : if (obj == NULL)
506 : 0 : return;
507 [ - + # # ]: 144 : if (repo->dfd == -1 && pkg_repo_open(repo) == EPKG_FATAL)
508 : 0 : return;
509 : 144 : int fd = openat(repo->dfd, dst_name, O_CREAT|O_TRUNC|O_RDWR, 0644);
510 [ + - ]: 144 : if (fd == -1) {
511 : 0 : pkg_emit_errno("openat", "repo save_ucl");
512 : 0 : return;
513 : : }
514 : 144 : ucl_object_emit_fd(obj, UCL_EMIT_JSON_COMPACT, fd);
515 : 144 : close(fd);
516 : 144 : }
517 : :
518 : : static int
519 : 134 : pkg_repo_binary_update_proceed(const char *name, struct pkg_repo *repo,
520 : : time_t *mtime, bool force)
521 : : {
522 : 134 : int rc = EPKG_FATAL, cancel = 0;
523 : 134 : sqlite3 *sqlite = NULL;
524 : 134 : int cnt = 0;
525 : : time_t local_t;
526 : 134 : bool in_trans = false;
527 : 134 : char *path = NULL;
528 : 134 : FILE *f = NULL;
529 : 134 : char *line = NULL;
530 : 134 : size_t linecap = 0;
531 : 134 : ssize_t linelen, totallen = 0;
532 : : struct pkg_repo_content prc;
533 : 134 : ucl_object_t *data = NULL;
534 : :
535 : 134 : pkg_debug(1, "Pkgrepo, begin update of '%s'", name);
536 : :
537 : : /* In forced mode, ignore mtime */
538 [ + + ]: 134 : if (force)
539 : 78 : *mtime = 0;
540 : :
541 : : /* Fetch meta */
542 : 134 : local_t = *mtime;
543 [ + + ]: 134 : if (pkg_repo_fetch_meta(repo, &local_t) == EPKG_FATAL)
544 : 8 : pkg_emit_notice("repository %s has no meta file, using "
545 : 8 : "default settings", repo->name);
546 : :
547 : : /* Fetch packagesite */
548 : 134 : local_t = *mtime;
549 : 134 : prc.manifest_fd = -1;
550 : 134 : prc.mtime = *mtime;
551 : 134 : prc.manifest_len = 0;
552 : 134 : prc.data_fd = -1;
553 : :
554 : 134 : rc = pkg_repo_fetch_data_fd(repo, &prc);
555 [ + + ]: 134 : if (rc == EPKG_UPTODATE)
556 : 54 : goto cleanup;
557 : :
558 [ + + ]: 80 : if (rc == EPKG_OK) {
559 : 72 : struct ucl_parser *p = ucl_parser_new(0);
560 [ + - ]: 72 : if (!ucl_parser_add_fd(p, prc.data_fd)) {
561 : 0 : pkg_emit_error("Error parsing data file: %s'",
562 : 0 : ucl_parser_get_error(p));
563 : 0 : ucl_parser_free(p);
564 : 0 : close(prc.data_fd);
565 : 0 : rc = EPKG_FATAL;
566 : 0 : goto cleanup;
567 : : }
568 : 72 : data = ucl_parser_get_object(p);
569 : 72 : ucl_parser_free(p);
570 : 72 : close(prc.data_fd);
571 : : /* XXX TODO check against a schema */
572 : 72 : } else {
573 : 8 : rc = pkg_repo_fetch_remote_extract_fd(repo, &prc);
574 [ + - ]: 8 : if (rc != EPKG_OK)
575 : 8 : goto cleanup;
576 : 0 : f = fdopen(prc.manifest_fd, "r");
577 : 0 : rewind(f);
578 : : }
579 : :
580 : 72 : *mtime = prc.mtime;
581 : : /*fconflicts = repo_fetch_remote_extract_tmp(repo,
582 : : repo_conflicts_archive, "txz", &local_t,
583 : : &rc, repo_conflicts_file);*/
584 : :
585 : : /* Load local repository data */
586 : 72 : xasprintf(&path, "%s-pkgtemp", name);
587 : 72 : rename(name, path);
588 : 72 : pkg_register_cleanup_callback(rollback_repo, (void *)name);
589 : 72 : rc = pkg_repo_binary_init_update(repo);
590 [ - + ]: 72 : if (rc != EPKG_OK) {
591 : 0 : rc = EPKG_FATAL;
592 : 0 : goto cleanup;
593 : : }
594 : :
595 : : /* Here sqlite is initialized */
596 [ + - ]: 72 : sqlite = PRIV_GET(repo);
597 : :
598 : 72 : pkg_debug(1, "Pkgrepo, reading new metadata");
599 : :
600 : 72 : pkg_emit_incremental_update_begin(repo->name);
601 : 72 : pkg_emit_progress_start("Processing entries");
602 : :
603 : : /* 200MB should be enough */
604 : 72 : sql_exec(sqlite, "PRAGMA mmap_size = 209715200;");
605 : 72 : sql_exec(sqlite, "PRAGMA page_size = %d;", getpagesize());
606 : 72 : sql_exec(sqlite, "PRAGMA foreign_keys = OFF;");
607 : 72 : sql_exec(sqlite, "PRAGMA journal_mode = TRUNCATE;");
608 : 72 : sql_exec(sqlite, "PRAGMA synchronous = FULL;");
609 : :
610 : 72 : rc = pkgdb_transaction_begin_sqlite(sqlite, "REPO");
611 [ - + ]: 72 : if (rc != EPKG_OK)
612 : 0 : goto cleanup;
613 : :
614 : 72 : in_trans = true;
615 [ + - ]: 72 : if (f != NULL) {
616 [ # # ]: 0 : while ((linelen = getline(&line, &linecap, f)) > 0) {
617 : 0 : cnt++;
618 : 0 : totallen += linelen;
619 [ # # ]: 0 : if ((cnt % 10 ) == 0)
620 : 0 : cancel = pkg_emit_progress_tick(totallen, prc.manifest_len);
621 : 0 : rc = pkg_repo_binary_add_from_manifest(line, sqlite,
622 : 0 : linelen, repo);
623 [ # # # # ]: 0 : if (rc != EPKG_OK || cancel != 0)
624 : 0 : break;
625 : : }
626 : 0 : pkg_emit_progress_tick(prc.manifest_len, prc.manifest_len);
627 : 0 : }
628 [ - + ]: 72 : if (data != NULL) {
629 : 72 : ucl_object_t *pkgs = ucl_object_ref(ucl_object_find_key(data, "packages"));
630 : 72 : int nbel = ucl_array_size(pkgs);
631 [ + + ]: 222 : while (cnt < nbel) {
632 : 150 : ucl_object_t *o = ucl_array_pop_first(pkgs);
633 : 150 : cnt++;
634 [ + - ]: 150 : if ((cnt % 10 ) == 0)
635 : 0 : cancel = pkg_emit_progress_tick(cnt, nbel);
636 : 150 : rc = pkg_repo_binary_add_from_ucl(sqlite, o, repo);
637 [ + - - + ]: 150 : if (rc != EPKG_OK || cancel != 0)
638 : 0 : break;
639 : : }
640 : 72 : pkg_emit_progress_tick(cnt, nbel);
641 : 144 : save_ucl(repo,
642 : 72 : ucl_object_ref(ucl_object_find_key(data, "groups")), "groups.ucl");
643 : 144 : save_ucl(repo,
644 : 72 : ucl_object_ref(ucl_object_find_key(data, "expired_packages")),
645 : : "expired_packages.ucl");
646 : 72 : }
647 : :
648 [ - + ]: 72 : if (rc == EPKG_OK)
649 : 72 : pkg_emit_incremental_update(repo->name, cnt);
650 : :
651 : 72 : sql_exec(sqlite, ""
652 : : "CREATE INDEX packages_origin ON packages(origin COLLATE NOCASE);"
653 : : "CREATE INDEX packages_name ON packages(name COLLATE NOCASE);"
654 : : "CREATE INDEX packages_uid_nocase ON packages(name COLLATE NOCASE, origin COLLATE NOCASE);"
655 : : "CREATE INDEX packages_version_nocase ON packages(name COLLATE NOCASE, version);"
656 : : "CREATE INDEX packages_uid ON packages(name, origin);"
657 : : "CREATE INDEX packages_version ON packages(name, version);"
658 : : "CREATE UNIQUE INDEX packages_digest ON packages(manifestdigest);"
659 : : );
660 : :
661 : : cleanup:
662 : :
663 [ + + ]: 134 : if (in_trans) {
664 [ + - ]: 72 : if (rc != EPKG_OK)
665 : 0 : pkgdb_transaction_rollback_sqlite(sqlite, "REPO");
666 : :
667 [ + - ]: 72 : if (pkgdb_transaction_commit_sqlite(sqlite, "REPO") != EPKG_OK)
668 : 0 : rc = EPKG_FATAL;
669 : 72 : }
670 [ + + ]: 134 : if (path != NULL) {
671 : : /* restore the previous db in case of failures */
672 [ - + # # ]: 72 : if (rc != EPKG_OK && rc != EPKG_UPTODATE) {
673 : 0 : unlink(name);
674 : 0 : rename(path, name);
675 : 0 : }
676 : 72 : unlink(path);
677 : 72 : free(path);
678 : 72 : }
679 : 134 : pkg_unregister_cleanup_callback(rollback_repo, (void *)name);
680 : 134 : free(line);
681 [ + - ]: 134 : if (f != NULL)
682 : 0 : fclose(f);
683 : :
684 : 134 : return (rc);
685 : : }
686 : :
687 : : int
688 : 134 : pkg_repo_binary_update(struct pkg_repo *repo, bool force)
689 : : {
690 : 134 : char *lockpath = NULL;
691 : 134 : const char update_finish_sql[] = ""
692 : : "DROP TABLE repo_update;";
693 : : char filename[PATH_MAX];
694 : : sqlite3 *sqlite;
695 : :
696 : : struct stat st;
697 : 134 : time_t t = 0;
698 : 134 : int ld, res = EPKG_FATAL;
699 : :
700 : 134 : bool got_meta = false;
701 : :
702 : 134 : sqlite3_initialize();
703 : :
704 [ - + ]: 134 : if (!pkg_repo_enabled(repo))
705 : 0 : return (EPKG_OK);
706 : :
707 : 134 : pkg_debug(1, "PkgRepo: verifying update for %s", repo->name);
708 : :
709 : 268 : (void)snprintf(filename, sizeof(filename), "%s/%s",
710 : 134 : ctx.dbdir, pkg_repo_binary_get_filename(repo));
711 : :
712 : : /* First of all, try to open and init repo and check whether it is fine */
713 [ + - + - ]: 134 : if (repo->dfd == -1 && pkg_repo_open(repo) == EPKG_FATAL)
714 : 0 : return (EPKG_FATAL);
715 : :
716 [ + + ]: 134 : if (repo->ops->open(repo, R_OK|W_OK) != EPKG_OK) {
717 : 74 : pkg_debug(1, "PkgRepo: need forced update of %s", repo->name);
718 : 74 : t = 0;
719 : 74 : force = true;
720 : 74 : }
721 : : else {
722 : 60 : repo->ops->close(repo, false);
723 [ - + ]: 60 : if (fstatat(repo->dfd, "meta", &st, 0) != -1) {
724 [ + + ]: 60 : t = force ? 0 : st.st_mtime;
725 : 60 : got_meta = true;
726 : 60 : }
727 : :
728 [ + - - + ]: 60 : if (got_meta && stat(filename, &st) != -1) {
729 [ + + ]: 60 : if (!force)
730 : 56 : t = st.st_mtime;
731 : 60 : }
732 : : }
733 : :
734 : 134 : ld = openat(repo->dfd, "lock", O_CREAT|O_TRUNC|O_WRONLY, 00644);
735 [ + - ]: 134 : if (ld == -1) {
736 : 0 : pkg_emit_errno("openat", "lock");
737 : 0 : }
738 [ + - ]: 134 : if (flock(ld, LOCK_EX|LOCK_NB) == -1) {
739 : : /* lock blocking anyway to let the other end finish */
740 : 0 : pkg_emit_notice("Waiting for another process to "
741 : 0 : "update repository %s", repo->name);
742 : 0 : flock(ld, LOCK_EX);
743 : 0 : res = EPKG_OK;
744 : 0 : t = 0;
745 : 0 : goto cleanup;
746 : : }
747 : :
748 : 134 : res = pkg_repo_binary_update_proceed(filename, repo, &t, force);
749 [ + + + + ]: 134 : if (res != EPKG_OK && res != EPKG_UPTODATE) {
750 : 8 : pkg_emit_notice("Unable to update repository %s", repo->name);
751 : 8 : goto cleanup;
752 : : }
753 : :
754 : : /* Finish updated repo */
755 [ + + ]: 198 : if (res == EPKG_OK) {
756 [ + - ]: 72 : sqlite = PRIV_GET(repo);
757 : 72 : sql_exec(sqlite, update_finish_sql);
758 : 72 : }
759 : :
760 : : cleanup:
761 [ - + ]: 134 : if (ld != -1) {
762 : 134 : flock(ld, LOCK_UN);
763 : 134 : close(ld);
764 : 134 : }
765 : :
766 [ + - ]: 134 : if (lockpath != NULL)
767 : 0 : unlinkat(repo->dfd, "lock", 0);
768 : :
769 : : /* Set mtime from http request if possible */
770 [ + + + + ]: 134 : if (t != 0 && res == EPKG_OK) {
771 : 72 : struct timespec ts[2] = {
772 : 144 : {
773 : 72 : .tv_sec = t,
774 : : .tv_nsec = 0
775 : : },
776 : 144 : {
777 : 72 : .tv_sec = t,
778 : : .tv_nsec = 0
779 : : }
780 : : };
781 : :
782 : 72 : utimensat(AT_FDCWD, filename, ts, 0);
783 [ + + ]: 72 : if (got_meta)
784 : 6 : utimensat(repo->dfd, "meta", ts, 0);
785 : 72 : }
786 : :
787 [ + + ]: 134 : if (repo->priv != NULL)
788 : 72 : repo->ops->close(repo, false);
789 : :
790 : 134 : return (res);
791 : 134 : }
|