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 == NULL || strchr(IGNORE_START, *entry))
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 == NULL || 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 != NULL);
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