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