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