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