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
isreloc(char * pkginstdir)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
has_rel_path(char * entry)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
is_relative(char * entry)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