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
instvol(struct cfextra ** extlist,char * srcinst,int part,int nparts,PKGserver pkgserver,VFP_T ** a_cfTmpVfp,char ** r_updated,char * a_zoneName)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
domerg(struct cfextra ** extlist,int part,int nparts,int myclass,char ** srcp,char ** dstp,char ** r_updated)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
dir_is_populated(char * dirpath)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
endofclass(struct cfextra ** extlist,int myclass,int ckflag,PKGserver pkgserver,VFP_T ** a_cfTmpVfp)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
fix_attributes(struct cfextra ** extlist,int idx)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
absolutepath(char * path)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
parametricpath(char * path,char ** relocpath)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
regfiles_free()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