1 /*-
2  * Copyright (c) 2011-2014 Baptiste Daroussin <bapt@FreeBSD.org>
3  * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4  * Copyright (c) 2011 Will Andrews <will@FreeBSD.org>
5  * Copyright (c) 2011 Philippe Pepiot <phil@philpep.org>
6  * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer
14  *    in this position and unchanged.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #ifdef HAVE_CONFIG_H
32 #include "pkg_config.h"
33 #endif
34 
35 #include <assert.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <stdlib.h>
40 #include <fcntl.h>
41 
42 #include <bsd_compat.h>
43 
44 #include "pkg.h"
45 #include "private/event.h"
46 #include "private/pkg.h"
47 #include "private/pkgdb.h"
48 #include "private/utils.h"
49 
50 #if defined(UF_NOUNLINK)
51 #define NOCHANGESFLAGS	(UF_IMMUTABLE | UF_APPEND | UF_NOUNLINK | SF_IMMUTABLE | SF_APPEND | SF_NOUNLINK)
52 #else
53 #define NOCHANGESFLAGS	(UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
54 #endif
55 
56 int
pkg_delete(struct pkg * pkg,struct pkgdb * db,unsigned flags)57 pkg_delete(struct pkg *pkg, struct pkgdb *db, unsigned flags)
58 {
59 	struct pkg_message	*msg;
60 	xstring		*message = NULL;
61 	int		 ret;
62 	bool		 handle_rc = false;
63 	const unsigned load_flags = PKG_LOAD_RDEPS|PKG_LOAD_FILES|PKG_LOAD_DIRS|
64 					PKG_LOAD_SCRIPTS|PKG_LOAD_ANNOTATIONS|PKG_LOAD_LUA_SCRIPTS;
65 
66 	assert(pkg != NULL);
67 	assert(db != NULL);
68 
69 	if (pkgdb_ensure_loaded(db, pkg, load_flags) != EPKG_OK)
70 		return (EPKG_FATAL);
71 
72 	if ((flags & PKG_DELETE_UPGRADE) == 0) {
73 		pkg_emit_new_action();
74 		pkg_emit_deinstall_begin(pkg);
75 	}
76 
77 	/* If the package is locked */
78 	if (pkg->locked) {
79 		pkg_emit_locked(pkg);
80 		return (EPKG_LOCKED);
81 	}
82 
83 	/*
84 	 * stop the different related services if the users do want that
85 	 * and that the service is running
86 	 */
87 	handle_rc = pkg_object_bool(pkg_config_get("HANDLE_RC_SCRIPTS"));
88 	if (handle_rc)
89 		pkg_start_stop_rc_scripts(pkg, PKG_RC_STOP);
90 
91 	if ((flags & PKG_DELETE_NOSCRIPT) == 0) {
92 		pkg_open_root_fd(pkg);
93 		if (!(flags & PKG_DELETE_UPGRADE)) {
94 			ret = pkg_lua_script_run(pkg, PKG_LUA_PRE_DEINSTALL, false);
95 			if (ret != EPKG_OK && ctx.developer_mode)
96 				return (ret);
97 			ret = pkg_script_run(pkg, PKG_SCRIPT_PRE_DEINSTALL, false);
98 			if (ret != EPKG_OK && ctx.developer_mode)
99 				return (ret);
100 		}
101 	}
102 
103 	if ((ret = pkg_delete_files(pkg, flags & PKG_DELETE_FORCE ? 1 : 0))
104             != EPKG_OK)
105 		return (ret);
106 
107 	if ((flags & (PKG_DELETE_NOSCRIPT | PKG_DELETE_UPGRADE)) == 0) {
108 		pkg_lua_script_run(pkg, PKG_LUA_POST_DEINSTALL, false);
109 		pkg_script_run(pkg, PKG_SCRIPT_POST_DEINSTALL, false);
110 	}
111 
112 	ret = pkg_delete_dirs(db, pkg, NULL);
113 	if (ret != EPKG_OK)
114 		return (ret);
115 
116 	if ((flags & PKG_DELETE_UPGRADE) == 0) {
117 		pkg_emit_deinstall_finished(pkg);
118 		LL_FOREACH(pkg->message, msg) {
119 			if (msg->type == PKG_MESSAGE_REMOVE) {
120 				if (message == NULL) {
121 					message = xstring_new();
122 					pkg_fprintf(message->fp, "Message from "
123 					    "%n-%v:\n", pkg, pkg);
124 				}
125 				fprintf(message->fp, "%s\n", msg->str);
126 			}
127 		}
128 		if (pkg->message != NULL && message != NULL) {
129 			fflush(message->fp);
130 			pkg_emit_message(message->buf);
131 			xstring_free(message);
132 		}
133 
134 	}
135 
136 	return (pkgdb_unregister_pkg(db, pkg->id));
137 }
138 
139 void
pkg_add_dir_to_del(struct pkg * pkg,const char * file,const char * dir)140 pkg_add_dir_to_del(struct pkg *pkg, const char *file, const char *dir)
141 {
142 	char path[MAXPATHLEN];
143 	char *tmp;
144 	size_t i, len, len2;
145 
146 	strlcpy(path, file != NULL ? file : dir, MAXPATHLEN);
147 
148 	if (file != NULL) {
149 		tmp = strrchr(path, '/');
150 		tmp[1] = '\0';
151 	}
152 
153 	len = strlen(path);
154 
155 	/* make sure to finish by a / */
156 	if (path[len - 1] != '/') {
157 		path[len] = '/';
158 		len++;
159 		path[len] = '\0';
160 	}
161 
162 	for (i = 0; i < pkg->dir_to_del_len ; i++) {
163 		len2 = strlen(pkg->dir_to_del[i]);
164 		if (len2 >= len && strncmp(path, pkg->dir_to_del[i], len) == 0)
165 			return;
166 
167 		if (strncmp(path, pkg->dir_to_del[i], len2) == 0) {
168 			pkg_debug(1, "Replacing in deletion %s with %s",
169 			    pkg->dir_to_del[i], path);
170 			free(pkg->dir_to_del[i]);
171 			pkg->dir_to_del[i] = xstrdup(path);
172 			return;
173 		}
174 	}
175 
176 	pkg_debug(1, "Adding to deletion %s", path);
177 
178 	if (pkg->dir_to_del_len + 1 > pkg->dir_to_del_cap) {
179 		pkg->dir_to_del_cap += 64;
180 		pkg->dir_to_del = xrealloc(pkg->dir_to_del,
181 		    pkg->dir_to_del_cap * sizeof(char *));
182 	}
183 
184 	pkg->dir_to_del[pkg->dir_to_del_len++] = xstrdup(path);
185 }
186 
187 static void
rmdir_p(struct pkgdb * db,struct pkg * pkg,char * dir,const char * prefix_r)188 rmdir_p(struct pkgdb *db, struct pkg *pkg, char *dir, const char *prefix_r)
189 {
190 	char *tmp;
191 	int64_t cnt;
192 	char fullpath[MAXPATHLEN];
193 	size_t len;
194 #ifdef HAVE_STRUCT_STAT_ST_FLAGS
195 	struct stat st;
196 #if !defined(HAVE_CHFLAGSAT)
197 	int fd;
198 #endif
199 #endif
200 
201 	len = snprintf(fullpath, sizeof(fullpath), "/%s", dir);
202 	while (fullpath[len -1] == '/') {
203 		fullpath[len - 1] = '\0';
204 		len--;
205 	}
206 	if (pkgdb_is_dir_used(db, pkg, fullpath, &cnt) != EPKG_OK)
207 		return;
208 
209 	pkg_debug(1, "Number of packages owning the directory '%s': %d",
210 	    fullpath, (int)cnt);
211 	/*
212 	 * At this moment the package we are removing have already been removed
213 	 * from the local database so if anything else is owning the directory
214 	 * that is another package meaning only remove the diretory is cnt == 0
215 	 */
216 	if (cnt > 0)
217 		return;
218 
219 	if (strcmp(prefix_r, fullpath + 1) == 0)
220 		return;
221 
222 	pkg_debug(1, "removing directory %s", fullpath);
223 #ifdef HAVE_STRUCT_STAT_ST_FLAGS
224 	if (fstatat(pkg->rootfd, dir, &st, AT_SYMLINK_NOFOLLOW) != -1) {
225 		if (st.st_flags & NOCHANGESFLAGS) {
226 #ifdef HAVE_CHFLAGSAT
227 			/* Disable all flags*/
228 			chflagsat(pkg->rootfd, dir, 0, AT_SYMLINK_NOFOLLOW);
229 #else
230 			fd = openat(pkg->rootfd, dir, O_NOFOLLOW);
231 			if (fd > 0) {
232 				fchflags(fd, 0);
233 				close(fd);
234 			}
235 #endif
236 		}
237 	}
238 #endif
239 
240 	if (unlinkat(pkg->rootfd, dir, AT_REMOVEDIR) == -1) {
241 		if (errno != ENOTEMPTY && errno != EBUSY)
242 			pkg_emit_errno("unlinkat", dir);
243 		/* If the directory was already removed by a bogus script, continue removing parents */
244 		if (errno != ENOENT)
245 			return;
246 	}
247 
248 	/* No recursivity for packages out of the prefix */
249 	if (strncmp(prefix_r, dir, strlen(prefix_r)) != 0)
250 		return;
251 
252 	/* remove the trailing '/' */
253 	tmp = strrchr(dir, '/');
254 	if (tmp == NULL)
255 		return;
256 	if (tmp == dir)
257 		return;
258 
259 	tmp[0] = '\0';
260 	tmp = strrchr(dir, '/');
261 	if (tmp == NULL)
262 		return;
263 
264 	tmp[1] = '\0';
265 
266 	rmdir_p(db, pkg, dir, prefix_r);
267 }
268 
269 static void
pkg_effective_rmdir(struct pkgdb * db,struct pkg * pkg)270 pkg_effective_rmdir(struct pkgdb *db, struct pkg *pkg)
271 {
272 	char prefix_r[MAXPATHLEN];
273 	size_t i;
274 
275 	snprintf(prefix_r, sizeof(prefix_r), "%s", pkg->prefix + 1);
276 	for (i = 0; i < pkg->dir_to_del_len; i++)
277 		rmdir_p(db, pkg, pkg->dir_to_del[i], prefix_r);
278 }
279 
280 void
pkg_delete_file(struct pkg * pkg,struct pkg_file * file,unsigned force)281 pkg_delete_file(struct pkg *pkg, struct pkg_file *file, unsigned force)
282 {
283 	const char *path;
284 	const char *prefix_rel;
285 	size_t len;
286 #ifdef HAVE_STRUCT_STAT_ST_FLAGS
287 	struct stat st;
288 #if !defined(HAVE_CHFLAGSAT)
289 	int fd;
290 #endif
291 #endif
292 	pkg_open_root_fd(pkg);
293 
294 	path = file->path;
295 	path++;
296 
297 	prefix_rel = pkg->prefix;
298 	prefix_rel++;
299 	len = strlen(prefix_rel);
300 	while (len > 0 && prefix_rel[len - 1] == '/')
301 		len--;
302 
303 #ifdef HAVE_STRUCT_STAT_ST_FLAGS
304 	if (fstatat(pkg->rootfd, path, &st, AT_SYMLINK_NOFOLLOW) != -1) {
305 		if (st.st_flags & NOCHANGESFLAGS) {
306 #ifdef HAVE_CHFLAGSAT
307 			chflagsat(pkg->rootfd, path,
308 			    st.st_flags & ~NOCHANGESFLAGS,
309 			    AT_SYMLINK_NOFOLLOW);
310 #else
311 			fd = openat(pkg->rootfd, path, O_NOFOLLOW);
312 			if (fd > 0) {
313 				fchflags(fd, st.st_flags & ~NOCHANGESFLAGS);
314 				close(fd);
315 			}
316 #endif
317 		}
318 	}
319 #endif
320 	pkg_debug(1, "Deleting file: '%s'", path);
321 	if (unlinkat(pkg->rootfd, path, 0) == -1) {
322 		if (force < 2) {
323 			if (errno == ENOENT)
324 				pkg_emit_file_missing(pkg, file);
325 			else
326 				pkg_emit_errno("unlinkat", path);
327 		}
328 		return;
329 	}
330 
331 	/* do not bother about directories not in prefix */
332 	if ((strncmp(prefix_rel, path, len) == 0) && path[len] == '/')
333 		pkg_add_dir_to_del(pkg, path, NULL);
334 }
335 
336 int
pkg_delete_files(struct pkg * pkg,unsigned force)337 pkg_delete_files(struct pkg *pkg, unsigned force)
338 	/* force: 0 ... be careful and vocal about it.
339 	 *        1 ... remove files without bothering about checksums.
340 	 *        2 ... like 1, but remain silent if removal fails.
341 	 */
342 {
343 	struct pkg_file	*file = NULL;
344 
345 	int		nfiles, cur_file = 0;
346 
347 	nfiles = kh_count(pkg->filehash);
348 
349 	if (nfiles == 0)
350 		return (EPKG_OK);
351 
352 	pkg_emit_delete_files_begin(pkg);
353 	pkg_emit_progress_start(NULL);
354 
355 	while (pkg_files(pkg, &file) == EPKG_OK) {
356 		append_touched_file(file->path);
357 		pkg_emit_progress_tick(cur_file++, nfiles);
358 		pkg_delete_file(pkg, file, force);
359 	}
360 
361 	pkg_emit_progress_tick(nfiles, nfiles);
362 	pkg_emit_delete_files_finished(pkg);
363 
364 	return (EPKG_OK);
365 }
366 
367 void
pkg_delete_dir(struct pkg * pkg,struct pkg_dir * dir)368 pkg_delete_dir(struct pkg *pkg, struct pkg_dir *dir)
369 {
370 	const char *path;
371 	const char *prefix_rel;
372 	size_t len;
373 
374 	pkg_open_root_fd(pkg);
375 
376 	path = dir->path;
377 	/* remove the first / */
378 	path++;
379 
380 	prefix_rel = pkg->prefix;
381 	prefix_rel++;
382 	len = strlen(prefix_rel);
383 	while (prefix_rel[len - 1] == '/')
384 		len--;
385 
386 	if ((strncmp(prefix_rel, path, len) == 0) && path[len] == '/') {
387 		pkg_add_dir_to_del(pkg, NULL, path);
388 	} else {
389 		if (pkg->dir_to_del_len + 1 > pkg->dir_to_del_cap) {
390 			pkg->dir_to_del_cap += 64;
391 			pkg->dir_to_del = xrealloc(pkg->dir_to_del,
392 			    pkg->dir_to_del_cap * sizeof(char *));
393 		}
394 		pkg->dir_to_del[pkg->dir_to_del_len++] = xstrdup(path);
395 	}
396 }
397 
398 int
pkg_delete_dirs(__unused struct pkgdb * db,struct pkg * pkg,struct pkg * new)399 pkg_delete_dirs(__unused struct pkgdb *db, struct pkg *pkg, struct pkg *new)
400 {
401 	struct pkg_dir	*dir = NULL;
402 
403 	while (pkg_dirs(pkg, &dir) == EPKG_OK) {
404 		if (new != NULL && !pkg_has_dir(new, dir->path))
405 			continue;
406 		pkg_delete_dir(pkg, dir);
407 	}
408 
409 	pkg_effective_rmdir(db, pkg);
410 
411 	return (EPKG_OK);
412 }
413