/*
* 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 <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include "pkgstrct.h"
#include "pkglib.h"
#include "pkglibmsgs.h"
#include "pkglocale.h"
#define ERR_CANT_READ_LCLPATH "unable to read local pathname"
#define ERR_BAD_VOLUME_NUMBER "bad volume number"
#define ERR_CANNOT_READ_PATHNAME_FIELD "unable to read pathname field"
#define ERR_CANNOT_READ_CONTENT_INFO "unable to read content info"
#define ERR_EXTRA_TOKENS_PRESENT "extra tokens on input line"
#define ERR_CANNOT_READ_CLASS_TOKEN "unable to read class token"
#define ERR_BAD_LINK_SPEC "missing or invalid link specification"
#define ERR_UNKNOWN_FTYPE "unknown ftype"
#define ERR_NO_LINKSOURCE "no link source specified"
#define ERR_CANNOT_READ_MM_DEVNUMS "unable to read major/minor "\
"device numbers"
static int eatwhite(FILE *fp);
static int getend(FILE *fp);
static int getstr(FILE *fp, char *sep, int n, char *str);
static int getnum(FILE *fp, int base, long *d, long bad);
static int getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad);
static int getvalmode(FILE *fp, mode_t *d, long bad, int map);
static int getendvfp(char **cp);
static void findendvfp(char **cp);
static int getstrvfp(char **cp, char *sep, int n, char *str);
static int getvalmodevfp(char **cp, mode_t *d, long bad, int map);
int getnumvfp(char **cp, int base, long *d, long bad);
int getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad);
static char mypath[PATH_MAX];
static char mylocal[PATH_MAX];
static int mapmode = MAPNONE;
static char *maptype = "";
static mode_t d_mode = BADMODE;
static char *d_owner = BADOWNER;
static char *d_group = BADGROUP;
/*
* These determine how gpkgmap() deals with mode, owner and group defaults.
* It is assumed that the owner and group arguments represent static fields
* which will persist until attrdefault() is called.
*/
void
attrpreset(int mode, char *owner, char *group)
{
d_mode = mode;
d_owner = owner;
d_group = group;
}
void
attrdefault()
{
d_mode = NOMODE;
d_owner = NOOWNER;
d_group = NOGROUP;
}
/*
* This determines how gpkgmap() deals with environment variables in the
* mode, owner and group. Path is evaluated at a higher level based upon
* other circumstances.
*/
void
setmapmode(int mode)
{
if (mode >= 0 || mode <= 3) {
mapmode = mode;
if (mode == MAPBUILD)
maptype = " build";
else if (mode == MAPINSTALL)
maptype = " install";
else
maptype = "";
}
}
/* This is the external query interface for mapmode. */
int
getmapmode(void)
{
return (mapmode);
}
/*
* Unpack the pkgmap or the contents file or whatever file is in that format.
* Based upon mapmode, environment parameters will be resolved for mode,
* owner and group.
*/
int
gpkgmap(struct cfent *ept, FILE *fp)
{
int c;
boolean_t first_char = B_TRUE;
setErrstr(NULL);
ept->volno = 0;
ept->ftype = BADFTYPE;
(void) strcpy(ept->pkg_class, BADCLASS);
ept->pkg_class_idx = -1;
ept->path = NULL;
ept->ainfo.local = NULL;
/* default attributes were supplied, so don't reset */
ept->ainfo.mode = d_mode;
(void) strcpy(ept->ainfo.owner, d_owner);
(void) strcpy(ept->ainfo.group, d_group);
ept->ainfo.major = BADMAJOR;
ept->ainfo.minor = BADMINOR;
ept->cinfo.cksum = ept->cinfo.modtime = ept->cinfo.size = (-1L);
ept->npkgs = 0;
if (!fp)
return (-1);
readline:
c = eatwhite(fp);
/*
* If the first character is not a digit, we assume that the
* volume number is 1.
*/
if (first_char && !isdigit(c)) {
ept->volno = 1;
}
first_char = B_FALSE;
switch (c) {
case EOF:
return (0);
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (ept->volno) {
setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
goto error;
}
do {
ept->volno = (ept->volno*10)+c-'0';
c = getc(fp);
} while (isdigit(c));
if (ept->volno == 0)
ept->volno = 1;
goto readline;
case ':':
case '#':
(void) getend(fp);
/*FALLTHRU*/
case '\n':
/*
* Since we are going to scan the next line,
* we need to reset volume number and first_char.
*/
ept->volno = 0;
first_char = B_TRUE;
goto readline;
case 'i':
ept->ftype = (char)c;
c = eatwhite(fp);
/*FALLTHRU*/
case '.':
case '/':
(void) ungetc(c, fp);
if (getstr(fp, "=", PATH_MAX, mypath)) {
setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
goto error;
}
ept->path = mypath;
c = getc(fp);
if (c == '=') {
if (getstr(fp, NULL, PATH_MAX, mylocal)) {
setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
goto error;
}
ept->ainfo.local = mylocal;
} else
(void) ungetc(c, fp);
if (ept->ftype == 'i') {
/* content info might exist */
if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size,
BADCONT) &&
(getnum(fp, 10, (long *)&ept->cinfo.cksum,
BADCONT) ||
getnum(fp, 10, (long *)&ept->cinfo.modtime,
BADCONT))) {
setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
goto error;
}
}
if (getend(fp)) {
setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
return (-1);
}
return (1);
case '?':
case 'f':
case 'v':
case 'e':
case 'l':
case 's':
case 'p':
case 'c':
case 'b':
case 'd':
case 'x':
ept->ftype = (char)c;
if (getstr(fp, NULL, CLSSIZ, ept->pkg_class)) {
setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
goto error;
}
if (getstr(fp, "=", PATH_MAX, mypath)) {
setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
goto error;
}
ept->path = mypath;
c = getc(fp);
if (c == '=') {
/* local path */
if (getstr(fp, NULL, PATH_MAX, mylocal)) {
if (ept->ftype == 's' || ept->ftype == 'l') {
setErrstr(pkg_gt(ERR_READLINK));
} else {
setErrstr(
pkg_gt(ERR_CANT_READ_LCLPATH));
}
goto error;
}
ept->ainfo.local = mylocal;
} else if (strchr("sl", ept->ftype)) {
if ((c != EOF) && (c != '\n'))
(void) getend(fp);
setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
return (-1);
} else
(void) ungetc(c, fp);
break;
default:
setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
error:
(void) getend(fp);
return (-1);
}
if (strchr("sl", ept->ftype) && (ept->ainfo.local == NULL)) {
setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
goto error;
}
if (strchr("cb", ept->ftype)) {
ept->ainfo.major = BADMAJOR;
ept->ainfo.minor = BADMINOR;
if (getnum(fp, 10, (long *)&ept->ainfo.major, BADMAJOR) ||
getnum(fp, 10, (long *)&ept->ainfo.minor, BADMINOR)) {
setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
goto error;
}
}
/*
* Links and information files don't have attributes associated with
* them. The following either resolves potential variables or passes
* them through. Mode is tested for validity to some degree. BAD???
* is returned to indicate that no meaningful mode was provided. A
* higher authority will decide if that's OK or not. CUR??? means that
* the prototype file specifically requires a wildcard ('?') for
* that entry. We issue an error if attributes were entered wrong.
* We just return BAD??? if there was no entry at all.
*/
if (strchr("cbdxpfve", ept->ftype)) {
int retval;
if ((retval = getvalmode(fp, &(ept->ainfo.mode), CURMODE,
(mapmode != MAPNONE))) == 1)
goto end; /* nothing else on the line */
else if (retval == 2)
goto error; /* mode is too no good */
/* owner & group should be here */
if ((retval = getstr(fp, NULL, ATRSIZ,
ept->ainfo.owner)) == 1)
goto end; /* no owner or group - warning */
if (retval == -1) {
setErrstr(pkg_gt(ERR_OWNTOOLONG));
goto error;
}
if ((retval = getstr(fp, NULL, ATRSIZ,
ept->ainfo.group)) == 1)
goto end; /* no group - warning */
if (retval == -1) {
setErrstr(pkg_gt(ERR_GRPTOOLONG));
goto error;
}
/* Resolve the parameters if required. */
if (mapmode != MAPNONE) {
if (mapvar(mapmode, ept->ainfo.owner)) {
(void) snprintf(getErrbufAddr(),
getErrbufSize(),
pkg_gt(ERR_NOVAR),
maptype, ept->ainfo.owner);
setErrstr(getErrbufAddr());
goto error;
}
if (mapvar(mapmode, ept->ainfo.group)) {
(void) snprintf(getErrbufAddr(),
getErrbufSize(), pkg_gt(ERR_NOVAR),
maptype, ept->ainfo.group);
setErrstr(getErrbufAddr());
goto error;
}
}
}
if (strchr("ifve", ept->ftype)) {
/* look for content description */
if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
(getnum(fp, 10, (long *)&ept->cinfo.cksum, BADCONT) ||
getnum(fp, 10, (long *)&ept->cinfo.modtime, BADCONT))) {
setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
goto error;
}
}
if (ept->ftype == 'i')
goto end;
end:
if (getend(fp) && ept->pinfo) {
setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
return (-1);
}
done:
return (1);
}
/*
* Get and validate the mode attribute. This returns an error if
* 1. the mode string is too long
* 2. the mode string includes alpha characters
* 3. the mode string is not octal
* 4. mode string is an install parameter
* 5. mode is an unresolved build parameter and MAPBUILD is
* in effect.
* If the mode is a build parameter, it is
* 1. returned as is if MAPNONE is in effect
* 2. evaluated if MAPBUILD is in effect
*
* NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
* time. At install time we just fix a mode with bad bits set by
* setting it to CURMODE. This should be an error in a few releases
* (2.8 maybe) but faulty modes are so common in existing packages
* that this is a reasonable exception. -- JST 1994-11-9
*
* RETURNS
* 0 if mode is being returned as a valid value
* 1 if no attributes are present on the line
* 2 if there was a fundamental error
*/
static int
getvalmode(FILE *fp, mode_t *d, long bad, int map)
{
char tempmode[20];
mode_t tempmode_t;
int retval;
if ((retval = getstr(fp, NULL, ATRSIZ, tempmode)) == 1)
return (1);
else if (retval == -1) {
setErrstr(pkg_gt(ERR_MODELONG));
return (2);
} else {
/*
* If it isn't a '?' (meaning go with whatever mode is
* there), validate the mode and convert it to a mode_t. The
* "bad" variable here is a misnomer. It doesn't necessarily
* mean bad.
*/
if (tempmode[0] == '?') {
*d = WILDCARD;
} else {
/*
* Mode may not be an install parameter or a
* non-build parameter.
*/
if (tempmode[0] == '$' &&
(isupper(tempmode[1]) || !islower(tempmode[1]))) {
setErrstr(pkg_gt(ERR_IMODE));
return (2);
}
if ((map) && (mapvar(mapmode, tempmode))) {
(void) snprintf(getErrbufAddr(),
getErrbufSize(),
pkg_gt(ERR_NOVAR),
maptype, tempmode);
setErrstr(getErrbufAddr());
return (2);
}
if (tempmode[0] == '$') {
*d = BADMODE; /* may be a problem */
} else {
/*
* At this point it's supposed to be
* something we can convert to a number.
*/
int n = 0;
/*
* We reject it if it contains nonnumbers or
* it's not octal.
*/
while (tempmode[n] && !isspace(tempmode[n])) {
if (!isdigit(tempmode[n])) {
setErrstr(
pkg_gt(ERR_MODEALPHA));
return (2);
}
if (strchr("89abcdefABCDEF",
tempmode[n])) {
setErrstr(
pkg_gt(ERR_BASEINVAL));
return (2);
}
n++;
}
tempmode_t = strtol(tempmode, NULL, 8);
/*
* We reject it if it contains inappropriate
* bits.
*/
if (tempmode_t & ~(S_IAMB |
S_ISUID | S_ISGID | S_ISVTX)) {
if (mapmode != MAPBUILD) {
tempmode_t = bad;
} else {
setErrstr(pkg_gt(ERR_MODEBITS));
return (2);
}
}
*d = tempmode_t;
}
}
return (0);
}
}
static int
getnum(FILE *fp, int base, long *d, long bad)
{
int c, b;
/* leading white space ignored */
c = eatwhite(fp);
if (c == '?') {
*d = bad;
return (0);
}
if ((c == EOF) || (c == '\n') || !isdigit(c)) {
(void) ungetc(c, fp);
return (1);
}
*d = 0;
while (isdigit(c)) {
b = (c & 017);
if (b >= base)
return (2);
*d = (*d * base) + b;
c = getc(fp);
}
(void) ungetc(c, fp);
return (0);
}
static int
getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad)
{
int c, b;
/* leading white space ignored */
c = eatwhite(fp);
if (c == '?') {
*d = bad;
return (0);
}
if ((c == EOF) || (c == '\n') || !isdigit(c)) {
(void) ungetc(c, fp);
return (1);
}
*d = 0;
while (isdigit(c)) {
b = (c & 017);
if (b >= base)
return (2);
*d = (*d * base) + b;
c = getc(fp);
}
(void) ungetc(c, fp);
return (0);
}
/*
* Get a string from the file. Returns
* 0 if all OK
* 1 if nothing there
* -1 if string is too long
*/
static int
getstr(FILE *fp, char *sep, int n, char *str)
{
int c;
/* leading white space ignored */
c = eatwhite(fp);
if ((c == EOF) || (c == '\n')) {
(void) ungetc(c, fp);
return (1); /* nothing there */
}
/* fill up string until space, tab, or separator */
while (!strchr(" \t", c) && (!sep || !strchr(sep, c))) {
if (n-- < 1) {
*str = '\0';
return (-1); /* too long */
}
*str++ = (char)c;
c = getc(fp);
if ((c == EOF) || (c == '\n'))
break; /* no more on this line */
}
*str = '\0';
(void) ungetc(c, fp);
return (0);
}
static int
getend(FILE *fp)
{
int c;
int n;
n = 0;
do {
if ((c = getc(fp)) == EOF)
return (n);
if (!isspace(c))
n++;
} while (c != '\n');
return (n);
}
static int
eatwhite(FILE *fp)
{
int c;
/* this test works around a side effect of getc() */
if (feof(fp))
return (EOF);
do
c = getc(fp);
while ((c == ' ') || (c == '\t'));
return (c);
}
int
gpkgmapvfp(struct cfent *ept, VFP_T *vfp)
{
int c;
boolean_t first_char = B_TRUE;
(void) strlcpy(ept->pkg_class, BADCLASS, sizeof (ept->pkg_class));
(void) strlcpy(ept->ainfo.owner, d_owner, sizeof (ept->ainfo.owner));
(void) strlcpy(ept->ainfo.group, d_group, sizeof (ept->ainfo.group));
setErrstr(NULL);
ept->volno = 0;
ept->ftype = BADFTYPE;
ept->pkg_class_idx = -1;
ept->path = NULL;
ept->ainfo.local = NULL;
ept->ainfo.mode = d_mode;
ept->ainfo.major = BADMAJOR;
ept->ainfo.minor = BADMINOR;
ept->cinfo.cksum = (-1L);
ept->cinfo.modtime = (-1L);
ept->cinfo.size = (-1L);
ept->npkgs = 0;
/* return error if no vfp specified */
if (vfp == (VFP_T *)NULL) {
return (-1);
}
readline:
while (((c = vfpGetcNoInc(vfp)) != '\0') && (isspace(vfpGetc(vfp))))
;
/*
* If the first character is not a digit, we assume that the
* volume number is 1.
*/
if (first_char && !isdigit(c)) {
ept->volno = 1;
}
first_char = B_FALSE;
/*
* In case of hsfs the zero-padding of partial pages
* returned by mmap is not done properly. A separate bug has been filed
* on this.
*/
if (vfp->_vfpCurr && (vfp->_vfpCurr > vfp->_vfpEnd)) {
return (0);
}
switch (c) {
case '\0':
return (0);
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (ept->volno) {
setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
goto error;
}
do {
ept->volno = (ept->volno*10)+c-'0';
c = vfpGetc(vfp);
} while (isdigit(c));
if (ept->volno == 0) {
ept->volno = 1;
}
goto readline;
case ':':
case '#':
(void) findendvfp(&vfpGetCurrCharPtr(vfp));
/*FALLTHRU*/
case '\n':
/*
* Since we are going to scan the next line,
* we need to reset volume number and first_char.
*/
ept->volno = 0;
first_char = B_TRUE;
goto readline;
case 'i':
ept->ftype = (char)c;
while (((c = vfpGetcNoInc(vfp)) != '\0') &&
(isspace(vfpGetc(vfp))))
;
/*FALLTHRU*/
case '.':
case '/':
vfpDecCurrPtr(vfp);
if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
goto error;
}
ept->path = mypath;
c = vfpGetc(vfp);
if (c == '=') {
if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, PATH_MAX,
mylocal)) {
setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
goto error;
}
ept->ainfo.local = mylocal;
} else {
vfpDecCurrPtr(vfp);
}
if (ept->ftype == 'i') {
/* content info might exist */
if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
(fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
(getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
(long *)&ept->cinfo.cksum, BADCONT) ||
getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
(long *)&ept->cinfo.modtime, BADCONT))) {
setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
goto error;
}
}
if (getendvfp(&vfpGetCurrCharPtr(vfp))) {
setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
return (-1);
}
return (1);
case '?':
case 'f':
case 'v':
case 'e':
case 'l':
case 's':
case 'p':
case 'c':
case 'b':
case 'd':
case 'x':
ept->ftype = (char)c;
if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
CLSSIZ, ept->pkg_class)) {
setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
goto error;
}
if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
goto error;
}
ept->path = mypath;
c = vfpGetc(vfp);
if (c == '=') {
/* local path */
if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
PATH_MAX, mylocal)) {
if (ept->ftype == 's' || ept->ftype == 'l') {
setErrstr(pkg_gt(ERR_READLINK));
} else {
setErrstr(
pkg_gt(ERR_CANT_READ_LCLPATH));
}
goto error;
}
ept->ainfo.local = mylocal;
} else if ((ept->ftype == 's') || (ept->ftype == 'l')) {
if ((c != '\0') && (c != '\n'))
(void) findendvfp(&vfpGetCurrCharPtr(vfp));
setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
return (-1);
} else {
vfpDecCurrPtr(vfp);
}
break;
default:
setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
error:
(void) findendvfp(&vfpGetCurrCharPtr(vfp));
return (-1);
}
if (((ept->ftype == 's') || (ept->ftype == 'l')) &&
(ept->ainfo.local == NULL)) {
setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
goto error;
}
if (((ept->ftype == 'c') || (ept->ftype == 'b'))) {
ept->ainfo.major = BADMAJOR;
ept->ainfo.minor = BADMINOR;
if (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
(long *)&ept->ainfo.major, BADMAJOR) ||
getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
(long *)&ept->ainfo.minor, BADMINOR)) {
setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
goto error;
}
}
/*
* Links and information files don't have attributes associated with
* them. The following either resolves potential variables or passes
* them through. Mode is tested for validity to some degree. BAD???
* is returned to indicate that no meaningful mode was provided. A
* higher authority will decide if that's OK or not. CUR??? means that
* the prototype file specifically requires a wildcard ('?') for
* that entry. We issue an error if attributes were entered wrong.
* We just return BAD??? if there was no entry at all.
*/
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')) {
int retval;
retval = getvalmodevfp(&vfpGetCurrCharPtr(vfp),
&(ept->ainfo.mode),
CURMODE, (mapmode != MAPNONE));
if (retval == 1) {
goto end; /* nothing else on the line */
} else if (retval == 2) {
goto error; /* mode is too no good */
}
/* owner & group should be here */
if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
ept->ainfo.owner)) == 1)
goto end; /* no owner or group - warning */
if (retval == -1) {
setErrstr(pkg_gt(ERR_OWNTOOLONG));
goto error;
}
if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
ept->ainfo.group)) == 1)
goto end; /* no group - warning */
if (retval == -1) {
setErrstr(pkg_gt(ERR_GRPTOOLONG));
goto error;
}
/* Resolve the parameters if required. */
if (mapmode != MAPNONE) {
if (mapvar(mapmode, ept->ainfo.owner)) {
(void) snprintf(getErrbufAddr(),
getErrbufSize(), pkg_gt(ERR_NOVAR),
maptype, ept->ainfo.owner);
setErrstr(getErrbufAddr());
goto error;
}
if (mapvar(mapmode, ept->ainfo.group)) {
(void) snprintf(getErrbufAddr(),
getErrbufSize(), pkg_gt(ERR_NOVAR),
maptype, ept->ainfo.group);
setErrstr(getErrbufAddr());
goto error;
}
}
}
if ((ept->ftype == 'i') || (ept->ftype == 'f') ||
(ept->ftype == 'v') || (ept->ftype == 'e')) {
/* look for content description */
if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
(fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
(getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
(long *)&ept->cinfo.cksum, BADCONT) ||
getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
(long *)&ept->cinfo.modtime, BADCONT))) {
setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
goto error;
}
}
if (ept->ftype == 'i')
goto end;
end:
if (getendvfp(&vfpGetCurrCharPtr(vfp)) && ept->pinfo) {
setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
return (-1);
}
done:
return (1);
}
/*
* Get and validate the mode attribute. This returns an error if
* 1. the mode string is too long
* 2. the mode string includes alpha characters
* 3. the mode string is not octal
* 4. mode string is an install parameter
* 5. mode is an unresolved build parameter and MAPBUILD is
* in effect.
* If the mode is a build parameter, it is
* 1. returned as is if MAPNONE is in effect
* 2. evaluated if MAPBUILD is in effect
*
* NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
* time. At install time we just fix a mode with bad bits set by
* setting it to CURMODE. This should be an error in a few releases
* (2.8 maybe) but faulty modes are so common in existing packages
* that this is a reasonable exception. -- JST 1994-11-9
*
* RETURNS
* 0 if mode is being returned as a valid value
* 1 if no attributes are present on the line
* 2 if there was a fundamental error
*/
static int
getvalmodevfp(char **cp, mode_t *d, long bad, int map)
{
char tempmode[ATRSIZ+1];
mode_t tempmode_t;
int retval;
int n;
if ((retval = getstrvfp(cp, NULL, sizeof (tempmode), tempmode)) == 1) {
return (1);
} else if (retval == -1) {
setErrstr(pkg_gt(ERR_MODELONG));
return (2);
}
/*
* If it isn't a '?' (meaning go with whatever mode is
* there), validate the mode and convert it to a mode_t. The
* "bad" variable here is a misnomer. It doesn't necessarily
* mean bad.
*/
if (tempmode[0] == '?') {
*d = WILDCARD;
return (0);
}
/*
* Mode may not be an install parameter or a
* non-build parameter.
*/
if (tempmode[0] == '$' &&
(isupper(tempmode[1]) || !islower(tempmode[1]))) {
setErrstr(pkg_gt(ERR_IMODE));
return (2);
}
if ((map) && (mapvar(mapmode, tempmode))) {
(void) snprintf(getErrbufAddr(), getErrbufSize(),
pkg_gt(ERR_NOVAR), maptype, tempmode);
setErrstr(getErrbufAddr());
return (2);
}
if (tempmode[0] == '$') {
*d = BADMODE; /* may be a problem */
return (0);
}
/* it's supposed to be something we can convert to a number */
n = 0;
/* reject it if it contains nonnumbers or it's not octal */
while (tempmode[n] && !isspace(tempmode[n])) {
if (!isdigit(tempmode[n])) {
setErrstr(pkg_gt(ERR_MODEALPHA));
return (2);
}
if (strchr("89abcdefABCDEF", tempmode[n])) {
setErrstr(pkg_gt(ERR_BASEINVAL));
return (2);
}
n++;
}
tempmode_t = strtol(tempmode, NULL, 8);
/*
* We reject it if it contains inappropriate
* bits.
*/
if (tempmode_t & (~(S_IAMB | S_ISUID | S_ISGID | S_ISVTX))) {
if (mapmode == MAPBUILD) {
setErrstr(pkg_gt(ERR_MODEBITS));
return (2);
}
tempmode_t = bad;
}
*d = tempmode_t;
return (0);
}
int
getnumvfp(char **cp, int base, long *d, long bad)
{
int c;
char *p = *cp;
if (*p == '\0') {
return (0);
}
/* leading white space ignored */
while (((c = *p) != '\0') && (isspace(*p++)))
;
if (c == '?') {
*d = bad;
*cp = p;
return (0);
}
if ((c == '\0') || (c == '\n') || !isdigit(c)) {
p--;
*cp = p;
return (1);
}
*d = 0;
while (isdigit(c)) {
*d = (*d * base) + (c & 017);
c = *p++;
}
p--;
*cp = p;
return (0);
}
int
getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad)
{
int c;
char *p = *cp;
if (*p == '\0') {
return (0);
}
/* leading white space ignored */
while (((c = *p) != '\0') && (isspace(*p++)))
;
if (c == '?') {
*d = bad;
*cp = p;
return (0);
}
if ((c == '\0') || (c == '\n') || !isdigit(c)) {
p--;
*cp = p;
return (1);
}
*d = 0;
while (isdigit(c)) {
*d = (*d * base) + (c & 017);
c = *p++;
}
p--;
*cp = p;
return (0);
}
static int
getstrvfp(char **cp, char *sep, int n, char *str)
{
char delims[256];
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--;
/* generate complete list of delimiters to scan for */
(void) strlcpy(delims, " \t\n", sizeof (delims));
if ((sep != (char *)NULL) && (*sep != '\0')) {
(void) strlcat(delims, sep, sizeof (delims));
}
/* compute length based on delimiter found or not */
p1 = strpbrk(p, delims);
if (p1 == (char *)NULL) {
len = strlen(p);
} else {
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);
}
/*
* Name: getendvfp
* Description: Locate the end of the current line given a pointer into a buffer
* containing characters that is null terminated.
* Arguments: char **cp - pointer to pointer to null-terminated string buffer
* Returns: int == 0 -- no non-space characters preceeded the newline
* != 0 -- one or more non-space characters preceeded newline
* Effects: cp is updated to point to the first character PAST the first new
* line character found. If no newline character is found, cp is
* updated to point to the '\0' at the end of the buffer.
*/
static int
getendvfp(char **cp)
{
int n;
char *p = *cp;
n = 0;
/* if at end of buffer return no more characters left */
if (*p == '\0') {
return (0);
}
/* find the first null or end of line character */
while ((*p != '\0') && (*p != '\n')) {
if (n == 0) {
if (!isspace(*p)) {
n++;
}
}
p++;
}
/* if at newline, increment pointer to first character past newline */
if (*p == '\n') {
p++;
}
/* set return pointer to null or first character past newline */
*cp = p;
/* return space/nospace indicator */
return (n);
}
/*
* Name: findendvfp
* Description: Locate the end of the current line given a pointer into a buffer
* containing characters that is null terminated.
* Arguments: char **cp - pointer to pointer to null-terminated string buffer
* Returns: none
* Effects: cp is updated to point to the first character PAST the first new
* line character found. If no newline character is found, cp is
* updated to point to the '\0' at the end of the buffer.
*/
static void
findendvfp(char **cp)
{
char *p1;
char *p = *cp;
/* if at end of buffer return no more characters left */
if (*p == '\0') {
return;
}
/* find the end of the line */
p1 = strchr(p, '\n');
if (p1 != (char *)NULL) {
*cp = ++p1;
return;
}
/* no newline found - point to null terminator */
*cp = strchr(p, '\0');
}