xref: /illumos-gate/usr/src/lib/libpkg/common/srchcfile.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 #include <stdio.h>
31 #include <limits.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <ctype.h>
36 #include <sys/types.h>
37 #include <libintl.h>
38 #include "pkglib.h"
39 #include "pkgstrct.h"
40 #include "pkglocale.h"
41 #include "pkglibmsgs.h"
42 
43 /*
44  * Forward declarations
45  */
46 
47 static int	getend(char **cp);
48 static int	getstr(char **cp, int n, char *str, int separator[]);
49 
50 /* from gpkgmap.c */
51 int	getnumvfp(char **cp, int base, long *d, long bad);
52 int	getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad);
53 
54 /*
55  * Module globals
56  */
57 
58 static char	lpath[PATH_MAX];	/* for ept->path */
59 static char	mylocal[PATH_MAX];	/* for ept->ainfo.local */
60 static int	decisionTableInit = 0;
61 
62 /*
63  * These arrays must be indexable by an unsigned char.
64  */
65 
66 static int	ISWORDSEP[UCHAR_MAX+1];
67 static int	ISPKGNAMESEP[UCHAR_MAX+1];
68 
69 /*
70  * Name:	COPYPATH
71  * Description:	copy path limiting size to destination capacity
72  * Arguments:	DEST - (char []) - [RW]
73  *		SRC - (char *) - [RO, *RO]
74  *			Pointer to first byte of path to copy
75  *		LEN - (int) - [RO]
76  *			Number of bytes to copy
77  */
78 
79 #define	COPYPATH(DEST, SRC, LEN)					\
80 	{								\
81 		/* assure return path does not overflow */		\
82 		if ((LEN) > sizeof ((DEST))) {				\
83 			(LEN) = sizeof ((DEST))-1;			\
84 		}							\
85 		/* copy return path to local storage */			\
86 		(void) memcpy((DEST), (SRC), (LEN));			\
87 		(DEST)[(LEN)] = '\0';					\
88 	}
89 
90 /*
91  * Name:	srchcfile
92  * Description:	search contents file looking for closest match to entry,
93  *		creating a new contents file if output contents file specified
94  * Arguments:	ept - (struct cfent *) - [RO, *RW]
95  *			- contents file entry, describing last item found
96  *		path - (char *) - [RO, *RO]
97  *			- path to search for in contents file
98  *			- If path is "*", then the next entry is returned;
99  *				the next entry always matches this path
100  *		PKGserver
101  *			- our door to the database server.
102  *
103  * Returns:	int
104  *		< 0 - error occurred
105  *			- Use getErrstr to retrieve character-string describing
106  *			  the reason for failure
107  *		== 0 - no match found
108  *			- specified path not in the contents file
109  *		== 1 - exact match found
110  *			- specified path found in contents file
111  *			- this value is always returned if path is "*" and the
112  *			  next entry is returned - 0 is returned when no more
113  *			  entries are left to process
114  * Side Effects:
115  *		- The ept structure supplied is filled in with a description of
116  *		  the item that caused the search to terminate, except in the
117  *		  case of '0' in which case the contents of 'ept' is undefined.
118  *		- NOTE: the ept->path item points to a path that is statically
119  *		  allocated and will be overwritten on the next call.
120  *		- NOTE: the ept->ainfo.local item points to a path that is
121  *		  statically allocated and will be overwritten on the next call.
122  */
123 
124 int
125 srchcfile(struct cfent *ept, char *path, PKGserver server)
126 {
127 	char		*cpath_start = NULL;
128 	char		classname[CLSSIZ+1];
129 	char		pkgname[PKGSIZ+1];
130 	int		anypath = 0;
131 	int		c;
132 	int		cpath_len = 0;
133 	struct pinfo	*lastpinfo;
134 	struct pinfo	*pinfo;
135 	char		*p;
136 	char		*curbuf;
137 	int		linelen;	/* includes NUL */
138 
139 	/*
140 	 * this code does not use nested subroutines because execution time
141 	 * of this routine is especially critical to installation and upgrade
142 	 */
143 
144 	/* initialize local variables */
145 
146 	setErrstr(NULL);	/* no error message currently cached */
147 	lpath[0] = '\0';
148 	lpath[sizeof (lpath)-1] = '\0';
149 
150 	/* initialize ept structure values */
151 
152 	(void) strlcpy(ept->ainfo.group, BADGROUP, sizeof (ept->ainfo.group));
153 	(void) strlcpy(ept->ainfo.owner, BADOWNER, sizeof (ept->ainfo.owner));
154 	(void) strlcpy(ept->pkg_class, BADCLASS,  sizeof (ept->pkg_class));
155 	ept->ainfo.local = (char *)NULL;
156 	ept->ainfo.mode = BADMODE;
157 	ept->cinfo.cksum = BADCONT;
158 	ept->cinfo.modtime = BADCONT;
159 	ept->cinfo.size = (fsblkcnt_t)BADCONT;
160 	ept->ftype = BADFTYPE;
161 	ept->npkgs = 0;
162 	ept->path = (char *)NULL;
163 	ept->pinfo = (struct pinfo *)NULL;
164 	ept->pkg_class_idx = -1;
165 	ept->volno = 0;
166 
167 	/*
168 	 * populate decision tables that implement fast character checking;
169 	 * this is much faster than the equivalent strpbrk() call or a
170 	 * while() loop checking for the characters. It is only faster if
171 	 * there are at least 3 characters to scan for - when checking for
172 	 * one or two characters (such as '\n' or '\0') its faster to do
173 	 * a simple while() loop.
174 	 */
175 
176 	if (decisionTableInit == 0) {
177 		/*
178 		 * any chars listed stop scan;
179 		 * scan stops on first byte found that is set to '1' below
180 		 */
181 
182 		/*
183 		 * Separators for normal words
184 		 */
185 		bzero(ISWORDSEP, sizeof (ISWORDSEP));
186 		ISWORDSEP[' '] = 1;
187 		ISWORDSEP['\t'] = 1;
188 		ISWORDSEP['\n'] = 1;
189 		ISWORDSEP['\0'] = 1;
190 
191 		/*
192 		 * Separators for list of packages, includes \\ for
193 		 * alternate ftype and : for classname
194 		 */
195 		bzero(ISPKGNAMESEP, sizeof (ISPKGNAMESEP));
196 		ISPKGNAMESEP[' '] = 1;
197 		ISPKGNAMESEP['\t'] = 1;
198 		ISPKGNAMESEP['\n'] = 1;
199 		ISPKGNAMESEP[':'] = 1;
200 		ISPKGNAMESEP['\\'] = 1;
201 		ISPKGNAMESEP['\0'] = 1;
202 
203 		decisionTableInit = 1;
204 	}
205 
206 	/* if the path to scan for is empty, act like no path was specified */
207 
208 	if ((path != NULL) && (*path == '\0')) {
209 		path = NULL;
210 	}
211 
212 	/*
213 	 * if path to search for is "*", then we will return the first path
214 	 * we encounter as a match, otherwise we return an error
215 	 */
216 
217 	if ((path != NULL) && (path[0] != '/')) {
218 		if (strcmp(path, "*") != 0) {
219 			setErrstr(pkg_gt(ERR_ILLEGAL_SEARCH_PATH));
220 			return (-1);
221 		}
222 		anypath = 1;
223 	}
224 
225 	/* attempt to narrow down the search for the specified path */
226 
227 	if (anypath == 0 && path == NULL)
228 		return (0);
229 
230 	/* determine first character of the next entry */
231 	if (anypath == 0)
232 		curbuf = pkggetentry_named(server, path, &linelen, &cpath_len);
233 	else
234 		curbuf = pkggetentry(server, &linelen, &cpath_len);
235 
236 	if (curbuf == NULL)
237 		return (0);
238 
239 	/*
240 	 * current entry DOES start with absolute path
241 	 * set ept->path to point to lpath
242 	 * set cpath_start/cpath_len to point to the file name
243 	 */
244 
245 	/* copy first token into path element of passed structure */
246 
247 	cpath_start = curbuf;
248 
249 	p = cpath_start + cpath_len;
250 
251 	ept->path = lpath;
252 
253 	/* copy path found to 'lpath' */
254 	COPYPATH(lpath, cpath_start, cpath_len);
255 
256 	/* get first character following the end of the path */
257 
258 	c = *p++;
259 
260 	/*
261 	 * we want to return information about this path in
262 	 * the structure provided, so parse any local path
263 	 * and jump to code which parses rest of the input line
264 	 */
265 	if (c == '=') {
266 		/* parse local path specification */
267 		if (getstr(&p, PATH_MAX, mylocal, ISWORDSEP)) {
268 			setErrstr(ERR_CANNOT_READ_LL_PATH);
269 			return (-1);
270 		}
271 		ept->ainfo.local = mylocal;
272 	}
273 
274 	/*
275 	 * if an exact match and processing a new style entry, read the
276 	 * remaining information from the new style entry.
277 	 */
278 
279 	while (isspace((c = *p++)))
280 		;
281 
282 	switch (c) {
283 	case '?': case 'f': case 'v': case 'e': case 'l':
284 	case 's': case 'p': case 'c': case 'b': case 'd':
285 	case 'x':
286 		/* save ftype */
287 		ept->ftype = (char)c;
288 
289 		/* save class */
290 		if (getstr(&p, CLSSIZ, ept->pkg_class, ISWORDSEP)) {
291 			setErrstr(ERR_CANNOT_READ_CLASS_TOKEN);
292 			return (-1);
293 		}
294 		break; /* we already read the pathname */
295 
296 	case '\0':
297 		/* end of line before new-line seen */
298 		setErrstr(ERR_INCOMPLETE_ENTRY);
299 		return (-1);
300 
301 	case '0': case '1': case '2': case '3': case '4':
302 	case '5': case '6': case '7': case '8': case '9':
303 		setErrstr(ERR_VOLUMENO_UNEXPECTED);
304 		return (-1);
305 
306 	case 'i':
307 		setErrstr(ERR_FTYPE_I_UNEXPECTED);
308 		return (-1);
309 
310 	default:
311 		/* unknown ftype */
312 		setErrstr(ERR_UNKNOWN_FTYPE);
313 		return (-1);
314 	}
315 
316 	/* link/symbolic link must have link destination */
317 
318 	if (((ept->ftype == 's') || (ept->ftype == 'l')) &&
319 	    (ept->ainfo.local == NULL)) {
320 		setErrstr(ERR_NO_LINK_SOURCE_SPECIFIED);
321 		return (-1);
322 	}
323 
324 	/* character/block devices have major/minor device numbers */
325 
326 	if (((ept->ftype == 'c') || (ept->ftype == 'b'))) {
327 		ept->ainfo.major = BADMAJOR;
328 		ept->ainfo.minor = BADMINOR;
329 		if (getnumvfp(&p, 10, (long *)&ept->ainfo.major, BADMAJOR) ||
330 		    getnumvfp(&p, 10, (long *)&ept->ainfo.minor, BADMINOR)) {
331 			setErrstr(pkg_gt(ERR_CANNOT_READ_MM_NUMS));
332 			return (-1);
333 		}
334 	}
335 
336 	/* most types have mode, owner, group identification components */
337 
338 	if ((ept->ftype == 'd') || (ept->ftype == 'x') || (ept->ftype == 'c') ||
339 	    (ept->ftype == 'b') || (ept->ftype == 'p') ||
340 	    (ept->ftype == 'f') || (ept->ftype == 'v') ||
341 	    (ept->ftype == 'e')) {
342 		/* mode, owner, group should be here */
343 		if (getnumvfp(&p, 8, (long *)&ept->ainfo.mode, BADMODE) ||
344 		    getstr(&p, sizeof (ept->ainfo.owner), ept->ainfo.owner,
345 		    ISWORDSEP) ||
346 		    getstr(&p, sizeof (ept->ainfo.group), ept->ainfo.group,
347 		    ISWORDSEP)) {
348 			setErrstr(ERR_CANNOT_READ_MOG);
349 			return (-1);
350 		}
351 	}
352 
353 	/* i/f/v/e have size, checksum, modification time components */
354 
355 	if ((ept->ftype == 'i') || (ept->ftype == 'f') ||
356 	    (ept->ftype == 'v') || (ept->ftype == 'e')) {
357 		/* look for content description */
358 		if (getlnumvfp(&p, 10, (fsblkcnt_t *)&ept->cinfo.size,
359 		    BADCONT) ||
360 		    getnumvfp(&p, 10, (long *)&ept->cinfo.cksum, BADCONT) ||
361 		    getnumvfp(&p, 10, (long *)&ept->cinfo.modtime, BADCONT)) {
362 			setErrstr(ERR_CANNOT_READ_CONTENT_INFO);
363 			return (-1);
364 		}
365 	}
366 
367 	/* i files processing is completed - return 'exact match found' */
368 
369 	if (ept->ftype == 'i') {
370 		return (1);
371 	}
372 
373 	/*
374 	 * determine list of packages which reference this entry
375 	 */
376 
377 	lastpinfo = (struct pinfo *)NULL;
378 	while ((c = getstr(&p, sizeof (pkgname), pkgname, ISPKGNAMESEP)) <= 0) {
379 		/* if c < 0 the string was too long to fix in the buffer */
380 
381 		if (c < 0) {
382 			setErrstr(ERR_PACKAGE_NAME_TOO_LONG);
383 			return (-1);
384 		}
385 
386 		/* a package is present - create and populate pinfo structure */
387 
388 		pinfo = (struct pinfo *)calloc(1, sizeof (struct pinfo));
389 		if (!pinfo) {
390 			setErrstr(ERR_NO_MEMORY);
391 			return (-1);
392 		}
393 		if (!lastpinfo) {
394 			ept->pinfo = pinfo; /* first one */
395 		} else {
396 			lastpinfo->next = pinfo; /* link list */
397 		}
398 		lastpinfo = pinfo;
399 
400 		if ((pkgname[0] == '-') || (pkgname[0] == '+') ||
401 		    (pkgname[0] == '*') || (pkgname[0] == '~') ||
402 		    (pkgname[0] == '!') || (pkgname[0] == '%')) {
403 			pinfo->status = pkgname[0];
404 			(void) strlcpy(pinfo->pkg, pkgname+1,
405 			    sizeof (pinfo->pkg));
406 		} else {
407 			(void) strlcpy(pinfo->pkg, pkgname,
408 			    sizeof (pinfo->pkg));
409 		}
410 
411 		/* pkg/[:[ftype][:class] */
412 		c = *p++;
413 		if (c == '\\') {
414 			/* get alternate ftype */
415 			pinfo->editflag++;
416 			c = *p++;
417 		}
418 
419 		if (c == ':') {
420 			/* get special classname */
421 			(void) getstr(&p, sizeof (classname), classname,
422 			    ISWORDSEP);
423 			(void) strlcpy(pinfo->aclass, classname,
424 			    sizeof (pinfo->aclass));
425 			c = *p++;
426 		}
427 		ept->npkgs++;
428 
429 		/* break out of while if at end of entry */
430 
431 		if ((c == '\n') || (c == '\0')) {
432 			break;
433 		}
434 
435 		/* if package not separated by a space return an error */
436 
437 		if (!isspace(c)) {
438 			setErrstr(ERR_BAD_ENTRY_END);
439 			return (-1);
440 		}
441 	}
442 
443 	/*
444 	 * parsing of the entry is complete
445 	 */
446 
447 	/* if not at the end of the entry, make it so */
448 
449 	if ((c != '\n') && (c != '\0')) {
450 		if (getend(&p) && ept->pinfo) {
451 			setErrstr(ERR_EXTRA_TOKENS);
452 			return (-1);
453 		}
454 	}
455 
456 	return (1);
457 }
458 
459 static int
460 getstr(char **cp, int n, char *str, int separator[])
461 {
462 	int	c;
463 	char	*p = *cp;
464 	char	*p1;
465 	size_t	len;
466 
467 	if (*p == '\0') {
468 		return (1);
469 	}
470 
471 	/* leading white space ignored */
472 
473 	while (((c = *p) != '\0') && (isspace(*p++)))
474 		;
475 	if ((c == '\0') || (c == '\n')) {
476 		p--;
477 		*cp = p;
478 		return (1); /* nothing there */
479 	}
480 
481 	p--;
482 
483 	/* compute length based on delimiter found or not */
484 
485 	p1 = p;
486 	while (separator[(int)(*(unsigned char *)p1)] == 0) {
487 		p1++;
488 	}
489 
490 	len = (ptrdiff_t)p1 - (ptrdiff_t)p;
491 
492 	/* if string will fit in result buffer copy string and return success */
493 
494 	if (len < n) {
495 		(void) memcpy(str, p, len);
496 		str[len] = '\0';
497 		p += len;
498 		*cp = p;
499 		return (0);
500 	}
501 
502 	/* result buffer too small; copy partial string, return error */
503 	(void) memcpy(str, p, n-1);
504 	str[n-1] = '\0';
505 	p += n;
506 	*cp = p;
507 	return (-1);
508 }
509 
510 static int
511 getend(char **cp)
512 {
513 	int	n;
514 	char	*p = *cp;
515 
516 	n = 0;
517 
518 	/* if at end of buffer return no more characters left */
519 
520 	if (*p == '\0') {
521 		return (0);
522 	}
523 
524 	while ((*p != '\0') && (*p != '\n')) {
525 		if (n == 0) {
526 			if (!isspace(*p)) {
527 				n++;
528 			}
529 		}
530 		p++;
531 	}
532 
533 	*cp = ++p;
534 	return (n);
535 }
536