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
34 static int
register_backup(struct pkgdb * db,int fd,const char * path)35 register_backup(struct pkgdb *db, int fd, const char *path)
36 {
37 struct pkgdb_it *it;
38 struct pkg *pkg = NULL;
39 time_t t;
40 char buf[BUFSIZ];
41 char *sum;
42 khint_t k;
43 struct pkg_file *f;
44 char *lpath;
45 struct stat st;
46
47 sum = pkg_checksum_generate_fileat(fd, RELATIVE_PATH(path), PKG_HASH_TYPE_SHA256_HEX);
48
49 it = pkgdb_query(db, "compat-libraries", MATCH_EXACT);
50 if (it != NULL) {
51 pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC|PKG_LOAD_FILES);
52 pkgdb_it_free(it);
53 }
54 if (pkg == NULL) {
55 if (pkg_new(&pkg, PKG_FILE) != EPKG_OK) {
56 return (EPKG_FATAL);
57 }
58 pkg->name = xstrdup("compat-libraries");
59 pkg->origin = xstrdup("compat/libraries");
60 pkg->comment = xstrdup("Compatibility libraries saved during local packages upgrade\n");
61 pkg->desc = xstrdup("Compatibility libraries saved during local packages upgrade\n");
62 pkg->maintainer = xstrdup("root@localhost");
63 pkg->www = xstrdup("N/A");
64 pkg->prefix = xstrdup("/");
65 pkg->abi = "*";
66 }
67 free(pkg->version);
68 t = time(NULL);
69 strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", localtime(&t));
70 if (pkg->filehash != NULL && (k = kh_get_pkg_files(pkg->filehash, path)) != kh_end(pkg->filehash)) {
71 f = kh_val(pkg->filehash, k);
72 kh_del_pkg_files(pkg->filehash, k);
73 DL_DELETE(pkg->files, f);
74 pkg_file_free(f);
75 }
76 xasprintf(&lpath, "%s/%s", ctx.backup_library_path, path);
77 pkg_addfile(pkg, lpath, sum, false);
78 free(lpath);
79 pkg->version = xstrdup(buf);
80 pkg_analyse_files(NULL, pkg, ctx.pkg_rootdir);
81 pkg_open_root_fd(pkg);
82 f = NULL;
83 while (pkg_files(pkg, &f) == EPKG_OK) {
84 if (fstatat(pkg->rootfd, RELATIVE_PATH(f->path), &st, AT_SYMLINK_NOFOLLOW) != -1)
85 pkg->flatsize += st.st_size;
86 }
87 pkgdb_register_finale(db, pkgdb_register_pkg(db, pkg, 0, "backuplib"), "backuplib");
88 return (EPKG_OK);
89 }
90
91 void
backup_library(struct pkgdb * db,struct pkg * p,const char * path)92 backup_library(struct pkgdb *db, struct pkg *p, const char *path)
93 {
94 const char *libname = strrchr(path, '/');
95 char buf[BUFSIZ];
96 char *outbuf;
97 int from, to, backupdir;
98 ssize_t nread, nwritten;
99
100 pkg_open_root_fd(p);
101 to = -1;
102
103 if (libname == NULL)
104 return;
105 /* skip the initial / */
106 libname++;
107
108 from = openat(p->rootfd, RELATIVE_PATH(path), O_RDONLY);
109 if (from == -1) {
110 pkg_debug(2, "unable to backup %s:%s", path, strerror(errno));
111 return;
112 }
113
114 if (mkdirat(p->rootfd, RELATIVE_PATH(ctx.backup_library_path), 0755) == -1) {
115 if (!mkdirat_p(p->rootfd, RELATIVE_PATH(ctx.backup_library_path))) {
116 pkg_emit_errno("Impossible to create the library backup "
117 "directory", ctx.backup_library_path);
118 close(from);
119 return;
120 }
121 }
122 backupdir = openat(p->rootfd, RELATIVE_PATH(ctx.backup_library_path),
123 O_DIRECTORY);
124 if (backupdir == -1) {
125 pkg_emit_error("Impossible to open the library backup "
126 "directory %s", ctx.backup_library_path);
127 goto out;
128 }
129
130 /*
131 * always overwrite the existing backup library, it might be older than
132 * this one
133 */
134 /* first always unlink to ensure we are not truncating a used library */
135 unlinkat(backupdir, libname, 0);
136 to = openat(backupdir, libname, O_EXCL|O_CREAT|O_WRONLY, 0644);
137 if (to == -1) {
138 pkg_emit_errno("Impossible to create the backup library", libname);
139 goto out;
140 }
141
142 while (nread = read(from, buf, sizeof(buf)), nread > 0) {
143 outbuf = buf;
144 do {
145 nwritten = write(to, outbuf, nread);
146 if (nwritten >= 0) {
147 nread -= nwritten;
148 outbuf += nwritten;
149 } else if (errno != EINTR) {
150 goto out;
151 }
152 } while (nread > 0);
153 }
154
155 if (nread == 0) {
156 if (close(to) < 0) {
157 to = -1;
158 goto out;
159 }
160 close(from);
161 register_backup(db, backupdir, libname);
162 close(backupdir);
163 return;
164 }
165
166
167 out:
168 pkg_emit_errno("Fail to backup the library", libname);
169 if (backupdir >= 0)
170 close(backupdir);
171 if (from >= 0)
172 close(from);
173 if (to >= 0)
174 close(to);
175
176 return;
177 }
178