Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Implement a mechanism to backup libraries during upgrade
Baptiste Daroussin committed 6 years ago
commit d3bf49ed417ed714f43b13f560be0cb0d8a0c432
parent 599aff7
8 files changed +216 -4
modified .gitignore
@@ -122,6 +122,7 @@ scripts/sbin/pkg2ng
/tests/frontend/vital
/tests/frontend/clean
/tests/frontend/shellscript
+
/tests/frontend/backup_lib
/tests/merge
/tests/pkg_add_dir_to_del
/tests/pkg_printf
modified libpkg/Makefile.autosetup
@@ -1,6 +1,7 @@
include @builddir@/mk/defs.mk
LIB=	pkg
SRCS=	backup.c \
+
	backup_lib.c \
	merge3.c \
	pkg_audit.c \
	pkg_deps.c \
added libpkg/backup_lib.c
@@ -0,0 +1,120 @@
+
/*-
+
 * Copyright (c) 2020 Baptiste Daroussin <bapt@FreeBSD.org>
+
 *
+
 * 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 <errno.h>
+
#include <fcntl.h>
+

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

+
void
+
backup_library(struct pkg *p, const char *path)
+
{
+
	const char *libname = strrchr(path, '/');
+
	char buf[BUFSIZ];
+
	char *outbuf;
+
	int from, to, backupdir;
+
	ssize_t nread, nwritten;
+

+
	pkg_open_root_fd(p);
+

+
	from = to = backupdir = -1;
+

+
	if (libname == NULL)
+
		return;
+
	/* skip the initial / */
+
	libname++;
+

+
	from = openat(p->rootfd, RELATIVE_PATH(path), O_RDONLY);
+
	if (from == -1) {
+
		pkg_debug(2, "unable to backup %s:%s", path, strerror(errno));
+
		return;
+
	}
+

+
	if (mkdirat(p->rootfd, RELATIVE_PATH(ctx.backup_library_path), 0755) == -1) {
+
		if (!mkdirat_p(p->rootfd, RELATIVE_PATH(ctx.backup_library_path))) {
+
			pkg_emit_errno("Impossible to create the library backup "
+
			    "directory", ctx.backup_library_path);
+
			close(from);
+
			return;
+
		}
+
	}
+
	backupdir = openat(p->rootfd, RELATIVE_PATH(ctx.backup_library_path),
+
	    O_DIRECTORY);
+
	if (backupdir == -1) {
+
		pkg_emit_error("Impossible to open the library backup "
+
		    "directory %s", ctx.backup_library_path);
+
		goto out;
+
	}
+

+
	/*
+
	 * always overwrite the existing backup library, it might be older than
+
	 * this one
+
	 */
+
	/* first always unlink to ensure we are not truncating a used library */
+
	unlinkat(backupdir, libname, 0);
+
	to = openat(backupdir, libname, O_EXCL|O_CREAT|O_WRONLY, 0644);
+
	if (to == -1) {
+
		pkg_emit_errno("Impossible to create the backup library", libname);
+
		goto out;
+
	}
+

+
	close(backupdir);
+
	backupdir = -1;
+

+
	while (nread = read(from, buf, sizeof(buf)), nread > 0) {
+
		outbuf = buf;
+
		do {
+
			nwritten = write(to, outbuf, nread);
+
			if (nwritten >= 0) {
+
				nread -= nwritten;
+
				outbuf += nwritten;
+
			} else if (errno != EINTR) {
+
				goto out;
+
			}
+
		} while (nread > 0);
+
	}
+

+
	if (nread == 0) {
+
		if (close(to) < 0) {
+
			to = -1;
+
			goto out;
+
		}
+
		close(from);
+
		return;
+
	}
+

+
out:
+
	pkg_emit_errno("Fail to backup the library", libname);
+
	if (backupdir >= 0)
+
		close(backupdir);
+
	if (from >= 0)
+
		close(from);
+
	if (to >= 0)
+
		close(to);
+

+
	return;
+
}
modified libpkg/pkg_add.c
@@ -1,8 +1,7 @@
/*-
-
 * Copyright (c) 2011-2016 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2011-2020 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2016, Vsevolod Stakhov
-
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
@@ -984,6 +983,14 @@ pkg_add_cleanup_old(struct pkgdb *db, struct pkg *old, struct pkg *new, int flag
		while (pkg_files(old, &f) == EPKG_OK) {
			if (!pkg_has_file(new, f->path)) {
				pkg_debug(2, "File %s is not in the new package", f->path);
+
				if (ctx.backup_libraries) {
+
					const char *libname;
+
					libname = strrchr(f->path, '/');
+
					if (libname != NULL &&
+
					    kh_contains(strings, old->shlibs_provided, libname+1)) {
+
						backup_library(old, f->path);
+
					}
+
				}
				pkg_delete_file(old, f, flags & PKG_DELETE_FORCE ? 1 : 0);
			}
		}
modified libpkg/pkg_config.c
@@ -74,6 +74,7 @@ struct pkg_ctx ctx = {
	.cachedirfd = -1,
	.pkg_dbdirfd = -1,
	.osversion = 0,
+
	.backup_libraries = false,
};

struct config_entry {
@@ -434,6 +435,18 @@ static struct config_entry c[] = {
		"Ignore FreeBSD OS version check",
	},
#endif
+
	{
+
		PKG_BOOL,
+
		"BACKUP_LIBRARIES",
+
		"NO",
+
		"Backup old versions of libraries during an upgrade",
+
	},
+
	{
+
		PKG_STRING,
+
		"BACKUP_LIBRARY_PATH",
+
		PREFIX "/lib/compat/pkg",
+
		"Path where pkg will backup libraries",
+
	},
};

static bool parsed = false;
@@ -1189,6 +1202,8 @@ pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
	ctx.developer_mode = pkg_object_bool(pkg_config_get("DEVELOPER_MODE"));
	ctx.dbdir = pkg_object_string(pkg_config_get("PKG_DBDIR"));
	ctx.cachedir = pkg_object_string(pkg_config_get("PKG_CACHEDIR"));
+
	ctx.backup_libraries = pkg_object_bool(pkg_config_get("BACKUP_LIBRARIES"));
+
	ctx.backup_library_path = pkg_object_string(pkg_config_get("BACKUP_LIBRARY_PATH"));

	it = NULL;
	object = ucl_object_find_key(config, "PKG_ENV");
modified libpkg/private/pkg.h
@@ -1,9 +1,8 @@
/*-
-
 * Copyright (c) 2011-2016 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2011-2020 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2013 Matthew Seaman <matthew@FreeBSD.org>
 * Copyright (c) 2013-2017 Vsevolod Stakhov <vsevolod@FreeBSD.org>
-
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
@@ -248,6 +247,8 @@ struct pkg_ctx {
	int dbdirfd;
	int pkg_dbdirfd;
	int osversion;
+
	bool backup_libraries;
+
	const char *backup_library_path;
};

extern struct pkg_ctx ctx;
@@ -864,5 +865,6 @@ int pkg_add_fromdir(struct pkg *, const char *);
struct pkg_dep* pkg_adddep_chain(struct pkg_dep *chain,
		struct pkg *pkg, const char *name, const char *origin, const
		char *version, bool locked);
+
void backup_library(struct pkg *pkg, const char *name);

#endif
modified tests/Makefile.autosetup
@@ -52,6 +52,7 @@ TESTS_SH= \
	frontend/lua.sh \
	frontend/shellscript.sh \
	frontend/clean.sh \
+
	frontend/backup_lib.sh

merge_OBJS=	lib/merge.o
plist_OBJS=	lib/plist.o
added tests/frontend/backup_lib.sh
@@ -0,0 +1,65 @@
+
#! /usr/bin/env atf-sh
+

+
. $(atf_get_srcdir)/test_environment.sh
+

+
tests_init \
+
	basic
+

+
basic_body() {
+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "test" "test" "1"
+
	cat << EOF >> test.ucl
+
files: {
+
	${TMPDIR}/libempty.so.1: "",
+
}
+

+
EOF
+

+
	mkdir ${TMPDIR}/target
+
	touch empty.c
+
	cc -shared -Wl,-soname=libempty.so.1 empty.c -o libempty.so.1
+
	sum=$(openssl dgst -sha256 -hex libempty.so.1)
+

+
	atf_check \
+
		-o empty \
+
		-e empty \
+
		-s exit:0 \
+
		pkg create -M test.ucl
+

+
	atf_check \
+
		-e empty \
+
		-o empty \
+
		-s exit:0 \
+
		pkg -o BACKUP_LIBRARIES=true -o REPOS_DIR=/dev/null -r ${TMPDIR}/target install -qfy ${TMPDIR}/test-1.txz
+

+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "test" "test" "2"
+

+
	atf_check \
+
		-o empty \
+
		-e empty \
+
		-s exit:0 \
+
		pkg create -M test.ucl
+

+
	rm test-1.txz
+
	atf_check \
+
		-o ignore \
+
		-s exit:0 pkg repo .
+

+
	mkdir reposconf
+
	cat <<EOF >> reposconf/repo.conf
+
local: {
+
	url: file:///${TMPDIR},
+
	enabled: true
+
}
+
EOF
+

+
	atf_check \
+
		-e empty \
+
		-o ignore \
+
		-s exit:0 \
+
		pkg -o BACKUP_LIBRARY_PATH=/back/ -o BACKUP_LIBRARIES=true -o REPOS_DIR=${TMPDIR}/reposconf -r ${TMPDIR}/target upgrade -y
+
	atf_check \
+
		-o ignore \
+
		-e empty \
+
		-s exit:0 \
+
		ls target/back/libempty.so.1
+
}