aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2001-07-27 14:37:07 +0000
committerH. Peter Anvin <hpa@zytor.com>2001-07-27 14:37:07 +0000
commitec1aabe7a65d739a76c6ed74ef1f109f57a09f65 (patch)
tree594c3e79332d1b02cbcac0ec97610ce57f5df22b
parent02fe6a77674886e53f61482c0203ce299608cce6 (diff)
downloadzisofs-tools-ec1aabe7a65d739a76c6ed74ef1f109f57a09f65.tar.gz
Modularize the source; add autoconf-based compilation; add long
command line options; clean up warnings.
-rw-r--r--MCONFIG.in37
-rw-r--r--MRULES22
-rw-r--r--Makefile40
-rw-r--r--aclocal.m4134
-rw-r--r--compress.c140
-rw-r--r--config.h.in27
-rw-r--r--configure.in41
-rw-r--r--hash.c68
-rwxr-xr-xinstall-sh238
-rw-r--r--iso9660.c111
-rw-r--r--iso9660.h46
-rw-r--r--mkzftree.c796
-rw-r--r--mkzftree.h77
-rw-r--r--uncompress.c133
-rw-r--r--util.c55
-rw-r--r--walk.c246
-rw-r--r--workers.c98
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@
diff --git a/MRULES b/MRULES
new file mode 100644
index 0000000..decd202
--- /dev/null
+++ b/MRULES
@@ -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 $@ $<
+
diff --git a/Makefile b/Makefile
index 68c1392..2a515e1 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
diff --git a/hash.c b/hash.c
new file mode 100644
index 0000000..993579b
--- /dev/null
+++ b/hash.c
@@ -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)
+
diff --git a/mkzftree.c b/mkzftree.c
index f37b6eb..9b66657 100644
--- a/mkzftree.c
+++ b/mkzftree.c
@@ -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;
+}
+
+
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..c3567b3
--- /dev/null
+++ b/util.c
@@ -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);
+}
+
diff --git a/walk.c b/walk.c
new file mode 100644
index 0000000..c037690
--- /dev/null
+++ b/walk.c
@@ -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);
+ }
+}
+