Imported Upstream version 1.1.11
[platform/upstream/cdrkit.git] / 3rd-party / zisofs_tools / mkzftree.c
1 /*
2  * This file has been modified for the cdrkit suite.
3  *
4  * The behaviour and appearence of the program code below can differ to a major
5  * extent from the version distributed by the original author(s).
6  *
7  * For details, see Changelog file distributed with the cdrkit package. If you
8  * received this file from another source then ask the distributing person for
9  * a log of modifications.
10  *
11  */
12
13 /* $Id: mkzftree.c,v 1.18 2006/07/04 04:57:42 hpa Exp $ */
14 /* ----------------------------------------------------------------------- *
15  *   
16  *   Copyright 2001 H. Peter Anvin - All Rights Reserved
17  *
18  *   This program is free software; you can redistribute it and/or modify
19  *   it under the terms of the GNU General Public License as published by
20  *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
21  *   USA; either version 2 of the License, or (at your option) any later
22  *   version; incorporated herein by reference.
23  *
24  * ----------------------------------------------------------------------- */
25
26 /*
27  * mkzffile.c
28  *
29  *      - Generate block-compression of files for use with
30  *        the "ZF" extension to the iso9660/RockRidge filesystem.
31  *
32  *        The file compression technique used is the "deflate"
33  *        algorithm used by the zlib library; each block must have a
34  *        valid (12-byte) zlib header.  In addition, the file itself
35  *        has the following structure:
36  *
37  *        Byte offset   iso9660 type    Contents
38  *          0           (8 bytes)       Magic number (37 E4 53 96 C9 DB D6 07)
39  *          8           7.3.1           Uncompressed file size
40  *         12           7.1.1           header_size >> 2 (currently 4)
41  *         13           7.1.1           log2(block_size)
42  *         14           (2 bytes)       Reserved, must be zero
43  *
44  * The header may get expanded in the future, at which point the
45  * header size field will be used to increase the space for the
46  * header.
47  *
48  * All implementations are required to support a block_size of 32K
49  * (byte 13 == 15).
50  *
51  * Note that bytes 12 and 13 and the uncompressed length are also
52  * present in the ZF record; THE TWO MUST BOTH BE CONSISTENT AND
53  * CORRECT.
54  *
55  * Given the uncompressed size, block_size, and header_size:
56  *
57  *     Nblocks := ceil(size/block_size)
58  *
59  * After the header follow (nblock+1) 32-bit pointers, recorded as
60  * iso9660 7.3.1 (littleendian); each indicate the byte offset (from
61  * the start of the file) to one block and the first byte beyond the
62  * end of the previous block; the first pointer thus point to the
63  * start of the data area and the last pointer to the first byte
64  * beyond it:
65  *
66  *     block_no := floor(byte_offset/block_size)
67  *
68  *     block_start := read_pointer_731( (header_size+block_no)*4 )
69  *     block_end   := read_pointer_731( (header_size+block_no+1)*4 )
70  *
71  * The block data is compressed according to "zlib".
72  */
73
74 #include "mkzftree.h"           /* Must be included first! */
75
76 #include <errno.h>
77 #include <stdlib.h>
78 #include <string.h>
79 #include <stdio.h>
80 #include <unistd.h>
81 #include <limits.h>
82 #include <sys/stat.h>
83 #include <sys/types.h>
84 #include <sys/time.h>
85
86 #ifdef HAVE_GETOPT_H
87 #include <getopt.h>
88 #endif
89
90 #include "version.h"
91
92 /* Command line options */
93 struct cmdline_options opt = {
94   0,                            /* Force compression */
95   9,                            /* Compression level */
96   0,                            /* Parallelism (0 = strictly serial) */
97   0,                            /* One filesystem only */
98   0,                            /* One directory only */
99   1,                            /* Create stub directories */
100   0,                            /* Root may be a file */
101   0,                            /* Be paranoid about metadata */
102   default_verbosity,            /* Default verbosity */
103   block_compress_file           /* Default transformation function */
104 };
105
106 /* Program name */
107 const char *program;
108
109 /* Long options */
110 #define OPTSTRING "fz:up:xXC:lLFvqV:hw"
111 #ifdef HAVE_GETOPT_LONG
112 const struct option long_options[] = {
113   { "force",                 0,  0,  'f' },
114   { "level",                 1,  0,  'z' },
115   { "uncompress",            0,  0,  'u' },
116   { "parallelism",           1,  0,  'p' },
117   { "one-filesystem",        0,  0,  'x' },
118   { "strict-one-filesystem", 0,  0,  'X' },
119   { "crib-tree",             1,  0,  'C' },
120   { "local",                 0,  0,  'l' },
121   { "strict-local",          0,  0,  'L' },
122   { "file",                  0,  0,  'F' },
123   { "verbose",               0,  0,  'v' },
124   { "quiet",                 0,  0,  'q' },
125   { "verbosity",             1,  0,  'V' },
126   { "help",                  0,  0,  'h' },
127   { "version",               0,  0,  'w' },
128   { 0, 0, 0, 0 }
129 };
130 #define LO(X) X
131 #else
132 #define getopt_long(C,V,O,L,I) getopt(C,V,O)
133 #define LO(X)
134 #endif
135   
136 static void usage(enum verbosity level, int err)
137 {
138   message(level,
139           "zisofs-tools " ZISOFS_TOOLS_VERSION "\n"
140           "Usage: %s [options] intree outtree\n"
141           LO("  --force                ")"  -f    Always compress, even if result is larger\n"
142           LO("  --level #              ")"  -z #  Set compression level (1-9)\n"
143           LO("  --uncompress           ")"  -u    Uncompress an already compressed tree\n"
144           LO("  --parallelism #        ")"  -p #  Process up to # files in parallel\n"
145           LO("  --one-filesystem       ")"  -x    Do not cross filesystem boundaries\n"
146           LO("  --strict-one-filesystem")"  -X    Same as -x, but don't create stubs dirs\n"
147           LO("  --crib-tree            ")"  -C    Steal \"crib\" files from an old tree\n"
148           LO("  --local                ")"  -l    Do not recurse into subdirectoires\n"
149           LO("  --strict-local         ")"  -L    Same as -l, but don't create stubs dirs\n"
150           LO("  --file                 ")"  -F    Operate possibly on a single file\n"
151           LO("  --sloppy               ")"  -s    Don't abort if metadata cannot be set\n"
152           LO("  --verbose              ")"  -v    Increase message verbosity\n"
153           LO("  --verbosity #          ")"  -V #  Set message verbosity to # (default = %d)\n"
154           LO("  --quiet                ")"  -q    No messages, not even errors (-V 0)\n"
155           LO("  --help                 ")"  -h    Display this message\n"
156           LO("  --version              ")"  -w    Display the program version\n"
157           ,program, (int)default_verbosity);
158   exit(err);
159 }
160
161 static int opt_atoi(const char *str)
162 {
163   char *endptr;
164   long out;
165
166   out = strtol(str, &endptr, 10);
167   if ( *endptr )
168     usage(vl_error, EX_USAGE);
169
170   return (int)out;
171 }
172
173
174 int main(int argc, char *argv[])
175 {
176   const char *in, *out, *crib = NULL;
177   struct stat st;
178   int optch, err;
179
180   program = argv[0];
181
182   while ( (optch = getopt_long(argc, argv, OPTSTRING, long_options, NULL))
183           != EOF ) {
184     switch(optch) {
185     case 'f':
186       opt.force = 1;            /* Always compress */
187       break;
188     case 'z':
189       opt.level = opt_atoi(optarg);
190       if ( opt.level < 1 || opt.level > 9 ) {
191         message(vl_error, "%s: invalid compression level: %d\n",
192                 program, optarg);
193         exit(EX_USAGE);
194       }
195       break;
196     case 'v':
197       opt.verbosity++;
198       break;
199     case 'V':
200       opt.verbosity = opt_atoi(optarg);
201       break;
202     case 'q':
203       opt.verbosity = vl_quiet;
204       break;
205     case 'u':
206       opt.munger = block_uncompress_file;
207       break;
208     case 'C':
209       crib = optarg;
210       break;
211     case 'p':
212       opt.parallel = opt_atoi(optarg);
213       break;
214     case 'x':
215       opt.onefs = 1;  opt.do_mkdir = 1;
216       break;
217     case 'l':
218       opt.onedir = 1; opt.do_mkdir = 1;
219       break;
220     case 'X':
221       opt.onefs = 1;  opt.do_mkdir = 0;
222       break;
223     case 'L':
224       opt.onedir = 1; opt.do_mkdir = 0;
225       break;
226     case 'F':
227       opt.file_root = 1;
228       break;
229     case 's':
230       opt.sloppy = 1;
231       break;
232     case 'h':
233       usage(vl_quiet, 0);
234       break;
235     case 'w':
236       message(vl_quiet, "zisofs-tools " ZISOFS_TOOLS_VERSION "\n");
237       exit(0);
238     default:
239       usage(vl_error, EX_USAGE);
240       break;
241     }
242   }
243
244   if ( (argc-optind) != 2 )
245     usage(vl_error, EX_USAGE);
246
247   in  = argv[optind];           /* Input tree */
248   out = argv[optind+1];         /* Output tree */
249
250   umask(077);
251
252   if ( opt.file_root ) {
253     if ( lstat(in, &st) ) {
254       message(vl_error, "%s: %s: %s\n", program, in, strerror(errno));
255       exit(EX_NOINPUT);
256     }
257
258     err = munge_entry(in, out, crib, NULL);
259   } else {
260     /* Special case: we use stat() for the root, not lstat() */
261     if ( stat(in, &st) ) {
262       message(vl_error, "%s: %s: %s\n", program, in, strerror(errno));
263       exit(EX_NOINPUT);
264     }
265     if ( !S_ISDIR(st.st_mode) ) {
266       message(vl_error, "%s: %s: Not a directory\n", program, in);
267       exit(EX_DATAERR);
268     }
269     
270     err = munge_tree(in, out, crib);
271   }    
272
273   wait_for_all_workers();
274     
275   if ( err )
276     exit(err);
277
278   if ( !opt.file_root ) {
279     if ( chown(out, st.st_uid, st.st_gid) && !opt.sloppy ) {
280       message(vl_error, "%s: %s: %s", program, out, strerror(errno));
281       err = EX_CANTCREAT;
282     }
283     if ( chmod(out, st.st_mode) && !opt.sloppy && !err ) {
284       message(vl_error, "%s: %s: %s", program, out, strerror(errno));
285       err = EX_CANTCREAT;
286     }
287     if ( copytime(out, &st) && !opt.sloppy && !err ) {
288       message(vl_error, "%s: %s: %s", program, out, strerror(errno));
289       err = EX_CANTCREAT;
290     }
291   }
292
293   return err;
294 }