1/*	$NetBSD: check.c,v 1.5 2021/04/10 19:49:59 nia Exp $	*/
2
3#ifdef HAVE_NBTOOL_CONFIG_H
4#include "nbtool_config.h"
5#else
6#if HAVE_CONFIG_H
7#include "config.h"
8#endif
9#include <nbcompat.h>
10#if HAVE_SYS_CDEFS_H
11#include <sys/cdefs.h>
12#endif
13#endif
14__RCSID("$NetBSD: check.c,v 1.5 2021/04/10 19:49:59 nia Exp $");
15
16/*-
17 * Copyright (c) 1999-2008 The NetBSD Foundation, Inc.
18 * All rights reserved.
19 *
20 * This code is derived from software contributed to The NetBSD Foundation
21 * by Hubert Feyrer <hubert@feyrer.de>.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
25 * are met:
26 * 1. Redistributions of source code must retain the above copyright
27 *    notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 *    notice, this list of conditions and the following disclaimer in the
30 *    documentation and/or other materials provided with the distribution.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
33 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
34 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
36 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 * POSSIBILITY OF SUCH DAMAGE.
43 */
44
45#if HAVE_SYS_TYPES_H
46#include <sys/types.h>
47#endif
48#if HAVE_SYS_STAT_H
49#include <sys/stat.h>
50#endif
51#if HAVE_DIRENT_H
52#include <dirent.h>
53#endif
54#if HAVE_ERR_H
55#include <err.h>
56#endif
57#if HAVE_ERRNO_H
58#include <errno.h>
59#endif
60#if HAVE_FCNTL_H
61#include <fcntl.h>
62#endif
63#ifndef NETBSD
64#include <nbcompat/md5.h>
65#else
66#include <md5.h>
67#endif
68#if HAVE_LIMITS_H
69#include <limits.h>
70#endif
71#if HAVE_STDIO_H
72#include <stdio.h>
73#endif
74#if HAVE_STRING_H
75#include <string.h>
76#endif
77
78#include "admin.h"
79#include "lib.h"
80
81static int checkpattern_fn(const char *, void *);
82
83/*
84 * Assumes CWD is in the database directory ($PREFIX/pkgdb/<pkg>)!
85 */
86static void
87check1pkg(const char *pkgdir, int *filecnt, int *pkgcnt)
88{
89	FILE   *f;
90	plist_t *p;
91	package_t Plist;
92	char   *PkgName, *dirp = NULL, *md5file;
93	char    file[MaxPathSize];
94	char   *content;
95
96	content = pkgdb_pkg_file(pkgdir, CONTENTS_FNAME);
97	f = fopen(content, "r");
98	if (f == NULL)
99		err(EXIT_FAILURE, "can't open %s", content);
100	free(content);
101
102	read_plist(&Plist, f);
103	p = find_plist(&Plist, PLIST_NAME);
104	if (p == NULL)
105		errx(EXIT_FAILURE, "Package %s has no @name, aborting.",
106		    pkgdir);
107	PkgName = p->name;
108	for (p = Plist.head; p; p = p->next) {
109		switch (p->type) {
110		case PLIST_FILE:
111			if (dirp == NULL) {
112				warnx("dirp not initialized, please send-pr!");
113				abort();
114			}
115
116			(void) snprintf(file, sizeof(file), "%s/%s", dirp, p->name);
117
118			if (isfile(file) || islinktodir(file)) {
119				if (p->next && p->next->type == PLIST_COMMENT) {
120					if (strncmp(p->next->name, CHECKSUM_HEADER, ChecksumHeaderLen) == 0) {
121						if ((md5file = MD5File(file, NULL)) != NULL) {
122							/* Mismatch? */
123							if (strcmp(md5file, p->next->name + ChecksumHeaderLen) != 0)
124								printf("%s fails MD5 checksum\n", file);
125
126							free(md5file);
127						}
128					} else if (strncmp(p->next->name, SYMLINK_HEADER, SymlinkHeaderLen) == 0) {
129						char	buf[MaxPathSize + SymlinkHeaderLen];
130						int	cc;
131
132						(void) strlcpy(buf, SYMLINK_HEADER, sizeof(buf));
133						if ((cc = readlink(file, &buf[SymlinkHeaderLen],
134							  sizeof(buf) - SymlinkHeaderLen - 1)) < 0) {
135							warnx("can't readlink `%s'", file);
136						} else {
137							buf[SymlinkHeaderLen + cc] = 0x0;
138							if (strcmp(buf, p->next->name) != 0) {
139								printf("symlink (%s) is not same as recorded value, %s: %s\n",
140								    file, buf, p->next->name);
141							}
142						}
143					}
144				}
145
146				(*filecnt)++;
147			} else if (isbrokenlink(file)) {
148				warnx("%s: Symlink `%s' exists and is in %s but target does not exist!", PkgName, file, CONTENTS_FNAME);
149			} else {
150				warnx("%s: File `%s' is in %s but not on filesystem!", PkgName, file, CONTENTS_FNAME);
151			}
152			break;
153		case PLIST_CWD:
154			if (strcmp(p->name, ".") != 0)
155				dirp = p->name;
156			else
157				dirp = pkgdb_pkg_dir(pkgdir);
158			break;
159		case PLIST_IGNORE:
160			p = p->next;
161			break;
162		case PLIST_SHOW_ALL:
163		case PLIST_SRC:
164		case PLIST_CMD:
165		case PLIST_CHMOD:
166		case PLIST_CHOWN:
167		case PLIST_CHGRP:
168		case PLIST_COMMENT:
169		case PLIST_NAME:
170		case PLIST_UNEXEC:
171		case PLIST_DISPLAY:
172		case PLIST_PKGDEP:
173		case PLIST_DIR_RM:
174		case PLIST_OPTION:
175		case PLIST_PKGCFL:
176		case PLIST_BLDDEP:
177		case PLIST_PKGDIR:
178			break;
179		}
180	}
181	free_plist(&Plist);
182	fclose(f);
183	(*pkgcnt)++;
184}
185
186struct checkpattern_arg {
187	int filecnt;
188	int pkgcnt;
189	int got_match;
190};
191
192static int
193checkpattern_fn(const char *pkg, void *vp)
194{
195	struct checkpattern_arg *arg = vp;
196
197	check1pkg(pkg, &arg->filecnt, &arg->pkgcnt);
198	if (!quiet)
199		printf(".");
200
201	arg->got_match = 1;
202
203	return 0;
204}
205
206static void
207check_pkg(const char *pkg, int *filecnt, int *pkgcnt, int allow_unmatched)
208{
209	struct checkpattern_arg arg;
210	char *pattern;
211
212	arg.filecnt = *filecnt;
213	arg.pkgcnt = *pkgcnt;
214	arg.got_match = 0;
215
216	if (match_installed_pkgs(pkg, checkpattern_fn, &arg) == -1)
217		errx(EXIT_FAILURE, "Cannot process pkdbdb");
218	if (arg.got_match != 0) {
219		*filecnt = arg.filecnt;
220		*pkgcnt = arg.pkgcnt;
221		return;
222	}
223
224	if (ispkgpattern(pkg)) {
225		if (allow_unmatched)
226			return;
227		errx(EXIT_FAILURE, "No matching pkg for %s.", pkg);
228	}
229
230	pattern = xasprintf("%s-[0-9]*", pkg);
231
232	if (match_installed_pkgs(pattern, checkpattern_fn, &arg) == -1)
233		errx(EXIT_FAILURE, "Cannot process pkdbdb");
234
235	if (arg.got_match == 0)
236		errx(EXIT_FAILURE, "cannot find package %s", pkg);
237	free(pattern);
238
239	*filecnt = arg.filecnt;
240	*pkgcnt = arg.pkgcnt;
241}
242
243void
244check(char **argv)
245{
246	int filecnt, pkgcnt;
247
248	filecnt = 0;
249	pkgcnt = 0;
250	setbuf(stdout, NULL);
251
252	if (*argv == NULL) {
253		check_pkg("*", &filecnt, &pkgcnt, 1);
254	} else {
255		for (; *argv != NULL; ++argv)
256			check_pkg(*argv, &filecnt, &pkgcnt, 0);
257	}
258
259	printf("\n");
260	printf("Checked %d file%s from %d package%s.\n",
261	    filecnt, (filecnt == 1) ? "" : "s",
262	    pkgcnt, (pkgcnt == 1) ? "" : "s");
263}
264