aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2001-05-05 02:44:25 +0000
committerH. Peter Anvin <hpa@zytor.com>2001-05-05 02:44:25 +0000
commit045d67690f7de0583936cb2643985c98c2498658 (patch)
tree6699758badc6f80ed496d766645f7aea9d930015
downloadzisofs-tools-045d67690f7de0583936cb2643985c98c2498658.tar.gz
Check in files corresponding to zisofs-tools 0.04zisofs-tools-0.04
-rw-r--r--Makefile34
-rw-r--r--README30
-rw-r--r--cdrtools-1.10-zisofs.diff158
-rw-r--r--mkzftree.c756
-rw-r--r--zisofs.magic8
5 files changed, 986 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..68c1392
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+## -----------------------------------------------------------------------
+##
+## 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.
+##
+## -----------------------------------------------------------------------
+
+##
+## Simple Makefile for mkzftree
+##
+## mkzftree mirrors a tree in a form suitable for "mkisofs -z".
+##
+
+CC = gcc
+CFLAGS = -O2
+LDFLAGS =
+LIBS = -lz
+
+all: mkzftree
+
+clean:
+ rm -f mkzftree
+
+distclean: clean
+ rm -f core *~ \#*
+
+mkzftree: mkzftree.c
+ $(CC) $(CFLAGS) $(LDFLAGS) -o mkzftree mkzftree.c $(LIBS)
+
diff --git a/README b/README
new file mode 100644
index 0000000..e976856
--- /dev/null
+++ b/README
@@ -0,0 +1,30 @@
+ May 4, 2001
+
+User tools for zisofs:
+
+The user tools for zisofs come in two parts: a utility "mkzftree" and
+a patch for cdrtools-1.10 (which includes mkisofs).
+
+First create a directory tree containing compressed files:
+
+ mkzftree input_dir compressed_dir
+
+mkzftree will not overwrite an existing directory; you may want to "rm
+-rf" the directory tree if you are doing this from a script:
+
+Second, invoke the patched mkisofs with the -z option:
+
+ mkisofs -z [other options] -o compressed.iso compressed_dir
+
+Note that if there are files you do not want compressed (for example,
+files involved in booting, or README files you want to be readable
+under all circumstances) you can simply put them in a separate tree
+and not run mkzftree on that tree.
+
+mkzftree will not compress files that end up larger when compressed;
+if you want it to compress the files anyway, you can give the -f
+option to mkzftree.
+
+mkzftree also accepts a -u option (uncompress), which can be used to
+convert a compressed tree back to normal form. This can be used to
+read a zisofs CD-ROM on a machine without zisofs kernel support.
diff --git a/cdrtools-1.10-zisofs.diff b/cdrtools-1.10-zisofs.diff
new file mode 100644
index 0000000..8c7ebe1
--- /dev/null
+++ b/cdrtools-1.10-zisofs.diff
@@ -0,0 +1,158 @@
+diff -ur cdrtools-1.10/mkisofs/mkisofs.c cdrtools-1.10-zisofs/mkisofs/mkisofs.c
+--- cdrtools-1.10/mkisofs/mkisofs.c Fri Apr 20 08:45:50 2001
++++ cdrtools-1.10-zisofs/mkisofs/mkisofs.c Thu May 3 21:59:56 2001
+@@ -565,10 +565,8 @@
+ '\0', "#", "Set numbers of load sectors", ONE_DASH},
+ {{"boot-info-table", no_argument, NULL, OPTION_BOOT_INFO_TABLE},
+ '\0', NULL, "Patch boot image with info table", ONE_DASH},
+-#ifdef ERIC_neverdef
+ {{"transparent-compression", no_argument, NULL, 'z'},
+ 'z', NULL, "Enable transparent compression of files", ONE_DASH},
+-#endif
+ #ifdef APPLE_HYB
+ {{"hfs-type", required_argument, NULL, OPTION_HFS_TYPE},
+ '\0', "TYPE", "Set HFS default TYPE", ONE_DASH},
+diff -ur cdrtools-1.10/mkisofs/rock.c cdrtools-1.10-zisofs/mkisofs/rock.c
+--- cdrtools-1.10/mkisofs/rock.c Tue Jan 23 04:28:34 2001
++++ cdrtools-1.10-zisofs/mkisofs/rock.c Fri May 4 02:34:25 2001
+@@ -45,7 +45,7 @@
+ #define PX_SIZE 36
+ #define RE_SIZE 4
+ #define SL_SIZE 20
+-#define ZZ_SIZE 15
++#define ZF_SIZE 16
+ #ifdef APPLE_HYB
+ #define AA_SIZE 14 /* size of Apple extension */
+ #endif /* APPLE_HYB */
+@@ -577,17 +577,24 @@
+ #ifndef VMS
+ /*
+ * If transparent compression was requested, fill in the correct field
+- * for this file
++ * for this file, if (and only if) it is actually a compressed file!
++ * This relies only on magic number, but it should in general not
++ * be an issue since if you're using -z odds are most of your
++ * files are already compressed.
++ *
++ * In the future it would be nice if mkisofs actually did the
++ * compression.
+ */
+- if (transparent_compression &&
+- S_ISREG(lstatbuf->st_mode) &&
+- strlen(name) > 3 &&
+- strcmp(name + strlen(name) - 3, ".gZ") == 0) {
+- FILE *zipfile;
++ if (transparent_compression && S_ISREG(lstatbuf->st_mode)) {
++ static const unsigned char zisofs_magic[8] =
++ { 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07 };
++ FILE *zffile;
+ char *checkname;
+ unsigned int file_size;
+- unsigned char header[8];
++ unsigned char header[16];
+ int OK_flag;
++ int blocksize;
++ int headersize;
+
+ /*
+ * First open file and verify that the correct algorithm was
+@@ -596,73 +603,43 @@
+ file_size = 0;
+ OK_flag = 1;
+
+- zipfile = fopen(whole_name, "rb");
+- fread(header, 1, sizeof(header), zipfile);
++ memset(header, 0, sizeof(header));
+
+- /* Check some magic numbers from gzip. */
+- if (header[0] != 0x1f || header[1] != 0x8b || header[2] != 8)
+- OK_flag = 0;
+- /* Make sure file was blocksized. */
+- if (((header[3] & 0x40) == 0))
+- OK_flag = 0;
+- /* OK, now go to the end of the file and get some more info */
+- if (OK_flag) {
+- int status;
+-
+- status = (long) lseek(fileno(zipfile), (off_t)(-8),
+- SEEK_END);
+- if (status == -1)
++ zffile = fopen(whole_name, "rb");
++ if ( zffile ) {
++ if ( fread(header, 1, sizeof(header), zffile) != sizeof(header) )
+ OK_flag = 0;
+- }
+- if (OK_flag) {
+- if (read(fileno(zipfile), (char *) header,
+- sizeof(header)) != sizeof(header)) {
++
++ /* Check magic number */
++ if ( memcmp(header, zisofs_magic, sizeof zisofs_magic) )
+ OK_flag = 0;
+- } else {
+- int blocksize;
+
+- blocksize = (header[3] << 8) | header[2];
+- file_size = ((unsigned int) header[7] << 24) |
+- ((unsigned int) header[6] << 16) |
+- ((unsigned int) header[5] << 8) |
+- header[4];
+-#if 0
+- fprintf(stderr, "Blocksize = %d %d\n",
+- blocksize, file_size);
+-#endif
+- if (blocksize != SECTOR_SIZE)
+- OK_flag = 0;
+- }
+- }
+- fclose(zipfile);
+-
+- checkname = strdup(whole_name);
+- checkname[strlen(whole_name) - 3] = 0;
+- zipfile = fopen(checkname, "rb");
+- if (zipfile) {
++ /* Get the real size of the file */
++ file_size = get_731(header+8);
++
++ /* Get the header size (>> 2) */
++ headersize = header[12];
++
++ /* Get the block size (log2) */
++ blocksize = header[13];
++
++ fclose(zffile);
++ } else {
+ OK_flag = 0;
+-#ifdef USE_LIBSCHILY
+- errmsg(
+- "Unable to insert transparent compressed file - name conflict\n");
+-#else
+- fprintf(stderr,
+- "Unable to insert transparent compressed file - name conflict\n");
+-#endif
+- fclose(zipfile);
+ }
+- free(checkname);
+-
++
+ if (OK_flag) {
+- if (MAYBE_ADD_CE_ENTRY(ZZ_SIZE))
++ if (MAYBE_ADD_CE_ENTRY(ZF_SIZE))
+ add_CE_entry();
+ Rock[ipnt++] = 'Z';
+- Rock[ipnt++] = 'Z';
+- Rock[ipnt++] = ZZ_SIZE;
++ Rock[ipnt++] = 'F';
++ Rock[ipnt++] = ZF_SIZE;
+ Rock[ipnt++] = SU_VERSION;
+- Rock[ipnt++] = 'g'; /* Identify compression
+- technique used */
++ Rock[ipnt++] = 'p'; /* Algorithm: "paged zlib" */
+ Rock[ipnt++] = 'z';
+- Rock[ipnt++] = 3;
++ /* 2 bytes for algorithm-specific information */
++ Rock[ipnt++] = headersize;
++ Rock[ipnt++] = blocksize;
+ set_733((char *) Rock + ipnt, file_size); /* Real file size */
+ ipnt += 8;
+ };
diff --git a/mkzftree.c b/mkzftree.c
new file mode 100644
index 0000000..14579f0
--- /dev/null
+++ b/mkzftree.c
@@ -0,0 +1,756 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * mkzffile.c
+ *
+ * - Generate block-compression of files for use with
+ * the "ZF" extension to the iso9660/RockRidge filesystem.
+ *
+ * The file compression technique used is the "deflate"
+ * algorithm used by the zlib library; each block must have a
+ * valid (12-byte) zlib header. In addition, the file itself
+ * has the following structure:
+ *
+ * Byte offset iso9660 type Contents
+ * 0 (8 bytes) Magic number (37 E4 53 96 C9 DB D6 07)
+ * 8 7.3.1 Uncompressed file size
+ * 12 7.1.1 header_size >> 2 (currently 4)
+ * 13 7.1.1 log2(block_size)
+ * 14 (2 bytes) Reserved, must be zero
+ *
+ * The header may get expanded in the future, at which point the
+ * header size field will be used to increase the space for the
+ * header.
+ *
+ * All implementations are required to support a block_size of 32K
+ * (byte 13 == 15).
+ *
+ * Note that bytes 12 and 13 and the uncompressed length are also
+ * present in the ZF record; THE TWO MUST BOTH BE CONSISTENT AND
+ * CORRECT.
+ *
+ * Given the uncompressed size, block_size, and header_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
+ * the start of the file) to one block and the first byte beyond the
+ * end of the previous block; the first pointer thus point to the
+ * start of the data area and the last pointer to the first byte
+ * beyond it:
+ *
+ * block_no := floor(byte_offset/block_size)
+ *
+ * block_start := read_pointer_731( (header_size+block_no)*4 )
+ * block_end := read_pointer_731( (header_size+block_no+1)*4 )
+ *
+ * The block data is compressed according to "zlib".
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <zlib.h>
+
+#define HAVE_LCHOWN 1 /* Should be obtained by autoconf or so */
+
+/* Command line options */
+int force = 0; /* Always compress */
+int level = 9; /* Compression level */
+int verbosity = 0;
+
+/* Program name */
+const char *program;
+
+/* 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;
+}
+
+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 */
+};
+
+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 > 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, unsigned long size, munger_func munger)
+{
+ FILE *in, *out;
+ int err, rv;
+
+ in = fopen(inpath, "rb");
+ if ( !in )
+ return -1;
+ out = fopen(outpath, "wb");
+ if ( !out ) {
+ err = errno;
+ fclose(in);
+ errno = err;
+ return -1;
+ }
+ rv = munger(in, out, size);
+
+ err = errno; /* Just in case */
+ 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)
+{
+ 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;
+}
+
+
+int munge_tree(const char *intree, const char *outtree, munger_func munger)
+{
+ char buffer[BUFSIZ];
+ char *in_path, *out_path, *in_file, *out_file;
+ DIR *thisdir;
+ struct dirent *dirent;
+ struct stat st;
+ 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++ = '/';
+
+ /* Open the directory */
+ thisdir = opendir(intree);
+ if ( !thisdir ) {
+ fprintf(stderr, "%s: Failed to open directory %s: %s\n",
+ program, intree, strerror(errno));
+ return 1;
+ }
+
+ /* Create output directory */
+ if ( mkdir(outtree, 0700) ) {
+ fprintf(stderr, "%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);
+
+ if ( lstat(in_path, &st) ) {
+ fprintf(stderr, "%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;
+
+ if ( (linkname = hash_find_file(&st)) != NULL ) {
+ /* We've seen it before, hard link it */
+
+ if ( link(linkname, out_path) ) {
+ fprintf(stderr, "%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.st_size, munger) ) {
+ fprintf(stderr, "%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.st_size, munger) ) {
+ fprintf(stderr, "%s: %s: %s", program, in_path, strerror(errno));
+ err = 1;
+ break;
+ }
+ }
+ } else if ( S_ISDIR(st.st_mode) ) {
+ /* Recursion: see recursion */
+ 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 ) {
+ fprintf(stderr, "%s: readlink failed for %s: %s\n",
+ program, in_path, strerror(errno));
+ err = 1;
+ break;
+ }
+ buffer[chars] = '\0';
+ if ( symlink(buffer, out_path) ) {
+ fprintf(stderr, "%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) ) {
+ fprintf(stderr, "%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) ) {
+ fprintf(stderr, "%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) ) {
+ fprintf(stderr, "%s: mknod failed for %s: %s\n",
+ program, out_path, strerror(errno));
+ err = 1;
+ break;
+ }
+ }
+ }
+#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;
+}
+
+static void usage(int err)
+{
+ fprintf(stderr,
+ "Usage: %s [-vfhu] [-z level] intree outtree\n",
+ program);
+ exit(err);
+}
+
+int main(int argc, char *argv[])
+{
+ const char *in, *out;
+ struct stat st;
+ struct utimbuf ut;
+ int opt, err;
+ munger_func munger = block_compress_file;
+
+ program = argv[0];
+
+ while ( (opt = getopt(argc, argv, "vfz:hu")) != EOF ) {
+ switch(opt) {
+ case 'f':
+ force = 1; /* Always compress */
+ break;
+ case 'z':
+ if ( optarg[0] < '0' || optarg[0] > '9' || optarg[1] ) {
+ fprintf(stderr, "%s: invalid compression level: %s\n",
+ program, optarg);
+ exit(1);
+ } else {
+ level = optarg[0] - '0';
+ }
+ break;
+ case 'h':
+ usage(0);
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ case 'u':
+ munger = block_uncompress_file;
+ break;
+ default:
+ usage(1);
+ break;
+ }
+ }
+
+ if ( (argc-optind) != 2 )
+ usage(1);
+
+ in = argv[optind]; /* Input tree */
+ out = argv[optind+1]; /* Output tree */
+
+ umask(077);
+
+ /* Special case: we use stat() for the root, not lstat() */
+ if ( stat(in, &st) ) {
+ fprintf(stderr, "%s: %s: %s\n", program, in, strerror(errno));
+ exit(1);
+ }
+ if ( !S_ISDIR(st.st_mode) ) {
+ fprintf(stderr, "%s: %s: Not a directory\n", program, in);
+ }
+
+ err = munge_tree(in, out, munger);
+
+ if ( err )
+ exit(err);
+
+ chown(out, st.st_uid, st.st_gid);
+ chmod(out, st.st_mode);
+ ut.actime = st.st_atime;
+ ut.modtime = st.st_mtime;
+ utime(out, &ut);
+}
diff --git a/zisofs.magic b/zisofs.magic
new file mode 100644
index 0000000..9aeb61e
--- /dev/null
+++ b/zisofs.magic
@@ -0,0 +1,8 @@
+
+#------------------------------------------------------------------------------
+# zisofs: file(1) magic for zisofs/RockRidge compressed files
+#
+# from H. Peter Anvin <hpa@zytor.com> May 4, 2001
+#
+0 string \x37\xE4\x53\x96\xC9\xDB\xD6\x07 zisofs/Rockridge compressed file
+>8 lelong x - %d bytes