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 28 29 #include <stdio.h> 30 #include <sys/types.h> 31 #include <sys/param.h> 32 #include <dirent.h> 33 #include <limits.h> 34 #include <errno.h> 35 #include <ctype.h> 36 #include <fcntl.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include <pkglib.h> 41 #include <libintl.h> 42 #include <libinst.h> 43 #include <install.h> 44 45 #define ERR_NOPKGMAP "Cannot open pkgmap file." 46 47 #define ENTRY_MAX (PATH_MAX + 38) 48 #define IGNORE_START ":#!" 49 #define IGNORE_TYPE "i" 50 51 static int has_rel_path(char *entry); 52 static int is_relative(char *entry); 53 54 /* 55 * This routine attempts to determine with certainty whether or not 56 * the package is relocatable or not. It first attempts to determine if 57 * there is a reloc directory by scanning pkginstdir. If that fails to 58 * provide a definite result (pkg is coming from a stream device and 59 * the directories aren't in place) it inspects the pkgmap in pkginstdir 60 * in order to determine if the package has relocatable elements. If 61 * there is a single relative pathname or $BASEDIR/... construct, 62 * this returns 1. If no relative pathnames are found it returns 0 63 * meaning absolute package and all the things that implies. 64 * 65 * This does not determine the validity of the pkgmap file. If the pkgmap 66 * is corrupted, this returns 0. 67 */ 68 int 69 isreloc(char *pkginstdir) 70 { 71 FILE *pkg_fp; 72 struct dirent *drp; 73 DIR *dirfp; 74 int retcode = 0; 75 76 /* First look in the directory */ 77 if ((dirfp = opendir(pkginstdir)) != NULL) { 78 while ((drp = readdir(dirfp)) != NULL) { 79 if (drp->d_name[0] == '.') 80 continue; 81 if (strlen(drp->d_name) < (size_t)5) 82 continue; 83 if (strncmp(drp->d_name, "reloc", 5) == 0) { 84 retcode = 1; 85 break; 86 } 87 } 88 (void) closedir(dirfp); 89 } 90 91 /* 92 * If retcode == 0, meaning we didn't find a reloc directory then we 93 * probably don't have a complete directory structure available to 94 * us. We'll have to determine what type of package it is by scanning 95 * the pkgmap file. 96 */ 97 if (retcode == 0) { 98 char path_buffer[ENTRY_MAX]; 99 100 (void) snprintf(path_buffer, sizeof (path_buffer), 101 "%s/pkgmap", pkginstdir); 102 103 canonize(path_buffer); 104 105 if ((pkg_fp = fopen(path_buffer, "r")) != NULL) { 106 while (fgets(path_buffer, sizeof (path_buffer), pkg_fp)) 107 if (has_rel_path(path_buffer)) { 108 retcode = 1; 109 break; 110 } 111 (void) fclose(pkg_fp); 112 } else { 113 progerr(gettext(ERR_NOPKGMAP)); 114 quit(99); 115 } 116 } 117 118 return (retcode); 119 } 120 121 /* 122 * Test the string for the presence of a relative path. If found, return 123 * 1 otherwise return 0. If we get past the IGNORE_TYPE test, we're working 124 * with a line of the form : 125 * 126 * dpart type classname pathname ... 127 * 128 * It's pathname we're going to test here. 129 * 130 * Yes, yes, I know about sscanf(); but, I don't need to reserve 4K of 131 * space and parse the whole string, I just need to get to two tokens. 132 * We're in a hurry. 133 */ 134 static int 135 has_rel_path(char *entry) 136 { 137 register int entry_pos = 1; 138 139 /* If the line is a comment or special directive, return 0 */ 140 if (*entry == '\0' || strchr(IGNORE_START, *entry) != NULL) 141 return (0); 142 143 /* Skip past this data entry if it is volume number. */ 144 if (isdigit(*entry)) { 145 while (*entry && !isspace(*entry)) { 146 entry++; 147 } 148 } 149 150 /* Skip past this white space */ 151 while (*entry && isspace(*entry)) { 152 entry++; 153 } 154 155 /* 156 * Now we're either pointing at the type or we're pointing at 157 * the termination of a degenerate entry. If the line is degenerate 158 * or the type indicates this line should be ignored, we return 159 * as though not relative. 160 */ 161 if (*entry == '\0' || strchr(IGNORE_TYPE, *entry)) 162 return (0); 163 164 /* The pathname is in the third position */ 165 do { 166 /* Skip past this data entry */ 167 while (*entry && !isspace(*entry)) { 168 entry++; 169 } 170 171 /* Skip past this white space and call this the next entry */ 172 while (*entry && isspace(*entry)) { 173 entry++; 174 } 175 } while (++entry_pos < 3 && *entry != '\0'); 176 177 /* 178 * Now we're pointing at the first character of the pathname. 179 * If the file is corrupted, we're pointing at NULL. is_relative() 180 * will return FALSE for NULL which will yield the correct return 181 * value. 182 */ 183 return (is_relative(entry)); 184 } 185 186 /* 187 * If the path doesn't begin with a variable, the first character in the 188 * path is tested for '/' to determine if it is absolute or not. If the 189 * path begins with a '$', that variable is resolved if possible. If it 190 * isn't defined yet, we exit with error code 1. 191 */ 192 static int 193 is_relative(char *entry) 194 { 195 register char *eopath = entry; /* end of full pathname pointer */ 196 register char **lasts = &entry; 197 198 /* If there is a path, test it */ 199 if (entry && *entry) { 200 if (*entry == '$') { /* it's an environment parameter */ 201 entry++; /* skip the '$' */ 202 203 while (*eopath && !isspace(*eopath)) 204 eopath++; 205 206 *eopath = '\0'; /* terminate the pathname */ 207 208 /* isolate the variable */ 209 entry = strtok_r(entry, "/", lasts); 210 211 /* 212 * Some packages call out $BASEDIR for relative 213 * paths in the pkgmap even though that is 214 * redundant. This special case is actually 215 * an indication that this is a relative 216 * path. 217 */ 218 if (strcmp(entry, "BASEDIR") == 0) 219 return (1); 220 /* 221 * Since entry is pointing to a now-expendable PATH_MAX 222 * size buffer, we can expand the path variable into it 223 * here. 224 */ 225 entry = getenv(entry); 226 } 227 228 /* 229 * Return type of path. If pathname was unresolvable 230 * variable, assume relative. This looks like a strange 231 * assumption since the resolved path may end up 232 * absolute and pkgadd may prompt the user for a basedir 233 * incorrectly because of this assumption. Unfortunately, 234 * the request script MUST have a final BASEDIR in the 235 * environment before it executes. 236 */ 237 if (entry && *entry) 238 return (RELATIVE(entry)); 239 else 240 return (1); 241 } else /* no path, so we skip it */ 242 return (0); 243 } 244