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