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 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27
28#include <stdio.h>
29#include <limits.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <errno.h>
33#include <string.h>
34#include <sys/types.h>
35#include <pkgstrct.h>
36#include <locale.h>
37#include <libintl.h>
38#include <pkglib.h>
39#include <install.h>
40#include <libinst.h>
41
42#define	WRN_NOPKGOBJ	"WARNING: no package objects found"
43
44#define	ERR_MEMORY	"memory allocation failure"
45#define	ERR_DUPPATH	"duplicate pathname <%s>"
46
47/* libpkg/gpkgmap */
48extern int	getmapmode(void);
49
50#define	EPTMALLOC	512
51
52static struct cfextra **extlist;
53
54int	eptnum;
55static int	array_preloaded = 0;
56static int	errflg;
57static int	nparts;
58static int	xspace = -1;
59
60void	pkgobjinit(void);
61static int	pkgobjassign(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);
68static int	dup_merg(struct cfextra *ext1, struct cfextra *ext2);
69
70void
71pkgobjinit(void)
72{
73	if (array_preloaded)	/* Already done. */
74		return;
75
76	errflg = nparts = eptnum = 0;
77
78	if (xspace != -1) {
79		ar_free(xspace);
80		xspace = -1;
81	}
82
83	/*
84	 * initialize dynamic memory used to store
85	 * path information which is read in
86	 */
87	(void) pathdup((char *)0);
88}
89
90/*
91 * This function assigns appropriate values based upon the pkgmap entry
92 * in the cfent structure.
93 */
94static int
95pkgobjassign(struct cfent *ept, char **server_local, char **client_local,
96    char **server_path, char **client_path, char **map_path, int mapflag,
97    int nc)
98{
99	int	path_duped = 0;
100	int	local_duped = 0;
101	char	source[PATH_MAX+1];
102
103	if (nc >= 0 && ept->ftype != 'i')
104		if ((ept->pkg_class_idx = cl_idx(ept->pkg_class)) == -1)
105			return (1);
106
107	if (ept->volno > nparts)
108		nparts++;
109
110	/*
111	 * Generate local (delivered source) paths for files
112	 * which need them so that the install routine will know
113	 * where to get the file from the package. Note that we
114	 * do not resolve path environment variables here since
115	 * they won't be resolved in the reloc directory.
116	 */
117	if ((mapflag > 1) && strchr("fve", ept->ftype)) {
118		if (ept->ainfo.local == NULL) {
119			source[0] = '~';
120			(void) strlcpy(&source[1], ept->path,
121						sizeof (source)-1);
122			ept->ainfo.local = pathdup(source);
123			*server_local = ept->ainfo.local;
124			*client_local = ept->ainfo.local;
125
126			local_duped = 1;
127		}
128	}
129
130	/*
131	 * Evaluate the destination path based upon available
132	 * environment, then produce a client-relative and
133	 * server-relative canonized path.
134	 */
135	if (mapflag && (ept->ftype != 'i')) {
136		mappath(getmapmode(), ept->path); /* evaluate variables */
137		canonize(ept->path);	/* Fix path as necessary. */
138
139		(void) eval_path(server_path,
140		    client_path,
141		    map_path,
142		    ept->path);
143		path_duped = 1;	/* eval_path dup's it */
144		ept->path = *server_path;	/* default */
145	}
146
147	/*
148	 * Deal with source for hard and soft links.
149	 */
150	if (strchr("sl", ept->ftype)) {
151		if (mapflag) {
152			mappath(getmapmode(), ept->ainfo.local);
153			if (!RELATIVE(ept->ainfo.local)) {
154				canonize(ept->ainfo.local);
155
156				/* check for hard link */
157				if (ept->ftype == 'l') {
158					(void) eval_path(
159					    server_local,
160					    client_local,
161					    NULL,
162					    ept->ainfo.local);
163					local_duped = 1;
164
165					/* Default to server. */
166					ept->ainfo.local = *server_local;
167				}
168			}
169		}
170	}
171
172	/*
173	 * For the paths (both source and target) that were too mundane to
174	 * have been copied into dup space yet, do that.
175	 */
176	if (!path_duped) {
177		*server_path = pathdup(ept->path);
178		*client_path = *server_path;
179		ept->path = *server_path;
180
181		path_duped = 1;
182	}
183	if (ept->ainfo.local != NULL)
184		if (!local_duped) {
185			*server_local = pathdup(ept->ainfo.local);
186			ept->ainfo.local = *server_local;
187			*client_local = ept->ainfo.local;
188
189		local_duped = 1;
190	}
191
192	return (0);
193}
194
195/* This initializes the package object array. */
196int
197init_pkgobjspace(void)
198{
199	if (array_preloaded)	/* Already done. */
200		return (1);
201
202	if (xspace == -1) {
203		xspace = ar_create(EPTMALLOC, sizeof (struct cfextra),
204		    "package object");
205		if (xspace == -1) {
206			progerr(gettext(ERR_MEMORY));
207			return (0);
208		}
209	}
210
211	return (1);
212}
213
214int
215seed_pkgobjmap(struct cfextra *ext_entry, char *path, char *local)
216{
217	struct cfextra *ext, **ext_ptr;
218
219	/* offsets for the various path images. */
220	int client_path_os;
221	int server_path_os;
222	int map_path_os;
223	int client_local_os;
224	int server_local_os;
225
226	ext_ptr = (struct cfextra **)ar_next_avail(xspace);
227
228	if (ext_ptr == NULL || *ext_ptr == NULL) {
229		progerr(gettext(ERR_MEMORY));
230		return (NULL);
231	}
232
233	ext = *ext_ptr;
234
235	(void) memcpy(ext, ext_entry, sizeof (struct cfextra));
236
237	/* Figure out all of the offsets. */
238	client_path_os = ((ptrdiff_t)ext->client_path -
239			(ptrdiff_t)ext->cf_ent.path);
240	server_path_os = ((ptrdiff_t)ext->server_path -
241			(ptrdiff_t)ext->cf_ent.path);
242	map_path_os = ((ptrdiff_t)ext->map_path -
243			(ptrdiff_t)ext->cf_ent.path);
244	client_local_os = ((ptrdiff_t)ext->client_local -
245			(ptrdiff_t)ext->cf_ent.ainfo.local);
246	server_local_os = ((ptrdiff_t)ext->server_local -
247			(ptrdiff_t)ext->cf_ent.ainfo.local);
248
249	/* Allocate and store the path name. */
250	ext->cf_ent.path = pathdup(path);
251
252	/* Assign the path substring pointers. */
253	ext->client_path = (ext->cf_ent.path + client_path_os);
254	ext->server_path = (ext->cf_ent.path + server_path_os);
255	ext->map_path = (ext->cf_ent.path + map_path_os);
256
257	/* If there's a local entry, allocate and store it as well. */
258	if (local) {
259		ext->cf_ent.ainfo.local = pathdup(local);
260
261		ext->client_local = (ext->cf_ent.ainfo.local + client_local_os);
262		ext->server_local = (ext->cf_ent.ainfo.local + server_local_os);
263	} else {
264		ext->cf_ent.ainfo.local = NULL;
265		ext->client_local = NULL;
266		ext->server_local = NULL;
267	}
268
269	eptnum++;
270	array_preloaded = 1;
271
272	return (0);
273}
274
275/*
276 * This function reads the pkgmap (or any file similarly formatted) and
277 * returns a pointer to a list of struct cfextra (each of which
278 * contains a struct cfent) representing the contents of that file.
279 */
280
281/* ARGSUSED ir in pkgobjmap */
282struct cfextra **
283pkgobjmap(VFP_T *vfp, int mapflag, char *ir)
284{
285	struct	cfextra *ext, **ext_ptr;
286	struct	cfent *ept, map_entry;
287	int	i;
288	int	n;
289	int	nc;
290
291	pkgobjinit();
292	if (!init_pkgobjspace())
293		quit(99);
294
295	nc = cl_getn();
296	for (;;) {
297		/* Clear the buffer. */
298		(void) memset(&map_entry, '\000', sizeof (struct cfent));
299
300		/*
301		 * Fill in a cfent structure in a very preliminary fashion.
302		 * ept->path and ept->ainfo.local point to static memory
303		 * areas of size PATH_MAX. These are manipulated and
304		 * then provided their own allocations later in this function.
305		 */
306		n = gpkgmapvfp(&map_entry, vfp);
307
308		if (n == 0)
309			break; /* no more entries in pkgmap */
310		else if (n < 0) {
311			char	*errstr = getErrstr();
312			progerr(gettext("bad entry read in pkgmap"));
313			logerr(gettext("pathname=%s"),
314			    (map_entry.path && *map_entry.path) ?
315			    map_entry.path : "Unknown");
316			logerr(gettext("problem=%s"),
317			    (errstr && *errstr) ? errstr : "Unknown");
318			return (NULL);
319		}
320
321		/*
322		 * A valid entry was found in the map, so allocate an
323		 * official record.
324		 */
325		ext_ptr = (struct cfextra **)ar_next_avail(xspace);
326		if (ext_ptr == NULL || *ext_ptr == NULL) {
327			progerr(gettext(ERR_MEMORY));
328			return (NULL);
329		}
330
331		ext = *ext_ptr;
332		ept = &(ext->cf_ent);
333
334		/* Transfer what we just read in. */
335		(void) memcpy(ept, &map_entry, sizeof (struct cfent));
336
337		/* And process it into the cfextra structure. */
338		if (pkgobjassign(ept,
339		    &(ext->server_local),
340		    &(ext->client_local),
341		    &(ext->server_path),
342		    &(ext->client_path),
343		    &(ext->map_path),
344		    mapflag, nc)) {
345			/* It didn't take. */
346			(void) ar_delete(xspace, eptnum);
347			continue;
348		}
349
350		eptnum++;
351		ext->fsys_value = BADFSYS;	/* No file system data yet */
352		ext->fsys_base = BADFSYS;
353	}
354
355	if (eptnum == 0) {
356		logerr(gettext(WRN_NOPKGOBJ));
357		return (NULL);
358	}
359
360	/* setup a pointer array to point to malloc'd entries space */
361	extlist = (struct cfextra **)ar_get_head(xspace);
362	if (extlist == NULL) {
363		progerr(gettext(ERR_MEMORY));
364		return (NULL);
365	}
366
367	(void) sortentry(-1);
368	for (i = 0; i < eptnum; /* void */) {
369		if (!sortentry(i))
370			i++;
371	}
372
373	return (errflg ? NULL : extlist);
374}
375
376/*
377 * This function sorts the final list of cfextra entries. If index = -1, the
378 * function is initialized. index = 0 doesn't get us anywhere because this
379 * sorts against index-1. Positive natural index values are compared and
380 * sorted into the array appropriately. Yes, it does seem we should use a
381 * quicksort on the whole array or something. The apparent reason for taking
382 * this approach is that there are enough special considerations to be
383 * applied to each package object that inserting them one-by-one doesn't cost
384 * that much.
385 */
386static int
387sortentry(int index)
388{
389	struct cfextra *ext;
390	struct cfent *ept, *ept_i;
391	static int last = 0;
392	int	i, n, j;
393	int	upper, lower;
394
395	if (index == 0)
396		return (0);
397	else if (index < 0) {
398		last = 0;
399		return (0);
400	}
401
402	/*
403	 * Based on the index, this is the package object we're going to
404	 * review. It may stay where it is or it may be repositioned in the
405	 * array.
406	 */
407	ext = extlist[index];
408	ept = &(ext->cf_ent);
409
410	/* quick comparison optimization for pre-sorted arrays */
411	if (strcmp(ept->path, extlist[index-1]->cf_ent.path) > 0) {
412		/* do nothing */
413		last = index-1;
414		return (0);
415	}
416
417	lower = 0;		/* lower bound of the unsorted elements */
418	upper = index;		/* upper bound */
419	i = last;
420	do {
421		/*
422		 * NOTE: This does a binary sort on path. There are lots of
423		 * other worthy items in the array, but path is the key into
424		 * the package database.
425		 */
426		ept_i = &(extlist[i]->cf_ent);
427
428		n = strcmp(ept->path, ept_i->path);
429		if (n == 0) {
430			if (!ckdup(ept, ept_i)) {
431				/*
432				 * If the array was seeded then there are
433				 * bound to be occasional duplicates.
434				 * Otherwise, duplicates are definitely a
435				 * sign of major damage.
436				 */
437				if (array_preloaded) {
438					if (!dup_merg(ext, extlist[i])) {
439						progerr(gettext(ERR_DUPPATH),
440						    ept->path);
441						errflg++;
442					}
443				} else {
444					progerr(gettext(ERR_DUPPATH),
445					    ept->path);
446					errflg++;
447				}
448			}
449			/* remove the entry at index */
450			(void) ar_delete(xspace, index);
451
452			eptnum--;
453			return (1);	/* Use this index again. */
454		} else if (n < 0) {
455			/*
456			 * The path of interest is smaller than the path
457			 * under test. Move down array using the method of
458			 * division
459			 */
460			upper = i;
461			i = lower + (upper-lower)/2;
462		} else {
463			/* Move up array */
464			lower = i+1;
465			i = upper - (upper-lower)/2 - 1;
466		}
467	} while (upper != lower);
468	last = i = upper;
469
470	/* expand to insert at i */
471	for (j = index; j > i; j--)
472		extlist[j] = extlist[j-1];
473
474	extlist[i] = ext;
475
476	return (0);
477}
478
479/* Return the number of blocks required by the package object provided. */
480static fsblkcnt_t
481nblks(short fsys_entry, struct cfextra *ext)
482{
483	fsblkcnt_t blk;
484	ulong_t block_size;
485	ulong_t frag_size;
486
487	block_size = (ulong_t)get_blk_size_n(fsys_entry);
488	frag_size = (ulong_t)get_frag_size_n(fsys_entry);
489
490	if (strchr("dxs", ext->cf_ent.ftype))
491		blk =
492		    nblk(block_size, block_size, frag_size);
493	else if (ext->cf_ent.cinfo.size != BADCONT)
494		blk = nblk(ext->cf_ent.cinfo.size, block_size,
495		    frag_size);
496	else
497		blk = 0;
498
499	return (blk);
500}
501
502/* Remove ext1 from the filesystem size calculations and add ext2. */
503static void
504size_xchng(struct cfextra *ext1, struct cfextra *ext2)
505{
506	fsblkcnt_t bused;
507	ulong_t block_size;
508	ulong_t frag_size;
509	fsblkcnt_t	blks1, blks2;
510	short	fsys_entry;
511
512	/*
513	 * Since these are on the same filesystem, either one will yield the
514	 * correct block and fragment size.
515	 */
516	fsys_entry = ext1->fsys_base;
517	block_size = (ulong_t)get_blk_size_n(fsys_entry);
518	frag_size = (ulong_t)get_frag_size_n(fsys_entry);
519
520	blks1 = nblk(ext1->cf_ent.cinfo.size, block_size, frag_size);
521	blks2 = nblk(ext2->cf_ent.cinfo.size, block_size, frag_size);
522
523	if (blks1 != blks2) {
524		/* First, lose the old size, then add the new size. */
525		bused = get_blk_used_n(fsys_entry);
526		bused -= nblks(fsys_entry, ext1);
527		bused += nblks(fsys_entry, ext2);
528
529		set_blk_used_n(fsys_entry, bused);
530	}
531}
532
533/*
534 * This function merges duplicate non-directory entries resulting from a
535 * dryrun or other procedure which preloads the extlist. It uses an odd
536 * heuristic to determine which package object is newest: only package
537 * objects from the dryrun file will have pinfo pointers. Therefore, the
538 * object with a pinfo pointer is from the dryrun file and it will be
539 * overwritten by the object being installed by this package.
540 *
541 * Assumptions:
542 *	1. The newer object will be overwriting the older object.
543 *	2. The two objects are close enough to the same size that
544 *	   the sizing is still OK.
545 *
546 * The calling routine will overwrite ept1, so this must return ept2 with
547 * the correct data to keep. There being only one logical outcome of a
548 * failure, this returns 1 for OK and 0 for FAIL.
549 */
550static int
551dup_merg(struct cfextra *ext1, struct cfextra *ext2)
552{
553	struct cfent *ept1, *ept2;
554
555	ept1 = &(ext1->cf_ent);
556	ept2 = &(ext2->cf_ent);
557
558	if (strchr("?dx", ept1->ftype))
559		return (0);
560
561	if (strchr("?dx", ept2->ftype))
562		return (0);
563
564	/* First, which is the eldest? */
565	if (ext2->mstat.preloaded) {
566		/*
567		 * While ept2 has the correct pinfo list (it was preloaded into
568		 * the array before the pkgmap was read), ept1 has everything
569		 * else. Here we copy the guts of ept1 into ept2.
570		 *
571		 * Start by grabbing the pointers to the ext2 items that we
572		 * need to either restore or free.
573		 */
574		/* to free() */
575		char *path = ept2->path;
576		char *local = ept2->ainfo.local;
577
578		/* to preserve */
579		short npkgs = ept2->npkgs;
580		struct pinfo *pinfo = ept2->pinfo;
581
582		/* Copy everything from the new entry to the old */
583		(void) memcpy(ept2, ept1, sizeof (struct cfent));
584
585		/* Now restore the original stuff.. */
586		ept2->path = path;
587		ept2->ainfo.local = local;
588		ept2->npkgs = npkgs;
589		ept2->pinfo = pinfo;
590
591		size_xchng(ext2, ext1);
592	} else if (ext1->mstat.preloaded) {
593		/*
594		 * ept2 is already the one we will keep. All we have to do is
595		 * copy over the pinfo pointer.
596		 */
597		ept2->pinfo = ept1->pinfo;
598		size_xchng(ext1, ext2);
599	} else
600		return (0);
601
602	return (1);
603}
604
605/*
606 * Check duplicate entries in the package object list. If it's a directory,
607 * this just merges them, if not, it returns a 0 to force further processing.
608 */
609static int
610ckdup(struct cfent *ept1, struct cfent *ept2)
611{
612	/* ept2 will be modified to contain "merged" entries */
613
614	if (!strchr("?dx", ept1->ftype))
615		return (0);
616
617	if (!strchr("?dx", ept2->ftype))
618		return (0);
619
620	if (ept2->ainfo.mode == BADMODE)
621		ept2->ainfo.mode = ept1->ainfo.mode;
622	if ((ept1->ainfo.mode != ept2->ainfo.mode) &&
623	    (ept1->ainfo.mode != BADMODE))
624		return (0);
625
626	if (strcmp(ept2->ainfo.owner, "?") == 0)
627		(void) strlcpy(ept2->ainfo.owner, ept1->ainfo.owner,
628			sizeof (ept2->ainfo.owner));
629	if (strcmp(ept1->ainfo.owner, ept2->ainfo.owner) &&
630	    strcmp(ept1->ainfo.owner, "?"))
631		return (0);
632
633	if (strcmp(ept2->ainfo.group, "?") == 0)
634		(void) strlcpy(ept2->ainfo.group, ept1->ainfo.group,
635			sizeof (ept2->ainfo.group));
636	if (strcmp(ept1->ainfo.group, ept2->ainfo.group) &&
637	    strcmp(ept1->ainfo.group, "?"))
638		return (0);
639
640	if (ept1->pinfo) {
641		ept2->npkgs = ept1->npkgs;
642		ept2->pinfo = ept1->pinfo;
643	}
644
645	return (1);
646}
647
648/*
649 * Replace the old package database entry with the new one preserving the
650 * data which remains constant across the replacement.
651 *	copied directly:
652 *		ftype, pkg_class
653 *
654 *	preserved from old:
655 *		path, npkgs, pinfo
656 */
657void
658repl_cfent(struct cfent *new, struct cfent *old)
659{
660	char *path = old->path;
661	short npkgs = old->npkgs;
662	struct pinfo *pinfo = old->pinfo;
663
664	/* Copy everything from the new entry over */
665	(void) memcpy(old, new, sizeof (struct cfent));
666
667	if (strchr("sl", new->ftype) == NULL)
668		old->ainfo.local = NULL;
669
670	old->path = path;
671	old->npkgs = npkgs;
672	old->pinfo = pinfo;
673
674	old->volno = 0;
675}
676
677/*
678 * Copy critical portions of cf_ent (from the package database) and el_ent
679 * (constructed from the pkgmap) into a merged cfent structure, tp. Then copy
680 * that to the el_ent structure. The approach we take here is to copy over
681 * everything from the package database entry, condition the paths based upon
682 * the currently installed path and then insert the following entries from
683 * the new structure :
684 *	cfent.volno
685 *	pkg_class
686 *	pkg_class_idx
687 *
688 * The pinfo list is then copied from the cfent list. While
689 * fsys_value is also copied over, it hasn't been set yet. This function
690 * copies over whatever the default value is from the new structure.
691 *
692 * The copied entry is returned in the el_ent argument and the function
693 * value is 1 on success, 0 on failure. There is no recovery plan for
694 * failure.
695 */
696int
697cp_cfent(struct cfent *cf_ent, struct cfextra *el_ent)
698{
699	struct cfextra	*tp;
700
701	/* Allocate space for cfent copy */
702	if ((tp = (struct cfextra *)calloc(1,
703	    sizeof (struct cfextra))) == NULL) {
704		progerr(gettext("cp_cfent: memory allocation error"));
705		return (0);
706	}
707
708	/* Copy everything from the package database over */
709	(void) memcpy(&(tp->cf_ent), cf_ent, sizeof (struct cfent));
710
711	/* Now overlay new items from the pkgmap */
712	tp->fsys_value = el_ent->fsys_value;
713	tp->cf_ent.volno = el_ent->cf_ent.volno;
714	(void) strlcpy(tp->cf_ent.pkg_class, el_ent->cf_ent.pkg_class,
715			sizeof (tp->cf_ent.pkg_class));
716	tp->cf_ent.pkg_class_idx = el_ent->cf_ent.pkg_class_idx;
717	tp->cf_ent.pinfo = cf_ent->pinfo;
718
719	/*
720	 * The paths are identical, so we get them from the new entry.  These
721	 * are pointing to a malloc'd section of memory containing a string
722	 * that we aren't moving in this operation, so everybody points to
723	 * the same thing during these transfers.
724	 */
725	tp->cf_ent.path = el_ent->client_path;
726	tp->server_path = el_ent->server_path;
727	tp->client_path = el_ent->client_path;
728	tp->map_path = el_ent->map_path;
729
730	/*
731	 * Since instvol() expects to work with the *original* mstat data,
732	 * mstat is just copied here. NOTE: mstat looks like a structure, but
733	 * it's really a short bit array.
734	 */
735	tp->mstat = el_ent->mstat;
736
737	/* Copy everything from the temporary structure to the new entry */
738	(void) memcpy(el_ent, tp, sizeof (struct cfextra));
739	free(tp);
740
741	return (1);
742}
743