|
Packit |
45fded |
/*
|
|
Packit |
45fded |
* This file has been modified for the cdrkit suite.
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* The behaviour and appearence of the program code below can differ to a major
|
|
Packit |
45fded |
* extent from the version distributed by the original author(s).
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* For details, see Changelog file distributed with the cdrkit package. If you
|
|
Packit |
45fded |
* received this file from another source then ask the distributing person for
|
|
Packit |
45fded |
* a log of modifications.
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
*/
|
|
Packit |
45fded |
|
|
Packit |
45fded |
/* $Id: mkzftree.c,v 1.18 2006/07/04 04:57:42 hpa Exp $ */
|
|
Packit |
45fded |
/* ----------------------------------------------------------------------- *
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* Copyright 2001 H. Peter Anvin - All Rights Reserved
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
45fded |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
45fded |
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
|
|
Packit |
45fded |
* USA; either version 2 of the License, or (at your option) any later
|
|
Packit |
45fded |
* version; incorporated herein by reference.
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* ----------------------------------------------------------------------- */
|
|
Packit |
45fded |
|
|
Packit |
45fded |
/*
|
|
Packit |
45fded |
* mkzffile.c
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* - Generate block-compression of files for use with
|
|
Packit |
45fded |
* the "ZF" extension to the iso9660/RockRidge filesystem.
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* The file compression technique used is the "deflate"
|
|
Packit |
45fded |
* algorithm used by the zlib library; each block must have a
|
|
Packit |
45fded |
* valid (12-byte) zlib header. In addition, the file itself
|
|
Packit |
45fded |
* has the following structure:
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* Byte offset iso9660 type Contents
|
|
Packit |
45fded |
* 0 (8 bytes) Magic number (37 E4 53 96 C9 DB D6 07)
|
|
Packit |
45fded |
* 8 7.3.1 Uncompressed file size
|
|
Packit |
45fded |
* 12 7.1.1 header_size >> 2 (currently 4)
|
|
Packit |
45fded |
* 13 7.1.1 log2(block_size)
|
|
Packit |
45fded |
* 14 (2 bytes) Reserved, must be zero
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* The header may get expanded in the future, at which point the
|
|
Packit |
45fded |
* header size field will be used to increase the space for the
|
|
Packit |
45fded |
* header.
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* All implementations are required to support a block_size of 32K
|
|
Packit |
45fded |
* (byte 13 == 15).
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* Note that bytes 12 and 13 and the uncompressed length are also
|
|
Packit |
45fded |
* present in the ZF record; THE TWO MUST BOTH BE CONSISTENT AND
|
|
Packit |
45fded |
* CORRECT.
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* Given the uncompressed size, block_size, and header_size:
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* Nblocks := ceil(size/block_size)
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* After the header follow (nblock+1) 32-bit pointers, recorded as
|
|
Packit |
45fded |
* iso9660 7.3.1 (littleendian); each indicate the byte offset (from
|
|
Packit |
45fded |
* the start of the file) to one block and the first byte beyond the
|
|
Packit |
45fded |
* end of the previous block; the first pointer thus point to the
|
|
Packit |
45fded |
* start of the data area and the last pointer to the first byte
|
|
Packit |
45fded |
* beyond it:
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* block_no := floor(byte_offset/block_size)
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* block_start := read_pointer_731( (header_size+block_no)*4 )
|
|
Packit |
45fded |
* block_end := read_pointer_731( (header_size+block_no+1)*4 )
|
|
Packit |
45fded |
*
|
|
Packit |
45fded |
* The block data is compressed according to "zlib".
|
|
Packit |
45fded |
*/
|
|
Packit |
45fded |
|
|
Packit |
45fded |
#include "mkzftree.h" /* Must be included first! */
|
|
Packit |
45fded |
|
|
Packit |
45fded |
#include <errno.h>
|
|
Packit |
45fded |
#include <stdlib.h>
|
|
Packit |
45fded |
#include <string.h>
|
|
Packit |
45fded |
#include <stdio.h>
|
|
Packit |
45fded |
#include <unistd.h>
|
|
Packit |
45fded |
#include <limits.h>
|
|
Packit |
45fded |
#include <sys/stat.h>
|
|
Packit |
45fded |
#include <sys/types.h>
|
|
Packit |
45fded |
#include <sys/time.h>
|
|
Packit |
45fded |
|
|
Packit |
45fded |
#ifdef HAVE_GETOPT_H
|
|
Packit |
45fded |
#include <getopt.h>
|
|
Packit |
45fded |
#endif
|
|
Packit |
45fded |
|
|
Packit |
45fded |
#include "version.h"
|
|
Packit |
45fded |
|
|
Packit |
45fded |
/* Command line options */
|
|
Packit |
45fded |
struct cmdline_options opt = {
|
|
Packit |
45fded |
0, /* Force compression */
|
|
Packit |
45fded |
9, /* Compression level */
|
|
Packit |
45fded |
0, /* Parallelism (0 = strictly serial) */
|
|
Packit |
45fded |
0, /* One filesystem only */
|
|
Packit |
45fded |
0, /* One directory only */
|
|
Packit |
45fded |
1, /* Create stub directories */
|
|
Packit |
45fded |
0, /* Root may be a file */
|
|
Packit |
45fded |
0, /* Be paranoid about metadata */
|
|
Packit |
45fded |
default_verbosity, /* Default verbosity */
|
|
Packit |
45fded |
block_compress_file /* Default transformation function */
|
|
Packit |
45fded |
};
|
|
Packit |
45fded |
|
|
Packit |
45fded |
/* Program name */
|
|
Packit |
45fded |
const char *program;
|
|
Packit |
45fded |
|
|
Packit |
45fded |
/* Long options */
|
|
Packit |
45fded |
#define OPTSTRING "fz:up:xXC:lLFvqV:hw"
|
|
Packit |
45fded |
#ifdef HAVE_GETOPT_LONG
|
|
Packit |
45fded |
const struct option long_options[] = {
|
|
Packit |
45fded |
{ "force", 0, 0, 'f' },
|
|
Packit |
45fded |
{ "level", 1, 0, 'z' },
|
|
Packit |
45fded |
{ "uncompress", 0, 0, 'u' },
|
|
Packit |
45fded |
{ "parallelism", 1, 0, 'p' },
|
|
Packit |
45fded |
{ "one-filesystem", 0, 0, 'x' },
|
|
Packit |
45fded |
{ "strict-one-filesystem", 0, 0, 'X' },
|
|
Packit |
45fded |
{ "crib-tree", 1, 0, 'C' },
|
|
Packit |
45fded |
{ "local", 0, 0, 'l' },
|
|
Packit |
45fded |
{ "strict-local", 0, 0, 'L' },
|
|
Packit |
45fded |
{ "file", 0, 0, 'F' },
|
|
Packit |
45fded |
{ "verbose", 0, 0, 'v' },
|
|
Packit |
45fded |
{ "quiet", 0, 0, 'q' },
|
|
Packit |
45fded |
{ "verbosity", 1, 0, 'V' },
|
|
Packit |
45fded |
{ "help", 0, 0, 'h' },
|
|
Packit |
45fded |
{ "version", 0, 0, 'w' },
|
|
Packit |
45fded |
{ 0, 0, 0, 0 }
|
|
Packit |
45fded |
};
|
|
Packit |
45fded |
#define LO(X) X
|
|
Packit |
45fded |
#else
|
|
Packit |
45fded |
#define getopt_long(C,V,O,L,I) getopt(C,V,O)
|
|
Packit |
45fded |
#define LO(X)
|
|
Packit |
45fded |
#endif
|
|
Packit |
45fded |
|
|
Packit |
45fded |
static void usage(enum verbosity level, int err)
|
|
Packit |
45fded |
{
|
|
Packit |
45fded |
message(level,
|
|
Packit |
45fded |
"zisofs-tools " ZISOFS_TOOLS_VERSION "\n"
|
|
Packit |
45fded |
"Usage: %s [options] intree outtree\n"
|
|
Packit |
45fded |
LO(" --force ")" -f Always compress, even if result is larger\n"
|
|
Packit |
45fded |
LO(" --level # ")" -z # Set compression level (1-9)\n"
|
|
Packit |
45fded |
LO(" --uncompress ")" -u Uncompress an already compressed tree\n"
|
|
Packit |
45fded |
LO(" --parallelism # ")" -p # Process up to # files in parallel\n"
|
|
Packit |
45fded |
LO(" --one-filesystem ")" -x Do not cross filesystem boundaries\n"
|
|
Packit |
45fded |
LO(" --strict-one-filesystem")" -X Same as -x, but don't create stubs dirs\n"
|
|
Packit |
45fded |
LO(" --crib-tree ")" -C Steal \"crib\" files from an old tree\n"
|
|
Packit |
45fded |
LO(" --local ")" -l Do not recurse into subdirectoires\n"
|
|
Packit |
45fded |
LO(" --strict-local ")" -L Same as -l, but don't create stubs dirs\n"
|
|
Packit |
45fded |
LO(" --file ")" -F Operate possibly on a single file\n"
|
|
Packit |
45fded |
LO(" --sloppy ")" -s Don't abort if metadata cannot be set\n"
|
|
Packit |
45fded |
LO(" --verbose ")" -v Increase message verbosity\n"
|
|
Packit |
45fded |
LO(" --verbosity # ")" -V # Set message verbosity to # (default = %d)\n"
|
|
Packit |
45fded |
LO(" --quiet ")" -q No messages, not even errors (-V 0)\n"
|
|
Packit |
45fded |
LO(" --help ")" -h Display this message\n"
|
|
Packit |
45fded |
LO(" --version ")" -w Display the program version\n"
|
|
Packit |
45fded |
,program, (int)default_verbosity);
|
|
Packit |
45fded |
exit(err);
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
|
|
Packit |
45fded |
static int opt_atoi(const char *str)
|
|
Packit |
45fded |
{
|
|
Packit |
45fded |
char *endptr;
|
|
Packit |
45fded |
long out;
|
|
Packit |
45fded |
|
|
Packit |
45fded |
out = strtol(str, &endptr, 10);
|
|
Packit |
45fded |
if ( *endptr )
|
|
Packit |
45fded |
usage(vl_error, EX_USAGE);
|
|
Packit |
45fded |
|
|
Packit |
45fded |
return (int)out;
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
|
|
Packit |
45fded |
|
|
Packit |
45fded |
int main(int argc, char *argv[])
|
|
Packit |
45fded |
{
|
|
Packit |
45fded |
const char *in, *out, *crib = NULL;
|
|
Packit |
45fded |
struct stat st;
|
|
Packit |
45fded |
int optch, err;
|
|
Packit |
45fded |
|
|
Packit |
45fded |
program = argv[0];
|
|
Packit |
45fded |
|
|
Packit |
45fded |
while ( (optch = getopt_long(argc, argv, OPTSTRING, long_options, NULL))
|
|
Packit |
45fded |
!= EOF ) {
|
|
Packit |
45fded |
switch(optch) {
|
|
Packit |
45fded |
case 'f':
|
|
Packit |
45fded |
opt.force = 1; /* Always compress */
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'z':
|
|
Packit |
45fded |
opt.level = opt_atoi(optarg);
|
|
Packit |
45fded |
if ( opt.level < 1 || opt.level > 9 ) {
|
|
Packit |
45fded |
message(vl_error, "%s: invalid compression level: %d\n",
|
|
Packit |
45fded |
program, optarg);
|
|
Packit |
45fded |
exit(EX_USAGE);
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'v':
|
|
Packit |
45fded |
opt.verbosity++;
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'V':
|
|
Packit |
45fded |
opt.verbosity = opt_atoi(optarg);
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'q':
|
|
Packit |
45fded |
opt.verbosity = vl_quiet;
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'u':
|
|
Packit |
45fded |
opt.munger = block_uncompress_file;
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'C':
|
|
Packit |
45fded |
crib = optarg;
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'p':
|
|
Packit |
45fded |
opt.parallel = opt_atoi(optarg);
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'x':
|
|
Packit |
45fded |
opt.onefs = 1; opt.do_mkdir = 1;
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'l':
|
|
Packit |
45fded |
opt.onedir = 1; opt.do_mkdir = 1;
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'X':
|
|
Packit |
45fded |
opt.onefs = 1; opt.do_mkdir = 0;
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'L':
|
|
Packit |
45fded |
opt.onedir = 1; opt.do_mkdir = 0;
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'F':
|
|
Packit |
45fded |
opt.file_root = 1;
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 's':
|
|
Packit |
45fded |
opt.sloppy = 1;
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'h':
|
|
Packit |
45fded |
usage(vl_quiet, 0);
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
case 'w':
|
|
Packit |
45fded |
message(vl_quiet, "zisofs-tools " ZISOFS_TOOLS_VERSION "\n");
|
|
Packit |
45fded |
exit(0);
|
|
Packit |
45fded |
default:
|
|
Packit |
45fded |
usage(vl_error, EX_USAGE);
|
|
Packit |
45fded |
break;
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
|
|
Packit |
45fded |
if ( (argc-optind) != 2 )
|
|
Packit |
45fded |
usage(vl_error, EX_USAGE);
|
|
Packit |
45fded |
|
|
Packit |
45fded |
in = argv[optind]; /* Input tree */
|
|
Packit |
45fded |
out = argv[optind+1]; /* Output tree */
|
|
Packit |
45fded |
|
|
Packit |
45fded |
umask(077);
|
|
Packit |
45fded |
|
|
Packit |
45fded |
if ( opt.file_root ) {
|
|
Packit |
45fded |
if ( lstat(in, &st) ) {
|
|
Packit |
45fded |
message(vl_error, "%s: %s: %s\n", program, in, strerror(errno));
|
|
Packit |
45fded |
exit(EX_NOINPUT);
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
|
|
Packit |
45fded |
err = munge_entry(in, out, crib, NULL);
|
|
Packit |
45fded |
} else {
|
|
Packit |
45fded |
/* Special case: we use stat() for the root, not lstat() */
|
|
Packit |
45fded |
if ( stat(in, &st) ) {
|
|
Packit |
45fded |
message(vl_error, "%s: %s: %s\n", program, in, strerror(errno));
|
|
Packit |
45fded |
exit(EX_NOINPUT);
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
if ( !S_ISDIR(st.st_mode) ) {
|
|
Packit |
45fded |
message(vl_error, "%s: %s: Not a directory\n", program, in);
|
|
Packit |
45fded |
exit(EX_DATAERR);
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
|
|
Packit |
45fded |
err = munge_tree(in, out, crib);
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
|
|
Packit |
45fded |
wait_for_all_workers();
|
|
Packit |
45fded |
|
|
Packit |
45fded |
if ( err )
|
|
Packit |
45fded |
exit(err);
|
|
Packit |
45fded |
|
|
Packit |
45fded |
if ( !opt.file_root ) {
|
|
Packit |
45fded |
if ( chown(out, st.st_uid, st.st_gid) && !opt.sloppy ) {
|
|
Packit |
45fded |
message(vl_error, "%s: %s: %s", program, out, strerror(errno));
|
|
Packit |
45fded |
err = EX_CANTCREAT;
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
if ( chmod(out, st.st_mode) && !opt.sloppy && !err ) {
|
|
Packit |
45fded |
message(vl_error, "%s: %s: %s", program, out, strerror(errno));
|
|
Packit |
45fded |
err = EX_CANTCREAT;
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
if ( copytime(out, &st) && !opt.sloppy && !err ) {
|
|
Packit |
45fded |
message(vl_error, "%s: %s: %s", program, out, strerror(errno));
|
|
Packit |
45fded |
err = EX_CANTCREAT;
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
}
|
|
Packit |
45fded |
|
|
Packit |
45fded |
return err;
|
|
Packit |
45fded |
}
|