[pacman-dev] [PATCH 2/2] Backup old database before updating and restore on failure
Andrew Gregory
andrew.gregory.8 at gmail.com
Thu Dec 19 03:23:38 UTC 2019
On 12/17/19 at 01:05am, Allan McRae wrote:
> If alpm_db_update() fails due to an invalid signature, then the system
> is left with an unusable repo database. Instead, backup the currently
> working database before performing the update, and restore on error.
>
> Note that the calls rename and unlink are not checked for errors. If these
> fail, there is nothing to be done anyway. It also allows for less complex
> flow, as these function fail gracefully when passed NULL arguments.
>
> Signed-off-by: Allan McRae <allan at archlinux.org>
> ---
I really don't like the backup/restore method and would much prefer to
download to a new file and rotate it in once verified, but this is at
least a step in the right direction.
> That is a lot of teduim for adding ".bak" to the end of a string...
Sounds like a great excuse to start using asprintf.
>
> I'd appreciate more eyes on these changes.
>
> lib/libalpm/be_sync.c | 58 ++++++++++++++++++++++++++++++++-----------
> 1 file changed, 44 insertions(+), 14 deletions(-)
>
> diff --git a/lib/libalpm/be_sync.c b/lib/libalpm/be_sync.c
> index 5502d92d..ae46c120 100644
> --- a/lib/libalpm/be_sync.c
> +++ b/lib/libalpm/be_sync.c
> @@ -136,6 +136,7 @@ valid:
> return 0;
> }
>
> +
Extra whitespace change.
> /** Update a package database
> *
> * An update of the package database \a db will be attempted. Unless
> @@ -173,14 +174,15 @@ valid:
> */
> int SYMEXPORT alpm_db_update(int force, alpm_db_t *db)
> {
> - char *syncpath;
> - const char *dbext;
> + char *syncpath, *dbsig = NULL, *dbfilebak = NULL, *dbsigbak = NULL;
> + const char *dbext, *dbfile;
> alpm_list_t *i;
> int updated = 0;
> int ret = -1;
> mode_t oldmask;
> alpm_handle_t *handle;
> int siglevel;
> + size_t len;
>
> /* Sanity checks */
> ASSERT(db != NULL, return -1);
> @@ -217,10 +219,39 @@ int SYMEXPORT alpm_db_update(int force, alpm_db_t *db)
>
> dbext = handle->dbext;
>
> + /* create backup of current database version */
> + dbfile = _alpm_db_path(db);
> + len = strlen(dbfile) + 5;
> + MALLOC(dbfilebak, len,
> + {
> + handle->pm_errno = ALPM_ERR_MEMORY;
> + ret = -1;
> + goto cleanup;
> + }
> + );
> + snprintf(dbfilebak, len, "%s.bak", dbfile);
> +
> +
> + dbsig = _alpm_sigpath(db->handle, dbfile);
> + len = strlen(dbsig) + 5;
> + MALLOC(dbsigbak, len,
> + {
> + handle->pm_errno = ALPM_ERR_MEMORY;
> + ret = -1;
> + goto cleanup;
> + }
> + );
> + snprintf(dbsigbak, len, "%s.bak", dbsig);
> +
> + /* remove any old backup files to prevent potential sig mismatch */
> + unlink(dbfilebak);
> + unlink(dbsigbak);
> + _alpm_copyfile(dbfile, dbfilebak);
> + _alpm_copyfile(dbsig, dbsigbak);
_alpm_copyfile doesn't currently use either O_EXCL or O_TRUNC when it
opens the destination, so if the unlink fails, this could result in
thoroughly broken backup files.
> for(i = db->servers; i; i = i->next) {
> const char *server = i->data, *final_db_url = NULL;
> struct dload_payload payload;
> - size_t len;
> int sig_ret = 0;
>
> memset(&payload, 0, sizeof(struct dload_payload));
> @@ -247,17 +278,6 @@ int SYMEXPORT alpm_db_update(int force, alpm_db_t *db)
> updated = (updated || ret == 0);
>
> if(ret != -1 && updated && (siglevel & ALPM_SIG_DATABASE)) {
> - /* an existing sig file is no good at this point */
> - char *sigpath = _alpm_sigpath(handle, _alpm_db_path(db));
> - if(!sigpath) {
> - /* pm_errno should be set */
> - ret = -1;
> - goto cleanup;
> - }
> - unlink(sigpath);
> - free(sigpath);
> -
> -
> /* check if the final URL from internal downloader looks reasonable */
> if(final_db_url != NULL) {
> if(strlen(final_db_url) < 3
> @@ -327,13 +347,23 @@ int SYMEXPORT alpm_db_update(int force, alpm_db_t *db)
>
> cleanup:
> if(ret == -1) {
> + rename(dbfilebak, dbfile);
> + rename(dbsigbak, dbsig);
This obliterates the invalid file, making troubleshooting impossible.
We really need to be sure that either this restoration succeeds or the
validity and cache were reset.
> +
> /* pm_errno was set by the download code */
> _alpm_log(handle, ALPM_LOG_DEBUG, "failed to sync db: %s\n",
> alpm_strerror(handle->pm_errno));
> } else {
> + unlink(dbfilebak);
> + unlink(dbsigbak);
> +
> handle->pm_errno = ALPM_ERR_OK;
> }
>
> + free(dbfilebak);
> + free(dbsig);
> + free(dbsigbak);
> +
> _alpm_handle_unlock(handle);
> free(syncpath);
> umask(oldmask);
> --
> 2.24.1
More information about the pacman-dev
mailing list