Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2011-2020 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4 : : * Copyright (c) 2016, Vsevolod Stakhov
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions
8 : : * are met:
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer
11 : : * in this position and unchanged.
12 : : * 2. Redistributions in binary form must reproduce the above copyright
13 : : * notice, this list of conditions and the following disclaimer in the
14 : : * documentation and/or other materials provided with the distribution.
15 : : *
16 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 : : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : : */
27 : :
28 : : #ifdef HAVE_CONFIG_H
29 : : #include "pkg_config.h"
30 : : #endif
31 : :
32 : : #include <archive.h>
33 : : #include <archive_entry.h>
34 : : #include <assert.h>
35 : : #include <libgen.h>
36 : : #include <string.h>
37 : : #include <errno.h>
38 : : #include <fcntl.h>
39 : : #include <glob.h>
40 : : #include <pwd.h>
41 : : #include <grp.h>
42 : : #include <sys/time.h>
43 : : #include <time.h>
44 : : #include <xstring.h>
45 : :
46 : : #include "pkg.h"
47 : : #include "private/event.h"
48 : : #include "private/utils.h"
49 : : #include "private/pkg.h"
50 : : #include "private/pkgdb.h"
51 : :
52 [ + - + + : 92 : KHASH_MAP_INIT_INT(hls, char *);
- + - + -
+ # # - +
- + - + #
# + - + -
# # # # #
# # # # #
# # # # #
# + - # #
- + - + +
- + - + -
- + + - -
+ - + - +
# # # # #
# # # +
- ]
53 : :
54 : : #if defined(UF_NOUNLINK)
55 : : #define NOCHANGESFLAGS (UF_IMMUTABLE | UF_APPEND | UF_NOUNLINK | SF_IMMUTABLE | SF_APPEND | SF_NOUNLINK)
56 : : #else
57 : : #define NOCHANGESFLAGS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
58 : : #endif
59 : :
60 : : static const unsigned char litchar[] =
61 : : "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
62 : :
63 : : static void
64 : 531 : pkg_add_file_random_suffix(char *buf, int buflen, int suflen)
65 : : {
66 : 531 : int nchars = strlen(buf);
67 : : char *pos;
68 : : int r;
69 : :
70 [ + - ]: 531 : if (nchars + suflen > buflen - 1) {
71 : 0 : suflen = buflen - nchars - 1;
72 [ # # ]: 0 : if (suflen <= 0)
73 : 0 : return;
74 : 0 : }
75 : :
76 : 531 : buf[nchars++] = '.';
77 : 531 : pos = buf + nchars;
78 : :
79 [ + + ]: 6903 : while(suflen --) {
80 : : #ifndef HAVE_ARC4RANDOM
81 : : r = rand() % (sizeof(litchar) - 1);
82 : : #else
83 : 6372 : r = arc4random_uniform(sizeof(litchar) - 1);
84 : : #endif
85 : 6372 : *pos++ = litchar[r];
86 : : }
87 : :
88 : 531 : *pos = '\0';
89 : 531 : }
90 : :
91 : : static void
92 : 531 : pkg_hidden_tempfile(char *buf, int buflen, const char *path)
93 : : {
94 : : const char *fname;
95 : :
96 : 531 : fname = strrchr(path, '/');
97 [ - + ]: 531 : if (fname != NULL)
98 : 531 : fname++;
99 : :
100 [ - + ]: 531 : if (fname != NULL)
101 : 531 : snprintf(buf, buflen, "%.*s.pkgtemp.%s", (int)(fname - path), path, fname);
102 : : else
103 : 0 : snprintf(buf, buflen, ".pkgtemp.%s", path);
104 : :
105 : 531 : pkg_add_file_random_suffix(buf, buflen, 12);
106 : 531 : }
107 : :
108 : : static void
109 : 42 : attempt_to_merge(int rootfd, struct pkg_config_file *rcf, struct pkg *local,
110 : : bool merge)
111 : : {
112 : 42 : const struct pkg_file *lf = NULL;
113 : : struct stat st;
114 : : xstring *newconf;
115 : 42 : struct pkg_config_file *lcf = NULL;
116 : :
117 : 42 : char *localconf = NULL;
118 : : off_t sz;
119 : : char *localsum;
120 : :
121 [ + - ]: 42 : if (rcf == NULL) {
122 : 0 : pkg_debug(3, "No remote config file");
123 : 0 : return;
124 : : }
125 : :
126 [ + + ]: 42 : if (local == NULL) {
127 : 20 : pkg_debug(3, "No local package");
128 [ + + ]: 20 : if (fstatat(rootfd, RELATIVE_PATH(rcf->path), &st, 0) == 0) {
129 : 5 : rcf->status = MERGE_NOT_LOCAL;
130 : 5 : }
131 : 20 : return;
132 : : }
133 : :
134 [ + + ]: 22 : if (!pkg_is_config_file(local, rcf->path, &lf, &lcf)) {
135 : 10 : pkg_debug(3, "No local package");
136 : 10 : rcf->status = MERGE_FAILED;
137 : 10 : return;
138 : : }
139 : :
140 [ + - ]: 12 : if (lcf->content == NULL) {
141 : 0 : pkg_debug(3, "Empty configuration content for local package");
142 : 0 : return;
143 : : }
144 : :
145 : 12 : pkg_debug(1, "Config file found %s", rcf->path);
146 [ - + ]: 12 : if (file_to_bufferat(rootfd, RELATIVE_PATH(rcf->path), &localconf, &sz) != EPKG_OK)
147 : 0 : return;
148 : :
149 : 12 : pkg_debug(2, "size: %jd vs %jd", (intmax_t)sz, (intmax_t)strlen(lcf->content));
150 : :
151 [ + - ]: 12 : if (sz == strlen(lcf->content)) {
152 : 0 : pkg_debug(2, "Ancient vanilla and deployed conf are the same size testing checksum");
153 : 0 : localsum = pkg_checksum_data(localconf, sz,
154 : : PKG_HASH_TYPE_SHA256_HEX);
155 [ # # # # ]: 0 : if (localsum && strcmp(localsum, lf->sum) == 0) {
156 : 0 : pkg_debug(2, "Checksum are the same %jd", (intmax_t)strlen(localconf));
157 : 0 : free(localconf);
158 : 0 : free(localsum);
159 : 0 : return;
160 : : }
161 : 0 : free(localsum);
162 : 0 : pkg_debug(2, "Checksum are different %jd", (intmax_t)strlen(localconf));
163 : 0 : }
164 : 12 : rcf->status = MERGE_FAILED;
165 [ + - ]: 12 : if (!merge) {
166 : 0 : free(localconf);
167 : 0 : return;
168 : : }
169 : :
170 : 12 : pkg_debug(1, "Attempting to merge %s", rcf->path);
171 : 12 : newconf = xstring_new();
172 [ - + ]: 12 : if (merge_3way(lcf->content, localconf, rcf->content, newconf) != 0) {
173 : 0 : xstring_free(newconf);
174 : 0 : pkg_emit_error("Impossible to merge configuration file");
175 : 0 : } else {
176 : 12 : char *conf = xstring_get(newconf);
177 : 12 : rcf->newcontent = conf;
178 : 12 : rcf->status = MERGE_SUCCESS;
179 : : }
180 : 12 : free(localconf);
181 : 42 : }
182 : :
183 : : static uid_t
184 : 512 : get_uid_from_archive(struct archive_entry *ae)
185 : : {
186 : : static char user_buffer[1024];
187 : : const char *user;
188 : : static struct passwd pwent;
189 : : struct passwd *result;
190 : : int err;
191 : :
192 : 512 : user = archive_entry_uname(ae);
193 [ + + + + ]: 512 : if (pwent.pw_name != NULL && strcmp(user, pwent.pw_name) == 0)
194 : 152 : goto out;
195 : 360 : pwent.pw_name = NULL;
196 : 360 : err = getpwnam_r(user, &pwent, user_buffer, sizeof(user_buffer),
197 : : &result);
198 [ - + ]: 360 : if (err != 0) {
199 : 0 : pkg_emit_errno("getpwnam_r", user );
200 : 0 : return (0);
201 : : }
202 [ + - ]: 360 : if (result == NULL)
203 : 0 : return (0);
204 : : out:
205 : 512 : return (pwent.pw_uid);
206 : 512 : }
207 : :
208 : : static gid_t
209 : 512 : get_gid_from_archive(struct archive_entry *ae)
210 : : {
211 : : static char group_buffer[1024];
212 : : static struct group grent;
213 : : struct group *result;
214 : : const char *group;
215 : : int err;
216 : :
217 : 512 : group = archive_entry_gname(ae);
218 [ + + + + ]: 512 : if (grent.gr_name != NULL && strcmp(group, grent.gr_name) == 0)
219 : 156 : goto out;
220 : 356 : grent.gr_name = NULL;
221 : 356 : err = getgrnam_r(group, &grent, group_buffer, sizeof(group_buffer),
222 : : &result);
223 [ - + ]: 356 : if (err != 0) {
224 : 0 : pkg_emit_errno("getgrnam_r", group );
225 : 0 : return (0);
226 : : }
227 [ + - ]: 356 : if (result == NULL)
228 : 0 : return (0);
229 : : out:
230 : 512 : return (grent.gr_gid);
231 : 512 : }
232 : :
233 : : int
234 : 528 : set_attrsat(int fd, const char *path, mode_t perm, uid_t uid, gid_t gid,
235 : : const struct timespec *ats, const struct timespec *mts)
236 : : {
237 : :
238 : : struct timeval tv[2];
239 : : struct stat st;
240 : : int fdcwd;
241 : : #ifdef HAVE_UTIMENSAT
242 : : struct timespec times[2];
243 : :
244 : 528 : times[0] = *ats;
245 : 528 : times[1] = *mts;
246 [ # # ]: 528 : if (utimensat(fd, RELATIVE_PATH(path), times,
247 [ - + ]: 528 : AT_SYMLINK_NOFOLLOW) == -1 && errno != EOPNOTSUPP){
248 : 0 : pkg_fatal_errno("Fail to set time on %s", path);
249 : 0 : }
250 [ + - ]: 528 : if (errno == EOPNOTSUPP) {
251 : : #endif
252 : :
253 : 0 : tv[0].tv_sec = ats->tv_sec;
254 : 0 : tv[0].tv_usec = ats->tv_nsec / 1000;
255 : 0 : tv[1].tv_sec = mts->tv_sec;
256 : 0 : tv[1].tv_usec = mts->tv_nsec / 1000;
257 : :
258 [ # # ]: 0 : if ((fdcwd = open(".", O_DIRECTORY|O_CLOEXEC)) == -1) {
259 : 0 : pkg_fatal_errno("Failed to open .%s", "");
260 : 0 : }
261 : 0 : fchdir(fd);
262 : :
263 [ # # ]: 0 : if (lutimes(RELATIVE_PATH(path), tv) == -1) {
264 : :
265 [ # # ]: 0 : if (errno != ENOSYS) {
266 : 0 : close(fdcwd);
267 : 0 : pkg_fatal_errno("Fail to set time on %s", path);
268 : 0 : }
269 : : else {
270 : : /* Fallback to utimes */
271 [ # # ]: 0 : if (utimes(RELATIVE_PATH(path), tv) == -1) {
272 : 0 : close(fdcwd);
273 : 0 : pkg_fatal_errno("Fail to set time(fallback) on "
274 : : "%s", path);
275 : 0 : }
276 : : }
277 : 0 : }
278 : 0 : fchdir(fdcwd);
279 : 0 : close(fdcwd);
280 : : #ifdef HAVE_UTIMENSAT
281 : 0 : }
282 : : #endif
283 : :
284 [ + + ]: 528 : if (getenv("INSTALL_AS_USER") == NULL) {
285 [ + - + - ]: 8 : if (fchownat(fd, RELATIVE_PATH(path), uid, gid,
286 : 4 : AT_SYMLINK_NOFOLLOW) == -1) {
287 [ # # ]: 0 : if (errno == ENOTSUP) {
288 [ # # ]: 0 : if (fchownat(fd, RELATIVE_PATH(path), uid, gid, 0) == -1) {
289 : 0 : pkg_fatal_errno("Fail to chown(fallback) %s", path);
290 : 0 : }
291 : 0 : }
292 : : else {
293 : 0 : pkg_fatal_errno("Fail to chown %s", path);
294 : : }
295 : 0 : }
296 : 4 : }
297 : :
298 : : /* zfs drops the setuid on fchownat */
299 [ + - ]: 528 : if (fchmodat(fd, RELATIVE_PATH(path), perm, AT_SYMLINK_NOFOLLOW) == -1) {
300 [ # # ]: 0 : if (errno == ENOTSUP) {
301 : : /*
302 : : * Executing fchmodat on a symbolic link results in
303 : : * ENOENT (file not found) on platforms that do not
304 : : * support AT_SYMLINK_NOFOLLOW. The file mode of
305 : : * symlinks cannot be modified via file descriptor
306 : : * reference on these systems. The lchmod function is
307 : : * also not an option because it is not a posix
308 : : * standard, nor is implemented everywhere. Since
309 : : * symlink permissions have never been evaluated and
310 : : * thus cosmetic, just skip them on these systems.
311 : : */
312 [ # # ]: 0 : if (fstatat(fd, RELATIVE_PATH(path), &st, AT_SYMLINK_NOFOLLOW) == -1) {
313 : 0 : pkg_fatal_errno("Fail to get file status %s", path);
314 : 0 : }
315 [ # # ]: 0 : if (!S_ISLNK(st.st_mode)) {
316 [ # # ]: 0 : if (fchmodat(fd, RELATIVE_PATH(path), perm, 0) == -1) {
317 : 0 : pkg_fatal_errno("Fail to chmod(fallback) %s", path);
318 : 0 : }
319 : 0 : }
320 : 0 : }
321 : : else {
322 : 0 : pkg_fatal_errno("Fail to chmod %s", path);
323 : : }
324 : 0 : }
325 : :
326 : 528 : return (EPKG_OK);
327 : 528 : }
328 : :
329 : : static void
330 : 512 : fill_timespec_buf(const struct stat *aest, struct timespec tspec[2])
331 : : {
332 : : #ifdef HAVE_STRUCT_STAT_ST_MTIM
333 : 512 : tspec[0].tv_sec = aest->st_atim.tv_sec;
334 : 512 : tspec[0].tv_nsec = aest->st_atim.tv_nsec;
335 : 512 : tspec[1].tv_sec = aest->st_mtim.tv_sec;
336 : 512 : tspec[1].tv_nsec = aest->st_mtim.tv_nsec;
337 : : #else
338 : : # if defined(_DARWIN_C_SOURCE) || defined(__APPLE__)
339 : : tspec[0].tv_sec = aest->st_atimespec.tv_sec;
340 : : tspec[0].tv_nsec = aest->st_atimespec.tv_nsec;
341 : : tspec[1].tv_sec = aest->st_mtimespec.tv_sec;
342 : : tspec[1].tv_nsec = aest->st_mtimespec.tv_nsec;
343 : : # else
344 : : /* Portable unix version */
345 : : tspec[0].tv_sec = aest->st_atime;
346 : : tspec[0].tv_nsec = 0;
347 : : tspec[1].tv_sec = aest->st_mtime;
348 : : tspec[1].tv_nsec = 0;
349 : : # endif
350 : : #endif
351 : 512 : }
352 : :
353 : : static int
354 : 24 : create_dir(struct pkg *pkg, struct pkg_dir *d)
355 : : {
356 : : struct stat st;
357 : :
358 [ + + ]: 24 : if (mkdirat(pkg->rootfd, RELATIVE_PATH(d->path), 0755) == -1)
359 [ + - ]: 16 : if (!mkdirat_p(pkg->rootfd, RELATIVE_PATH(d->path)))
360 : 0 : return (EPKG_FATAL);
361 [ + + ]: 24 : if (fstatat(pkg->rootfd, RELATIVE_PATH(d->path), &st, 0) == -1) {
362 [ + - ]: 4 : if (errno != ENOENT) {
363 : 0 : pkg_fatal_errno("Fail to stat directory %s", d->path);
364 : 0 : }
365 [ + - ]: 4 : if (fstatat(pkg->rootfd, RELATIVE_PATH(d->path), &st, AT_SYMLINK_NOFOLLOW) == 0) {
366 : 4 : unlinkat(pkg->rootfd, RELATIVE_PATH(d->path), 0);
367 : 4 : }
368 [ - + ]: 4 : if (mkdirat(pkg->rootfd, RELATIVE_PATH(d->path), 0755) == -1) {
369 : 0 : pkg_fatal_errno("Fail to create directory %s", d->path);
370 : 0 : }
371 : 4 : }
372 : :
373 [ + + + - : 24 : if (st.st_uid == d->uid && st.st_gid == d->gid &&
- + ]
374 : 12 : (st.st_mode & ~S_IFMT) == (d->perm & ~S_IFMT)) {
375 : 12 : d->noattrs = true;
376 : 12 : }
377 : :
378 : 24 : return (EPKG_OK);
379 : 24 : }
380 : :
381 : : /* In case of directories create the dir and extract the creds */
382 : : static int
383 : 24 : do_extract_dir(struct pkg* pkg, struct archive *a __unused, struct archive_entry *ae,
384 : : const char *path, struct pkg *local __unused)
385 : : {
386 : : struct pkg_dir *d;
387 : : const struct stat *aest;
388 : : unsigned long clear;
389 : :
390 : 24 : d = pkg_get_dir(pkg, path);
391 [ + - ]: 24 : if (d == NULL) {
392 : 0 : pkg_emit_error("Directory %s not specified in the manifest, skipping",
393 : 0 : path);
394 : 0 : return (EPKG_OK);
395 : : }
396 : 24 : aest = archive_entry_stat(ae);
397 : 24 : d->perm = aest->st_mode;
398 : 24 : d->uid = get_uid_from_archive(ae);
399 : 24 : d->gid = get_gid_from_archive(ae);
400 : 24 : fill_timespec_buf(aest, d->time);
401 : 24 : archive_entry_fflags(ae, &d->fflags, &clear);
402 : :
403 [ - + ]: 24 : if (create_dir(pkg, d) == EPKG_FATAL) {
404 : 0 : return (EPKG_FATAL);
405 : : }
406 : :
407 : 48 : metalog_add(PKG_METALOG_DIR, RELATIVE_PATH(path),
408 : 24 : archive_entry_uname(ae), archive_entry_gname(ae),
409 : 24 : aest->st_mode & ~S_IFDIR, d->fflags, NULL);
410 : :
411 : 24 : return (EPKG_OK);
412 : 24 : }
413 : :
414 : :
415 : : static bool
416 : 96 : try_mkdir(int fd, const char *path)
417 : : {
418 : 96 : char *p = get_dirname(xstrdup(path));
419 : :
420 [ - + ]: 96 : if (!mkdirat_p(fd, RELATIVE_PATH(p))) {
421 : 0 : free(p);
422 : 0 : return (false);
423 : : }
424 : 96 : free(p);
425 : 96 : return (true);
426 : 96 : }
427 : :
428 : : static int
429 : 48 : create_symlinks(struct pkg *pkg, struct pkg_file *f, const char *target)
430 : : {
431 : 48 : bool tried_mkdir = false;
432 : :
433 : 48 : pkg_hidden_tempfile(f->temppath, sizeof(f->temppath), f->path);
434 : : retry:
435 [ + + ]: 56 : if (symlinkat(target, pkg->rootfd, RELATIVE_PATH(f->temppath)) == -1) {
436 [ + - ]: 8 : if (!tried_mkdir) {
437 [ + - ]: 8 : if (!try_mkdir(pkg->rootfd, f->path))
438 : 0 : return (EPKG_FATAL);
439 : 8 : tried_mkdir = true;
440 : 8 : goto retry;
441 : : }
442 : :
443 : 0 : pkg_fatal_errno("Fail to create symlink: %s", f->temppath);
444 : 0 : }
445 : :
446 [ + - + - : 144 : if (set_attrsat(pkg->rootfd, f->temppath, f->perm, f->uid, f->gid,
+ - ]
447 : 96 : &f->time[0], &f->time[1]) != EPKG_OK) {
448 : 0 : return (EPKG_FATAL);
449 : : }
450 : 48 : return (EPKG_OK);
451 : 48 : }
452 : :
453 : : /* In case of a symlink create it directly with a random name */
454 : : static int
455 : 36 : do_extract_symlink(struct pkg *pkg, struct archive *a __unused, struct archive_entry *ae,
456 : : const char *path, struct pkg *local __unused)
457 : : {
458 : : struct pkg_file *f;
459 : : const struct stat *aest;
460 : : unsigned long clear;
461 : :
462 : 36 : f = pkg_get_file(pkg, path);
463 [ + - ]: 36 : if (f == NULL) {
464 : 0 : pkg_emit_error("Symlink %s not specified in the manifest", path);
465 : 0 : return (EPKG_FATAL);
466 : : }
467 : :
468 : 36 : aest = archive_entry_stat(ae);
469 : 36 : archive_entry_fflags(ae, &f->fflags, &clear);
470 : 36 : f->uid = get_uid_from_archive(ae);
471 : 36 : f->gid = get_gid_from_archive(ae);
472 : 36 : f->perm = aest->st_mode;
473 : 36 : fill_timespec_buf(aest, f->time);
474 : 36 : archive_entry_fflags(ae, &f->fflags, &clear);
475 : :
476 [ - + ]: 36 : if (create_symlinks(pkg, f, archive_entry_symlink(ae)) == EPKG_FATAL)
477 : 0 : return (EPKG_FATAL);
478 : :
479 : 72 : metalog_add(PKG_METALOG_LINK, RELATIVE_PATH(path),
480 : 36 : archive_entry_uname(ae), archive_entry_gname(ae),
481 : 36 : aest->st_mode & ~S_IFLNK, f->fflags, archive_entry_symlink(ae));
482 : :
483 : 36 : return (EPKG_OK);
484 : 36 : }
485 : :
486 : : static int
487 : 19 : create_hardlink(struct pkg *pkg, struct pkg_file *f, const char *path)
488 : : {
489 : 19 : bool tried_mkdir = false;
490 : : struct pkg_file *fh;
491 : :
492 : 19 : pkg_hidden_tempfile(f->temppath, sizeof(f->temppath), f->path);
493 : 19 : fh = pkg_get_file(pkg, path);
494 [ + - ]: 19 : if (fh == NULL) {
495 : 0 : pkg_emit_error("Can't find the file %s is supposed to be"
496 : 0 : " hardlinked to %s", f->path, path);
497 : 0 : return (EPKG_FATAL);
498 : : }
499 : :
500 : :
501 : : retry:
502 [ + - + - : 57 : if (linkat(pkg->rootfd, RELATIVE_PATH(fh->temppath),
+ - ]
503 : 38 : pkg->rootfd, RELATIVE_PATH(f->temppath), 0) == -1) {
504 [ # # ]: 0 : if (!tried_mkdir) {
505 [ # # ]: 0 : if (!try_mkdir(pkg->rootfd, f->path))
506 : 0 : return (EPKG_FATAL);
507 : 0 : tried_mkdir = true;
508 : 0 : goto retry;
509 : : }
510 : :
511 : 0 : pkg_fatal_errno("Fail to create hardlink: %s", f->temppath);
512 : 0 : }
513 : :
514 : 19 : return (EPKG_OK);
515 : 19 : }
516 : :
517 : : static int
518 : 15 : do_extract_hardlink(struct pkg *pkg, struct archive *a __unused, struct archive_entry *ae,
519 : : const char *path, struct pkg *local __unused)
520 : : {
521 : : struct pkg_file *f;
522 : : const struct stat *aest;
523 : : const char *lp;
524 : :
525 : 15 : f = pkg_get_file(pkg, path);
526 [ + - ]: 15 : if (f == NULL) {
527 : 0 : pkg_emit_error("Hardlink %s not specified in the manifest", path);
528 : 0 : return (EPKG_FATAL);
529 : : }
530 : 15 : lp = archive_entry_hardlink(ae);
531 : 15 : aest = archive_entry_stat(ae);
532 : :
533 [ - + ]: 15 : if (create_hardlink(pkg, f, lp) == EPKG_FATAL)
534 : 0 : return (EPKG_FATAL);
535 : :
536 : 30 : metalog_add(PKG_METALOG_FILE, RELATIVE_PATH(path),
537 : 15 : archive_entry_uname(ae), archive_entry_gname(ae),
538 : 15 : aest->st_mode & ~S_IFREG, 0, NULL);
539 : :
540 : 15 : return (EPKG_OK);
541 : 15 : }
542 : :
543 : : static int
544 : 464 : create_regfile(struct pkg *pkg, struct pkg_file *f, struct archive *a,
545 : : struct archive_entry *ae, int fromfd, struct pkg *local)
546 : : {
547 : 464 : int fd = -1;
548 : 464 : bool tried_mkdir = false;
549 : : size_t len;
550 : : char buf[32768];
551 : :
552 : 464 : pkg_hidden_tempfile(f->temppath, sizeof(f->temppath), f->path);
553 : :
554 : : retry:
555 : : /* Create the new temp file */
556 : 1104 : fd = openat(pkg->rootfd, RELATIVE_PATH(f->temppath),
557 : 552 : O_CREAT|O_WRONLY|O_EXCL, f->perm);
558 [ + + ]: 552 : if (fd == -1) {
559 [ + - ]: 88 : if (!tried_mkdir) {
560 [ + - ]: 88 : if (!try_mkdir(pkg->rootfd, f->path))
561 : 0 : return (EPKG_FATAL);
562 : 88 : tried_mkdir = true;
563 : 88 : goto retry;
564 : : }
565 : 0 : pkg_fatal_errno("Fail to create temporary file: %s",
566 : : f->temppath);
567 : 0 : }
568 : :
569 [ + + ]: 464 : if (fromfd == -1) {
570 : : /* check if this is a config file */
571 : 452 : f->config = pkghash_get_value(pkg->config_files_hash, f->path);
572 [ + + ]: 452 : if (f->config) {
573 : : const char *cfdata;
574 : 42 : bool merge = pkg_object_bool(pkg_config_get("AUTOMERGE"));
575 : :
576 : 42 : pkg_debug(1, "Populating config_file %s", f->path);
577 : 42 : len = archive_entry_size(ae);
578 : 42 : f->config->content = xmalloc(len + 1);
579 : 42 : archive_read_data(a, f->config->content, len);
580 : 42 : f->config->content[len] = '\0';
581 : 42 : cfdata = f->config->content;
582 : 42 : attempt_to_merge(pkg->rootfd, f->config, local, merge);
583 [ + + ]: 42 : if (f->config->status == MERGE_SUCCESS)
584 : 12 : cfdata = f->config->newcontent;
585 : 42 : dprintf(fd, "%s", cfdata);
586 [ + + ]: 42 : if (f->config->newcontent != NULL)
587 : 12 : free(f->config->newcontent);
588 : 42 : } else {
589 [ - + ]: 410 : if (ftruncate(fd, archive_entry_size(ae)) == -1) {
590 : 0 : pkg_errno("Fail to truncate file: %s", f->temppath);
591 : 0 : }
592 : : }
593 : :
594 [ + + + - ]: 452 : if (!f->config && archive_read_data_into_fd(a, fd) != ARCHIVE_OK) {
595 : 0 : pkg_emit_error("Fail to extract %s from package: %s",
596 : 0 : f->path, archive_error_string(a));
597 : 0 : return (EPKG_FATAL);
598 : : }
599 : 452 : } else {
600 [ + + ]: 20 : while ((len = read(fromfd, buf, sizeof(buf))) > 0)
601 [ + - ]: 8 : if (write(fd, buf, len) == -1) {
602 : 0 : pkg_errno("Fail to write file: %s", f->temppath);
603 : 0 : }
604 : : }
605 [ - + ]: 464 : if (fd != -1) {
606 : 464 : close(fd);
607 : 464 : }
608 : :
609 [ - + - + : 1392 : if (set_attrsat(pkg->rootfd, f->temppath, f->perm, f->uid, f->gid,
- + ]
610 : 928 : &f->time[0], &f->time[1]) != EPKG_OK)
611 : 0 : return (EPKG_FATAL);
612 : :
613 : 464 : return (EPKG_OK);
614 : 464 : }
615 : :
616 : : static int
617 : 452 : do_extract_regfile(struct pkg *pkg, struct archive *a, struct archive_entry *ae,
618 : : const char *path, struct pkg *local)
619 : : {
620 : : struct pkg_file *f;
621 : : const struct stat *aest;
622 : : unsigned long clear;
623 : :
624 : 452 : f = pkg_get_file(pkg, path);
625 [ + - ]: 452 : if (f == NULL) {
626 : 0 : pkg_emit_error("File %s not specified in the manifest", path);
627 : 0 : return (EPKG_FATAL);
628 : : }
629 : :
630 : 452 : aest = archive_entry_stat(ae);
631 : 452 : archive_entry_fflags(ae, &f->fflags, &clear);
632 : 452 : f->perm = aest->st_mode;
633 : 452 : f->uid = get_uid_from_archive(ae);
634 : 452 : f->gid = get_gid_from_archive(ae);
635 : 452 : fill_timespec_buf(aest, f->time);
636 : 452 : archive_entry_fflags(ae, &f->fflags, &clear);
637 : :
638 [ - + ]: 452 : if (create_regfile(pkg, f, a, ae, -1, local) == EPKG_FATAL)
639 : 0 : return (EPKG_FATAL);
640 : :
641 : 904 : metalog_add(PKG_METALOG_FILE, RELATIVE_PATH(path),
642 : 452 : archive_entry_uname(ae), archive_entry_gname(ae),
643 : 452 : aest->st_mode & ~S_IFREG, f->fflags, NULL);
644 : :
645 : 452 : return (EPKG_OK);
646 : 452 : }
647 : :
648 : : static int
649 : 440 : do_extract(struct archive *a, struct archive_entry *ae,
650 : : int nfiles, struct pkg *pkg, struct pkg *local)
651 : : {
652 : 440 : int retcode = EPKG_OK;
653 : 440 : int ret = 0, cur_file = 0;
654 : : char path[MAXPATHLEN];
655 : : int (*extract_cb)(struct pkg *pkg, struct archive *a,
656 : : struct archive_entry *ae, const char *path, struct pkg *local);
657 : :
658 : : #ifndef HAVE_ARC4RANDOM
659 : : srand(time(NULL));
660 : : #endif
661 : :
662 [ + - ]: 440 : if (nfiles == 0)
663 : 0 : return (EPKG_OK);
664 : :
665 : 440 : pkg_emit_extract_begin(pkg);
666 : 440 : pkg_emit_progress_start(NULL);
667 : :
668 : 440 : do {
669 : 527 : pkg_absolutepath(archive_entry_pathname(ae), path, sizeof(path), true);
670 [ - + - + ]: 1054 : if (match_ucl_lists(path,
671 : 527 : pkg_config_get("FILES_IGNORE_GLOB"),
672 : 527 : pkg_config_get("FILES_IGNORE_REGEX")))
673 : 0 : continue;
674 [ + + + + : 527 : switch (archive_entry_filetype(ae)) {
- - - - -
- ]
675 : : case AE_IFDIR:
676 : 24 : extract_cb = do_extract_dir;
677 : 24 : break;
678 : : case AE_IFLNK:
679 : 36 : extract_cb = do_extract_symlink;
680 : 36 : break;
681 : : case 0: /* HARDLINKS */
682 : 15 : extract_cb = do_extract_hardlink;
683 : 15 : break;
684 : : case AE_IFREG:
685 : 452 : extract_cb = do_extract_regfile;
686 : 452 : break;
687 : : case AE_IFMT:
688 : 0 : pkg_emit_error("Archive contains an unsupported filetype (AE_IFMT): %s", path);
689 : 0 : retcode = EPKG_FATAL;
690 : 0 : goto cleanup;
691 : : break;
692 : : case AE_IFSOCK:
693 : 0 : pkg_emit_error("Archive contains an unsupported filetype (AE_IFSOCK): %s", path);
694 : 0 : retcode = EPKG_FATAL;
695 : 0 : goto cleanup;
696 : : break;
697 : : case AE_IFCHR:
698 : 0 : pkg_emit_error("Archive contains an unsupported filetype (AE_IFCHR): %s", path);
699 : 0 : retcode = EPKG_FATAL;
700 : 0 : goto cleanup;
701 : : break;
702 : : case AE_IFIFO:
703 : 0 : pkg_emit_error("Archive contains an unsupported filetype (AE_IFIFO): %s", path);
704 : 0 : retcode = EPKG_FATAL;
705 : 0 : goto cleanup;
706 : : break;
707 : : case AE_IFBLK:
708 : 0 : pkg_emit_error("Archive contains an unsupported filetype (AE_IFBLK): %s", path);
709 : 0 : retcode = EPKG_FATAL;
710 : 0 : goto cleanup;
711 : : break;
712 : : default:
713 : 0 : pkg_emit_error("Archive contains an unsupported filetype (%d): %s", archive_entry_filetype(ae), path);
714 : 0 : retcode = EPKG_FATAL;
715 : 0 : goto cleanup;
716 : : break;
717 : : }
718 : :
719 [ + - ]: 527 : if (extract_cb(pkg, a, ae, path, local) != EPKG_OK) {
720 : 0 : retcode = EPKG_FATAL;
721 : 0 : goto cleanup;
722 : : }
723 [ + + ]: 527 : if (archive_entry_filetype(ae) != AE_IFDIR) {
724 : 503 : pkg_emit_progress_tick(cur_file++, nfiles);
725 : 503 : }
726 [ + + ]: 527 : } while ((ret = archive_read_next_header(a, &ae)) == ARCHIVE_OK);
727 : 440 : pkg_emit_progress_tick(cur_file++, nfiles);
728 : :
729 [ + - ]: 440 : if (ret != ARCHIVE_EOF) {
730 : 0 : pkg_emit_error("archive_read_next_header(): %s",
731 : 0 : archive_error_string(a));
732 : 0 : retcode = EPKG_FATAL;
733 : 0 : }
734 : :
735 : : cleanup:
736 : 440 : pkg_emit_progress_tick(nfiles, nfiles);
737 : 440 : pkg_emit_extract_finished(pkg);
738 : :
739 : 440 : return (retcode);
740 : 440 : }
741 : :
742 : : static int
743 : 711 : pkg_extract_finalize(struct pkg *pkg)
744 : : {
745 : : struct stat st;
746 : 711 : struct pkg_file *f = NULL;
747 : 711 : struct pkg_dir *d = NULL;
748 : : char path[MAXPATHLEN + 8];
749 : : const char *fto;
750 : : #ifdef HAVE_CHFLAGSAT
751 : : bool install_as_user;
752 : :
753 : 711 : install_as_user = (getenv("INSTALL_AS_USER") != NULL);
754 : : #endif
755 : :
756 [ + + ]: 1242 : while (pkg_files(pkg, &f) == EPKG_OK) {
757 : :
758 [ - + - + ]: 1062 : if (match_ucl_lists(f->path,
759 : 531 : pkg_config_get("FILES_IGNORE_GLOB"),
760 : 531 : pkg_config_get("FILES_IGNORE_REGEX")))
761 : 0 : continue;
762 : 531 : append_touched_file(f->path);
763 [ + - ]: 531 : if (*f->temppath == '\0')
764 : 0 : continue;
765 : 531 : fto = f->path;
766 [ + + + + : 531 : if (f->config && f->config->status == MERGE_FAILED &&
+ + ]
767 : 10 : f->previous != PKG_FILE_NONE) {
768 : 5 : snprintf(path, sizeof(path), "%s.pkgnew", f->path);
769 : 5 : fto = path;
770 : 5 : }
771 : :
772 [ + + + + ]: 531 : if (f->config && f->config->status == MERGE_NOT_LOCAL) {
773 : 5 : snprintf(path, sizeof(path), "%s.pkgsave", f->path);
774 [ + - + - : 15 : if (renameat(pkg->rootfd, RELATIVE_PATH(fto),
+ - ]
775 : 10 : pkg->rootfd, RELATIVE_PATH(path)) == -1) {
776 : 0 : pkg_fatal_errno("Fail to rename %s -> %s",
777 : : fto, path);
778 : 0 : }
779 : 5 : }
780 : :
781 : : /*
782 : : * enforce an unlink of the file to workaround a bug that
783 : : * results in renameat returning 0 of the from file is hardlink
784 : : * on the to file, but the to file is not removed
785 : : */
786 [ + + + + ]: 531 : if (f->previous != PKG_FILE_NONE &&
787 : 280 : fstatat(pkg->rootfd, RELATIVE_PATH(fto), &st,
788 : 280 : AT_SYMLINK_NOFOLLOW) != -1) {
789 : : #ifdef HAVE_CHFLAGSAT
790 [ - + # # ]: 270 : if (!install_as_user && st.st_flags & NOCHANGESFLAGS) {
791 : 0 : chflagsat(pkg->rootfd, RELATIVE_PATH(fto), 0,
792 : : AT_SYMLINK_NOFOLLOW);
793 : 0 : }
794 : : #endif
795 : : /* if the files does not belong to any package, we do save it */
796 [ + + ]: 270 : if (f->previous == PKG_FILE_SAVE) {
797 : 228 : snprintf(path, sizeof(path), "%s.pkgsave", f->path);
798 : 456 : renameat(pkg->rootfd, RELATIVE_PATH(fto),
799 : 228 : pkg->rootfd, RELATIVE_PATH(path));
800 : 228 : }
801 : 270 : unlinkat(pkg->rootfd, RELATIVE_PATH(fto), 0);
802 : 270 : }
803 [ + - + - : 1593 : if (renameat(pkg->rootfd, RELATIVE_PATH(f->temppath),
+ - ]
804 : 1062 : pkg->rootfd, RELATIVE_PATH(fto)) == -1) {
805 : 0 : pkg_fatal_errno("Fail to rename %s -> %s",
806 : : f->temppath, fto);
807 : 0 : }
808 : :
809 : : #ifdef HAVE_CHFLAGSAT
810 [ - + # # ]: 531 : if (!install_as_user && f->fflags != 0) {
811 [ # # # # : 0 : if (chflagsat(pkg->rootfd, RELATIVE_PATH(fto),
# # ]
812 : 0 : f->fflags, AT_SYMLINK_NOFOLLOW) == -1) {
813 : 0 : pkg_fatal_errno("Fail to chflags %s", fto);
814 : 0 : }
815 : 0 : }
816 : : #endif
817 : : }
818 : :
819 [ + + ]: 735 : while (pkg_dirs(pkg, &d) == EPKG_OK) {
820 [ + + ]: 24 : if (d->noattrs)
821 : 12 : continue;
822 [ + - + - : 36 : if (set_attrsat(pkg->rootfd, d->path, d->perm,
+ - ]
823 : 24 : d->uid, d->gid, &d->time[0], &d->time[1]) != EPKG_OK)
824 : 0 : return (EPKG_FATAL);
825 : : }
826 : :
827 : 711 : return (EPKG_OK);
828 : 711 : }
829 : :
830 : : static char *
831 : 78 : pkg_globmatch(char *pattern, const char *name)
832 : : {
833 : : glob_t g;
834 : : int i;
835 : : char *buf, *buf2;
836 : 78 : char *path = NULL;
837 : :
838 [ + + ]: 78 : if (glob(pattern, 0, NULL, &g) == GLOB_NOMATCH) {
839 : 39 : globfree(&g);
840 : :
841 : 39 : return (NULL);
842 : : }
843 : :
844 [ + + ]: 78 : for (i = 0; i < g.gl_pathc; i++) {
845 : : /* the version starts here */
846 : 39 : buf = strrchr(g.gl_pathv[i], '-');
847 [ + - ]: 39 : if (buf == NULL)
848 : 0 : continue;
849 : 39 : buf2 = strrchr(g.gl_pathv[i], '/');
850 [ + - ]: 39 : if (buf2 == NULL)
851 : 0 : buf2 = g.gl_pathv[i];
852 : : else
853 : 39 : buf2++;
854 : : /* ensure we have match the proper name */
855 [ - + ]: 39 : if (strncmp(buf2, name, buf - buf2) != 0)
856 : 0 : continue;
857 [ - + ]: 39 : if (path == NULL) {
858 : 39 : path = g.gl_pathv[i];
859 : 39 : continue;
860 : : }
861 [ # # ]: 0 : if (pkg_version_cmp(path, g.gl_pathv[i]) == 1)
862 : 0 : path = g.gl_pathv[i];
863 : 0 : }
864 [ + - ]: 39 : if (path)
865 : 39 : path = xstrdup(path);
866 : 39 : globfree(&g);
867 : :
868 : 39 : return (path);
869 : 78 : }
870 : :
871 : : static int
872 : 203 : pkg_add_check_pkg_archive(struct pkgdb *db, struct pkg *pkg,
873 : : const char *path, int flags,
874 : : struct pkg_manifest_key *keys, const char *location)
875 : : {
876 : : const char *arch;
877 : : int ret, retcode;
878 : 203 : struct pkg_dep *dep = NULL;
879 : 203 : char bd[MAXPATHLEN], *basedir = NULL;
880 : : char dpath[MAXPATHLEN], *ppath;
881 : 203 : const char *ext = NULL;
882 : 203 : struct pkg *pkg_inst = NULL;
883 : : bool fromstdin;
884 : :
885 [ - + ]: 203 : arch = pkg->abi != NULL ? pkg->abi : pkg->arch;
886 : :
887 [ - + # # ]: 203 : if (!is_valid_abi(arch, true) && (flags & PKG_ADD_FORCE) == 0) {
888 : 0 : return (EPKG_FATAL);
889 : : }
890 : :
891 [ - + # # ]: 203 : if (!is_valid_os_version(pkg) && (flags & PKG_ADD_FORCE) == 0) {
892 : 0 : return (EPKG_FATAL);
893 : : }
894 : :
895 : : /* XX check */
896 : 203 : ret = pkg_try_installed(db, pkg->name, &pkg_inst, PKG_LOAD_BASIC);
897 [ + - ]: 203 : if (ret == EPKG_OK) {
898 [ # # ]: 0 : if ((flags & PKG_ADD_FORCE) == 0) {
899 : 0 : pkg_emit_already_installed(pkg_inst);
900 : 0 : pkg_free(pkg_inst);
901 : 0 : pkg_inst = NULL;
902 : 0 : return (EPKG_INSTALLED);
903 : : }
904 [ # # ]: 0 : if (pkg_inst->locked) {
905 : 0 : pkg_emit_locked(pkg_inst);
906 : 0 : pkg_free(pkg_inst);
907 : 0 : pkg_inst = NULL;
908 : 0 : return (EPKG_LOCKED);
909 : : }
910 : 0 : pkg_emit_notice("package %s is already installed, forced "
911 : 0 : "install", pkg->name);
912 : 0 : pkg_free(pkg_inst);
913 : 0 : pkg_inst = NULL;
914 [ - + ]: 203 : } else if (ret != EPKG_END) {
915 : 0 : return (ret);
916 : : }
917 : :
918 : : /*
919 : : * Check for dependencies by searching the same directory as
920 : : * the package archive we're reading. Of course, if we're
921 : : * reading from a file descriptor or a unix domain socket or
922 : : * whatever, there's no valid directory to search.
923 : : */
924 : 203 : fromstdin = (strcmp(path, "-") == 0);
925 : 203 : strlcpy(bd, path, sizeof(bd));
926 [ + + ]: 203 : if (!fromstdin) {
927 : 164 : basedir = get_dirname(bd);
928 : 164 : strlcpy(bd, basedir, sizeof(bd));
929 [ + - ]: 164 : if ((ext = strrchr(path, '.')) == NULL) {
930 : 0 : pkg_emit_error("%s has no extension", path);
931 : 0 : return (EPKG_FATAL);
932 : : }
933 : 164 : }
934 : :
935 : 203 : retcode = EPKG_FATAL;
936 : 203 : pkg_emit_add_deps_begin(pkg);
937 : :
938 [ + + ]: 272 : while (pkg_deps(pkg, &dep) == EPKG_OK) {
939 : 108 : dpath[0] = '\0';
940 : :
941 [ + - ]: 108 : if (pkg_is_installed(db, dep->name) == EPKG_OK)
942 : 0 : continue;
943 : :
944 [ + + ]: 108 : if (fromstdin) {
945 : 26 : pkg_emit_missing_dep(pkg, dep);
946 [ + + ]: 26 : if ((flags & PKG_ADD_FORCE_MISSING) == 0)
947 : 13 : goto cleanup;
948 : 13 : continue;
949 : : }
950 : :
951 [ + + - + ]: 82 : if (dep->version != NULL && dep->version[0] != '\0') {
952 : 86 : snprintf(dpath, sizeof(dpath), "%s/%s-%s%s", bd,
953 : 43 : dep->name, dep->version, ext);
954 : 43 : }
955 : :
956 [ + + + + ]: 82 : if (strlen(dpath) == 0 || access(dpath, F_OK) != 0) {
957 : 156 : snprintf(dpath, sizeof(dpath), "%s/%s-*%s", bd,
958 : 78 : dep->name, ext);
959 : 78 : ppath = pkg_globmatch(dpath, dep->name);
960 [ + + ]: 78 : if (ppath == NULL) {
961 : 39 : pkg_emit_missing_dep(pkg, dep);
962 [ + + ]: 39 : if ((flags & PKG_ADD_FORCE_MISSING) == 0)
963 : 26 : goto cleanup;
964 : 13 : continue;
965 : : }
966 : 39 : strlcpy(dpath, ppath, sizeof(dpath));
967 : 39 : free(ppath);
968 : 39 : }
969 : :
970 [ + - - + ]: 43 : if ((flags & PKG_ADD_UPGRADE) == 0 &&
971 : 43 : access(dpath, F_OK) == 0) {
972 : 86 : ret = pkg_add(db, dpath, PKG_ADD_AUTOMATIC,
973 : 43 : keys, location);
974 : :
975 [ + - ]: 43 : if (ret != EPKG_OK)
976 : 0 : goto cleanup;
977 : 43 : } else {
978 : 0 : pkg_emit_missing_dep(pkg, dep);
979 [ # # ]: 0 : if ((flags & PKG_ADD_FORCE_MISSING) == 0)
980 : 0 : goto cleanup;
981 : : }
982 : : }
983 : :
984 : 164 : retcode = EPKG_OK;
985 : : cleanup:
986 : 203 : pkg_emit_add_deps_finished(pkg);
987 : :
988 : 203 : return (retcode);
989 : 203 : }
990 : :
991 : : static int
992 : 132 : pkg_add_cleanup_old(struct pkgdb *db, struct pkg *old, struct pkg *new, int flags)
993 : : {
994 : : struct pkg_file *f;
995 : 132 : int ret = EPKG_OK;
996 : :
997 : 132 : pkg_start_stop_rc_scripts(old, PKG_RC_STOP);
998 : :
999 : : /*
1000 : : * Execute pre deinstall scripts
1001 : : */
1002 [ - + ]: 132 : if ((flags & PKG_ADD_NOSCRIPT) == 0) {
1003 : 132 : ret = pkg_lua_script_run(old, PKG_LUA_PRE_DEINSTALL, (old != NULL));
1004 [ - + # # ]: 132 : if (ret != EPKG_OK && ctx.developer_mode) {
1005 : 0 : return (ret);
1006 : : } else {
1007 : 132 : ret = pkg_script_run(old, PKG_SCRIPT_PRE_DEINSTALL, (old != NULL));
1008 [ - + # # ]: 132 : if (ret != EPKG_OK && ctx.developer_mode) {
1009 : 0 : return (ret);
1010 : : } else {
1011 : 132 : ret = EPKG_OK;
1012 : : }
1013 : : }
1014 : 132 : }
1015 : :
1016 : : /* Now remove files that no longer exist in the new package */
1017 [ + - ]: 132 : if (new != NULL) {
1018 : 132 : f = NULL;
1019 [ + + ]: 188 : while (pkg_files(old, &f) == EPKG_OK) {
1020 [ + + - + : 56 : if (!pkg_has_file(new, f->path) || match_ucl_lists(f->path,
- + ]
1021 : 48 : pkg_config_get("FILES_IGNORE_GLOB"),
1022 : 48 : pkg_config_get("FILES_IGNORE_REGEX"))) {
1023 : 8 : pkg_debug(2, "File %s is not in the new package", f->path);
1024 [ - + ]: 8 : if (ctx.backup_libraries) {
1025 : : const char *libname;
1026 : 8 : libname = strrchr(f->path, '/');
1027 [ + - - + ]: 8 : if (libname != NULL &&
1028 : 8 : pkghash_get(old->shlibs_provided, libname+1) != NULL) {
1029 : 8 : backup_library(db, old, f->path);
1030 : 8 : }
1031 : 8 : }
1032 : 8 : pkg_delete_file(old, f, flags & PKG_DELETE_FORCE ? 1 : 0);
1033 : 8 : }
1034 : : }
1035 : :
1036 : 132 : pkg_delete_dirs(db, old, new);
1037 : 132 : }
1038 : :
1039 : 132 : return (ret);
1040 : 132 : }
1041 : :
1042 : : void
1043 : 0 : pkg_rollback_pkg(struct pkg *p)
1044 : : {
1045 : 0 : struct pkg_file *f = NULL;
1046 : :
1047 [ # # ]: 0 : while (pkg_files(p, &f) == EPKG_OK) {
1048 [ # # # # ]: 0 : if (match_ucl_lists(f->path,
1049 : 0 : pkg_config_get("FILES_IGNORE_GLOB"),
1050 : 0 : pkg_config_get("FILES_IGNORE_REGEX")))
1051 : 0 : continue;
1052 [ # # ]: 0 : if (*f->temppath != '\0') {
1053 : 0 : unlinkat(p->rootfd, f->temppath, 0);
1054 : 0 : }
1055 : : }
1056 : 0 : }
1057 : :
1058 : : void
1059 : 0 : pkg_rollback_cb(void *data)
1060 : : {
1061 : 0 : pkg_rollback_pkg((struct pkg *)data);
1062 : 0 : }
1063 : :
1064 : : int
1065 : 160 : pkg_add_triggers(void)
1066 : : {
1067 : 160 : return (triggers_execute(NULL));
1068 : : }
1069 : :
1070 : : static int
1071 : 742 : pkg_add_common(struct pkgdb *db, const char *path, unsigned flags,
1072 : : struct pkg_manifest_key *keys, const char *reloc, struct pkg *remote,
1073 : : struct pkg *local)
1074 : : {
1075 : : struct archive *a;
1076 : : struct archive_entry *ae;
1077 : 742 : struct pkg *pkg = NULL;
1078 : 742 : xstring *message = NULL;
1079 : : struct pkg_message *msg;
1080 : : struct pkg_file *f;
1081 : : const char *msgstr;
1082 : 742 : bool extract = true;
1083 : 742 : int retcode = EPKG_OK;
1084 : : int ret;
1085 : : int nfiles;
1086 : :
1087 [ + - ]: 742 : assert(path != NULL);
1088 : :
1089 [ + + ]: 742 : if (local != NULL)
1090 : 132 : flags |= PKG_ADD_UPGRADE;
1091 : :
1092 : : /*
1093 : : * Open the package archive file, read all the meta files and set the
1094 : : * current archive_entry to the first non-meta file.
1095 : : * If there is no non-meta files, EPKG_END is returned.
1096 : : */
1097 : 742 : ret = pkg_open2(&pkg, &a, &ae, path, keys, 0, -1);
1098 [ + + ]: 742 : if (ret == EPKG_END)
1099 : 276 : extract = false;
1100 [ - + ]: 466 : else if (ret != EPKG_OK) {
1101 : 0 : retcode = ret;
1102 : 0 : goto cleanup;
1103 : : }
1104 [ + + ]: 742 : if ((flags & PKG_ADD_SPLITTED_UPGRADE) != PKG_ADD_SPLITTED_UPGRADE)
1105 : 734 : pkg_emit_new_action();
1106 [ + + ]: 742 : if ((flags & PKG_ADD_UPGRADE) == 0)
1107 : 203 : pkg_emit_install_begin(pkg);
1108 : : else {
1109 [ + + ]: 539 : if (local != NULL)
1110 : 132 : pkg_emit_upgrade_begin(pkg, local);
1111 : : else
1112 : 407 : pkg_emit_install_begin(pkg);
1113 : : }
1114 : :
1115 [ - + ]: 742 : if (pkg_is_valid(pkg) != EPKG_OK) {
1116 : 0 : pkg_emit_error("the package is not valid");
1117 : 0 : return (EPKG_FATAL);
1118 : : }
1119 : :
1120 [ + + ]: 742 : if (flags & PKG_ADD_AUTOMATIC)
1121 : 164 : pkg->automatic = true;
1122 : :
1123 : : /*
1124 : : * Additional checks for non-remote package
1125 : : */
1126 [ + + ]: 742 : if (remote == NULL) {
1127 : 406 : ret = pkg_add_check_pkg_archive(db, pkg, path, flags, keys,
1128 : 203 : reloc);
1129 [ + + ]: 203 : if (ret != EPKG_OK) {
1130 : : /* Do not return error on installed package */
1131 [ - + ]: 39 : retcode = (ret == EPKG_INSTALLED ? EPKG_OK : ret);
1132 : 39 : goto cleanup;
1133 : : }
1134 : 164 : }
1135 : : else {
1136 [ + + ]: 539 : if (remote->repo != NULL) {
1137 : : /* Save reponame */
1138 : 327 : pkg_kv_add(&pkg->annotations, "repository", remote->repo->name, "annotation");
1139 : 327 : pkg_kv_add(&pkg->annotations, "repo_type", remote->repo->ops->type, "annotation");
1140 : 327 : }
1141 : :
1142 : 539 : free(pkg->digest);
1143 : 539 : pkg->digest = xstrdup(remote->digest);
1144 : : /* only preserve flags if -A has not been passed */
1145 [ + + ]: 539 : if ((flags & PKG_ADD_AUTOMATIC) == 0)
1146 : 431 : pkg->automatic = remote->automatic;
1147 : : }
1148 : :
1149 [ - + ]: 703 : if (reloc != NULL)
1150 : 0 : pkg_kv_add(&pkg->annotations, "relocated", reloc, "annotation");
1151 : :
1152 : 703 : pkg_open_root_fd(pkg);
1153 : : /* analyse previous files */
1154 : 703 : f = NULL;
1155 [ + + ]: 1206 : while (pkg_files(pkg, &f) == EPKG_OK) {
1156 [ - + - + ]: 1006 : if (match_ucl_lists(f->path,
1157 : 503 : pkg_config_get("FILES_IGNORE_GLOB"),
1158 : 503 : pkg_config_get("FILES_IGNORE_REGEX"))) {
1159 : 0 : continue;
1160 : : }
1161 [ + + ]: 503 : if (faccessat(pkg->rootfd, RELATIVE_PATH(f->path), F_OK, 0) == 0) {
1162 : 280 : f->previous = PKG_FILE_EXIST;
1163 [ + + ]: 280 : if (!pkgdb_file_exists(db, f->path)) {
1164 : 233 : f->previous = PKG_FILE_SAVE;
1165 : 233 : }
1166 : 280 : }
1167 : : }
1168 : :
1169 : : /* register the package before installing it in case there are
1170 : : * problems that could be caught here. */
1171 : 703 : retcode = pkgdb_register_pkg(db, pkg, flags & PKG_ADD_FORCE, NULL);
1172 : :
1173 [ - + ]: 703 : if (retcode != EPKG_OK)
1174 : 0 : goto cleanup;
1175 : :
1176 : : /*
1177 : : * Execute pre-install scripts
1178 : : */
1179 [ + + ]: 703 : if ((flags & PKG_ADD_NOSCRIPT) == 0) {
1180 [ - + ]: 690 : if ((retcode = pkg_lua_script_run(pkg, PKG_LUA_PRE_INSTALL, (local != NULL))) != EPKG_OK)
1181 : 0 : goto cleanup;
1182 [ + + ]: 690 : if ((retcode = pkg_script_run(pkg, PKG_SCRIPT_PRE_INSTALL, (local != NULL))) != EPKG_OK)
1183 : 4 : goto cleanup;
1184 : 686 : }
1185 : :
1186 : :
1187 : : /* add the user and group if necessary */
1188 : :
1189 : 699 : nfiles = pkghash_count(pkg->filehash) + pkghash_count(pkg->dirhash);
1190 : : /*
1191 : : * Extract the files on disk.
1192 : : */
1193 [ + + ]: 699 : if (extract) {
1194 : 440 : pkg_register_cleanup_callback(pkg_rollback_cb, pkg);
1195 : 440 : retcode = do_extract(a, ae, nfiles, pkg, local);
1196 : 440 : pkg_unregister_cleanup_callback(pkg_rollback_cb, pkg);
1197 [ - + ]: 440 : if (retcode != EPKG_OK) {
1198 : : /* If the add failed, clean up (silently) */
1199 : :
1200 : 0 : pkg_rollback_pkg(pkg);
1201 : 0 : pkg_delete_dirs(db, pkg, NULL);
1202 : 0 : pkgdb_register_finale(db, retcode, NULL);
1203 : 0 : goto cleanup;
1204 : : }
1205 : 440 : }
1206 : :
1207 [ + + ]: 699 : if (local != NULL) {
1208 : 132 : pkg_open_root_fd(local);
1209 : 132 : pkg_debug(1, "Cleaning up old version");
1210 [ - + ]: 132 : if (pkg_add_cleanup_old(db, local, pkg, flags) != EPKG_OK) {
1211 : 0 : retcode = EPKG_FATAL;
1212 : 0 : goto cleanup;
1213 : : }
1214 : 132 : }
1215 : :
1216 : :
1217 : : /* Update configuration file content with db with newer versions */
1218 : 699 : pkgdb_update_config_file_content(pkg, db->sqlite);
1219 : :
1220 : 699 : retcode = pkg_extract_finalize(pkg);
1221 : :
1222 : 699 : pkgdb_register_finale(db, retcode, NULL);
1223 : : /*
1224 : : * Execute post install scripts
1225 : : */
1226 : :
1227 [ - + ]: 699 : if (retcode != EPKG_OK)
1228 : 0 : goto cleanup;
1229 [ + + ]: 699 : if ((flags & PKG_ADD_NOSCRIPT) == 0) {
1230 : 686 : pkg_lua_script_run(pkg, PKG_LUA_POST_INSTALL, (local != NULL));
1231 : 686 : pkg_script_run(pkg, PKG_SCRIPT_POST_INSTALL, (local != NULL));
1232 : 686 : }
1233 : :
1234 : : /*
1235 : : * start the different related services if the users do want that
1236 : : * and that the service is running
1237 : : */
1238 : :
1239 : 699 : pkg_start_stop_rc_scripts(pkg, PKG_RC_START);
1240 : :
1241 [ + + ]: 699 : if ((flags & PKG_ADD_UPGRADE) == 0)
1242 : 164 : pkg_emit_install_finished(pkg, local);
1243 : : else {
1244 [ + + ]: 535 : if (local != NULL)
1245 : 132 : pkg_emit_upgrade_finished(pkg, local);
1246 : : else
1247 : 403 : pkg_emit_install_finished(pkg, local);
1248 : : }
1249 : :
1250 [ + + ]: 783 : LL_FOREACH(pkg->message, msg) {
1251 : 84 : msgstr = NULL;
1252 [ + + ]: 84 : if (msg->type == PKG_MESSAGE_ALWAYS) {
1253 : 12 : msgstr = msg->str;
1254 [ + - + + ]: 84 : } else if (local != NULL &&
1255 : 72 : msg->type == PKG_MESSAGE_UPGRADE) {
1256 [ + + + + ]: 48 : if (msg->maximum_version == NULL &&
1257 : 24 : msg->minimum_version == NULL) {
1258 : 12 : msgstr = msg->str;
1259 [ + + ]: 48 : } else if (msg->maximum_version == NULL) {
1260 [ + + ]: 12 : if (pkg_version_cmp(local->version, msg->minimum_version) == 1) {
1261 : 8 : msgstr = msg->str;
1262 : 8 : }
1263 [ + + ]: 36 : } else if (msg->minimum_version == NULL) {
1264 [ + + ]: 12 : if (pkg_version_cmp(local->version, msg->maximum_version) == -1) {
1265 : 4 : msgstr = msg->str;
1266 : 4 : }
1267 [ + + + + ]: 24 : } else if (pkg_version_cmp(local->version, msg->maximum_version) == -1 &&
1268 : 8 : pkg_version_cmp(local->version, msg->minimum_version) == 1) {
1269 : 4 : msgstr = msg->str;
1270 : 4 : }
1271 [ - + # # ]: 72 : } else if (local == NULL &&
1272 : 0 : msg->type == PKG_MESSAGE_INSTALL) {
1273 : 0 : msgstr = msg->str;
1274 : 0 : }
1275 [ + + ]: 84 : if (msgstr != NULL) {
1276 [ + + ]: 40 : if (message == NULL) {
1277 : 12 : message = xstring_new();
1278 : 24 : pkg_fprintf(message->fp, "=====\nMessage from "
1279 : 12 : "%n-%v:\n\n", pkg, pkg);
1280 : 12 : }
1281 : 40 : fprintf(message->fp, "--\n%s\n", msgstr);
1282 : 40 : }
1283 : 84 : }
1284 [ + + + - ]: 711 : if (pkg->message != NULL && message != NULL) {
1285 : 12 : fflush(message->fp);
1286 : 12 : pkg_emit_message(message->buf);
1287 : 12 : xstring_free(message);
1288 : 12 : }
1289 : :
1290 : : cleanup:
1291 [ + - ]: 742 : if (a != NULL) {
1292 : 742 : archive_read_close(a);
1293 : 742 : archive_read_free(a);
1294 : 742 : }
1295 : :
1296 : 742 : pkg_free(pkg);
1297 : :
1298 : 742 : return (retcode);
1299 : 742 : }
1300 : :
1301 : : int
1302 : 203 : pkg_add(struct pkgdb *db, const char *path, unsigned flags,
1303 : : struct pkg_manifest_key *keys, const char *location)
1304 : : {
1305 : 203 : return pkg_add_common(db, path, flags, keys, location, NULL, NULL);
1306 : : }
1307 : :
1308 : : int
1309 : 407 : pkg_add_from_remote(struct pkgdb *db, const char *path, unsigned flags,
1310 : : struct pkg_manifest_key *keys, const char *location, struct pkg *rp)
1311 : : {
1312 : 407 : return pkg_add_common(db, path, flags, keys, location, rp, NULL);
1313 : : }
1314 : :
1315 : : int
1316 : 132 : pkg_add_upgrade(struct pkgdb *db, const char *path, unsigned flags,
1317 : : struct pkg_manifest_key *keys, const char *location,
1318 : : struct pkg *rp, struct pkg *lp)
1319 : : {
1320 [ - + - + ]: 264 : if (pkgdb_ensure_loaded(db, lp,
1321 : 132 : PKG_LOAD_FILES|PKG_LOAD_SCRIPTS|PKG_LOAD_DIRS|PKG_LOAD_LUA_SCRIPTS) != EPKG_OK)
1322 : 0 : return (EPKG_FATAL);
1323 : :
1324 : 132 : return pkg_add_common(db, path, flags, keys, location, rp, lp);
1325 : 132 : }
1326 : :
1327 : : int
1328 : 12 : pkg_add_fromdir(struct pkg *pkg, const char *src)
1329 : : {
1330 : : struct stat st;
1331 : 12 : struct pkg_dir *d = NULL;
1332 : 12 : struct pkg_file *f = NULL;
1333 : : char target[MAXPATHLEN];
1334 : : struct passwd *pw, pwent;
1335 : : struct group *gr, grent;
1336 : : int err, fd, fromfd;
1337 : : int retcode;
1338 : 12 : kh_hls_t *hardlinks = NULL;;
1339 : : const char *path;
1340 : : char buffer[1024];
1341 : : size_t link_len;
1342 : : bool install_as_user;
1343 : :
1344 : 12 : install_as_user = (getenv("INSTALL_AS_USER") != NULL);
1345 : :
1346 : 12 : fromfd = open(src, O_DIRECTORY);
1347 [ + - ]: 12 : if (fromfd == -1) {
1348 : 0 : pkg_fatal_errno("Unable to open source directory '%s'", src);
1349 : 0 : }
1350 : 12 : pkg_open_root_fd(pkg);
1351 : :
1352 [ - + ]: 12 : while (pkg_dirs(pkg, &d) == EPKG_OK) {
1353 [ # # ]: 0 : if (fstatat(fromfd, RELATIVE_PATH(d->path), &st, 0) == -1) {
1354 : 0 : close(fromfd);
1355 : 0 : pkg_fatal_errno("%s%s", src, d->path);
1356 : 0 : }
1357 [ # # ]: 0 : if (d->perm == 0)
1358 : 0 : d->perm = st.st_mode & ~S_IFMT;
1359 [ # # ]: 0 : if (d->uname[0] != '\0') {
1360 : 0 : err = getpwnam_r(d->uname, &pwent, buffer,
1361 : : sizeof(buffer), &pw);
1362 [ # # ]: 0 : if (err != 0) {
1363 : 0 : pkg_emit_errno("getpwnam_r", d->uname);
1364 : 0 : retcode = EPKG_FATAL;
1365 : 0 : goto cleanup;
1366 : : }
1367 : 0 : d->uid = pwent.pw_uid;
1368 : 0 : } else {
1369 [ # # ]: 0 : d->uid = install_as_user ? st.st_uid : 0;
1370 : : }
1371 [ # # ]: 0 : if (d->gname[0] != '\0') {
1372 : 0 : err = getgrnam_r(d->gname, &grent, buffer,
1373 : : sizeof(buffer), &gr);
1374 [ # # ]: 0 : if (err != 0) {
1375 : 0 : pkg_emit_errno("getgrnam_r", d->gname);
1376 : 0 : retcode = EPKG_FATAL;
1377 : 0 : goto cleanup;
1378 : : }
1379 : 0 : d->gid = grent.gr_gid;
1380 : 0 : } else {
1381 : 0 : d->gid = st.st_gid;
1382 : : }
1383 : : #ifdef HAVE_STRUCT_STAT_ST_MTIM
1384 : 0 : d->time[0] = st.st_atim;
1385 : 0 : d->time[1] = st.st_mtim;
1386 : : #else
1387 : : #if defined(_DARWIN_C_SOURCE) || defined(__APPLE__)
1388 : : d->time[0] = st.st_atimespec;
1389 : : d->time[1] = st.st_mtimespec;
1390 : : #else
1391 : : d->time[0].tv_sec = st.st_atime;
1392 : : d->time[0].tv_nsec = 0;
1393 : : d->time[1].tv_sec = st.st_mtime;
1394 : : d->time[1].tv_nsec = 0;
1395 : : #endif
1396 : : #endif
1397 : :
1398 [ # # ]: 0 : if (create_dir(pkg, d) == EPKG_FATAL) {
1399 : 0 : retcode = EPKG_FATAL;
1400 : 0 : goto cleanup;
1401 : : }
1402 : : }
1403 : :
1404 : 12 : hardlinks = kh_init_hls();
1405 [ + + ]: 40 : while (pkg_files(pkg, &f) == EPKG_OK) {
1406 [ - + - + ]: 56 : if (match_ucl_lists(f->path,
1407 : 28 : pkg_config_get("FILES_IGNORE_GLOB"),
1408 : 28 : pkg_config_get("FILES_IGNORE_REGEX")))
1409 : 0 : continue;
1410 [ + - + - ]: 56 : if (fstatat(fromfd, RELATIVE_PATH(f->path), &st,
1411 : 28 : AT_SYMLINK_NOFOLLOW) == -1) {
1412 : 0 : kh_destroy_hls(hardlinks);
1413 : 0 : close(fromfd);
1414 : 0 : pkg_fatal_errno("%s%s", src, f->path);
1415 : 0 : }
1416 [ + + ]: 28 : if (f->uname[0] != '\0') {
1417 : 24 : err = getpwnam_r(f->uname, &pwent, buffer,
1418 : : sizeof(buffer), &pw);
1419 [ + - ]: 24 : if (err != 0) {
1420 : 0 : pkg_emit_errno("getpwnam_r", f->uname);
1421 : 0 : retcode = EPKG_FATAL;
1422 : 0 : goto cleanup;
1423 : : }
1424 : 24 : f->uid = pwent.pw_uid;
1425 : 24 : } else {
1426 [ + - ]: 4 : f->uid = install_as_user ? st.st_uid : 0;
1427 : : }
1428 : :
1429 [ + + ]: 28 : if (f->gname[0] != '\0') {
1430 : 24 : err = getgrnam_r(f->gname, &grent, buffer,
1431 : : sizeof(buffer), &gr);
1432 [ + - ]: 24 : if (err != 0) {
1433 : 0 : pkg_emit_errno("getgrnam_r", f->gname);
1434 : 0 : retcode = EPKG_FATAL;
1435 : 0 : goto cleanup;
1436 : : }
1437 : 24 : f->gid = grent.gr_gid;
1438 : 24 : } else {
1439 : 4 : f->gid = st.st_gid;
1440 : : }
1441 : :
1442 [ - + ]: 28 : if (f->perm == 0)
1443 : 28 : f->perm = st.st_mode & ~S_IFMT;
1444 [ + + - + ]: 28 : if (f->uid == 0 && install_as_user)
1445 : 24 : f->uid = st.st_uid;
1446 : : #ifdef HAVE_STRUCT_STAT_ST_MTIM
1447 : 28 : f->time[0] = st.st_atim;
1448 : 28 : f->time[1] = st.st_mtim;
1449 : : #else
1450 : : #if defined(_DARWIN_C_SOURCE) || defined(__APPLE__)
1451 : : f->time[0] = st.st_atimespec;
1452 : : f->time[1] = st.st_mtimespec;
1453 : : #else
1454 : : f->time[0].tv_sec = st.st_atime;
1455 : : f->time[0].tv_nsec = 0;
1456 : : f->time[1].tv_sec = st.st_mtime;
1457 : : f->time[1].tv_nsec = 0;
1458 : : #endif
1459 : : #endif
1460 : :
1461 [ + + ]: 28 : if (S_ISLNK(st.st_mode)) {
1462 [ + - + - : 36 : if ((link_len = readlinkat(fromfd,
+ - ]
1463 : 12 : RELATIVE_PATH(f->path), target,
1464 : 12 : sizeof(target))) == -1) {
1465 : 0 : kh_destroy_hls(hardlinks);
1466 : 0 : close(fromfd);
1467 : 0 : pkg_fatal_errno("Impossible to read symlinks "
1468 : : "'%s'", f->path);
1469 : 0 : }
1470 : 12 : target[link_len] = '\0';
1471 [ + - ]: 12 : if (create_symlinks(pkg, f, target) == EPKG_FATAL) {
1472 : 0 : retcode = EPKG_FATAL;
1473 : 0 : goto cleanup;
1474 : : }
1475 [ + - ]: 28 : } else if (S_ISREG(st.st_mode)) {
1476 [ + - + - ]: 32 : if ((fd = openat(fromfd, RELATIVE_PATH(f->path),
1477 : 16 : O_RDONLY)) == -1) {
1478 : 0 : kh_destroy_hls(hardlinks);
1479 : 0 : close(fromfd);
1480 : 0 : pkg_fatal_errno("Impossible to open source file"
1481 : : " '%s'", RELATIVE_PATH(f->path));
1482 : 0 : }
1483 [ - + + + ]: 16 : kh_find(hls, hardlinks, st.st_ino, path);
1484 [ + + ]: 16 : if (path != NULL) {
1485 [ + - ]: 4 : if (create_hardlink(pkg, f, path) == EPKG_FATAL) {
1486 : 0 : close(fd);
1487 : 0 : retcode = EPKG_FATAL;
1488 : 0 : goto cleanup;
1489 : : }
1490 : 4 : } else {
1491 [ + - ]: 12 : if (create_regfile(pkg, f, NULL, NULL, fd, NULL) == EPKG_FATAL) {
1492 : 0 : close(fd);
1493 : 0 : retcode = EPKG_FATAL;
1494 : 0 : goto cleanup;
1495 : : }
1496 [ + - - + ]: 12 : kh_safe_add(hls, hardlinks, f->path, st.st_ino);
1497 : : }
1498 : 16 : close(fd);
1499 : 16 : } else {
1500 : 0 : pkg_emit_error("Invalid file type");
1501 : 0 : retcode = EPKG_FATAL;
1502 : 0 : goto cleanup;
1503 : : }
1504 : : }
1505 : :
1506 : 12 : retcode = pkg_extract_finalize(pkg);
1507 : :
1508 : : cleanup:
1509 : 12 : kh_destroy_hls(hardlinks);
1510 : 12 : close(fromfd);
1511 : 12 : return (retcode);
1512 : 12 : }
1513 : :
|