Branch data 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 : 172 : pkg_delete(struct pkg *pkg, struct pkgdb *db, unsigned flags)
58 : : {
59 : : struct pkg_message *msg;
60 : 172 : xstring *message = NULL;
61 : : int ret;
62 : 172 : bool handle_rc = false;
63 : 172 : 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 [ + - ]: 172 : assert(pkg != NULL);
67 [ - + ]: 172 : assert(db != NULL);
68 : :
69 [ - + ]: 172 : if (pkgdb_ensure_loaded(db, pkg, load_flags) != EPKG_OK)
70 : 0 : return (EPKG_FATAL);
71 : :
72 [ - + ]: 172 : if ((flags & PKG_DELETE_UPGRADE) == 0) {
73 : 172 : pkg_emit_new_action();
74 : 172 : pkg_emit_deinstall_begin(pkg);
75 : 172 : }
76 : :
77 : : /* If the package is locked */
78 [ - + ]: 172 : if (pkg->locked) {
79 : 0 : pkg_emit_locked(pkg);
80 : 0 : 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 : 172 : handle_rc = pkg_object_bool(pkg_config_get("HANDLE_RC_SCRIPTS"));
88 [ + - ]: 172 : if (handle_rc)
89 : 0 : pkg_start_stop_rc_scripts(pkg, PKG_RC_STOP);
90 : :
91 [ - + ]: 172 : if ((flags & PKG_DELETE_NOSCRIPT) == 0) {
92 : 172 : pkg_open_root_fd(pkg);
93 [ - + ]: 172 : if (!(flags & PKG_DELETE_UPGRADE)) {
94 : 172 : ret = pkg_lua_script_run(pkg, PKG_LUA_PRE_DEINSTALL, false);
95 [ - + # # ]: 172 : if (ret != EPKG_OK && ctx.developer_mode)
96 : 0 : return (ret);
97 : 172 : ret = pkg_script_run(pkg, PKG_SCRIPT_PRE_DEINSTALL, false);
98 [ - + # # ]: 172 : if (ret != EPKG_OK && ctx.developer_mode)
99 : 0 : return (ret);
100 : 172 : }
101 : 172 : }
102 : :
103 [ - + - + ]: 344 : if ((ret = pkg_delete_files(pkg, flags & PKG_DELETE_FORCE ? 1 : 0))
104 : 172 : != EPKG_OK)
105 : 0 : return (ret);
106 : :
107 [ - + ]: 172 : if ((flags & (PKG_DELETE_NOSCRIPT | PKG_DELETE_UPGRADE)) == 0) {
108 : 172 : pkg_lua_script_run(pkg, PKG_LUA_POST_DEINSTALL, false);
109 : 172 : pkg_script_run(pkg, PKG_SCRIPT_POST_DEINSTALL, false);
110 : 172 : }
111 : :
112 : 172 : ret = pkg_delete_dirs(db, pkg, NULL);
113 [ - + ]: 172 : if (ret != EPKG_OK)
114 : 0 : return (ret);
115 : :
116 [ - + ]: 172 : if ((flags & PKG_DELETE_UPGRADE) == 0) {
117 : 172 : pkg_emit_deinstall_finished(pkg);
118 [ + + ]: 256 : LL_FOREACH(pkg->message, msg) {
119 [ + + ]: 84 : if (msg->type == PKG_MESSAGE_REMOVE) {
120 [ - + ]: 12 : if (message == NULL) {
121 : 12 : message = xstring_new();
122 : 24 : pkg_fprintf(message->fp, "Message from "
123 : 12 : "%n-%v:\n", pkg, pkg);
124 : 12 : }
125 : 12 : fprintf(message->fp, "%s\n", msg->str);
126 : 12 : }
127 : 84 : }
128 [ + + - + ]: 172 : if (pkg->message != NULL && message != NULL) {
129 : 12 : fflush(message->fp);
130 : 12 : pkg_emit_message(message->buf);
131 : 12 : xstring_free(message);
132 : 12 : }
133 : :
134 : 172 : }
135 : :
136 : 172 : return (pkgdb_unregister_pkg(db, pkg->id));
137 : 172 : }
138 : :
139 : : void
140 : 75 : 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 [ + + ]: 75 : strlcpy(path, file != NULL ? file : dir, MAXPATHLEN);
147 : :
148 [ + + ]: 75 : if (file != NULL) {
149 : 45 : tmp = strrchr(path, '/');
150 : 45 : tmp[1] = '\0';
151 : 45 : }
152 : :
153 : 75 : len = strlen(path);
154 : :
155 : : /* make sure to finish by a / */
156 [ + + ]: 75 : if (path[len - 1] != '/') {
157 : 30 : path[len] = '/';
158 : 30 : len++;
159 : 30 : path[len] = '\0';
160 : 30 : }
161 : :
162 [ + + ]: 88 : for (i = 0; i < pkg->dir_to_del_len ; i++) {
163 : 38 : len2 = strlen(pkg->dir_to_del[i]);
164 [ + - + + ]: 38 : if (len2 >= len && strncmp(path, pkg->dir_to_del[i], len) == 0)
165 : 25 : return;
166 : :
167 [ + - ]: 13 : if (strncmp(path, pkg->dir_to_del[i], len2) == 0) {
168 : 0 : pkg_debug(1, "Replacing in deletion %s with %s",
169 : 0 : pkg->dir_to_del[i], path);
170 : 0 : free(pkg->dir_to_del[i]);
171 : 0 : pkg->dir_to_del[i] = xstrdup(path);
172 : 0 : return;
173 : : }
174 : 13 : }
175 : :
176 : 50 : pkg_debug(1, "Adding to deletion %s", path);
177 : :
178 [ + + ]: 50 : if (pkg->dir_to_del_len + 1 > pkg->dir_to_del_cap) {
179 : 37 : pkg->dir_to_del_cap += 64;
180 : 74 : pkg->dir_to_del = xrealloc(pkg->dir_to_del,
181 : 37 : pkg->dir_to_del_cap * sizeof(char *));
182 : 37 : }
183 : :
184 : 50 : pkg->dir_to_del[pkg->dir_to_del_len++] = xstrdup(path);
185 : 75 : }
186 : :
187 : : static void
188 : 36 : 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 : 36 : len = snprintf(fullpath, sizeof(fullpath), "/%s", dir);
202 [ + + ]: 72 : while (fullpath[len -1] == '/') {
203 : 36 : fullpath[len - 1] = '\0';
204 : 36 : len--;
205 : : }
206 [ - + ]: 36 : if (pkgdb_is_dir_used(db, pkg, fullpath, &cnt) != EPKG_OK)
207 : 0 : return;
208 : :
209 : 36 : pkg_debug(1, "Number of packages owning the directory '%s': %d",
210 : 36 : 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 [ + + ]: 36 : if (cnt > 0)
217 : 4 : return;
218 : :
219 [ + + ]: 32 : if (strcmp(prefix_r, fullpath + 1) == 0)
220 : 12 : return;
221 : :
222 : 20 : pkg_debug(1, "removing directory %s", fullpath);
223 : : #ifdef HAVE_STRUCT_STAT_ST_FLAGS
224 [ + - ]: 20 : if (fstatat(pkg->rootfd, dir, &st, AT_SYMLINK_NOFOLLOW) != -1) {
225 [ + - ]: 20 : if (st.st_flags & NOCHANGESFLAGS) {
226 : : #ifdef HAVE_CHFLAGSAT
227 : : /* Disable all flags*/
228 : 0 : 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 : 0 : }
237 : 20 : }
238 : : #endif
239 : :
240 [ + + ]: 20 : if (unlinkat(pkg->rootfd, dir, AT_REMOVEDIR) == -1) {
241 [ - + # # ]: 8 : if (errno != ENOTEMPTY && errno != EBUSY)
242 : 0 : pkg_emit_errno("unlinkat", dir);
243 : : /* If the directory was already removed by a bogus script, continue removing parents */
244 [ + - ]: 8 : if (errno != ENOENT)
245 : 8 : return;
246 : 0 : }
247 : :
248 : : /* No recursivity for packages out of the prefix */
249 [ - + ]: 12 : if (strncmp(prefix_r, dir, strlen(prefix_r)) != 0)
250 : 0 : return;
251 : :
252 : : /* remove the trailing '/' */
253 : 12 : tmp = strrchr(dir, '/');
254 [ + - ]: 12 : if (tmp == NULL)
255 : 0 : return;
256 [ + - ]: 12 : if (tmp == dir)
257 : 0 : return;
258 : :
259 : 12 : tmp[0] = '\0';
260 : 12 : tmp = strrchr(dir, '/');
261 [ - + ]: 12 : if (tmp == NULL)
262 : 0 : return;
263 : :
264 : 12 : tmp[1] = '\0';
265 : :
266 : 12 : rmdir_p(db, pkg, dir, prefix_r);
267 : 36 : }
268 : :
269 : : static void
270 : 304 : pkg_effective_rmdir(struct pkgdb *db, struct pkg *pkg)
271 : : {
272 : : char prefix_r[MAXPATHLEN];
273 : : size_t i;
274 : :
275 : 304 : snprintf(prefix_r, sizeof(prefix_r), "%s", pkg->prefix + 1);
276 [ + + ]: 328 : for (i = 0; i < pkg->dir_to_del_len; i++)
277 : 24 : rmdir_p(db, pkg, pkg->dir_to_del[i], prefix_r);
278 : 304 : }
279 : :
280 : : void
281 : 158 : 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 : 158 : pkg_open_root_fd(pkg);
293 : :
294 : 158 : path = file->path;
295 : 158 : path++;
296 : :
297 : 158 : prefix_rel = pkg->prefix;
298 : 158 : prefix_rel++;
299 : 158 : len = strlen(prefix_rel);
300 [ + + + + ]: 174 : while (len > 0 && prefix_rel[len - 1] == '/')
301 : 16 : len--;
302 : :
303 : : #ifdef HAVE_STRUCT_STAT_ST_FLAGS
304 [ - + ]: 158 : if (fstatat(pkg->rootfd, path, &st, AT_SYMLINK_NOFOLLOW) != -1) {
305 [ + - ]: 158 : if (st.st_flags & NOCHANGESFLAGS) {
306 : : #ifdef HAVE_CHFLAGSAT
307 : 0 : chflagsat(pkg->rootfd, path,
308 : 0 : 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 : 0 : }
318 : 158 : }
319 : : #endif
320 : 158 : pkg_debug(1, "Deleting file: '%s'", path);
321 [ + - ]: 158 : if (unlinkat(pkg->rootfd, path, 0) == -1) {
322 [ # # ]: 0 : if (force < 2) {
323 [ # # ]: 0 : if (errno == ENOENT)
324 : 0 : pkg_emit_file_missing(pkg, file);
325 : : else
326 : 0 : pkg_emit_errno("unlinkat", path);
327 : 0 : }
328 : 0 : return;
329 : : }
330 : :
331 : : /* do not bother about directories not in prefix */
332 [ + + + + ]: 158 : if ((strncmp(prefix_rel, path, len) == 0) && path[len] == '/')
333 : 32 : pkg_add_dir_to_del(pkg, path, NULL);
334 : 158 : }
335 : :
336 : : int
337 : 172 : 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 : 172 : struct pkg_file *file = NULL;
344 : :
345 : 172 : int nfiles, cur_file = 0;
346 : :
347 : 172 : nfiles = pkghash_count(pkg->filehash);
348 : :
349 [ + + ]: 172 : if (nfiles == 0)
350 : 79 : return (EPKG_OK);
351 : :
352 : 93 : pkg_emit_delete_files_begin(pkg);
353 : 93 : pkg_emit_progress_start(NULL);
354 : :
355 [ + + ]: 243 : while (pkg_files(pkg, &file) == EPKG_OK) {
356 : 150 : append_touched_file(file->path);
357 : 150 : pkg_emit_progress_tick(cur_file++, nfiles);
358 : 150 : pkg_delete_file(pkg, file, force);
359 : : }
360 : :
361 : 93 : pkg_emit_progress_tick(nfiles, nfiles);
362 : 93 : pkg_emit_delete_files_finished(pkg);
363 : :
364 : 93 : return (EPKG_OK);
365 : 172 : }
366 : :
367 : : void
368 : 4 : 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 : 4 : pkg_open_root_fd(pkg);
375 : :
376 : 4 : path = dir->path;
377 : : /* remove the first / */
378 : 4 : path++;
379 : :
380 : 4 : prefix_rel = pkg->prefix;
381 : 4 : prefix_rel++;
382 : 4 : len = strlen(prefix_rel);
383 [ + + ]: 8 : while (prefix_rel[len - 1] == '/')
384 : 4 : len--;
385 : :
386 [ + - + - ]: 4 : if ((strncmp(prefix_rel, path, len) == 0) && path[len] == '/') {
387 : 4 : pkg_add_dir_to_del(pkg, NULL, path);
388 : 4 : } else {
389 [ # # ]: 0 : if (pkg->dir_to_del_len + 1 > pkg->dir_to_del_cap) {
390 : 0 : pkg->dir_to_del_cap += 64;
391 : 0 : pkg->dir_to_del = xrealloc(pkg->dir_to_del,
392 : 0 : pkg->dir_to_del_cap * sizeof(char *));
393 : 0 : }
394 : 0 : pkg->dir_to_del[pkg->dir_to_del_len++] = xstrdup(path);
395 : : }
396 : 4 : }
397 : :
398 : : int
399 : 304 : pkg_delete_dirs(__unused struct pkgdb *db, struct pkg *pkg, struct pkg *new)
400 : : {
401 : 304 : struct pkg_dir *dir = NULL;
402 : :
403 [ + + ]: 308 : while (pkg_dirs(pkg, &dir) == EPKG_OK) {
404 [ - + # # ]: 4 : if (new != NULL && !pkg_has_dir(new, dir->path))
405 : 0 : continue;
406 : 4 : pkg_delete_dir(pkg, dir);
407 : : }
408 : :
409 : 304 : pkg_effective_rmdir(db, pkg);
410 : :
411 : 304 : return (EPKG_OK);
412 : : }
|