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