Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/pkg_delete.c
2649 views
1
/*-
2
* Copyright (c) 2011-2014 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2011-2012 Julien Laffaye <[email protected]>
4
* Copyright (c) 2011 Will Andrews <[email protected]>
5
* Copyright (c) 2011 Philippe Pepiot <[email protected]>
6
* Copyright (c) 2014 Vsevolod Stakhov <[email protected]>
7
* Copyright (c) 2023 Serenity Cyber Security, LLC
8
* Author: Gleb Popov <[email protected]>
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
if ((flags & PKG_DELETE_KEEPFILES) == 0) {
104
ret = pkg_delete_files(db, pkg, rpkg, flags, t);
105
if (ret == EPKG_CANCEL)
106
cancel = 1;
107
else if (ret != EPKG_OK)
108
return (ret);
109
}
110
111
if ((flags & PKG_DELETE_NOSCRIPT) == 0) {
112
bool noexec = ((flags & PKG_DELETE_NOEXEC) == PKG_DELETE_NOEXEC);
113
pkg_lua_script_run(pkg, PKG_LUA_POST_DEINSTALL, false);
114
ret = pkg_script_run(pkg, PKG_SCRIPT_POST_DEINSTALL, false, noexec);
115
if (ret != EPKG_OK && (ctx.developer_mode || noexec))
116
return (ret);
117
}
118
119
if ((flags & PKG_DELETE_KEEPFILES) == 0) {
120
ret = pkg_delete_dirs(db, pkg, NULL);
121
if (ret != EPKG_OK)
122
return (ret);
123
}
124
125
pkg_emit_deinstall_finished(pkg);
126
vec_foreach(pkg->message, i) {
127
if (pkg->message.d[i]->type == PKG_MESSAGE_REMOVE) {
128
if (message == NULL) {
129
message = xstring_new();
130
pkg_fprintf(message->fp, "Message from "
131
"%n-%v:\n", pkg, pkg);
132
}
133
fprintf(message->fp, "%s\n", pkg->message.d[i]->str);
134
}
135
}
136
if (pkg_has_message(pkg) && message != NULL) {
137
fflush(message->fp);
138
pkg_emit_message(message->buf);
139
xstring_free(message);
140
}
141
142
ret = pkgdb_unregister_pkg(db, pkg->id);
143
if (ret != EPKG_OK)
144
return ret;
145
146
return (cancel ? EPKG_CANCEL : ret);
147
}
148
149
void
150
pkg_add_dir_to_del(struct pkg *pkg, const char *file, const char *dir)
151
{
152
char path[MAXPATHLEN];
153
char *tmp;
154
size_t len, len2;
155
156
strlcpy(path, file != NULL ? file : dir, MAXPATHLEN);
157
158
if (file != NULL) {
159
tmp = strrchr(path, '/');
160
tmp[1] = '\0';
161
}
162
163
len = strlen(path);
164
165
/* make sure to finish by a / */
166
if (len > 0 && path[len - 1] != '/' && len < MAXPATHLEN) {
167
path[len] = '/';
168
len++;
169
path[len] = '\0';
170
}
171
172
vec_foreach(pkg->dir_to_del, i) {
173
len2 = strlen(pkg->dir_to_del.d[i]);
174
if (len2 >= len && strncmp(path, pkg->dir_to_del.d[i], len) == 0)
175
return;
176
177
if (strncmp(path, pkg->dir_to_del.d[i], len2) == 0) {
178
pkg_debug(1, "Replacing in deletion %s with %s",
179
pkg->dir_to_del.d[i], path);
180
free(pkg->dir_to_del.d[i]);
181
pkg->dir_to_del.d[i] = xstrdup(path);
182
return;
183
}
184
}
185
186
pkg_debug(1, "Adding to deletion %s", path);
187
vec_push(&pkg->dir_to_del, xstrdup(path));
188
}
189
190
static void
191
rmdir_p(struct pkgdb *db, struct pkg *pkg, char *dir, const char *prefix_r)
192
{
193
char *tmp;
194
int64_t cnt;
195
char fullpath[MAXPATHLEN];
196
size_t len;
197
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
198
struct stat st;
199
#if !defined(HAVE_CHFLAGSAT)
200
int fd;
201
#endif
202
#endif
203
204
len = snprintf(fullpath, sizeof(fullpath), "/%s", dir);
205
while (fullpath[len -1] == '/') {
206
fullpath[len - 1] = '\0';
207
len--;
208
}
209
if (pkgdb_is_dir_used(db, pkg, fullpath, &cnt) != EPKG_OK)
210
return;
211
212
pkg_debug(1, "Number of packages owning the directory '%s': %d",
213
fullpath, (int)cnt);
214
/*
215
* At this moment the package we are removing have already been removed
216
* from the local database so if anything else is owning the directory
217
* that is another package meaning only remove the diretory is cnt == 0
218
*/
219
if (cnt > 0)
220
return;
221
222
if (STREQ(prefix_r, fullpath + 1))
223
return;
224
225
pkg_debug(1, "removing directory %s", fullpath);
226
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
227
if (fstatat(pkg->rootfd, dir, &st, AT_SYMLINK_NOFOLLOW) != -1) {
228
if (st.st_flags & NOCHANGESFLAGS) {
229
#ifdef HAVE_CHFLAGSAT
230
/* Disable all flags*/
231
chflagsat(pkg->rootfd, dir, 0, AT_SYMLINK_NOFOLLOW);
232
#else
233
fd = openat(pkg->rootfd, dir, O_NOFOLLOW);
234
if (fd > 0) {
235
fchflags(fd, 0);
236
close(fd);
237
}
238
#endif
239
}
240
}
241
#endif
242
243
if (unlinkat(pkg->rootfd, dir, AT_REMOVEDIR) == -1) {
244
if (errno != ENOTEMPTY && errno != EBUSY)
245
pkg_emit_errno("unlinkat", dir);
246
/* If the directory was already removed by a bogus script, continue removing parents */
247
if (errno != ENOENT)
248
return;
249
}
250
251
/* No recursivity for packages out of the prefix */
252
if (strncmp(prefix_r, dir, strlen(prefix_r)) != 0)
253
return;
254
255
/* remove the trailing '/' */
256
tmp = strrchr(dir, '/');
257
if (tmp == NULL)
258
return;
259
if (tmp == dir)
260
return;
261
262
tmp[0] = '\0';
263
tmp = strrchr(dir, '/');
264
if (tmp == NULL)
265
return;
266
267
tmp[1] = '\0';
268
269
rmdir_p(db, pkg, dir, prefix_r);
270
}
271
272
static void
273
pkg_effective_rmdir(struct pkgdb *db, struct pkg *pkg)
274
{
275
char prefix_r[MAXPATHLEN];
276
277
snprintf(prefix_r, sizeof(prefix_r), "%s", pkg->prefix[0] ? pkg->prefix + 1 : "");
278
vec_foreach(pkg->dir_to_del, i) {
279
rmdir_p(db, pkg, pkg->dir_to_del.d[i], prefix_r);
280
}
281
}
282
283
void
284
pkg_delete_file(struct pkg *pkg, struct pkg_file *file)
285
{
286
const char *path;
287
const char *prefix_rel;
288
size_t len;
289
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
290
struct stat st;
291
#if !defined(HAVE_CHFLAGSAT)
292
int fd;
293
#endif
294
#endif
295
pkg_open_root_fd(pkg);
296
297
path = file->path;
298
path++;
299
300
prefix_rel = pkg->prefix;
301
prefix_rel++;
302
len = strlen(prefix_rel);
303
while (len > 0 && prefix_rel[len - 1] == '/')
304
len--;
305
306
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
307
if (fstatat(pkg->rootfd, path, &st, AT_SYMLINK_NOFOLLOW) != -1) {
308
if (st.st_flags & NOCHANGESFLAGS) {
309
#ifdef HAVE_CHFLAGSAT
310
chflagsat(pkg->rootfd, path,
311
st.st_flags & ~NOCHANGESFLAGS,
312
AT_SYMLINK_NOFOLLOW);
313
#else
314
fd = openat(pkg->rootfd, path, O_NOFOLLOW);
315
if (fd > 0) {
316
fchflags(fd, st.st_flags & ~NOCHANGESFLAGS);
317
close(fd);
318
}
319
#endif
320
}
321
}
322
#endif
323
pkg_debug(1, "Deleting file: '%s'", path);
324
if (unlinkat(pkg->rootfd, path, 0) == -1) {
325
if (errno == ENOENT)
326
pkg_emit_file_missing(pkg, file);
327
else
328
pkg_emit_errno("unlinkat", path);
329
return;
330
}
331
332
/* do not bother about directories not in prefix */
333
if ((strncmp(prefix_rel, path, len) == 0) && path[len] == '/')
334
pkg_add_dir_to_del(pkg, path, NULL);
335
}
336
337
/*
338
* Handle a special case: the package is to be upgraded but is being deleted
339
* temporarily to handle a file path conflict. In this situation we shouldn't
340
* remove configuration files. For now, keep them if the replacement package
341
* contains a configuration file at the same path.
342
*
343
* Note, this currently doesn't handle the case where a configuration file
344
* participates in the conflict, i.e., it moves from one package to another.
345
*/
346
static bool
347
pkg_delete_skip_config(struct pkg *pkg, struct pkg *rpkg, struct pkg_file *file,
348
int flags)
349
{
350
if ((flags & PKG_DELETE_UPGRADE) == 0)
351
return (false);
352
if (pkghash_get(pkg->config_files_hash, file->path) == NULL)
353
return (false);
354
if (pkghash_get(rpkg->config_files_hash, file->path) == NULL)
355
return (false);
356
return (true);
357
}
358
359
int
360
pkg_delete_files(struct pkgdb *db, struct pkg *pkg, struct pkg *rpkg, int flags,
361
struct triggers *t)
362
{
363
struct pkg_file *file = NULL;
364
int nfiles, cur_file = 0;
365
int retcode = EPKG_OK;
366
367
nfiles = pkghash_count(pkg->filehash);
368
if (nfiles == 0)
369
return (EPKG_OK);
370
371
pkg_emit_delete_files_begin(pkg);
372
pkg_emit_progress_start(NULL);
373
374
while (pkg_files(pkg, &file) == EPKG_OK) {
375
if (pkg_delete_skip_config(pkg, rpkg, file, flags))
376
continue;
377
if ((flags & PKG_DELETE_UPGRADE) != 0)
378
pkg_maybe_backup_library(db, pkg, file->path);
379
append_touched_file(file->path);
380
if (pkg_emit_progress_tick(cur_file++, nfiles))
381
retcode = EPKG_CANCEL;
382
trigger_is_it_a_cleanup(t, file->path);
383
pkg_delete_file(pkg, file);
384
}
385
386
pkg_emit_progress_tick(nfiles, nfiles);
387
pkg_emit_delete_files_finished(pkg);
388
389
return (retcode);
390
}
391
392
void
393
pkg_delete_dir(struct pkg *pkg, struct pkg_dir *dir)
394
{
395
const char *path;
396
const char *prefix_rel;
397
size_t len;
398
399
pkg_open_root_fd(pkg);
400
401
path = dir->path;
402
/* remove the first / */
403
path++;
404
405
prefix_rel = pkg->prefix;
406
prefix_rel++;
407
len = strlen(prefix_rel);
408
while (len > 0 && prefix_rel[len - 1] == '/')
409
len--;
410
411
if ((strncmp(prefix_rel, path, len) == 0) && path[len] == '/') {
412
pkg_add_dir_to_del(pkg, NULL, path);
413
} else {
414
vec_push(&pkg->dir_to_del, xstrdup(path));
415
}
416
}
417
418
int
419
pkg_delete_dirs(__unused struct pkgdb *db, struct pkg *pkg, struct pkg *new)
420
{
421
struct pkg_dir *dir = NULL;
422
423
while (pkg_dirs(pkg, &dir) == EPKG_OK) {
424
if (new != NULL && pkg_has_dir(new, dir->path))
425
continue;
426
pkg_delete_dir(pkg, dir);
427
}
428
429
pkg_effective_rmdir(db, pkg);
430
431
return (EPKG_OK);
432
}
433
434