Home | History | Annotate | Line # | Download | only in lib
      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 (at) 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
    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
    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 *
    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
    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 *
    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   *
    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
    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 *
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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