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