Home | History | Annotate | Download | only in protocmp
      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, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  *
     26  * This utility takes a list of arguments specifying directories with
     27  * packages, and a cpio archive on standard input.  Standard output
     28  * will have a modified version of the cpio archive with mode, uid,
     29  * and gid fixed according to the packaging.  Standard error will list
     30  * the files that don't have corresponding packaging information.
     31  *
     32  * This utility supports "ASC" (cpio -c; "070701") type archives only.
     33  * This is what the mkbfu utility uses.
     34  *
     35  * It assumes that the local system architecture and the proto area
     36  * architecture are the same.  The wrong packages will be used if this
     37  * is not true.
     38  */
     39 
     40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     41 
     42 #include <stdio.h>
     43 #include <stdlib.h>
     44 #include <sys/types.h>
     45 #include <sys/stat.h>
     46 #include <archives.h>
     47 #include <string.h>
     48 
     49 #include "list.h"
     50 #include "protodir.h"
     51 #include "proto_list.h"
     52 #include "exception_list.h"
     53 #include "stdusers.h"
     54 
     55 static const char *myname;
     56 
     57 static elem_list list;
     58 
     59 #define	MAX_EXCEPT_LIST	5
     60 static const char *except_file[MAX_EXCEPT_LIST];
     61 static int except_count;
     62 
     63 static void
     64 usage(void)
     65 {
     66 	(void) fprintf(stderr, "usage: %s [-v] [-e except] pkgdir/list...\n",
     67 	    myname);
     68 	exit(1);
     69 }
     70 
     71 int
     72 main(int argc, char **argv)
     73 {
     74 	char buffer[65536];
     75 	elem elt;
     76 	elem *ep;
     77 	int hsize;
     78 	ulong_t mode, filesize, namesize;
     79 	size_t nsize;
     80 	int uid, gid;
     81 	char extra;
     82 	struct stat sb;
     83 	int chr;
     84 	boolean_t trailer_copy;
     85 	int errflg = 0;
     86 	int verbose = 0;
     87 	size_t inoffset;
     88 	boolean_t no_unknowns;
     89 
     90 	if ((myname = *argv) == NULL)
     91 		myname = "cpiotranslate";
     92 
     93 	while (errflg == 0 && (chr = getopt(argc, argv, "ve:")) != EOF) {
     94 		switch (chr) {
     95 		case 'v':
     96 			verbose++;
     97 			break;
     98 		case 'e':
     99 			if (except_count >= MAX_EXCEPT_LIST)
    100 				errflg++;
    101 			else
    102 				except_file[except_count++] = optarg;
    103 			break;
    104 		default:
    105 			errflg++;
    106 			break;
    107 		}
    108 	}
    109 
    110 	if (errflg != 0)
    111 		usage();
    112 
    113 	/* Allows selection of previous BFU behavior */
    114 	no_unknowns = getenv("CPIOTRANSLATE_ALL") != NULL;
    115 
    116 	init_list(&exception_list, HASH_SIZE);
    117 	while (--except_count >= 0) {
    118 		(void) read_in_exceptions(except_file[except_count], verbose);
    119 	}
    120 
    121 	/* Read in all the packaging information */
    122 	init_list(&list, HASH_SIZE);
    123 	while (optind < argc) {
    124 		if (stat(argv[optind], &sb) == -1) {
    125 			perror(argv[optind]);
    126 		} else if (S_ISDIR(sb.st_mode)) {
    127 			(void) read_in_protodir(argv[optind], &list, verbose);
    128 		} else if (S_ISREG(sb.st_mode)) {
    129 			(void) read_in_protolist(argv[optind], &list, verbose);
    130 		} else {
    131 			(void) fprintf(stderr, "%s: %s: bad type of object\n",
    132 			    myname, argv[optind]);
    133 			return (1);
    134 		}
    135 
    136 		optind++;
    137 	}
    138 
    139 	/* Process the cpio stream, one file at a time. */
    140 	inoffset = 0;
    141 	for (;;) {
    142 		/* Read the next cpio header */
    143 		hsize = fread(buffer, 1, ASCSZ, stdin);
    144 		if (hsize == 0 || (hsize == -1 && feof(stdin))) {
    145 			return (0);
    146 		}
    147 		if (hsize == -1) {
    148 			perror("cpio input");
    149 			break;
    150 		}
    151 		inoffset += hsize;
    152 		if (hsize != ASCSZ) {
    153 			(void) fprintf(stderr,
    154 			    "%s: bad cpio header; only %d bytes\n",
    155 			    myname, hsize);
    156 			break;
    157 		}
    158 
    159 		/* Get the data we care about: mode and name sizes */
    160 		if (sscanf(buffer+14, "%8lx%*32s%8lx%*32s%8lx", &mode,
    161 		    &filesize, &namesize) != 3) {
    162 			(void) fprintf(stderr,
    163 			    "%s: bad cpio header; cannot read file size\n",
    164 			    myname);
    165 			if (verbose != 0)
    166 				(void) fprintf(stderr, "Header: '%.*s'\n",
    167 				    hsize, buffer);
    168 			break;
    169 		}
    170 
    171 		/* Read in file name; account for padding */
    172 		nsize = ASCSZ + namesize;
    173 		if (namesize <= 1 || nsize >= sizeof (buffer)) {
    174 			(void) fprintf(stderr,
    175 			    "%s: bad cpio header; file name size %lu\n",
    176 			    myname, namesize);
    177 			break;
    178 		}
    179 		if ((nsize & 3) != 0)
    180 			nsize += 4 - (nsize & 3);
    181 		hsize = fread(buffer + ASCSZ, 1, nsize - ASCSZ, stdin);
    182 		if (hsize == -1) {
    183 			if (feof(stdin)) {
    184 				(void) fprintf(stderr,
    185 				    "%s: missing file name\n", myname);
    186 			} else {
    187 				perror("cpio input");
    188 			}
    189 			break;
    190 		}
    191 		inoffset += hsize;
    192 		if (hsize != nsize - ASCSZ) {
    193 			(void) fprintf(stderr, "%s: truncated file name\n",
    194 			    myname);
    195 			break;
    196 		}
    197 		buffer[nsize] = '\0';
    198 
    199 #ifdef DEBUG
    200 		if (verbose) {
    201 			(void) fprintf(stderr,
    202 			    "'%s' at offset %d: nlen %lu flen %lu\n",
    203 			    buffer + ASCSZ, inoffset - nsize, namesize,
    204 			    filesize);
    205 		}
    206 #endif
    207 
    208 		/* Locate file name in packaging information database */
    209 		(void) strlcpy(elt.name, buffer + ASCSZ, sizeof (elt.name));
    210 		if (nsize == ASCSZ + 14 && filesize == 0 &&
    211 		    strcmp(elt.name, "TRAILER!!!") == 0) {
    212 			trailer_copy = B_TRUE;
    213 			goto skip_update;
    214 		}
    215 		trailer_copy = B_FALSE;
    216 		elt.arch = P_ISA;
    217 		ep = find_elem(&list, &elt, FOLLOW_LINK);
    218 		if (ep == NULL) {
    219 			ep = find_elem_mach(&list, &elt, FOLLOW_LINK);
    220 		}
    221 
    222 		if (ep == NULL) {
    223 			/*
    224 			 * If it's on the exception list, remove it
    225 			 * from the archive.  It's not part of the
    226 			 * system.
    227 			 */
    228 			ep = find_elem(&exception_list, &elt, FOLLOW_LINK);
    229 			if (ep != NULL) {
    230 				if (verbose) {
    231 					(void) fprintf(stderr,
    232 					    "%s: %s: removed; exception list\n",
    233 					    myname, elt.name);
    234 				}
    235 				/*
    236 				 * Cannot use fseek here because input
    237 				 * is usually a pipeline.
    238 				 */
    239 				if (filesize & 3)
    240 					filesize += 4 - (filesize & 3);
    241 				while (filesize > 0) {
    242 					nsize = filesize;
    243 					if (nsize > sizeof (buffer))
    244 						nsize = sizeof (buffer);
    245 					hsize = fread(buffer, 1, nsize, stdin);
    246 					if (hsize == -1 && ferror(stdin)) {
    247 						perror("cpio read");
    248 						goto failure;
    249 					}
    250 					if (hsize != -1)
    251 						inoffset += hsize;
    252 					if (hsize != nsize) {
    253 						(void) fprintf(stderr,
    254 						    "%s: cpio file truncated\n",
    255 						    myname);
    256 						goto failure;
    257 					}
    258 					filesize -= hsize;
    259 				}
    260 				continue;
    261 			}
    262 		}
    263 
    264 		/*
    265 		 * No mode, user, group on symlinks in the packaging
    266 		 * information.  Leave mode alone and set user and
    267 		 * group to 'root' (0).  This is what a netinstall
    268 		 * would do.
    269 		 */
    270 		if (ep == NULL) {
    271 			uid = 0;
    272 			gid = 3;
    273 
    274 			if (!no_unknowns) {
    275 				(void) fprintf(stderr,
    276 				    "%s: %s: no packaging info\n", myname,
    277 				    elt.name);
    278 				goto skip_update;
    279 			}
    280 		} else if (ep->file_type == SYM_LINK_T) {
    281 			uid = gid = 0;
    282 		} else {
    283 			mode = (mode & S_IFMT) | (ep->perm & ~S_IFMT);
    284 			if ((uid = stdfind(ep->owner, usernames)) == -1) {
    285 				(void) fprintf(stderr,
    286 				    "%s: %s: user '%s' unknown\n", myname,
    287 				    elt.name, ep->owner);
    288 				uid = 0;
    289 			}
    290 			if ((gid = stdfind(ep->group, groupnames)) == -1) {
    291 				(void) fprintf(stderr,
    292 				    "%s: %s: group '%s' unknown\n", myname,
    293 				    elt.name, ep->group);
    294 				gid = 3;
    295 			}
    296 		}
    297 		/* save character overwritten by sprintf's NUL terminator. */
    298 		extra = buffer[38];
    299 		/* snprintf not needed; cannot possibly overflow */
    300 		(void) sprintf(buffer + 14, "%08lx%08x%08x", mode, uid, gid);
    301 		/* recover char overwritten with NUL by sprintf above. */
    302 		buffer[38] = extra;
    303 
    304 		/* Write out the updated header information */
    305 	skip_update:
    306 		hsize = fwrite(buffer, 1, nsize, stdout);
    307 		if (hsize == -1) {
    308 			perror("cpio output");
    309 			break;
    310 		}
    311 		if (hsize != nsize) {
    312 			(void) fprintf(stderr, "%s: cpio output disk full\n",
    313 			    myname);
    314 			break;
    315 		}
    316 
    317 		if (trailer_copy) {
    318 			while ((chr = getchar()) != EOF && chr != '0')
    319 				(void) putchar(chr);
    320 			if (chr == '0')
    321 				(void) ungetc(chr, stdin);
    322 			continue;
    323 		}
    324 
    325 		/* Copy the file data */
    326 		while (filesize > 0) {
    327 			if ((nsize = filesize) > sizeof (buffer))
    328 				nsize = sizeof (buffer);
    329 			if (nsize & 3)
    330 				nsize += 4 - (nsize & 3);
    331 			hsize = fread(buffer, 1, nsize, stdin);
    332 			if (hsize == -1 && ferror(stdin)) {
    333 				perror("cpio read");
    334 				goto failure;
    335 			}
    336 			if (hsize != -1)
    337 				inoffset += hsize;
    338 			if (hsize != nsize) {
    339 				(void) fprintf(stderr,
    340 				    "%s: cpio file truncated\n",
    341 				    myname);
    342 				goto failure;
    343 			}
    344 			hsize = fwrite(buffer, 1, nsize, stdout);
    345 			if (hsize == -1) {
    346 				perror("cpio output");
    347 				goto failure;
    348 			}
    349 			if (hsize != nsize) {
    350 				(void) fprintf(stderr,
    351 				    "%s: cpio output disk full\n", myname);
    352 				goto failure;
    353 			}
    354 			if (hsize > filesize)
    355 				break;
    356 			filesize -= hsize;
    357 		}
    358 	}
    359 
    360 failure:
    361 	if (verbose != 0) {
    362 		(void) fprintf(stderr, "%s: stopped at offset %u\n",
    363 		    myname, inoffset);
    364 	}
    365 
    366 	return (1);
    367 }
    368