1 /*        $NetBSD: plist.c,v 1.7 2021/04/10 19:49:59 nia Exp $        */
2 
3 #if HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 #include <nbcompat.h>
7 #if HAVE_SYS_CDEFS_H
8 #include <sys/cdefs.h>
9 #endif
10 __RCSID("$NetBSD: plist.c,v 1.7 2021/04/10 19:49:59 nia Exp $");
11 
12 /*
13  * FreeBSD install - a package for the installation and maintainance
14  * of non-core utilities.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  *
25  * Jordan K. Hubbard
26  * 18 July 1993
27  *
28  * General packing list routines.
29  *
30  */
31 
32 /*-
33  * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  *
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in
44  *    the documentation and/or other materials provided with the
45  *    distribution.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
48  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
49  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
50  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
51  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
52  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
53  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
54  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
55  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
56  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
57  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58  * SUCH DAMAGE.
59  */
60 
61 #include "lib.h"
62 #if HAVE_ERRNO_H
63 #include <errno.h>
64 #endif
65 #if HAVE_ERR_H
66 #include <err.h>
67 #endif
68 #ifndef NETBSD
69 #include <nbcompat/md5.h>
70 #else
71 #include <md5.h>
72 #endif
73 
74 static int     delete_with_parents(const char *, Boolean, Boolean);
75 
76 /* This struct defines a plist command type */
77 typedef struct cmd_t {
78           const char   *c_s;            /* string to recognise */
79           pl_ent_t c_type;    /* type of command */
80           int     c_argc;               /* # of arguments */
81           int     c_subst;    /* can substitute real prefix */
82 }       cmd_t;
83 
84 /* Commands to recognise */
85 static const cmd_t cmdv[] = {
86           {"cwd", PLIST_CWD, 1, 1},
87           {"src", PLIST_SRC, 1, 1},
88           {"exec", PLIST_CMD, 1, 0},
89           {"unexec", PLIST_UNEXEC, 1, 0},
90           {"mode", PLIST_CHMOD, 1, 0},
91           {"owner", PLIST_CHOWN, 1, 0},
92           {"group", PLIST_CHGRP, 1, 0},
93           {"comment", PLIST_COMMENT, 1, 0},
94           {"ignore", PLIST_IGNORE, 0, 0},
95           {"name", PLIST_NAME, 1, 0},
96           {"display", PLIST_DISPLAY, 1, 0},
97           {"pkgdep", PLIST_PKGDEP, 1, 0},
98           {"pkgcfl", PLIST_PKGCFL, 1, 0},
99           {"pkgdir", PLIST_PKGDIR, 1, 0},
100           {"dirrm", PLIST_DIR_RM, 1, 0},
101           {"option", PLIST_OPTION, 1, 0},
102           {"blddep", PLIST_BLDDEP, 1, 0},
103           {NULL, FAIL, 0, 0}
104 };
105 
106 /*
107  * Add an item to the end of a packing list
108  */
109 void
add_plist(package_t * p,pl_ent_t type,const char * arg)110 add_plist(package_t *p, pl_ent_t type, const char *arg)
111 {
112           plist_t *tmp;
113 
114           tmp = new_plist_entry();
115           tmp->name = (arg == NULL) ? NULL : xstrdup(arg);
116           tmp->type = type;
117           if (!p->head) {
118                     p->head = p->tail = tmp;
119           } else {
120                     tmp->prev = p->tail;
121                     p->tail->next = tmp;
122                     p->tail = tmp;
123           }
124 }
125 
126 /*
127  * Add an item to the start of a packing list
128  */
129 void
add_plist_top(package_t * p,pl_ent_t type,const char * arg)130 add_plist_top(package_t *p, pl_ent_t type, const char *arg)
131 {
132           plist_t *tmp;
133 
134           tmp = new_plist_entry();
135           tmp->name = (arg == NULL) ? NULL : xstrdup(arg);
136           tmp->type = type;
137           if (!p->head) {
138                     p->head = p->tail = tmp;
139           } else {
140                     tmp->next = p->head;
141                     p->head->prev = tmp;
142                     p->head = tmp;
143           }
144 }
145 
146 /*
147  * Return the last (most recent) entry in a packing list
148  */
149 plist_t *
last_plist(package_t * p)150 last_plist(package_t *p)
151 {
152           return p->tail;
153 }
154 
155 /*
156  * Mark all items in a packing list to prevent iteration over them
157  */
158 void
mark_plist(package_t * pkg)159 mark_plist(package_t *pkg)
160 {
161           plist_t *pp;
162 
163           for (pp = pkg->head; pp; pp = pp->next) {
164                     pp->marked = TRUE;
165           }
166 }
167 
168 /*
169  * Find a given item in a packing list and, if so, return it (else NULL)
170  */
171 plist_t *
find_plist(package_t * pkg,pl_ent_t type)172 find_plist(package_t *pkg, pl_ent_t type)
173 {
174           plist_t *pp;
175 
176           for (pp = pkg->head; pp && pp->type != type; pp = pp->next) {
177           }
178           return pp;
179 }
180 
181 /*
182  * Look for a specific boolean option argument in the list
183  */
184 char   *
find_plist_option(package_t * pkg,const char * name)185 find_plist_option(package_t *pkg, const char *name)
186 {
187           plist_t *p;
188 
189           for (p = pkg->head; p; p = p->next) {
190                     if (p->type == PLIST_OPTION
191                         && strcmp(p->name, name) == 0) {
192                               return p->name;
193                     }
194           }
195 
196           return (char *) NULL;
197 }
198 
199 /*
200  * Delete plist item 'type' in the list (if 'name' is non-null, match it
201  * too.)  If 'all' is set, delete all items, not just the first occurance.
202  */
203 void
delete_plist(package_t * pkg,Boolean all,pl_ent_t type,char * name)204 delete_plist(package_t *pkg, Boolean all, pl_ent_t type, char *name)
205 {
206           plist_t *p = pkg->head;
207 
208           while (p) {
209                     plist_t *pnext = p->next;
210 
211                     if (p->type == type && (!name || !strcmp(name, p->name))) {
212                               free(p->name);
213                               if (p->prev)
214                                         p->prev->next = pnext;
215                               else
216                                         pkg->head = pnext;
217                               if (pnext)
218                                         pnext->prev = p->prev;
219                               else
220                                         pkg->tail = p->prev;
221                               free(p);
222                               if (!all)
223                                         return;
224                               p = pnext;
225                     } else
226                               p = p->next;
227           }
228 }
229 
230 /*
231  * Allocate a new packing list entry, and return a pointer to it.
232  */
233 plist_t *
new_plist_entry(void)234 new_plist_entry(void)
235 {
236           return xcalloc(1, sizeof(plist_t));
237 }
238 
239 /*
240  * Free an entire packing list
241  */
242 void
free_plist(package_t * pkg)243 free_plist(package_t *pkg)
244 {
245           plist_t *p = pkg->head;
246 
247           while (p) {
248                     plist_t *p1 = p->next;
249 
250                     free(p->name);
251                     free(p);
252                     p = p1;
253           }
254           pkg->head = pkg->tail = NULL;
255 }
256 
257 /*
258  * For an ASCII string denoting a plist command, return its code and
259  * optionally its argument(s)
260  */
261 static int
plist_cmd(const char * s,char ** arg)262 plist_cmd(const char *s, char **arg)
263 {
264           const cmd_t *cmdp;
265           const char *cp, *sp;
266           char *sp2;
267 
268           sp = NULL; /* Older GCC can't detect that the loop is executed */
269 
270           for (cmdp = cmdv; cmdp->c_s; ++cmdp) {
271                     for (sp = s, cp = cmdp->c_s; *sp && *cp; ++cp, ++sp)
272                               if (*sp != *cp)
273                                         break;
274                     if (*cp == '\0')
275                               break;
276           }
277 
278           if (cmdp->c_s == NULL || arg == NULL)
279                     return cmdp->c_type;
280 
281           while (isspace((unsigned char)*sp))
282                     ++sp;
283           *arg = xstrdup(sp);
284           if (*sp) {
285                     sp2 = *arg + strlen(*arg) - 1;
286                     /*
287                      * The earlier loop ensured that at least one non-whitespace
288                      * is in the string.
289                      */
290                     while (isspace((unsigned char)*sp2))
291                               --sp2;
292                     sp2[1] = '\0';
293           }
294           return cmdp->c_type;
295 }
296 
297 /*
298  * Parse a packaging list from a memory buffer.
299  */
300 void
parse_plist(package_t * pkg,const char * buf)301 parse_plist(package_t *pkg, const char *buf)
302 {
303           int cmd;
304           char *line, *cp;
305           const char *eol, *next;
306           size_t len;
307 
308           pkg->head = NULL;
309           pkg->tail = NULL;
310 
311           for (; *buf; buf = next) {
312                     /* Until add_plist can deal with trailing whitespace. */
313                     if ((eol = strchr(buf, '\n')) != NULL) {
314                               next = eol + 1;
315                               len = eol - buf;
316                     } else {
317                               len = strlen(buf);
318                               next = buf + len;
319                     }
320 
321                     while (len && isspace((unsigned char)buf[len - 1]))
322                               --len;
323 
324                     if (len == 0)
325                               continue;
326 
327                     line = xmalloc(len + 1);
328                     memcpy(line, buf, len);
329                     line[len] = '\0';
330 
331                     if (*(cp = line) == CMD_CHAR) {
332                               if ((cmd = plist_cmd(line + 1, &cp)) == FAIL) {
333                                         warnx("Unrecognised PLIST command `%s'", line);
334                                         continue;
335                               }
336                               if (*cp == '\0') {
337                                         free(cp);
338                                         cp = NULL;
339                               }
340                     } else {
341                               cmd = PLIST_FILE;
342                     }
343                     add_plist(pkg, cmd, cp);
344                     free(cp);
345           }
346 }
347 
348 /*
349  * Read a packing list from a file
350  */
351 void
append_plist(package_t * pkg,FILE * fp)352 append_plist(package_t *pkg, FILE * fp)
353 {
354           char    pline[MaxPathSize];
355           char   *cp;
356           int     cmd;
357           int     len;
358           int       free_cp;
359 
360           while (fgets(pline, MaxPathSize, fp) != (char *) NULL) {
361                     for (len = strlen(pline); len &&
362                         isspace((unsigned char) pline[len - 1]);) {
363                               pline[--len] = '\0';
364                     }
365                     if (len == 0) {
366                               continue;
367                     }
368                     free_cp = 0;
369                     if (*(cp = pline) == CMD_CHAR) {
370                               if ((cmd = plist_cmd(pline + 1, &cp)) == FAIL) {
371                                         warnx("Unrecognised PLIST command `%s'", pline);
372                                         continue;
373                               }
374                               if (*cp == '\0') {
375                                         free(cp);
376                                         cp = NULL;
377                               }
378                               free_cp = 1;
379                     } else {
380                               cmd = PLIST_FILE;
381                     }
382                     add_plist(pkg, cmd, cp);
383                     if (free_cp)
384                               free(cp);
385           }
386 }
387 
388 void
read_plist(package_t * pkg,FILE * fp)389 read_plist(package_t *pkg, FILE * fp)
390 {
391           pkg->head = NULL;
392           pkg->tail = NULL;
393 
394           append_plist(pkg, fp);
395 }
396 
397 /*
398  * Write a packing list to a file, converting commands to ASCII equivs
399  */
400 void
write_plist(package_t * pkg,FILE * fp,char * realprefix)401 write_plist(package_t *pkg, FILE * fp, char *realprefix)
402 {
403           plist_t *p;
404           const cmd_t *cmdp;
405 
406           for (p = pkg->head; p; p = p->next) {
407                     if (p->type == PLIST_FILE) {
408                               /* Fast-track files - these are the most common */
409                               (void) fprintf(fp, "%s\n", p->name);
410                               continue;
411                     }
412                     for (cmdp = cmdv; cmdp->c_type != FAIL && cmdp->c_type != p->type; cmdp++) {
413                     }
414                     if (cmdp->c_type == FAIL) {
415                               warnx("Unknown PLIST command type %d (%s)", p->type, p->name);
416                     } else if (cmdp->c_argc == 0) {
417                               (void) fprintf(fp, "%c%s\n", CMD_CHAR, cmdp->c_s);
418                     } else if (cmdp->c_subst && realprefix) {
419                               (void) fprintf(fp, "%c%s %s\n", CMD_CHAR, cmdp->c_s, realprefix);
420                     } else {
421                               (void) fprintf(fp, "%c%s %s\n", CMD_CHAR, cmdp->c_s,
422                                   (p->name) ? p->name : "");
423                     }
424           }
425 }
426 
427 /*
428  * Like write_plist, but compute memory string.
429  */
430 void
stringify_plist(package_t * pkg,char ** real_buf,size_t * real_len,const char * realprefix)431 stringify_plist(package_t *pkg, char **real_buf, size_t *real_len,
432     const char *realprefix)
433 {
434           plist_t *p;
435           const cmd_t *cmdp;
436           char *buf;
437           size_t len;
438           int item_len;
439 
440           /* Pass One: compute output size only. */
441           len = 0;
442 
443           for (p = pkg->head; p; p = p->next) {
444                     if (p->type == PLIST_FILE) {
445                               len += strlen(p->name) + 1;
446                               continue;
447                     }
448                     for (cmdp = cmdv; cmdp->c_type != FAIL && cmdp->c_type != p->type; cmdp++) {
449                     }
450                     if (cmdp->c_type == FAIL)
451                               continue;
452                     if (cmdp->c_argc == 0)
453                               len += 1 + strlen(cmdp->c_s) + 1;
454                     else if (cmdp->c_subst && realprefix)
455                               len += 1 + strlen(cmdp->c_s) + 1 + strlen(realprefix) + 1;
456                     else
457                               len += 1 + strlen(cmdp->c_s) + 1 + strlen(p->name ? p->name : "") + 1;
458           }
459 
460           /* Pass Two: build actual string. */
461           buf = xmalloc(len + 1);
462           *real_buf = buf;
463           *real_len = len;
464           ++len;
465 
466 #define   UPDATE_LEN                                                                      \
467 do {                                                                                      \
468           if (item_len < 0 || (size_t)item_len > len)                           \
469                     errx(2, "Size computation failed, aborted.");               \
470           buf += item_len;                                                      \
471           len -= item_len;                                                      \
472 } while (/* CONSTCOND */0)
473 
474           for (p = pkg->head; p; p = p->next) {
475                     if (p->type == PLIST_FILE) {
476                               /* Fast-track files - these are the most common */
477                               item_len = snprintf(buf, len, "%s\n", p->name);
478                               UPDATE_LEN;
479                               continue;
480                     }
481                     for (cmdp = cmdv; cmdp->c_type != FAIL && cmdp->c_type != p->type; cmdp++) {
482                     }
483                     if (cmdp->c_type == FAIL) {
484                               warnx("Unknown PLIST command type %d (%s)", p->type, p->name);
485                     } else if (cmdp->c_argc == 0) {
486                               item_len = snprintf(buf, len, "%c%s\n", CMD_CHAR, cmdp->c_s);
487                               UPDATE_LEN;
488                     } else if (cmdp->c_subst && realprefix) {
489                               item_len = snprintf(buf, len, "%c%s %s\n", CMD_CHAR, cmdp->c_s, realprefix);
490                               UPDATE_LEN;
491                     } else {
492                               item_len = snprintf(buf, len, "%c%s %s\n", CMD_CHAR, cmdp->c_s,
493                                   (p->name) ? p->name : "");
494                               UPDATE_LEN;
495                     }
496           }
497 
498           if (len != 1)
499                     errx(2, "Size computation failed, aborted.");
500 }
501 
502 /*
503  * Delete the results of a package installation.
504  *
505  * This is here rather than in the pkg_delete code because pkg_add needs to
506  * run it too in cases of failure.
507  */
508 int
delete_package(Boolean ign_err,package_t * pkg,Boolean NoDeleteFiles,const char * destdir)509 delete_package(Boolean ign_err, package_t *pkg, Boolean NoDeleteFiles,
510     const char *destdir)
511 {
512           plist_t *p;
513           const char *last_file = "";
514           int     fail = SUCCESS;
515           Boolean preserve;
516           char    tmp[MaxPathSize];
517           const char *prefix = NULL, *name = NULL;
518 
519           if (!pkgdb_open(ReadWrite)) {
520                     err(EXIT_FAILURE, "cannot open pkgdb");
521           }
522 
523           preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
524 
525           for (p = pkg->head; p; p = p->next) {
526                     switch (p->type) {
527                     case PLIST_NAME:
528                               name = p->name;
529                               break;
530                     case PLIST_CWD:
531                               if (prefix == NULL)
532                                         prefix = p->name;
533                               break;
534                     default:
535                               break;
536                     }
537           }
538 
539           if (name == NULL || prefix == NULL)
540                     errx(EXIT_FAILURE, "broken PLIST");
541 
542           /*
543            * Remove database entries first, directory removal is done
544            * in the main loop below.
545            */
546           for (p = pkg->head; p; p = p->next) {
547                     if (p->type == PLIST_PKGDIR)
548                               delete_pkgdir(name, prefix, p->name);
549           }
550 
551           for (p = pkg->head; p; p = p->next) {
552                     switch (p->type) {
553                     case PLIST_NAME:
554                               /* Handled already */
555                               break;
556 
557                     case PLIST_PKGDIR:
558                     case PLIST_DIR_RM:
559                               (void) snprintf(tmp, sizeof(tmp), "%s/%s",
560                                   prefix, p->name);
561                               if (has_pkgdir(tmp))
562                                         continue;
563                               (void) snprintf(tmp, sizeof(tmp), "%s%s%s/%s",
564                                   destdir ? destdir : "", destdir ? "/" : "",
565                                   prefix, p->name);
566                               if (!fexists(tmp)) {
567                                         if (p->type == PLIST_PKGDIR)
568                                                   warnx("Directory `%s' disappeared, skipping", tmp);
569                               } else if (!isdir(tmp)) {
570                                         warnx("attempting to delete a file `%s' as a directory\n"
571                                             "this packing list is incorrect - ignoring delete request", tmp);
572                               } else if (delete_with_parents(tmp, ign_err, TRUE))
573                                         fail = FAIL;
574                               break;
575 
576                     case PLIST_IGNORE:
577                               p = p->next;
578                               break;
579 
580                     case PLIST_UNEXEC:
581                               if (NoDeleteFiles)
582                                         break;
583                               format_cmd(tmp, sizeof(tmp), p->name, prefix, last_file);
584                               printf("Executing `%s'\n", tmp);
585                               if (!Fake && system(tmp)) {
586                                         warnx("unexec command for `%s' failed", tmp);
587                                         fail = FAIL;
588                               }
589                               break;
590 
591                     case PLIST_FILE:
592                               last_file = p->name;
593                               (void) snprintf(tmp, sizeof(tmp), "%s%s%s/%s",
594                                   destdir ? destdir : "", destdir ? "/" : "",
595                                   prefix, p->name);
596                               if (isdir(tmp)) {
597                                         warnx("attempting to delete directory `%s' as a file\n"
598                                             "this packing list is incorrect - ignoring delete request", tmp);
599                               } else {
600                                         int     restored = 0;         /* restored from preserve? */
601 
602                                         if (p->next && p->next->type == PLIST_COMMENT) {
603                                                   if (strncmp(p->next->name, CHECKSUM_HEADER, ChecksumHeaderLen) == 0) {
604                                                             char   *cp, buf[LegibleChecksumLen];
605 
606                                                             if ((cp = MD5File(tmp, buf)) != NULL) {
607                                                                       /* Mismatch? */
608                                                                       if (strcmp(cp, p->next->name + ChecksumHeaderLen) != 0) {
609                                                                                 printf("original MD5 checksum failed, %s: %s\n",
610                                                                                     Force ? "deleting anyway" : "not deleting", tmp);
611                                                                                 if (!Force) {
612                                                                                           fail = FAIL;
613                                                                                           goto pkgdb_cleanup;
614                                                                                 }
615                                                                       }
616                                                             }
617                                                   } else if (strncmp(p->next->name, SYMLINK_HEADER, SymlinkHeaderLen) == 0) {
618                                                             char      buf[MaxPathSize + SymlinkHeaderLen];
619                                                             int       cc;
620 
621                                                             (void) strlcpy(buf, SYMLINK_HEADER,
622                                                                 sizeof(buf));
623                                                             if ((cc = readlink(tmp, &buf[SymlinkHeaderLen],
624                                                                         sizeof(buf) - SymlinkHeaderLen - 1)) < 0) {
625                                                                       warn("can't readlink `%s'", tmp);
626                                                                       goto pkgdb_cleanup;
627                                                             }
628                                                             buf[SymlinkHeaderLen + cc] = 0x0;
629                                                             if (strcmp(buf, p->next->name) != 0) {
630                                                                       char    tmp2[MaxPathSize];
631 
632                                                                       if ((cc = readlink(&buf[SymlinkHeaderLen], tmp2,
633                                                                                   sizeof(tmp2))) < 0) {
634                                                                                 printf("symlink %s is not same as recorded value, %s: %s\n",
635                                                                                     buf, Force ? "deleting anyway" : "not deleting", tmp);
636                                                                                 if (!Force) {
637                                                                                           fail = FAIL;
638                                                                                           goto pkgdb_cleanup;
639                                                                                 }
640                                                                       } else {
641                                                                                 memcpy(&buf[SymlinkHeaderLen], tmp2, cc);
642                                                                                 buf[SymlinkHeaderLen + cc] = 0x0;
643                                                                                 if (strcmp(buf, p->next->name) != 0) {
644                                                                                           printf("symlink %s is not same as recorded value, %s: %s\n",
645                                                                                               buf, Force ? "deleting anyway" : "not deleting", tmp);
646                                                                                           if (!Force) {
647                                                                                                     fail = FAIL;
648                                                                                                     goto pkgdb_cleanup;
649                                                                                           }
650                                                                                 }
651                                                                       }
652                                                             }
653                                                   }
654                                         }
655                                         if (Verbose && !NoDeleteFiles)
656                                                   printf("Delete file %s\n", tmp);
657                                         if (!Fake && !NoDeleteFiles) {
658                                                   if (delete_with_parents(tmp, ign_err, FALSE))
659                                                             fail = FAIL;
660                                                   if (preserve && name) {
661                                                             char    tmp2[MaxPathSize];
662 
663                                                             if (make_preserve_name(tmp2, MaxPathSize, name, tmp)) {
664                                                                       if (fexists(tmp2)) {
665                                                                                 if (rename(tmp2, tmp))
666                                                                                           warn("preserve: unable to restore %s as %s",
667                                                                                               tmp2, tmp);
668                                                                                 else
669                                                                                           restored = 1;
670                                                                       }
671                                                             }
672                                                   }
673                                         }
674 
675 pkgdb_cleanup:
676                                         if (!Fake) {
677                                                   if (!restored) {
678                                                             errno = 0;
679                                                             if (pkgdb_remove(tmp) && errno)
680                                                                       perror("pkgdb_remove");
681                                                   }
682                                         }
683                               }
684                               break;
685                     default:
686                               break;
687                     }
688           }
689           pkgdb_close();
690           return fail;
691 }
692 
693 /*
694  * Selectively delete a hierarchy
695  * Returns 1 on error, 0 else.
696  */
697 static int
delete_with_parents(const char * fname,Boolean ign_err,Boolean ign_nonempty)698 delete_with_parents(const char *fname, Boolean ign_err, Boolean ign_nonempty)
699 {
700           char   *cp, *cp2;
701 
702           if (remove(fname)) {
703                     if (!ign_err && (!ign_nonempty || errno != ENOTEMPTY))
704                               warn("Couldn't remove %s", fname);
705                     return 0;
706           }
707           cp = xstrdup(fname);
708           while (*cp) {
709                     if ((cp2 = strrchr(cp, '/')) != NULL)
710                               *cp2 = '\0';
711                     if (!isemptydir(cp))
712                               break;
713                     if (has_pkgdir(cp))
714                               break;
715                     if (rmdir(cp))
716                               break;
717           }
718           free(cp);
719 
720           return 0;
721 }
722 
723 void
add_pkgdir(const char * pkg,const char * prefix,const char * path)724 add_pkgdir(const char *pkg, const char *prefix, const char *path)
725 {
726           char *fullpath, *oldvalue, *newvalue;
727 
728           fullpath = xasprintf("%s/%s", prefix, path);
729           oldvalue = pkgdb_retrieve(fullpath);
730           if (oldvalue) {
731                     if (strncmp(oldvalue, "@pkgdir ", 8) != 0)
732                               errx(EXIT_FAILURE, "Internal error while processing pkgdb, run pkg_admin rebuild");
733                     newvalue = xasprintf("%s %s", oldvalue, pkg);
734                     pkgdb_remove(fullpath);
735           } else {
736                     newvalue = xasprintf("@pkgdir %s", pkg);
737           }
738           pkgdb_store(fullpath, newvalue);
739 
740           free(fullpath);
741           free(newvalue);
742 }
743 
744 void
delete_pkgdir(const char * pkg,const char * prefix,const char * path)745 delete_pkgdir(const char *pkg, const char *prefix, const char *path)
746 {
747           size_t pkg_len, len;
748           char *fullpath, *oldvalue, *newvalue, *iter;
749 
750           fullpath = xasprintf("%s/%s", prefix, path);
751           oldvalue = pkgdb_retrieve(fullpath);
752           if (oldvalue && strncmp(oldvalue, "@pkgdir ", 8) == 0) {
753                     newvalue = xstrdup(oldvalue);
754                     iter = newvalue + 8;
755                     pkg_len = strlen(pkg);
756                     while (*iter) {
757                               if (strncmp(iter, pkg, pkg_len) == 0 &&
758                                   (iter[pkg_len] == ' ' || iter[pkg_len] == '\0')) {
759                                         len = strlen(iter + pkg_len);
760                                         memmove(iter, iter + pkg_len + 1, len);
761                                         if (len == 0)
762                                                   *iter = '\0';
763                               } else {
764                                         iter += strcspn(iter, " ");
765                                         iter += strspn(iter, " ");
766                               }
767                     }
768                     pkgdb_remove(fullpath);
769                     if (iter != newvalue + 8)
770                               pkgdb_store(fullpath, newvalue);
771                     free(newvalue);
772           }
773           free(fullpath);
774 }
775 
776 int
has_pkgdir(const char * path)777 has_pkgdir(const char *path)
778 {
779           const char *value;
780 
781           value = pkgdb_retrieve(path);
782 
783           if (value && strncmp(value, "@pkgdir ", 8) == 0)
784                     return 1;
785           else
786                     return 0;
787 }
788