diff options
author | H. Peter Anvin <hpa@zytor.com> | 2001-05-05 02:44:25 +0000 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2001-05-05 02:44:25 +0000 |
commit | 045d67690f7de0583936cb2643985c98c2498658 (patch) | |
tree | 6699758badc6f80ed496d766645f7aea9d930015 | |
download | zisofs-tools-045d67690f7de0583936cb2643985c98c2498658.tar.gz |
Check in files corresponding to zisofs-tools 0.04zisofs-tools-0.04
-rw-r--r-- | Makefile | 34 | ||||
-rw-r--r-- | README | 30 | ||||
-rw-r--r-- | cdrtools-1.10-zisofs.diff | 158 | ||||
-rw-r--r-- | mkzftree.c | 756 | ||||
-rw-r--r-- | zisofs.magic | 8 |
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) + @@ -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 |