Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
HardenedBSD-pkg libpkg backup.c
/*-
 * Copyright (c) 2011-2020 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2012 Matthew Seaman <matthew@FreeBSD.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer
 *    in this position and unchanged.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <assert.h>
#include <fcntl.h>
#include <libgen.h>
#include <string.h>
#include <errno.h>

#include "pkg.h"
#include "private/event.h"
#include "private/pkg.h"
#include "private/pkgdb.h"

#include <bsd_compat.h>

/* Number of pages to copy per call to sqlite3_backup_step()
   Default page size is 1024 bytes on Unix */
#define NPAGES	4

static int
ps_cb(void *ps, int ncols, char **coltext, __unused char **colnames)
{
	/* We should have exactly one row and one column of output */
	if (ncols != 1)
		return (-1);	/* ABORT! */

	*(off_t *)ps = strtoll(coltext[0], NULL, 10);

	return (0);
}

static int
copy_database(sqlite3 *src, sqlite3 *dst)
{
	sqlite3_backup	*b;
	char		*errmsg;
	off_t		 total;
	off_t		 done;
	off_t		 page_size;
	int		 ret;

	assert(src != NULL);
	assert(dst != NULL);

	ret = sqlite3_exec(dst, "PRAGMA main.locking_mode=EXCLUSIVE;"
			   "BEGIN IMMEDIATE;COMMIT;", NULL, NULL, &errmsg);
	if (ret != SQLITE_OK)
		goto out_error;

	ret = sqlite3_exec(dst, "PRAGMA page_size", ps_cb, &page_size, &errmsg);
	if (ret != SQLITE_OK)
	        goto out_error;

	b = sqlite3_backup_init(dst, "main", src, "main");

	pkg_emit_progress_start(NULL);
	do {
		ret = sqlite3_backup_step(b, NPAGES);
		total = sqlite3_backup_pagecount(b);
		done = total - sqlite3_backup_remaining(b);
		pkg_emit_progress_tick(done, total);

		if (ret != SQLITE_OK && ret != SQLITE_DONE ) {
			if (ret == SQLITE_BUSY) {
				sqlite3_sleep(250);
			} else {
				ERROR_SQLITE(dst, "backup step");
				break;
			}
		}
	} while(done < total);

	ret = sqlite3_backup_finish(b);
	pkg_emit_progress_tick(total, total);

	sqlite3_exec(dst, "PRAGMA main.locking_mode=NORMAL;"
			   "BEGIN IMMEDIATE;COMMIT;", NULL, NULL, &errmsg);

	if (ret == SQLITE_OK)
		return (EPKG_OK);

out_error:
	pkg_emit_error("sqlite error -- %s", errmsg);
	sqlite3_free(errmsg);
	return (EPKG_FATAL);
}

int
pkgdb_dump(struct pkgdb *db, const char *dest)
{
	sqlite3	*backup;
	int	 ret;
	int destdbfd;
	int savedfd;
	char *dup, *dir;

	dup = xstrdup(dest);
	dir = get_dirname(dup);
	destdbfd = open(dir, O_DIRECTORY|O_CLOEXEC);
	if (destdbfd == -1)
		pkg_fatal_errno("Unable to access '%s'", dir);

	savedfd = pkg_get_dbdirfd();
	ctx.pkg_dbdirfd = destdbfd;
	ret = sqlite3_open(dest, &backup);
	free(dup);
	free(dir);

	if (ret != SQLITE_OK) {
		ERROR_SQLITE(backup, "sqlite3_open");
		sqlite3_close(backup);
		return (EPKG_FATAL);
	}

	pkg_emit_backup();
	ret = copy_database(db->sqlite, backup);

	sqlite3_close(backup);
	ctx.pkg_dbdirfd = savedfd;
	close(savedfd);

	return (ret);
}

int
pkgdb_load(struct pkgdb *db, const char *src)
{
	sqlite3	*restore;
	int	 ret;

	if (faccessat(AT_FDCWD, src, R_OK, AT_EACCESS))
		pkg_fatal_errno("Unable to access '%s'", src);

	ret = sqlite3_open(src, &restore);

	if (ret != SQLITE_OK) {
		ERROR_SQLITE(restore, "sqlite3_open");
		sqlite3_close(restore);
		return (EPKG_FATAL);
	}

	pkg_emit_restore();
	ret = copy_database(restore, db->sqlite);

	sqlite3_close(restore);

	return (ret);
}