/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include "pkglib.h" #include "pkgstrct.h" #include "pkglocale.h" #include "pkglibmsgs.h" /* * Forward declarations */ static int getend(char **cp); static int getstr(char **cp, int n, char *str, int separator[]); /* from gpkgmap.c */ int getnumvfp(char **cp, int base, long *d, long bad); int getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad); /* * Module globals */ static char lpath[PATH_MAX]; /* for ept->path */ static char mylocal[PATH_MAX]; /* for ept->ainfo.local */ static int decisionTableInit = 0; /* * These arrays must be indexable by an unsigned char. */ static int ISWORDSEP[UCHAR_MAX+1]; static int ISPKGNAMESEP[UCHAR_MAX+1]; /* * Name: COPYPATH * Description: copy path limiting size to destination capacity * Arguments: DEST - (char []) - [RW] * SRC - (char *) - [RO, *RO] * Pointer to first byte of path to copy * LEN - (int) - [RO] * Number of bytes to copy */ #define COPYPATH(DEST, SRC, LEN) \ { \ /* assure return path does not overflow */ \ if ((LEN) > sizeof ((DEST))) { \ (LEN) = sizeof ((DEST))-1; \ } \ /* copy return path to local storage */ \ (void) memcpy((DEST), (SRC), (LEN)); \ (DEST)[(LEN)] = '\0'; \ } /* * Name: srchcfile * Description: search contents file looking for closest match to entry, * creating a new contents file if output contents file specified * Arguments: ept - (struct cfent *) - [RO, *RW] * - contents file entry, describing last item found * path - (char *) - [RO, *RO] * - path to search for in contents file * - If path is "*", then the next entry is returned; * the next entry always matches this path * PKGserver * - our door to the database server. * * Returns: int * < 0 - error occurred * - Use getErrstr to retrieve character-string describing * the reason for failure * == 0 - no match found * - specified path not in the contents file * == 1 - exact match found * - specified path found in contents file * - this value is always returned if path is "*" and the * next entry is returned - 0 is returned when no more * entries are left to process * Side Effects: * - The ept structure supplied is filled in with a description of * the item that caused the search to terminate, except in the * case of '0' in which case the contents of 'ept' is undefined. * - NOTE: the ept->path item points to a path that is statically * allocated and will be overwritten on the next call. * - NOTE: the ept->ainfo.local item points to a path that is * statically allocated and will be overwritten on the next call. */ int srchcfile(struct cfent *ept, char *path, PKGserver server) { char *cpath_start = NULL; char classname[CLSSIZ+1]; char pkgname[PKGSIZ+1]; int anypath = 0; int c; int cpath_len = 0; struct pinfo *lastpinfo; struct pinfo *pinfo; char *p; char *curbuf; int linelen; /* includes NUL */ /* * this code does not use nested subroutines because execution time * of this routine is especially critical to installation and upgrade */ /* initialize local variables */ setErrstr(NULL); /* no error message currently cached */ lpath[0] = '\0'; lpath[sizeof (lpath)-1] = '\0'; /* initialize ept structure values */ (void) strlcpy(ept->ainfo.group, BADGROUP, sizeof (ept->ainfo.group)); (void) strlcpy(ept->ainfo.owner, BADOWNER, sizeof (ept->ainfo.owner)); (void) strlcpy(ept->pkg_class, BADCLASS, sizeof (ept->pkg_class)); ept->ainfo.local = (char *)NULL; ept->ainfo.mode = BADMODE; ept->cinfo.cksum = BADCONT; ept->cinfo.modtime = BADCONT; ept->cinfo.size = (fsblkcnt_t)BADCONT; ept->ftype = BADFTYPE; ept->npkgs = 0; ept->path = (char *)NULL; ept->pinfo = (struct pinfo *)NULL; ept->pkg_class_idx = -1; ept->volno = 0; /* * populate decision tables that implement fast character checking; * this is much faster than the equivalent strpbrk() call or a * while() loop checking for the characters. It is only faster if * there are at least 3 characters to scan for - when checking for * one or two characters (such as '\n' or '\0') its faster to do * a simple while() loop. */ if (decisionTableInit == 0) { /* * any chars listed stop scan; * scan stops on first byte found that is set to '1' below */ /* * Separators for normal words */ bzero(ISWORDSEP, sizeof (ISWORDSEP)); ISWORDSEP[' '] = 1; ISWORDSEP['\t'] = 1; ISWORDSEP['\n'] = 1; ISWORDSEP['\0'] = 1; /* * Separators for list of packages, includes \\ for * alternate ftype and : for classname */ bzero(ISPKGNAMESEP, sizeof (ISPKGNAMESEP)); ISPKGNAMESEP[' '] = 1; ISPKGNAMESEP['\t'] = 1; ISPKGNAMESEP['\n'] = 1; ISPKGNAMESEP[':'] = 1; ISPKGNAMESEP['\\'] = 1; ISPKGNAMESEP['\0'] = 1; decisionTableInit = 1; } /* if the path to scan for is empty, act like no path was specified */ if ((path != NULL) && (*path == '\0')) { path = NULL; } /* * if path to search for is "*", then we will return the first path * we encounter as a match, otherwise we return an error */ if ((path != NULL) && (path[0] != '/')) { if (strcmp(path, "*") != 0) { setErrstr(pkg_gt(ERR_ILLEGAL_SEARCH_PATH)); return (-1); } anypath = 1; } /* attempt to narrow down the search for the specified path */ if (anypath == 0 && path == NULL) return (0); /* determine first character of the next entry */ if (anypath == 0) curbuf = pkggetentry_named(server, path, &linelen, &cpath_len); else curbuf = pkggetentry(server, &linelen, &cpath_len); if (curbuf == NULL) return (0); /* * current entry DOES start with absolute path * set ept->path to point to lpath * set cpath_start/cpath_len to point to the file name */ /* copy first token into path element of passed structure */ cpath_start = curbuf; p = cpath_start + cpath_len; ept->path = lpath; /* copy path found to 'lpath' */ COPYPATH(lpath, cpath_start, cpath_len); /* get first character following the end of the path */ c = *p++; /* * we want to return information about this path in * the structure provided, so parse any local path * and jump to code which parses rest of the input line */ if (c == '=') { /* parse local path specification */ if (getstr(&p, PATH_MAX, mylocal, ISWORDSEP)) { setErrstr(ERR_CANNOT_READ_LL_PATH); return (-1); } ept->ainfo.local = mylocal; } /* * if an exact match and processing a new style entry, read the * remaining information from the new style entry. */ while (isspace((c = *p++))) ; switch (c) { case '?': case 'f': case 'v': case 'e': case 'l': case 's': case 'p': case 'c': case 'b': case 'd': case 'x': /* save ftype */ ept->ftype = (char)c; /* save class */ if (getstr(&p, CLSSIZ, ept->pkg_class, ISWORDSEP)) { setErrstr(ERR_CANNOT_READ_CLASS_TOKEN); return (-1); } break; /* we already read the pathname */ case '\0': /* end of line before new-line seen */ setErrstr(ERR_INCOMPLETE_ENTRY); return (-1); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': setErrstr(ERR_VOLUMENO_UNEXPECTED); return (-1); case 'i': setErrstr(ERR_FTYPE_I_UNEXPECTED); return (-1); default: /* unknown ftype */ setErrstr(ERR_UNKNOWN_FTYPE); return (-1); } /* link/symbolic link must have link destination */ if (((ept->ftype == 's') || (ept->ftype == 'l')) && (ept->ainfo.local == NULL)) { setErrstr(ERR_NO_LINK_SOURCE_SPECIFIED); return (-1); } /* character/block devices have major/minor device numbers */ if (((ept->ftype == 'c') || (ept->ftype == 'b'))) { ept->ainfo.major = BADMAJOR; ept->ainfo.minor = BADMINOR; if (getnumvfp(&p, 10, (long *)&ept->ainfo.major, BADMAJOR) || getnumvfp(&p, 10, (long *)&ept->ainfo.minor, BADMINOR)) { setErrstr(pkg_gt(ERR_CANNOT_READ_MM_NUMS)); return (-1); } } /* most types have mode, owner, group identification components */ if ((ept->ftype == 'd') || (ept->ftype == 'x') || (ept->ftype == 'c') || (ept->ftype == 'b') || (ept->ftype == 'p') || (ept->ftype == 'f') || (ept->ftype == 'v') || (ept->ftype == 'e')) { /* mode, owner, group should be here */ if (getnumvfp(&p, 8, (long *)&ept->ainfo.mode, BADMODE) || getstr(&p, sizeof (ept->ainfo.owner), ept->ainfo.owner, ISWORDSEP) || getstr(&p, sizeof (ept->ainfo.group), ept->ainfo.group, ISWORDSEP)) { setErrstr(ERR_CANNOT_READ_MOG); return (-1); } } /* i/f/v/e have size, checksum, modification time components */ if ((ept->ftype == 'i') || (ept->ftype == 'f') || (ept->ftype == 'v') || (ept->ftype == 'e')) { /* look for content description */ if (getlnumvfp(&p, 10, (fsblkcnt_t *)&ept->cinfo.size, BADCONT) || getnumvfp(&p, 10, (long *)&ept->cinfo.cksum, BADCONT) || getnumvfp(&p, 10, (long *)&ept->cinfo.modtime, BADCONT)) { setErrstr(ERR_CANNOT_READ_CONTENT_INFO); return (-1); } } /* i files processing is completed - return 'exact match found' */ if (ept->ftype == 'i') { return (1); } /* * determine list of packages which reference this entry */ lastpinfo = (struct pinfo *)NULL; while ((c = getstr(&p, sizeof (pkgname), pkgname, ISPKGNAMESEP)) <= 0) { /* if c < 0 the string was too long to fix in the buffer */ if (c < 0) { setErrstr(ERR_PACKAGE_NAME_TOO_LONG); return (-1); } /* a package is present - create and populate pinfo structure */ pinfo = (struct pinfo *)calloc(1, sizeof (struct pinfo)); if (!pinfo) { setErrstr(ERR_NO_MEMORY); return (-1); } if (!lastpinfo) { ept->pinfo = pinfo; /* first one */ } else { lastpinfo->next = pinfo; /* link list */ } lastpinfo = pinfo; if ((pkgname[0] == '-') || (pkgname[0] == '+') || (pkgname[0] == '*') || (pkgname[0] == '~') || (pkgname[0] == '!') || (pkgname[0] == '%')) { pinfo->status = pkgname[0]; (void) strlcpy(pinfo->pkg, pkgname+1, sizeof (pinfo->pkg)); } else { (void) strlcpy(pinfo->pkg, pkgname, sizeof (pinfo->pkg)); } /* pkg/[:[ftype][:class] */ c = *p++; if (c == '\\') { /* get alternate ftype */ pinfo->editflag++; c = *p++; } if (c == ':') { /* get special classname */ (void) getstr(&p, sizeof (classname), classname, ISWORDSEP); (void) strlcpy(pinfo->aclass, classname, sizeof (pinfo->aclass)); c = *p++; } ept->npkgs++; /* break out of while if at end of entry */ if ((c == '\n') || (c == '\0')) { break; } /* if package not separated by a space return an error */ if (!isspace(c)) { setErrstr(ERR_BAD_ENTRY_END); return (-1); } } /* * parsing of the entry is complete */ /* if not at the end of the entry, make it so */ if ((c != '\n') && (c != '\0')) { if (getend(&p) && ept->pinfo) { setErrstr(ERR_EXTRA_TOKENS); return (-1); } } return (1); } static int getstr(char **cp, int n, char *str, int separator[]) { int c; char *p = *cp; char *p1; size_t len; if (*p == '\0') { return (1); } /* leading white space ignored */ while (((c = *p) != '\0') && (isspace(*p++))) ; if ((c == '\0') || (c == '\n')) { p--; *cp = p; return (1); /* nothing there */ } p--; /* compute length based on delimiter found or not */ p1 = p; while (separator[(int)(*(unsigned char *)p1)] == 0) { p1++; } len = (ptrdiff_t)p1 - (ptrdiff_t)p; /* if string will fit in result buffer copy string and return success */ if (len < n) { (void) memcpy(str, p, len); str[len] = '\0'; p += len; *cp = p; return (0); } /* result buffer too small; copy partial string, return error */ (void) memcpy(str, p, n-1); str[n-1] = '\0'; p += n; *cp = p; return (-1); } static int getend(char **cp) { int n; char *p = *cp; n = 0; /* if at end of buffer return no more characters left */ if (*p == '\0') { return (0); } while ((*p != '\0') && (*p != '\n')) { if (n == 0) { if (!isspace(*p)) { n++; } } p++; } *cp = ++p; return (n); }