diff options
author | H. Peter Anvin <hpa@zytor.com> | 2001-07-27 14:37:07 +0000 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2001-07-27 14:37:07 +0000 |
commit | ec1aabe7a65d739a76c6ed74ef1f109f57a09f65 (patch) | |
tree | 594c3e79332d1b02cbcac0ec97610ce57f5df22b | |
parent | 02fe6a77674886e53f61482c0203ce299608cce6 (diff) | |
download | zisofs-tools-ec1aabe7a65d739a76c6ed74ef1f109f57a09f65.tar.gz |
Modularize the source; add autoconf-based compilation; add long
command line options; clean up warnings.
-rw-r--r-- | MCONFIG.in | 37 | ||||
-rw-r--r-- | MRULES | 22 | ||||
-rw-r--r-- | Makefile | 40 | ||||
-rw-r--r-- | aclocal.m4 | 134 | ||||
-rw-r--r-- | compress.c | 140 | ||||
-rw-r--r-- | config.h.in | 27 | ||||
-rw-r--r-- | configure.in | 41 | ||||
-rw-r--r-- | hash.c | 68 | ||||
-rwxr-xr-x | install-sh | 238 | ||||
-rw-r--r-- | iso9660.c | 111 | ||||
-rw-r--r-- | iso9660.h | 46 | ||||
-rw-r--r-- | mkzftree.c | 796 | ||||
-rw-r--r-- | mkzftree.h | 77 | ||||
-rw-r--r-- | uncompress.c | 133 | ||||
-rw-r--r-- | util.c | 55 | ||||
-rw-r--r-- | walk.c | 246 | ||||
-rw-r--r-- | workers.c | 98 |
17 files changed, 1563 insertions, 746 deletions
diff --git a/MCONFIG.in b/MCONFIG.in new file mode 100644 index 0000000..2cdbbd7 --- /dev/null +++ b/MCONFIG.in @@ -0,0 +1,37 @@ +# Prefixes +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +# Directory for user binaries +BINDIR = @bindir@ + +# Man page tree +MANDIR = @mandir@ + +# System binaries +SBINDIR = @sbindir@ + +# Install into chroot area +INSTALLROOT = + +# Install program +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +# Compiler and compiler flags +CC = @CC@ +CFLAGS = @CFLAGS@ + +# Link flags +LDFLAGS = @LDFLAGS@ + +# Libraries +LIBS = @LIBS@ + +# Additional library we need to build +LIBOBJS = @LIBOBJS@ + +# ar and ranlib (for making libraries) +AR = ar cq +RANLIB = @RANLIB@ @@ -0,0 +1,22 @@ +# Standard compilation rules (don't use make builtins) + +.SUFFIXES: .c .cc .o .s .S .i + +.c.o: + $(CC) $(CFLAGS) -c $< + +.c.s: + $(CC) $(CFLAGS) -S -o $@ $< + +.c.i: + $(CC) $(CFLAGS) -E -o $@ $< + +.cc.o: + $(CXX) $(CXXFLAGS) -c $< + +.cc.s: + $(CXX) $(CXXFLAGS) -S -o $@ $< + +.cc.i: + $(CXX) $(CXXFLAGS) -E -o $@ $< + @@ -11,24 +11,46 @@ ## ----------------------------------------------------------------------- ## -## Simple Makefile for mkzftree +## Makefile for mkzftree ## ## mkzftree mirrors a tree in a form suitable for "mkisofs -z". ## -CC = gcc -CFLAGS = -O2 -LDFLAGS = -LIBS = -lz +-include MCONFIG +include MRULES + +OBJS = mkzftree.o walk.o workers.o util.o hash.o iso9660.o \ + compress.o uncompress.o all: mkzftree clean: - rm -f mkzftree + rm -f *.o *.i *.s mkzftree distclean: clean - rm -f core *~ \#* + rm -f MCONFIG config.status config.cache config.log config.h *~ \#* + rm -f core *.orig *.rej + +mkzftree: $(OBJS) + $(CC) $(LDFLAGS) -o mkzftree $(OBJS) $(LIBS) + +spotless: distclean + rm -f configure + +config: MCONFIG + +MCONFIG: configure MCONFIG.in config.h.in + ./configure + +config.h: MCONFIG + : Generated by side effect -mkzftree: mkzftree.c - $(CC) $(CFLAGS) $(LDFLAGS) -o mkzftree mkzftree.c $(LIBS) +configure: configure.in aclocal.m4 + autoconf + rm -f MCONFIG config.cache config.log config.status config.h +# +# Dependencies +# +mkzftree.o: mkzftree.c mkzftree.h config.h +workers.o: workers.c mkzftree.h config.h
\ No newline at end of file diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..495c0d0 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,134 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_CFLAGS() +dnl +dnl Attempt to add the given option to CFLAGS, if it doesn't break compilation +dnl -------------------------------------------------------------------------- +AC_DEFUN(PA_ADD_CFLAGS, +[AC_MSG_CHECKING([if $CC accepts $1]) + pa_add_cflags__old_cflags="$CFLAGS" + CFLAGS="$CFLAGS $1" + AC_TRY_COMPILE([#include <stdio.h>], + [printf("Hello, World!\n");], + AC_MSG_RESULT([yes]), + AC_MSG_RESULT([no]) + CFLAGS="$pa_add_cflags__old_cflags")]) + +dnl -------------------------------------------------------------------------- +dnl PA_SIGSETJMP +dnl +dnl Do we have sigsetjmp/siglongjmp? (AC_CHECK_FUNCS doesn't seem to work +dnl for these particular functions.) +dnl -------------------------------------------------------------------------- +AC_DEFUN(PA_SIGSETJMP, +[AC_MSG_CHECKING([for sigsetjmp]) + AC_TRY_LINK( + [#include <setjmp.h>], + [sigjmp_buf buf; + sigsetjmp(buf,1); + siglongjmp(buf,2);], + AC_MSG_RESULT([yes]) + $1, + AC_MSG_RESULT([no]) + $2)]) + +dnl -------------------------------------------------------------------------- +dnl PA_MSGHDR_MSG_CONTROL +dnl +dnl Does struct msghdr have the msg_control field? +dnl -------------------------------------------------------------------------- +AC_DEFUN(PA_MSGHDR_MSG_CONTROL, +[AC_MSG_CHECKING([for msg_control in struct msghdr]) + AC_TRY_COMPILE( +[ +#include <sys/types.h> +#include <sys/socket.h> +], +[ + struct msghdr msg; + void *p = (void *) &msg.msg_control; +], +[ + AC_DEFINE(HAVE_MSGHDR_MSG_CONTROL) + AC_MSG_RESULT([yes]) +], +[ + AC_MSG_RESULT([no]) +])]) + +dnl ------------------------------------------------------------------------ +dnl PA_STRUCT_IN_PKTINFO +dnl +dnl Look for definition of struct in_pktinfo. Some versions of glibc +dnl lack struct in_pktinfo; if so we need to include the definition +dnl ourselves -- but we only want to do that if absolutely necessary! +dnl ------------------------------------------------------------------------ +AC_DEFUN(PA_STRUCT_IN_PKTINFO, +[AC_MSG_CHECKING([for definition of struct in_pktinfo]) + AC_TRY_COMPILE( +[ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <sys/uio.h> +], +[ + struct in_pktinfo pktinfo; + int foo = sizeof(struct in_pktinfo); + void *quux = (void *)(&pktinfo.ipi_addr); +], +[ + AC_DEFINE(HAVE_STRUCT_IN_PKTINFO) + AC_MSG_RESULT([yes]) +], +[ + AC_MSG_RESULT([no]) +])]) + +dnl -------------------------------------------------------------------------- +dnl PA_HAVE_TCPWRAPPERS +dnl +dnl Do we have the tcpwrappers -lwrap? This can't be done using AC_CHECK_LIBS +dnl due to the need to provide "allow_severity" and "deny_severity" variables +dnl -------------------------------------------------------------------------- +AC_DEFUN(PA_HAVE_TCPWRAPPERS, +[AC_CHECK_LIB([wrap], [main]) + AC_MSG_CHECKING([for tcpwrappers]) + AC_TRY_LINK( +[ +#include <tcpd.h> +int allow_severity = 0; +int deny_severity = 0; +], +[ + hosts_ctl("sample_daemon", STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN); +], +[ + AC_DEFINE(HAVE_TCPWRAPPERS) + AC_MSG_RESULT([yes]) +], +[ + AC_MSG_RESULT([no]) +])]) + +dnl ------------------------------------------------------------------------ +dnl PA_WITH_BOOL +dnl +dnl PA_WITH_BOOL(option, default, help, enable, disable) +dnl +dnl Provides a more convenient way to specify --with-option and +dnl --without-option, with a default. default should be either 0 or 1. +dnl ------------------------------------------------------------------------ +AC_DEFUN(PA_WITH_BOOL, +[AC_ARG_WITH([$1], [$3], +if test ["$withval"] != no; then +[$4] +else +[$5] +fi, +if test [$2] -ne 0; then +[$4] +else +[$5] +fi)]) diff --git a/compress.c b/compress.c new file mode 100644 index 0000000..b0a238f --- /dev/null +++ b/compress.c @@ -0,0 +1,140 @@ +/* $Id$ */ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include <stdlib.h> +#include <stdio.h> +#include <utime.h> +#include <unistd.h> +#include <zlib.h> +#include "mkzftree.h" +#include "iso9660.h" + + +int block_compress_file(FILE *input, FILE *output, off_t size) +{ + struct compressed_file_header hdr; + char inbuf[CBLOCK_SIZE], outbuf[2*CBLOCK_SIZE]; + int bytes, pointer_bytes, nblocks, block; + uLong cbytes; /* uLong is a zlib datatype */ + char *pointer_block, *curptr; + unsigned long position; + int i; + int force_compress = force; + int zerr; + int err = EX_SOFTWARE; + + if ( (sizeof hdr) & 3 ) { + fputs("INTERNAL ERROR: header is not a multiple of 4\n", stderr); + abort(); + } + + memset(&hdr, 0, sizeof hdr); + memcpy(&hdr.magic, zisofs_magic, sizeof zisofs_magic); + hdr.header_size = (sizeof hdr) >> 2; + hdr.block_size = CBLOCK_SIZE_LG2; + set_731(&hdr.uncompressed_len, size); + + if ( fwrite(&hdr, sizeof hdr, 1, output) != 1 ) + return EX_CANTCREAT; + + nblocks = (size+CBLOCK_SIZE-1) >> CBLOCK_SIZE_LG2; + pointer_bytes = 4*(nblocks+1); + pointer_block = xmalloc(pointer_bytes); + memset(pointer_block, 0, pointer_bytes); + + if ( fseek(output, pointer_bytes, SEEK_CUR) == -1 ) { + err = EX_CANTCREAT; + goto free_ptr_bail; + } + + curptr = pointer_block; + position = sizeof hdr + pointer_bytes; + + block = 0; + while ( (bytes = fread(inbuf, 1, CBLOCK_SIZE, input)) > 0 ) { + if ( bytes < CBLOCK_SIZE && block < nblocks-1 ) { + err = EX_IOERR; + goto free_ptr_bail; + } + + /* HACK: If the file has our magic number, always compress */ + if ( block == 0 && bytes >= sizeof zisofs_magic ) { + if ( !memcmp(inbuf, zisofs_magic, sizeof zisofs_magic) ) + force_compress = 1; + } + + set_731(curptr, position); curptr += 4; + + /* We have two special cases: a zero-length block is defined as all zero, + and a block the length of which is equal to the block size is unencoded. */ + + for ( i = 0 ; i < CBLOCK_SIZE ; i++ ) { + if ( inbuf[i] ) break; + } + + if ( i == CBLOCK_SIZE ) { + /* All-zero block. No output */ + } else { + cbytes = 2*CBLOCK_SIZE; + if ( (zerr = compress2(outbuf, &cbytes, inbuf, bytes, level)) != Z_OK ) { + err = (zerr == Z_MEM_ERROR) ? EX_OSERR : EX_SOFTWARE; + goto free_ptr_bail; /* Compression failure */ + } + if ( fwrite(outbuf, 1, cbytes, output) != cbytes ) { + err = EX_CANTCREAT; + goto free_ptr_bail; + } + position += cbytes; + } + block++; + } + + /* Set pointer to the end of the final block */ + set_731(curptr, position); + + /* Now write the pointer table */ + if ( fseek(output, sizeof hdr, SEEK_SET) == -1 || + fwrite(pointer_block, 1, pointer_bytes, output) != pointer_bytes ) { + err = EX_CANTCREAT; + goto free_ptr_bail; + } + + free(pointer_block); + + /* Now make sure that this was actually the right thing to do */ + if ( !force_compress && position >= size ) { + /* Incompressible file, just copy it */ + rewind(input); + rewind(output); + + position = 0; + while ( (bytes = fread(inbuf, 1, CBLOCK_SIZE, input)) > 0 ) { + if ( fwrite(inbuf, 1, bytes, output) != bytes ) + return EX_CANTCREAT; + position += bytes; + } + + /* Truncate the file to the correct size */ + fflush(output); + ftruncate(fileno(output), position); + } + + /* If we get here, we're done! */ + return 0; + + /* Common bailout code */ + free_ptr_bail: + free(pointer_block); + return err; +} + diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..107e638 --- /dev/null +++ b/config.h.in @@ -0,0 +1,27 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ +/* $Id$ */ + +/* + * config.h.in + * + * Pattern file for configurations + */ + +#undef HAVE_SYSEXITS_H /* Define if <sysexits.h> exists */ +#undef HAVE_GETOPT_H /* Define if <getopt.h> exists */ +#undef HAVE_GETOPT_LONG /* Define if we have getopt_long() */ +#undef HAVE_LCHOWN /* Define if we have lchown() */ +#undef off_t /* Define to long if off_t missing */ +#undef size_t /* Define to unsigned long if size_t missing */ +#undef ssize_t /* Define to long if ssize_t missing */ + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..160ed54 --- /dev/null +++ b/configure.in @@ -0,0 +1,41 @@ +dnl +dnl autoconf input file to generate MCONFIG +dnl + +AC_INIT(MCONFIG.in) +AC_PREFIX_DEFAULT(/usr) + +AC_PROG_CC +AC_C_CONST +AC_C_INLINE + +PA_ADD_CFLAGS(-Wall) +PA_ADD_CFLAGS(-W) +PA_ADD_CFLAGS(-Wpointer-arith) +PA_ADD_CFLAGS(-Wbad-function-cast) +PA_ADD_CFLAGS(-Wcast-equal) +PA_ADD_CFLAGS(-Wstrict-prototypes) +PA_ADD_CFLAGS(-Wmissing-prototypes) +PA_ADD_CFLAGS(-Wmissing-declarations) +PA_ADD_CFLAGS(-Wnested-externs) +PA_ADD_CFLAGS(-Winline) +PA_ADD_CFLAGS(-Wcast-align) +PA_ADD_CFLAGS(-pipe) + +AC_CHECK_HEADERS(sysexits.h) +AC_CHECK_HEADERS(getopt.h) + +AC_CHECK_TYPE(off_t, signed long) +AC_CHECK_TYPE(size_t, unsigned long) +AC_CHECK_TYPE(ssize_t, signed long) + +AC_CHECK_FUNCS(lchown) + +AC_SEARCH_LIBS(compress2, z, , [AC_MSG_ERROR(zlib not found, cannot continue)]) +AC_SEARCH_LIBS(getopt_long, [getopt getopt_long], AC_DEFINE(HAVE_GETOPT_LONG)) + +AC_PROG_RANLIB +AC_PROG_INSTALL + +AC_CONFIG_HEADER(config.h) +AC_OUTPUT(MCONFIG) @@ -0,0 +1,68 @@ +#ident "$Id$" +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * hash.c + * + * Hash table used to find hard-linked files + */ + +#include <stdlib.h> +#include "mkzftree.h" + +#define HASH_BUCKETS 2683 + +struct file_hash { + struct file_hash *next; + struct stat st; + const char *outfile_name; +}; + +static struct file_hash *hashp[HASH_BUCKETS]; + +const char *hash_find_file(struct stat *st) +{ + int bucket = (st->st_ino + st->st_dev) % HASH_BUCKETS; + struct file_hash *hp; + + for ( hp = hashp[bucket] ; hp ; hp = hp->next ) { + if ( hp->st.st_ino == st->st_ino && + hp->st.st_dev == st->st_dev && + hp->st.st_mode == st->st_mode && + hp->st.st_nlink == st->st_nlink && + hp->st.st_uid == st->st_uid && + hp->st.st_gid == st->st_gid && + hp->st.st_size == st->st_size && + hp->st.st_mtime == st->st_mtime ) { + /* Good enough, it's the same file */ + return hp->outfile_name; + } + } + return NULL; /* No match */ +} + +/* Note: the stat structure is the input file; the name + is the output file to link to */ +void hash_insert_file(struct stat *st, const char *outfile) +{ + int bucket = (st->st_ino + st->st_dev) % HASH_BUCKETS; + struct file_hash *hp = xmalloc(sizeof(struct file_hash)); + + hp->next = hashp[bucket]; + memcpy(&hp->st, st, sizeof(struct stat)); + hp->outfile_name = xstrdup(outfile); + + hashp[bucket] = hp; +} + + diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..89fc9b0 --- /dev/null +++ b/install-sh @@ -0,0 +1,238 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +tranformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/iso9660.c b/iso9660.c new file mode 100644 index 0000000..3ed440b --- /dev/null +++ b/iso9660.c @@ -0,0 +1,111 @@ +/* $Id$ */ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include "iso9660.h" + +/* zisofs magic */ + +const unsigned char zisofs_magic[8] = { + 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07 +}; + +/* iso9660 integer formats */ + +void +set_721(void *pnt, unsigned int i) +{ + unsigned char *p = (unsigned char *)pnt; + p[0] = i & 0xff; + p[1] = (i >> 8) & 0xff; +} + +unsigned int +get_721(void *pnt) +{ + unsigned char *p = (unsigned char *)pnt; + return ((unsigned int)p[0]) + ((unsigned int)p[1] << 8); +} + +void +set_722(void *pnt, unsigned int i) +{ + unsigned char *p = (unsigned char *)pnt; + p[0] = (i >> 8) & 0xff; + p[1] = i & 0xff; +} + +unsigned int +get_722(void *pnt) +{ + unsigned char *p = (unsigned char *)pnt; + return ((unsigned int)p[0] << 8) + ((unsigned int)p[1]); +} + +void +set_723(void *pnt, unsigned int i) +{ + unsigned char *p = (unsigned char *)pnt; + p[3] = p[0] = i & 0xff; + p[2] = p[1] = (i >> 8) & 0xff; +} + +#define get_723(x) get_721(x) + +void +set_731(void *pnt, unsigned int i) +{ + unsigned char *p = (unsigned char *)pnt; + p[0] = i & 0xff; + p[1] = (i >> 8) & 0xff; + p[2] = (i >> 16) & 0xff; + p[3] = (i >> 24) & 0xff; +} + +unsigned int +get_731(void *pnt) +{ + unsigned char *p = (unsigned char *)pnt; + return ((unsigned int)p[0]) + ((unsigned int)p[1] << 8) + + ((unsigned int)p[2] << 16) + ((unsigned int)p[3] << 24); +} + +void +set_732(void *pnt, unsigned int i) +{ + unsigned char *p = (unsigned char *)pnt; + p[3] = i & 0xff; + p[2] = (i >> 8) & 0xff; + p[1] = (i >> 16) & 0xff; + p[0] = (i >> 24) & 0xff; +} + +unsigned int +get_732(void *pnt) +{ + unsigned char *p = (unsigned char *)pnt; + return ((unsigned int)p[0] << 24) + ((unsigned int)p[1] << 16) + + ((unsigned int)p[2] << 8) + ((unsigned int)p[3]); +} + +void +set_733(void *pnt, unsigned int i) +{ + unsigned char *p = (unsigned char *)pnt; + p[7] = p[0] = i & 0xff; + p[6] = p[1] = (i >> 8) & 0xff; + p[5] = p[2] = (i >> 16) & 0xff; + p[4] = p[3] = (i >> 24) & 0xff; +} + +#define get_733(x) get_731(x) + diff --git a/iso9660.h b/iso9660.h new file mode 100644 index 0000000..e85e7ac --- /dev/null +++ b/iso9660.h @@ -0,0 +1,46 @@ +#ident "$Id$" +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* zisofs definitions */ + +#ifndef CBLOCK_SIZE_LG2 +#define CBLOCK_SIZE_LG2 15 /* Compressed block size */ +#endif +#define CBLOCK_SIZE (1U << CBLOCK_SIZE_LG2) + +/* Compressed file magic */ +extern const unsigned char zisofs_magic[8]; + +/* VERY VERY VERY IMPORTANT: Must be a multiple of 4 bytes */ +struct compressed_file_header { + char magic[8]; + char uncompressed_len[4]; + unsigned char header_size; + unsigned char block_size; + char reserved[2]; /* Reserved for future use, MBZ */ +}; + +/* iso9660 integer formats */ +void set_721(void *, unsigned int); +unsigned int get_721(void *); +void set_722(void *, unsigned int); +unsigned int get_722(void *); +void set_723(void *, unsigned int); +#define get_723(x) get_721(x) +void set_731(void *pnt, unsigned int); +unsigned int get_731(void *); +void set_732(void *, unsigned int); +unsigned int get_732(void *); +void set_733(void *, unsigned int); +#define get_733(x) get_731(x) + @@ -42,7 +42,7 @@ * * Given the uncompressed size, block_size, and header_size: * - * nblocks := ceil(size/block_size) + * Nblocks := ceil(size/block_size) * * After the header follow (nblock+1) 32-bit pointers, recorded as * iso9660 7.3.1 (littleendian); each indicate the byte offset (from @@ -59,7 +59,6 @@ * The block data is compressed according to "zlib". */ -#include <dirent.h> #include <errno.h> #include <stdlib.h> #include <string.h> @@ -67,757 +66,75 @@ #include <unistd.h> #include <limits.h> #include <utime.h> -#include <signal.h> -#include <stdarg.h> #include <sys/stat.h> #include <sys/types.h> -#include <sys/wait.h> -#include <zlib.h> +#include "mkzftree.h" -#define HAVE_LCHOWN 1 /* Should be obtained by autoconf or so */ +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif -/* Verbosity levels */ -enum verbosity { - vl_quiet, /* No messages */ - vl_error, /* Error messages only */ - vl_filename, /* Display filenames */ -}; - /* Command line options */ int force = 0; /* Always compress */ int level = 9; /* Compression level */ int parallel = 0; /* Parallelism (0 = strictly serial) */ int onefs = 0; /* One filesystem only */ -enum verbosity verbosity = vl_error; /* Default verbosity */ +enum verbosity verbosity = default_verbosity; /* Default verbosity */ +munger_func munger = block_compress_file; /* Default action */ /* Program name */ const char *program; -/* Functions related to parallel execution */ -static volatile int work_threads = 0; -static int is_worker = 0; - -/* This waits for one worker to finish */ -void wait_for_one_worker(void) -{ - int status; - - if ( wait(&status) > 0 ) { - work_threads--; - - if ( WIFSIGNALED(status) || WEXITSTATUS(status) ) - kill(getpid(), SIGTERM); /* We had problems, stop now */ - } -} - -/* This waits for *all* workers to finish */ -void wait_for_all_workers(void) -{ - while ( work_threads ) - wait_for_one_worker(); -} - -/* This returns 1 if the "job" at hand should be performed */ -int spawn_worker(void) -{ - pid_t f; - - if ( parallel == 0 ) - return 1; - - fflush(NULL); - - /* Wait for a work slot */ - while ( work_threads >= parallel ) - wait_for_one_worker(); - - /* Spawn worker process */ - work_threads++; /* Avoids race conditions */ - f = fork(); - if ( f == -1 ) { - work_threads--; - return 1; /* Do it ourselves */ - } - - if ( f == 0 ) { - /* Worker process */ - is_worker = 1; - return 1; - } else { - /* Control process */ - return 0; - } -} - -/* Routine to perform at the end of the job */ -void end_worker(int err) -{ - if ( is_worker ) { - exit(err); - } -} - -/* Convenience functions */ -void *xmalloc(size_t size) -{ - void *p = malloc(size); - - if ( !p ) { - perror(program); - exit(1); - } - - return p; -} - -char *xstrdup(const char *str) -{ - char *s = strdup(str); - - if ( !s ) { - perror(program); - exit(1); - } - - return s; -} - -void message(int level, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - if ( verbosity >= level ) - vfprintf(stderr, format, ap); - va_end(ap); -} - -/* iso9660 integer formats */ -static void -set_721(void *pnt, unsigned int i) -{ - unsigned char *p = (unsigned char *)pnt; - p[0] = i & 0xff; - p[1] = (i >> 8) & 0xff; -} - -static unsigned int -get_721(void *pnt) -{ - unsigned char *p = (unsigned char *)pnt; - return ((unsigned int)p[0]) + ((unsigned int)p[1] << 8); -} - -static void -set_722(void *pnt, unsigned int i) -{ - unsigned char *p = (unsigned char *)pnt; - p[0] = (i >> 8) & 0xff; - p[1] = i & 0xff; -} - -static unsigned int -get_722(void *pnt) -{ - unsigned char *p = (unsigned char *)pnt; - return ((unsigned int)p[0] << 8) + ((unsigned int)p[1]); -} - -static void -set_723(void *pnt, unsigned int i) -{ - unsigned char *p = (unsigned char *)pnt; - p[3] = p[0] = i & 0xff; - p[2] = p[1] = (i >> 8) & 0xff; -} - -#define get_723(x) get_721(x) - -static void -set_731(void *pnt, unsigned int i) -{ - unsigned char *p = (unsigned char *)pnt; - p[0] = i & 0xff; - p[1] = (i >> 8) & 0xff; - p[2] = (i >> 16) & 0xff; - p[3] = (i >> 24) & 0xff; -} - -static unsigned int -get_731(void *pnt) -{ - unsigned char *p = (unsigned char *)pnt; - return ((unsigned int)p[0]) + ((unsigned int)p[1] << 8) + - ((unsigned int)p[2] << 16) + ((unsigned int)p[3] << 24); -} - -static void -set_732(void *pnt, unsigned int i) -{ - unsigned char *p = (unsigned char *)pnt; - p[3] = i & 0xff; - p[2] = (i >> 8) & 0xff; - p[1] = (i >> 16) & 0xff; - p[0] = (i >> 24) & 0xff; -} - -static unsigned int -get_732(void *pnt) -{ - unsigned char *p = (unsigned char *)pnt; - return ((unsigned int)p[0] << 24) + ((unsigned int)p[1] << 16) + - ((unsigned int)p[2] << 8) + ((unsigned int)p[3]); -} - -static void -set_733(void *pnt, unsigned int i) -{ - unsigned char *p = (unsigned char *)pnt; - p[7] = p[0] = i & 0xff; - p[6] = p[1] = (i >> 8) & 0xff; - p[5] = p[2] = (i >> 16) & 0xff; - p[4] = p[3] = (i >> 24) & 0xff; -} - -#define get_733(x) get_731(x) - -/* File transformation function */ -typedef int (*munger_func)(FILE *, FILE *, unsigned long); - -/* zisofs definitions */ - -#ifndef CBLOCK_SIZE_LG2 -#define CBLOCK_SIZE_LG2 15 /* Compressed block size */ -#endif -#define CBLOCK_SIZE (1U << CBLOCK_SIZE_LG2) - -/* Compressed file magic */ -const unsigned char zisofs_magic[8] = - { 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07 }; - -/* VERY VERY VERY IMPORTANT: Must be a multiple of 4 bytes */ -struct compressed_file_header { - char magic[8]; - char uncompressed_len[4]; - unsigned char header_size; - unsigned char block_size; - char reserved[2]; /* Reserved for future use, MBZ */ +/* Long options */ +#define OPTSTRING "fl:up:xvqV:h" +#ifdef HAVE_GETOPT_LONG +const struct option long_options[] = { + { "force", 0, 0, 'f' }, + { "level", 1, 0, 'l' }, + { "uncompress", 0, 0, 'u' }, + { "parallelism", 1, 0, 'p' }, + { "one-filesystem", 0, 0, 'x' }, + { "verbose", 0, 0, 'v' }, + { "quiet", 0, 0, 'q' }, + { "verbosity", 1, 0, 'V' }, + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } }; - -int block_uncompress_file(FILE *input, FILE *output, unsigned long size) -{ - struct compressed_file_header hdr; - char *inbuf, *outbuf; - long bytes; - int block_shift; - char *pointer_block, *pptr; - unsigned long position; - unsigned long nblocks; - unsigned long fullsize, block_size, block_size2; - unsigned long ptrblock_bytes; - unsigned long cstart, cend, csize; - int zerr; - int err = -1; - - if ( (bytes = fread(&hdr, 1, sizeof hdr, input)) != sizeof hdr ) { - if ( bytes == size ) { - /* Very short file; not compressed */ - return ( fwrite(&hdr, 1, bytes, output) != bytes ) ? -1 : 0; - } else { - return -1; /* Read error */ - } - } - - if ( memcmp(&hdr.magic, zisofs_magic, sizeof zisofs_magic) ) { - inbuf = xmalloc(CBLOCK_SIZE); - /* Not compressed */ - memcpy(inbuf, &hdr, sizeof hdr); - bytes = sizeof hdr; - do { - if ( fwrite(inbuf, 1, bytes, output) != bytes ) - return -1; - } while ( (bytes = fread(inbuf, 1, CBLOCK_SIZE, input)) > 0 ); - free(inbuf); - return (bytes < 0) ? -1 : 0; - } - - /* Now we know the file must be compressed. Get the pointer table. */ - if ( fseek(input, hdr.header_size << 2, SEEK_SET) == -1 ) - return -1; - - fullsize = get_731(hdr.uncompressed_len); - block_shift = hdr.block_size; - block_size = 1UL << block_shift; - block_size2 = block_size << 1; - inbuf = xmalloc(block_size2); - outbuf = xmalloc(block_size); - - nblocks = (fullsize + block_size - 1) >> block_shift; - - ptrblock_bytes = (nblocks+1) * 4; - pointer_block = xmalloc(ptrblock_bytes); - - errno = 0; - if ( (bytes = fread(pointer_block, 1, ptrblock_bytes, input)) != ptrblock_bytes ) { - if ( errno == 0 ) errno = EINVAL; - goto free_ptr_bail; - } - - pptr = pointer_block; - while ( fullsize ) { - cstart = get_731(pptr); - pptr += 4; - cend = get_731(pptr); - - csize = cend-cstart; - - if ( csize == 0 ) { - memset(outbuf, 0, block_size); - bytes = block_size; - } else { - if ( csize > block_size2 ) { - errno = EINVAL; - goto free_ptr_bail; - } - - if ( fseek(input, cstart, SEEK_SET) == -1 ) - goto free_ptr_bail; - - errno = 0; - if ( (bytes = fread(inbuf, 1, csize, input)) != csize ) { - if ( errno == 0 ) errno = EINVAL; - goto free_ptr_bail; - } - - bytes = block_size; /* Max output buffer size */ - if ( (zerr = uncompress(outbuf, &bytes, inbuf, csize)) != Z_OK ) { - errno = (zerr = Z_MEM_ERROR) ? ENOMEM : EINVAL; - goto free_ptr_bail; - } - } - - if ( ((fullsize > block_size) && (bytes != block_size)) - || ((fullsize <= block_size) && (bytes < fullsize)) ) { - errno = EINVAL; - goto free_ptr_bail; - } - - if ( bytes > fullsize ) - bytes = fullsize; - - errno = 0; - if ( fwrite(outbuf, 1, bytes, output) != bytes ) { - if ( errno == 0 ) errno = EINVAL; - goto free_ptr_bail; - } - - fullsize -= bytes; - } - - err = 0; - - free_ptr_bail: - free(pointer_block); - free(inbuf); - free(outbuf); - return err; -} - - -int block_compress_file(FILE *input, FILE *output, unsigned long size) -{ - struct compressed_file_header hdr; - char inbuf[CBLOCK_SIZE], outbuf[2*CBLOCK_SIZE]; - int bytes, pointer_bytes, nblocks, block; - uLong cbytes; /* uLong is a zlib datatype */ - char *pointer_block, *curptr; - unsigned long position; - int i; - int header_size; - int force_compress = force; - int zerr; - - if ( (sizeof hdr) & 3 ) { - fputs("INTERNAL ERROR: header is not a multiple of 4\n", stderr); - abort(); - } - - memset(&hdr, 0, sizeof hdr); - memcpy(&hdr.magic, zisofs_magic, sizeof zisofs_magic); - hdr.header_size = (sizeof hdr) >> 2; - hdr.block_size = CBLOCK_SIZE_LG2; - set_731(&hdr.uncompressed_len, size); - - if ( fwrite(&hdr, sizeof hdr, 1, output) != 1 ) - return -1; - - nblocks = (size+CBLOCK_SIZE-1) >> CBLOCK_SIZE_LG2; - pointer_bytes = 4*(nblocks+1); - pointer_block = xmalloc(pointer_bytes); - if ( !pointer_block ) - return -1; - memset(pointer_block, 0, pointer_bytes); - - if ( fseek(output, pointer_bytes, SEEK_CUR) == -1 ) - goto free_ptr_bail; - - curptr = pointer_block; - position = sizeof hdr + pointer_bytes; - - block = 0; - while ( (bytes = fread(inbuf, 1, CBLOCK_SIZE, input)) > 0 ) { - if ( bytes < CBLOCK_SIZE && block < nblocks-1 ) { - errno = EINVAL; /* Someone changed the file on us */ - goto free_ptr_bail; - } - - /* HACK: If the file has our magic number, always compress */ - if ( block == 0 && bytes >= sizeof zisofs_magic ) { - if ( !memcmp(inbuf, zisofs_magic, sizeof zisofs_magic) ) - force_compress = 1; - } - - set_731(curptr, position); curptr += 4; - - /* We have two special cases: a zero-length block is defined as all zero, - and a block the length of which is equal to the block size is unencoded. */ - - for ( i = 0 ; i < CBLOCK_SIZE ; i++ ) { - if ( inbuf[i] ) break; - } - - if ( i == CBLOCK_SIZE ) { - /* All-zero block. No output */ - } else { - cbytes = 2*CBLOCK_SIZE; - if ( (zerr = compress2(outbuf, &cbytes, inbuf, bytes, level)) != Z_OK ) { - errno = (zerr == Z_MEM_ERROR) ? ENOMEM : EINVAL; - goto free_ptr_bail; /* Compression failure */ - } - if ( fwrite(outbuf, 1, cbytes, output) != cbytes ) - goto free_ptr_bail; - - position += cbytes; - } - block++; - } - - /* Set pointer to the end of the final block */ - set_731(curptr, position); - - /* Now write the pointer table */ - if ( fseek(output, sizeof hdr, SEEK_SET) == -1 ) - goto free_ptr_bail; - - if ( fwrite(pointer_block, 1, pointer_bytes, output) != pointer_bytes ) - goto free_ptr_bail; - - free(pointer_block); - - /* Now make sure that this was actually the right thing to do */ - if ( !force_compress && position >= size ) { - /* Incompressible file, just copy it */ - rewind(input); - rewind(output); - - position = 0; - while ( (bytes = fread(inbuf, 1, CBLOCK_SIZE, input)) > 0 ) { - if ( fwrite(inbuf, 1, bytes, output) != bytes ) - return -1; - position += bytes; - } - - /* Truncate the file to the correct size */ - fflush(output); - ftruncate(fileno(output), position); - } - - /* If we get here, we're done! */ - return 0; - - /* Common bailout code */ - free_ptr_bail: - free(pointer_block); - return -1; -} - -int munge_path(const char *inpath, const char *outpath, struct stat *st, munger_func munger) -{ - FILE *in, *out; - int err = 0, rv = 0; - struct utimbuf ut; - - in = fopen(inpath, "rb"); - if ( !in ) - return -1; - out = fopen(outpath, "wb"); - if ( !out ) { - err = errno; - fclose(in); - errno = err; - return -1; - } - - if ( spawn_worker() ) { - rv = munger(in, out, st->st_size); - - err = rv ? errno : 0; - fclose(in); - fclose(out); - -#ifdef HAVE_LCHOWN - lchown(outpath, st->st_uid, st->st_gid); -#endif - if ( !S_ISLNK(st->st_mode) ) { -#ifndef HAVE_LCHOWN - chown(outpath, st->st_uid, st->st_gid); +#define LO(X) X +#else +#define getopt_long(C,V,O,L,I) getopt(C,V,O) +#define LO(X) #endif - chmod(outpath, st->st_mode); - ut.actime = st->st_atime; - ut.modtime = st->st_mtime; - utime(outpath, &ut); - } - - end_worker(err); - } else { - fclose(in); - fclose(out); - } - - errno = err; - return rv; -} - -/* Hash table used to find hard-linked files */ -#define HASH_BUCKETS 2683 -struct file_hash { - struct file_hash *next; - struct stat st; - const char *outfile_name; -}; - -static struct file_hash *hashp[HASH_BUCKETS]; - -const char *hash_find_file(struct stat *st) -{ - int bucket = (st->st_ino + st->st_dev) % HASH_BUCKETS; - struct file_hash *hp; - - for ( hp = hashp[bucket] ; hp ; hp = hp->next ) { - if ( hp->st.st_ino == st->st_ino && - hp->st.st_dev == st->st_dev && - hp->st.st_mode == st->st_mode && - hp->st.st_nlink == st->st_nlink && - hp->st.st_uid == st->st_uid && - hp->st.st_gid == st->st_gid && - hp->st.st_size == st->st_size && - hp->st.st_mtime == st->st_mtime ) { - /* Good enough, it's the same file */ - return hp->outfile_name; - } - } - return NULL; /* No match */ -} - -/* Note: the stat structure is the input file; the name - is the output file to link to */ -void hash_insert_file(struct stat *st, const char *outfile) + +static void usage(enum verbosity level, int err) { - int bucket = (st->st_ino + st->st_dev) % HASH_BUCKETS; - struct file_hash *hp = xmalloc(sizeof(struct file_hash)); - - hp->next = hashp[bucket]; - memcpy(&hp->st, st, sizeof(struct stat)); - hp->outfile_name = xstrdup(outfile); - - hashp[bucket] = hp; + message(level, + "Usage: %s [options] intree outtree\n" + LO(" --force ")" -f Always compress, even if result is larger\n" + LO(" --level # ")" -z # Set compression level (1-9)\n" + LO(" --uncompress ")" -u Uncompress an already compressed tree\n" + LO(" --parallelism # ")" -p # Process up to # files in parallel\n" + LO(" --one-filesystem")" -x Do not cross filesystem boundaries\n" + LO(" --verbose ")" -v Increase message verbosity\n" + LO(" --verbosity # ")" -V # Set message verbosity to # (default = %d)\n" + LO(" --quiet ")" -q No messages, not even errors (-V 0)\n" + ,program, (int)default_verbosity); + exit(err); } - -int munge_tree(const char *intree, const char *outtree, munger_func munger) +static int opt_atoi(const char *str) { - char buffer[BUFSIZ]; - char *in_path, *out_path, *in_file, *out_file; - DIR *thisdir; - struct dirent *dirent; - struct stat st, dirst; - struct utimbuf ut; - int err = 0; - - /* Construct buffers with the common filename prefix, and point to the end */ - - in_path = xmalloc(strlen(intree) + NAME_MAX + 2); - out_path = xmalloc(strlen(outtree) + NAME_MAX + 2); - - strcpy(in_path, intree); - strcpy(out_path, outtree); - - in_file = strchr(in_path, '\0'); - out_file = strchr(out_path, '\0'); - - *in_file++ = '/'; - *out_file++ = '/'; - - /* Get directory information */ - if ( stat(intree, &dirst) ) { - message(vl_error, "%s: Failed to stat directory %s: %s\n", - program, intree, strerror(errno)); - return 1; - } - - /* Open the directory */ - thisdir = opendir(intree); - if ( !thisdir ) { - message(vl_error, "%s: Failed to open directory %s: %s\n", - program, intree, strerror(errno)); - return 1; - } - - /* Create output directory */ - if ( mkdir(outtree, 0700) ) { - message(vl_error, "%s: Cannot create output directory %s: %s\n", - program, outtree, strerror(errno)); - return 1; - } - - while ( (dirent = readdir(thisdir)) != NULL ) { - if ( !strcmp(dirent->d_name, ".") || - !strcmp(dirent->d_name, "..") ) - continue; /* Ignore . and .. */ - - strcpy(in_file, dirent->d_name); - strcpy(out_file, dirent->d_name); - - message(vl_filename, "%s -> %s\n", in_path, out_path); - - if ( lstat(in_path, &st) ) { - message(vl_error, "%s: Failed to stat file %s: %s\n", - program, in_path, strerror(errno)); - err = 1; - break; - } - - if ( S_ISREG(st.st_mode) ) { - if ( st.st_nlink > 1 ) { - /* Hard link. */ - const char *linkname; + char *endptr; + long out; - if ( (linkname = hash_find_file(&st)) != NULL ) { - /* We've seen it before, hard link it */ + out = strtol(str, &endptr, 10); + if ( *endptr ) + usage(vl_error, EX_USAGE); - if ( link(linkname, out_path) ) { - message(vl_error, "%s: hard link %s -> %s failed: %s\n", - program, out_path, linkname, strerror(errno)); - err = 1; - break; - } - } else { - /* First encounter, compress and enter into hash */ - if ( munge_path(in_path, out_path, &st, munger) ) { - message(vl_error, "%s: %s: %s", program, in_path, strerror(errno)); - err = 1; - break; - } - hash_insert_file(&st, out_path); - } - } else { - /* Singleton file; no funnies */ - if ( munge_path(in_path, out_path, &st, munger) ) { - message(vl_error, "%s: %s: %s", program, in_path, strerror(errno)); - err = 1; - break; - } - } - } else if ( S_ISDIR(st.st_mode) ) { - /* Recursion: see recursion */ - /* Don't decend if -x is specified and st_dev is different */ - if ( !onefs || dirst.st_dev == st.st_dev ) { - err = munge_tree(in_path, out_path, munger); - if ( err ) - break; - } - } else if ( S_ISLNK(st.st_mode) ) { - int chars; - if ( (chars = readlink(in_path, buffer, BUFSIZ)) < 0 ) { - message(vl_error, "%s: readlink failed for %s: %s\n", - program, in_path, strerror(errno)); - err = 1; - break; - } - buffer[chars] = '\0'; - if ( symlink(buffer, out_path) ) { - message(vl_error, "%s: symlink %s -> %s failed: %s\n", - program, out_path, buffer, strerror(errno)); - err = 1; - break; - } - } else { - if ( st.st_nlink > 1 ) { - /* Hard link. */ - const char *linkname; - - if ( (linkname = hash_find_file(&st)) != NULL ) { - /* We've seen it before, hard link it */ - - if ( link(linkname, out_path) ) { - message(vl_error, "%s: hard link %s -> %s failed: %s\n", - program, out_path, linkname, strerror(errno)); - err = 1; - break; - } - } else { - /* First encounter, create and enter into hash */ - if ( mknod(out_path, st.st_mode, st.st_rdev) ) { - message(vl_error, "%s: mknod failed for %s: %s\n", - program, out_path, strerror(errno)); - err = 1; - break; - } - hash_insert_file(&st, out_path); - } - } else { - /* Singleton node; no funnies */ - if ( mknod(out_path, st.st_mode, st.st_rdev) ) { - message(vl_error, "%s: mknod failed for %s: %s\n", - program, out_path, strerror(errno)); - err = 1; - break; - } - } - } - - /* This is done by munge_path() for files */ - if ( !S_ISREG(st.st_mode) ) { -#ifdef HAVE_LCHOWN - lchown(out_path, st.st_uid, st.st_gid); -#endif - if ( !S_ISLNK(st.st_mode) ) { -#ifndef HAVE_LCHOWN - chown(out_path, st.st_uid, st.st_gid); -#endif - chmod(out_path, st.st_mode); - ut.actime = st.st_atime; - ut.modtime = st.st_mtime; - utime(out_path, &ut); - } - } - } - closedir(thisdir); - - free(in_path); - free(out_path); - - return err; + return (int)out; } -static void usage(enum verbosity level, int err) -{ - message(level, - "Usage: %s [-vqfhux] [-p parallelism] [-z level] intree outtree\n", - program); - exit(err); -} int main(int argc, char *argv[]) { @@ -825,11 +142,11 @@ int main(int argc, char *argv[]) struct stat st; struct utimbuf ut; int opt, err; - munger_func munger = block_compress_file; program = argv[0]; - while ( (opt = getopt(argc, argv, "vqfz:p:hux")) != EOF ) { + while ( (opt = getopt_long(argc, argv, OPTSTRING, long_options, NULL)) + != EOF ) { switch(opt) { case 'f': force = 1; /* Always compress */ @@ -838,7 +155,7 @@ int main(int argc, char *argv[]) if ( optarg[0] < '0' || optarg[0] > '9' || optarg[1] ) { message(vl_error, "%s: invalid compression level: %s\n", program, optarg); - exit(1); + exit(EX_USAGE); } else { level = optarg[0] - '0'; } @@ -849,6 +166,9 @@ int main(int argc, char *argv[]) case 'v': verbosity++; break; + case 'V': + verbosity = opt_atoi(optarg); + break; case 'q': verbosity = vl_quiet; break; @@ -856,13 +176,13 @@ int main(int argc, char *argv[]) munger = block_uncompress_file; break; case 'p': - parallel = atoi(optarg); + parallel = opt_atoi(optarg); break; case 'x': onefs = 1; break; default: - usage(vl_error, 1); + usage(vl_error, EX_USAGE); break; } } @@ -878,14 +198,14 @@ int main(int argc, char *argv[]) /* Special case: we use stat() for the root, not lstat() */ if ( stat(in, &st) ) { message(vl_error, "%s: %s: %s\n", program, in, strerror(errno)); - exit(1); + exit(EX_NOINPUT); } if ( !S_ISDIR(st.st_mode) ) { message(vl_error, "%s: %s: Not a directory\n", program, in); - exit(1); + exit(EX_DATAERR); } - err = munge_tree(in, out, munger); + err = munge_tree(in, out); wait_for_all_workers(); @@ -897,4 +217,6 @@ int main(int argc, char *argv[]) ut.actime = st.st_atime; ut.modtime = st.st_mtime; utime(out, &ut); + + return 0; } diff --git a/mkzftree.h b/mkzftree.h new file mode 100644 index 0000000..25b7171 --- /dev/null +++ b/mkzftree.h @@ -0,0 +1,77 @@ +#ident "$Id$" +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include "config.h" +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef HAVE_SYSEXITS_H +#include <sysexits.h> +#else +#define EX_USAGE 64 /* command line usage error */ +#define EX_DATAERR 65 /* data format error */ +#define EX_NOINPUT 66 /* cannot open input */ +#define EX_NOUSER 67 /* addressee unknown */ +#define EX_NOHOST 68 /* host name unknown */ +#define EX_UNAVAILABLE 69 /* service unavailable */ +#define EX_SOFTWARE 70 /* internal software error */ +#define EX_OSERR 71 /* system error (e.g., can't fork) */ +#define EX_OSFILE 72 /* critical OS file missing */ +#define EX_CANTCREAT 73 /* can't create (user) output file */ +#define EX_IOERR 74 /* input/output error */ +#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ +#define EX_PROTOCOL 76 /* remote error in protocol */ +#define EX_NOPERM 77 /* permission denied */ +#define EX_CONFIG 78 /* configuration error */ +#endif + +/* File transformation functions */ +typedef int (*munger_func)(FILE *, FILE *, off_t); +int block_compress_file(FILE *, FILE *, off_t); +int block_uncompress_file(FILE *, FILE *, off_t); + +/* mkzftree.c */ +extern const char *program; /* Program name */ +extern int force; /* Always compress */ +extern int level; /* Compression level */ +extern int parallel; /* Parallelism (0 = strictly serial) */ +extern int onefs; /* One filesystem only */ +extern munger_func munger; /* Default action */ +enum verbosity { + vl_quiet, /* No messages */ + vl_error, /* Error messages only */ + vl_filename, /* Display filenames */ +}; +#define default_verbosity vl_error +extern enum verbosity verbosity; /* Message verbosity */ + +/* walk.c */ +int munge_tree(const char *, const char *); + +/* workers.c */ +void wait_for_all_workers(void); +int spawn_worker(void); +void end_worker(int); + +/* util.c */ +void *xmalloc(size_t); +char *xstrdup(const char *); +void message(enum verbosity, const char *, ...); + +/* hash.c */ +const char *hash_find_file(struct stat *); +void hash_insert_file(struct stat *, const char *); + diff --git a/uncompress.c b/uncompress.c new file mode 100644 index 0000000..901889c --- /dev/null +++ b/uncompress.c @@ -0,0 +1,133 @@ +/* $Id$ */ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include <stdio.h> +#include <stdlib.h> +#include <zlib.h> +#include "mkzftree.h" +#include "iso9660.h" + +int block_uncompress_file(FILE *input, FILE *output, off_t size) +{ + struct compressed_file_header hdr; + char *inbuf, *outbuf; + long bytes; + int block_shift; + char *pointer_block, *pptr; + unsigned long nblocks; + unsigned long fullsize, block_size, block_size2; + unsigned long ptrblock_bytes; + unsigned long cstart, cend, csize; + int zerr; + int err = EX_SOFTWARE; + + if ( (bytes = fread(&hdr, 1, sizeof hdr, input)) != sizeof hdr ) { + if ( bytes == size ) { + /* Very short file; not compressed */ + return ( fwrite(&hdr, 1, bytes, output) != bytes ) ? EX_CANTCREAT : 0; + } else { + return EX_IOERR; /* Read error */ + } + } + + if ( memcmp(&hdr.magic, zisofs_magic, sizeof zisofs_magic) ) { + inbuf = xmalloc(CBLOCK_SIZE); + /* Not compressed */ + memcpy(inbuf, &hdr, sizeof hdr); + bytes = sizeof hdr; + do { + if ( fwrite(inbuf, 1, bytes, output) != bytes ) + return EX_CANTCREAT; + } while ( (bytes = fread(inbuf, 1, CBLOCK_SIZE, input)) > 0 ); + free(inbuf); + return (bytes < 0) ? EX_IOERR : 0; + } + + /* Now we know the file must be compressed. Get the pointer table. */ + if ( fseek(input, hdr.header_size << 2, SEEK_SET) == -1 ) + return EX_IOERR; + + fullsize = get_731(hdr.uncompressed_len); + block_shift = hdr.block_size; + block_size = 1UL << block_shift; + block_size2 = block_size << 1; + inbuf = xmalloc(block_size2); + outbuf = xmalloc(block_size); + + nblocks = (fullsize + block_size - 1) >> block_shift; + + ptrblock_bytes = (nblocks+1) * 4; + pointer_block = xmalloc(ptrblock_bytes); + + if ( (bytes = fread(pointer_block, 1, ptrblock_bytes, input)) != ptrblock_bytes ) { + err = EX_IOERR; + goto free_ptr_bail; + } + + pptr = pointer_block; + while ( fullsize ) { + cstart = get_731(pptr); + pptr += 4; + cend = get_731(pptr); + + csize = cend-cstart; + + if ( csize == 0 ) { + memset(outbuf, 0, block_size); + bytes = block_size; + } else { + if ( csize > block_size2 ) { + err = EX_DATAERR; + goto free_ptr_bail; + } + + if ( fseek(input, cstart, SEEK_SET) == -1 || + (bytes = fread(inbuf, 1, csize, input)) != csize ) { + err = EX_IOERR; + goto free_ptr_bail; + } + + bytes = block_size; /* Max output buffer size */ + if ( (zerr = uncompress(outbuf, &bytes, inbuf, csize)) != Z_OK ) { + err = (zerr = Z_MEM_ERROR) ? EX_OSERR : EX_DATAERR; + goto free_ptr_bail; + } + } + + if ( ((fullsize > block_size) && (bytes != block_size)) + || ((fullsize <= block_size) && (bytes < fullsize)) ) { + err = EX_DATAERR; + goto free_ptr_bail; + } + + if ( bytes > fullsize ) + bytes = fullsize; + + if ( fwrite(outbuf, 1, bytes, output) != bytes ) { + err = EX_CANTCREAT; + goto free_ptr_bail; + } + + fullsize -= bytes; + } + + err = 0; + + free_ptr_bail: + free(pointer_block); + free(inbuf); + free(outbuf); + return err; +} + + @@ -0,0 +1,55 @@ +/* $Id$ */ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include "mkzftree.h" + +/* Convenience functions */ +void *xmalloc(size_t size) +{ + void *p = malloc(size); + + if ( !p ) { + perror(program); + exit(EX_OSERR); + } + + return p; +} + +char *xstrdup(const char *str) +{ + char *s = strdup(str); + + if ( !s ) { + perror(program); + exit(EX_OSERR); + } + + return s; +} + +void message(enum verbosity level, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + if ( verbosity >= level ) + vfprintf(stderr, format, ap); + va_end(ap); +} + @@ -0,0 +1,246 @@ +#ident "$Id$" +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * walk.c + * + * Functions to walk the file tree + */ + +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> +#include <utime.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include "mkzftree.h" + +static int munge_path(const char *inpath, const char *outpath, struct stat *st) +{ + FILE *in, *out; + int err = 0; + struct utimbuf ut; + + in = fopen(inpath, "rb"); + if ( !in ) + return EX_NOINPUT; + out = fopen(outpath, "wb"); + if ( !out ) { + fclose(in); + return EX_CANTCREAT; + } + + if ( spawn_worker() ) { + err = munger(in, out, st->st_size); + fclose(in); + fclose(out); + +#ifdef HAVE_LCHOWN + lchown(outpath, st->st_uid, st->st_gid); +#endif + if ( !S_ISLNK(st->st_mode) ) { +#ifndef HAVE_LCHOWN + chown(outpath, st->st_uid, st->st_gid); +#endif + chmod(outpath, st->st_mode); + ut.actime = st->st_atime; + ut.modtime = st->st_mtime; + utime(outpath, &ut); + } + + end_worker(err); + } else { + fclose(in); + fclose(out); + } + + return err; +} + +int munge_tree(const char *intree, const char *outtree) +{ + char buffer[BUFSIZ]; + char *in_path, *out_path, *in_file, *out_file; + DIR *thisdir; + struct dirent *dirent; + struct stat st, dirst; + struct utimbuf ut; + int err = 0; + + /* Construct buffers with the common filename prefix, and point to the end */ + + in_path = xmalloc(strlen(intree) + NAME_MAX + 2); + out_path = xmalloc(strlen(outtree) + NAME_MAX + 2); + + strcpy(in_path, intree); + strcpy(out_path, outtree); + + in_file = strchr(in_path, '\0'); + out_file = strchr(out_path, '\0'); + + *in_file++ = '/'; + *out_file++ = '/'; + + /* Get directory information */ + if ( stat(intree, &dirst) ) { + message(vl_error, "%s: Failed to stat directory %s: %s\n", + program, intree, strerror(errno)); + return EX_NOINPUT; + } + + /* Open the directory */ + thisdir = opendir(intree); + if ( !thisdir ) { + message(vl_error, "%s: Failed to open directory %s: %s\n", + program, intree, strerror(errno)); + return EX_NOINPUT; + } + + /* Create output directory */ + if ( mkdir(outtree, 0700) ) { + message(vl_error, "%s: Cannot create output directory %s: %s\n", + program, outtree, strerror(errno)); + return EX_CANTCREAT; + } + + while ( (dirent = readdir(thisdir)) != NULL ) { + if ( !strcmp(dirent->d_name, ".") || + !strcmp(dirent->d_name, "..") ) + continue; /* Ignore . and .. */ + + strcpy(in_file, dirent->d_name); + strcpy(out_file, dirent->d_name); + + message(vl_filename, "%s -> %s\n", in_path, out_path); + + if ( lstat(in_path, &st) ) { + message(vl_error, "%s: Failed to stat file %s: %s\n", + program, in_path, strerror(errno)); + err = EX_NOINPUT; + break; + } + + if ( S_ISREG(st.st_mode) ) { + if ( st.st_nlink > 1 ) { + /* Hard link. */ + const char *linkname; + + if ( (linkname = hash_find_file(&st)) != NULL ) { + /* We've seen it before, hard link it */ + + if ( link(linkname, out_path) ) { + message(vl_error, "%s: hard link %s -> %s failed: %s\n", + program, out_path, linkname, strerror(errno)); + err = EX_CANTCREAT; + break; + } + } else { + /* First encounter, compress and enter into hash */ + if ( (err = munge_path(in_path, out_path, &st)) != 0 ) { + message(vl_error, "%s: %s: %s", program, in_path, strerror(errno)); + break; + } + hash_insert_file(&st, out_path); + } + } else { + /* Singleton file; no funnies */ + if ( (err = munge_path(in_path, out_path, &st)) != 0 ) { + message(vl_error, "%s: %s: %s", program, in_path, strerror(errno)); + break; + } + } + } else if ( S_ISDIR(st.st_mode) ) { + /* Recursion: see recursion */ + /* Don't decend if -x is specified and st_dev is different */ + if ( !onefs || dirst.st_dev == st.st_dev ) { + if ( (err = munge_tree(in_path, out_path)) != 0 ) + break; + } + } else if ( S_ISLNK(st.st_mode) ) { + int chars; + if ( (chars = readlink(in_path, buffer, BUFSIZ)) < 0 ) { + message(vl_error, "%s: readlink failed for %s: %s\n", + program, in_path, strerror(errno)); + err = EX_NOINPUT; + break; + } + buffer[chars] = '\0'; + if ( symlink(buffer, out_path) ) { + message(vl_error, "%s: symlink %s -> %s failed: %s\n", + program, out_path, buffer, strerror(errno)); + err = EX_CANTCREAT; + break; + } + } else { + if ( st.st_nlink > 1 ) { + /* Hard link. */ + const char *linkname; + + if ( (linkname = hash_find_file(&st)) != NULL ) { + /* We've seen it before, hard link it */ + + if ( link(linkname, out_path) ) { + message(vl_error, "%s: hard link %s -> %s failed: %s\n", + program, out_path, linkname, strerror(errno)); + err = EX_CANTCREAT; + break; + } + } else { + /* First encounter, create and enter into hash */ + if ( mknod(out_path, st.st_mode, st.st_rdev) ) { + message(vl_error, "%s: mknod failed for %s: %s\n", + program, out_path, strerror(errno)); + err = EX_CANTCREAT; + break; + } + hash_insert_file(&st, out_path); + } + } else { + /* Singleton node; no funnies */ + if ( mknod(out_path, st.st_mode, st.st_rdev) ) { + message(vl_error, "%s: mknod failed for %s: %s\n", + program, out_path, strerror(errno)); + err = EX_CANTCREAT; + break; + } + } + } + + /* This is done by munge_path() for files */ + if ( !S_ISREG(st.st_mode) ) { +#ifdef HAVE_LCHOWN + lchown(out_path, st.st_uid, st.st_gid); +#endif + if ( !S_ISLNK(st.st_mode) ) { +#ifndef HAVE_LCHOWN + chown(out_path, st.st_uid, st.st_gid); +#endif + chmod(out_path, st.st_mode); + ut.actime = st.st_atime; + ut.modtime = st.st_mtime; + utime(out_path, &ut); + } + } + } + closedir(thisdir); + + free(in_path); + free(out_path); + + return err; +} + diff --git a/workers.c b/workers.c new file mode 100644 index 0000000..d1b04db --- /dev/null +++ b/workers.c @@ -0,0 +1,98 @@ +#ident "$Id$" +/* ----------------------------------------------------------------------- * + * + * Copyright 2001 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * workers.c + * + * Parallel job maintenance + */ + +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <stdio.h> +#include <sys/wait.h> +#include "mkzftree.h" + +/* Global option */ +extern int parallel; /* Number of parallel jobs */ + +/* Functions related to parallel execution */ +static volatile int work_threads = 0; +static int is_worker = 0; + +/* This waits for one worker to finish */ +static void wait_for_one_worker(void) +{ + int status; + + if ( wait(&status) > 0 ) { + work_threads--; + + if ( WIFSIGNALED(status) ) { + kill(getpid(), WTERMSIG(status)); + exit(EX_SOFTWARE); + } else if ( WEXITSTATUS(status) ) { + exit(WEXITSTATUS(status)); + } + } +} + +/* This waits for *all* workers to finish */ +void wait_for_all_workers(void) +{ + while ( work_threads ) + wait_for_one_worker(); +} + +/* This returns 1 if the "job" at hand should be performed */ +int spawn_worker(void) +{ + pid_t f; + + if ( parallel == 0 ) + return 1; + + fflush(NULL); + + /* Wait for a work slot */ + while ( work_threads >= parallel ) + wait_for_one_worker(); + + /* Spawn worker process */ + work_threads++; /* Avoids race conditions */ + f = fork(); + if ( f == -1 ) { + work_threads--; + return 1; /* Do it ourselves */ + } + + if ( f == 0 ) { + /* Worker process */ + is_worker = 1; + return 1; + } else { + /* Control process */ + return 0; + } +} + +/* Routine to perform at the end of the job */ +void end_worker(int err) +{ + if ( is_worker ) { + exit(err); + } +} + |