diff --git a/Makefile.am b/Makefile.am index dc7508d..13b16b1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -31,6 +31,9 @@ lrztar_SCRIPTS = lrztar bin_PROGRAMS = lrzip lrzip_SOURCES = \ zpipe.cpp \ + liblrzip.h \ + lrzip.c \ + lrzip.h \ main.c \ rzip.h \ rzip.c \ diff --git a/configure.ac b/configure.ac index e691c4a..9898114 100644 --- a/configure.ac +++ b/configure.ac @@ -70,7 +70,7 @@ AC_CHECK_HEADERS(sys/param.h ctype.h sys/wait.h sys/ioctl.h errno.h) AC_TYPE_OFF_T AC_TYPE_SIZE_T - +AC_C___ATTRIBUTE__ AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(short) diff --git a/liblrzip.h b/liblrzip.h new file mode 100644 index 0000000..342149b --- /dev/null +++ b/liblrzip.h @@ -0,0 +1,70 @@ +/* + Copyright (C) 2006-2011 Con Kolivas + Copyright (C) 2011 Peter Hyman + Copyright (C) 1998-2003 Andrew Tridgell + + 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; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#if !defined(LIBLRZIP_H) && !defined(MAIN_C) +#define LIBLRZIP_H + +#define FLAG_VERBOSE (FLAG_VERBOSITY | FLAG_VERBOSITY_MAX) +#define FLAG_NOT_LZMA (FLAG_NO_COMPRESS | FLAG_LZO_COMPRESS | FLAG_BZIP2_COMPRESS | FLAG_ZLIB_COMPRESS | FLAG_ZPAQ_COMPRESS) +#define LZMA_COMPRESS (!(control->flags & FLAG_NOT_LZMA)) + +#define SHOW_PROGRESS (control->flags & FLAG_SHOW_PROGRESS) +#define KEEP_FILES (control->flags & FLAG_KEEP_FILES) +#define TEST_ONLY (control->flags & FLAG_TEST_ONLY) +#define FORCE_REPLACE (control->flags & FLAG_FORCE_REPLACE) +#define DECOMPRESS (control->flags & FLAG_DECOMPRESS) +#define NO_COMPRESS (control->flags & FLAG_NO_COMPRESS) +#define LZO_COMPRESS (control->flags & FLAG_LZO_COMPRESS) +#define BZIP2_COMPRESS (control->flags & FLAG_BZIP2_COMPRESS) +#define ZLIB_COMPRESS (control->flags & FLAG_ZLIB_COMPRESS) +#define ZPAQ_COMPRESS (control->flags & FLAG_ZPAQ_COMPRESS) +#define VERBOSE (control->flags & FLAG_VERBOSE) +#define VERBOSITY (control->flags & FLAG_VERBOSITY) +#define MAX_VERBOSE (control->flags & FLAG_VERBOSITY_MAX) +#define STDIN (control->flags & FLAG_STDIN) +#define STDOUT (control->flags & FLAG_STDOUT) +#define INFO (control->flags & FLAG_INFO) +#define UNLIMITED (control->flags & FLAG_UNLIMITED) +#define HASH_CHECK (control->flags & FLAG_HASH) +#define HAS_MD5 (control->flags & FLAG_MD5) +#define CHECK_FILE (control->flags & FLAG_CHECK) +#define KEEP_BROKEN (control->flags & FLAG_KEEP_BROKEN) +#define LZO_TEST (control->flags & FLAG_THRESHOLD) + +#define print_output(format, args...) do {\ + fprintf(control->msgout, format, ##args); \ + fflush(control->msgout); \ +} while (0) + +#define print_progress(format, args...) do {\ + if (SHOW_PROGRESS) \ + print_output(format, ##args); \ +} while (0) + +#define print_verbose(format, args...) do {\ + if (VERBOSE) \ + print_output(format, ##args); \ +} while (0) + +#define print_maxverbose(format, args...) do {\ + if (MAX_VERBOSE) \ + print_output(format, ##args); \ +} while (0) + +#endif diff --git a/lrzip.c b/lrzip.c new file mode 100644 index 0000000..2ff2569 --- /dev/null +++ b/lrzip.c @@ -0,0 +1,668 @@ +/* + Copyright (C) 2006-2011 Con Kolivas + Copyright (C) 2011 Peter Hyman + Copyright (C) 1998-2003 Andrew Tridgell + + 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; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "rzip.h" +#include "liblrzip.h" /* flag defines */ + +void write_magic(rzip_control *control, int fd_in, int fd_out) +{ + struct stat st; + char magic[24]; + int i; + + memset(magic, 0, sizeof(magic)); + strcpy(magic, "LRZI"); + magic[4] = LRZIP_MAJOR_VERSION; + magic[5] = LRZIP_MINOR_VERSION; + + if (unlikely(fstat(fd_in, &st))) + fatal("bad magic file descriptor!?\n"); + + memcpy(&magic[6], &control->st_size, 8); + + /* save LZMA compression flags */ + if (LZMA_COMPRESS) { + for (i = 0; i < 5; i++) + magic[i + 16] = (char)control->lzma_properties[i]; + } + + /* This is a flag that the archive contains an md5 sum at the end + * which can be used as an integrity check instead of crc check. + * crc is still stored for compatibility with 0.5 versions. + */ + magic[21] = 1; + + if (unlikely(lseek(fd_out, 0, SEEK_SET))) + fatal("Failed to seek to BOF to write Magic Header\n"); + + if (unlikely(write(fd_out, magic, sizeof(magic)) != sizeof(magic))) + fatal("Failed to write magic header\n"); +} + +void read_magic(rzip_control *control, int fd_in, i64 *expected_size) +{ + char magic[24]; + uint32_t v; + int md5, i; + + if (unlikely(read(fd_in, magic, sizeof(magic)) != sizeof(magic))) + fatal("Failed to read magic header\n"); + + *expected_size = 0; + + if (unlikely(strncmp(magic, "LRZI", 4))) + failure("Not an lrzip file\n"); + + memcpy(&control->major_version, &magic[4], 1); + memcpy(&control->minor_version, &magic[5], 1); + + /* Support the convoluted way we described size in versions < 0.40 */ + if (control->major_version == 0 && control->minor_version < 4) { + memcpy(&v, &magic[6], 4); + *expected_size = ntohl(v); + memcpy(&v, &magic[10], 4); + *expected_size |= ((i64)ntohl(v)) << 32; + } else + memcpy(expected_size, &magic[6], 8); + + /* restore LZMA compression flags only if stored */ + if ((int) magic[16]) { + for (i = 0; i < 5; i++) + control->lzma_properties[i] = magic[i + 16]; + } + + /* Whether this archive contains md5 data at the end or not */ + md5 = magic[21]; + if (md5 == 1) + control->flags |= FLAG_MD5; + + print_verbose("Detected lrzip version %d.%d file.\n", control->major_version, control->minor_version); + if (control->major_version > LRZIP_MAJOR_VERSION || + (control->major_version == LRZIP_MAJOR_VERSION && control->minor_version > LRZIP_MINOR_VERSION)) + print_output("Attempting to work with file produced by newer lrzip version %d.%d file.\n", control->major_version, control->minor_version); +} + +/* preserve ownership and permissions where possible */ +void preserve_perms(rzip_control *control, int fd_in, int fd_out) +{ + struct stat st; + + if (unlikely(fstat(fd_in, &st))) + fatal("Failed to fstat input file\n"); + if (unlikely(fchmod(fd_out, (st.st_mode & 0777)))) + print_err("Warning, unable to set permissions on %s\n", control->outfile); + + /* chown fail is not fatal */ + if (unlikely(fchown(fd_out, st.st_uid, st.st_gid))) + print_err("Warning, unable to set owner on %s\n", control->outfile); +} + +/* Open a temporary outputfile to emulate stdout */ +int open_tmpoutfile(rzip_control *control) +{ + int fd_out; + + if (STDOUT) + print_verbose("Outputting to stdout.\n"); + if (control->tmpdir) { + control->outfile = realloc(NULL, strlen(control->tmpdir)+16); + if (unlikely(!control->outfile)) + fatal("Failed to allocate outfile name\n"); + strcpy(control->outfile, control->tmpdir); + strcat(control->outfile, "lrzipout.XXXXXX"); + } else { + control->outfile = realloc(NULL, 16); + if (unlikely(!control->outfile)) + fatal("Failed to allocate outfile name\n"); + strcpy(control->outfile, "lrzipout.XXXXXX"); + } + + fd_out = mkstemp(control->outfile); + if (unlikely(fd_out == -1)) + fatal("Failed to create out tmpfile: %s\n", strerror(errno)); + register_outfile(control->outfile, TEST_ONLY || STDOUT || !KEEP_BROKEN); + return fd_out; +} + +/* Dump temporary outputfile to perform stdout */ +void dump_tmpoutfile(rzip_control *control, int fd_out) +{ + FILE *tmpoutfp; + int tmpchar; + + print_progress("Dumping to stdout.\n"); + /* flush anything not yet in the temporary file */ + fsync(fd_out); + tmpoutfp = fdopen(fd_out, "r"); + if (unlikely(tmpoutfp == NULL)) + fatal("Failed to fdopen out tmpfile: %s\n", strerror(errno)); + rewind(tmpoutfp); + + while ((tmpchar = fgetc(tmpoutfp)) != EOF) + putchar(tmpchar); + + fflush(control->msgout); +} + +/* Open a temporary inputfile to perform stdin decompression */ +int open_tmpinfile(rzip_control *control) +{ + int fd_in; + + if (control->tmpdir) { + control->infile = malloc(strlen(control->tmpdir)+15); + if (unlikely(!control->infile)) + fatal("Failed to allocate infile name\n"); + strcpy(control->infile, control->tmpdir); + strcat(control->infile, "lrzipin.XXXXXX"); + } else { + control->infile = malloc(15); + if (unlikely(!control->infile)) + fatal("Failed to allocate infile name\n"); + strcpy(control->infile, "lrzipin.XXXXXX"); + } + + fd_in = mkstemp(control->infile); + if (unlikely(fd_in == -1)) + fatal("Failed to create in tmpfile: %s\n", strerror(errno)); + register_infile(control->infile, (DECOMPRESS || TEST_ONLY) && STDIN); + return fd_in; +} + +/* Read data from stdin into temporary inputfile */ +void read_tmpinfile(rzip_control *control, int fd_in) +{ + FILE *tmpinfp; + int tmpchar; + + if (control->flags & FLAG_SHOW_PROGRESS) + fprintf(control->msgout, "Copying from stdin.\n"); + tmpinfp = fdopen(fd_in, "w+"); + if (unlikely(tmpinfp == NULL)) + fatal("Failed to fdopen in tmpfile: %s\n", strerror(errno)); + + while ((tmpchar = getchar()) != EOF) + fputc(tmpchar, tmpinfp); + + fflush(tmpinfp); + rewind(tmpinfp); +} + +/* + decompress one file from the command line +*/ +void decompress_file(rzip_control *control) +{ + char *tmp, *tmpoutfile, *infilecopy = NULL; + int fd_in, fd_out = -1, fd_hist = -1; + i64 expected_size, free_space; + struct statvfs fbuf; + + if (!STDIN) { + if ((tmp = strrchr(control->infile, '.')) && strcmp(tmp,control->suffix)) { + /* make sure infile has an extension. If not, add it + * because manipulations may be made to input filename, set local ptr + */ + infilecopy = malloc(strlen(control->infile) + strlen(control->suffix) + 1); + if (unlikely(infilecopy == NULL)) + fatal("Failed to allocate memory for infile suffix\n"); + else { + strcpy(infilecopy, control->infile); + strcat(infilecopy, control->suffix); + } + } else + infilecopy = strdup(control->infile); + /* regardless, infilecopy has the input filename */ + } + + if (!STDOUT && !TEST_ONLY) { + /* if output name already set, use it */ + if (control->outname) { + control->outfile = strdup(control->outname); + } else { + /* default output name from infilecopy + * test if outdir specified. If so, strip path from filename of + * infilecopy, then remove suffix. + */ + if (control->outdir && (tmp = strrchr(infilecopy, '/'))) + tmpoutfile = strdup(tmp + 1); + else + tmpoutfile = strdup(infilecopy); + + /* remove suffix to make outfile name */ + if ((tmp = strrchr(tmpoutfile, '.')) && !strcmp(tmp, control->suffix)) + *tmp='\0'; + + control->outfile = malloc((control->outdir == NULL? 0: strlen(control->outdir)) + strlen(tmpoutfile) + 1); + if (unlikely(!control->outfile)) + fatal("Failed to allocate outfile name\n"); + + if (control->outdir) { /* prepend control->outdir */ + strcpy(control->outfile, control->outdir); + strcat(control->outfile, tmpoutfile); + } else + strcpy(control->outfile, tmpoutfile); + free(tmpoutfile); + } + + if (!STDOUT) + print_progress("Output filename is: %s...Decompressing...\n", control->outfile); + } + + if (STDIN) { + fd_in = open_tmpinfile(control); + read_tmpinfile(control, fd_in); + } else { + fd_in = open(infilecopy, O_RDONLY); + if (unlikely(fd_in == -1)) { + fatal("Failed to open %s: %s\n", + infilecopy, + strerror(errno)); + } + } + + if (!(TEST_ONLY | STDOUT)) { + if (FORCE_REPLACE) + fd_out = open(control->outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); + else + fd_out = open(control->outfile, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (unlikely(fd_out == -1)) { + /* We must ensure we don't delete a file that already + * exists just because we tried to create a new one */ + control->flags |= FLAG_KEEP_BROKEN; + fatal("Failed to create %s: %s\n", control->outfile, strerror(errno)); + } + + preserve_perms(control, fd_in, fd_out); + } else + fd_out = open_tmpoutfile(control); + control->fd_out = fd_out; + + /* Check if there's enough free space on the device chosen to fit the + * decompressed file. */ + if (unlikely(fstatvfs(fd_out, &fbuf))) + fatal("Failed to fstatvfs in decompress_file\n"); + free_space = fbuf.f_bsize * fbuf.f_bavail; + if (free_space < expected_size) { + if (FORCE_REPLACE) + print_err("Warning, inadequate free space detected, but attempting to decompress due to -f option being used.\n"); + else + failure("Inadequate free space to decompress file, use -f to override.\n"); + } + + fd_hist = open(control->outfile, O_RDONLY); + if (unlikely(fd_hist == -1)) + fatal("Failed to open history file %s\n", control->outfile); + + read_magic(control, fd_in, &expected_size); + if (NO_MD5) + print_verbose("Not performing MD5 hash check\n"); + if (HAS_MD5) + print_verbose("MD5 "); + else + print_verbose("CRC32 "); + print_verbose("being used for integrity testing.\n"); + + print_progress("Decompressing..."); + + runzip_fd(control, fd_in, fd_out, fd_hist, expected_size); + + if (STDOUT) + dump_tmpoutfile(control, fd_out); + + /* if we get here, no fatal errors during decompression */ + print_progress("\r"); + if (!(STDOUT | TEST_ONLY)) + print_output("Output filename is: %s: ", control->outfile); + print_progress("[OK] - %lld bytes \n", expected_size); + + if (unlikely(close(fd_hist) || close(fd_out))) + fatal("Failed to close files\n"); + + if (TEST_ONLY | STDOUT) { + /* Delete temporary files generated for testing or faking stdout */ + if (unlikely(unlink(control->outfile))) + fatal("Failed to unlink tmpfile: %s\n", strerror(errno)); + } + + close(fd_in); + + if (!(KEEP_FILES | TEST_ONLY) || STDIN) { + if (unlikely(unlink(control->infile))) + fatal("Failed to unlink %s: %s\n", infilecopy, strerror(errno)); + } + + free(control->outfile); + free(infilecopy); +} + +void get_header_info(rzip_control *control, int fd_in, uchar *ctype, i64 *c_len, i64 *u_len, i64 *last_head) +{ + if (unlikely(read(fd_in, ctype, 1) != 1)) + fatal("Failed to read in get_header_info\n"); + + if (control->major_version == 0 && control->minor_version < 4) { + u32 c_len32, u_len32, last_head32; + + if (unlikely(read(fd_in, &c_len32, 4) != 4)) + fatal("Failed to read in get_header_info"); + if (unlikely(read(fd_in, &u_len32, 4) != 4)) + fatal("Failed to read in get_header_info"); + if (unlikely(read(fd_in, &last_head32, 4) != 4)) + fatal("Failed to read in get_header_info"); + *c_len = c_len32; + *u_len = u_len32; + *last_head = last_head32; + } else { + if (unlikely(read(fd_in, c_len, 8) != 8)) + fatal("Failed to read in get_header_info"); + if (unlikely(read(fd_in, u_len, 8) != 8)) + fatal("Failed to read in get_header_info"); + if (unlikely(read(fd_in, last_head, 8) != 8)) + fatal("Failed to read_i64 in get_header_info"); + } +} + +void get_fileinfo(rzip_control *control) +{ + i64 expected_size, infile_size; + int seekspot, fd_in; + long double cratio; + uchar ctype = 0; + struct stat st; + + char *tmp, *infilecopy = NULL; + + if (!STDIN) { + if ((tmp = strrchr(control->infile, '.')) && strcmp(tmp,control->suffix)) { + infilecopy = malloc(strlen(control->infile) + strlen(control->suffix) + 1); + if (unlikely(infilecopy == NULL)) + fatal("Failed to allocate memory for infile suffix\n"); + else { + strcpy(infilecopy, control->infile); + strcat(infilecopy, control->suffix); + } + } else + infilecopy = strdup(control->infile); + } + + if (STDIN) + fd_in = 0; + else { + fd_in = open(infilecopy, O_RDONLY); + if (unlikely(fd_in == -1)) + fatal("Failed to open %s: %s\n", infilecopy, strerror(errno)); + } + + /* Get file size */ + if (unlikely(fstat(fd_in, &st))) + fatal("bad magic file descriptor!?\n"); + memcpy(&infile_size, &st.st_size, 8); + + /* Get decompressed size */ + read_magic(control, fd_in, &expected_size); + + /* Version < 0.4 had different file format */ + if (control->major_version == 0 && control->minor_version < 4) + seekspot = 50; + else if (control->major_version == 0 && control->minor_version == 4) + seekspot = 74; + else + seekspot = 75; + if (unlikely(lseek(fd_in, seekspot, SEEK_SET) == -1)) + fatal("Failed to lseek in get_fileinfo: %s\n", strerror(errno)); + + /* Read the compression type of the first block. It's possible that + not all blocks are compressed so this may not be accurate. + */ + if (unlikely(read(fd_in, &ctype, 1) != 1)) + fatal("Failed to read in get_fileinfo\n"); + + cratio = (long double)expected_size / (long double)infile_size; + + print_output("%s:\nlrzip version: %d.%d file\n", infilecopy, control->major_version, control->minor_version); + print_output("Compression: "); + if (ctype == CTYPE_NONE) + print_output("rzip alone\n"); + else if (ctype == CTYPE_BZIP2) + print_output("rzip + bzip2\n"); + else if (ctype == CTYPE_LZO) + print_output("rzip + lzo\n"); + else if (ctype == CTYPE_LZMA) + print_output("rzip + lzma\n"); + else if (ctype == CTYPE_GZIP) + print_output("rzip + gzip\n"); + else if (ctype == CTYPE_ZPAQ) + print_output("rzip + zpaq\n"); + else + print_output("Dunno wtf\n"); + print_output("Decompressed file size: %llu\n", expected_size); + print_output("Compressed file size: %llu\n", infile_size); + print_output("Compression ratio: %.3Lf\n", cratio); + + if (HAS_MD5) { + char md5_stored[MD5_DIGEST_SIZE]; + int i; + + print_output("MD5 used for integrity testing\n"); + if (unlikely(lseek(fd_in, -MD5_DIGEST_SIZE, SEEK_END)) == -1) + fatal("Failed to seek to md5 data in runzip_fd\n"); + if (unlikely(read(fd_in, md5_stored, MD5_DIGEST_SIZE) != MD5_DIGEST_SIZE)) + fatal("Failed to read md5 data in runzip_fd\n"); + print_output("MD5: "); + for (i = 0; i < MD5_DIGEST_SIZE; i++) + print_output("%02x", md5_stored[i] & 0xFF); + print_output("\n"); + } else + print_output("CRC32 used for integrity testing\n"); + + if (VERBOSE || MAX_VERBOSE) { + i64 u_len, c_len, last_head, utotal = 0, ctotal = 0, ofs = 25, + stream_head[2]; + int header_length = 25, stream = 0, chunk = 0; + + if (control->major_version == 0 && control->minor_version < 4) + header_length = 13; +next_chunk: + stream = 0; + stream_head[0] = 0; + stream_head[1] = stream_head[0] + header_length; + + print_output("Rzip chunk %d:\n", ++chunk); + while (stream < NUM_STREAMS) { + int block = 1; + + if (unlikely(lseek(fd_in, stream_head[stream] + ofs, SEEK_SET)) == -1) + fatal("Failed to seek to header data in get_fileinfo\n"); + get_header_info(control, fd_in, &ctype, &c_len, &u_len, &last_head); + + print_output("Stream: %d\n", stream); + print_maxverbose("Offset: %lld\n", ofs); + print_output("Block\tComp\tPercent\tSize\n"); + do { + i64 head_off; + + if (unlikely(last_head + ofs > infile_size)) + failure("Offset greater than archive size, likely corrupted/truncated archive.\n"); + if (unlikely(head_off = lseek(fd_in, last_head + ofs, SEEK_SET)) == -1) + fatal("Failed to seek to header data in get_fileinfo\n"); + get_header_info(control, fd_in, &ctype, &c_len, &u_len, &last_head); + if (unlikely(last_head < 0 || c_len < 0 || u_len < 0)) + failure("Entry negative, likely corrupted archive.\n"); + print_output("%d\t", block); + if (ctype == CTYPE_NONE) + print_output("none"); + else if (ctype == CTYPE_BZIP2) + print_output("bzip2"); + else if (ctype == CTYPE_LZO) + print_output("lzo"); + else if (ctype == CTYPE_LZMA) + print_output("lzma"); + else if (ctype == CTYPE_GZIP) + print_output("gzip"); + else if (ctype == CTYPE_ZPAQ) + print_output("zpaq"); + else + print_output("Dunno wtf"); + utotal += u_len; + ctotal += c_len; + print_output("\t%.1f%%\t%lld / %lld", (double)c_len / (double)(u_len / 100), c_len, u_len); + print_maxverbose("\tOffset: %lld\tHead: %lld", head_off, last_head); + print_output("\n"); + block++; + } while (last_head); + ++stream; + } + ofs = lseek(fd_in, 0, SEEK_CUR) + c_len; + /* Chunk byte entry */ + if (control->major_version == 0 && control->minor_version > 4) + ofs++; + if (ofs < infile_size - MD5_DIGEST_SIZE) + goto next_chunk; + if (unlikely(ofs > infile_size)) + failure("Offset greater than archive size, likely corrupted/truncated archive.\n"); + print_output("Rzip compression: %.1f%% %lld / %lld\n", + (double)utotal / (double)(expected_size / 100), + utotal, expected_size); + print_output("Back end compression: %.1f%% %lld / %lld\n", + (double)ctotal / (double)(utotal / 100), + ctotal, utotal); + print_output("Overall compression: %.1f%% %lld / %lld\n", + (double)ctotal / (double)(expected_size / 100), + ctotal, expected_size); + } + + if (STDIN) { + if (unlikely(unlink(control->infile))) + fatal("Failed to unlink %s: %s\n", infilecopy, strerror(errno)); + } else + if (unlikely(close(fd_in))) + fatal("Failed to close fd_in in get_fileinfo\n"); + + free(control->outfile); + free(infilecopy); +} + +/* + compress one file from the command line +*/ +void compress_file(rzip_control *control) +{ + const char *tmp, *tmpinfile; /* we're just using this as a proxy for control->infile. + * Spares a compiler warning + */ + int fd_in, fd_out; + char header[24]; + + memset(header, 0, sizeof(header)); + + if (!STDIN) { + /* is extension at end of infile? */ + if ((tmp = strrchr(control->infile, '.')) && !strcmp(tmp, control->suffix)) { + print_err("%s: already has %s suffix. Skipping...\n", control->infile, control->suffix); + return; + } + + fd_in = open(control->infile, O_RDONLY); + if (unlikely(fd_in == -1)) + fatal("Failed to open %s: %s\n", control->infile, strerror(errno)); + } else + fd_in = 0; + + if (!STDOUT) { + if (control->outname) { + /* check if outname has control->suffix */ + if (*(control->suffix) == '\0') /* suffix is empty string */ + control->outfile = strdup(control->outname); + else if ((tmp=strrchr(control->outname, '.')) && strcmp(tmp, control->suffix)) { + control->outfile = malloc(strlen(control->outname) + strlen(control->suffix) + 1); + if (unlikely(!control->outfile)) + fatal("Failed to allocate outfile name\n"); + strcpy(control->outfile, control->outname); + strcat(control->outfile, control->suffix); + print_output("Suffix added to %s.\nFull pathname is: %s\n", control->outname, control->outfile); + } else /* no, already has suffix */ + control->outfile = strdup(control->outname); + } else { + /* default output name from control->infile + * test if outdir specified. If so, strip path from filename of + * control->infile + */ + if (control->outdir && (tmp = strrchr(control->infile, '/'))) + tmpinfile = tmp + 1; + else + tmpinfile = control->infile; + + control->outfile = malloc((control->outdir == NULL? 0: strlen(control->outdir)) + strlen(tmpinfile) + strlen(control->suffix) + 1); + if (unlikely(!control->outfile)) + fatal("Failed to allocate outfile name\n"); + + if (control->outdir) { /* prepend control->outdir */ + strcpy(control->outfile, control->outdir); + strcat(control->outfile, tmpinfile); + } else + strcpy(control->outfile, tmpinfile); + strcat(control->outfile, control->suffix); + print_progress("Output filename is: %s\n", control->outfile); + } + + if (FORCE_REPLACE) + fd_out = open(control->outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); + else + fd_out = open(control->outfile, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (unlikely(fd_out == -1)) { + /* We must ensure we don't delete a file that already + * exists just because we tried to create a new one */ + control->flags |= FLAG_KEEP_BROKEN; + fatal("Failed to create %s: %s\n", control->outfile, strerror(errno)); + } + } else + fd_out = open_tmpoutfile(control); + control->fd_out = fd_out; + + preserve_perms(control, fd_in, fd_out); + + /* write zeroes to 24 bytes at beginning of file */ + if (unlikely(write(fd_out, header, sizeof(header)) != sizeof(header))) + fatal("Cannot write file header\n"); + + rzip_fd(control, fd_in, fd_out); + + /* write magic at end b/c lzma does not tell us properties until it is done */ + write_magic(control, fd_in, fd_out); + + if (STDOUT) + dump_tmpoutfile(control, fd_out); + + if (unlikely(close(fd_in) || close(fd_out))) + fatal("Failed to close files\n"); + + if (STDOUT) { + /* Delete temporary files generated for testing or faking stdout */ + if (unlikely(unlink(control->outfile))) + fatal("Failed to unlink tmpfile: %s\n", strerror(errno)); + } + + if (!KEEP_FILES) { + if (unlikely(unlink(control->infile))) + fatal("Failed to unlink %s: %s\n", control->infile, strerror(errno)); + } + + free(control->outfile); +} diff --git a/lrzip.h b/lrzip.h new file mode 100644 index 0000000..ab40853 --- /dev/null +++ b/lrzip.h @@ -0,0 +1,115 @@ +/* + Copyright (C) 2006-2011 Con Kolivas + Copyright (C) 2011 Peter Hyman + Copyright (C) 1998-2003 Andrew Tridgell + + 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; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef LRZIP_H +#define LRZIP_H + +#define LRZIP_MAJOR_VERSION 0 +#define LRZIP_MINOR_VERSION 5 +#define LRZIP_MINOR_SUBVERSION 70 + +#define NUM_STREAMS 2 +#define STREAM_BUFSIZE (1024 * 1024 * 10) + +#include "config.h" +#include + +#ifndef uchar +#define uchar unsigned char +#endif + +#ifndef int32 +#if (SIZEOF_INT == 4) +#define int32 int +#elif (SIZEOF_LONG == 4) +#define int32 long +#elif (SIZEOF_SHORT == 4) +#define int32 short +#endif +#endif + +#ifndef int16 +#if (SIZEOF_INT == 2) +#define int16 int +#elif (SIZEOF_SHORT == 2) +#define int16 short +#endif +#endif + +#ifndef uint32 +#define uint32 unsigned int32 +#endif + +#ifndef uint16 +#define uint16 unsigned int16 +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b)? (a): (b)) +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b)? (a): (b)) +#endif + +#if !HAVE_STRERROR +extern char *sys_errlist[]; +#define strerror(i) sys_errlist[i] +#endif + +#ifndef HAVE_ERRNO_H +extern int errno; +#endif + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +typedef long long int i64; +typedef uint16_t u16; +typedef uint32_t u32; + +#ifndef MAP_ANONYMOUS + #define MAP_ANONYMOUS MAP_ANON +#endif + +#if defined(NOTHREAD) || !defined(_SC_NPROCESSORS_ONLN) + #define PROCESSORS (1) +#else + #define PROCESSORS (sysconf(_SC_NPROCESSORS_ONLN)) +#endif + +#ifdef _SC_PAGE_SIZE + #define PAGE_SIZE (sysconf(_SC_PAGE_SIZE)) +#else + #define PAGE_SIZE (4096) +#endif + +typedef struct rzip_control rzip_control; + +void write_magic(rzip_control *control, int fd_in, int fd_out); +void read_magic(rzip_control *control, int fd_in, i64 *expected_size); +void preserve_perms(rzip_control *control, int fd_in, int fd_out); +int open_tmpoutfile(rzip_control *control); +void dump_tmpoutfile(rzip_control *control, int fd_out); +int open_tmpinfile(rzip_control *control); +void read_tmpinfile(rzip_control *control, int fd_in); +void decompress_file(rzip_control *control); +void get_header_info(rzip_control *control, int fd_in, uchar *ctype, i64 *c_len, i64 *u_len, i64 *last_head); +void get_fileinfo(rzip_control *control); +void compress_file(rzip_control *control); +#endif diff --git a/m4/ac_attribute.m4 b/m4/ac_attribute.m4 new file mode 100644 index 0000000..23479a9 --- /dev/null +++ b/m4/ac_attribute.m4 @@ -0,0 +1,47 @@ +dnl Copyright (C) 2004-2008 Kim Woelders +dnl Copyright (C) 2008 Vincent Torri +dnl That code is public domain and can be freely used or copied. +dnl Originally snatched from somewhere... + +dnl Macro for checking if the compiler supports __attribute__ + +dnl Usage: AC_C___ATTRIBUTE__ +dnl call AC_DEFINE for HAVE___ATTRIBUTE__ and __UNUSED__ +dnl if the compiler supports __attribute__, HAVE___ATTRIBUTE__ is +dnl defined to 1 and __UNUSED__ is defined to __attribute__((unused)) +dnl otherwise, HAVE___ATTRIBUTE__ is not defined and __UNUSED__ is +dnl defined to nothing. + +AC_DEFUN([AC_C___ATTRIBUTE__], +[ + +AC_MSG_CHECKING([for __attribute__]) + +AC_CACHE_VAL([ac_cv___attribute__], + [AC_TRY_COMPILE( + [ +#include + +int func(int x); +int foo(int x __attribute__ ((unused))) +{ + exit(1); +} + ], + [], + [ac_cv___attribute__="yes"], + [ac_cv___attribute__="no"] + )]) + +AC_MSG_RESULT($ac_cv___attribute__) + +if test "x${ac_cv___attribute__}" = "xyes" ; then + AC_DEFINE([HAVE___ATTRIBUTE__], [1], [Define to 1 if your compiler has __attribute__]) + AC_DEFINE([__UNUSED__], [__attribute__((unused))], [Macro declaring a function argument to be unused]) + else + AC_DEFINE([__UNUSED__], [], [Macro declaring a function argument to be unused]) +fi + +]) + +dnl End of ac_attribute.m4 diff --git a/main.c b/main.c index 91c6631..56acd8e 100644 --- a/main.c +++ b/main.c @@ -17,7 +17,55 @@ along with this program. If not, see . */ /* lrzip compression - main program */ +#define MAIN_C #include "rzip.h" +/* main() defines, different from liblrzip defines */ +#define FLAG_VERBOSE (FLAG_VERBOSITY | FLAG_VERBOSITY_MAX) +#define FLAG_NOT_LZMA (FLAG_NO_COMPRESS | FLAG_LZO_COMPRESS | FLAG_BZIP2_COMPRESS | FLAG_ZLIB_COMPRESS | FLAG_ZPAQ_COMPRESS) +#define LZMA_COMPRESS (!(control.flags & FLAG_NOT_LZMA)) + +#define SHOW_PROGRESS (control.flags & FLAG_SHOW_PROGRESS) +#define KEEP_FILES (control.flags & FLAG_KEEP_FILES) +#define TEST_ONLY (control.flags & FLAG_TEST_ONLY) +#define FORCE_REPLACE (control.flags & FLAG_FORCE_REPLACE) +#define DECOMPRESS (control.flags & FLAG_DECOMPRESS) +#define NO_COMPRESS (control.flags & FLAG_NO_COMPRESS) +#define LZO_COMPRESS (control.flags & FLAG_LZO_COMPRESS) +#define BZIP2_COMPRESS (control.flags & FLAG_BZIP2_COMPRESS) +#define ZLIB_COMPRESS (control.flags & FLAG_ZLIB_COMPRESS) +#define ZPAQ_COMPRESS (control.flags & FLAG_ZPAQ_COMPRESS) +#define VERBOSE (control.flags & FLAG_VERBOSE) +#define VERBOSITY (control.flags & FLAG_VERBOSITY) +#define MAX_VERBOSE (control.flags & FLAG_VERBOSITY_MAX) +#define STDIN (control.flags & FLAG_STDIN) +#define STDOUT (control.flags & FLAG_STDOUT) +#define INFO (control.flags & FLAG_INFO) +#define UNLIMITED (control.flags & FLAG_UNLIMITED) +#define HASH_CHECK (control.flags & FLAG_HASH) +#define HAS_MD5 (control.flags & FLAG_MD5) +#define CHECK_FILE (control.flags & FLAG_CHECK) +#define KEEP_BROKEN (control.flags & FLAG_KEEP_BROKEN) +#define LZO_TEST (control.flags & FLAG_THRESHOLD) + +#define print_output(format, args...) do {\ + fprintf(control.msgout, format, ##args); \ + fflush(control.msgout); \ +} while (0) + +#define print_progress(format, args...) do {\ + if (SHOW_PROGRESS) \ + print_output(format, ##args); \ +} while (0) + +#define print_verbose(format, args...) do {\ + if (VERBOSE) \ + print_output(format, ##args); \ +} while (0) + +#define print_maxverbose(format, args...) do {\ + if (MAX_VERBOSE) \ + print_output(format, ##args); \ +} while (0) struct rzip_control control; @@ -66,652 +114,6 @@ static void usage(void) } -static void write_magic(int fd_in, int fd_out) -{ - struct stat st; - char magic[24]; - int i; - - memset(magic, 0, sizeof(magic)); - strcpy(magic, "LRZI"); - magic[4] = LRZIP_MAJOR_VERSION; - magic[5] = LRZIP_MINOR_VERSION; - - if (unlikely(fstat(fd_in, &st))) - fatal("bad magic file descriptor!?\n"); - - memcpy(&magic[6], &control.st_size, 8); - - /* save LZMA compression flags */ - if (LZMA_COMPRESS) { - for (i = 0; i < 5; i++) - magic[i + 16] = (char)control.lzma_properties[i]; - } - - /* This is a flag that the archive contains an md5 sum at the end - * which can be used as an integrity check instead of crc check. - * crc is still stored for compatibility with 0.5 versions. - */ - magic[21] = 1; - - if (unlikely(lseek(fd_out, 0, SEEK_SET))) - fatal("Failed to seek to BOF to write Magic Header\n"); - - if (unlikely(write(fd_out, magic, sizeof(magic)) != sizeof(magic))) - fatal("Failed to write magic header\n"); -} - -static void read_magic(int fd_in, i64 *expected_size) -{ - char magic[24]; - uint32_t v; - int md5, i; - - if (unlikely(read(fd_in, magic, sizeof(magic)) != sizeof(magic))) - fatal("Failed to read magic header\n"); - - *expected_size = 0; - - if (unlikely(strncmp(magic, "LRZI", 4))) - failure("Not an lrzip file\n"); - - memcpy(&control.major_version, &magic[4], 1); - memcpy(&control.minor_version, &magic[5], 1); - - /* Support the convoluted way we described size in versions < 0.40 */ - if (control.major_version == 0 && control.minor_version < 4) { - memcpy(&v, &magic[6], 4); - *expected_size = ntohl(v); - memcpy(&v, &magic[10], 4); - *expected_size |= ((i64)ntohl(v)) << 32; - } else - memcpy(expected_size, &magic[6], 8); - - /* restore LZMA compression flags only if stored */ - if ((int) magic[16]) { - for (i = 0; i < 5; i++) - control.lzma_properties[i] = magic[i + 16]; - } - - /* Whether this archive contains md5 data at the end or not */ - md5 = magic[21]; - if (md5 == 1) - control.flags |= FLAG_MD5; - - print_verbose("Detected lrzip version %d.%d file.\n", control.major_version, control.minor_version); - if (control.major_version > LRZIP_MAJOR_VERSION || - (control.major_version == LRZIP_MAJOR_VERSION && control.minor_version > LRZIP_MINOR_VERSION)) - print_output("Attempting to work with file produced by newer lrzip version %d.%d file.\n", control.major_version, control.minor_version); -} - -/* preserve ownership and permissions where possible */ -static void preserve_perms(int fd_in, int fd_out) -{ - struct stat st; - - if (unlikely(fstat(fd_in, &st))) - fatal("Failed to fstat input file\n"); - if (unlikely(fchmod(fd_out, (st.st_mode & 0777)))) - print_err("Warning, unable to set permissions on %s\n", control.outfile); - - /* chown fail is not fatal */ - if (unlikely(fchown(fd_out, st.st_uid, st.st_gid))) - print_err("Warning, unable to set owner on %s\n", control.outfile); -} - -/* Open a temporary outputfile to emulate stdout */ -static int open_tmpoutfile(void) -{ - int fd_out; - - if (STDOUT && !TEST_ONLY) - print_verbose("Outputting to stdout.\n"); - if (control.tmpdir) { - control.outfile = realloc(NULL, strlen(control.tmpdir) + 16); - if (unlikely(!control.outfile)) - fatal("Failed to allocate outfile name\n"); - strcpy(control.outfile, control.tmpdir); - strcat(control.outfile, "lrzipout.XXXXXX"); - } else { - control.outfile = realloc(NULL, 16); - if (unlikely(!control.outfile)) - fatal("Failed to allocate outfile name\n"); - strcpy(control.outfile, "lrzipout.XXXXXX"); - } - - fd_out = mkstemp(control.outfile); - if (unlikely(fd_out == -1)) - fatal("Failed to create out tmpfile: %s\n", control.outfile); - return fd_out; -} - -/* Dump temporary outputfile to perform stdout */ -void dump_tmpoutfile(int fd_out) -{ - FILE *tmpoutfp; - int tmpchar; - - /* flush anything not yet in the temporary file */ - fsync(fd_out); - tmpoutfp = fdopen(fd_out, "r"); - if (unlikely(tmpoutfp == NULL)) - fatal("Failed to fdopen out tmpfile\n"); - rewind(tmpoutfp); - - if (!TEST_ONLY) { - print_verbose("Dumping temporary file to stdout.\n"); - while ((tmpchar = fgetc(tmpoutfp)) != EOF) - putchar(tmpchar); - fflush(stdout); - rewind(tmpoutfp); - } - - if (unlikely(ftruncate(fd_out, 0))) - fatal("Failed to ftruncate fd_out in dump_tmpoutfile\n"); -} - -/* Open a temporary inputfile to perform stdin decompression */ -static int open_tmpinfile(void) -{ - int fd_in; - - if (control.tmpdir) { - control.infile = malloc(strlen(control.tmpdir) + 15); - if (unlikely(!control.infile)) - fatal("Failed to allocate infile name\n"); - strcpy(control.infile, control.tmpdir); - strcat(control.infile, "lrzipin.XXXXXX"); - } else { - control.infile = malloc(15); - if (unlikely(!control.infile)) - fatal("Failed to allocate infile name\n"); - strcpy(control.infile, "lrzipin.XXXXXX"); - } - - fd_in = mkstemp(control.infile); - if (unlikely(fd_in == -1)) - fatal("Failed to create in tmpfile: %s\n", control.infile); - /* Unlink temporary file immediately to minimise chance of files left - * lying around in cases of failure. */ - if (unlikely(unlink(control.infile))) - fatal("Failed to unlink tmpfile: %s\n", control.infile); - return fd_in; -} - -/* Read data from stdin into temporary inputfile */ -static void read_tmpinfile(int fd_in) -{ - FILE *tmpinfp; - int tmpchar; - - if (control.flags & FLAG_SHOW_PROGRESS) - fprintf(control.msgout, "Copying from stdin.\n"); - tmpinfp = fdopen(fd_in, "w+"); - if (unlikely(tmpinfp == NULL)) - fatal("Failed to fdopen in tmpfile\n"); - - while ((tmpchar = getchar()) != EOF) - fputc(tmpchar, tmpinfp); - - fflush(tmpinfp); - rewind(tmpinfp); -} - -/* - decompress one file from the command line -*/ -static void decompress_file(void) -{ - char *tmp, *tmpoutfile, *infilecopy = NULL; - int fd_in, fd_out = -1, fd_hist = -1; - i64 expected_size, free_space; - struct statvfs fbuf; - - if (!STDIN) { - if ((tmp = strrchr(control.infile, '.')) && strcmp(tmp,control.suffix)) { - /* make sure infile has an extension. If not, add it - * because manipulations may be made to input filename, set local ptr - */ - infilecopy = malloc(strlen(control.infile) + strlen(control.suffix) + 1); - if (unlikely(infilecopy == NULL)) - fatal("Failed to allocate memory for infile suffix\n"); - else { - strcpy(infilecopy, control.infile); - strcat(infilecopy, control.suffix); - } - } else - infilecopy = strdup(control.infile); - /* regardless, infilecopy has the input filename */ - } - - if (!STDOUT && !TEST_ONLY) { - /* if output name already set, use it */ - if (control.outname) { - control.outfile = strdup(control.outname); - } else { - /* default output name from infilecopy - * test if outdir specified. If so, strip path from filename of - * infilecopy, then remove suffix. - */ - if (control.outdir && (tmp = strrchr(infilecopy, '/'))) - tmpoutfile = strdup(tmp + 1); - else - tmpoutfile = strdup(infilecopy); - - /* remove suffix to make outfile name */ - if ((tmp = strrchr(tmpoutfile, '.')) && !strcmp(tmp, control.suffix)) - *tmp='\0'; - - control.outfile = malloc((control.outdir == NULL? 0: strlen(control.outdir)) + strlen(tmpoutfile) + 1); - if (unlikely(!control.outfile)) - fatal("Failed to allocate outfile name\n"); - - if (control.outdir) { /* prepend control.outdir */ - strcpy(control.outfile, control.outdir); - strcat(control.outfile, tmpoutfile); - } else - strcpy(control.outfile, tmpoutfile); - free(tmpoutfile); - } - - if (!STDOUT) - print_progress("Output filename is: %s...Decompressing...\n", control.outfile); - } - - if (STDIN) { - fd_in = open_tmpinfile(); - read_tmpinfile(fd_in); - } else { - fd_in = open(infilecopy, O_RDONLY); - if (unlikely(fd_in == -1)) { - fatal("Failed to open %s\n",infilecopy); - } - } - - if (!(TEST_ONLY | STDOUT)) { - if (FORCE_REPLACE) - fd_out = open(control.outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); - else - fd_out = open(control.outfile, O_WRONLY | O_CREAT | O_EXCL, 0666); - if (unlikely(fd_out == -1)) { - /* We must ensure we don't delete a file that already - * exists just because we tried to create a new one */ - control.flags |= FLAG_KEEP_BROKEN; - fatal("Failed to create %s\n", control.outfile); - } - - preserve_perms(fd_in, fd_out); - } else - fd_out = open_tmpoutfile(); - control.fd_out = fd_out; - - read_magic(fd_in, &expected_size); - - if (!STDOUT) { - /* Check if there's enough free space on the device chosen to fit the - * decompressed file. */ - if (unlikely(fstatvfs(fd_out, &fbuf))) - fatal("Failed to fstatvfs in decompress_file\n"); - free_space = fbuf.f_bsize * fbuf.f_bavail; - if (free_space < expected_size) { - if (FORCE_REPLACE) - print_err("Warning, inadequate free space detected, but attempting to decompress due to -f option being used.\n"); - else - failure("Inadequate free space to decompress file, use -f to override.\n"); - } - } - - fd_hist = open(control.outfile, O_RDONLY); - if (unlikely(fd_hist == -1)) - fatal("Failed to open history file %s\n", control.outfile); - - /* Unlink temporary file as soon as possible */ - if (unlikely((STDOUT || TEST_ONLY) && unlink(control.outfile))) - fatal("Failed to unlink tmpfile: %s\n", control.outfile); - - if (NO_MD5) - print_verbose("Not performing MD5 hash check\n"); - if (HAS_MD5) - print_verbose("MD5 "); - else - print_verbose("CRC32 "); - print_verbose("being used for integrity testing.\n"); - - print_progress("Decompressing..."); - - runzip_fd(fd_in, fd_out, fd_hist, expected_size); - - if (STDOUT) - dump_tmpoutfile(fd_out); - - /* if we get here, no fatal errors during decompression */ - print_progress("\r"); - if (!(STDOUT | TEST_ONLY)) - print_output("Output filename is: %s: ", control.outfile); - print_progress("[OK] - %lld bytes \n", expected_size); - - if (unlikely(close(fd_hist) || close(fd_out))) - fatal("Failed to close files\n"); - - close(fd_in); - - if (!KEEP_FILES) { - if (unlikely(unlink(control.infile))) - fatal("Failed to unlink %s\n", infilecopy); - } - - free(control.outfile); - free(infilecopy); -} - -static void get_header_info(int fd_in, uchar *ctype, i64 *c_len, i64 *u_len, i64 *last_head) -{ - if (unlikely(read(fd_in, ctype, 1) != 1)) - fatal("Failed to read in get_header_info\n"); - - if (control.major_version == 0 && control.minor_version < 4) { - u32 c_len32, u_len32, last_head32; - - if (unlikely(read(fd_in, &c_len32, 4) != 4)) - fatal("Failed to read in get_header_info"); - if (unlikely(read(fd_in, &u_len32, 4) != 4)) - fatal("Failed to read in get_header_info"); - if (unlikely(read(fd_in, &last_head32, 4) != 4)) - fatal("Failed to read in get_header_info"); - *c_len = c_len32; - *u_len = u_len32; - *last_head = last_head32; - } else { - if (unlikely(read(fd_in, c_len, 8) != 8)) - fatal("Failed to read in get_header_info"); - if (unlikely(read(fd_in, u_len, 8) != 8)) - fatal("Failed to read in get_header_info"); - if (unlikely(read(fd_in, last_head, 8) != 8)) - fatal("Failed to read_i64 in get_header_info"); - } -} - -static void get_fileinfo(void) -{ - i64 expected_size, infile_size; - int seekspot, fd_in; - long double cratio; - uchar ctype = 0; - struct stat st; - - char *tmp, *infilecopy = NULL; - - if (!STDIN) { - if ((tmp = strrchr(control.infile, '.')) && strcmp(tmp,control.suffix)) { - infilecopy = malloc(strlen(control.infile) + strlen(control.suffix) + 1); - if (unlikely(infilecopy == NULL)) - fatal("Failed to allocate memory for infile suffix\n"); - else { - strcpy(infilecopy, control.infile); - strcat(infilecopy, control.suffix); - } - } else - infilecopy = strdup(control.infile); - } - - if (STDIN) - fd_in = 0; - else { - fd_in = open(infilecopy, O_RDONLY); - if (unlikely(fd_in == -1)) - fatal("Failed to open %s\n", infilecopy); - } - - /* Get file size */ - if (unlikely(fstat(fd_in, &st))) - fatal("bad magic file descriptor!?\n"); - memcpy(&infile_size, &st.st_size, 8); - - /* Get decompressed size */ - read_magic(fd_in, &expected_size); - - /* Version < 0.4 had different file format */ - if (control.major_version == 0 && control.minor_version < 4) - seekspot = 50; - else if (control.major_version == 0 && control.minor_version == 4) - seekspot = 74; - else - seekspot = 75; - if (unlikely(lseek(fd_in, seekspot, SEEK_SET) == -1)) - fatal("Failed to lseek in get_fileinfo\n"); - - /* Read the compression type of the first block. It's possible that - not all blocks are compressed so this may not be accurate. - */ - if (unlikely(read(fd_in, &ctype, 1) != 1)) - fatal("Failed to read in get_fileinfo\n"); - - cratio = (long double)expected_size / (long double)infile_size; - - print_output("%s:\nlrzip version: %d.%d file\n", infilecopy, control.major_version, control.minor_version); - print_output("Compression: "); - if (ctype == CTYPE_NONE) - print_output("rzip alone\n"); - else if (ctype == CTYPE_BZIP2) - print_output("rzip + bzip2\n"); - else if (ctype == CTYPE_LZO) - print_output("rzip + lzo\n"); - else if (ctype == CTYPE_LZMA) - print_output("rzip + lzma\n"); - else if (ctype == CTYPE_GZIP) - print_output("rzip + gzip\n"); - else if (ctype == CTYPE_ZPAQ) - print_output("rzip + zpaq\n"); - else - print_output("Dunno wtf\n"); - print_output("Decompressed file size: %llu\n", expected_size); - print_output("Compressed file size: %llu\n", infile_size); - print_output("Compression ratio: %.3Lf\n", cratio); - - if (HAS_MD5) { - char md5_stored[MD5_DIGEST_SIZE]; - int i; - - print_output("MD5 used for integrity testing\n"); - if (unlikely(lseek(fd_in, -MD5_DIGEST_SIZE, SEEK_END)) == -1) - fatal("Failed to seek to md5 data in runzip_fd\n"); - if (unlikely(read(fd_in, md5_stored, MD5_DIGEST_SIZE) != MD5_DIGEST_SIZE)) - fatal("Failed to read md5 data in runzip_fd\n"); - print_output("MD5: "); - for (i = 0; i < MD5_DIGEST_SIZE; i++) - print_output("%02x", md5_stored[i] & 0xFF); - print_output("\n"); - } else - print_output("CRC32 used for integrity testing\n"); - - if (VERBOSE || MAX_VERBOSE) { - i64 u_len, c_len, last_head, utotal = 0, ctotal = 0, ofs = 25, - stream_head[2]; - int header_length = 25, stream = 0, chunk = 0; - - if (control.major_version == 0 && control.minor_version < 4) - header_length = 13; -next_chunk: - stream = 0; - stream_head[0] = 0; - stream_head[1] = stream_head[0] + header_length; - - print_output("Rzip chunk %d:\n", ++chunk); - while (stream < NUM_STREAMS) { - int block = 1; - - if (unlikely(lseek(fd_in, stream_head[stream] + ofs, SEEK_SET)) == -1) - fatal("Failed to seek to header data in get_fileinfo\n"); - get_header_info(fd_in, &ctype, &c_len, &u_len, &last_head); - - print_output("Stream: %d\n", stream); - print_maxverbose("Offset: %lld\n", ofs); - print_output("Block\tComp\tPercent\tSize\n"); - do { - i64 head_off; - - if (unlikely(last_head + ofs > infile_size)) - failure("Offset greater than archive size, likely corrupted/truncated archive.\n"); - if (unlikely(head_off = lseek(fd_in, last_head + ofs, SEEK_SET)) == -1) - fatal("Failed to seek to header data in get_fileinfo\n"); - get_header_info(fd_in, &ctype, &c_len, &u_len, &last_head); - if (unlikely(last_head < 0 || c_len < 0 || u_len < 0)) - failure("Entry negative, likely corrupted archive.\n"); - print_output("%d\t", block); - if (ctype == CTYPE_NONE) - print_output("none"); - else if (ctype == CTYPE_BZIP2) - print_output("bzip2"); - else if (ctype == CTYPE_LZO) - print_output("lzo"); - else if (ctype == CTYPE_LZMA) - print_output("lzma"); - else if (ctype == CTYPE_GZIP) - print_output("gzip"); - else if (ctype == CTYPE_ZPAQ) - print_output("zpaq"); - else - print_output("Dunno wtf"); - utotal += u_len; - ctotal += c_len; - print_output("\t%.1f%%\t%lld / %lld", (double)c_len / (double)(u_len / 100), c_len, u_len); - print_maxverbose("\tOffset: %lld\tHead: %lld", head_off, last_head); - print_output("\n"); - block++; - } while (last_head); - ++stream; - } - ofs = lseek(fd_in, 0, SEEK_CUR) + c_len; - /* Chunk byte entry */ - if (control.major_version == 0 && control.minor_version > 4) - ofs++; - if (ofs < infile_size - MD5_DIGEST_SIZE) - goto next_chunk; - if (unlikely(ofs > infile_size)) - failure("Offset greater than archive size, likely corrupted/truncated archive.\n"); - print_output("Rzip compression: %.1f%% %lld / %lld\n", - (double)utotal / (double)(expected_size / 100), - utotal, expected_size); - print_output("Back end compression: %.1f%% %lld / %lld\n", - (double)ctotal / (double)(utotal / 100), - ctotal, utotal); - print_output("Overall compression: %.1f%% %lld / %lld\n", - (double)ctotal / (double)(expected_size / 100), - ctotal, expected_size); - } - - if (unlikely(close(fd_in))) - fatal("Failed to close fd_in in get_fileinfo\n"); - - free(control.outfile); - free(infilecopy); -} - -/* - compress one file from the command line -*/ -static void compress_file(void) -{ - const char *tmp, *tmpinfile; /* we're just using this as a proxy for control.infile. - * Spares a compiler warning - */ - int fd_in, fd_out; - char header[24]; - - memset(header, 0, sizeof(header)); - - if (!STDIN) { - /* is extension at end of infile? */ - if ((tmp = strrchr(control.infile, '.')) && !strcmp(tmp, control.suffix)) { - print_err("%s: already has %s suffix. Skipping...\n", control.infile, control.suffix); - return; - } - - fd_in = open(control.infile, O_RDONLY); - if (unlikely(fd_in == -1)) - fatal("Failed to open %s\n", control.infile); - } else - fd_in = 0; - - if (!STDOUT) { - if (control.outname) { - /* check if outname has control.suffix */ - if (*(control.suffix) == '\0') /* suffix is empty string */ - control.outfile = strdup(control.outname); - else if ((tmp=strrchr(control.outname, '.')) && strcmp(tmp, control.suffix)) { - control.outfile = malloc(strlen(control.outname) + strlen(control.suffix) + 1); - if (unlikely(!control.outfile)) - fatal("Failed to allocate outfile name\n"); - strcpy(control.outfile, control.outname); - strcat(control.outfile, control.suffix); - print_output("Suffix added to %s.\nFull pathname is: %s\n", control.outname, control.outfile); - } else /* no, already has suffix */ - control.outfile = strdup(control.outname); - } else { - /* default output name from control.infile - * test if outdir specified. If so, strip path from filename of - * control.infile - */ - if (control.outdir && (tmp = strrchr(control.infile, '/'))) - tmpinfile = tmp + 1; - else - tmpinfile = control.infile; - - control.outfile = malloc((control.outdir == NULL? 0: strlen(control.outdir)) + strlen(tmpinfile) + strlen(control.suffix) + 1); - if (unlikely(!control.outfile)) - fatal("Failed to allocate outfile name\n"); - - if (control.outdir) { /* prepend control.outdir */ - strcpy(control.outfile, control.outdir); - strcat(control.outfile, tmpinfile); - } else - strcpy(control.outfile, tmpinfile); - strcat(control.outfile, control.suffix); - print_progress("Output filename is: %s\n", control.outfile); - } - - if (FORCE_REPLACE) - fd_out = open(control.outfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); - else - fd_out = open(control.outfile, O_WRONLY | O_CREAT | O_EXCL, 0666); - if (unlikely(fd_out == -1)) { - /* We must ensure we don't delete a file that already - * exists just because we tried to create a new one */ - control.flags |= FLAG_KEEP_BROKEN; - fatal("Failed to create %s\n", control.outfile); - } - } else - fd_out = open_tmpoutfile(); - control.fd_out = fd_out; - - if (unlikely(STDOUT && unlink(control.outfile))) - fatal("Failed to unlink tmpfile: %s\n", control.outfile); - - preserve_perms(fd_in, fd_out); - - /* write zeroes to 24 bytes at beginning of file */ - if (unlikely(write(fd_out, header, sizeof(header)) != sizeof(header))) - fatal("Cannot write file header\n"); - - rzip_fd(fd_in, fd_out); - - /* write magic at end b/c lzma does not tell us properties until it is done */ - write_magic(fd_in, fd_out); - - if (STDOUT) - dump_tmpoutfile(fd_out); - - if (unlikely(close(fd_in) || close(fd_out))) - fatal("Failed to close files\n"); - - if (!KEEP_FILES) { - if (unlikely(unlink(control.infile))) - fatal("Failed to unlink %s\n", control.infile); - } - - free(control.outfile); -} - static void show_summary(void) { /* OK, if verbosity set, print summary of options selected */ @@ -790,6 +192,7 @@ int main(int argc, char *argv[]) memset(&control, 0, sizeof(control)); control.msgout = stderr; + register_outputfile(control.msgout); control.flags = FLAG_SHOW_PROGRESS | FLAG_KEEP_FILES | FLAG_THRESHOLD; control.suffix = ".lrz"; control.outdir = NULL; @@ -1013,16 +416,20 @@ int main(int argc, char *argv[]) if (control.outname && (strcmp(control.outname, "-") == 0)) { control.flags |= FLAG_STDOUT; control.msgout = stderr; + register_outputfile(control.msgout); } /* If we're using stdin and no output filename, use stdout */ if (STDIN && !control.outname) { control.flags |= FLAG_STDOUT; control.msgout = stderr; + register_outputfile(control.msgout); } - if (!STDOUT) + if (!STDOUT) { control.msgout = stdout; + register_outputfile(control.msgout); + } /* Implement signal handler only once flags are set */ handler.sa_handler = &sighandler; sigaction(SIGTERM, &handler, 0); @@ -1056,11 +463,11 @@ int main(int argc, char *argv[]) gettimeofday(&start_time, NULL); if (control.flags & (FLAG_DECOMPRESS | FLAG_TEST_ONLY)) - decompress_file(); + decompress_file(&control); else if (INFO) - get_fileinfo(); + get_fileinfo(&control); else - compress_file(); + compress_file(&control); /* compute total time */ gettimeofday(&end_time, NULL); diff --git a/runzip.c b/runzip.c index 4661c05..7070f86 100644 --- a/runzip.c +++ b/runzip.c @@ -19,26 +19,26 @@ #include "rzip.h" -static inline uchar read_u8(void *ss, int stream) +static inline uchar read_u8(rzip_control *control, void *ss, int stream) { uchar b; - if (unlikely(read_stream(ss, stream, &b, 1) != 1)) + if (unlikely(read_stream(control, ss, stream, &b, 1) != 1)) fatal("Stream read u8 failed\n"); return b; } -static inline u32 read_u32(void *ss, int stream) +static inline u32 read_u32(rzip_control *control, void *ss, int stream) { u32 ret; - if (unlikely(read_stream(ss, stream, (uchar *)&ret, 4) != 4)) + if (unlikely(read_stream(control, ss, stream, (uchar *)&ret, 4) != 4)) fatal("Stream read u32 failed\n"); return ret; } /* Read a variable length of chars dependant on how big the chunk was */ -static inline i64 read_vchars(void *ss, int stream, int length) +static inline i64 read_vchars(rzip_control *control, void *ss, int stream, int length) { int bytes; i64 s = 0; @@ -46,24 +46,24 @@ static inline i64 read_vchars(void *ss, int stream, int length) for (bytes = 0; bytes < length; bytes++) { int bits = bytes * 8; - uchar sb = read_u8(ss, stream); + uchar sb = read_u8(control, ss, stream); s |= (i64)sb << bits; } return s; } -static i64 read_header(void *ss, uchar *head) +static i64 read_header(rzip_control *control, void *ss, uchar *head) { int chunk_bytes = 2; /* All chunks were unnecessarily encoded 8 bytes wide version 0.4x */ - if (control.major_version == 0 && control.minor_version == 4) + if (control->major_version == 0 && control->minor_version == 4) chunk_bytes = 8; - *head = read_u8(ss, 0); - return read_vchars(ss, 0, chunk_bytes); + *head = read_u8(control, ss, 0); + return read_vchars(control, ss, 0, chunk_bytes); } -static i64 unzip_literal(void *ss, i64 len, int fd_out, uint32 *cksum) +static i64 unzip_literal(rzip_control *control, void *ss, i64 len, int fd_out, uint32 *cksum) { i64 stream_read; uchar *buf; @@ -75,7 +75,7 @@ static i64 unzip_literal(void *ss, i64 len, int fd_out, uint32 *cksum) if (unlikely(!buf)) fatal("Failed to malloc literal buffer of size %lld\n", len); - stream_read = read_stream(ss, 1, buf, len); + stream_read = read_stream(control, ss, 1, buf, len); if (unlikely(stream_read == -1 )) fatal("Failed to read_stream in unzip_literal\n"); @@ -85,13 +85,13 @@ static i64 unzip_literal(void *ss, i64 len, int fd_out, uint32 *cksum) if (!HAS_MD5) *cksum = CrcUpdate(*cksum, buf, stream_read); if (!NO_MD5) - md5_process_bytes(buf, stream_read, &control.ctx); + md5_process_bytes(buf, stream_read, &control->ctx); free(buf); return stream_read; } -static i64 unzip_match(void *ss, i64 len, int fd_out, int fd_hist, uint32 *cksum, int chunk_bytes) +static i64 unzip_match(rzip_control *control, void *ss, i64 len, int fd_out, int fd_hist, uint32 *cksum, int chunk_bytes) { i64 offset, n, total, cur_pos; uchar *buf, *off_buf; @@ -105,7 +105,7 @@ static i64 unzip_match(void *ss, i64 len, int fd_out, int fd_hist, uint32 *cksum fatal("Seek failed on out file in unzip_match.\n"); /* Note the offset is in a different format v0.40+ */ - offset = read_vchars(ss, 0, chunk_bytes); + offset = read_vchars(control, ss, 0, chunk_bytes); if (unlikely(lseek(fd_hist, cur_pos - offset, SEEK_SET) == -1)) fatal("Seek failed by %d from %d on history file in unzip_match\n", offset, cur_pos); @@ -127,7 +127,7 @@ static i64 unzip_match(void *ss, i64 len, int fd_out, int fd_hist, uint32 *cksum if (!HAS_MD5) *cksum = CrcUpdate(*cksum, off_buf, n); if (!NO_MD5) - md5_process_bytes(off_buf, n, &control.ctx); + md5_process_bytes(off_buf, n, &control->ctx); len -= n; off_buf += n; @@ -142,7 +142,7 @@ static i64 unzip_match(void *ss, i64 len, int fd_out, int fd_hist, uint32 *cksum /* decompress a section of an open file. Call fatal() on error return the number of bytes that have been retrieved */ -static i64 runzip_chunk(int fd_in, int fd_out, int fd_hist, i64 expected_size, i64 tally) +static i64 runzip_chunk(rzip_control *control, int fd_in, int fd_out, int fd_hist, i64 expected_size, i64 tally) { uint32 good_cksum, cksum = 0; i64 len, ofs, total = 0; @@ -172,9 +172,9 @@ static i64 runzip_chunk(int fd_in, int fd_out, int fd_hist, i64 expected_size, i /* Determine the chunk_byte width size. Versions < 0.4 used 4 * bytes for all offsets, version 0.4 used 8 bytes. Versions 0.5+ use * a variable number of bytes depending on chunk size.*/ - if (control.major_version == 0 && control.minor_version < 4) + if (control->major_version == 0 && control->minor_version < 4) chunk_bytes = 4; - else if (control.major_version == 0 && control.minor_version == 4) + else if (control->major_version == 0 && control->minor_version == 4) chunk_bytes = 8; else { /* Read in the stored chunk byte width from the file */ @@ -192,18 +192,18 @@ static i64 runzip_chunk(int fd_in, int fd_out, int fd_hist, i64 expected_size, i if (fstat(fd_in, &st) || st.st_size - ofs == 0) return 0; - ss = open_stream_in(fd_in, NUM_STREAMS); + ss = open_stream_in(control, fd_in, NUM_STREAMS); if (unlikely(!ss)) fatal("Failed to open_stream_in in runzip_chunk\n"); - while ((len = read_header(ss, &head)) || head) { + while ((len = read_header(control, ss, &head)) || head) { switch (head) { case 0: - total += unzip_literal(ss, len, fd_out, &cksum); + total += unzip_literal(control, ss, len, fd_out, &cksum); break; default: - total += unzip_match(ss, len, fd_out, fd_hist, &cksum, chunk_bytes); + total += unzip_match(control, ss, len, fd_out, fd_hist, &cksum, chunk_bytes); break; } p = 100 * ((double)(tally + total) / (double)expected_size); @@ -216,7 +216,7 @@ static i64 runzip_chunk(int fd_in, int fd_out, int fd_hist, i64 expected_size, i } if (!HAS_MD5) { - good_cksum = read_u32(ss, 0); + good_cksum = read_u32(control, ss, 0); if (unlikely(good_cksum != cksum)) failure("Bad checksum: 0x%08x - expected: 0x%08x\n", cksum, good_cksum); print_maxverbose("Checksum for block: 0x%08x\n", cksum); @@ -231,7 +231,7 @@ static i64 runzip_chunk(int fd_in, int fd_out, int fd_hist, i64 expected_size, i /* Decompress an open file. Call fatal() on error return the number of bytes that have been retrieved */ -i64 runzip_fd(int fd_in, int fd_out, int fd_hist, i64 expected_size) +i64 runzip_fd(rzip_control *control, int fd_in, int fd_out, int fd_hist, i64 expected_size) { char md5_resblock[MD5_DIGEST_SIZE]; char md5_stored[MD5_DIGEST_SIZE]; @@ -239,13 +239,13 @@ i64 runzip_fd(int fd_in, int fd_out, int fd_hist, i64 expected_size) i64 total = 0; if (!NO_MD5) - md5_init_ctx (&control.ctx); + md5_init_ctx (&control->ctx); gettimeofday(&start,NULL); while (total < expected_size) { - total += runzip_chunk(fd_in, fd_out, fd_hist, expected_size, total); + total += runzip_chunk(control, fd_in, fd_out, fd_hist, expected_size, total); if (STDOUT) - dump_tmpoutfile(fd_out); + dump_tmpoutfile(control, fd_out); } gettimeofday(&end,NULL); @@ -255,7 +255,7 @@ i64 runzip_fd(int fd_in, int fd_out, int fd_hist, i64 expected_size) if (!NO_MD5) { int i,j; - md5_finish_ctx (&control.ctx, md5_resblock); + md5_finish_ctx (&control->ctx, md5_resblock); if (HAS_MD5) { if (unlikely(lseek(fd_in, -MD5_DIGEST_SIZE, SEEK_END)) == -1) fatal("Failed to seek to md5 data in runzip_fd\n"); diff --git a/rzip.c b/rzip.c index 5f92daf..1e8d861 100644 --- a/rzip.c +++ b/rzip.c @@ -100,7 +100,7 @@ struct sliding_buffer { int fd; /* The fd of the mmap */ } sb; /* Sliding buffer */ -static void remap_low_sb(void) +static void remap_low_sb(rzip_control *control) { i64 new_offset; @@ -117,14 +117,14 @@ static void remap_low_sb(void) fatal("Failed to re mmap in remap_low_sb\n"); } -static inline void remap_high_sb(i64 p) +static inline void remap_high_sb(rzip_control *control, i64 p) { if (unlikely(munmap(sb.buf_high, sb.size_high))) fatal("Failed to munmap in remap_high_sb\n"); sb.size_high = sb.high_length; /* In case we shrunk it when we hit the end of the file */ sb.offset_high = p; /* Make sure offset is rounded to page size of total offset */ - sb.offset_high -= (sb.offset_high + sb.orig_offset) % control.page_size; + sb.offset_high -= (sb.offset_high + sb.orig_offset) % control->page_size; if (unlikely(sb.offset_high + sb.size_high > sb.orig_size)) sb.size_high = sb.orig_size - sb.offset_high; sb.buf_high = (uchar *)mmap(sb.buf_high, sb.size_high, PROT_READ, MAP_SHARED, sb.fd, sb.orig_offset + sb.offset_high); @@ -138,36 +138,36 @@ static inline void remap_high_sb(i64 p) * it, and a 64k mmap block that slides up and down as is required for any * offsets outside the range of the lower one. This is much slower than mmap * but makes it possible to have unlimited sized compression windows. */ -static uchar *get_sb(i64 p) +static uchar *get_sb(rzip_control *control, i64 p) { i64 low_end = sb.offset_low + sb.size_low; if (unlikely(sb.offset_search > low_end)) - remap_low_sb(); + remap_low_sb(control); if (p >= sb.offset_low && p < low_end) return (sb.buf_low + p - sb.offset_low); if (p >= sb.offset_high && p < (sb.offset_high + sb.size_high)) return (sb.buf_high + (p - sb.offset_high)); /* p is not within the low or high buffer range */ - remap_high_sb(p); + remap_high_sb(control, p); return (sb.buf_high + (p - sb.offset_high)); } /* All put_u8/u32/vchars go to stream 0 */ -static inline void put_u8(void *ss, uchar b) +static inline void put_u8(rzip_control *control, void *ss, uchar b) { - if (unlikely(write_stream(ss, 0, &b, 1))) + if (unlikely(write_stream(control, ss, 0, &b, 1))) fatal("Failed to put_u8\n"); } -static inline void put_u32(void *ss, uint32_t s) +static inline void put_u32(rzip_control *control, void *ss, uint32_t s) { - if (unlikely(write_stream(ss, 0, (uchar *)&s, 4))) + if (unlikely(write_stream(control, ss, 0, (uchar *)&s, 4))) fatal("Failed to put_u32\n"); } /* Put a variable length of bytes dependant on how big the chunk is */ -static inline void put_vchars(void *ss, i64 s, int length) +static inline void put_vchars(rzip_control *control, void *ss, i64 s, int length) { int bytes; @@ -175,17 +175,17 @@ static inline void put_vchars(void *ss, i64 s, int length) int bits = bytes * 8; uchar sb = (s >> bits) & (i64)0XFF; - put_u8(ss, sb); + put_u8(control, ss, sb); } } -static void put_header(void *ss, uchar head, i64 len) +static void put_header(rzip_control *control, void *ss, uchar head, i64 len) { - put_u8(ss, head); - put_vchars(ss, len, 2); + put_u8(control, ss, head); + put_vchars(control, ss, len, 2); } -static void put_match(struct rzip_state *st, i64 p, i64 offset, i64 len) +static void put_match(rzip_control *control, struct rzip_state *st, i64 p, i64 offset, i64 len) { do { i64 ofs; @@ -194,8 +194,8 @@ static void put_match(struct rzip_state *st, i64 p, i64 offset, i64 len) n = 0xFFFF; ofs = (p - offset); - put_header(st->ss, 1, n); - put_vchars(st->ss, ofs, st->chunk_bytes); + put_header(control, st->ss, 1, n); + put_vchars(control, st->ss, ofs, st->chunk_bytes); st->stats.matches++; st->stats.match_bytes += n; @@ -206,7 +206,7 @@ static void put_match(struct rzip_state *st, i64 p, i64 offset, i64 len) } /* write some data to a stream mmap encoded. Return -1 on failure */ -int write_sbstream(void *ss, int stream, i64 p, i64 len) +int write_sbstream(rzip_control *control, void *ss, int stream, i64 p, i64 len) { struct stream_info *sinfo = ss; @@ -217,19 +217,19 @@ int write_sbstream(void *ss, int stream, i64 p, i64 len) for (i = 0; i < n; i++) { memcpy(sinfo->s[stream].buf + sinfo->s[stream].buflen + i, - get_sb(p + i), 1); + get_sb(control, p + i), 1); } sinfo->s[stream].buflen += n; p += n; len -= n; if (sinfo->s[stream].buflen == sinfo->bufsize) - flush_buffer(sinfo, stream); + flush_buffer(control, sinfo, stream); } return 0; } -static void put_literal(struct rzip_state *st, i64 last, i64 p) +static void put_literal(rzip_control *control, struct rzip_state *st, i64 last, i64 p) { do { i64 len = p - last; @@ -239,9 +239,9 @@ static void put_literal(struct rzip_state *st, i64 last, i64 p) st->stats.literals++; st->stats.literal_bytes += len; - put_header(st->ss, 0, len); + put_header(control, st->ss, 0, len); - if (unlikely(len && write_sbstream(st->ss, 1, last, len))) + if (unlikely(len && write_sbstream(control, st->ss, 1, last, len))) fatal("Failed to write_stream in put_literal\n"); last += len; } while (p > last); @@ -337,7 +337,7 @@ static void insert_hash(struct rzip_state *st, tag t, i64 offset) /* Eliminate one hash entry with minimum number of lower bits set. Returns tag requirement for any new entries. */ -static tag clean_one_from_hash(struct rzip_state *st) +static tag clean_one_from_hash(rzip_control *control, struct rzip_state *st) { tag better_than_min; @@ -364,24 +364,24 @@ again: goto again; } -static inline tag next_tag(struct rzip_state *st, i64 p, tag t) +static inline tag next_tag(rzip_control *control, struct rzip_state *st, i64 p, tag t) { - t ^= st->hash_index[*get_sb(p - 1)]; - t ^= st->hash_index[*get_sb(p + MINIMUM_MATCH - 1)]; + t ^= st->hash_index[*get_sb(control, p - 1)]; + t ^= st->hash_index[*get_sb(control, p + MINIMUM_MATCH - 1)]; return t; } -static inline tag full_tag(struct rzip_state *st, i64 p) +static inline tag full_tag(rzip_control *control, struct rzip_state *st, i64 p) { tag ret = 0; int i; for (i = 0; i < MINIMUM_MATCH; i++) - ret ^= st->hash_index[*get_sb(p + i)]; + ret ^= st->hash_index[*get_sb(control, p + i)]; return ret; } -static inline i64 match_len(struct rzip_state *st, i64 p0, i64 op, i64 end, +static inline i64 match_len(rzip_control *control, struct rzip_state *st, i64 p0, i64 op, i64 end, i64 *rev) { i64 p = p0; @@ -390,7 +390,7 @@ static inline i64 match_len(struct rzip_state *st, i64 p0, i64 op, i64 end, if (op >= p0) return 0; - while ((*get_sb(p) == *get_sb(op)) && (p < end)) { + while ((*get_sb(control, p) == *get_sb(control, op)) && (p < end)) { p++; op++; } @@ -403,7 +403,7 @@ static inline i64 match_len(struct rzip_state *st, i64 p0, i64 op, i64 end, if (end < st->last_match) end = st->last_match; - while (p > end && op > 0 && *get_sb(op - 1) == *get_sb(p - 1)) { + while (p > end && op > 0 && *get_sb(control, op - 1) == *get_sb(control, p - 1)) { op--; p--; } @@ -417,7 +417,7 @@ static inline i64 match_len(struct rzip_state *st, i64 p0, i64 op, i64 end, return len; } -static i64 find_best_match(struct rzip_state *st, tag t, i64 p, i64 end, +static i64 find_best_match(rzip_control *control, struct rzip_state *st, tag t, i64 p, i64 end, i64 *offset, i64 *reverse) { i64 length = 0; @@ -434,7 +434,7 @@ static i64 find_best_match(struct rzip_state *st, tag t, i64 p, i64 end, i64 mlen; if (t == st->hash_table[h].t) { - mlen = match_len(st, p, st->hash_table[h].offset, end, + mlen = match_len(control, st, p, st->hash_table[h].offset, end, &rev); if (mlen) @@ -457,7 +457,7 @@ static i64 find_best_match(struct rzip_state *st, tag t, i64 p, i64 end, return length; } -static void show_distrib(struct rzip_state *st) +static void show_distrib(rzip_control *control, struct rzip_state *st) { i64 primary = 0; i64 total = 0; @@ -478,7 +478,7 @@ static void show_distrib(struct rzip_state *st) primary*100.0/total); } -static void hash_search(struct rzip_state *st, double pct_base, double pct_multiple) +static void hash_search(rzip_control *control, struct rzip_state *st, double pct_base, double pct_multiple) { i64 cksum_limit = 0, p, end; tag t = 0; @@ -520,7 +520,7 @@ static void hash_search(struct rzip_state *st, double pct_base, double pct_multi current.p = p; current.ofs = 0; - t = full_tag(st, p); + t = full_tag(control, st, p); while (p < end) { int lastpct = 0, last_chunkpct = 0; @@ -528,14 +528,14 @@ static void hash_search(struct rzip_state *st, double pct_base, double pct_multi p++; sb.offset_search = p; - t = next_tag(st, p, t); + t = next_tag(control, st, p, t); /* Don't look for a match if there are no tags with this number of bits in the hash table. */ if ((t & st->minimum_tag_mask) != st->minimum_tag_mask) continue; - mlen = find_best_match(st, t, p, end, &offset, &reverse); + mlen = find_best_match(control, st, t, p, end, &offset, &reverse); /* Only insert occasionally into hash. */ if ((t & tag_mask) == tag_mask) { @@ -543,7 +543,7 @@ static void hash_search(struct rzip_state *st, double pct_base, double pct_multi st->hash_count++; insert_hash(st, t, p); if (st->hash_count > st->hash_limit) - tag_mask = clean_one_from_hash(st); + tag_mask = clean_one_from_hash(control, st); } if (mlen > current.len) { @@ -555,12 +555,12 @@ static void hash_search(struct rzip_state *st, double pct_base, double pct_multi if ((current.len >= GREAT_MATCH || p >= current.p + MINIMUM_MATCH) && current.len >= MINIMUM_MATCH) { if (st->last_match < current.p) - put_literal(st, st->last_match, current.p); - put_match(st, current.p, current.ofs, current.len); + put_literal(control, st, st->last_match, current.p); + put_match(control, st, current.p, current.ofs, current.len); st->last_match = current.p + current.len; current.p = p = st->last_match; current.len = 0; - t = full_tag(st, p); + t = full_tag(control, st, p); } if (unlikely(p % 128 == 0)) { @@ -579,25 +579,25 @@ static void hash_search(struct rzip_state *st, double pct_base, double pct_multi } if (p > (i64)cksum_limit) { - i64 i, n = MIN(st->chunk_size - p, control.page_size); + i64 i, n = MIN(st->chunk_size - p, control->page_size); uchar *ckbuf = malloc(n); if (unlikely(!ckbuf)) fatal("Failed to malloc ckbuf in hash_search\n"); for (i = 0; i < n; i++) - memcpy(ckbuf + i, get_sb(cksum_limit + i), 1); + memcpy(ckbuf + i, get_sb(control, cksum_limit + i), 1); st->cksum = CrcUpdate(st->cksum, ckbuf, n); - md5_process_bytes(ckbuf, n, &control.ctx); + md5_process_bytes(ckbuf, n, &control->ctx); cksum_limit += n; free(ckbuf); } } if (MAX_VERBOSE) - show_distrib(st); + show_distrib(control, st); if (st->last_match < st->chunk_size) - put_literal(st, st->last_match, st->chunk_size); + put_literal(control, st, st->last_match, st->chunk_size); if (st->chunk_size > cksum_limit) { i64 i, n = st->chunk_size - cksum_limit; @@ -606,15 +606,15 @@ static void hash_search(struct rzip_state *st, double pct_base, double pct_multi if (unlikely(!ckbuf)) fatal("Failed to malloc ckbuf in hash_search\n"); for (i = 0; i < n; i++) - memcpy(ckbuf + i, get_sb(cksum_limit + i), 1); + memcpy(ckbuf + i, get_sb(control, cksum_limit + i), 1); st->cksum = CrcUpdate(st->cksum, ckbuf, n); - md5_process_bytes(ckbuf, n, &control.ctx); + md5_process_bytes(ckbuf, n, &control->ctx); cksum_limit += n; free(ckbuf); } - put_literal(st, 0, 0); - put_u32(st->ss, st->cksum); + put_literal(control, st, 0, 0); + put_u32(control, st->ss, st->cksum); } @@ -639,7 +639,7 @@ static inline void *fake_mremap(void *old_address, size_t old_size, size_t new_s * anonymous ram and reading stdin into it. It means the maximum ram * we can use will be less but we will already have determined this in * rzip_chunk */ -static void mmap_stdin(uchar *buf, struct rzip_state *st) +static void mmap_stdin(rzip_control *control, uchar *buf, struct rzip_state *st) { i64 len = st->chunk_size; uchar *offset_buf = buf; @@ -669,17 +669,17 @@ static void mmap_stdin(uchar *buf, struct rzip_state *st) offset_buf += ret; len -= ret; } - control.st_size += total; + control->st_size += total; } -static void init_sliding_mmap(struct rzip_state *st, int fd_in, i64 offset) +static void init_sliding_mmap(rzip_control *control, struct rzip_state *st, int fd_in, i64 offset) { /* Initialise the high buffer */ if (!STDIN) { sb.high_length = 65536; /* Round up to the next biggest page size */ - if (sb.high_length % control.page_size) - sb.high_length += control.page_size - (sb.high_length % control.page_size); + if (sb.high_length % control->page_size) + sb.high_length += control->page_size - (sb.high_length % control->page_size); sb.buf_high = (uchar *)mmap(NULL, sb.high_length, PROT_READ, MAP_SHARED, fd_in, offset); if (unlikely(sb.buf_high == MAP_FAILED)) fatal("Unable to mmap buf_high in init_sliding_mmap\n"); @@ -695,17 +695,17 @@ static void init_sliding_mmap(struct rzip_state *st, int fd_in, i64 offset) /* compress a chunk of an open file. Assumes that the file is able to be mmap'd and is seekable */ -static void rzip_chunk(struct rzip_state *st, int fd_in, int fd_out, i64 offset, +static void rzip_chunk(rzip_control *control, struct rzip_state *st, int fd_in, int fd_out, i64 offset, double pct_base, double pct_multiple) { - init_sliding_mmap(st, fd_in, offset); + init_sliding_mmap(control, st, fd_in, offset); - st->ss = open_stream_out(fd_out, NUM_STREAMS, st->chunk_size, st->chunk_bytes); + st->ss = open_stream_out(control, fd_out, NUM_STREAMS, st->chunk_size, st->chunk_bytes); if (unlikely(!st->ss)) fatal("Failed to open streams in rzip_chunk\n"); print_verbose("Beginning rzip pre-processing phase\n"); - hash_search(st, pct_base, pct_multiple); + hash_search(control, st, pct_base, pct_multiple); /* unmap buffer before closing and reallocating streams */ if (unlikely(munmap(sb.buf_low, sb.size_low))) @@ -715,7 +715,7 @@ static void rzip_chunk(struct rzip_state *st, int fd_in, int fd_out, i64 offset, fatal("Failed to munmap in rzip_chunk\n"); } - if (unlikely(close_stream_out(st->ss))) + if (unlikely(close_stream_out(control, st->ss))) fatal("Failed to flush/close streams in rzip_chunk\n"); } @@ -723,7 +723,7 @@ static void rzip_chunk(struct rzip_state *st, int fd_in, int fd_out, i64 offset, const i64 two_gig = (1ull << 31) - 4096; /* compress a whole file chunks at a time */ -void rzip_fd(int fd_in, int fd_out) +void rzip_fd(rzip_control *control, int fd_in, int fd_out) { /* add timers for ETA estimates * Base it off the file size and number of iterations required @@ -741,7 +741,7 @@ void rzip_fd(int fd_in, int fd_out) double chunkmbs; i64 free_space; - md5_init_ctx (&control.ctx); + md5_init_ctx (&control->ctx); st = calloc(sizeof(*st), 1); if (unlikely(!st)) @@ -756,10 +756,10 @@ void rzip_fd(int fd_in, int fd_out) fatal("Failed to stat fd_in in rzip_fd\n"); if (!STDIN) { - len = control.st_size = s.st_size; + len = control->st_size = s.st_size; print_verbose("File size: %lld\n", len); } else - control.st_size = 0; + control->st_size = 0; /* Check if there's enough free space on the device chosen to fit the * compressed file, based on the compressed file being as large as the @@ -767,7 +767,7 @@ void rzip_fd(int fd_in, int fd_out) if (unlikely(fstatvfs(fd_out, &fbuf))) fatal("Failed to fstatvfs in compress_file\n"); free_space = fbuf.f_bsize * fbuf.f_bavail; - if (free_space < control.st_size) { + if (free_space < control->st_size) { if (FORCE_REPLACE) print_err("Warning, possibly inadequate free space detected, but attempting to compress due to -f option being used.\n"); else @@ -778,35 +778,35 @@ void rzip_fd(int fd_in, int fd_out) * allocate 1/3 of it to the main buffer and use a sliding mmap * buffer to work on 2/3 ram size, leaving enough ram for the * compression backends */ - control.max_mmap = control.maxram; + control->max_mmap = control->maxram; /* On 32 bits we can have a big window with sliding mmap, but can * not enable much per mmap/malloc */ if (BITS32) - control.max_mmap = MIN(control.max_mmap, two_gig); - round_to_page(&control.max_mmap); + control->max_mmap = MIN(control->max_mmap, two_gig); + round_to_page(&control->max_mmap); /* Set maximum chunk size to 2/3 of ram if not unlimited or specified * by a control window. When it's smaller than the file size, round it * to page size for efficiency. */ if (UNLIMITED) - control.max_chunk = control.st_size; - else if (control.window) - control.max_chunk = control.window * CHUNK_MULTIPLE; + control->max_chunk = control->st_size; + else if (control->window) + control->max_chunk = control->window * CHUNK_MULTIPLE; else - control.max_chunk = control.ramsize / 3 * 2; - control.max_mmap = MIN(control.max_mmap, control.max_chunk); - if (control.max_chunk < control.st_size) - round_to_page(&control.max_chunk); + control->max_chunk = control->ramsize / 3 * 2; + control->max_mmap = MIN(control->max_mmap, control->max_chunk); + if (control->max_chunk < control->st_size) + round_to_page(&control->max_chunk); if (!STDIN) - st->chunk_size = MIN(control.max_chunk, len); + st->chunk_size = MIN(control->max_chunk, len); else - st->chunk_size = control.max_mmap; + st->chunk_size = control->max_mmap; if (st->chunk_size < len) round_to_page(&st->chunk_size); - st->level = &levels[control.compression_level]; + st->level = &levels[control->compression_level]; st->fd_in = fd_in; st->fd_out = fd_out; st->stdin_eof = 0; @@ -819,15 +819,15 @@ void rzip_fd(int fd_in, int fd_out) last.tv_sec = last.tv_usec = 0; gettimeofday(&start, NULL); - prepare_streamout_threads(); + prepare_streamout_threads(control); while (len > 0 || (STDIN && !st->stdin_eof)) { double pct_base, pct_multiple; i64 offset = s.st_size - len; int bits = 8; - st->chunk_size = control.max_chunk; - st->mmap_size = control.max_mmap; + st->chunk_size = control->max_chunk; + st->mmap_size = control->max_mmap; if (!STDIN) { st->chunk_size = MIN(st->chunk_size, len); st->mmap_size = MIN(st->mmap_size, len); @@ -840,7 +840,7 @@ retry: /* Better to shrink the window to the largest size that works than fail */ if (sb.buf_low == MAP_FAILED) { if (unlikely(errno != ENOMEM)) - fatal("Failed to mmap %s\n", control.infile); + fatal("Failed to mmap %s\n", control->infile); st->mmap_size = st->mmap_size / 10 * 9; round_to_page(&st->mmap_size); if (unlikely(!st->mmap_size)) @@ -848,13 +848,13 @@ retry: goto retry; } st->chunk_size = st->mmap_size; - mmap_stdin(sb.buf_low, st); + mmap_stdin(control, sb.buf_low, st); } else { /* NOTE The buf is saved here for !STDIN mode */ sb.buf_low = (uchar *)mmap(sb.buf_low, st->mmap_size, PROT_READ, MAP_SHARED, fd_in, offset); if (sb.buf_low == MAP_FAILED) { if (unlikely(errno != ENOMEM)) - fatal("Failed to mmap %s\n", control.infile); + fatal("Failed to mmap %s\n", control->infile); st->mmap_size = st->mmap_size / 10 * 9; round_to_page(&st->mmap_size); if (unlikely(!st->mmap_size)) @@ -866,7 +866,7 @@ retry: } print_maxverbose("Succeeded in testing %lld sized mmap for rzip pre-processing\n", st->mmap_size); - if (st->chunk_size > control.ramsize) + if (st->chunk_size > control->ramsize) print_verbose("Compression window is larger than ram, will proceed with unlimited mode possibly much slower\n"); if (!passes && !STDIN) { @@ -893,10 +893,10 @@ retry: print_maxverbose("Byte width: %d\n", st->chunk_bytes); if (STDIN) - pct_base = (100.0 * -len) / control.st_size; + pct_base = (100.0 * -len) / control->st_size; else - pct_base = (100.0 * (control.st_size - len)) / control.st_size; - pct_multiple = ((double)st->chunk_size) / control.st_size; + pct_base = (100.0 * (control->st_size - len)) / control->st_size; + pct_multiple = ((double)st->chunk_size) / control->st_size; pass++; if (st->stdin_eof) passes = pass; @@ -929,28 +929,28 @@ retry: last.tv_sec = current.tv_sec; last.tv_usec = current.tv_usec; - rzip_chunk(st, fd_in, fd_out, offset, pct_base, pct_multiple); + rzip_chunk(control, st, fd_in, fd_out, offset, pct_base, pct_multiple); /* st->chunk_size may be shrunk in rzip_chunk */ last_chunk = st->chunk_size; len -= st->chunk_size; } - close_streamout_threads(); + close_streamout_threads(control); - md5_finish_ctx (&control.ctx, md5_resblock); + md5_finish_ctx (&control->ctx, md5_resblock); if (HASH_CHECK || MAX_VERBOSE) { print_output("MD5: "); for (j = 0; j < MD5_DIGEST_SIZE; j++) print_output("%02x", md5_resblock[j] & 0xFF); print_output("\n"); } - if (unlikely(write(control.fd_out, md5_resblock, MD5_DIGEST_SIZE) != MD5_DIGEST_SIZE)) + if (unlikely(write(control->fd_out, md5_resblock, MD5_DIGEST_SIZE) != MD5_DIGEST_SIZE)) fatal("Failed to write md5 in rzip_fd\n"); gettimeofday(¤t, NULL); if (STDIN) - s.st_size = control.st_size; + s.st_size = control->st_size; chunkmbs = (s.st_size / 1024 / 1024) / ((double)(current.tv_sec-start.tv_sec)? : 1); fstat(fd_out, &s2); @@ -966,7 +966,7 @@ retry: (1.0 + st->stats.match_bytes) / st->stats.literal_bytes); if (!STDIN) - print_progress("%s - ", control.infile); + print_progress("%s - ", control->infile); print_progress("Compression Ratio: %.3f. Average Compression Speed: %6.3fMB/s.\n", 1.0 * s.st_size / s2.st_size, chunkmbs); diff --git a/rzip.h b/rzip.h index 3f559ec..2179f01 100644 --- a/rzip.h +++ b/rzip.h @@ -17,14 +17,10 @@ along with this program. If not, see . */ -#define LRZIP_MAJOR_VERSION 0 -#define LRZIP_MINOR_VERSION 5 -#define LRZIP_MINOR_SUBVERSION 71 - -#define NUM_STREAMS 2 -#define STREAM_BUFSIZE (1024 * 1024 * 10) - -#include "config.h" +#ifndef RZIP_H +#define RZIP_H +#include "lrzip.h" /* includes config.h */ +#include "liblrzip.h" #include "md5.h" #include @@ -75,76 +71,6 @@ /* needed for CRC routines */ #include "lzma/C/7zCrc.h" -#ifndef uchar -#define uchar unsigned char -#endif - -#ifndef int32 -#if (SIZEOF_INT == 4) -#define int32 int -#elif (SIZEOF_LONG == 4) -#define int32 long -#elif (SIZEOF_SHORT == 4) -#define int32 short -#endif -#endif - -#ifndef int16 -#if (SIZEOF_INT == 2) -#define int16 int -#elif (SIZEOF_SHORT == 2) -#define int16 short -#endif -#endif - -#ifndef uint32 -#define uint32 unsigned int32 -#endif - -#ifndef uint16 -#define uint16 unsigned int16 -#endif - -#ifndef MIN -#define MIN(a, b) ((a) < (b)? (a): (b)) -#endif - -#ifndef MAX -#define MAX(a, b) ((a) > (b)? (a): (b)) -#endif - -#if !HAVE_STRERROR -extern char *sys_errlist[]; -#define strerror(i) sys_errlist[i] -#endif - -#ifndef HAVE_ERRNO_H -extern int errno; -#endif - -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) - -typedef long long int i64; -typedef uint16_t u16; -typedef uint32_t u32; - -#ifndef MAP_ANONYMOUS - #define MAP_ANONYMOUS MAP_ANON -#endif - -#if defined(NOTHREAD) || !defined(_SC_NPROCESSORS_ONLN) - #define PROCESSORS (1) -#else - #define PROCESSORS (sysconf(_SC_NPROCESSORS_ONLN)) -#endif - -#ifdef _SC_PAGE_SIZE - #define PAGE_SIZE (sysconf(_SC_PAGE_SIZE)) -#else - #define PAGE_SIZE (4096) -#endif - void fatal(const char *format, ...); void failure(const char *format, ...); @@ -222,33 +148,6 @@ static inline i64 get_ram(void) #define FLAG_KEEP_BROKEN (1 << 19) #define FLAG_THRESHOLD (1 << 20) -#define FLAG_VERBOSE (FLAG_VERBOSITY | FLAG_VERBOSITY_MAX) -#define FLAG_NOT_LZMA (FLAG_NO_COMPRESS | FLAG_LZO_COMPRESS | FLAG_BZIP2_COMPRESS | FLAG_ZLIB_COMPRESS | FLAG_ZPAQ_COMPRESS) -#define LZMA_COMPRESS (!(control.flags & FLAG_NOT_LZMA)) - -#define SHOW_PROGRESS (control.flags & FLAG_SHOW_PROGRESS) -#define KEEP_FILES (control.flags & FLAG_KEEP_FILES) -#define TEST_ONLY (control.flags & FLAG_TEST_ONLY) -#define FORCE_REPLACE (control.flags & FLAG_FORCE_REPLACE) -#define DECOMPRESS (control.flags & FLAG_DECOMPRESS) -#define NO_COMPRESS (control.flags & FLAG_NO_COMPRESS) -#define LZO_COMPRESS (control.flags & FLAG_LZO_COMPRESS) -#define BZIP2_COMPRESS (control.flags & FLAG_BZIP2_COMPRESS) -#define ZLIB_COMPRESS (control.flags & FLAG_ZLIB_COMPRESS) -#define ZPAQ_COMPRESS (control.flags & FLAG_ZPAQ_COMPRESS) -#define VERBOSE (control.flags & FLAG_VERBOSE) -#define VERBOSITY (control.flags & FLAG_VERBOSITY) -#define MAX_VERBOSE (control.flags & FLAG_VERBOSITY_MAX) -#define STDIN (control.flags & FLAG_STDIN) -#define STDOUT (control.flags & FLAG_STDOUT) -#define INFO (control.flags & FLAG_INFO) -#define UNLIMITED (control.flags & FLAG_UNLIMITED) -#define HASH_CHECK (control.flags & FLAG_HASH) -#define HAS_MD5 (control.flags & FLAG_MD5) -#define CHECK_FILE (control.flags & FLAG_CHECK) -#define KEEP_BROKEN (control.flags & FLAG_KEEP_BROKEN) -#define LZO_TEST (control.flags & FLAG_THRESHOLD) - #define NO_MD5 (!(HASH_CHECK) && !(HAS_MD5)) #define BITS32 (sizeof(long) == 4) @@ -285,8 +184,9 @@ struct rzip_control { long page_size; int fd_out; struct md5_ctx ctx; + void *data; // random data pointer associated for use in callbacks i64 md5_read; // How far into the file the md5 has done so far -} control; +}; struct stream { i64 last_head; @@ -316,52 +216,36 @@ struct stream_info { }; void sighandler(); -i64 runzip_fd(int fd_in, int fd_out, int fd_hist, i64 expected_size); -void rzip_fd(int fd_in, int fd_out); -void *open_stream_out(int f, int n, i64 limit, char cbytes); -void *open_stream_in(int f, int n); -int write_stream(void *ss, int stream, uchar *p, i64 len); -i64 read_stream(void *ss, int stream, uchar *p, i64 len); -int close_stream_out(void *ss); +i64 runzip_fd(rzip_control *control, int fd_in, int fd_out, int fd_hist, i64 expected_size); +void rzip_fd(rzip_control *control, int fd_in, int fd_out); +void *open_stream_out(rzip_control *control, int f, int n, i64 limit, char cbytes); +void *open_stream_in(rzip_control *control, int f, int n); +int write_stream(rzip_control *control, void *ss, int stream, uchar *p, i64 len); +i64 read_stream(rzip_control *control, void *ss, int stream, uchar *p, i64 len); +int close_stream_out(rzip_control *control, void *ss); int close_stream_in(void *ss); -void flush_buffer(struct stream_info *sinfo, int stream); +void flush_buffer(rzip_control *control, struct stream_info *sinfo, int stream); void read_config(struct rzip_control *s); ssize_t write_1g(int fd, void *buf, i64 len); ssize_t read_1g(int fd, void *buf, i64 len); void zpipe_compress(FILE *in, FILE *out, FILE *msgout, long long int buf_len, int progress, long thread); void zpipe_decompress(FILE *in, FILE *out, FILE *msgout, long long int buf_len, int progress, long thread); const i64 two_gig; -void prepare_streamout_threads(void); -void close_streamout_threads(void); +void prepare_streamout_threads(rzip_control *control); +void close_streamout_threads(rzip_control *control); void round_to_page(i64 *size); -void dump_tmpoutfile(int fd_out); + +void register_infile(const char *name, char delete); +void register_outfile(const char *name, char delete); +void register_outputfile(FILE *f); #define print_err(format, args...) do {\ fprintf(stderr, format, ##args); \ } while (0) -#define print_output(format, args...) do {\ - fprintf(control.msgout, format, ##args); \ - fflush(control.msgout); \ -} while (0) - -#define print_progress(format, args...) do {\ - if (SHOW_PROGRESS) \ - print_output(format, ##args); \ -} while (0) - -#define print_verbose(format, args...) do {\ - if (VERBOSE) \ - print_output(format, ##args); \ -} while (0) - -#define print_maxverbose(format, args...) do {\ - if (MAX_VERBOSE) \ - print_output(format, ##args); \ -} while (0) - /* Macros for testing parameters */ #define isparameter( parmstring, value ) (!strcasecmp( parmstring, value )) #define iscaseparameter( parmvalue, value ) (!strcmp( parmvalue, value )) +#endif diff --git a/stream.c b/stream.c index dbcd001..a6fb6fa 100644 --- a/stream.c +++ b/stream.c @@ -88,7 +88,7 @@ void join_pthread(pthread_t th, void **thread_return) /* just to keep things clean, declare function here * but move body to the end since it's a work function */ -static int lzo_compresses(uchar *s_buf, i64 s_len); +static int lzo_compresses(rzip_control *control, uchar *s_buf, i64 s_len); static inline FILE *fake_fmemopen(void *buf, size_t buflen, char *mode) { @@ -145,13 +145,13 @@ static inline int fake_open_memstream_update_buffer(FILE *fp, uchar **buf, size_ length in c_len */ -static int zpaq_compress_buf(struct compress_thread *cthread, long thread) +static int zpaq_compress_buf(rzip_control *control, struct compress_thread *cthread, long thread) { uchar *c_buf = NULL; size_t dlen = 0; FILE *in, *out; - if (!lzo_compresses(cthread->s_buf, cthread->s_len)) + if (!lzo_compresses(control, cthread->s_buf, cthread->s_len)) return 0; in = fmemopen(cthread->s_buf, cthread->s_len, "r"); @@ -166,7 +166,7 @@ static int zpaq_compress_buf(struct compress_thread *cthread, long thread) return -1; } - zpipe_compress(in, out, control.msgout, cthread->s_len, + zpipe_compress(in, out, control->msgout, cthread->s_len, (int)(SHOW_PROGRESS), thread); if (unlikely(memstream_update_buffer(out, &c_buf, &dlen))) @@ -189,13 +189,13 @@ static int zpaq_compress_buf(struct compress_thread *cthread, long thread) return 0; } -static int bzip2_compress_buf(struct compress_thread *cthread) +static int bzip2_compress_buf(rzip_control *control, struct compress_thread *cthread) { u32 dlen = cthread->s_len; int bzip2_ret; uchar *c_buf; - if (!lzo_compresses(cthread->s_buf, cthread->s_len)) + if (!lzo_compresses(control, cthread->s_buf, cthread->s_len)) return 0; c_buf = malloc(dlen); @@ -206,7 +206,7 @@ static int bzip2_compress_buf(struct compress_thread *cthread) bzip2_ret = BZ2_bzBuffToBuffCompress((char *)c_buf, &dlen, (char *)cthread->s_buf, cthread->s_len, - control.compression_level, 0, control.compression_level * 10); + control->compression_level, 0, control->compression_level * 10); /* if compressed data is bigger then original data leave as * CTYPE_NONE */ @@ -238,7 +238,7 @@ static int bzip2_compress_buf(struct compress_thread *cthread) return 0; } -static int gzip_compress_buf(struct compress_thread *cthread) +static int gzip_compress_buf(rzip_control *control, struct compress_thread *cthread) { unsigned long dlen = cthread->s_len; uchar *c_buf; @@ -251,7 +251,7 @@ static int gzip_compress_buf(struct compress_thread *cthread) } gzip_ret = compress2(c_buf, &dlen, cthread->s_buf, cthread->s_len, - control.compression_level); + control->compression_level); /* if compressed data is bigger then original data leave as * CTYPE_NONE */ @@ -283,18 +283,18 @@ static int gzip_compress_buf(struct compress_thread *cthread) return 0; } -static int lzma_compress_buf(struct compress_thread *cthread) +static int lzma_compress_buf(rzip_control *control, struct compress_thread *cthread) { int lzma_level, lzma_ret; size_t prop_size = 5; /* return value for lzma_properties */ uchar *c_buf; size_t dlen; - if (!lzo_compresses(cthread->s_buf, cthread->s_len)) + if (!lzo_compresses(control, cthread->s_buf, cthread->s_len)) return 0; /* only 7 levels with lzma, scale them */ - lzma_level = control.compression_level * 7 / 9 ? : 1; + lzma_level = control->compression_level * 7 / 9 ? : 1; print_verbose("Starting lzma back end compression thread...\n"); retry: @@ -309,11 +309,11 @@ retry: * and receive properties in control->lzma_properties */ lzma_ret = LzmaCompress(c_buf, &dlen, cthread->s_buf, - (size_t)cthread->s_len, control.lzma_properties, &prop_size, + (size_t)cthread->s_len, control->lzma_properties, &prop_size, lzma_level, 0, /* dict size. set default, choose by level */ -1, -1, -1, -1, /* lc, lp, pb, fb */ - control.threads); + control->threads); if (lzma_ret != SZ_OK) { switch (lzma_ret) { case SZ_ERROR_MEM: @@ -343,7 +343,7 @@ retry: * fall back to bzip2 compression so the block doesn't * remain uncompressed */ print_verbose("Unable to allocate enough RAM for any sized compression window, falling back to bzip2 compression.\n"); - return bzip2_compress_buf(cthread); + return bzip2_compress_buf(control, cthread); } else if (lzma_ret == SZ_ERROR_OUTPUT_EOF) return 0; return -1; @@ -363,7 +363,7 @@ retry: return 0; } -static int lzo_compress_buf(struct compress_thread *cthread) +static int lzo_compress_buf(rzip_control *control, struct compress_thread *cthread) { lzo_uint in_len = cthread->s_len; lzo_uint dlen = in_len + in_len / 16 + 64 + 3; @@ -411,7 +411,7 @@ out_free: try to decompress a buffer. Return 0 on success and -1 on failure. */ -static int zpaq_decompress_buf(struct uncomp_thread *ucthread, long thread) +static int zpaq_decompress_buf(rzip_control *control, struct uncomp_thread *ucthread, long thread) { uchar *c_buf = NULL; size_t dlen = 0; @@ -428,7 +428,7 @@ static int zpaq_decompress_buf(struct uncomp_thread *ucthread, long thread) return -1; } - zpipe_decompress(in, out, control.msgout, ucthread->u_len, (int)(SHOW_PROGRESS), thread); + zpipe_decompress(in, out, control->msgout, ucthread->u_len, (int)(SHOW_PROGRESS), thread); if (unlikely(memstream_update_buffer(out, &c_buf, &dlen))) fatal("Failed to memstream_update_buffer in zpaq_decompress_buf"); @@ -447,7 +447,7 @@ static int zpaq_decompress_buf(struct uncomp_thread *ucthread, long thread) return 0; } -static int bzip2_decompress_buf(struct uncomp_thread *ucthread) +static int bzip2_decompress_buf(rzip_control *control __UNUSED__, struct uncomp_thread *ucthread) { u32 dlen = ucthread->u_len; int ret = 0, bzerr; @@ -481,7 +481,7 @@ out: return ret; } -static int gzip_decompress_buf(struct uncomp_thread *ucthread) +static int gzip_decompress_buf(rzip_control *control __UNUSED__, struct uncomp_thread *ucthread) { unsigned long dlen = ucthread->u_len; int ret = 0, gzerr; @@ -515,7 +515,7 @@ out: return ret; } -static int lzma_decompress_buf(struct uncomp_thread *ucthread) +static int lzma_decompress_buf(rzip_control *control, struct uncomp_thread *ucthread) { size_t dlen = (size_t)ucthread->u_len; int ret = 0, lzmaerr; @@ -529,9 +529,9 @@ static int lzma_decompress_buf(struct uncomp_thread *ucthread) goto out; } - /* With LZMA SDK 4.63 we pass control.lzma_properties + /* With LZMA SDK 4.63 we pass control->lzma_properties * which is needed for proper uncompress */ - lzmaerr = LzmaUncompress(ucthread->s_buf, &dlen, c_buf, (SizeT *)&ucthread->c_len, control.lzma_properties, 5); + lzmaerr = LzmaUncompress(ucthread->s_buf, &dlen, c_buf, (SizeT *)&ucthread->c_len, control->lzma_properties, 5); if (unlikely(lzmaerr)) { print_err("Failed to decompress buffer - lzmaerr=%d\n", lzmaerr); ret = -1; @@ -551,7 +551,7 @@ out: return ret; } -static int lzo_decompress_buf(struct uncomp_thread *ucthread) +static int lzo_decompress_buf(rzip_control *control __UNUSED__, struct uncomp_thread *ucthread) { lzo_uint dlen = ucthread->u_len; int ret = 0, lzerr; @@ -718,7 +718,7 @@ static int seekto(struct stream_info *sinfo, i64 pos) static pthread_t *threads; -void prepare_streamout_threads(void) +void prepare_streamout_threads(rzip_control *control) { int i; @@ -726,19 +726,19 @@ void prepare_streamout_threads(void) * pre-processing stage, it's faster to have one more thread available * to keep all CPUs busy. There is no point splitting up the chunks * into multiple threads if there will be no compression back end. */ - if (control.threads > 1) - ++control.threads; + if (control->threads > 1) + ++control->threads; if (NO_COMPRESS) - control.threads = 1; - threads = calloc(sizeof(pthread_t), control.threads); + control->threads = 1; + threads = calloc(sizeof(pthread_t), control->threads); if (unlikely(!threads)) fatal("Unable to calloc threads in prepare_streamout_threads\n"); - cthread = calloc(sizeof(struct compress_thread), control.threads); + cthread = calloc(sizeof(struct compress_thread), control->threads); if (unlikely(!cthread)) fatal("Unable to calloc cthread in prepare_streamout_threads\n"); - for (i = 0; i < control.threads; i++) + for (i = 0; i < control->threads; i++) init_mutex(&cthread[i].mutex); } @@ -746,15 +746,15 @@ static long output_thread; static pthread_mutex_t output_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t output_cond = PTHREAD_COND_INITIALIZER; -void close_streamout_threads(void) +void close_streamout_threads(rzip_control *control) { int i, close_thread = output_thread; /* Wait for the threads in the correct order in case they end up * serialised */ - for (i = 0; i < control.threads; i++) { + for (i = 0; i < control->threads; i++) { lock_mutex(&cthread[close_thread].mutex); - if (++close_thread == control.threads) + if (++close_thread == control->threads) close_thread = 0; } free(cthread); @@ -763,7 +763,7 @@ void close_streamout_threads(void) /* open a set of output streams, compressing with the given compression level and algorithm */ -void *open_stream_out(int f, int n, i64 chunk_limit, char cbytes) +void *open_stream_out(rzip_control *control, int f, int n, i64 chunk_limit, char cbytes) { struct stream_info *sinfo; i64 testsize, limit; @@ -799,24 +799,24 @@ void *open_stream_out(int f, int n, i64 chunk_limit, char cbytes) /* Serious limits imposed on 32 bit capabilities */ if (BITS32) limit = MIN(limit, (two_gig / testbufs) - - (control.overhead * control.threads)); + (control->overhead * control->threads)); - testsize = (limit * testbufs) + (control.overhead * control.threads); - if (testsize > control.maxram) - limit = (control.maxram - (control.overhead * control.threads)) / testbufs; + testsize = (limit * testbufs) + (control->overhead * control->threads); + if (testsize > control->maxram) + limit = (control->maxram - (control->overhead * control->threads)) / testbufs; /* If we don't have enough ram for the number of threads, decrease the * number of threads till we do, or only have one thread. */ while (limit < STREAM_BUFSIZE && limit < chunk_limit) { - if (control.threads > 1) - --control.threads; + if (control->threads > 1) + --control->threads; else break; - limit = (control.maxram - (control.overhead * control.threads)) / testbufs; + limit = (control->maxram - (control->overhead * control->threads)) / testbufs; limit = MIN(limit, chunk_limit); } retest_malloc: - testsize = (limit * testbufs) + (control.overhead * control.threads); + testsize = (limit * testbufs) + (control->overhead * control->threads); testmalloc = malloc(testsize); if (!testmalloc) { limit = limit / 10 * 9; @@ -827,12 +827,12 @@ retest_malloc: /* Make the bufsize no smaller than STREAM_BUFSIZE. Round up the * bufsize to fit X threads into it */ - sinfo->bufsize = MIN(limit, MAX((limit + control.threads - 1) / control.threads, + sinfo->bufsize = MIN(limit, MAX((limit + control->threads - 1) / control->threads, STREAM_BUFSIZE)); - if (control.threads > 1) + if (control->threads > 1) print_maxverbose("Using up to %d threads to compress up to %lld bytes each.\n", - control.threads, sinfo->bufsize); + control->threads, sinfo->bufsize); else print_maxverbose("Using only 1 thread to compress up to %lld bytes\n", sinfo->bufsize); @@ -847,7 +847,7 @@ retest_malloc: } /* prepare a set of n streams for reading on file descriptor f */ -void *open_stream_in(int f, int n) +void *open_stream_in(rzip_control *control, int f, int n) { struct stream_info *sinfo; int total_threads, i; @@ -859,10 +859,10 @@ void *open_stream_in(int f, int n) /* We have one thread dedicated to stream 0, and one more thread than * CPUs to keep them busy, unless we're running single-threaded. */ - if (control.threads > 1) - total_threads = control.threads + 2; + if (control->threads > 1) + total_threads = control->threads + 2; else - total_threads = control.threads + 1; + total_threads = control->threads + 1; threads = calloc(sizeof(pthread_t), total_threads); if (unlikely(!threads)) return NULL; @@ -897,7 +897,7 @@ again: goto failed; /* Compatibility crap for versions < 0.40 */ - if (control.major_version == 0 && control.minor_version < 4) { + if (control->major_version == 0 && control->minor_version < 4) { u32 v132, v232, last_head32; if (unlikely(read_u32(f, &v132))) @@ -953,14 +953,16 @@ failed: /* Enter with s_buf allocated,s_buf points to the compressed data after the * backend compression and is then freed here */ -static void *compthread(void *t) +static void *compthread(void *data) { - long i = (long)t; + rzip_control *control = data; + long i = (long)control->data; + control->data = NULL; struct compress_thread *cti = &cthread[i]; struct stream_info *ctis = cti->sinfo; int waited = 0, ret = 0; - if (unlikely(setpriority(PRIO_PROCESS, 0, control.nice_val) == -1)) + if (unlikely(setpriority(PRIO_PROCESS, 0, control->nice_val) == -1)) print_err("Warning, unable to set nice value on thread\n"); cti->c_type = CTYPE_NONE; @@ -972,15 +974,15 @@ static void *compthread(void *t) retry: if (!NO_COMPRESS && cti->c_len) { if (LZMA_COMPRESS) - ret = lzma_compress_buf(cti); + ret = lzma_compress_buf(control, cti); else if (LZO_COMPRESS) - ret = lzo_compress_buf(cti); + ret = lzo_compress_buf(control, cti); else if (BZIP2_COMPRESS) - ret = bzip2_compress_buf(cti); + ret = bzip2_compress_buf(control, cti); else if (ZLIB_COMPRESS) - ret = gzip_compress_buf(cti); + ret = gzip_compress_buf(control, cti); else if (ZPAQ_COMPRESS) - ret = zpaq_compress_buf(cti, i); + ret = zpaq_compress_buf(control, cti, i); else failure("Dunno wtf compression to use!\n"); } @@ -1047,7 +1049,7 @@ retry: free(cti->s_buf); lock_mutex(&output_lock); - if (++output_thread == control.threads) + if (++output_thread == control->threads) output_thread = 0; cond_broadcast(&output_cond); unlock_mutex(&output_lock); @@ -1057,7 +1059,7 @@ retry: return 0; } -static void clear_buffer(struct stream_info *sinfo, int stream, int newbuf) +static void clear_buffer(rzip_control *control, struct stream_info *sinfo, int stream, int newbuf) { static long i = 0; @@ -1071,7 +1073,8 @@ static void clear_buffer(struct stream_info *sinfo, int stream, int newbuf) print_maxverbose("Starting thread %ld to compress %lld bytes from stream %d\n", i, cthread[i].s_len, stream); - create_pthread(&threads[i], NULL, compthread, (void *)i); + control->data = (void*)i; + create_pthread(&threads[i], NULL, compthread, control); if (newbuf) { /* The stream buffer has been given to the thread, allocate a new one */ @@ -1081,42 +1084,44 @@ static void clear_buffer(struct stream_info *sinfo, int stream, int newbuf) sinfo->s[stream].buflen = 0; } - if (++i == control.threads) + if (++i == control->threads) i = 0; } /* flush out any data in a stream buffer */ -void flush_buffer(struct stream_info *sinfo, int stream) +void flush_buffer(rzip_control *control, struct stream_info *sinfo, int stream) { - clear_buffer(sinfo, stream, 1); + clear_buffer(control, sinfo, stream, 1); } -static void *ucompthread(void *t) +static void *ucompthread(void *data) { - long i = (long)t; + rzip_control *control = data; + long i = (long)control->data; + control->data = NULL; struct uncomp_thread *uci = &ucthread[i]; int waited = 0, ret = 0; - if (unlikely(setpriority(PRIO_PROCESS, 0, control.nice_val) == -1)) + if (unlikely(setpriority(PRIO_PROCESS, 0, control->nice_val) == -1)) print_err("Warning, unable to set nice value on thread\n"); retry: if (uci->c_type != CTYPE_NONE) { switch (uci->c_type) { case CTYPE_LZMA: - ret = lzma_decompress_buf(uci); + ret = lzma_decompress_buf(control, uci); break; case CTYPE_LZO: - ret = lzo_decompress_buf(uci); + ret = lzo_decompress_buf(control, uci); break; case CTYPE_BZIP2: - ret = bzip2_decompress_buf(uci); + ret = bzip2_decompress_buf(control, uci); break; case CTYPE_GZIP: - ret = gzip_decompress_buf(uci); + ret = gzip_decompress_buf(control, uci); break; case CTYPE_ZPAQ: - ret = zpaq_decompress_buf(uci, i); + ret = zpaq_decompress_buf(control, uci, i); break; default: failure("Dunno wtf decompression type to use!\n"); @@ -1147,7 +1152,7 @@ retry: } /* fill a buffer from a stream - return -1 on failure */ -static int fill_buffer(struct stream_info *sinfo, int stream) +static int fill_buffer(rzip_control *control, struct stream_info *sinfo, int stream) { i64 header_length, u_len, c_len, last_head; struct stream *s = &sinfo->s[stream]; @@ -1168,7 +1173,7 @@ fill_another: return -1; /* Compatibility crap for versions < 0.4 */ - if (control.major_version == 0 && control.minor_version < 4) { + if (control->major_version == 0 && control->minor_version < 4) { u32 c_len32, u_len32, last_head32; if (unlikely(read_u32(sinfo->fd, &c_len32))) @@ -1193,7 +1198,7 @@ fill_another: sinfo->total_read += header_length; - fsync(control.fd_out); + fsync(control->fd_out); s_buf = malloc(u_len); if (unlikely(u_len && !s_buf)) @@ -1216,7 +1221,8 @@ fill_another: ucthread[s->uthread_no].busy = 1; print_maxverbose("Starting thread %ld to decompress %lld bytes from stream %d\n", s->uthread_no, c_len, stream); - create_pthread(&threads[s->uthread_no], NULL, ucompthread, (void *)s->uthread_no); + control->data = (void*)s->uthread_no; + create_pthread(&threads[s->uthread_no], NULL, ucompthread, control); if (++s->uthread_no == s->base_thread + s->total_threads) s->uthread_no = s->base_thread; @@ -1228,7 +1234,7 @@ fill_another: if (!last_head) s->eos = 1; else if (s->uthread_no != s->unext_thread && !ucthread[s->uthread_no].busy && - sinfo->ram_alloced < control.ramsize / 3) + sinfo->ram_alloced < control->ramsize / 3) goto fill_another; out: lock_mutex(&output_lock); @@ -1253,7 +1259,7 @@ out: } /* write some data to a stream. Return -1 on failure */ -int write_stream(void *ss, int stream, uchar *p, i64 len) +int write_stream(rzip_control *control, void *ss, int stream, uchar *p, i64 len) { struct stream_info *sinfo = ss; @@ -1269,14 +1275,14 @@ int write_stream(void *ss, int stream, uchar *p, i64 len) /* Flush the buffer every sinfo->bufsize into one thread */ if (sinfo->s[stream].buflen == sinfo->bufsize) - flush_buffer(sinfo, stream); + flush_buffer(control, sinfo, stream); } return 0; } /* read some data from a stream. Return number of bytes read, or -1 on failure */ -i64 read_stream(void *ss, int stream, uchar *p, i64 len) +i64 read_stream(rzip_control *control, void *ss, int stream, uchar *p, i64 len) { struct stream_info *sinfo = ss; i64 ret = 0; @@ -1295,7 +1301,7 @@ i64 read_stream(void *ss, int stream, uchar *p, i64 len) } if (len && sinfo->s[stream].bufp == sinfo->s[stream].buflen) { - if (unlikely(fill_buffer(sinfo, stream))) + if (unlikely(fill_buffer(control, sinfo, stream))) return -1; if (sinfo->s[stream].bufp == sinfo->s[stream].buflen) break; @@ -1306,14 +1312,14 @@ i64 read_stream(void *ss, int stream, uchar *p, i64 len) } /* flush and close down a stream. return -1 on failure */ -int close_stream_out(void *ss) +int close_stream_out(rzip_control *control, void *ss) { struct stream_info *sinfo = ss; int i; for (i = 0; i < sinfo->num_streams; i++) { if (sinfo->s[i].buflen) - clear_buffer(sinfo, i, 0); + clear_buffer(control, sinfo, i, 0); } #if 0 @@ -1351,7 +1357,7 @@ int close_stream_in(void *ss) to see if there is any compression at all with lzo first. It is unlikely that others will be able to compress if lzo is unable to drop a single byte so do not compress any block that is incompressible by lzo. */ -static int lzo_compresses(uchar *s_buf, i64 s_len) +static int lzo_compresses(rzip_control *control, uchar *s_buf, i64 s_len) { lzo_bytep wrkmem = NULL; lzo_uint in_len, test_len = s_len, save_len = s_len; diff --git a/util.c b/util.c index 04c359e..b5ca605 100644 --- a/util.c +++ b/util.c @@ -32,20 +32,44 @@ #include "rzip.h" +static const char *infile = NULL; +static char delete_infile = 0; +static const char *outfile = NULL; +static char delete_outfile = 0; +static FILE *outputfile = NULL; + +void register_infile(const char *name, char delete) +{ + infile = name; + delete_infile = delete; +} + +void register_outfile(const char *name, char delete) +{ + outfile = name; + delete_outfile = delete; +} + +void register_outputfile(FILE *f) +{ + outputfile = f; +} + static void unlink_files(void) { /* Delete temporary files generated for testing or faking stdio */ - if (TEST_ONLY || STDOUT || !KEEP_BROKEN) - unlink(control.outfile); + if (outfile && delete_outfile) + unlink(outfile); - if ((DECOMPRESS || TEST_ONLY) && STDIN) - unlink(control.infile); + if (infile && delete_infile) + unlink(infile); } static void fatal_exit(void) { unlink_files(); - print_output("Fatal error - exiting\n"); + fprintf(outputfile, "Fatal error - exiting\n"); + fflush(outputfile); exit(1); } @@ -85,9 +109,9 @@ void sighandler() void round_to_page(i64 *size) { - *size -= *size % control.page_size; + *size -= *size % PAGE_SIZE; if (unlikely(!*size)) - *size = control.page_size; + *size = PAGE_SIZE; } void read_config( struct rzip_control *control )