instvol.c revision 12734:76969fc28795
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
77extern int		pkgverbose;
78extern fsblkcnt_t	pkgmap_blks; 		/* main.c */
79
80extern struct pkgdev pkgdev;
81
82extern char	tmpdir[];
83extern char	pkgbin[];
84extern char	instdir[];
85extern char	saveSpoolInstallDir[];
86extern char	*pkginst;
87
88extern int	dbchg;
89extern int	nosetuid;
90extern int	nocnflct;
91extern 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
100static int	eocflag;
101
102/*
103 * The variable below indicates that fix_attributes() will be inadequate
104 * because a replacement was permitted.
105 */
106static int	repl_permitted = 0;
107
108static int	domerg(struct cfextra **extlist, int part, int nparts,
109			int myclass, char **srcp, char **dstp,
110			char **r_updated);
111static void	endofclass(struct cfextra **extlist, int myclass,
112			int ckflag, PKGserver server, VFP_T **a_cfTmpVfp);
113static int	fix_attributes(struct cfextra **, int);
114static int	dir_is_populated(char *dirpath);
115static boolean_t absolutepath(char *path);
116static boolean_t parametricpath(char *path, char **relocpath);
117
118/* Used to keep track of the entries in extlist that are regular files. */
119struct reg_files {
120	struct reg_files *next;
121	int val;
122};
123static 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 */
136void
137instvol(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
837static int
838domerg(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 */
1172static int
1173dir_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 */
1204static void
1205endofclass(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 */
1437static int
1438fix_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 */
1539static boolean_t
1540absolutepath(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 */
1560static boolean_t
1561parametricpath(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
1584void
1585regfiles_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