/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * This utility takes a list of arguments specifying directories with * packages, and a cpio archive on standard input. Standard output * will have a modified version of the cpio archive with mode, uid, * and gid fixed according to the packaging. Standard error will list * the files that don't have corresponding packaging information. * * This utility supports "ASC" (cpio -c; "070701") type archives only. * This is what the mkbfu utility uses. * * It assumes that the local system architecture and the proto area * architecture are the same. The wrong packages will be used if this * is not true. */ #pragma ident "%Z%%M% %I% %E% SMI" #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <archives.h> #include <string.h> #include "list.h" #include "protodir.h" #include "proto_list.h" #include "exception_list.h" #include "stdusers.h" static const char *myname; static elem_list list; #define MAX_EXCEPT_LIST 5 static const char *except_file[MAX_EXCEPT_LIST]; static int except_count; static void usage(void) { (void) fprintf(stderr, "usage: %s [-v] [-e except] pkgdir/list...\n", myname); exit(1); } int main(int argc, char **argv) { char buffer[65536]; elem elt; elem *ep; int hsize; ulong_t mode, filesize, namesize; size_t nsize; int uid, gid; char extra; struct stat sb; int chr; boolean_t trailer_copy; int errflg = 0; int verbose = 0; size_t inoffset; boolean_t no_unknowns; if ((myname = *argv) == NULL) myname = "cpiotranslate"; while (errflg == 0 && (chr = getopt(argc, argv, "ve:")) != EOF) { switch (chr) { case 'v': verbose++; break; case 'e': if (except_count >= MAX_EXCEPT_LIST) errflg++; else except_file[except_count++] = optarg; break; default: errflg++; break; } } if (errflg != 0) usage(); /* Allows selection of previous BFU behavior */ no_unknowns = getenv("CPIOTRANSLATE_ALL") != NULL; init_list(&exception_list, HASH_SIZE); while (--except_count >= 0) { (void) read_in_exceptions(except_file[except_count], verbose); } /* Read in all the packaging information */ init_list(&list, HASH_SIZE); while (optind < argc) { if (stat(argv[optind], &sb) == -1) { perror(argv[optind]); } else if (S_ISDIR(sb.st_mode)) { (void) read_in_protodir(argv[optind], &list, verbose); } else if (S_ISREG(sb.st_mode)) { (void) read_in_protolist(argv[optind], &list, verbose); } else { (void) fprintf(stderr, "%s: %s: bad type of object\n", myname, argv[optind]); return (1); } optind++; } /* Process the cpio stream, one file at a time. */ inoffset = 0; for (;;) { /* Read the next cpio header */ hsize = fread(buffer, 1, ASCSZ, stdin); if (hsize == 0 || (hsize == -1 && feof(stdin))) { return (0); } if (hsize == -1) { perror("cpio input"); break; } inoffset += hsize; if (hsize != ASCSZ) { (void) fprintf(stderr, "%s: bad cpio header; only %d bytes\n", myname, hsize); break; } /* Get the data we care about: mode and name sizes */ if (sscanf(buffer+14, "%8lx%*32s%8lx%*32s%8lx", &mode, &filesize, &namesize) != 3) { (void) fprintf(stderr, "%s: bad cpio header; cannot read file size\n", myname); if (verbose != 0) (void) fprintf(stderr, "Header: '%.*s'\n", hsize, buffer); break; } /* Read in file name; account for padding */ nsize = ASCSZ + namesize; if (namesize <= 1 || nsize >= sizeof (buffer)) { (void) fprintf(stderr, "%s: bad cpio header; file name size %lu\n", myname, namesize); break; } if ((nsize & 3) != 0) nsize += 4 - (nsize & 3); hsize = fread(buffer + ASCSZ, 1, nsize - ASCSZ, stdin); if (hsize == -1) { if (feof(stdin)) { (void) fprintf(stderr, "%s: missing file name\n", myname); } else { perror("cpio input"); } break; } inoffset += hsize; if (hsize != nsize - ASCSZ) { (void) fprintf(stderr, "%s: truncated file name\n", myname); break; } buffer[nsize] = '\0'; #ifdef DEBUG if (verbose) { (void) fprintf(stderr, "'%s' at offset %d: nlen %lu flen %lu\n", buffer + ASCSZ, inoffset - nsize, namesize, filesize); } #endif /* Locate file name in packaging information database */ (void) strlcpy(elt.name, buffer + ASCSZ, sizeof (elt.name)); if (nsize == ASCSZ + 14 && filesize == 0 && strcmp(elt.name, "TRAILER!!!") == 0) { trailer_copy = B_TRUE; goto skip_update; } trailer_copy = B_FALSE; elt.arch = P_ISA; ep = find_elem(&list, &elt, FOLLOW_LINK); if (ep == NULL) { ep = find_elem_mach(&list, &elt, FOLLOW_LINK); } if (ep == NULL) { /* * If it's on the exception list, remove it * from the archive. It's not part of the * system. */ ep = find_elem(&exception_list, &elt, FOLLOW_LINK); if (ep != NULL) { if (verbose) { (void) fprintf(stderr, "%s: %s: removed; exception list\n", myname, elt.name); } /* * Cannot use fseek here because input * is usually a pipeline. */ if (filesize & 3) filesize += 4 - (filesize & 3); while (filesize > 0) { nsize = filesize; if (nsize > sizeof (buffer)) nsize = sizeof (buffer); hsize = fread(buffer, 1, nsize, stdin); if (hsize == -1 && ferror(stdin)) { perror("cpio read"); goto failure; } if (hsize != -1) inoffset += hsize; if (hsize != nsize) { (void) fprintf(stderr, "%s: cpio file truncated\n", myname); goto failure; } filesize -= hsize; } continue; } } /* * No mode, user, group on symlinks in the packaging * information. Leave mode alone and set user and * group to 'root' (0). This is what a netinstall * would do. */ if (ep == NULL) { uid = 0; gid = 3; if (!no_unknowns) { (void) fprintf(stderr, "%s: %s: no packaging info\n", myname, elt.name); goto skip_update; } } else if (ep->file_type == SYM_LINK_T) { uid = gid = 0; } else { mode = (mode & S_IFMT) | (ep->perm & ~S_IFMT); if ((uid = stdfind(ep->owner, usernames)) == -1) { (void) fprintf(stderr, "%s: %s: user '%s' unknown\n", myname, elt.name, ep->owner); uid = 0; } if ((gid = stdfind(ep->group, groupnames)) == -1) { (void) fprintf(stderr, "%s: %s: group '%s' unknown\n", myname, elt.name, ep->group); gid = 3; } } /* save character overwritten by sprintf's NUL terminator. */ extra = buffer[38]; /* snprintf not needed; cannot possibly overflow */ (void) sprintf(buffer + 14, "%08lx%08x%08x", mode, uid, gid); /* recover char overwritten with NUL by sprintf above. */ buffer[38] = extra; /* Write out the updated header information */ skip_update: hsize = fwrite(buffer, 1, nsize, stdout); if (hsize == -1) { perror("cpio output"); break; } if (hsize != nsize) { (void) fprintf(stderr, "%s: cpio output disk full\n", myname); break; } if (trailer_copy) { while ((chr = getchar()) != EOF && chr != '0') (void) putchar(chr); if (chr == '0') (void) ungetc(chr, stdin); continue; } /* Copy the file data */ while (filesize > 0) { if ((nsize = filesize) > sizeof (buffer)) nsize = sizeof (buffer); if (nsize & 3) nsize += 4 - (nsize & 3); hsize = fread(buffer, 1, nsize, stdin); if (hsize == -1 && ferror(stdin)) { perror("cpio read"); goto failure; } if (hsize != -1) inoffset += hsize; if (hsize != nsize) { (void) fprintf(stderr, "%s: cpio file truncated\n", myname); goto failure; } hsize = fwrite(buffer, 1, nsize, stdout); if (hsize == -1) { perror("cpio output"); goto failure; } if (hsize != nsize) { (void) fprintf(stderr, "%s: cpio output disk full\n", myname); goto failure; } if (hsize > filesize) break; filesize -= hsize; } } failure: if (verbose != 0) { (void) fprintf(stderr, "%s: stopped at offset %u\n", myname, inoffset); } return (1); }