2 * be_sync.c : backend for sync databases
4 * Copyright (c) 2006-2012 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/types.h>
29 #include <archive_entry.h>
35 #include "alpm_list.h"
42 static char *get_sync_dir(alpm_handle_t
*handle
)
44 size_t len
= strlen(handle
->dbpath
) + 6;
48 MALLOC(syncpath
, len
, RET_ERR(handle
, ALPM_ERR_MEMORY
, NULL
));
49 sprintf(syncpath
, "%s%s", handle
->dbpath
, "sync/");
51 if(stat(syncpath
, &buf
) != 0) {
52 _alpm_log(handle
, ALPM_LOG_DEBUG
, "database dir '%s' does not exist, creating it\n",
54 if(_alpm_makepath(syncpath
) != 0) {
56 RET_ERR(handle
, ALPM_ERR_SYSTEM
, NULL
);
58 } else if(!S_ISDIR(buf
.st_mode
)) {
59 _alpm_log(handle
, ALPM_LOG_WARNING
, _("removing invalid file: %s\n"), syncpath
);
60 if(unlink(syncpath
) != 0 || _alpm_makepath(syncpath
) != 0) {
62 RET_ERR(handle
, ALPM_ERR_SYSTEM
, NULL
);
69 static int sync_db_validate(alpm_db_t
*db
)
71 alpm_siglevel_t level
;
74 if(db
->status
& DB_STATUS_VALID
|| db
->status
& DB_STATUS_MISSING
) {
77 if(db
->status
& DB_STATUS_INVALID
) {
78 db
->handle
->pm_errno
= ALPM_ERR_DB_INVALID_SIG
;
82 dbpath
= _alpm_db_path(db
);
84 /* pm_errno set in _alpm_db_path() */
88 /* we can skip any validation if the database doesn't exist */
89 if(_alpm_access(db
->handle
, NULL
, dbpath
, R_OK
) != 0 && errno
== ENOENT
) {
90 db
->status
&= ~DB_STATUS_EXISTS
;
91 db
->status
|= DB_STATUS_MISSING
;
92 _alpm_log(db
->handle
, ALPM_LOG_WARNING
,
93 "database file for '%s' does not exist\n", db
->treename
);
96 db
->status
|= DB_STATUS_EXISTS
;
97 db
->status
&= ~DB_STATUS_MISSING
;
99 /* this takes into account the default verification level if UNKNOWN
100 * was assigned to this db */
101 level
= alpm_db_get_siglevel(db
);
103 if(level
& ALPM_SIG_DATABASE
) {
107 alpm_siglist_t
*siglist
;
108 ret
= _alpm_check_pgp_helper(db
->handle
, dbpath
, NULL
,
109 level
& ALPM_SIG_DATABASE_OPTIONAL
, level
& ALPM_SIG_DATABASE_MARGINAL_OK
,
110 level
& ALPM_SIG_DATABASE_UNKNOWN_OK
, &siglist
);
112 retry
= _alpm_process_siglist(db
->handle
, db
->treename
, siglist
,
113 level
& ALPM_SIG_DATABASE_OPTIONAL
, level
& ALPM_SIG_DATABASE_MARGINAL_OK
,
114 level
& ALPM_SIG_DATABASE_UNKNOWN_OK
);
116 alpm_siglist_cleanup(siglist
);
121 db
->status
&= ~DB_STATUS_VALID
;
122 db
->status
|= DB_STATUS_INVALID
;
123 db
->handle
->pm_errno
= ALPM_ERR_DB_INVALID_SIG
;
129 db
->status
|= DB_STATUS_VALID
;
130 db
->status
&= ~DB_STATUS_INVALID
;
134 /** Update a package database
136 * An update of the package database \a db will be attempted. Unless
137 * \a force is true, the update will only be performed if the remote
138 * database was modified since the last update.
140 * This operation requires a database lock, and will return an applicable error
141 * if the lock could not be obtained.
145 * alpm_list_t *syncs = alpm_get_syncdbs();
146 * for(i = syncs; i; i = alpm_list_next(i)) {
147 * alpm_db_t *db = alpm_list_getdata(i);
148 * result = alpm_db_update(0, db);
151 * printf("Unable to update database: %s\n", alpm_strerrorlast());
152 * } else if(result == 1) {
153 * printf("Database already up to date\n");
155 * printf("Database updated\n");
160 * @ingroup alpm_databases
161 * @note After a successful update, the \link alpm_db_get_pkgcache()
162 * package cache \endlink will be invalidated
163 * @param force if true, then forces the update, otherwise update only in case
164 * the database isn't up to date
165 * @param db pointer to the package database to update
166 * @return 0 on success, -1 on error (pm_errno is set accordingly), 1 if up to
169 int SYMEXPORT
alpm_db_update(int force
, alpm_db_t
*db
)
175 alpm_handle_t
*handle
;
176 alpm_siglevel_t level
;
179 ASSERT(db
!= NULL
, return -1);
181 handle
->pm_errno
= 0;
182 ASSERT(db
!= handle
->db_local
, RET_ERR(handle
, ALPM_ERR_WRONG_ARGS
, -1));
183 ASSERT(db
->servers
!= NULL
, RET_ERR(handle
, ALPM_ERR_SERVER_NONE
, -1));
185 syncpath
= get_sync_dir(handle
);
190 /* make sure we have a sane umask */
191 oldmask
= umask(0022);
193 level
= alpm_db_get_siglevel(db
);
195 /* attempt to grab a lock */
196 if(_alpm_handle_lock(handle
)) {
199 RET_ERR(handle
, ALPM_ERR_HANDLE_LOCK
, -1);
202 for(i
= db
->servers
; i
; i
= i
->next
) {
203 const char *server
= i
->data
;
204 struct dload_payload payload
;
208 memset(&payload
, 0, sizeof(struct dload_payload
));
210 /* set hard upper limit of 25MiB */
211 payload
.max_size
= 25 * 1024 * 1024;
213 /* print server + filename into a buffer */
214 len
= strlen(server
) + strlen(db
->treename
) + 5;
215 /* TODO fix leak syncpath and umask unset */
216 MALLOC(payload
.fileurl
, len
, RET_ERR(handle
, ALPM_ERR_MEMORY
, -1));
217 snprintf(payload
.fileurl
, len
, "%s/%s.db", server
, db
->treename
);
218 payload
.handle
= handle
;
219 payload
.force
= force
;
220 payload
.unlink_on_fail
= 1;
222 ret
= _alpm_download(&payload
, syncpath
, NULL
);
223 _alpm_dload_payload_reset(&payload
);
225 if(ret
== 0 && (level
& ALPM_SIG_DATABASE
)) {
226 /* an existing sig file is no good at this point */
227 char *sigpath
= _alpm_sigpath(handle
, _alpm_db_path(db
));
235 /* if we downloaded a DB, we want the .sig from the same server */
236 /* print server + filename into a buffer (leave space for .sig) */
237 len
= strlen(server
) + strlen(db
->treename
) + 9;
238 /* TODO fix leak syncpath and umask unset */
239 MALLOC(payload
.fileurl
, len
, RET_ERR(handle
, ALPM_ERR_MEMORY
, -1));
240 snprintf(payload
.fileurl
, len
, "%s/%s.db.sig", server
, db
->treename
);
241 payload
.handle
= handle
;
243 payload
.errors_ok
= (level
& ALPM_SIG_DATABASE_OPTIONAL
);
245 /* set hard upper limit of 16KiB */
246 payload
.max_size
= 16 * 1024;
248 sig_ret
= _alpm_download(&payload
, syncpath
, NULL
);
249 /* errors_ok suppresses error messages, but not the return code */
250 sig_ret
= payload
.errors_ok
? 0 : sig_ret
;
251 _alpm_dload_payload_reset(&payload
);
254 if(ret
!= -1 && sig_ret
!= -1) {
260 /* files match, do nothing */
261 handle
->pm_errno
= 0;
263 } else if(ret
== -1) {
264 /* pm_errno was set by the download code */
265 _alpm_log(handle
, ALPM_LOG_DEBUG
, "failed to sync db: %s\n",
266 alpm_strerror(handle
->pm_errno
));
270 /* Cache needs to be rebuilt */
271 _alpm_db_free_pkgcache(db
);
273 /* clear all status flags regarding validity/existence */
274 db
->status
&= ~DB_STATUS_VALID
;
275 db
->status
&= ~DB_STATUS_INVALID
;
276 db
->status
&= ~DB_STATUS_EXISTS
;
277 db
->status
&= ~DB_STATUS_MISSING
;
279 if(sync_db_validate(db
)) {
280 /* pm_errno should be set */
286 if(_alpm_handle_unlock(handle
)) {
287 _alpm_log(handle
, ALPM_LOG_WARNING
, _("could not remove lock file %s\n"),
295 /* Forward decl so I don't reorganize the whole file right now */
296 static int sync_db_read(alpm_db_t
*db
, struct archive
*archive
,
297 struct archive_entry
*entry
, alpm_pkg_t
**likely_pkg
);
299 static alpm_pkgvalidation_t
_sync_get_validation(alpm_pkg_t
*pkg
)
301 if(pkg
->validation
) {
302 return pkg
->validation
;
306 pkg
->validation
|= ALPM_PKG_VALIDATION_MD5SUM
;
309 pkg
->validation
|= ALPM_PKG_VALIDATION_SHA256SUM
;
311 if(pkg
->base64_sig
) {
312 pkg
->validation
|= ALPM_PKG_VALIDATION_SIGNATURE
;
315 if(!pkg
->validation
) {
316 pkg
->validation
|= ALPM_PKG_VALIDATION_NONE
;
319 return pkg
->validation
;
322 static alpm_pkg_t
*load_pkg_for_entry(alpm_db_t
*db
, const char *entryname
,
323 const char **entry_filename
, alpm_pkg_t
*likely_pkg
)
325 char *pkgname
= NULL
, *pkgver
= NULL
;
326 unsigned long pkgname_hash
;
329 /* get package and db file names */
331 char *fname
= strrchr(entryname
, '/');
333 *entry_filename
= fname
+ 1;
335 *entry_filename
= NULL
;
338 if(_alpm_splitname(entryname
, &pkgname
, &pkgver
, &pkgname_hash
) != 0) {
339 _alpm_log(db
->handle
, ALPM_LOG_ERROR
,
340 _("invalid name for database entry '%s'\n"), entryname
);
344 if(likely_pkg
&& pkgname_hash
== likely_pkg
->name_hash
345 && strcmp(likely_pkg
->name
, pkgname
) == 0) {
348 pkg
= _alpm_pkghash_find(db
->pkgcache
, pkgname
);
351 pkg
= _alpm_pkg_new();
353 RET_ERR(db
->handle
, ALPM_ERR_MEMORY
, NULL
);
357 pkg
->version
= pkgver
;
358 pkg
->name_hash
= pkgname_hash
;
360 pkg
->origin
= ALPM_PKG_FROM_SYNCDB
;
361 pkg
->origin_data
.db
= db
;
362 pkg
->ops
= &default_pkg_ops
;
363 pkg
->ops
->get_validation
= _sync_get_validation
;
364 pkg
->handle
= db
->handle
;
366 /* add to the collection */
367 _alpm_log(db
->handle
, ALPM_LOG_FUNCTION
, "adding '%s' to package cache for db '%s'\n",
368 pkg
->name
, db
->treename
);
369 db
->pkgcache
= _alpm_pkghash_add(db
->pkgcache
, pkg
);
378 /* This function doesn't work as well as one might think, as size of database
379 * entries varies considerably. Adding signatures nearly doubles the size of a
380 * single entry; deltas also can make for large variations in size. These
381 * current values are heavily influenced by Arch Linux; databases with no
382 * deltas and a single signature per package. */
383 static size_t estimate_package_count(struct stat
*st
, struct archive
*archive
)
387 switch(archive_compression(archive
)) {
388 case ARCHIVE_COMPRESSION_NONE
:
391 case ARCHIVE_COMPRESSION_GZIP
:
392 case ARCHIVE_COMPRESSION_COMPRESS
:
395 case ARCHIVE_COMPRESSION_BZIP2
:
398 case ARCHIVE_COMPRESSION_LZMA
:
399 case ARCHIVE_COMPRESSION_XZ
:
402 #ifdef ARCHIVE_COMPRESSION_UU
403 case ARCHIVE_COMPRESSION_UU
:
404 per_package
= 3015 * 4 / 3;
408 /* assume it is at least somewhat compressed */
411 return (size_t)((st
->st_size
/ per_package
) + 1);
414 static int sync_db_populate(alpm_db_t
*db
)
420 struct archive
*archive
;
421 struct archive_entry
*entry
;
422 alpm_pkg_t
*pkg
= NULL
;
424 if(db
->status
& DB_STATUS_INVALID
) {
425 RET_ERR(db
->handle
, ALPM_ERR_DB_INVALID
, -1);
427 if(db
->status
& DB_STATUS_MISSING
) {
428 RET_ERR(db
->handle
, ALPM_ERR_DB_NOT_FOUND
, -1);
430 dbpath
= _alpm_db_path(db
);
432 /* pm_errno set in _alpm_db_path() */
436 fd
= _alpm_open_archive(db
->handle
, dbpath
, &buf
,
437 &archive
, ALPM_ERR_DB_OPEN
);
441 est_count
= estimate_package_count(&buf
, archive
);
443 db
->pkgcache
= _alpm_pkghash_create(est_count
);
444 if(db
->pkgcache
== NULL
) {
445 db
->handle
->pm_errno
= ALPM_ERR_MEMORY
;
450 while(archive_read_next_header(archive
, &entry
) == ARCHIVE_OK
) {
451 mode_t mode
= archive_entry_mode(entry
);
455 /* we have desc, depends or deltas - parse it */
456 if(sync_db_read(db
, archive
, entry
, &pkg
) != 0) {
457 _alpm_log(db
->handle
, ALPM_LOG_ERROR
,
458 _("could not parse package description file '%s' from db '%s'\n"),
459 archive_entry_pathname(entry
), db
->treename
);
465 count
= alpm_list_count(db
->pkgcache
->list
);
467 db
->pkgcache
->list
= alpm_list_msort(db
->pkgcache
->list
,
468 (size_t)count
, _alpm_pkg_cmp
);
470 _alpm_log(db
->handle
, ALPM_LOG_DEBUG
,
471 "added %d packages to package cache for db '%s'\n",
472 count
, db
->treename
);
475 archive_read_finish(archive
);
482 #define READ_NEXT() do { \
483 if(_alpm_archive_fgets(archive, &buf) != ARCHIVE_OK) goto error; \
485 _alpm_strip_newline(line, buf.real_line_size); \
488 #define READ_AND_STORE(f) do { \
490 STRDUP(f, line, goto error); \
493 #define READ_AND_STORE_ALL(f) do { \
495 if(_alpm_archive_fgets(archive, &buf) != ARCHIVE_OK) goto error; \
496 if(_alpm_strip_newline(buf.line, buf.real_line_size) == 0) break; \
497 STRDUP(linedup, buf.line, goto error); \
498 f = alpm_list_add(f, linedup); \
499 } while(1) /* note the while(1) and not (0) */
501 #define READ_AND_SPLITDEP(f) do { \
502 if(_alpm_archive_fgets(archive, &buf) != ARCHIVE_OK) goto error; \
503 if(_alpm_strip_newline(buf.line, buf.real_line_size) == 0) break; \
504 f = alpm_list_add(f, _alpm_splitdep(line)); \
505 } while(1) /* note the while(1) and not (0) */
507 static int sync_db_read(alpm_db_t
*db
, struct archive
*archive
,
508 struct archive_entry
*entry
, alpm_pkg_t
**likely_pkg
)
510 const char *entryname
, *filename
;
512 struct archive_read_buffer buf
;
514 entryname
= archive_entry_pathname(entry
);
515 if(entryname
== NULL
) {
516 _alpm_log(db
->handle
, ALPM_LOG_DEBUG
,
517 "invalid archive entry provided to _alpm_sync_db_read, skipping\n");
521 _alpm_log(db
->handle
, ALPM_LOG_FUNCTION
, "loading package data from archive entry %s\n",
524 memset(&buf
, 0, sizeof(buf
));
525 /* 512K for a line length seems reasonable */
526 buf
.max_line_size
= 512 * 1024;
528 pkg
= load_pkg_for_entry(db
, entryname
, &filename
, *likely_pkg
);
531 _alpm_log(db
->handle
, ALPM_LOG_DEBUG
,
532 "entry %s could not be loaded into %s sync database",
533 entryname
, db
->treename
);
537 if(strcmp(filename
, "desc") == 0 || strcmp(filename
, "depends") == 0
538 || strcmp(filename
, "deltas") == 0) {
540 while((ret
= _alpm_archive_fgets(archive
, &buf
)) == ARCHIVE_OK
) {
541 char *line
= buf
.line
;
542 if(_alpm_strip_newline(line
, buf
.real_line_size
) == 0) {
543 /* length of stripped line was zero */
547 if(strcmp(line
, "%NAME%") == 0) {
549 if(strcmp(line
, pkg
->name
) != 0) {
550 _alpm_log(db
->handle
, ALPM_LOG_ERROR
, _("%s database is inconsistent: name "
551 "mismatch on package %s\n"), db
->treename
, pkg
->name
);
553 } else if(strcmp(line
, "%VERSION%") == 0) {
555 if(strcmp(line
, pkg
->version
) != 0) {
556 _alpm_log(db
->handle
, ALPM_LOG_ERROR
, _("%s database is inconsistent: version "
557 "mismatch on package %s\n"), db
->treename
, pkg
->name
);
559 } else if(strcmp(line
, "%FILENAME%") == 0) {
560 READ_AND_STORE(pkg
->filename
);
561 } else if(strcmp(line
, "%DESC%") == 0) {
562 READ_AND_STORE(pkg
->desc
);
563 } else if(strcmp(line
, "%GROUPS%") == 0) {
564 READ_AND_STORE_ALL(pkg
->groups
);
565 } else if(strcmp(line
, "%URL%") == 0) {
566 READ_AND_STORE(pkg
->url
);
567 } else if(strcmp(line
, "%LICENSE%") == 0) {
568 READ_AND_STORE_ALL(pkg
->licenses
);
569 } else if(strcmp(line
, "%ARCH%") == 0) {
570 READ_AND_STORE(pkg
->arch
);
571 } else if(strcmp(line
, "%BUILDDATE%") == 0) {
573 pkg
->builddate
= _alpm_parsedate(line
);
574 } else if(strcmp(line
, "%PACKAGER%") == 0) {
575 READ_AND_STORE(pkg
->packager
);
576 } else if(strcmp(line
, "%CSIZE%") == 0) {
578 pkg
->size
= _alpm_strtoofft(line
);
579 } else if(strcmp(line
, "%ISIZE%") == 0) {
581 pkg
->isize
= _alpm_strtoofft(line
);
582 } else if(strcmp(line
, "%MD5SUM%") == 0) {
583 READ_AND_STORE(pkg
->md5sum
);
584 } else if(strcmp(line
, "%SHA256SUM%") == 0) {
585 READ_AND_STORE(pkg
->sha256sum
);
586 } else if(strcmp(line
, "%PGPSIG%") == 0) {
587 READ_AND_STORE(pkg
->base64_sig
);
588 } else if(strcmp(line
, "%REPLACES%") == 0) {
589 READ_AND_SPLITDEP(pkg
->replaces
);
590 } else if(strcmp(line
, "%DEPENDS%") == 0) {
591 READ_AND_SPLITDEP(pkg
->depends
);
592 } else if(strcmp(line
, "%OPTDEPENDS%") == 0) {
593 READ_AND_SPLITDEP(pkg
->optdepends
);
594 } else if(strcmp(line
, "%MAKEDEPENDS%") == 0) {
595 /* currently unused */
598 if(strlen(line
) == 0) break;
600 } else if(strcmp(line
, "%CHECKDEPENDS%") == 0) {
601 /* currently unused */
604 if(strlen(line
) == 0) break;
606 } else if(strcmp(line
, "%CONFLICTS%") == 0) {
607 READ_AND_SPLITDEP(pkg
->conflicts
);
608 } else if(strcmp(line
, "%PROVIDES%") == 0) {
609 READ_AND_SPLITDEP(pkg
->provides
);
610 } else if(strcmp(line
, "%DELTAS%") == 0) {
611 /* Different than the rest because of the _alpm_delta_parse call. */
614 if(strlen(line
) == 0) break;
615 pkg
->deltas
= alpm_list_add(pkg
->deltas
,
616 _alpm_delta_parse(db
->handle
, line
));
620 if(ret
!= ARCHIVE_EOF
) {
624 } else if(strcmp(filename
, "files") == 0) {
625 /* currently do nothing with this file */
627 /* unknown database file */
628 _alpm_log(db
->handle
, ALPM_LOG_DEBUG
, "unknown database file: %s\n", filename
);
634 _alpm_log(db
->handle
, ALPM_LOG_DEBUG
, "error parsing database file: %s\n", filename
);
638 struct db_operations sync_db_ops
= {
639 .validate
= sync_db_validate
,
640 .populate
= sync_db_populate
,
641 .unregister
= _alpm_db_unregister
,
644 alpm_db_t
*_alpm_db_register_sync(alpm_handle_t
*handle
, const char *treename
,
645 alpm_siglevel_t level
)
649 _alpm_log(handle
, ALPM_LOG_DEBUG
, "registering sync database '%s'\n", treename
);
651 #ifndef HAVE_LIBGPGME
652 if(level
!= 0 && level
!= ALPM_SIG_USE_DEFAULT
) {
653 RET_ERR(handle
, ALPM_ERR_WRONG_ARGS
, NULL
);
657 db
= _alpm_db_new(treename
, 0);
659 RET_ERR(handle
, ALPM_ERR_DB_CREATE
, NULL
);
661 db
->ops
= &sync_db_ops
;
663 db
->siglevel
= level
;
665 sync_db_validate(db
);
667 handle
->dbs_sync
= alpm_list_add(handle
->dbs_sync
, db
);
671 /* vim: set ts=2 sw=2 noet: */