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
srchcfile(struct cfent * ept,char * path,PKGserver server)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
getstr(char ** cp,int n,char * str,int separator[])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
getend(char ** cp)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