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 */
49extern int	getmapmode(void);
50
51#define	EPTMALLOC	512
52
53static struct cfent **eptlist;
54
55static int	eptnum;
56static int	errflg;
57static int	nparts;
58static int	space = -1;
59
60static void	procinit(void);
61static 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
66static int	ckdup(struct cfent *ept1, struct cfent *ept2);
67static int	sortentry(int index);
68
69static void
70procinit(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 */
90static int
91procassign(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*/
195struct cfent **
196procmap(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 */
287static int
288sortentry(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 */
368static int
369ckdup(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