1/*	$NetBSD: pkgdb.c,v 1.6 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: pkgdb.c,v 1.6 2021/04/10 19:49:59 nia Exp $");
11
12/*-
13 * Copyright (c) 1999-2010 The NetBSD Foundation, Inc.
14 * All rights reserved.
15 *
16 * This code is derived from software contributed to The NetBSD Foundation
17 * by Hubert Feyrer <hubert@feyrer.de>.
18 *
19 * Redistribution and use in source and binary forms, with or without
20 * modification, are permitted provided that the following conditions
21 * are met:
22 * 1. Redistributions of source code must retain the above copyright
23 *    notice, this list of conditions and the following disclaimer.
24 * 2. Redistributions in binary form must reproduce the above copyright
25 *    notice, this list of conditions and the following disclaimer in the
26 *    documentation and/or other materials provided with the distribution.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 */
40
41#ifdef NETBSD
42#include <db.h>
43#else
44#include <nbcompat/db.h>
45#endif
46#if HAVE_ERR_H
47#include <err.h>
48#endif
49#if HAVE_ERRNO_H
50#include <errno.h>
51#endif
52#if HAVE_FCNTL_H
53#include <fcntl.h>
54#endif
55#if HAVE_STDARG_H
56#include <stdarg.h>
57#endif
58#if HAVE_STDIO_H
59#include <stdio.h>
60#endif
61#if HAVE_STRING_H
62#include <string.h>
63#endif
64
65#include "lib.h"
66
67#define PKGDB_FILE	"pkgdb.byfile.db"	/* indexed by filename */
68
69/*
70 * Where we put logging information by default if PKG_DBDIR is unset.
71 */
72#ifndef DEF_LOG_DIR
73#define DEF_LOG_DIR		PREFIX "/pkgdb"
74#endif
75
76static DB   *pkgdbp;
77static char pkgdb_dir_default[] = DEF_LOG_DIR;
78static char *pkgdb_dir = pkgdb_dir_default;
79static int pkgdb_dir_prio = 0;
80
81/*
82 *  Return name of cache file in the buffer that was passed.
83 */
84char *
85pkgdb_get_database(void)
86{
87	return xasprintf("%s/%s", pkgdb_get_dir(), PKGDB_FILE);
88}
89
90/*
91 *  Open the pkg-database
92 *  Return value:
93 *   1: everything ok
94 *   0: error
95 */
96int
97pkgdb_open(int mode)
98{
99	BTREEINFO info;
100	char *cachename;
101
102	/* try our btree format first */
103	info.flags = 0;
104	info.cachesize = 2*1024*1024;
105	info.maxkeypage = 0;
106	info.minkeypage = 0;
107	info.psize = 4096;
108	info.compare = NULL;
109	info.prefix = NULL;
110	info.lorder = 0;
111	cachename = pkgdb_get_database();
112	pkgdbp = (DB *) dbopen(cachename,
113	    (mode == ReadOnly) ? O_RDONLY : O_RDWR | O_CREAT,
114	    0644, DB_BTREE, (void *) &info);
115	free(cachename);
116	return (pkgdbp != NULL);
117}
118
119/*
120 * Close the pkg database
121 */
122void
123pkgdb_close(void)
124{
125	if (pkgdbp != NULL) {
126		(void) (*pkgdbp->close) (pkgdbp);
127		pkgdbp = NULL;
128	}
129}
130
131/*
132 * Store value "val" with key "key" in database
133 * Return value is as from ypdb_store:
134 *  0: ok
135 *  1: key already present
136 * -1: some other error, see errno
137 */
138int
139pkgdb_store(const char *key, const char *val)
140{
141	DBT     keyd, vald;
142
143	if (pkgdbp == NULL)
144		return -1;
145
146	keyd.data = __UNCONST(key);
147	keyd.size = strlen(key) + 1;
148	vald.data = __UNCONST(val);
149	vald.size = strlen(val) + 1;
150
151	if (keyd.size > MaxPathSize || vald.size > MaxPathSize)
152		return -1;
153
154	return (*pkgdbp->put) (pkgdbp, &keyd, &vald, R_NOOVERWRITE);
155}
156
157/*
158 * Recall value for given key
159 * Return value:
160 *  NULL if some error occurred or value for key not found (check errno!)
161 *  String for "value" else
162 */
163char   *
164pkgdb_retrieve(const char *key)
165{
166	DBT     keyd, vald;
167	int     status;
168	char	*eos;
169	static int corruption_warning;
170
171	if (pkgdbp == NULL)
172		return NULL;
173
174	keyd.data = __UNCONST(key);
175	keyd.size = strlen(key) + 1;
176	errno = 0;		/* to be sure it's 0 if the key doesn't match anything */
177
178	vald.data = (void *)NULL;
179	vald.size = 0;
180	status = (*pkgdbp->get) (pkgdbp, &keyd, &vald, 0);
181	if (status)
182		return NULL;
183	eos = memchr(vald.data, 0, vald.size);
184	if (eos == NULL || eos + 1 != (char *)vald.data + vald.size) {
185		if (!corruption_warning) {
186			warnx("pkgdb corrupted, please run ``pkg_admin rebuild''");
187			corruption_warning = 1;
188		}
189		return NULL;
190	}
191
192	return vald.data;
193}
194
195/* dump contents of the database to stdout */
196int
197pkgdb_dump(void)
198{
199	DBT     key;
200	DBT	val;
201	int	type;
202
203	if (pkgdb_open(ReadOnly)) {
204		for (type = R_FIRST ; (*pkgdbp->seq)(pkgdbp, &key, &val, type) == 0 ; type = R_NEXT) {
205			printf("file: %.*s pkg: %.*s\n",
206				(int) key.size, (char *) key.data,
207				(int) val.size, (char *) val.data);
208		}
209		pkgdb_close();
210		return 0;
211	} else
212		return -1;
213}
214
215/*
216 *  Remove data set from pkgdb
217 *  Return value as ypdb_delete:
218 *   0: everything ok
219 *   1: key not present
220 *  -1: some error occurred (see errno)
221 */
222int
223pkgdb_remove(const char *key)
224{
225	DBT     keyd;
226
227	if (pkgdbp == NULL)
228		return -1;
229
230	keyd.data = __UNCONST(key);
231	keyd.size = strlen(key) + 1;
232	if (keyd.size > MaxPathSize)
233		return -1;
234
235	return (*pkgdbp->del) (pkgdbp, &keyd, 0);
236}
237
238/*
239 *  Remove any entry from the cache which has a data field of `pkg'.
240 *  Return value:
241 *   1: everything ok
242 *   0: error
243 */
244int
245pkgdb_remove_pkg(const char *pkg)
246{
247	DBT     data;
248	DBT     key;
249	int	type;
250	int	ret;
251	size_t	cc;
252	char	*cachename;
253
254	if (pkgdbp == NULL) {
255		return 0;
256	}
257	cachename = pkgdb_get_database();
258	cc = strlen(pkg);
259	for (ret = 1, type = R_FIRST; (*pkgdbp->seq)(pkgdbp, &key, &data, type) == 0 ; type = R_NEXT) {
260		if ((cc + 1) == data.size && strncmp(data.data, pkg, cc) == 0) {
261			if (Verbose) {
262				printf("Removing file `%s' from %s\n", (char *)key.data, cachename);
263			}
264			switch ((*pkgdbp->del)(pkgdbp, &key, 0)) {
265			case -1:
266				warn("Error removing `%s' from %s", (char *)key.data, cachename);
267				ret = 0;
268				break;
269			case 1:
270				warn("Key `%s' not present in %s", (char *)key.data, cachename);
271				ret = 0;
272				break;
273
274			}
275		}
276	}
277	free(cachename);
278	return ret;
279}
280
281/*
282 *  Return the location of the package reference counts database directory.
283 */
284char *
285pkgdb_refcount_dir(void)
286{
287	static char buf[MaxPathSize];
288	char *tmp;
289
290	if ((tmp = getenv(PKG_REFCOUNT_DBDIR_VNAME)) != NULL)
291		strlcpy(buf, tmp, sizeof(buf));
292	else
293		snprintf(buf, sizeof(buf), "%s.refcount", pkgdb_get_dir());
294	return buf;
295}
296
297/*
298 *  Return directory where pkgdb is stored
299 */
300const char *
301pkgdb_get_dir(void)
302{
303
304#ifdef NETBSD
305	/*
306	 * NetBSD upgrade case.
307	 * NetBSD used to ship pkg_install with /var/db/pkg as
308	 * the default. We support continuing to install to
309	 * this location.
310	 *
311	 * This is NetBSD-specific because we can't assume that
312	 * /var/db/pkg is pkgsrc-owned on other systems (OpenBSD,
313	 * FreeBSD...)
314	 *
315	 * XXX: once postinstall is taught to automatically
316	 * handle migration, we can deprecate this behaviour.
317	 */
318
319#define PREVIOUS_LOG_DIR	"/var/db/pkg"
320	static char pkgdb_dir_previous[] = PREVIOUS_LOG_DIR;
321
322	struct stat sb;
323	if (strcmp(pkgdb_dir, DEF_LOG_DIR) == 0 &&
324	    stat(pkgdb_dir, &sb) == -1 && errno == ENOENT &&
325	    stat(PREVIOUS_LOG_DIR, &sb) == 0) {
326		return pkgdb_dir_previous;
327	}
328#endif
329
330        return pkgdb_dir;
331}
332
333/*
334 *  Set the first place we look for where pkgdb is stored.
335 */
336void
337pkgdb_set_dir(const char *dir, int prio)
338{
339
340	if (prio < pkgdb_dir_prio)
341		return;
342
343	pkgdb_dir_prio = prio;
344
345	if (dir == pkgdb_dir)
346		return;
347	if (pkgdb_dir != pkgdb_dir_default)
348		free(pkgdb_dir);
349	pkgdb_dir = xstrdup(dir);
350}
351
352char *
353pkgdb_pkg_dir(const char *pkg)
354{
355	return xasprintf("%s/%s", pkgdb_get_dir(), pkg);
356}
357
358char *
359pkgdb_pkg_file(const char *pkg, const char *file)
360{
361	return xasprintf("%s/%s/%s", pkgdb_get_dir(), pkg, file);
362}
363