Line data Source code
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
57 6 : pkg_delete(struct pkg *pkg, struct pkgdb *db, unsigned flags)
58 : {
59 : int ret;
60 6 : bool handle_rc = false;
61 6 : const unsigned load_flags = PKG_LOAD_RDEPS|PKG_LOAD_FILES|PKG_LOAD_DIRS|
62 : PKG_LOAD_SCRIPTS|PKG_LOAD_ANNOTATIONS;
63 :
64 6 : assert(pkg != NULL);
65 6 : assert(db != NULL);
66 :
67 6 : if (pkgdb_ensure_loaded(db, pkg, load_flags) != EPKG_OK)
68 0 : return (EPKG_FATAL);
69 :
70 6 : if ((flags & PKG_DELETE_UPGRADE) == 0)
71 6 : pkg_emit_deinstall_begin(pkg);
72 :
73 : /* If the package is locked */
74 6 : if (pkg->locked) {
75 0 : pkg_emit_locked(pkg);
76 0 : return (EPKG_LOCKED);
77 : }
78 :
79 : /*
80 : * stop the different related services if the users do want that
81 : * and that the service is running
82 : */
83 6 : handle_rc = pkg_object_bool(pkg_config_get("HANDLE_RC_SCRIPTS"));
84 6 : if (handle_rc)
85 0 : pkg_start_stop_rc_scripts(pkg, PKG_RC_STOP);
86 :
87 6 : if ((flags & PKG_DELETE_NOSCRIPT) == 0) {
88 6 : if (flags & PKG_DELETE_UPGRADE) {
89 0 : ret = pkg_script_run(pkg, PKG_SCRIPT_PRE_UPGRADE);
90 0 : if (ret != EPKG_OK)
91 0 : return (ret);
92 : } else {
93 6 : ret = pkg_script_run(pkg, PKG_SCRIPT_PRE_DEINSTALL);
94 6 : if (ret != EPKG_OK)
95 0 : return (ret);
96 : }
97 : }
98 :
99 6 : if ((ret = pkg_delete_files(pkg, flags & PKG_DELETE_FORCE ? 1 : 0))
100 : != EPKG_OK)
101 0 : return (ret);
102 :
103 6 : if ((flags & (PKG_DELETE_NOSCRIPT | PKG_DELETE_UPGRADE)) == 0) {
104 6 : ret = pkg_script_run(pkg, PKG_SCRIPT_POST_DEINSTALL);
105 6 : if (ret != EPKG_OK)
106 0 : return (ret);
107 : }
108 :
109 6 : ret = pkg_delete_dirs(db, pkg, NULL);
110 6 : if (ret != EPKG_OK)
111 0 : return (ret);
112 :
113 6 : if ((flags & PKG_DELETE_UPGRADE) == 0)
114 6 : pkg_emit_deinstall_finished(pkg);
115 :
116 6 : return (pkgdb_unregister_pkg(db, pkg->id));
117 : }
118 :
119 : void
120 10 : pkg_add_dir_to_del(struct pkg *pkg, const char *file, const char *dir)
121 : {
122 : char path[MAXPATHLEN];
123 : char *tmp;
124 : size_t i, len, len2;
125 :
126 10 : strlcpy(path, file != NULL ? file : dir, MAXPATHLEN);
127 :
128 10 : if (file != NULL) {
129 7 : tmp = strrchr(path, '/');
130 7 : tmp[1] = '\0';
131 : }
132 :
133 10 : len = strlen(path);
134 :
135 : /* make sure to finish by a / */
136 10 : if (path[len - 1] != '/') {
137 3 : path[len] = '/';
138 3 : len++;
139 3 : path[len] = '\0';
140 : }
141 :
142 11 : for (i = 0; i < pkg->dir_to_del_len ; i++) {
143 5 : len2 = strlen(pkg->dir_to_del[i]);
144 5 : if (len2 >= len && strncmp(path, pkg->dir_to_del[i], len) == 0)
145 8 : return;
146 :
147 1 : if (strncmp(path, pkg->dir_to_del[i], len2) == 0) {
148 0 : pkg_debug(1, "Replacing in deletion %s with %s",
149 0 : pkg->dir_to_del[i], path);
150 0 : free(pkg->dir_to_del[i]);
151 0 : pkg->dir_to_del[i] = strdup(path);
152 0 : return;
153 : }
154 : }
155 :
156 6 : pkg_debug(1, "Adding to deletion %s", path);
157 :
158 6 : if (pkg->dir_to_del_len + 1 > pkg->dir_to_del_cap) {
159 5 : pkg->dir_to_del_cap += 64;
160 5 : pkg->dir_to_del = realloc(pkg->dir_to_del,
161 5 : pkg->dir_to_del_cap * sizeof(char *));
162 : }
163 :
164 6 : pkg->dir_to_del[pkg->dir_to_del_len++] = strdup(path);
165 : }
166 :
167 : static void
168 7 : rmdir_p(struct pkgdb *db, struct pkg *pkg, char *dir, const char *prefix_r)
169 : {
170 : char *tmp;
171 : int64_t cnt;
172 : char fullpath[MAXPATHLEN];
173 : size_t len;
174 : struct stat st;
175 : #if defined(HAVE_CHFLAGS) && !defined(HAVE_CHFLAGSAT)
176 : int fd;
177 : #endif
178 :
179 7 : len = snprintf(fullpath, sizeof(fullpath), "/%s", dir);
180 21 : while (fullpath[len -1] == '/') {
181 7 : fullpath[len - 1] = '\0';
182 7 : len--;
183 : }
184 7 : if (pkgdb_is_dir_used(db, pkg, fullpath, &cnt) != EPKG_OK)
185 4 : return;
186 :
187 7 : pkg_debug(1, "Number of packages owning the directory '%s': %d",
188 : fullpath, cnt);
189 : /*
190 : * At this moment the package we are removing have already been removed
191 : * from the local database so if anything else is owning the directory
192 : * that is another package meaning only remove the diretory is cnt == 0
193 : */
194 7 : if (cnt > 0)
195 1 : return;
196 :
197 6 : if (strcmp(prefix_r, fullpath + 1) == 0)
198 1 : return;
199 :
200 5 : pkg_debug(1, "removing directory %s", fullpath);
201 : #ifdef HAVE_CHFLAGS
202 5 : if (fstatat(pkg->rootfd, dir, &st, AT_SYMLINK_NOFOLLOW) != -1) {
203 5 : if (st.st_flags & NOCHANGESFLAGS) {
204 : #ifdef HAVE_CHFLAGSAT
205 : /* Disable all flags*/
206 0 : chflagsat(pkg->rootfd, dir, 0, AT_SYMLINK_NOFOLLOW);
207 : #else
208 : fd = openat(pkg->rootfd, dir, O_NOFOLLOW);
209 : if (fd > 0) {
210 : fchflags(fd, 0);
211 : close(fd);
212 : }
213 : #endif
214 : }
215 : }
216 : #endif
217 :
218 5 : if (unlinkat(pkg->rootfd, dir, AT_REMOVEDIR) == -1) {
219 2 : if (errno != ENOTEMPTY && errno != EBUSY)
220 0 : pkg_emit_errno("unlinkat", dir);
221 : /* If the directory was already removed by a bogus script, continue removing parents */
222 2 : if (errno != ENOENT)
223 2 : return;
224 : }
225 :
226 : /* No recursivity for packages out of the prefix */
227 3 : if (strncmp(prefix_r, dir, strlen(prefix_r)) != 0)
228 0 : return;
229 :
230 : /* remove the trailing '/' */
231 3 : tmp = strrchr(dir, '/');
232 3 : if (tmp == dir)
233 0 : return;
234 :
235 3 : tmp[0] = '\0';
236 3 : tmp = strrchr(dir, '/');
237 3 : if (tmp == NULL)
238 0 : return;
239 :
240 3 : tmp[1] = '\0';
241 :
242 3 : rmdir_p(db, pkg, dir, prefix_r);
243 : }
244 :
245 : static void
246 6 : pkg_effective_rmdir(struct pkgdb *db, struct pkg *pkg)
247 : {
248 : char prefix_r[MAXPATHLEN];
249 : size_t i;
250 :
251 6 : snprintf(prefix_r, sizeof(prefix_r), "%s", pkg->prefix + 1);
252 10 : for (i = 0; i < pkg->dir_to_del_len; i++)
253 4 : rmdir_p(db, pkg, pkg->dir_to_del[i], prefix_r);
254 6 : }
255 :
256 : void
257 9 : pkg_delete_file(struct pkg *pkg, struct pkg_file *file, unsigned force)
258 : {
259 : const char *path;
260 : const char *prefix_rel;
261 : struct stat st;
262 : size_t len;
263 : #if defined(HAVE_CHFLAGS) && !defined(HAVE_CHFLAGSAT)
264 : int fd;
265 : #endif
266 :
267 9 : pkg_open_root_fd(pkg);
268 :
269 9 : path = file->path;
270 9 : path++;
271 :
272 9 : prefix_rel = pkg->prefix;
273 9 : prefix_rel++;
274 9 : len = strlen(prefix_rel);
275 23 : while (prefix_rel[len - 1] == '/')
276 5 : len--;
277 :
278 : /* Regular files and links */
279 : /* check checksum */
280 9 : if (!force && file->sum != NULL) {
281 0 : if (!pkg_checksum_validate_fileat(pkg->rootfd, path, file->sum)) {
282 0 : pkg_emit_error("%s%s%s different from original "
283 0 : "checksum, not removing", pkg->rootpath,
284 0 : pkg->rootpath[strlen(pkg->rootpath) - 1] == '/' ? "" : "/",
285 : path);
286 0 : return;
287 : }
288 : }
289 :
290 : #ifdef HAVE_CHFLAGS
291 9 : if (fstatat(pkg->rootfd, path, &st, AT_SYMLINK_NOFOLLOW) != -1) {
292 9 : if (st.st_flags & NOCHANGESFLAGS) {
293 : #ifdef HAVE_CHFLAGSAT
294 0 : chflagsat(pkg->rootfd, path,
295 0 : st.st_flags & ~NOCHANGESFLAGS,
296 : AT_SYMLINK_NOFOLLOW);
297 : #else
298 : fd = openat(pkg->rootfd, path, O_NOFOLLOW);
299 : if (fd > 0) {
300 : fchflags(fd, st.st_flags & ~NOCHANGESFLAGS);
301 : close(fd);
302 : }
303 : #endif
304 : }
305 : }
306 : #endif
307 9 : pkg_debug(1, "Deleting file: '%s'", path);
308 9 : if (unlinkat(pkg->rootfd, path, 0) == -1) {
309 0 : if (force < 2)
310 0 : pkg_emit_errno("unlinkat", path);
311 0 : return;
312 : }
313 :
314 : /* do not bother about directories not in prefix */
315 9 : if ((strncmp(prefix_rel, path, len) == 0) && path[len] == '/')
316 6 : pkg_add_dir_to_del(pkg, path, NULL);
317 : }
318 :
319 : int
320 6 : pkg_delete_files(struct pkg *pkg, unsigned force)
321 : /* force: 0 ... be careful and vocal about it.
322 : * 1 ... remove files without bothering about checksums.
323 : * 2 ... like 1, but remain silent if removal fails.
324 : */
325 : {
326 6 : struct pkg_file *file = NULL;
327 :
328 6 : int nfiles, cur_file = 0;
329 :
330 6 : nfiles = kh_count(pkg->files);
331 :
332 6 : if (nfiles == 0)
333 1 : return (EPKG_OK);
334 :
335 5 : pkg_emit_delete_files_begin(pkg);
336 5 : pkg_emit_progress_start(NULL);
337 :
338 19 : while (pkg_files(pkg, &file) == EPKG_OK) {
339 9 : pkg_emit_progress_tick(cur_file++, nfiles);
340 9 : pkg_delete_file(pkg, file, force);
341 : }
342 :
343 5 : pkg_emit_progress_tick(nfiles, nfiles);
344 5 : pkg_emit_delete_files_finished(pkg);
345 :
346 5 : return (EPKG_OK);
347 : }
348 :
349 : void
350 1 : pkg_delete_dir(struct pkg *pkg, struct pkg_dir *dir)
351 : {
352 : const char *path;
353 : const char *prefix_rel;
354 : size_t len;
355 :
356 1 : pkg_open_root_fd(pkg);
357 :
358 1 : path = dir->path;
359 : /* remove the first / */
360 1 : path++;
361 :
362 1 : prefix_rel = pkg->prefix;
363 1 : prefix_rel++;
364 1 : len = strlen(prefix_rel);
365 3 : while (prefix_rel[len - 1] == '/')
366 1 : len--;
367 :
368 1 : if ((strncmp(prefix_rel, path, len) == 0) && path[len] == '/') {
369 1 : pkg_add_dir_to_del(pkg, NULL, path);
370 : } else {
371 0 : if (pkg->dir_to_del_len + 1 > pkg->dir_to_del_cap) {
372 0 : pkg->dir_to_del_cap += 64;
373 0 : pkg->dir_to_del = realloc(pkg->dir_to_del,
374 0 : pkg->dir_to_del_cap * sizeof(char *));
375 : }
376 0 : pkg->dir_to_del[pkg->dir_to_del_len++] = strdup(path);
377 : }
378 1 : }
379 :
380 : int
381 6 : pkg_delete_dirs(__unused struct pkgdb *db, struct pkg *pkg, struct pkg *new)
382 : {
383 6 : struct pkg_dir *dir = NULL;
384 :
385 13 : while (pkg_dirs(pkg, &dir) == EPKG_OK) {
386 1 : if (new != NULL && !pkg_has_dir(new, dir->path))
387 0 : continue;
388 1 : pkg_delete_dir(pkg, dir);
389 : }
390 :
391 6 : pkg_effective_rmdir(db, pkg);
392 :
393 6 : return (EPKG_OK);
394 : }
|