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 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29
30 #include <stdio.h>
31 #include <sys/param.h>
32 #include <fcntl.h>
33 #include <stdlib.h>
34 #include <strings.h>
35 #include <errno.h>
36 #include <dirent.h>
37 #include <sys/stat.h>
38
39 #include "list.h"
40 #include "protodir.h"
41 #include "arch.h"
42 #include "exception_list.h"
43
44 #define FS " \t\n"
45
46 static char *
resolve_relative(const char * source,const char * link)47 resolve_relative(const char *source, const char *link)
48 {
49 char *p;
50 char *l_next;
51 char *l_pos;
52 static char curpath[MAXPATHLEN];
53
54 /* if absolute path - no relocation required */
55 if (link[0] == '/')
56 return (strcpy(curpath, link));
57
58 (void) strcpy(curpath, source);
59 p = rindex(curpath, '/');
60 *p = '\0';
61 l_pos = (char *)link;
62 do {
63 l_next = index(l_pos, '/');
64 if (strncmp(l_pos, "../", 3) == 0) {
65 if ((p = rindex(curpath, '/')) != NULL)
66 *p = '\0';
67 else
68 curpath[0] = '\0';
69 } else if (strncmp(l_pos, "./", 2)) {
70 /* if not . then we process */
71 if (curpath[0])
72 (void) strcat(curpath, "/");
73 if (l_next) {
74 (void) strncat(curpath, l_pos,
75 (l_next - l_pos));
76 } else
77 (void) strcat(curpath, l_pos);
78 }
79 l_pos = l_next + 1;
80 } while (l_next);
81
82 return (curpath);
83 }
84
85
86 static int
parse_proto_line(const char * basedir,char * line,elem_list * list,short arch,const char * pkgname)87 parse_proto_line(const char *basedir, char *line, elem_list *list, short arch,
88 const char *pkgname)
89 {
90 char *type, *class, *file, *src, *maj, *min, *perm, *owner, *group;
91 char p_line[BUFSIZ];
92 elem *dup;
93 static elem *e = NULL;
94
95 (void) strcpy(p_line, line);
96 if (!e)
97 e = (elem *)calloc(1, sizeof (elem));
98
99 e->flag = 0;
100
101 if (!(type = strtok(p_line, FS))) {
102 (void) fprintf(stderr, "error: bad line(type) : %s\n", line);
103 return (-1);
104 }
105
106 e->file_type = type[0];
107
108 if ((class = strtok(NULL, FS)) == NULL) {
109 (void) fprintf(stderr, "error: bad line(class) : %s\n", line);
110 return (-1);
111 }
112
113 /*
114 * Just ignore 'legacy' entries. These are not present in the proto
115 * area at all. They're phantoms.
116 */
117 if (strcmp(class, "legacy") == 0)
118 return (0);
119
120 if (!(file = strtok(NULL, FS))) {
121 (void) fprintf(stderr, "error: bad line(file_name) : %s\n",
122 line);
123 return (-1);
124 }
125
126 e->symsrc = NULL;
127 if ((src = index(file, '=')) != NULL) {
128 /*
129 * The '=' operator is subtly different for link and non-link
130 * entries. For the hard or soft link case, the left hand side
131 * exists in the proto area and is created by the package.
132 *
133 * When the file is an editable file, it's very likely that the
134 * right hand side is only a fragment of that file, which is
135 * delivered by multiple packages in the consolidation. Thus it
136 * can't exist in the proto area, and because we can't really
137 * know where the file's root directory is, we should skip the
138 * file.
139 *
140 * For all other filetypes, assume the right hand side is in the
141 * proto area.
142 */
143 if (e->file_type == SYM_LINK_T || e->file_type == LINK_T) {
144 *src++ = '\0';
145 e->symsrc = strdup(src);
146 } else if (e->file_type == EDIT_T) {
147 return (0);
148 } else {
149 file = src + 1;
150 }
151 }
152
153 /*
154 * if a basedir has a value, prepend it to the filename
155 */
156 if (basedir[0])
157 (void) strcat(strcat(strcpy(e->name, basedir), "/"), file);
158 else
159 (void) strcpy(e->name, file);
160
161 if (e->file_type != SYM_LINK_T) {
162 if ((e->file_type == CHAR_DEV_T) ||
163 (e->file_type == BLOCK_DEV_T)) {
164 if (!(maj = strtok(NULL, FS))) {
165 (void) fprintf(stderr,
166 "error: bad line(major number) : %s\n",
167 line);
168 return (-1);
169 }
170 e->major = atoi(maj);
171
172 if (!(min = strtok(NULL, FS))) {
173 (void) fprintf(stderr,
174 "error: bad line(minor number) : %s\n",
175 line);
176 return (-1);
177 }
178 e->minor = atoi(min);
179 } else {
180 e->major = -1;
181 e->minor = -1;
182 }
183
184 if (!(perm = strtok(NULL, FS))) {
185 (void) fprintf(stderr,
186 "error: bad line(permission) : %s\n", line);
187 return (-1);
188 }
189 e->perm = strtol(perm, NULL, 8);
190
191 if (!(owner = strtok(NULL, FS))) {
192 (void) fprintf(stderr,
193 "error: bad line(owner) : %s\n", line);
194 return (-1);
195 }
196 (void) strcpy(e->owner, owner);
197
198 if (!(group = strtok(NULL, FS))) {
199 (void) fprintf(stderr,
200 "error: bad line(group) : %s\n", line);
201 return (-1);
202 }
203 (void) strcpy(e->group, group);
204 }
205
206 e->inode = 0;
207 e->ref_cnt = 1;
208 e->arch = arch;
209 e->link_parent = NULL;
210
211 if (!(dup = find_elem(list, e, FOLLOW_LINK))) {
212 e->pkgs = add_pkg(NULL, pkgname); /* init pkgs list */
213 add_elem(list, e);
214 e = NULL;
215 return (1);
216 } else if (dup->file_type == DIR_T) {
217 if (!(dup->pkgs = add_pkg(dup->pkgs, pkgname))) {
218 /* add entry to pkgs */
219 (void) fprintf(stderr,
220 "warning: %s: Duplicate entry for %s\n",
221 pkgname, dup->name);
222 return (-1);
223 }
224 if (e->perm != dup->perm) {
225 (void) fprintf(stderr,
226 "warning: %s: permissions %#o of %s do not match "
227 "previous permissions %#o\n",
228 pkgname, e->perm, dup->name, dup->perm);
229 }
230 if (strcmp(e->owner, dup->owner) != 0) {
231 (void) fprintf(stderr,
232 "warning: %s: owner \"%s\" of %s does not match "
233 "previous owner \"%s\"\n",
234 pkgname, e->owner, dup->name, dup->owner);
235 }
236 if (strcmp(e->group, dup->group) != 0) {
237 (void) fprintf(stderr,
238 "warning: %s: group \"%s\" of %s does not match "
239 "previous group \"%s\"\n",
240 pkgname, e->group, dup->name, dup->group);
241 }
242 } else {
243 /*
244 * Signal an error only if this is something that's not on the
245 * exception list.
246 */
247 (void) strcpy(e->name, file);
248 if (find_elem(&exception_list, e, FOLLOW_LINK) == NULL) {
249 (void) fprintf(stderr,
250 "warning: %s: duplicate entry for %s - ignored\n",
251 pkgname, e->name);
252 return (-1);
253 }
254 }
255
256 return (0);
257 }
258
259 static int
parse_proto_link(const char * basedir,char * line,elem_list * list,short arch,const char * pkgname)260 parse_proto_link(const char *basedir, char *line, elem_list *list, short arch,
261 const char *pkgname)
262 {
263 char *type, *file, *src;
264 char p_line[BUFSIZ];
265 elem *p, *dup;
266 elem key;
267 static elem *e = NULL;
268
269
270 (void) strcpy(p_line, line);
271 if (!e)
272 e = (elem *)calloc(1, sizeof (elem));
273
274 e->flag = 0;
275 type = strtok(p_line, FS);
276 e->arch = arch;
277
278 e->file_type = type[0];
279 (void) strtok(NULL, FS); /* burn class */
280
281 file = strtok(NULL, FS);
282 if ((src = index(file, '=')) != NULL) {
283 *src++ = '\0';
284 e->symsrc = strdup(src);
285 } else {
286 (void) fprintf(stderr,
287 "error: %s: hard link does not have destination (%s)\n",
288 pkgname, file);
289 return (0);
290 }
291
292 /*
293 * if a basedir has a value, prepend it to the filename
294 */
295 if (basedir[0])
296 (void) strcat(strcat(strcpy(e->name, basedir), "/"), file);
297 else
298 (void) strcpy(e->name, file);
299
300 /*
301 * now we need to find the file that we link to - to do this
302 * we build a key.
303 */
304
305 src = resolve_relative(e->name, e->symsrc);
306 (void) strcpy(key.name, src);
307 key.arch = e->arch;
308 if ((p = find_elem(list, &key, NO_FOLLOW_LINK)) == NULL) {
309 (void) fprintf(stderr,
310 "error: %s: hardlink to non-existent file: %s=%s\n",
311 pkgname, e->name, e->symsrc);
312 return (0);
313 }
314 if ((p->file_type == SYM_LINK_T) || (p->file_type == LINK_T)) {
315 (void) fprintf(stderr,
316 "error: %s: hardlink must link to a file or directory "
317 "(not other links): %s=%s\n", pkgname, e->name, p->name);
318 return (0);
319 }
320 e->link_parent = p;
321 e->link_sib = p->link_sib;
322 p->link_sib = e;
323 p->ref_cnt++;
324 e->inode = p->inode;
325 e->perm = p->perm;
326 e->ref_cnt = p->ref_cnt;
327 e->major = p->major;
328 e->minor = p->minor;
329 (void) strcpy(e->owner, p->owner);
330 (void) strcpy(e->group, p->owner);
331
332 if (!(dup = find_elem(list, e, NO_FOLLOW_LINK))) {
333 e->pkgs = add_pkg(NULL, pkgname); /* init pkgs list */
334 e->link_sib = NULL;
335 add_elem(list, e);
336 e = NULL;
337 return (1);
338 } else {
339 /*
340 * Signal an error only if this is something that's not on the
341 * exception list.
342 */
343 (void) strcpy(e->name, file);
344 if (find_elem(&exception_list, e, FOLLOW_LINK) == NULL) {
345 (void) fprintf(stderr,
346 "warning: %s: duplicate entry for %s - ignored\n",
347 pkgname, e->name);
348 return (-1);
349 }
350 }
351
352 return (0);
353 }
354
355
356 /*
357 * open up the pkginfo file and find the ARCH= and the BASEDIR= macros.
358 * I will set the arch and basedir variables based on these fields.
359 */
360 static void
read_pkginfo(const char * protodir,short * arch,char * basedir)361 read_pkginfo(const char *protodir, short *arch, char *basedir)
362 {
363 char pkginfofile[MAXPATHLEN];
364 char architecture[MAXPATHLEN];
365 char buf[BUFSIZ];
366 FILE *pkginfo_fp;
367 int hits = 0;
368 int i;
369 int index;
370
371
372 architecture[0] = '\0';
373 basedir[0] = '\0';
374 *arch = P_ISA;
375
376 /*
377 * determine whether the pkginfo file is a pkginfo.tmpl or
378 * a pkginfo file
379 */
380 (void) strcat(strcat(strcpy(pkginfofile, protodir), "/"),
381 "pkginfo.tmpl");
382
383 if ((pkginfo_fp = fopen(pkginfofile, "r")) == NULL) {
384 (void) strcat(strcat(strcpy(pkginfofile, protodir), "/"),
385 "pkginfo");
386 if ((pkginfo_fp = fopen(pkginfofile, "r")) == NULL) {
387 perror(pkginfofile);
388 return;
389 }
390 }
391
392
393 while (fgets(buf, BUFSIZ, pkginfo_fp) && (hits != 3)) {
394 if (strncmp(buf, "ARCH=", 5) == 0) {
395 index = 0;
396 /*
397 * remove any '"' in the ARCH field.
398 */
399 for (i = 5; buf[i]; i++) {
400 if (buf[i] != '"')
401 architecture[index++] = buf[i];
402 }
403 /* -1 because above copy included '\n' */
404 architecture[index-1] = '\0';
405 hits += 1;
406 } else if (strncmp(buf, "BASEDIR=", 8) == 0) {
407 index = 0;
408 /*
409 * remove any '"' in the BASEDIR field, and
410 * strip off a leading '/' if present.
411 */
412 for (i = 8; buf[i]; i++) {
413 if (buf[i] != '"' &&
414 (buf[i] != '/' || index != 0)) {
415 buf[index++] = buf[i];
416 }
417 }
418 /* -1 because above copy included '\n' */
419 buf[index-1] = '\0';
420 (void) strcpy(basedir, &buf[0]);
421 hits += 2;
422 }
423 }
424 (void) fclose(pkginfo_fp);
425
426 if (architecture[0])
427 if ((*arch = assign_arch(architecture)) == NULL) {
428 (void) fprintf(stderr,
429 "warning: Unknown architecture %s found in %s\n",
430 architecture, pkginfofile);
431 }
432 }
433
434 /*
435 * The first pass through the prototype file goes through and reads
436 * in all the entries except 'hard links'. Those must be processed
437 * in a second pass.
438 *
439 * If any !includes are found in the prototype file this routine
440 * will be called recursively.
441 *
442 * Args:
443 * protofile - full pathname to prototype file to be processed.
444 * protodir - directory in which prototype file resides.
445 * list - list to which elements will be added
446 * arch - architecture of current prototype
447 * basedir - basedir for package
448 * pkgname - name of package
449 *
450 * Returns:
451 * returns number of items added to list.
452 *
453 */
454 static int
first_pass_prototype(const char * protofile,const char * protodir,elem_list * list,short arch,const char * basedir,const char * pkgname)455 first_pass_prototype(const char *protofile, const char *protodir,
456 elem_list *list, short arch, const char *basedir, const char *pkgname)
457 {
458 int elem_count = 0;
459 FILE *proto_fp;
460 char include_file[MAXPATHLEN];
461 char buf[BUFSIZ];
462
463 if ((proto_fp = fopen(protofile, "r")) == NULL) {
464 perror(protofile);
465 return (0);
466 }
467
468 /*
469 * first pass through file - process everything but
470 * hard links.
471 */
472 while (fgets(buf, BUFSIZ, proto_fp)) {
473 int rc;
474
475 switch (buf[0]) {
476 case FILE_T:
477 case EDIT_T:
478 case VOLATILE_T:
479 case DIR_T:
480 case SYM_LINK_T:
481 case CHAR_DEV_T:
482 case BLOCK_DEV_T:
483 if ((rc = parse_proto_line(basedir, buf, list, arch,
484 pkgname)) >= 0) {
485 elem_count += rc;
486 } else {
487 (void) fprintf(stderr,
488 "error: Errors found in %s\n", protofile);
489 }
490 break;
491 case LINK_T:
492 case 'i':
493 case '#':
494 case '\n':
495 break;
496 case '!':
497 /* Is this an include statement - if so process */
498 if (strncmp(buf, "!include", 8) == 0) {
499 char *inc_file = (char *)(buf + 9);
500
501 /* burn white space */
502 while ((*inc_file == ' ') ||
503 (*inc_file == '\t'))
504 inc_file++;
505 if (*inc_file) {
506 /* remove trailing \n */
507 inc_file[strlen(inc_file) - 1] = '\0';
508 (void) strcat(strcat(strcpy(
509 include_file, protodir), "/"),
510 inc_file);
511 elem_count +=
512 first_pass_prototype(include_file,
513 protodir, list, arch, basedir,
514 pkgname);
515 } else {
516 (void) fprintf(stderr,
517 "warning: bad !include statement "
518 "in prototype %s : %s\n",
519 protofile, buf);
520 }
521 } else {
522 (void) fprintf(stderr,
523 "warning: unexpected ! notation in "
524 "prototype %s : %s\n", protofile, buf);
525
526 }
527 break;
528 default:
529 (void) fprintf(stderr,
530 "warning: unexpected line in prototype %s : %s\n",
531 protofile, buf);
532 break;
533 }
534 }
535
536 (void) fclose(proto_fp);
537
538 return (elem_count);
539 }
540
541 /*
542 * The second pass through the prototype file goes through and reads
543 * and processes only the 'hard links' in the prototype file. These
544 * are resolved and added accordingly to the elements list(list).
545 *
546 * If any !includes are found in the prototype file this routine
547 * will be called recursively.
548 *
549 * Args:
550 * protofile - full pathname to prototype file to be processed.
551 * protodir - directory in which prototype file resides.
552 * list - list to which elements will be added
553 * arch - architecture of current prototype
554 * basedir - basedir for package
555 * pkgname - package name
556 *
557 * Returns:
558 * returns number of items added to list.
559 *
560 */
561 static int
second_pass_prototype(const char * protofile,const char * protodir,elem_list * list,short arch,const char * basedir,const char * pkgname)562 second_pass_prototype(const char *protofile, const char *protodir,
563 elem_list *list, short arch, const char *basedir, const char *pkgname)
564 {
565 FILE *proto_fp;
566 int elem_count = 0;
567 char include_file[MAXPATHLEN];
568 char buf[BUFSIZ];
569
570 if ((proto_fp = fopen(protofile, "r")) == NULL) {
571 perror(protofile);
572 return (0);
573 }
574
575 /*
576 * second pass through prototype file - process the hard links
577 * now.
578 */
579 while (fgets(buf, BUFSIZ, proto_fp))
580 if (buf[0] == LINK_T) {
581 int rc;
582
583 if ((rc = parse_proto_link(basedir, buf, list, arch,
584 pkgname)) >= 0) {
585 elem_count += rc;
586 } else {
587 (void) fprintf(stderr,
588 "error: Errors found in %s\n", protofile);
589 }
590 } else if (strncmp(buf, "!include", 8) == 0) {
591 /*
592 * This is a file to include
593 */
594 char *inc_file = (char *)(buf + 9);
595
596 /* burn white space */
597 while ((*inc_file == ' ') || (*inc_file == '\t'))
598 inc_file++;
599
600 if (*inc_file) {
601 /* remove trailing \n */
602 inc_file[strlen(inc_file) - 1] = '\0';
603 /* build up include file name to be opened. */
604 (void) strcat(strcat(strcpy(include_file,
605 protodir), "/"), inc_file);
606 /*
607 * recursively call this routine to process the
608 * !include file.
609 */
610 elem_count +=
611 second_pass_prototype(include_file,
612 protodir, list, arch, basedir, pkgname);
613 } else {
614 (void) fprintf(stderr,
615 "warning: Bad !include statement in "
616 "prototype %s : %s\n", protofile, buf);
617 }
618 }
619
620 (void) fclose(proto_fp);
621
622 return (elem_count);
623 }
624
625 /*
626 * Args:
627 * pkgname - name of package being processed
628 * protodir - pathname to package defs directory
629 * list - List of elements read in, elements are added to this
630 * as they are read in.
631 * verbose - verbose output
632 *
633 * Returns:
634 * number of elements added to list
635 */
636 int
process_package_dir(const char * pkgname,const char * protodir,elem_list * list,int verbose)637 process_package_dir(const char *pkgname, const char *protodir,
638 elem_list *list, int verbose)
639 {
640 struct stat st_buf;
641 char protofile[MAXPATHLEN];
642 char basedir[MAXPATHLEN];
643 short arch;
644 int count = 0;
645
646
647 /*
648 * skip any packages we've already handled (because of
649 * dependencies)
650 */
651 if (processed_package(pkgname)) {
652 return (0);
653 }
654
655 /*
656 * find the prototype file. Legal forms of the name are:
657 * prototype
658 * prototype_<mach> (where mach == (sparc || i386 || ppc)
659 */
660 (void) strcat(strcat(strcpy(protofile, protodir), "/"), "prototype");
661 if (stat(protofile, &st_buf) < 0) {
662 if (errno == ENOENT) {
663 (void) strcat(strcat(strcat(strcpy(protofile,
664 protodir), "/"), "prototype"), PROTO_EXT);
665 if (stat(protofile, &st_buf) < 0) {
666 if (errno == ENOENT) {
667 if (verbose) {
668 (void) fprintf(stderr,
669 "warning: no prototype "
670 "file found in %s, "
671 "skipping...\n",
672 protodir);
673 }
674 } else
675 perror(protofile);
676 return (0);
677 }
678 } else {
679 perror(protofile);
680 return (0);
681 }
682 }
683
684 mark_processed(pkgname);
685
686 read_pkginfo(protodir, &arch, basedir);
687
688 count += first_pass_prototype(protofile, protodir, list, arch,
689 basedir, pkgname);
690 count += second_pass_prototype(protofile, protodir, list, arch,
691 basedir, pkgname);
692
693 /* print_list(list); */
694 return (count);
695 }
696
697 int
read_in_protodir(const char * dir_name,elem_list * list,int verbose)698 read_in_protodir(const char *dir_name, elem_list *list, int verbose)
699 {
700 DIR *p_dir;
701 struct dirent *dp;
702 char protodir[MAXPATHLEN];
703 struct stat st_buf;
704 int count = 0;
705
706 if ((p_dir = opendir(dir_name)) == NULL) {
707 perror(dir_name);
708 exit(1);
709 }
710
711 list->type = PROTODIR_LIST;
712
713 while ((dp = readdir(p_dir)) != NULL) {
714 /*
715 * let's not check "." and ".." - I don't really like this
716 * but I wasn't really sure you could be sure that they
717 * are always the first two entries in the directory
718 * structure - so I put it in the loop.
719 *
720 * Also - we skip all directories that are names .del-*.
721 * and any SCCS directories too.
722 */
723 if ((strcmp(dp->d_name, ".") == 0) ||
724 (strcmp(dp->d_name, "..") == 0) ||
725 (strncmp(dp->d_name, ".del-", 5) == 0) ||
726 (strcmp(dp->d_name, "SCCS") == 0))
727 continue;
728
729 (void) strcat(strcat(strcpy(protodir, dir_name), "/"),
730 dp->d_name);
731 if (stat(protodir, &st_buf) < 0) {
732 perror(protodir);
733 continue;
734 }
735 if (!S_ISDIR(st_buf.st_mode)) {
736 if (verbose) {
737 (void) fprintf(stderr,
738 "warning: %s not a directory\n", protodir);
739 }
740 continue;
741 }
742
743 count += process_dependencies(dp->d_name, dir_name, list,
744 verbose);
745
746 count += process_package_dir(dp->d_name, protodir, list,
747 verbose);
748 }
749
750 if (verbose)
751 (void) printf("read in %d lines\n", count);
752
753 (void) closedir(p_dir);
754
755 return (count);
756 }
757