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 }