/*
* 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.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <dirent.h>
#include <limits.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pkglib.h>
#include <libintl.h>
#include <libinst.h>
#include <install.h>
#define ERR_NOPKGMAP "Cannot open pkgmap file."
#define ENTRY_MAX (PATH_MAX + 38)
#define IGNORE_START ":#!"
#define IGNORE_TYPE "i"
static int has_rel_path(char *entry);
static int is_relative(char *entry);
/*
* This routine attempts to determine with certainty whether or not
* the package is relocatable or not. It first attempts to determine if
* there is a reloc directory by scanning pkginstdir. If that fails to
* provide a definite result (pkg is coming from a stream device and
* the directories aren't in place) it inspects the pkgmap in pkginstdir
* in order to determine if the package has relocatable elements. If
* there is a single relative pathname or $BASEDIR/... construct,
* this returns 1. If no relative pathnames are found it returns 0
* meaning absolute package and all the things that implies.
*
* This does not determine the validity of the pkgmap file. If the pkgmap
* is corrupted, this returns 0.
*/
int
isreloc(char *pkginstdir)
{
FILE *pkg_fp;
struct dirent *drp;
DIR *dirfp;
int retcode = 0;
/* First look in the directory */
if ((dirfp = opendir(pkginstdir)) != NULL) {
while ((drp = readdir(dirfp)) != NULL) {
if (drp->d_name[0] == '.')
continue;
if (strlen(drp->d_name) < (size_t)5)
continue;
if (strncmp(drp->d_name, "reloc", 5) == 0) {
retcode = 1;
break;
}
}
(void) closedir(dirfp);
}
/*
* If retcode == 0, meaning we didn't find a reloc directory then we
* probably don't have a complete directory structure available to
* us. We'll have to determine what type of package it is by scanning
* the pkgmap file.
*/
if (retcode == 0) {
char path_buffer[ENTRY_MAX];
(void) snprintf(path_buffer, sizeof (path_buffer),
"%s/pkgmap", pkginstdir);
canonize(path_buffer);
if ((pkg_fp = fopen(path_buffer, "r")) != NULL) {
while (fgets(path_buffer, sizeof (path_buffer), pkg_fp))
if (has_rel_path(path_buffer)) {
retcode = 1;
break;
}
(void) fclose(pkg_fp);
} else {
progerr(gettext(ERR_NOPKGMAP));
quit(99);
}
}
return (retcode);
}
/*
* Test the string for the presence of a relative path. If found, return
* 1 otherwise return 0. If we get past the IGNORE_TYPE test, we're working
* with a line of the form :
*
* dpart type classname pathname ...
*
* It's pathname we're going to test here.
*
* Yes, yes, I know about sscanf(); but, I don't need to reserve 4K of
* space and parse the whole string, I just need to get to two tokens.
* We're in a hurry.
*/
static int
has_rel_path(char *entry)
{
register int entry_pos = 1;
/* If the line is a comment or special directive, return 0 */
if (*entry == NULL || strchr(IGNORE_START, *entry))
return (0);
/* Skip past this data entry if it is volume number. */
if (isdigit(*entry)) {
while (*entry && !isspace(*entry)) {
entry++;
}
}
/* Skip past this white space */
while (*entry && isspace(*entry)) {
entry++;
}
/*
* Now we're either pointing at the type or we're pointing at
* the termination of a degenerate entry. If the line is degenerate
* or the type indicates this line should be ignored, we return
* as though not relative.
*/
if (*entry == NULL || strchr(IGNORE_TYPE, *entry))
return (0);
/* The pathname is in the third position */
do {
/* Skip past this data entry */
while (*entry && !isspace(*entry)) {
entry++;
}
/* Skip past this white space and call this the next entry */
while (*entry && isspace(*entry)) {
entry++;
}
} while (++entry_pos < 3 && *entry != NULL);
/*
* Now we're pointing at the first character of the pathname.
* If the file is corrupted, we're pointing at NULL. is_relative()
* will return FALSE for NULL which will yield the correct return
* value.
*/
return (is_relative(entry));
}
/*
* If the path doesn't begin with a variable, the first character in the
* path is tested for '/' to determine if it is absolute or not. If the
* path begins with a '$', that variable is resolved if possible. If it
* isn't defined yet, we exit with error code 1.
*/
static int
is_relative(char *entry)
{
register char *eopath = entry; /* end of full pathname pointer */
register char **lasts = &entry;
/* If there is a path, test it */
if (entry && *entry) {
if (*entry == '$') { /* it's an environment parameter */
entry++; /* skip the '$' */
while (*eopath && !isspace(*eopath))
eopath++;
*eopath = '\0'; /* terminate the pathname */
/* isolate the variable */
entry = strtok_r(entry, "/", lasts);
/*
* Some packages call out $BASEDIR for relative
* paths in the pkgmap even though that is
* redundant. This special case is actually
* an indication that this is a relative
* path.
*/
if (strcmp(entry, "BASEDIR") == 0)
return (1);
/*
* Since entry is pointing to a now-expendable PATH_MAX
* size buffer, we can expand the path variable into it
* here.
*/
entry = getenv(entry);
}
/*
* Return type of path. If pathname was unresolvable
* variable, assume relative. This looks like a strange
* assumption since the resolved path may end up
* absolute and pkgadd may prompt the user for a basedir
* incorrectly because of this assumption. Unfortunately,
* the request script MUST have a final BASEDIR in the
* environment before it executes.
*/
if (entry && *entry)
return (RELATIVE(entry));
else
return (1);
} else /* no path, so we skip it */
return (0);
}