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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 
31 #include <stdio.h>
32 #include <limits.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <pkgstrct.h>
39 #include <locale.h>
40 #include <libintl.h>
41 #include <pkglib.h>
42 #include <install.h>
43 #include <libinst.h>
44 
45 #define	ERR_MEMORY	"memory allocation failure"
46 #define	ERR_DUPPATH	"duplicate pathname <%s>"
47 
48 /* libpkg/gpkgmap */
49 extern int	getmapmode(void);
50 
51 #define	EPTMALLOC	512
52 
53 static struct cfent **eptlist;
54 
55 static int	eptnum;
56 static int	errflg;
57 static int	nparts;
58 static int	space = -1;
59 
60 static void	procinit(void);
61 static int	procassign(struct cfent *ept, char **server_local,
62 		    char **client_local, char **server_path,
63 		    char **client_path, char **map_path, int mapflag,
64 		    int nc);
65 
66 static int	ckdup(struct cfent *ept1, struct cfent *ept2);
67 static int	sortentry(int index);
68 
69 static void
procinit(void)70 procinit(void)
71 {
72 	errflg = nparts = eptnum = 0;
73 
74 	if (space != -1) {
75 		ar_free(space);
76 		space = -1;
77 	}
78 
79 	/*
80 	 * initialize dynamic memory used to store
81 	 * path information which is read in
82 	 */
83 	(void) pathdup((char *)0);
84 }
85 
86 /*
87  * This function assigns appropriate values based upon the pkgmap entry
88  * in the cfent structure.
89  */
90 static int
procassign(struct cfent * ept,char ** server_local,char ** client_local,char ** server_path,char ** client_path,char ** map_path,int mapflag,int nc)91 procassign(struct cfent *ept, char **server_local, char **client_local,
92     char **server_path, char **client_path, char **map_path, int mapflag,
93     int nc)
94 {
95 	int	path_duped = 0;
96 	int	local_duped = 0;
97 	char	source[PATH_MAX+1];
98 
99 	if (nc >= 0 && ept->ftype != 'i')
100 		if ((ept->pkg_class_idx = cl_idx(ept->pkg_class)) == -1)
101 			return (1);
102 
103 	if (ept->volno > nparts)
104 		nparts++;
105 
106 	/*
107 	 * Generate local (delivered source) paths for files
108 	 * which need them so that the install routine will know
109 	 * where to get the file from the package. Note that we
110 	 * do not resolve path environment variables here since
111 	 * they won't be resolved in the reloc directory.
112 	 */
113 	if ((mapflag > 1) && strchr("fve", ept->ftype)) {
114 		if (ept->ainfo.local == NULL) {
115 			source[0] = '~';
116 			(void) strcpy(&source[1], ept->path);
117 			ept->ainfo.local = pathdup(source);
118 			*server_local = ept->ainfo.local;
119 			*client_local = ept->ainfo.local;
120 
121 			local_duped = 1;
122 		}
123 	}
124 
125 	/*
126 	 * Evaluate the destination path based upon available
127 	 * environment, then produce a client-relative and
128 	 * server-relative canonized path.
129 	 */
130 	if (mapflag && (ept->ftype != 'i')) {
131 		mappath(getmapmode(), ept->path); /* evaluate variables */
132 		canonize(ept->path);	/* Fix path as necessary. */
133 
134 		(void) eval_path(server_path,
135 		    client_path,
136 		    map_path,
137 		    ept->path);
138 		path_duped = 1;	/* eval_path dup's it */
139 		ept->path = *server_path;	/* default */
140 	}
141 
142 	/*
143 	 * Deal with source for hard and soft links.
144 	 */
145 	if (strchr("sl", ept->ftype)) {
146 		if (mapflag) {
147 			mappath(getmapmode(), ept->ainfo.local);
148 			if (!RELATIVE(ept->ainfo.local)) {
149 				canonize(ept->ainfo.local);
150 
151 				/* check for hard link */
152 				if (ept->ftype == 'l') {
153 					(void) eval_path(
154 					    server_local,
155 					    client_local,
156 					    NULL,
157 					    ept->ainfo.local);
158 					local_duped = 1;
159 
160 					/* Default to server. */
161 					ept->ainfo.local = *server_local;
162 				}
163 			}
164 		}
165 	}
166 
167 	/*
168 	 * For the paths (both source and target) that were too mundane to
169 	 * have been copied into dup space yet, do that.
170 	 */
171 	if (!path_duped) {
172 		*server_path = pathdup(ept->path);
173 		*client_path = *server_path;
174 		ept->path = *server_path;
175 
176 		path_duped = 1;
177 	}
178 	if (ept->ainfo.local != NULL)
179 		if (!local_duped) {
180 			*server_local = pathdup(ept->ainfo.local);
181 			ept->ainfo.local = *server_local;
182 			*client_local = ept->ainfo.local;
183 
184 		local_duped = 1;
185 	}
186 
187 	return (0);
188 }
189 
190 /*
191  * This function reads the prototype file and returns a pointer to a list of
192  * struct cfent representing the contents of that file.
193  */
194 /*ARGSUSED*/
195 struct cfent **
procmap(VFP_T * vfp,int mapflag,char * ir)196 procmap(VFP_T *vfp, int mapflag, char *ir)
197 {
198 	struct cfent	*ept = (struct cfent *)NULL;
199 	struct cfent	map_entry;
200 	struct cfent	**ept_ptr;
201 	int	i;
202 	int	n;
203 	int	nc;
204 	static char *server_local, *client_local;
205 	static char *server_path, *client_path, *map_path;
206 
207 	procinit();
208 
209 	space = ar_create(EPTMALLOC, (unsigned)sizeof (struct cfent),
210 	    "prototype object");
211 	if (space == -1) {
212 		progerr(gettext(ERR_MEMORY));
213 		return (NULL);
214 	}
215 
216 	nc = cl_getn();
217 	for (;;) {
218 		/* Clear the buffer. */
219 		(void) memset(&map_entry, '\000', sizeof (struct cfent));
220 
221 		n = gpkgmapvfp(&map_entry, vfp);
222 
223 		if (n == 0)
224 			break; /* no more entries in pkgmap */
225 		else if (n < 0) {
226 			char	*errstr = getErrstr();
227 			progerr(gettext("bad entry read in pkgmap"));
228 			logerr(gettext("pathname=%s"),
229 				(ept && ept->path && *ept->path) ?
230 				ept->path : "Unknown");
231 			logerr(gettext("problem=%s"),
232 			    (errstr && *errstr) ? errstr : "Unknown");
233 			return (NULL);
234 		}
235 
236 		/*
237 		 * A valid entry was found in the map, so allocate an
238 		 * official record.
239 		 */
240 		ept_ptr = (struct cfent **)ar_next_avail(space);
241 		if (ept_ptr == NULL || *ept_ptr == NULL) {
242 			progerr(gettext(ERR_MEMORY));
243 			return (NULL);
244 		}
245 
246 		ept = *ept_ptr;
247 
248 		/* Transfer what we just read in. */
249 		(void) memcpy(ept, &map_entry, sizeof (struct cfent));
250 
251 		if (procassign(ept, &server_local, &client_local,
252 		    &server_path, &client_path, &map_path,
253 		    mapflag, nc)) {
254 			/* It didn't take. */
255 			(void) ar_delete(space, eptnum);
256 			continue;
257 		}
258 
259 		eptnum++;
260 	}
261 
262 	/* setup a pointer array to point to malloc'd entries space */
263 	eptlist = (struct cfent **)ar_get_head(space);
264 	if (eptlist == NULL) {
265 		progerr(gettext(ERR_MEMORY));
266 		return (NULL);
267 	}
268 
269 	(void) sortentry(-1);
270 	for (i = 0; i < eptnum; /* void */) {
271 		if (!sortentry(i))
272 			i++;
273 	}
274 	return (errflg ? NULL : eptlist);
275 }
276 
277 /*
278  * This function sorts the final list of cfent entries. If index = -1, the
279  * function is initialized. index = 0 doesn't get us anywhere because this
280  * sorts against index-1. Positive natural index values are compared and
281  * sorted into the array appropriately. Yes, it does seem we should use a
282  * quicksort on the whole array or something. The apparent reason for taking
283  * this approach is that there are enough special considerations to be
284  * applied to each package object that inserting them one-by-one doesn't cost
285  * that much.
286  */
287 static int
sortentry(int index)288 sortentry(int index)
289 {
290 	struct cfent *ept, *ept_i;
291 	static int last = 0;
292 	int	i, n, j;
293 	int	upper, lower;
294 
295 	if (index == 0)
296 		return (0);
297 	else if (index < 0) {
298 		last = 0;
299 		return (0);
300 	}
301 
302 	/*
303 	 * Based on the index, this is the package object we're going to
304 	 * review. It may stay where it is or it may be repositioned in the
305 	 * array.
306 	 */
307 	ept = eptlist[index];
308 
309 	/* quick comparison optimization for pre-sorted arrays */
310 	if (strcmp(ept->path, eptlist[index-1]->path) > 0) {
311 		/* do nothing */
312 		last = index-1;
313 		return (0);
314 	}
315 
316 	lower = 0;		/* lower bound of the unsorted elements */
317 	upper = index;		/* upper bound */
318 	i = last;
319 	do {
320 		/*
321 		 * NOTE: This does a binary sort on path. There are lots of
322 		 * other worthy items in the array, but path is the key into
323 		 * the package database.
324 		 */
325 		ept_i = eptlist[i];
326 
327 		n = strcmp(ept->path, ept_i->path);
328 		if (n == 0) {
329 			if (!ckdup(ept, ept_i)) {
330 				progerr(gettext(ERR_DUPPATH),
331 				    ept->path);
332 				errflg++;
333 			}
334 			/* remove the entry at index */
335 			(void) ar_delete(space, index);
336 
337 			eptnum--;
338 			return (1);	/* Use this index again. */
339 		} else if (n < 0) {
340 			/*
341 			 * The path of interest is smaller than the path
342 			 * under test. Move down array using the method of
343 			 * division
344 			 */
345 			upper = i;
346 			i = lower + (upper-lower)/2;
347 		} else {
348 			/* Move up array */
349 			lower = i+1;
350 			i = upper - (upper-lower)/2 - 1;
351 		}
352 	} while (upper != lower);
353 	last = i = upper;
354 
355 	/* expand to insert at i */
356 	for (j = index; j > i; j--)
357 		eptlist[j] = eptlist[j-1];
358 
359 	eptlist[i] = ept;
360 
361 	return (0);
362 }
363 
364 /*
365  * Check duplicate entries in the package object list. If it's a directory,
366  * this just merges them, if not, it returns a 0 to force further processing.
367  */
368 static int
ckdup(struct cfent * ept1,struct cfent * ept2)369 ckdup(struct cfent *ept1, struct cfent *ept2)
370 {
371 	/* ept2 will be modified to contain "merged" entries */
372 
373 	if (!strchr("?dx", ept1->ftype))
374 		return (0);
375 
376 	if (!strchr("?dx", ept2->ftype))
377 		return (0);
378 
379 	if (ept2->ainfo.mode == BADMODE)
380 		ept2->ainfo.mode = ept1->ainfo.mode;
381 	if ((ept1->ainfo.mode != ept2->ainfo.mode) &&
382 	    (ept1->ainfo.mode != BADMODE))
383 		return (0);
384 
385 	if (strcmp(ept2->ainfo.owner, "?") == 0)
386 		(void) strcpy(ept2->ainfo.owner, ept1->ainfo.owner);
387 	if (strcmp(ept1->ainfo.owner, ept2->ainfo.owner) &&
388 	    strcmp(ept1->ainfo.owner, "?"))
389 		return (0);
390 
391 	if (strcmp(ept2->ainfo.group, "?") == 0)
392 		(void) strcpy(ept2->ainfo.group, ept1->ainfo.group);
393 	if (strcmp(ept1->ainfo.group, ept2->ainfo.group) &&
394 	    strcmp(ept1->ainfo.group, "?"))
395 		return (0);
396 
397 	if (ept1->pinfo) {
398 		ept2->npkgs = ept1->npkgs;
399 		ept2->pinfo = ept1->pinfo;
400 	}
401 
402 	return (1);
403 }
404