/ libpkg / backup_lib.c
backup_lib.c
  1  /*-
  2   * Copyright (c) 2020 Baptiste Daroussin <bapt@FreeBSD.org>
  3   *
  4   * Redistribution and use in source and binary forms, with or without
  5   * modification, are permitted provided that the following conditions
  6   * are met:
  7   * 1. Redistributions of source code must retain the above copyright
  8   *    notice, this list of conditions and the following disclaimer
  9   *    in this position and unchanged.
 10   * 2. Redistributions in binary form must reproduce the above copyright
 11   *    notice, this list of conditions and the following disclaimer in the
 12   *    documentation and/or other materials provided with the distribution.
 13   *
 14   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 15   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 16   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 17   * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 18   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 19   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 20   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 21   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 23   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 24   */
 25  
 26  #include <errno.h>
 27  #include <fcntl.h>
 28  #include <time.h>
 29  
 30  #include "pkg.h"
 31  #include "private/event.h"
 32  #include "private/pkg.h"
 33  #include "private/pkgdb.h"
 34  
 35  static int
 36  register_backup(struct pkgdb *db, struct pkg *orig, int fd, const char *libname)
 37  {
 38  	struct pkgdb_it *it;
 39  	struct pkg *pkg = NULL;
 40  	time_t t;
 41  	char buf[BUFSIZ];
 42  	char *lpath, *name, *sum;
 43  	struct pkg_file *f;
 44  	struct stat st;
 45  	pkghash_entry *e;
 46  	int retcode;
 47  
 48  	sum = pkg_checksum_generate_fileat(fd, RELATIVE_PATH(libname),
 49  	    PKG_HASH_TYPE_SHA256_HEX);
 50  
 51  	(void)xasprintf(&name, "%s-backup-%s", orig->name, libname);
 52  
 53  	it = pkgdb_query(db, name, MATCH_EXACT);
 54  	if (it != NULL) {
 55  		pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC|PKG_LOAD_FILES);
 56  		pkgdb_it_free(it);
 57  	}
 58  	if (pkg == NULL) {
 59  		char *origin;
 60  
 61  		if (pkg_new(&pkg, PKG_FILE) != EPKG_OK) {
 62  			return (EPKG_FATAL);
 63  		}
 64  		pkg->name = name;
 65  		(void)xasprintf(&origin, "%s-backup-%s", orig->origin, libname);
 66  		pkg->origin = origin;
 67  		pkg->comment = xstrdup(
 68  		    "Compatibility libraries saved during package upgrade");
 69  		pkg->desc = xstrdup(
 70  		    "Compatibility libraries saved during package upgrade\n");
 71  		pkg->maintainer = xstrdup("root@localhost");
 72  		pkg->www = xstrdup("N/A");
 73  		pkg->prefix = xstrdup("/");
 74  		pkg->abi = xstrdup(orig->abi);
 75  
 76  		if (pkgdb_ensure_loaded(db, orig, PKG_LOAD_SHLIBS_PROVIDED_IGNORE |
 77  		    PKG_LOAD_SHLIBS_REQUIRED_IGNORE) != EPKG_OK) {
 78  			return (EPKG_FATAL);
 79  		}
 80  		vec_foreach(orig->shlibs_provided_ignore, i) {
 81  			pkg_addshlib_provided_ignore(pkg, orig->shlibs_provided_ignore.d[i]);
 82  		}
 83  		vec_foreach(orig->shlibs_required_ignore, i) {
 84  			pkg_addshlib_required_ignore(pkg, orig->shlibs_required_ignore.d[i]);
 85  		}
 86  	} else {
 87  		free(name);
 88  		name = NULL;
 89  	}
 90  	if ((e = pkghash_get(pkg->filehash, libname)) != NULL) {
 91  		DL_DELETE(pkg->files, (struct pkg_file *)e->value);
 92  		pkg_file_free(e->value);
 93  		pkghash_del(pkg->filehash, libname);
 94  	}
 95  	xasprintf(&lpath, "%s/%s", ctx.backup_library_path, libname);
 96  	pkg_addfile(pkg, lpath, sum, false);
 97  	free(lpath);
 98  
 99  	t = time(NULL);
100  	strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", localtime(&t));
101  	free(pkg->version);
102  	pkg->version = xstrdup(buf);
103  
104  	pkg_analyse_files(NULL, pkg, ctx.pkg_rootdir);
105  	pkg_open_root_fd(pkg);
106  	f = NULL;
107  	while (pkg_files(pkg, &f) == EPKG_OK) {
108  		if (fstatat(pkg->rootfd, RELATIVE_PATH(f->path), &st,
109  		    AT_SYMLINK_NOFOLLOW) != -1)
110  			pkg->flatsize += st.st_size;
111  	}
112  	retcode = pkgdb_register_pkg(db, pkg, 1, "backuplib");
113  	if (retcode == EPKG_OK)
114  		retcode = pkgdb_register_finale(db, EPKG_OK, "backuplib");
115  	return (retcode);
116  }
117  
118  static void
119  backup_library(struct pkgdb *db, struct pkg *p, const char *path)
120  {
121  	const char *libname;
122  	int from, to, backupdir;
123  
124  	if ((libname = strrchr(path, '/')) == NULL)
125  		return;
126  	/* skip the initial / */
127  	libname++;
128  
129  	pkg_open_root_fd(p);
130  	to = -1;
131  
132  	from = openat(p->rootfd, RELATIVE_PATH(path), O_RDONLY);
133  	if (from == -1) {
134  		pkg_debug(2, "unable to backup %s:%s", path, strerror(errno));
135  		return;
136  	}
137  
138  	if (mkdirat(p->rootfd, RELATIVE_PATH(ctx.backup_library_path), 0755) == -1) {
139  		if (!mkdirat_p(p->rootfd, RELATIVE_PATH(ctx.backup_library_path))) {
140  			pkg_emit_errno("Unable to create the library backup "
141  			    "directory", ctx.backup_library_path);
142  			close(from);
143  			return;
144  		}
145  	}
146  	backupdir = openat(p->rootfd, RELATIVE_PATH(ctx.backup_library_path),
147  	    O_DIRECTORY);
148  	if (backupdir == -1) {
149  		pkg_emit_error("Unable to open the library backup "
150  		    "directory %s", ctx.backup_library_path);
151  		goto out;
152  	}
153  
154  	/*
155  	 * always overwrite the existing backup library, it might be older than
156  	 * this one
157  	 */
158  	/* first always unlink to ensure we are not truncating a used library */
159  	unlinkat(backupdir, libname, 0);
160  	to = openat(backupdir, libname, O_EXCL|O_CREAT|O_WRONLY, 0644);
161  	if (to == -1) {
162  		pkg_emit_errno("Unable to create the backup library", libname);
163  		goto out;
164  	}
165  
166  	if (pkg_copy_file(from, to)) {
167  		if (close(to) < 0) {
168  			to = -1;
169  			goto out;
170  		}
171  		close(from);
172  		register_backup(db, p, backupdir, libname);
173  		close(backupdir);
174  		return;
175  	}
176  
177  out:
178  	pkg_emit_errno("Failed to backup the library", libname);
179  	if (backupdir >= 0)
180  		close(backupdir);
181  	if (from >= 0)
182  		close(from);
183  	if (to >= 0)
184  		close(to);
185  }
186  
187  /*
188   * We're about to remove an installed file as part of an upgrade.  See if it's a
189   * library and whether the user asked us to back up libraries, and if so, back
190   * it up.
191   */
192  void
193  pkg_maybe_backup_library(struct pkgdb *db, struct pkg *pkg, const char *path)
194  {
195  	const char *libname;
196  
197  	if (!ctx.backup_libraries)
198  		return;
199  
200  	libname = strrchr(path, '/');
201  	if (libname != NULL &&
202  	    charv_search(&pkg->shlibs_provided, libname + 1) != NULL)
203  		backup_library(db, pkg, path);
204  }