Blame 3rd-party/zisofs_tools/mkzftree.c

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
}