xref: /illumos-gate/usr/src/cmd/svr4pkg/pkginstall/instvol.c (revision d1aea6f139360e9e7f1504facb24f8521047b15c)
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 (c) 2017 Peter Tribble.
24  */
25 
26 /*
27  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
28  */
29 
30 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
31 /* All Rights Reserved */
32 
33 
34 #include <stdio.h>
35 #include <string.h>
36 #include <locale.h>
37 #include <libintl.h>
38 #include <dirent.h>
39 #include <pkgstrct.h>
40 #include <pkgdev.h>
41 #include <pkglocs.h>
42 #include <archives.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <sys/stat.h>
46 #include <sys/param.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <assert.h>
50 #include <wait.h>
51 
52 /*
53  * libinstzones includes
54  */
55 
56 #include <instzones_api.h>
57 
58 /*
59  * consolidation pkg command library includes
60  */
61 
62 #include <pkglib.h>
63 
64 /*
65  * local pkg command library includes
66  */
67 
68 #include <install.h>
69 #include <libinst.h>
70 #include <libadm.h>
71 #include <dryrun.h>
72 #include <messages.h>
73 
74 /*
75  * pkginstall local includes
76  */
77 
78 #include "pkginstall.h"
79 
80 extern int		pkgverbose;
81 extern fsblkcnt_t	pkgmap_blks; 		/* main.c */
82 
83 extern struct pkgdev pkgdev;
84 
85 extern char	tmpdir[];
86 extern char	pkgbin[];
87 extern char	instdir[];
88 extern char	saveSpoolInstallDir[];
89 extern char	*pkginst;
90 
91 extern int	dbchg;
92 extern int	nosetuid;
93 extern int	nocnflct;
94 extern int	warnflag;
95 
96 #define	DMRG_DONE	-1
97 
98 #define	ck_efile(s, p)	\
99 		((p->cinfo.modtime >= 0) && \
100 		p->ainfo.local && \
101 		cverify(0, &p->ftype, s, &p->cinfo, 1))
102 
103 static int	eocflag;
104 
105 /*
106  * The variable below indicates that fix_attributes() will be inadequate
107  * because a replacement was permitted.
108  */
109 static int	repl_permitted = 0;
110 
111 static int	domerg(struct cfextra **extlist, int part, int nparts,
112 			int myclass, char **srcp, char **dstp,
113 			char **r_updated);
114 static void	endofclass(struct cfextra **extlist, int myclass,
115 			int ckflag, PKGserver server, VFP_T **a_cfTmpVfp);
116 static int	fix_attributes(struct cfextra **, int);
117 static int	dir_is_populated(char *dirpath);
118 static boolean_t absolutepath(char *path);
119 static boolean_t parametricpath(char *path, char **relocpath);
120 
121 /* Used to keep track of the entries in extlist that are regular files. */
122 struct reg_files {
123 	struct reg_files *next;
124 	int val;
125 };
126 static struct reg_files *regfiles_head = NULL;
127 
128 /*
129  * This is the function that actually installs one volume (usually that's
130  * all there is). Upon entry, the extlist is entirely correct:
131  *
132  *	1. It contains only those files which are to be installed
133  *	   from all volumes.
134  *	2. The mode bits in the ainfo structure for each file are set
135  *	   correctly in accordance with administrative defaults.
136  *	3. mstat.setuid/setgid reflect what the status *was* before
137  *	   pkgdbmerg() processed compliance.
138  */
139 void
140 instvol(struct cfextra **extlist, char *srcinst, int part,
141 	int nparts, PKGserver pkgserver, VFP_T **a_cfTmpVfp,
142 	char **r_updated, char *a_zoneName)
143 {
144 	FILE		*listfp;
145 	char		*updated = (char *)NULL;
146 	char		*relocpath = (char *)NULL;
147 	char		*dstp;
148 	char		*listfile;
149 	char		*srcp;
150 	char		*pspool_loc;
151 	char		scrpt_dst[PATH_MAX];
152 	int		count;
153 	int		entryidx;	/* array of current package objects */
154 	int		n;
155 	int		nc = 0;
156 	int		pass;		/* pass count through the for loop. */
157 	int		tcount;
158 	struct cfent	*ept;
159 	struct cfextra	*ext;
160 	struct mergstat	*mstat;
161 	struct reg_files *rfp = NULL;
162 
163 	/*
164 	 * r_updated is an optional parameter that can be passed in
165 	 * by the caller if the caller wants to know if any objects are
166 	 * updated. Do not initialize r_updated; the call to instvol
167 	 * could be cumulative and any previous update indication must not
168 	 * be disturbed - this flag is only set, it must never be reset.
169 	 * This flag is a "char *" pointer so that the object that was
170 	 * updated can be displayed in debugging output.
171 	 */
172 
173 	if (part == 1) {
174 		pkgvolume(&pkgdev, srcinst, part, nparts);
175 	}
176 
177 	tcount = 0;
178 	nc = cl_getn();
179 
180 	/*
181 	 * For each class in this volume, install those files.
182 	 *
183 	 * NOTE : This loop index may be decremented by code below forcing a
184 	 * second trip through for the same class. This happens only when a
185 	 * class is split between an archive and the tree. Examples would be
186 	 * old WOS packages and the occasional class containing dynamic
187 	 * libraries which require special treatment.
188 	 */
189 
190 	if (is_depend_pkginfo_DB() == B_FALSE) {
191 	    int		classidx;	/* the current class */
192 
193 	    for (classidx = 0; classidx < nc; classidx++) {
194 		int pass_relative = 0;
195 		int rel_init = 0;
196 
197 		eocflag = count = pass = 0;
198 		listfp = (FILE *)0;
199 		listfile = NULL;
200 
201 		/* Now what do we pass to the class action script */
202 
203 		if (cl_pthrel(classidx) == REL_2_CAS) {
204 			pass_relative = 1;
205 		}
206 
207 		for (;;) {
208 			if (!tcount++) {
209 				/* first file to install */
210 				if (a_zoneName == (char *)NULL) {
211 					echo(MSG_INS_N_N, part, nparts);
212 				} else {
213 					echo(MSG_INS_N_N_LZ, part, nparts,
214 						a_zoneName);
215 				}
216 			}
217 
218 			/*
219 			 * If there's an install class action script and no
220 			 * list file has been created yet, create that file
221 			 * and provide the pointer in listfp.
222 			 */
223 			if (cl_iscript(classidx) && !listfp) {
224 				/* create list file */
225 				putparam("TMPDIR", tmpdir);
226 				listfile = tempnam(tmpdir, "list");
227 				if ((listfp = fopen(listfile, "w")) == NULL) {
228 					progerr(ERR_WTMPFILE, listfile);
229 					quit(99);
230 				}
231 			}
232 
233 			/*
234 			 * The following function goes through the package
235 			 * object list returning the array index of the next
236 			 * regular file. If it encounters a directory,
237 			 * symlink, named pipe or device, it just creates it.
238 			 */
239 
240 			entryidx = domerg(extlist, (pass++ ? 0 : part), nparts,
241 				classidx, &srcp, &dstp, &updated);
242 
243 			/* Evaluate the return code */
244 			if (entryidx == DMRG_DONE) {
245 				/*
246 				 * Set ept to the first entry in extlist
247 				 * which is guaranteed to exist so
248 				 * later checks against ept->ftype are
249 				 * not compared to NULL.
250 				 */
251 				ext = extlist[0];
252 				ept = &(ext->cf_ent);
253 				break; /* no more entries to process */
254 			}
255 
256 			ext = extlist[entryidx];
257 			ept = &(ext->cf_ent);
258 			mstat = &(ext->mstat);
259 
260 			/*
261 			 * If not installing from a partially spooled package
262 			 * (the "save/pspool" area), and the file contents can
263 			 * be changed (type is 'e' or 'v'), and the class is not
264 			 * "none": copy the file from the package (in pristine
265 			 * state with no actions performed) into the appropriate
266 			 * location in the packages destination "save/pspool"
267 			 * area.
268 			 */
269 
270 			if ((!is_partial_inst()) &&
271 				((ept->ftype == 'e') || (ept->ftype == 'v')) &&
272 				(strcmp(ept->pkg_class, "none") != 0)) {
273 
274 				if (absolutepath(ext->map_path) == B_TRUE &&
275 					parametricpath(ext->cf_ent.ainfo.local,
276 						&relocpath) == B_FALSE) {
277 					pspool_loc = ROOT;
278 				} else {
279 					pspool_loc = RELOC;
280 				}
281 
282 				n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s",
283 					saveSpoolInstallDir, pspool_loc,
284 					relocpath ? relocpath : ext->map_path);
285 
286 				if (n >= PATH_MAX) {
287 					progerr(ERR_CREATE_PATH_2,
288 						saveSpoolInstallDir,
289 						ext->map_path);
290 					quit(99);
291 				}
292 
293 				/* copy, preserve source file mode */
294 
295 				if (cppath(MODE_SRC, srcp, scrpt_dst, 0644)) {
296 					warnflag++;
297 				}
298 			}
299 
300 			/*
301 			 * If this isn't writeable anyway, it's not going
302 			 * into the list file. Only count it if it's going
303 			 * into the list file.
304 			 */
305 			if (is_fs_writeable(ext->cf_ent.path,
306 				&(ext->fsys_value)))
307 				count++;
308 
309 			pkgvolume(&pkgdev, srcinst, part, nparts);
310 
311 			/*
312 			 * If source verification is OK for this class, make
313 			 * sure the source we're passing to the class action
314 			 * script is useable.
315 			 */
316 			if (cl_svfy(classidx) != NOVERIFY) {
317 				if (cl_iscript(classidx) ||
318 					((ept->ftype == 'e') ||
319 					(ept->ftype == 'n'))) {
320 					if (ck_efile(srcp, ept)) {
321 						progerr(ERR_CORRUPT,
322 							srcp);
323 						logerr(getErrbufAddr());
324 						warnflag++;
325 						continue;
326 					}
327 				}
328 			}
329 
330 			/*
331 			 * If there's a class action script for this class,
332 			 * just collect names in a temporary file
333 			 * that will be used as the stdin when the
334 			 * class action script is invoked.
335 			 */
336 
337 			if ((cl_iscript(classidx)) &&
338 					((is_fs_writeable(ept->path,
339 						&(ext->fsys_value))))) {
340 				if (pass_relative) {
341 					if (!rel_init) {
342 						(void) fputs(instdir, listfp);
343 						(void) putc('\n', listfp);
344 						rel_init++;
345 					}
346 					(void) fputs(ext->map_path, listfp);
347 					(void) putc('\n', listfp);
348 				} else {
349 					(void) fputs(srcp ?
350 						srcp : "/dev/null", listfp);
351 					(void) putc(' ', listfp);
352 					(void) fputs(dstp, listfp);
353 					(void) putc('\n', listfp);
354 				}
355 				/*
356 				 * Note which entries in extlist are regular
357 				 * files to be installed via the class action
358 				 * script.
359 				 */
360 				if (regfiles_head == NULL) {
361 					assert(rfp == NULL);
362 					regfiles_head =
363 					    malloc(sizeof (struct reg_files));
364 					if (regfiles_head == NULL) {
365 						progerr(ERR_MEMORY, errno);
366 						quit(99);
367 					}
368 					regfiles_head->next = NULL;
369 					regfiles_head->val = entryidx;
370 					rfp = regfiles_head;
371 				} else {
372 					assert(rfp != NULL);
373 					rfp->next =
374 					    malloc(sizeof (struct reg_files));
375 					if (rfp->next == NULL) {
376 						progerr(ERR_MEMORY, errno);
377 						quit(99);
378 					}
379 					rfp = rfp->next;
380 					rfp->next = NULL;
381 					rfp->val = entryidx;
382 				}
383 
384 				/*
385 				 * A warning message about unwritable targets
386 				 * in a class may be appropriate here.
387 				 */
388 				continue;
389 			}
390 
391 			/*
392 			 * If not installing from a partially spooled package
393 			 * (the "save/pspool" area), and the file contents can
394 			 * be changed (type is 'e' or 'v') and the class
395 			 * identifier is not "none": copy the file from the
396 			 * package (in pristine state with no actions performed)
397 			 * into the appropriate location in the packages
398 			 * destination "save/pspool" area.
399 			 */
400 
401 			if ((!is_partial_inst()) &&
402 			    ((ept->ftype == 'e') || (ept->ftype == 'v') &&
403 			    (strcmp(ept->pkg_class, "none") != 0))) {
404 
405 				if (absolutepath(ext->map_path) == B_TRUE &&
406 					parametricpath(ext->cf_ent.ainfo.local,
407 						&relocpath) == B_FALSE) {
408 					pspool_loc = ROOT;
409 				} else {
410 					pspool_loc = RELOC;
411 				}
412 
413 				n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s",
414 					saveSpoolInstallDir, pspool_loc,
415 					relocpath ? relocpath : ext->map_path);
416 
417 				if (n >= PATH_MAX) {
418 					progerr(ERR_CREATE_PATH_2,
419 						saveSpoolInstallDir,
420 						ext->map_path);
421 					quit(99);
422 				}
423 
424 				/* copy, preserve source file mode */
425 
426 				if (cppath(MODE_SRC, srcp, scrpt_dst, 0644)) {
427 					warnflag++;
428 				}
429 			}
430 
431 			/*
432 			 * There are several tests here to determine
433 			 * how we're going to deal with objects
434 			 * intended for remote read-only filesystems.
435 			 * We don't use is_served() because this may be
436 			 * a server. We're actually interested in if
437 			 * it's *really* remote and *really* not
438 			 * writeable.
439 			 */
440 
441 			n = is_remote_fs(ept->path, &(ext->fsys_value));
442 			if ((n != 0) &&
443 				!is_fs_writeable(ept->path,
444 				&(ext->fsys_value))) {
445 
446 				/*
447 				 * Don't change the file, we can't write
448 				 * to it anyway.
449 				 */
450 
451 				mstat->attrchg = 0;
452 				mstat->contchg = 0;
453 
454 				/*
455 				 * If it's currently mounted, we can
456 				 * at least test it for existence.
457 				 */
458 
459 				if (is_mounted(ept->path, &(ext->fsys_value))) {
460 					if (!isfile(NULL, dstp)) {
461 						echo(MSG_IS_PRESENT, dstp);
462 					} else {
463 						echo(WRN_INSTVOL_NONE, dstp);
464 					}
465 				} else {
466 					char *server_host;
467 
468 					server_host = get_server_host(
469 						ext->fsys_value);
470 
471 					/* If not, we're just stuck. */
472 					echo(WRN_INSTVOL_NOVERIFY,
473 						dstp, server_host);
474 				}
475 
476 				continue;
477 			}
478 
479 			/* echo output destination name */
480 
481 			echo("%s", dstp);
482 
483 			/*
484 			 * if no source then no need to copy/verify
485 			 */
486 
487 			if (srcp == (char *)NULL) {
488 				continue;
489 			}
490 
491 			/*
492 			 * If doing a partial installation (creating a
493 			 * non-global zone), extra steps need to be taken:
494 			 *
495 			 * If the file is not type 'e' and not type 'v' and
496 			 * the class is "none": then the file must already
497 			 * exist (as a result of the initial non-global zone
498 			 * installation which caused all non-e/v files to be
499 			 * copied from the global zone to the non-global
500 			 * zone). If this is the case, verify that the file
501 			 * exists and has the correct attributes.
502 			 */
503 
504 			if (is_partial_inst() != 0) {
505 
506 				/*
507 				 * if not type 'e|v' and class 'none', then the
508 				 * file must already exist.
509 				 */
510 
511 				if ((ept->ftype != 'e') &&
512 					(ept->ftype != 'v') &&
513 					(strcmp(cl_nam(ept->pkg_class_idx),
514 								"none") == 0)) {
515 
516 					/* is file changed? */
517 					n = finalck(ept, 1, 1, B_TRUE);
518 
519 					/* not - ok - warn */
520 					if (n != 0) {
521 						/* output warning message */
522 						logerr(NOTE_INSTVOL_FINALCKFAIL,
523 						    pkginst, ext->map_path);
524 					}
525 					continue;
526 				}
527 			}
528 
529 			/*
530 			 * Copy from source media to target path and fix file
531 			 * mode and permission now in case installation halted.
532 			 */
533 
534 			/*
535 			 * If the filesystem is read-only don't attempt
536 			 * to copy a file. Just check that the content
537 			 * and attributes of the file are correct.
538 			 *
539 			 * Normally this doesn't happen, because files,
540 			 * which don't change, are not returned by
541 			 * domerg().
542 			 */
543 			n = 0;
544 			if (is_fs_writeable(ept->path,
545 			    &(ext->fsys_value)))
546 				n = cppath(MODE_SET|DIR_DISPLAY, srcp,
547 				    dstp, ept->ainfo.mode);
548 
549 			if (n != 0) {
550 				warnflag++;
551 			} else if (!finalck(ept, 1, 1, B_FALSE)) {
552 				/*
553 				 * everything checks here
554 				 */
555 				mstat->attrchg = 0;
556 				mstat->contchg = 0;
557 			}
558 
559 			/* NOTE: a package object was updated */
560 
561 			if (updated == (char *)NULL) {
562 				echoDebug(DBG_INSTVOL_OBJ_UPDATED, dstp);
563 				updated = dstp;
564 			}
565 		}
566 
567 		/*
568 		 * We have now completed processing of all pathnames
569 		 * associated with this volume and class.
570 		 */
571 		if (cl_iscript(classidx)) {
572 			/*
573 			 * Execute appropriate class action script
574 			 * with list of source/destination pathnames
575 			 * as the input to the script.
576 			 */
577 
578 			if (chdir(pkgbin)) {
579 				progerr(ERR_CHGDIR, pkgbin);
580 				quit(99);
581 			}
582 
583 			if (listfp) {
584 				(void) fclose(listfp);
585 			}
586 
587 			/* nothing updated */
588 
589 			echoDebug(DBG_INSTVOL_CAS_INFO, is_partial_inst(),
590 				updated ? updated : "");
591 
592 			if ((is_partial_inst() != 0) &&
593 					(updated == (char *)NULL)) {
594 
595 				/*
596 				 * installing in non-global zone, and no object
597 				 * has been updated (installed/verified):
598 				 * do not run the class action script.
599 				 */
600 
601 				echoDebug(DBG_INSTVOL_NOT_RUNNING_CAS,
602 					a_zoneName ? a_zoneName : "?",
603 					eocflag ? "ENDOFCLASS" :
604 							cl_iscript(classidx),
605 					cl_nam(classidx),
606 					cl_iscript(classidx));
607 
608 			} else {
609 				/* run the class action script */
610 
611 				echoDebug(DBG_INSTVOL_RUNNING_CAS,
612 					a_zoneName ? a_zoneName : "?",
613 					eocflag ? "ENDOFCLASS" :
614 							cl_iscript(classidx),
615 					cl_nam(classidx),
616 					cl_iscript(classidx));
617 
618 				/* Use ULIMIT if supplied. */
619 				set_ulimit(cl_iscript(classidx), ERR_CASFAIL);
620 
621 				if (eocflag) {
622 					/*
623 					 * end of class detected.
624 					 * Since there are no more volumes which
625 					 * contain pathnames associated with
626 					 * this class, execute class action
627 					 * script with the ENDOFCLASS argument;
628 					 * we do this even if none of the path
629 					 * names associated with this class and
630 					 * volume needed installation to
631 					 * guarantee the class action script is
632 					 * executed at least once during package
633 					 * installation.
634 					 */
635 					if (pkgverbose) {
636 						n = pkgexecl((listfp ?
637 							listfile : CAS_STDIN),
638 							CAS_STDOUT,
639 							CAS_USER, CAS_GRP,
640 							SHELL, "-x",
641 							cl_iscript(classidx),
642 							"ENDOFCLASS", NULL);
643 					} else {
644 						n = pkgexecl(
645 							(listfp ?
646 							listfile : CAS_STDIN),
647 							CAS_STDOUT, CAS_USER,
648 							CAS_GRP, SHELL,
649 							cl_iscript(classidx),
650 							"ENDOFCLASS", NULL);
651 					}
652 					ckreturn(n, ERR_CASFAIL);
653 				} else if (count) {
654 					/* execute class action script */
655 					if (pkgverbose) {
656 						n = pkgexecl(listfile,
657 							CAS_STDOUT, CAS_USER,
658 							CAS_GRP, SHELL, "-x",
659 							cl_iscript(classidx),
660 							NULL);
661 					} else {
662 						n = pkgexecl(listfile,
663 							CAS_STDOUT, CAS_USER,
664 							CAS_GRP, SHELL,
665 							cl_iscript(classidx),
666 							NULL);
667 					}
668 					ckreturn(n, ERR_CASFAIL);
669 				}
670 
671 				/*
672 				 * Ensure the mod times on disk match those
673 				 * in the pkgmap. In this case, call cverify
674 				 * with checksumming disabled, since the only
675 				 * action that needs to be done is to verify
676 				 * that the attributes are correct.
677 				 */
678 
679 				if ((rfp = regfiles_head) != NULL) {
680 					while (rfp != NULL) {
681 					    ept = &(extlist[rfp->val]->cf_ent);
682 					    cverify(1, &ept->ftype, ept->path,
683 						&ept->cinfo, 0);
684 					    rfp = rfp->next;
685 					}
686 					regfiles_free();
687 				}
688 
689 				clr_ulimit();
690 
691 				if ((r_updated != (char **)NULL) &&
692 					(*r_updated == (char *)NULL) &&
693 					(updated == (char *)NULL)) {
694 					updated = "postinstall";
695 					echoDebug(DBG_INSTVOL_OBJ_UPDATED,
696 								updated);
697 				}
698 			}
699 			if (listfile) {
700 				(void) remove(listfile);
701 			}
702 		}
703 
704 		if (eocflag && (!is_partial_inst() || (is_partial_inst() &&
705 			strcmp(cl_nam(classidx), "none") != 0))) {
706 			if (cl_dvfy(classidx) == QKVERIFY && !repl_permitted) {
707 				/*
708 				 * The quick verify just fixes everything.
709 				 * If it returns 0, all is well. If it
710 				 * returns 1, then the class installation
711 				 * was incomplete and we retry on the
712 				 * stuff that failed in the conventional
713 				 * way (without a CAS). this is primarily
714 				 * to accomodate old archives such as are
715 				 * found in pre-2.5 WOS; but, it is also
716 				 * used when a critical dynamic library
717 				 * is not archived with its class.
718 				 */
719 				if (!fix_attributes(extlist, classidx)) {
720 					/*
721 					 * Reset the CAS pointer. If the
722 					 * function returns 0 then there
723 					 * was no script there in the first
724 					 * place and we'll just have to
725 					 * call this a miss.
726 					 */
727 					if (cl_deliscript(classidx))
728 						/*
729 						 * Decrement classidx for
730 						 * next pass.
731 						 */
732 						classidx--;
733 				}
734 			} else {
735 				/*
736 				 * Finalize merge. This checks to make sure
737 				 * file attributes are correct and any links
738 				 * specified are created.
739 				 */
740 				(void) endofclass(extlist, classidx,
741 					(cl_iscript(classidx) ? 0 : 1),
742 					pkgserver, a_cfTmpVfp);
743 			}
744 		}
745 	    }
746 	}
747 
748 	/*
749 	 * Instead of creating links back to the GZ files the logic is
750 	 * to let zdo recreate the files from the GZ then invoke pkgadd to
751 	 * install the editable files and skip over any 'f'type files.
752 	 * The commented out block is to create the links which should be
753 	 * removed once the current code is tested to be correct.
754 	 */
755 
756 	/*
757 	 * Go through extlist creating links for 'f'type files
758 	 * if we're in a global zone. Note that this code lies
759 	 * here instead of in the main loop to support CAF packages.
760 	 * In a CAF package the files are installed by the i.none script
761 	 * and don't exist until all files are done being processed, thus
762 	 * the additional loop through extlist.
763 	 */
764 
765 	/*
766 	 * output appropriate completion message
767 	 */
768 
769 	if (is_depend_pkginfo_DB() == B_TRUE) {
770 		/* updating database only (hollow package) */
771 		if (a_zoneName == (char *)NULL) {
772 			echo(MSG_DBUPD_N_N, part, nparts);
773 		} else {
774 			echo(MSG_DBUPD_N_N_LZ, part, nparts, a_zoneName);
775 		}
776 	} else if (tcount == 0) {
777 		/* updating package (non-hollow package) */
778 		if (a_zoneName == (char *)NULL) {
779 			echo(MSG_INST_N_N, part, nparts);
780 		} else {
781 			echo(MSG_INST_N_N_LZ, part, nparts, a_zoneName);
782 		}
783 	}
784 
785 	/*
786 	 * if any package objects were updated (not otherwise already in
787 	 * existence), set the updated flag as appropriate
788 	 */
789 
790 	if (updated != (char *)NULL) {
791 		echoDebug(DBG_INSTVOL_OBJ_UPDATED, updated);
792 		if (r_updated != (char **)NULL) {
793 			*r_updated = updated;
794 		}
795 	}
796 
797 }
798 
799 /*
800  * Name:	domerg
801  * Description: For the specified class, review each entry and return the array
802  *		index number of the next regular file to process. Hard links are
803  *		skipped (they are created in endofclass() and directories,
804  *		symlinks, pipes and devices are created here, as well as any
805  *		file that already exists and has the correct attributes.
806  * Arguments:	struct cfextra **extlist - [RO, *RW]
807  *			- Pointer to list of cfextra structures representing
808  *			  the pkgmap of the package to be installed
809  *		int part - [RO, *RO]
810  *			- the part of the package currently being processed;
811  *			  packages begin with part "1" and proceed for the
812  *			  number (nparts) that comprise the package (volume).
813  *		int nparts - [RO, *RO]
814  *			- the number of parts the package is divided into
815  *		int myclass - [RO, *RO]
816  *			- index into class array of the current class
817  *		char **srcp - [RW, *RW]
818  *			- pointer to pointer to string representing the source
819  *			  path for the next package to process - if this
820  *			  function returns != DMRG_DONE then this pointer is
821  *			  set to a pointer to a string representing the source
822  *			  path for the next object from the package to process
823  *		char **dstp - [RW, *RW]
824  *			- pointer to pointer to string representing the target
825  *			  path for the next package to process - if this
826  *			  function returns != DMRG_DONE then this pointer is
827  *			  set to a pointer to a string representing the target
828  *			  path for the next object from the package to process
829  *		char **r_updated - [RO, *RW]
830  *			- pointer to pointer to string - set if the last path
831  *			  returned exists or does not need updating. This is
832  *			  always set when a path to be installed exists and
833  *			  has the correct contents.
834  * Returns:	int
835  *			!= DMRG_DONE - index into extlist of the next path to
836  *				be processed - that needs to be installed/copied
837  *			== DMRG_DONE - all entries processed
838  */
839 
840 static int
841 domerg(struct cfextra **extlist, int part, int nparts,
842 	int myclass, char **srcp, char **dstp,
843 	char **r_updated)
844 {
845 	boolean_t	stateFlag = B_FALSE;
846 	int		i;
847 	int		msg_ugid;
848 	static int	maxvol = 0;
849 	static int	svindx = 0;
850 	static int	svpart = 0;
851 	struct cfent	*ept = (struct cfent *)NULL;
852 	struct mergstat *mstat = (struct mergstat *)NULL;
853 
854 	/* reset returned path pointers */
855 
856 	*dstp = (char *)NULL;
857 	*srcp = (char *)NULL;
858 
859 	/* set to start or continue based on which part being processed */
860 
861 	if (part != 0) {
862 		maxvol = 0;
863 		svindx = 0;
864 		svpart = part;
865 	} else {
866 		i = svindx;
867 		part = svpart;
868 	}
869 
870 	/*
871 	 * This goes through the pkgmap entries one by one testing them
872 	 * for inclusion in the package database as well as for validity
873 	 * against existing files.
874 	 */
875 	for (i = svindx; extlist[i]; i++) {
876 		ept = &(extlist[i]->cf_ent);
877 		mstat = &(extlist[i]->mstat);
878 
879 		/* if this isn't the class of current interest, skip it */
880 
881 		if (myclass != ept->pkg_class_idx) {
882 			continue;
883 		}
884 
885 		/* if the class is invalid, announce it & exit */
886 		if (ept->pkg_class_idx == -1) {
887 			progerr(ERR_CLIDX, ept->pkg_class_idx,
888 			    (ept->path && *ept->path) ? ept->path : "unknown");
889 			logerr(gettext("pathname=%s"),
890 			    (ept->path && *ept->path) ? ept->path : "unknown");
891 			logerr(gettext("class=<%s>"),
892 			    (ept->pkg_class && *ept->pkg_class) ?
893 			    ept->pkg_class : "Unknown");
894 			logerr(gettext("CLASSES=<%s>"),
895 			    getenv("CLASSES") ? getenv("CLASSES") : "Not Set");
896 			quit(99);
897 		}
898 
899 		/*
900 		 * Next check to see if we are going to try to delete a
901 		 * populated directory in some distressing way.
902 		 */
903 		if (mstat->dir2nondir)
904 			if (dir_is_populated(ept->path)) {
905 				logerr(WRN_INSTVOL_NOTDIR, ept->path);
906 				warnflag++;
907 				mstat->denied = 1;	/* install denied! */
908 				continue;
909 			} else {	/* Replace is OK. */
910 				/*
911 				 * Remove this directory, so it won't
912 				 * interfere with creation of the new object.
913 				 */
914 				if (rmdir(ept->path)) {
915 					/*
916 					 * If it didn't work, there's nothing
917 					 * we can do. To continue would
918 					 * likely corrupt the filesystem
919 					 * which is unacceptable.
920 					 */
921 					progerr(ERR_RMDIR, ept->path);
922 					quit(99);
923 				}
924 
925 				repl_permitted = 1;	/* flag it */
926 			}
927 
928 		/* adjust the max volume number appropriately */
929 
930 		if (ept->volno > maxvol) {
931 			maxvol = ept->volno;
932 		}
933 
934 		/* if this part goes into another volume, skip it */
935 
936 		if (part != ept->volno) {
937 			continue;
938 		}
939 
940 		/*
941 		 * If it's a conflicting file and it's not supposed to be
942 		 * installed, note it and skip.
943 		 */
944 		if (nocnflct && mstat->shared && ept->ftype != 'e') {
945 			if (mstat->contchg || mstat->attrchg) {
946 				echo(MSG_SHIGN, ept->path);
947 			}
948 			continue;
949 		}
950 
951 		/*
952 		 * If we want to set uid or gid but user says no, note it.
953 		 * Remember that the actual mode bits in the structure have
954 		 * already been adjusted and the mstat flag is telling us
955 		 * about the original mode.
956 		 */
957 		if (nosetuid && (mstat->setuid || mstat->setgid)) {
958 			msg_ugid = 1;	/* don't repeat attribute message. */
959 			if (is_fs_writeable(ept->path,
960 				&(extlist[i]->fsys_value))) {
961 				if (!(mstat->contchg) && mstat->attrchg) {
962 					echo(MSG_UGMOD, ept->path);
963 				} else {
964 					echo(MSG_UGID, ept->path);
965 				}
966 			}
967 		} else {
968 			msg_ugid = 0;
969 		}
970 
971 		switch (ept->ftype) {
972 			case 'l':	/* hard link */
973 				/* links treated as object "update/skip" */
974 				stateFlag = B_TRUE;
975 				continue; /* defer to final proc */
976 
977 			case 's': /* for symlink, verify without fix first */
978 				/* links treated as object "update/skip" */
979 				stateFlag = B_TRUE;
980 
981 				/* Do this only for default verify */
982 				if (cl_dvfy(myclass) == DEFAULT) {
983 					if (averify(0, &ept->ftype,
984 						ept->path, &ept->ainfo))
985 						echo(MSG_SLINK, ept->path);
986 				}
987 
988 				/*FALLTHRU*/
989 
990 			case 'd':	/* directory */
991 			case 'x':	/* exclusive directory */
992 			case 'c':	/* character special device */
993 			case 'b':	/* block special device */
994 			case 'p':	/* named pipe */
995 				/* these NOT treated as object "update/skip" */
996 				stateFlag = B_FALSE;
997 
998 				/*
999 				 * If we can't get to it for legitimate reasons,
1000 				 * don't try to verify it.
1001 				 */
1002 				if (is_remote_fs(ept->path,
1003 				    &(extlist[i]->fsys_value)) &&
1004 				    !is_fs_writeable(ept->path,
1005 				    &(extlist[i]->fsys_value))) {
1006 					mstat->attrchg = 0;
1007 					mstat->contchg = 0;
1008 					break;
1009 				}
1010 
1011 				if (averify(1, &ept->ftype, ept->path,
1012 							&ept->ainfo) == 0) {
1013 					mstat->contchg = mstat->attrchg = 0;
1014 				} else {
1015 					progerr(ERR_CREATE_PKGOBJ, ept->path);
1016 					logerr(getErrbufAddr());
1017 					warnflag++;
1018 				}
1019 
1020 				break;
1021 
1022 			case 'i':	/* information file */
1023 				/* not treated as object "update/skip" */
1024 				stateFlag = B_FALSE;
1025 				break;
1026 
1027 			default:
1028 				/* all files treated as object "update/skip" */
1029 				stateFlag = B_TRUE;
1030 				break;
1031 		}
1032 
1033 		/*
1034 		 * Both contchg and shared flags have to be taken into
1035 		 * account. contchg is set if the file is already present
1036 		 * in the package database, if it does not exist or if it
1037 		 * exists and is modified.
1038 		 * The shared flag is set when 'e' or 'v' file is not
1039 		 * present in the package database, exists and is not
1040 		 * modified. It also has to be checked here.
1041 		 * Shared flag is also set when file is present in package
1042 		 * database and owned by more than one package, but for
1043 		 * this case contchg has already been set.
1044 		 */
1045 		if (mstat->contchg || (mstat->shared &&
1046 		    ((ept->ftype == 'e') || (ept->ftype == 'v')))) {
1047 			*dstp = ept->path;
1048 			if ((ept->ftype == 'f') || (ept->ftype == 'e') ||
1049 				(ept->ftype == 'v')) {
1050 				*srcp = ept->ainfo.local;
1051 				if (is_partial_inst() != 0) {
1052 					if (*srcp[0] == '~') {
1053 						/* Done only for C style */
1054 						char *tmp_ptr;
1055 						tmp_ptr = extlist[i]->map_path;
1056 						if (ept->ftype != 'f') {
1057 							/*
1058 							 * translate source
1059 							 * pathname
1060 							 */
1061 							*srcp =
1062 							    srcpath(instdir,
1063 							    tmp_ptr,
1064 							    part,
1065 							    nparts);
1066 						} else {
1067 						/*
1068 						 * instdir has the absolute path
1069 						 * to saveSpoolInstallDir for
1070 						 * the package. This is only
1071 						 * useful for 'e','v' types.
1072 						 *
1073 						 * For 'f', we generate the
1074 						 * absolute src path with the
1075 						 * help of install root and the
1076 						 * basedir.
1077 						 */
1078 							*srcp = trans_srcp_pi(
1079 							    ept->ainfo.local);
1080 						}
1081 					} else {
1082 						*srcp = extlist[i]->map_path;
1083 					}
1084 				} else {
1085 					if (*srcp[0] == '~') {
1086 						/* translate source pathname */
1087 						*srcp = srcpath(instdir,
1088 						    &(ept->ainfo.local[1]),
1089 						    part, nparts);
1090 					}
1091 				}
1092 
1093 				echoDebug(DBG_DOMERG_NO_SUCH_FILE,
1094 					ept->ftype, cl_nam(ept->pkg_class_idx),
1095 					ept->path);
1096 			} else {
1097 				/*
1098 				 * At this point, we're returning a non-file
1099 				 * that couldn't be created in the standard
1100 				 * way. If it refers to a filesystem that is
1101 				 * not writeable by us, don't waste the
1102 				 * calling process's time.
1103 				 */
1104 				if (!is_fs_writeable(ept->path,
1105 					&(extlist[i]->fsys_value))) {
1106 					echoDebug(DBG_DOMERG_NOT_WRITABLE,
1107 						ept->ftype,
1108 						cl_nam(ept->pkg_class_idx),
1109 						ept->path);
1110 					continue;
1111 				}
1112 
1113 				*srcp = NULL;
1114 				echoDebug(DBG_DOMERG_NOT_THERE,
1115 					ept->ftype, cl_nam(ept->pkg_class_idx),
1116 					ept->path);
1117 			}
1118 
1119 			svindx = i+1;
1120 			backup(*dstp, 1);
1121 			return (i);
1122 		}
1123 
1124 		if (mstat->attrchg) {
1125 			backup(ept->path, 0);
1126 			if (!msg_ugid)
1127 				echo(MSG_ATTRIB, ept->path);
1128 
1129 			/* fix the attributes now for robustness sake */
1130 			if (averify(1, &ept->ftype,
1131 				ept->path,
1132 				&ept->ainfo) == 0) {
1133 				mstat->attrchg = 0;
1134 			}
1135 		}
1136 
1137 		/*
1138 		 * package object exists, or does not need updating:
1139 		 * treat the object as if it were "updated"
1140 		 */
1141 
1142 		/* LINTED warning: statement has no consequent: if */
1143 		if ((stateFlag == B_FALSE) || (ept == (struct cfent *)NULL)) {
1144 			/*
1145 			 * the object in question is a directory or special
1146 			 * file - the fact that this type of object already
1147 			 * exists or does not need updating must not trigger
1148 			 * the object updated indication - that would cause
1149 			 * class action scripts to be run when installing a
1150 			 * new non-global zone
1151 			 */
1152 		} else {
1153 			if (r_updated != (char **)NULL) {
1154 				if (*r_updated == (char *)NULL) {
1155 					echoDebug(DBG_INSTVOL_OBJ_UPDATED,
1156 								ept->path);
1157 				}
1158 				*r_updated = ept->path;
1159 			}
1160 		}
1161 	}
1162 
1163 	if (maxvol == part) {
1164 		eocflag++;	/* endofclass */
1165 	}
1166 
1167 	return (DMRG_DONE);	/* no remaining entries on this volume */
1168 }
1169 
1170 /*
1171  * Determine if the provided directory is populated. Return 0 if so and 1 if
1172  * not. This also returns 0 if the dirpath is not a directory or if it does
1173  * not exist.
1174  */
1175 static int
1176 dir_is_populated(char *dirpath) {
1177 	DIR	*dirfp;
1178 	struct	dirent *drp;
1179 	int	retcode = 0;
1180 
1181 	if ((dirfp = opendir(dirpath)) != NULL) {
1182 		while ((drp = readdir(dirfp)) != NULL) {
1183 			if (strcmp(drp->d_name, ".") == 0) {
1184 				continue;
1185 			}
1186 			if (strcmp(drp->d_name, "..") == 0) {
1187 				continue;
1188 			}
1189 			/*
1190 			 * If we get here, there's a real file in the
1191 			 * directory
1192 			 */
1193 			retcode = 1;
1194 			break;
1195 		}
1196 		(void) closedir(dirfp);
1197 	}
1198 
1199 	return (retcode);
1200 }
1201 
1202 /*
1203  * This is the function that cleans up the installation of this class.
1204  * This is where hard links get put in since the stuff they're linking
1205  * probably exists by now.
1206  */
1207 static void
1208 endofclass(struct cfextra **extlist, int myclass, int ckflag,
1209 	PKGserver pkgserver, VFP_T **a_cfTmpVfp)
1210 {
1211 	char		*temppath;
1212 	char 		*pspool_loc;
1213 	char 		*relocpath = (char *)NULL;
1214 	char 		scrpt_dst[PATH_MAX];
1215 	int		flag;
1216 	int		idx;
1217 	int		n;
1218 	struct cfent	*ept;	/* entry from the internal list */
1219 	struct cfextra	entry;	/* entry from the package database */
1220 	struct mergstat	*mstat;	/* merge status */
1221 	struct pinfo	*pinfo;
1222 
1223 	/* open the package database (contents) file */
1224 
1225 	if (!ocfile(&pkgserver, a_cfTmpVfp, pkgmap_blks)) {
1226 		quit(99);
1227 	}
1228 
1229 	echo(MSG_VERIFYING_CLASS, cl_nam(myclass));
1230 
1231 	for (idx = 0; /* void */; idx++) {
1232 		/* find next package object in this class */
1233 		while (extlist[idx]) {
1234 			if ((extlist[idx]->cf_ent.ftype != 'i') &&
1235 				extlist[idx]->cf_ent.pkg_class_idx == myclass) {
1236 				break;
1237 			}
1238 			idx++;
1239 		}
1240 
1241 		if (extlist[idx] == NULL)
1242 			break;
1243 
1244 
1245 		ept = &(extlist[idx]->cf_ent);
1246 		mstat = &(extlist[idx]->mstat);
1247 
1248 		temppath = extlist[idx]->client_path;
1249 
1250 		/*
1251 		 * At this point  the only difference between the entry
1252 		 * in the contents file and the entry in extlist[] is
1253 		 * that the status indicator contains CONFIRM_CONT.
1254 		 * This function should return one or something is wrong.
1255 		 */
1256 
1257 		n = srchcfile(&(entry.cf_ent), temppath, pkgserver);
1258 
1259 		if (n < 0) {
1260 			char	*errstr = getErrstr();
1261 			progerr(ERR_CFBAD);
1262 			logerr(gettext("pathname=%s"),
1263 				entry.cf_ent.path && *entry.cf_ent.path ?
1264 				entry.cf_ent.path : "Unknown");
1265 			logerr(gettext("problem=%s"),
1266 				(errstr && *errstr) ? errstr : "Unknown");
1267 			quit(99);
1268 		} else if (n != 1) {
1269 			/*
1270 			 * Check if path should be in the package
1271 			 * database.
1272 			 */
1273 			if ((mstat->shared && nocnflct)) {
1274 				continue;
1275 			}
1276 			progerr(ERR_CFMISSING, ept->path);
1277 			quit(99);
1278 		}
1279 
1280 		/*
1281 		 * If merge was not appropriate for this object, now is the
1282 		 * time to choose one or the other.
1283 		 */
1284 		if (mstat->denied) {
1285 			/*
1286 			 * If installation was denied AFTER the package
1287 			 * database was updated, skip this. We've already
1288 			 * announced the discrepancy and the verifications
1289 			 * that follow will make faulty decisions based on
1290 			 * the ftype, which may not be correct.
1291 			 */
1292 			progerr(ERR_COULD_NOT_INSTALL, ept->path);
1293 			warnflag++;
1294 		} else {
1295 			if (mstat->replace)
1296 				/*
1297 				 * This replaces the old entry with the new
1298 				 * one. This should never happen in the new
1299 				 * DB since the entries are already identical.
1300 				 */
1301 				repl_cfent(ept, &(entry.cf_ent));
1302 
1303 			/*
1304 			 * Validate this entry and change the status flag in
1305 			 * the package database.
1306 			 */
1307 			if (ept->ftype == RM_RDY) {
1308 				(void) eptstat(&(entry.cf_ent), pkginst,
1309 					STAT_NEXT);
1310 			} else {
1311 				/* check the hard link now. */
1312 				if (ept->ftype == 'l') {
1313 					if (averify(0, &ept->ftype,
1314 						ept->path, &ept->ainfo)) {
1315 						echo(MSG_HRDLINK,
1316 							ept->path);
1317 						mstat->attrchg++;
1318 					}
1319 				}
1320 
1321 				/*
1322 				 * Don't install or verify objects for
1323 				 * remote, read-only filesystems.  We need
1324 				 * only flag them as shared from some server.
1325 				 * Otherwise, ok to do final check.
1326 				 */
1327 				if (is_remote_fs(ept->path,
1328 					&(extlist[idx]->fsys_value)) &&
1329 					!is_fs_writeable(ept->path,
1330 					&(extlist[idx]->fsys_value))) {
1331 					flag = -1;
1332 				} else {
1333 					flag = finalck(ept, mstat->attrchg,
1334 						(ckflag ? mstat->contchg :
1335 						(-1)), B_FALSE);
1336 				}
1337 
1338 				pinfo = entry.cf_ent.pinfo;
1339 
1340 				/* Find this package in the list. */
1341 				while (pinfo) {
1342 					if (strcmp(pkginst, pinfo->pkg) == 0) {
1343 						break;
1344 					}
1345 					pinfo = pinfo->next;
1346 				}
1347 
1348 				/*
1349 				 * If this package owns this file, then store
1350 				 * it in the database with the appropriate
1351 				 * status. Need to check pinfo in case it
1352 				 * points to NULL which could happen if
1353 				 * pinfo->next = NULL above.
1354 				 */
1355 				if (pinfo) {
1356 					if (flag < 0 || is_served(ept->path,
1357 						&(extlist[idx]->fsys_value))) {
1358 						/*
1359 						 * This is provided to
1360 						 * clients by a server.
1361 						 */
1362 						pinfo->status = SERVED_FILE;
1363 					} else {
1364 						/*
1365 						 * It's either there or it's
1366 						 * not.
1367 						 */
1368 						pinfo->status = (flag ?
1369 							NOT_FND : ENTRY_OK);
1370 					}
1371 				}
1372 			}
1373 		}
1374 
1375 		/*
1376 		 * If not installing from a partially spooled package, the
1377 		 * "save/pspool" area, and the file contents can be
1378 		 * changed (type is 'e' or 'v'), and the class IS "none":
1379 		 * copy the installed volatile file into the appropriate
1380 		 * location in the packages destination "save/pspool" area.
1381 		 */
1382 
1383 		if ((!is_partial_inst()) &&
1384 			((ept->ftype == 'e') || (ept->ftype == 'v')) &&
1385 			(strcmp(ept->pkg_class, "none") == 0)) {
1386 
1387 			if (absolutepath(extlist[idx]->map_path) == B_TRUE &&
1388 				parametricpath(extlist[idx]->cf_ent.ainfo.local,
1389 					&relocpath) == B_FALSE) {
1390 				pspool_loc = ROOT;
1391 			} else {
1392 				pspool_loc = RELOC;
1393 			}
1394 
1395 			n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s",
1396 				saveSpoolInstallDir, pspool_loc,
1397 				relocpath ? relocpath : extlist[idx]->map_path);
1398 
1399 			if (n >= PATH_MAX) {
1400 				progerr(ERR_CREATE_PATH_2,
1401 					saveSpoolInstallDir,
1402 					extlist[idx]->map_path);
1403 				quit(99);
1404 			}
1405 
1406 			/* copy, preserve source file mode */
1407 
1408 			if (cppath(MODE_SRC, ept->path, scrpt_dst, 0644)) {
1409 				warnflag++;
1410 			}
1411 		}
1412 
1413 		/*
1414 		 * Now insert this potentially changed package database
1415 		 * entry.
1416 		 */
1417 		if (entry.cf_ent.npkgs) {
1418 			if (putcvfpfile(&(entry.cf_ent), *a_cfTmpVfp)) {
1419 				quit(99);
1420 			}
1421 		}
1422 	}
1423 
1424 	n = swapcfile(pkgserver, a_cfTmpVfp, pkginst, dbchg);
1425 	if (n == RESULT_WRN) {
1426 		warnflag++;
1427 	} else if (n == RESULT_ERR) {
1428 		quit(99);
1429 	}
1430 }
1431 
1432 /*
1433  * This function goes through and fixes all the attributes. This is called
1434  * out by using DST_QKVERIFY=this_class in the pkginfo file. The primary
1435  * use for this is to fix up files installed by a class action script
1436  * which is time-critical and reliable enough to assume likely success.
1437  * The first such format was for WOS compressed-cpio'd file sets.
1438  * The second format is the Class Archive Format.
1439  */
1440 static int
1441 fix_attributes(struct cfextra **extlist, int idx)
1442 {
1443 	struct	cfextra *ext;
1444 	int	i, retval = 1;
1445 	int 	nc = cl_getn();
1446 	int	n;
1447 	struct cfent *ept;
1448 	struct mergstat *mstat;
1449 	char scrpt_dst[PATH_MAX];
1450 	char *pspool_loc;
1451 	char *relocpath = (char *)NULL;
1452 
1453 	for (i = 0; extlist[i]; i++) {
1454 		ext = extlist[i];
1455 		ept = &(extlist[i]->cf_ent);
1456 		mstat = &(extlist[i]->mstat);
1457 
1458 		/*
1459 		 * We don't care about 'i'nfo files because, they
1460 		 * aren't laid down, 'e'ditable files can change
1461 		 * anyway, so who cares and 's'ymlinks were already
1462 		 * fixed in domerg(); however, certain old WOS
1463 		 * package symlinks depend on a bug in the old
1464 		 * pkgadd which has recently been expunged. For
1465 		 * those packages in 2.2, we repeat the verification
1466 		 * of symlinks.
1467 		 *
1468 		 * By 2.6 or so, ftype == 's' should be added to this.
1469 		 */
1470 		if (ept->ftype == 'i' || ept->ftype == 'e' ||
1471 			(mstat->shared && nocnflct))
1472 			continue;
1473 
1474 		if (mstat->denied) {
1475 			progerr(ERR_COULD_NOT_INSTALL, ept->path);
1476 			warnflag++;
1477 			continue;
1478 		}
1479 
1480 		if (ept->pkg_class_idx < 0 || ept->pkg_class_idx > nc) {
1481 			progerr(ERR_CLIDX, ept->pkg_class_idx,
1482 			    (ept->path && *ept->path) ? ept->path : "unknown");
1483 			continue;
1484 		}
1485 
1486 		/* If this is the right class, do the fast verify. */
1487 		if (ept->pkg_class_idx == idx) {
1488 			if (fverify(1, &ept->ftype, ept->path,
1489 				&ept->ainfo, &ept->cinfo) == 0) {
1490 				mstat->attrchg = 0;
1491 				mstat->contchg =  0;
1492 			} else	/* We'll try full verify later */
1493 				retval = 0;
1494 		}
1495 		/*
1496 		 * Need to copy the installed volitale file back to the
1497 		 * partial spooled area if we are installing to a local zone
1498 		 * or similar installation method.
1499 		 */
1500 
1501 		if ((!is_partial_inst()) &&
1502 			((ept->ftype == 'e') || (ept->ftype == 'v')) &&
1503 			(strcmp(ept->pkg_class, "none") == 0)) {
1504 
1505 			if (absolutepath(ext->map_path) == B_TRUE &&
1506 				parametricpath(ext->cf_ent.ainfo.local,
1507 					&relocpath) == B_FALSE) {
1508 				pspool_loc = ROOT;
1509 			} else {
1510 				pspool_loc = RELOC;
1511 			}
1512 
1513 			n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s",
1514 				saveSpoolInstallDir, pspool_loc,
1515 				relocpath ? relocpath : ext->map_path);
1516 
1517 			if (n >= PATH_MAX) {
1518 				progerr(ERR_CREATE_PATH_2,
1519 					saveSpoolInstallDir,
1520 					ext->map_path);
1521 				quit(99);
1522 			}
1523 
1524 			/* copy, preserve source file mode */
1525 
1526 			if (cppath(MODE_SRC, ept->path, scrpt_dst, 0644)) {
1527 				warnflag++;
1528 			}
1529 		}
1530 	}
1531 
1532 	return (retval);
1533 }
1534 
1535 /*
1536  * Check to see if first charcter in path is a '/'.
1537  *
1538  * Return:
1539  * 			B_TRUE - if path is prepended with '/'
1540  * 			B_FALSE - if not
1541  */
1542 static boolean_t
1543 absolutepath(char *path)
1544 {
1545 	assert(path != NULL);
1546 	assert(path[0] != '\0');
1547 
1548 	return (path[0] == '/' ? B_TRUE : B_FALSE);
1549 }
1550 
1551 /*
1552  * Check to see if path contains a '$' which makes it
1553  * a parametric path and therefore relocatable.
1554  *
1555  * Parameters:
1556  *             path - The path to determine if it is absolute
1557  *             relocpath - The value of the unconditioned path
1558  *                         i.e. $OPTDIR/usr/ls
1559  * Return:
1560  * 			B_TRUE - if path is a parametric path
1561  * 			B_FALSE - if not
1562  */
1563 static boolean_t
1564 parametricpath(char *path, char **relocpath)
1565 {
1566 	assert(path != NULL);
1567 	assert(path[0] != '\0');
1568 
1569 	/*
1570 	 * If this is a valid parametric path then a '$' MUST occur at the
1571 	 * first or second character.
1572 	 */
1573 
1574 	if (path[0] == '$' || path[1] == '$') {
1575 		/*
1576 		 * If a parametric path exists then when copying the
1577 		 * path to the pspool directoy from the installing
1578 		 * pkgs reloc directory we want to use the uncononditional
1579 		 * varaiable path.
1580 		 */
1581 		*relocpath = (path + 1);
1582 		return (B_TRUE);
1583 	}
1584 	return (B_FALSE);
1585 }
1586 
1587 void
1588 regfiles_free()
1589 {
1590 	if (regfiles_head != NULL) {
1591 		struct reg_files *rfp = regfiles_head->next;
1592 
1593 		while (rfp != NULL) {
1594 			free(regfiles_head);
1595 			regfiles_head = rfp;
1596 			rfp = regfiles_head->next;
1597 		}
1598 		free(regfiles_head);
1599 		regfiles_head = NULL;
1600 	}
1601 }
1602