Commit message: Fix control->suffix being deallocated as heap memory as reported by Pietro Borrello.
CVE: CVE-2022-28044
CWE: CWE-703
Vulnerability description: Irzip v0.640 was discovered to contain a heap memory corruption via the component lrzip.c:initialise_control.

Project: lrzip
File: lrzip.c

static bool preserve_times(rzip_control *control, int fd_in)
{
	struct utimbuf times;
	struct stat st;

	if (unlikely(fstat(fd_in, &st)))
		fatal_return(("Failed to fstat input file\n"), false);
	times.actime = 0;
	times.modtime = st.st_mtime;
	if (unlikely(utime(control->outfile, &times)))
		print_verbose("Warning, unable to set time on %s\n", control->outfile);

	return true;
}

Project: lrzip
File: lrzip.c

bool read_tmpinfile(rzip_control *control, int fd_in)
{
	FILE *tmpinfp;
	int tmpchar;

	if (fd_in == -1)
		return false;
	if (control->flags & FLAG_SHOW_PROGRESS)
		fprintf(control->msgout, "Copying from stdin.\n");
	tmpinfp = fdopen(fd_in, "w+");
	if (unlikely(tmpinfp == NULL))
		fatal_return(("Failed to fdopen in tmpfile\n"), false);

	while ((tmpchar = getchar()) != EOF)
		fputc(tmpchar, tmpinfp);

	fflush(tmpinfp);
	rewind(tmpinfp);
	return true;
}

Project: lrzip
File: lrzip.c

static bool get_magic(rzip_control *control, char *magic)
{
	int encrypted, md5, i;
	i64 expected_size;
	uint32_t v;

	if (unlikely(strncmp(magic, "LRZI", 4)))
		failure_return(("Not an lrzip file\n"), false);

	memcpy(&control->major_version, &magic[4], 1);
	memcpy(&control->minor_version, &magic[5], 1);

	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);

	/* 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);
		expected_size = le64toh(expected_size);
	}
	control->st_size = expected_size;
	if (control->major_version == 0 && control->minor_version < 6)
		control->eof = 1;

	/* restore LZMA compression flags only if stored */
	if ((int) magic[16]) {
		for (i = 0; i < 5; i++)
			control->lzma_properties[i] = magic[i + 16];
		/* Cludge to allow us to read possibly corrupted archives */
		if (!control->lzma_properties[0])
			control->lzma_properties[0] = 93;
	}

	/* Whether this archive contains md5 data at the end or not */
	md5 = magic[21];
	if (md5 && MD5_RELIABLE) {
		if (md5 == 1)
			control->flags |= FLAG_MD5;
		else
			print_verbose("Unknown hash, falling back to CRC\n");
	}
	encrypted = magic[22];
	if (encrypted) {
		if (encrypted == 1)
			control->flags |= FLAG_ENCRYPT;
		else
			failure_return(("Unknown encryption\n"), false);
		/* In encrypted files, the size field is used to store the salt
		 * instead and the size is unknown, just like a STDOUT chunked
		 * file */
		memcpy(&control->salt, &magic[6], 8);
		control->st_size = expected_size = 0;
		control->encloops = enc_loops(control->salt[0], control->salt[1]);
		print_maxverbose("Encryption hash loops %lld\n", control->encloops);
	} else if (ENCRYPT) {
		print_output("Asked to decrypt a non-encrypted archive. Bypassing decryption.\n");
		control->flags &= ~FLAG_ENCRYPT;
	}
	return true;
}

Project: lrzip
File: lrzip.c

static bool open_tmpoutbuf(rzip_control *control)
{
	i64 maxlen = control->maxram;
	void *buf;

	while (42) {
		round_to_page(&maxlen);
		buf = malloc(maxlen);
		if (buf) {
			print_maxverbose("Malloced %"PRId64" for tmp_outbuf\n", maxlen);
			break;
		}
		maxlen = maxlen / 3 * 2;
		if (maxlen < 100000000)
			fatal_return(("Unable to even malloc 100MB for tmp_outbuf\n"), false);
	}
	control->flags |= FLAG_TMP_OUTBUF;
	/* Allocate slightly more so we can cope when the buffer overflows and
	 * fall back to a real temporary file */
	control->out_maxlen = maxlen - control->page_size;
	control->tmp_outbuf = buf;
	if (!DECOMPRESS && !TEST_ONLY)
		control->out_ofs = control->out_len = MAGIC_LEN;\
	return true;
}

Project: lrzip
File: main.c

static void show_summary(void)
{
	/* OK, if verbosity set, print summary of options selected */
	if (!INFO) {
		if (!TEST_ONLY)
			print_verbose("The following options are in effect for this %s.\n",
				      DECOMPRESS ? "DECOMPRESSION" : "COMPRESSION");
		print_verbose("Threading is %s. Number of CPUs detected: %d\n", control->threads > 1? "ENABLED" : "DISABLED",
			      control->threads);
		print_verbose("Detected %lld bytes ram\n", control->ramsize);
		print_verbose("Compression level %d\n", control->compression_level);
		print_verbose("Nice Value: %d\n", control->nice_val);
		print_verbose("Show Progress\n");
		print_maxverbose("Max ");
		print_verbose("Verbose\n");
		if (FORCE_REPLACE)
			print_verbose("Overwrite Files\n");
		if (!KEEP_FILES)
			print_verbose("Remove input files on completion\n");
		if (control->outdir)
			print_verbose("Output Directory Specified: %s\n", control->outdir);
		else if (control->outname)
			print_verbose("Output Filename Specified: %s\n", control->outname);
		if (TEST_ONLY)
			print_verbose("Test file integrity\n");
		if (control->tmpdir)
			print_verbose("Temporary Directory set as: %s\n", control->tmpdir);

		/* show compression options */
		if (!DECOMPRESS && !TEST_ONLY) {
			print_verbose("Compression mode is: ");
			if (LZMA_COMPRESS)
				print_verbose("LZMA. LZ4 Compressibility testing %s\n", (LZ4_TEST? "enabled" : "disabled"));
			else if (LZO_COMPRESS)
				print_verbose("LZO\n");
			else if (BZIP2_COMPRESS)
				print_verbose("BZIP2. LZ4 Compressibility testing %s\n", (LZ4_TEST? "enabled" : "disabled"));
			else if (ZLIB_COMPRESS)
				print_verbose("GZIP\n");
			else if (ZPAQ_COMPRESS)
				print_verbose("ZPAQ. LZ4 Compressibility testing %s\n", (LZ4_TEST? "enabled" : "disabled"));
			else if (NO_COMPRESS)
				print_verbose("RZIP pre-processing only\n");
			if (control->window)
				print_verbose("Compression Window: %lld = %lldMB\n", control->window, control->window * 100ull);
			/* show heuristically computed window size */
			if (!control->window && !UNLIMITED) {
				i64 temp_chunk, temp_window;

				if (STDOUT || STDIN)
					temp_chunk = control->maxram;
				else
					temp_chunk = control->ramsize * 2 / 3;
				temp_window = temp_chunk / (100 * 1024 * 1024);
				print_verbose("Heuristically Computed Compression Window: %lld = %lldMB\n", temp_window, temp_window * 100ull);
			}
			if (UNLIMITED)
				print_verbose("Using Unlimited Window size\n");
		}
		if (!DECOMPRESS && !TEST_ONLY)
			print_maxverbose("Storage time in seconds %lld\n", control->secs);
		if (ENCRYPT)
			print_maxverbose("Encryption hash loops %lld\n", control->encloops);
	}
}

Project: lrzip
File: main.c

static void sighandler(int sig __UNUSED__)
{
	signal(sig, SIG_IGN);
	signal(SIGTERM, SIG_IGN);
	signal(SIGTTIN, SIG_IGN);
	signal(SIGTTOU, SIG_IGN);
	print_err("Interrupted\n");
	fatal_exit(&local_control);
}

Project: lrzip
File: lrzip.c

static bool preserve_perms(rzip_control *control, int fd_in, int fd_out)
{
	struct stat st;

	if (unlikely(fstat(fd_in, &st)))
		fatal_return(("Failed to fstat input file\n"), false);
	if (unlikely(fchmod(fd_out, (st.st_mode & 0666))))
		print_verbose("Warning, unable to set permissions on %s\n", control->outfile);

	/* chown fail is not fatal_return(( */
	if (unlikely(fchown(fd_out, st.st_uid, st.st_gid)))
		print_verbose("Warning, unable to set owner on %s\n", control->outfile);
	return true;
}

Project: lrzip
File: lrzip.c

bool clear_tmpinfile(rzip_control *control)
{
	if (unlikely(lseek(control->fd_in, 0, SEEK_SET)))
		fatal_return(("Failed to lseek on fd_in in clear_tmpinfile\n"), false);
	if (unlikely(ftruncate(control->fd_in, 0)))
		fatal_return(("Failed to truncate fd_in in clear_tmpinfile\n"), false);
	return true;
}

Project: lrzip
File: lrzip.c

bool read_magic(rzip_control *control, int fd_in, i64 *expected_size)
{
	char magic[MAGIC_LEN];

	memset(magic, 0, sizeof(magic));
	/* Initially read only <v0.6x header */
	if (unlikely(read(fd_in, magic, 24) != 24))
		fatal_return(("Failed to read magic header\n"), false);

	if (unlikely(!get_magic(control, magic)))
		return false;
	*expected_size = control->st_size;
	return true;
}

Project: lrzip
File: main.c

static void set_stdout(struct rzip_control *control)
{
	control->flags |= FLAG_STDOUT;
	control->outFILE = stdout;
	control->msgout = stderr;
	register_outputfile(control, control->msgout);
}

Project: lrzip
File: main.c

static void recurse_dirlist(char *indir, char **dirlist, int *entries)
{
	char fname[MAX_PATH_LEN];
	struct stat istat;
	struct dirent *dp;
	DIR *dirp;

	dirp = opendir(indir);
	if (unlikely(!dirp))
		failure("Unable to open directory %s\n", indir);
	while ((dp = readdir(dirp)) != NULL) {
		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
			continue;
		sprintf(fname, "%s/%s", indir, dp->d_name);
		if (unlikely(stat(fname, &istat)))
			failure("Unable to stat file %s\n", fname);
		if (S_ISDIR(istat.st_mode)) {
			recurse_dirlist(fname, dirlist, entries);
			continue;
		}
		if (!S_ISREG(istat.st_mode)) {
			print_err("Not regular file %s\n", fname);
			continue;
		}
		print_maxverbose("Added file %s\n", fname);
		*dirlist = realloc(*dirlist, MAX_PATH_LEN * (*entries + 1));
		strcpy(*dirlist + MAX_PATH_LEN * (*entries)++, fname);
	}
	closedir(dirp);
}

Project: lrzip
File: lrzip.c

void clear_tmpinbuf(rzip_control *control)
{
	control->in_len = control->in_ofs = 0;
}

Project: lrzip
File: lrzip.c

bool get_header_info(rzip_control *control, int fd_in, uchar *ctype, i64 *c_len,
		     i64 *u_len, i64 *last_head, int chunk_bytes)
{
	if (unlikely(read(fd_in, ctype, 1) != 1))
		fatal_return(("Failed to read in get_header_info\n"), false);

	*c_len = *u_len = *last_head = 0;
	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_return(("Failed to read in get_header_info"), false);
		if (unlikely(read(fd_in, &u_len32, 4) != 4))
			fatal_return(("Failed to read in get_header_info"), false);
		if (unlikely(read(fd_in, &last_head32, 4) != 4))
			fatal_return(("Failed to read in get_header_info"), false);
		c_len32 = le32toh(c_len32);
		u_len32 = le32toh(u_len32);
		last_head32 = le32toh(last_head32);
		*c_len = c_len32;
		*u_len = u_len32;
		*last_head = last_head32;
	} else {
		int read_len;

		if (control->major_version == 0 && control->minor_version == 5)
			read_len = 8;
		else
			read_len = chunk_bytes;
		if (unlikely(read(fd_in, c_len, read_len) != read_len))
			fatal_return(("Failed to read in get_header_info"), false);
		if (unlikely(read(fd_in, u_len, read_len) != read_len))
			fatal_return(("Failed to read in get_header_info"), false);
		if (unlikely(read(fd_in, last_head, read_len) != read_len))
			fatal_return(("Failed to read_i64 in get_header_info"), false);
		*c_len = le64toh(*c_len);
		*u_len = le64toh(*u_len);
		*last_head = le64toh(*last_head);
	}
	return true;
}

Project: lrzip
File: lrzip.c

bool flush_tmpoutbuf(rzip_control *control)
{
	if (!TEST_ONLY) {
		print_maxverbose("Dumping buffer to physical file.\n");
		if (STDOUT) {
			if (unlikely(!fwrite_stdout(control, control->tmp_outbuf, control->out_len)))
				return false;
		} else {
			if (unlikely(!write_fdout(control, control->tmp_outbuf, control->out_len)))
				return false;
		}
	}
	control->out_relofs += control->out_len;
	control->out_ofs = control->out_len = 0;
	return true;
}

Project: lrzip
File: main.c

int main(int argc, char *argv[])
{
	bool lrzcat = false, compat = false, recurse = false;
	bool options_file = false, conf_file_compression_set = false; /* for environment and tracking of compression setting */
	struct timeval start_time, end_time;
	struct sigaction handler;
	double seconds,total_time; // for timers
	bool nice_set = false;
	int c, i;
	int hours,minutes;
	extern int optind;
	char *eptr, *av; /* for environment */
	char *endptr = NULL;

        control = &base_control;

	initialise_control(control);

	av = basename(argv[0]);
	if (!strcmp(av, "lrunzip"))
		control->flags |= FLAG_DECOMPRESS;
	else if (!strcmp(av, "lrzcat")) {
		control->flags |= FLAG_DECOMPRESS | FLAG_STDOUT;
		lrzcat = true;
	} else if (!strcmp(av, "lrz")) {
		/* Called in gzip compatible command line mode */
		control->flags &= ~FLAG_SHOW_PROGRESS;
		control->flags &= ~FLAG_KEEP_FILES;
		compat = true;
		long_options[1].name = "stdout";
		long_options[11].name = "keep";
	}

	/* generate crc table */
	CrcGenerateTable();

	/* Get Preloaded Defaults from lrzip.conf
	 * Look in ., $HOME/.lrzip/, /etc/lrzip.
	 * If LRZIP=NOCONFIG is set, then ignore config
	 * If lrzip.conf sets a compression mode, options_file will be true.
	 * This will allow for a test to permit an override of compression mode.
	 * If there is an override, then all compression settings will be reset
	 * and command line switches will prevail, including for --lzma.
	 */
	eptr = getenv("LRZIP");
	if (eptr == NULL)
		options_file = read_config(control);
	else if (!strstr(eptr,"NOCONFIG"))
		options_file = read_config(control);
	if (options_file && (control->flags & FLAG_NOT_LZMA))		/* if some compression set in lrzip.conf    */
		conf_file_compression_set = true;			/* need this to allow command line override */

	while ((c = getopt_long(argc, argv, compat ? coptions : loptions, long_options, &i)) != -1) {
		switch (c) {
		case 'b':
		case 'g':
		case 'l':
		case 'n':
		case 'z':
			/* If some compression was chosen in lrzip.conf, allow this one time
			 * because conf_file_compression_set will be true
			 */
			if ((control->flags & FLAG_NOT_LZMA) && conf_file_compression_set == false)
				failure("Can only use one of -l, -b, -g, -z or -n\n");
			/* Select Compression Mode */
			control->flags &= ~FLAG_NOT_LZMA; /* must clear all compressions first */
			if (c == 'b')
				control->flags |= FLAG_BZIP2_COMPRESS;
			else if (c == 'g')
				control->flags |= FLAG_ZLIB_COMPRESS;
			else if (c == 'l')
				control->flags |= FLAG_LZO_COMPRESS;
			else if (c == 'n')
				control->flags |= FLAG_NO_COMPRESS;
			else if (c == 'z')
				control->flags |= FLAG_ZPAQ_COMPRESS;
			/* now FLAG_NOT_LZMA will evaluate as true */
			conf_file_compression_set = false;
			break;
		case '/':							/* LZMA Compress selected */
			control->flags &= ~FLAG_NOT_LZMA;			/* clear alternate compression flags */
			break;
		case 'c':
			if (compat) {
				control->flags |= FLAG_KEEP_FILES;
				set_stdout(control);
				break;
			}
			/* FALLTHRU */
		case 'C':
			control->flags |= FLAG_CHECK;
			control->flags |= FLAG_HASH;
			break;
		case 'd':
			control->flags |= FLAG_DECOMPRESS;
			break;
		case 'D':
			control->flags &= ~FLAG_KEEP_FILES;
			break;
		case 'e':
			control->flags |= FLAG_ENCRYPT;
			control->passphrase = optarg;
			break;
		case 'f':
			control->flags |= FLAG_FORCE_REPLACE;
			break;
		case 'h':
			usage(compat);
			exit(0);
			break;
		case 'H':
			control->flags |= FLAG_HASH;
			break;
		case 'i':
			control->flags |= FLAG_INFO;
			control->flags &= ~FLAG_DECOMPRESS;
			break;
		case 'k':
			if (compat) {
				control->flags |= FLAG_KEEP_FILES;
				break;
			}
			/* FALLTHRU */
		case 'K':
			control->flags |= FLAG_KEEP_BROKEN;
			break;
		case 'L':
			if (compat) {
				license();
				exit(0);
			}
			control->compression_level = strtol(optarg, &endptr, 10);
			if (control->compression_level < 1 || control->compression_level > 9)
				failure("Invalid compression level (must be 1-9)\n");
			if (*endptr)
				failure("Extra characters after compression level: \'%s\'\n", endptr);
			break;
		case 'm':
			control->ramsize = strtol(optarg, &endptr, 10) * 1024 * 1024 * 100;
			if (*endptr)
				failure("Extra characters after ramsize: \'%s\'\n", endptr);
			break;
		case 'N':
			nice_set = true;
			control->nice_val = strtol(optarg, &endptr, 10);
			if (control->nice_val < PRIO_MIN || control->nice_val > PRIO_MAX)
				failure("Invalid nice value (must be %d...%d)\n", PRIO_MIN, PRIO_MAX);
			if (*endptr)
				failure("Extra characters after nice level: \'%s\'\n", endptr);
			break;
		case 'o':
			if (control->outdir)
				failure("Cannot have -o and -O together\n");
			if (unlikely(STDOUT))
				failure("Cannot specify an output filename when outputting to stdout\n");
			control->outname = optarg;
			dealloc(control->suffix);
			control->suffix = strdup("");
			break;
		case 'O':
			if (control->outname)	/* can't mix -o and -O */
				failure("Cannot have options -o and -O together\n");
			if (unlikely(STDOUT))
				failure("Cannot specify an output directory when outputting to stdout\n");
			control->outdir = malloc(strlen(optarg) + 2);
			if (control->outdir == NULL)
				fatal("Failed to allocate for outdir\n");
			strcpy(control->outdir,optarg);
			if (strcmp(optarg+strlen(optarg) - 1, "/")) 	/* need a trailing slash */
				strcat(control->outdir, "/");
			break;
		case 'p':
			control->threads = strtol(optarg, &endptr, 10);
			if (control->threads < 1)
				failure("Must have at least one thread\n");
			if (*endptr)
				failure("Extra characters after number of threads: \'%s\'\n", endptr);
			break;
		case 'P':
			control->flags |= FLAG_SHOW_PROGRESS;
			break;
		case 'q':
			control->flags &= ~FLAG_SHOW_PROGRESS;
			break;
		case 'r':
			recurse = true;
			break;
		case 'S':
			if (control->outname)
				failure("Specified output filename already, can't specify an extension.\n");
			if (unlikely(STDOUT))
				failure("Cannot specify a filename suffix when outputting to stdout\n");
			dealloc(control->suffix);
			control->suffix = strdup(optarg);
			break;
		case 't':
			if (control->outname)
				failure("Cannot specify an output file name when just testing.\n");
			if (compat)
				control->flags |= FLAG_KEEP_FILES;
			if (!KEEP_FILES)
				failure("Doubt that you want to delete a file when just testing.\n");
			control->flags |= FLAG_TEST_ONLY;
			break;
		case 'T':
			control->flags &= ~FLAG_THRESHOLD;
			break;
		case 'U':
			control->flags |= FLAG_UNLIMITED;
			break;
		case 'v':
			/* set verbosity flag */
			if (!(control->flags & FLAG_SHOW_PROGRESS))
				control->flags |= FLAG_SHOW_PROGRESS;
			else if (!(control->flags & FLAG_VERBOSITY) && !(control->flags & FLAG_VERBOSITY_MAX))
				control->flags |= FLAG_VERBOSITY;
			else if ((control->flags & FLAG_VERBOSITY)) {
				control->flags &= ~FLAG_VERBOSITY;
				control->flags |= FLAG_VERBOSITY_MAX;
			}
			break;
		case 'V':
			control->msgout = stdout;
			print_output("lrzip version %s\n", PACKAGE_VERSION);
			exit(0);
			break;
		case 'w':
			control->window = strtol(optarg, &endptr, 10);
			if (control->window < 1)
				failure("Window must be positive\n");
			if (*endptr)
				failure("Extra characters after window size: \'%s\'\n", endptr);
			break;
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			control->compression_level = c - '0';
			break;
		default:
			usage(compat);
			return 2;
		}
	}

	argc -= optind;
	argv += optind;

	if (control->outname) {
		if (argc > 1)
			failure("Cannot specify output filename with more than 1 file\n");
		if (recurse)
			failure("Cannot specify output filename with recursive\n");
	}

	if (VERBOSE && !SHOW_PROGRESS) {
		print_err("Cannot have -v and -q options. -v wins.\n");
		control->flags |= FLAG_SHOW_PROGRESS;
	}

	if (UNLIMITED && control->window) {
		print_err("If -U used, cannot specify a window size with -w.\n");
		control->window = 0;
	}

	if (argc < 1)
		control->flags |= FLAG_STDIN;

	if (UNLIMITED && STDIN) {
		print_err("Cannot have -U and stdin, unlimited mode disabled.\n");
		control->flags &= ~FLAG_UNLIMITED;
	}

	setup_overhead(control);

	/* Set the main nice value to half that of the backend threads since
	 * the rzip stage is usually the rate limiting step */
	control->current_priority = getpriority(PRIO_PROCESS, 0);
	if (nice_set) {
		if (!NO_COMPRESS) {
			/* If niceness can't be set. just reset process priority */
			if (unlikely(setpriority(PRIO_PROCESS, 0, control->nice_val/2) == -1)) {
				print_err("Warning, unable to set nice value %d...Resetting to %d\n",
					control->nice_val, control->current_priority);
				setpriority(PRIO_PROCESS, 0, (control->nice_val=control->current_priority));
			}
		} else {
			if (unlikely(setpriority(PRIO_PROCESS, 0, control->nice_val) == -1)) {
				print_err("Warning, unable to set nice value %d...Resetting to %d\n",
					control->nice_val, control->current_priority);
				setpriority(PRIO_PROCESS, 0, (control->nice_val=control->current_priority));
			}
		}
	}

	/* One extra iteration for the case of no parameters means we will default to stdin/out */
	for (i = 0; i <= argc; i++) {
		char *dirlist = NULL, *infile = NULL;
		int direntries = 0, curentry = 0;

		if (i < argc)
			infile = argv[i];
		else if (!(i == 0 && STDIN))
			break;
		if (infile) {
			if ((strcmp(infile, "-") == 0))
				control->flags |= FLAG_STDIN;
			else {
				bool isdir = false;
				struct stat istat;

				if (unlikely(stat(infile, &istat)))
					failure("Failed to stat %s\n", infile);
				isdir = S_ISDIR(istat.st_mode);
				if (!recurse && (isdir || !S_ISREG(istat.st_mode))) {
					failure("lrzip only works directly on regular FILES.\n"
					"Use -r recursive, lrztar or pipe through tar for compressing directories.\n");
				}
				if (recurse && !isdir)
					failure("%s not a directory, -r recursive needs a directory\n", infile);
			}
		}

		if (recurse) {
			if (unlikely(STDIN || STDOUT))
				failure("Cannot use -r recursive with STDIO\n");
			recurse_dirlist(infile, &dirlist, &direntries);
		}

		if (INFO && STDIN)
			failure("Will not get file info from STDIN\n");
recursion:
		if (recurse) {
			if (curentry >= direntries) {
				infile = NULL;
				continue;
			}
			infile = dirlist + MAX_PATH_LEN * curentry++;
		}
		control->infile = infile;

		/* If no output filename is specified, and we're using
		 * stdin, use stdout */
		if ((control->outname && (strcmp(control->outname, "-") == 0)) ||
		    (!control->outname && STDIN) || lrzcat)
				set_stdout(control);

		if (lrzcat) {
			control->msgout = stderr;
			control->outFILE = stdout;
			register_outputfile(control, control->msgout);
		}

		if (!STDOUT) {
			control->msgout = stdout;
			register_outputfile(control, control->msgout);
		}

		if (STDIN)
			control->inFILE = stdin;

		/* Implement signal handler only once flags are set */
		sigemptyset(&handler.sa_mask);
		handler.sa_flags = 0;
		handler.sa_handler = &sighandler;
		sigaction(SIGTERM, &handler, 0);
		sigaction(SIGINT, &handler, 0);

		if (!FORCE_REPLACE) {
			if (STDIN && isatty(fileno((FILE *)stdin))) {
				print_err("Will not read stdin from a terminal. Use -f to override.\n");
				usage(compat);
				exit (1);
			}
			if (!TEST_ONLY && STDOUT && isatty(fileno((FILE *)stdout)) && !compat) {
				print_err("Will not write stdout to a terminal. Use -f to override.\n");
				usage(compat);
				exit (1);
			}
		}

		if (CHECK_FILE) {
			if (!DECOMPRESS) {
				print_err("Can only check file written on decompression.\n");
				control->flags &= ~FLAG_CHECK;
			} else if (STDOUT) {
				print_err("Can't check file written when writing to stdout. Checking disabled.\n");
				control->flags &= ~FLAG_CHECK;
			}
		}

		setup_ram(control);
		show_summary();

		gettimeofday(&start_time, NULL);

		if (unlikely((STDIN || STDOUT) && ENCRYPT))
			failure("Unable to work from STDIO while reading password\n");

		memcpy(&local_control, &base_control, sizeof(rzip_control));
		if (DECOMPRESS || TEST_ONLY)
			decompress_file(&local_control);
		else if (INFO)
			get_fileinfo(&local_control);
		else
			compress_file(&local_control);

		/* compute total time */
		gettimeofday(&end_time, NULL);
		total_time = (end_time.tv_sec + (double)end_time.tv_usec / 1000000) -
			      (start_time.tv_sec + (double)start_time.tv_usec / 1000000);
		hours = (int)total_time / 3600;
		minutes = (int)(total_time / 60) % 60;
		seconds = total_time - hours * 3600 - minutes * 60;
		if (!INFO)
			print_progress("Total time: %02d:%02d:%05.2f\n", hours, minutes, seconds);
		if (recurse)
			goto recursion;
	}

	return 0;
}

Project: lrzip
File: lrzip.c

static bool get_hash(rzip_control *control, int make_hash)
{
	char *passphrase, *testphrase;
	struct termios termios_p;
	int prompt = control->passphrase == NULL;

	passphrase = calloc(PASS_LEN, 1);
	testphrase = calloc(PASS_LEN, 1);
	control->salt_pass = calloc(PASS_LEN, 1);
	control->hash = calloc(HASH_LEN, 1);
	if (unlikely(!passphrase || !testphrase || !control->salt_pass || !control->hash)) {
		fatal("Failed to calloc encrypt buffers in compress_file\n");
		dealloc(testphrase);
		dealloc(passphrase);
		return false;
	}
	mlock(passphrase, PASS_LEN);
	mlock(testphrase, PASS_LEN);
	mlock(control->salt_pass, PASS_LEN);
	mlock(control->hash, HASH_LEN);

	if (control->pass_cb) {
		control->pass_cb(control->pass_data, passphrase, PASS_LEN - SALT_LEN);
		if (!passphrase[0]) {
			fatal("Supplied password was null!");
			munlock(passphrase, PASS_LEN);
			munlock(testphrase, PASS_LEN);
			dealloc(testphrase);
			dealloc(passphrase);
			release_hashes(control);
			return false;
		}
		control->salt_pass_len = strlen(passphrase) + SALT_LEN;
	} else {
		/* Disable stdin echo to screen */
		tcgetattr(fileno(stdin), &termios_p);
		termios_p.c_lflag &= ~ECHO;
		tcsetattr(fileno(stdin), 0, &termios_p);
retry_pass:
		if (prompt)
			print_output("Enter passphrase: ");
		control->salt_pass_len = get_pass(control, passphrase) + SALT_LEN;
		if (prompt)
			print_output("\n");
		if (make_hash) {
			if (prompt)
				print_output("Re-enter passphrase: ");
			get_pass(control, testphrase);
			if (prompt)
				print_output("\n");
			if (strcmp(passphrase, testphrase)) {
				print_output("Passwords do not match. Try again.\n");
				goto retry_pass;
			}
		}
		termios_p.c_lflag |= ECHO;
		tcsetattr(fileno(stdin), 0, &termios_p);
		memset(testphrase, 0, PASS_LEN);
	}
	memcpy(control->salt_pass, control->salt, SALT_LEN);
	memcpy(control->salt_pass + SALT_LEN, passphrase, PASS_LEN - SALT_LEN);
	lrz_stretch(control);
	memset(passphrase, 0, PASS_LEN);
	munlock(passphrase, PASS_LEN);
	munlock(testphrase, PASS_LEN);
	dealloc(testphrase);
	dealloc(passphrase);
	return true;
}

Project: lrzip
File: lrzip.c

i64 get_ram(rzip_control *control)
{
	int mib[2];
	size_t len;
	i64 *p, ramsize;

	mib[0] = CTL_HW;
	mib[1] = HW_MEMSIZE;
	sysctl(mib, 2, NULL, &len, NULL, 0);
	p = malloc(len);
	sysctl(mib, 2, p, &len, NULL, 0);
	ramsize = *p;

	return ramsize;
}

Project: lrzip
File: lrzip.c

bool write_magic(rzip_control *control)
{
	char magic[MAGIC_LEN] = { 
		'L', 'R', 'Z', 'I', LRZIP_MAJOR_VERSION, LRZIP_MINOR_VERSION 
	};

	/* File size is stored as zero for streaming STDOUT blocks when the
	 * file size is unknown. In encrypted files, the size is left unknown
	 * and instead the salt is stored here to preserve space. */
	if (ENCRYPT)
		memcpy(&magic[6], &control->salt, 8);
	else if (!STDIN || !STDOUT || control->eof) {
		i64 esize = htole64(control->st_size);

		memcpy(&magic[6], &esize, 8);
	}

	/* save LZMA compression flags */
	if (LZMA_COMPRESS) {
		int i;

		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.
	 */
	if (!NO_MD5)
		magic[21] = 1;
	if (ENCRYPT)
		magic[22] = 1;

	if (unlikely(fdout_seekto(control, 0)))
		fatal_return(("Failed to seek to BOF to write Magic Header\n"), false);

	if (unlikely(put_fdout(control, magic, MAGIC_LEN) != MAGIC_LEN))
		fatal_return(("Failed to write magic header\n"), false);
	control->magic_written = 1;
	return true;
}

Project: lrzip
File: lrzip.c

static i64 fdout_seekto(rzip_control *control, i64 pos)
{
	if (TMP_OUTBUF) {
		pos -= control->out_relofs;
		control->out_ofs = pos;
		if (unlikely(pos > control->out_len || pos < 0)) {
			print_err("Trying to seek to %lld outside tmp outbuf in fdout_seekto\n", pos);
			return -1;
		}
		return 0;
	}
	return lseek(control->fd_out, pos, SEEK_SET);
}

Project: lrzip
File: lrzip.c

bool initialise_control(rzip_control *control)
{
	time_t now_t, tdiff;
	char localeptr[] = "./", *eptr; /* for environment */
	size_t len;

	memset(control, 0, sizeof(rzip_control));
	control->msgout = stderr;
	control->msgerr = stderr;
	register_outputfile(control, control->msgout);
	control->flags = FLAG_SHOW_PROGRESS | FLAG_KEEP_FILES | FLAG_THRESHOLD;
	control->suffix = strdup(".lrz");
	control->compression_level = 7;
	control->ramsize = get_ram(control);
	if (unlikely(control->ramsize == -1))
		return false;
	/* for testing single CPU */
	control->threads = PROCESSORS;	/* get CPUs for LZMA */
	control->page_size = PAGE_SIZE;
	control->nice_val = 19;

	/* The first 5 bytes of the salt is the time in seconds.
	 * The next 2 bytes encode how many times to hash the password.
	 * The last 9 bytes are random data, making 16 bytes of salt */
	if (unlikely((now_t = time(NULL)) == ((time_t)-1)))
		fatal_return(("Failed to call time in main\n"), false);
	if (unlikely(now_t < T_ZERO)) {
		print_output("Warning your time reads before the year 2011, check your system clock\n");
		now_t = T_ZERO;
	}
	/* Workaround for CPUs no longer keeping up with Moore's law!
	 * This way we keep the magic header format unchanged. */
	tdiff = (now_t - T_ZERO) / 4;
	now_t = T_ZERO + tdiff;
	control->secs = now_t;
	control->encloops = nloops(control->secs, control->salt, control->salt + 1);
	if (unlikely(!get_rand(control, control->salt + 2, 6)))
		return false;

	/* Get Temp Dir. Try variations on canonical unix environment variable */
	eptr = getenv("TMPDIR");
	if (!eptr)
		eptr = getenv("TMP");
	if (!eptr)
		eptr = getenv("TEMPDIR");
	if (!eptr)
		eptr = getenv("TEMP");
	if (!eptr)
		eptr = localeptr;
	len = strlen(eptr);

	control->tmpdir = malloc(len + 2);
	if (control->tmpdir == NULL)
		fatal_return(("Failed to allocate for tmpdir\n"), false);
	strcpy(control->tmpdir, eptr);
	if (control->tmpdir[len - 1] != '/') {
		control->tmpdir[len] = '/'; /* need a trailing slash */
		control->tmpdir[len + 1] = '\0';
	}
	return true;
}

Project: lrzip
File: main.c

static void license(void)
{
	print_output("lrz version %s\n", PACKAGE_VERSION);
	print_output("Copyright (C) Con Kolivas 2006-2016\n");
	print_output("Based on rzip ");
	print_output("Copyright (C) Andrew Tridgell 1998-2003\n\n");
	print_output("This is free software.  You may redistribute copies of it under the terms of\n");
	print_output("the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n");
	print_output("There is NO WARRANTY, to the extent permitted by law.\n");
}

Project: lrzip
File: lrzip.c

i64 nloops(i64 seconds, uchar *b1, uchar *b2)
{
	i64 nloops;
	int nbits;

	nloops = ARBITRARY_AT_EPOCH * pow(MOORE_TIMES_PER_SECOND, seconds);
	if (nloops < ARBITRARY)
		nloops = ARBITRARY;
	for (nbits = 0; nloops > 255; nbits ++)
		nloops = nloops >> 1;
	*b1 = nbits;
	*b2 = nloops;
	return nloops << nbits;
}

Project: lrzip
File: lrzip.c

static bool fwrite_stdout(rzip_control *control, void *buf, i64 len)
{
	uchar *offset_buf = buf;
	ssize_t ret;
	i64 total;

	total = 0;
	while (len > 0) {
		ssize_t wrote;

		if (len > one_g)
			ret = one_g;
		else
			ret = len;
		wrote = fwrite(offset_buf, 1, ret, control->outFILE);
		if (unlikely(wrote != ret))
			fatal_return(("Failed to fwrite in fwrite_stdout\n"), false);
		len -= ret;
		offset_buf += ret;
		total += ret;
	}
	fflush(control->outFILE);
	return true;
}

Project: lrzip
File: lrzip.c

static inline i64 enc_loops(uchar b1, uchar b2)
{
	return (i64)b2 << (i64)b1;
}

Project: lrzip
File: lrzip.c

bool dump_tmpoutfile(rzip_control *control, int fd_out)
{
	FILE *tmpoutfp;
	int tmpchar;

	if (unlikely(fd_out == -1))
		fatal_return(("Failed: No temporary outfile created, unable to do in ram\n"), false);
	/* flush anything not yet in the temporary file */
	fsync(fd_out);
	tmpoutfp = fdopen(fd_out, "r");
	if (unlikely(tmpoutfp == NULL))
		fatal_return(("Failed to fdopen out tmpfile\n"), false);
	rewind(tmpoutfp);

	if (!TEST_ONLY) {
		print_verbose("Dumping temporary file to control->outFILE.\n");
		while ((tmpchar = fgetc(tmpoutfp)) != EOF)
			putchar(tmpchar);
		fflush(control->outFILE);
		rewind(tmpoutfp);
	}

	if (unlikely(ftruncate(fd_out, 0)))
		fatal_return(("Failed to ftruncate fd_out in dump_tmpoutfile\n"), false);
	return true;
}

Project: lrzip
File: lrzip.c

static int get_pass(rzip_control *control, char *s)
{
	int len;

	memset(s, 0, PASS_LEN - SALT_LEN);
	if (control->passphrase)
		strncpy(s, control->passphrase, PASS_LEN - SALT_LEN - 1);
	else if (unlikely(fgets(s, PASS_LEN - SALT_LEN, stdin) == NULL))
		failure_return(("Failed to retrieve passphrase\n"), -1);
	len = strlen(s);
	if (len > 0 && ('\r' ==  s[len - 1] || '\n' == s[len - 1]))
		s[len - 1] = '\0';
	if (len > 1 && ('\r' ==  s[len - 2] || '\n' == s[len - 2]))
		s[len - 2] = '\0';
	len = strlen(s);
	if (unlikely(0 == len))
		failure_return(("Empty passphrase\n"), -1);
	return len;
}

Project: lrzip
File: lrzip.c

bool 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 = -1, fd_out = -1;
	char header[MAGIC_LEN];

	if (MD5_RELIABLE)
		control->flags |= FLAG_MD5;
	if (ENCRYPT)
		if (unlikely(!get_hash(control, 1)))
			return false;
	memset(header, 0, sizeof(header));

	if ( IS_FROM_FILE )
		fd_in = fileno(control->inFILE);
	else 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 false;
		}

        fd_in = open(control->infile, O_RDONLY);
		if (unlikely(fd_in == -1))
			fatal_return(("Failed to open %s\n", control->infile), false);
	} 
	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_goto(("Failed to allocate outfile name\n"), error);
					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_goto(("Failed to allocate outfile name\n"), error);

			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);
		}

		fd_out = open(control->outfile, O_RDWR | O_CREAT | O_EXCL, 0666);
		if (FORCE_REPLACE && (-1 == fd_out) && (EEXIST == errno)) {
			if (unlikely(unlink(control->outfile)))
				fatal_goto(("Failed to unlink an existing file: %s\n", control->outfile), error);
			fd_out = open(control->outfile, O_RDWR | 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_goto(("Failed to create %s\n", control->outfile), error);
		}
		control->fd_out = fd_out;
		if (!STDIN) {
			if (unlikely(!preserve_perms(control, fd_in, fd_out)))
				goto error;
		}
	} else {
		if (unlikely(!open_tmpoutbuf(control)))
			goto error;
	}

	/* Write zeroes to header at beginning of file */
	if (unlikely(!STDOUT && write(fd_out, header, sizeof(header)) != sizeof(header)))
		fatal_goto(("Cannot write file header\n"), error);

	rzip_fd(control, fd_in, fd_out);

	/* Wwrite magic at end b/c lzma does not tell us properties until it is done */
	if (!STDOUT) {
		if (unlikely(!write_magic(control)))
			goto error;
	}

	if (ENCRYPT)
		release_hashes(control);

	if (unlikely(!STDIN && !STDOUT && !preserve_times(control, fd_in))) {
		fatal("Failed to preserve times on output file\n");
		goto error;
	}

	if (unlikely(close(fd_in))) {
		fatal("Failed to close fd_in\n");
		fd_in = -1;
		goto error;
	}
	if (unlikely(!STDOUT && close(fd_out)))
		fatal_return(("Failed to close fd_out\n"), false);
	if (TMP_OUTBUF)
		close_tmpoutbuf(control);

	if (!KEEP_FILES && !STDIN) {
		if (unlikely(unlink(control->infile)))
			fatal_return(("Failed to unlink %s\n", control->infile), false);
	}

	dealloc(control->outfile);
	return true;
error:
	if (! IS_FROM_FILE && STDIN && (fd_in > 0))
		close(fd_in);
	if ((!STDOUT) && (fd_out > 0))
		close(fd_out);
	return false;
}

Project: lrzip
File: lrzip.c

bool write_fdout(rzip_control *control, void *buf, i64 len)
{
	uchar *offset_buf = buf;
	ssize_t ret;

	while (len > 0) {
		ret = MIN(len, one_g);
		ret = write(control->fd_out, offset_buf, (size_t)ret);
		if (unlikely(ret <= 0))
			fatal_return(("Failed to write to fd_out in write_fdout\n"), false);
		len -= ret;
		offset_buf += ret;
	}
	return true;
}

Project: lrzip
File: lrzip.c

void close_tmpinbuf(rzip_control *control)
{
	control->flags &= ~FLAG_TMP_INBUF;
	dealloc(control->tmp_inbuf);
	if (!BITS32)
		control->usable_ram = control->maxram += control->ramsize / 18;
}

Project: lrzip
File: lrzip.c

static bool read_tmpinmagic(rzip_control *control)
{
	char magic[MAGIC_LEN];
	int i, tmpchar;

	memset(magic, 0, sizeof(magic));
	for (i = 0; i < 24; i++) {
		tmpchar = getchar();
		if (unlikely(tmpchar == EOF))
			failure_return(("Reached end of file on STDIN prematurely on v05 magic read\n"), false);
		magic[i] = (char)tmpchar;
	}
	return get_magic(control, magic);
}

Project: lrzip
File: lrzip.c

int open_tmpinfile(rzip_control *control)
{
	int fd_in = -1;

	/* Use temporary directory if there is one */
	if (control->tmpdir) {
		control->infile = malloc(strlen(control->tmpdir) + 15);
		if (unlikely(!control->infile))
			fatal_return(("Failed to allocate infile name\n"), -1);
		strcpy(control->infile, control->tmpdir);
		strcat(control->infile, "lrzipin.XXXXXX");
		fd_in = mkstemp(control->infile);
	}

	/* Try the current directory */
	if (fd_in == -1) {
		dealloc(control->infile);
		control->infile = malloc(16);
		if (unlikely(!control->infile))
			fatal_return(("Failed to allocate infile name\n"), -1);
		strcpy(control->infile, "lrzipin.XXXXXX");
		fd_in = mkstemp(control->infile);
	}

	/* Use /tmp if nothing is writeable so far */
	if (fd_in == -1) {
		dealloc(control->infile);
		control->infile = malloc(20);
		if (unlikely(!control->infile))
			fatal_return(("Failed to allocate infile name\n"), -1);
		strcpy(control->infile, "/tmp/lrzipin.XXXXXX");
		fd_in = mkstemp(control->infile);
	}

	if (fd_in == -1) {
		print_progress("WARNING: Failed to create in tmpfile: %s, will fail if cannot perform %scompression entirely in ram\n",
			       control->infile, DECOMPRESS ? "de" : "");
	} else {
		register_infile(control, control->infile, (DECOMPRESS || TEST_ONLY) && STDIN);
		/* Unlink temporary file immediately to minimise chance of files left
		* lying around in cases of failure_return((. */
		if (unlikely(unlink(control->infile))) {
			fatal("Failed to unlink tmpfile: %s\n", control->infile);
			close(fd_in);
			return -1;
		}
	}
	return fd_in;
}

Project: lrzip
File: lrzip.c

void close_tmpoutbuf(rzip_control *control)
{
	control->flags &= ~FLAG_TMP_OUTBUF;
	dealloc(control->tmp_outbuf);
	if (!BITS32)
		control->usable_ram = control->maxram += control->ramsize / 18;
}

Project: lrzip
File: lrzip.c

static bool open_tmpinbuf(rzip_control *control)
{
	control->flags |= FLAG_TMP_INBUF;
	control->in_maxlen = control->maxram;
	control->tmp_inbuf = malloc(control->maxram + control->page_size);
	if (unlikely(!control->tmp_inbuf))
		fatal_return(("Failed to malloc tmp_inbuf in open_tmpinbuf\n"), false);
	return true;
}

Project: lrzip
File: lrzip.c

static void release_hashes(rzip_control *control)
{
	memset(control->salt_pass, 0, PASS_LEN);
	memset(control->hash, 0, SALT_LEN);
	munlock(control->salt_pass, PASS_LEN);
	munlock(control->hash, HASH_LEN);
	dealloc(control->salt_pass);
	dealloc(control->hash);
}

Project: lrzip
File: lrzip.c

int open_tmpoutfile(rzip_control *control)
{
	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_return(("Failed to allocate outfile name\n"), -1);
		strcpy(control->outfile, control->tmpdir);
		strcat(control->outfile, "lrzipout.XXXXXX");
	}

	fd_out = mkstemp(control->outfile);
	if (fd_out == -1) {
		print_progress("WARNING: Failed to create out tmpfile: %s, will fail if cannot perform %scompression entirely in ram\n",
			       control->outfile, DECOMPRESS ? "de" : "");
	} else
		register_outfile(control, control->outfile, TEST_ONLY || STDOUT || !KEEP_BROKEN);
	return fd_out;
}

Project: lrzip
File: lrzip.c

bool write_fdin(rzip_control *control)
{
	uchar *offset_buf = control->tmp_inbuf;
	i64 len = control->in_len;
	ssize_t ret;

	while (len > 0) {
		ret = MIN(len, one_g);
		ret = write(control->fd_in, offset_buf, (size_t)ret);
		if (unlikely(ret <= 0))
			fatal_return(("Failed to write to fd_in in write_fdin\n"), false);
		len -= ret;
		offset_buf += ret;
	}
	return true;
}

Project: lrzip
File: lrzip.c

bool get_fileinfo(rzip_control *control)
{
	i64 u_len, c_len, second_last, last_head, utotal = 0, ctotal = 0, ofs = 25, stream_head[2];
	i64 expected_size, infile_size, chunk_size = 0, chunk_total = 0;
	int header_length, stream = 0, chunk = 0;
	char *tmp, *infilecopy = NULL;
	char chunk_byte = 0;
	long double cratio;
	uchar ctype = 0;
	uchar save_ctype = 255;
	struct stat st;
	int fd_in;

	if (!STDIN) {
		struct stat fdin_stat;

		stat(control->infile, &fdin_stat);
		if (!S_ISREG(fdin_stat.st_mode) && (tmp = strrchr(control->infile, '.')) &&
		    strcmp(tmp,control->suffix)) {
			infilecopy = alloca(strlen(control->infile) + strlen(control->suffix) + 1);
			strcpy(infilecopy, control->infile);
			strcat(infilecopy, control->suffix);
		} else
			infilecopy = strdupa(control->infile);
	}

	if ( IS_FROM_FILE )
		fd_in = fileno(control->inFILE);
	else if (STDIN)
		fd_in = 0;
	else {
		fd_in = open(infilecopy, O_RDONLY);
		if (unlikely(fd_in == -1))
			fatal_return(("Failed to open %s\n", infilecopy), false);
	}

	/* Get file size */
	if (unlikely(fstat(fd_in, &st)))
		fatal_goto(("bad magic file descriptor!?\n"), error);
	infile_size = st.st_size;

	/* Get decompressed size */
	if (unlikely(!read_magic(control, fd_in, &expected_size)))
		goto error;

	if (ENCRYPT) {
		print_output("Encrypted lrzip archive. No further information available\n");
		if (!STDIN && !IS_FROM_FILE)
			close(fd_in);
		goto out;
	}

	if (control->major_version == 0 && control->minor_version > 4) {
		if (unlikely(read(fd_in, &chunk_byte, 1) != 1))
			fatal_goto(("Failed to read chunk_byte in get_fileinfo\n"), error);
		if (unlikely(chunk_byte < 1 || chunk_byte > 8))
			fatal_goto(("Invalid chunk bytes %d\n", chunk_byte), error);
		if (control->major_version == 0 && control->minor_version > 5) {
			if (unlikely(read(fd_in, &control->eof, 1) != 1))
				fatal_goto(("Failed to read eof in get_fileinfo\n"), error);
			if (unlikely(read(fd_in, &chunk_size, chunk_byte) != chunk_byte))
				fatal_goto(("Failed to read chunk_size in get_fileinfo\n"), error);
			chunk_size = le64toh(chunk_size);
			if (unlikely(chunk_size < 0))
				fatal_goto(("Invalid chunk size %lld\n", chunk_size), error);
		}
	}

	if (control->major_version == 0 && control->minor_version < 4) {
		ofs = 24;
		header_length = 13;
	} else if (control->major_version == 0 && control->minor_version == 4) {
		ofs = 24;
		header_length = 25;
	} else if (control->major_version == 0 && control->minor_version == 5) {
		ofs = 25;
		header_length = 25;
	} else {
		ofs = 26 + chunk_byte;
		header_length = 1 + (chunk_byte * 3);
	}
	if (control->major_version == 0 && control->minor_version < 6 &&
		!expected_size)
			goto done;
next_chunk:
	stream = 0;
	stream_head[0] = 0;
	stream_head[1] = stream_head[0] + header_length;

	print_verbose("Rzip chunk %d:\n", ++chunk);
	if (chunk_byte)
		print_verbose("Chunk byte width: %d\n", chunk_byte);
	if (chunk_size) {
		chunk_total += chunk_size;
		print_verbose("Chunk size: %lld\n", chunk_size);
	}
	if (unlikely(chunk_byte && (chunk_byte > 8 || chunk_size < 0)))
		failure("Invalid chunk data\n");
	while (stream < NUM_STREAMS) {
		int block = 1;

		second_last = 0;
		if (unlikely(lseek(fd_in, stream_head[stream] + ofs, SEEK_SET) == -1))
			fatal_goto(("Failed to seek to header data in get_fileinfo\n"), error);
		if (unlikely(!get_header_info(control, fd_in, &ctype, &c_len, &u_len, &last_head, chunk_byte)))
			return false;

		print_verbose("Stream: %d\n", stream);
		print_maxverbose("Offset: %lld\n", stream_head[stream] + ofs);
		print_verbose("Block\tComp\tPercent\tSize\n");
		do {
			i64 head_off;

			if (unlikely(last_head && last_head < second_last))
				failure_goto(("Invalid earlier last_head position, corrupt archive.\n"), error);
			second_last = last_head;
			if (unlikely(last_head + ofs > infile_size))
				failure_goto(("Offset greater than archive size, likely corrupted/truncated archive.\n"), error);
			if (unlikely((head_off = lseek(fd_in, last_head + ofs, SEEK_SET)) == -1))
				fatal_goto(("Failed to seek to header data in get_fileinfo\n"), error);
			if (unlikely(!get_header_info(control, fd_in, &ctype, &c_len, &u_len,
					&last_head, chunk_byte)))
				return false;
			if (unlikely(last_head < 0 || c_len < 0 || u_len < 0))
				failure_goto(("Entry negative, likely corrupted archive.\n"), error);
			print_verbose("%d\t", block);
			if (ctype == CTYPE_NONE)
				print_verbose("none");
			else if (ctype == CTYPE_BZIP2)
				print_verbose("bzip2");
			else if (ctype == CTYPE_LZO)
				print_verbose("lzo");
			else if (ctype == CTYPE_LZMA)
				print_verbose("lzma");
			else if (ctype == CTYPE_GZIP)
				print_verbose("gzip");
			else if (ctype == CTYPE_ZPAQ)
				print_verbose("zpaq");
			else
				print_verbose("Dunno wtf");
			if (save_ctype == 255)
				save_ctype = ctype; /* need this for lzma when some chunks could have no compression
						     * and info will show rzip + none on info display if last chunk
						     * is not compressed. Adjust for all types in case it's used in
						     * the future */
			utotal += u_len;
			ctotal += c_len;
			print_verbose("\t%.1f%%\t%lld / %lld", percentage(c_len, u_len), c_len, u_len);
			print_maxverbose("\tOffset: %lld\tHead: %lld", head_off, last_head);
			print_verbose("\n");
			block++;
		} while (last_head);
		++stream;
	}

	if (unlikely((ofs = lseek(fd_in, c_len, SEEK_CUR)) == -1))
		fatal_goto(("Failed to lseek c_len in get_fileinfo\n"), error);

	if (ofs >= infile_size - (HAS_MD5 ? MD5_DIGEST_SIZE : 0))
		goto done;
	/* Chunk byte entry */
	if (control->major_version == 0 && control->minor_version > 4) {
		if (unlikely(read(fd_in, &chunk_byte, 1) != 1))
			fatal_goto(("Failed to read chunk_byte in get_fileinfo\n"), error);
		if (unlikely(chunk_byte < 1 || chunk_byte > 8))
			fatal_goto(("Invalid chunk bytes %d\n", chunk_byte), error);
		ofs++;
		if (control->major_version == 0 && control->minor_version > 5) {
			if (unlikely(read(fd_in, &control->eof, 1) != 1))
				fatal_goto(("Failed to read eof in get_fileinfo\n"), error);
			if (unlikely(read(fd_in, &chunk_size, chunk_byte) != chunk_byte))
				fatal_goto(("Failed to read chunk_size in get_fileinfo\n"), error);
			chunk_size = le64toh(chunk_size);
			if (unlikely(chunk_size < 0))
				fatal_goto(("Invalid chunk size %lld\n", chunk_size), error);
			ofs += 1 + chunk_byte;
			header_length = 1 + (chunk_byte * 3);
		}
	}
	goto next_chunk;
done:
	if (unlikely(ofs > infile_size))
		failure_goto(("Offset greater than archive size, likely corrupted/truncated archive.\n"), error);
	print_verbose("Rzip compression: %.1f%% %lld / %lld\n",
			percentage (utotal, expected_size),
			utotal, expected_size);
	print_verbose("Back end compression: %.1f%% %lld / %lld\n",
			percentage(ctotal, utotal),
			ctotal, utotal);
	print_verbose("Overall compression: %.1f%% %lld / %lld\n",
			percentage(ctotal, expected_size),
			ctotal, expected_size);

	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 (save_ctype == CTYPE_NONE)
		print_output("rzip alone\n");
	else if (save_ctype == CTYPE_BZIP2)
		print_output("rzip + bzip2\n");
	else if (save_ctype == CTYPE_LZO)
		print_output("rzip + lzo\n");
	else if (save_ctype == CTYPE_LZMA)
		print_output("rzip + lzma\n");
	else if (save_ctype == CTYPE_GZIP)
		print_output("rzip + gzip\n");
	else if (save_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_goto(("Failed to seek to md5 data in runzip_fd\n"), error);
		if (unlikely(read(fd_in, md5_stored, MD5_DIGEST_SIZE) != MD5_DIGEST_SIZE))
			fatal_goto(("Failed to read md5 data in runzip_fd\n"), error);
		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 ( !IS_FROM_FILE )
		if (unlikely(close(fd_in)))
			fatal_return(("Failed to close fd_in in get_fileinfo\n"), false);

out:
	dealloc(control->outfile);
	return true;
error:
	if (!STDIN && ! IS_FROM_FILE) close(fd_in);
	return false;
}

Project: lrzip
File: main.c

static void usage(bool compat)
{
	print_output("lrz%s version %s\n", compat ? "" : "ip", PACKAGE_VERSION);
	print_output("Copyright (C) Con Kolivas 2006-2021\n");
	print_output("Based on rzip ");
	print_output("Copyright (C) Andrew Tridgell 1998-2003\n\n");
	print_output("Usage: lrz%s [options] <file...>\n", compat ? "" : "ip");
	print_output("General options:\n");
	if (compat) {
		print_output("	-c, --stdout		output to STDOUT\n");
		print_output("	-C, --check		check integrity of file written on decompression\n");
	} else
		print_output("	-c, -C, --check		check integrity of file written on decompression\n");
	print_output("	-d, --decompress	decompress\n");
	print_output("	-e, --encrypt[=password] password protected sha512/aes128 encryption on compression\n");
	print_output("	-h, -?, --help		show help\n");
	print_output("	-H, --hash		display md5 hash integrity information\n");
	print_output("	-i, --info		show compressed file information\n");
	if (compat) {
		print_output("	-L, --license		display software version and license\n");
		print_output("	-P, --progress		show compression progress\n");
	} else
		print_output("	-q, --quiet		don't show compression progress\n");
	print_output("	-r, --recursive		operate recursively on directories\n");
	print_output("	-t, --test		test compressed file integrity\n");
	print_output("	-v[v%s], --verbose	Increase verbosity\n", compat ? "v" : "");
	print_output("	-V, --version		show version\n");
	print_output("Options affecting output:\n");
	if (!compat)
		print_output("	-D, --delete		delete existing files\n");
	print_output("	-f, --force		force overwrite of any existing files\n");
	if (compat)
		print_output("	-k, --keep		don't delete source files on de/compression\n");
	print_output("	-K, --keep-broken	keep broken or damaged output files\n");
	print_output("	-o, --outfile filename	specify the output file name and/or path\n");
	print_output("	-O, --outdir directory	specify the output directory when -o is not used\n");
	print_output("	-S, --suffix suffix	specify compressed suffix (default '.lrz')\n");
	print_output("Options affecting compression:\n");
	print_output("	--lzma			lzma compression (default)\n");
	print_output("	-b, --bzip2		bzip2 compression\n");
	print_output("	-g, --gzip		gzip compression using zlib\n");
	print_output("	-l, --lzo		lzo compression (ultra fast)\n");
	print_output("	-n, --no-compress	no backend compression - prepare for other compressor\n");
	print_output("	-z, --zpaq		zpaq compression (best, extreme compression, extremely slow)\n");
	print_output("Low level options:\n");
	if (compat) {
		print_output("	-1 .. -9		set lzma/bzip2/gzip compression level (1-9, default 7)\n");
		print_output("	--fast			alias for -1\n");
		print_output("	--best			alias for -9\n");
	}
	if (!compat)
		print_output("	-L, --level level	set lzma/bzip2/gzip compression level (1-9, default 7)\n");
	print_output("	-N, --nice-level value	Set nice value to value (default %d)\n", compat ? 0 : 19);
	print_output("	-p, --threads value	Set processor count to override number of threads\n");
	print_output("	-m, --maxram size	Set maximum available ram in hundreds of MB\n");
	print_output("				overrides detected amount of available ram\n");
	print_output("	-T, --threshold		Disable LZ4 compressibility testing\n");
	print_output("	-U, --unlimited		Use unlimited window size beyond ramsize (potentially much slower)\n");
	print_output("	-w, --window size	maximum compression window in hundreds of MB\n");
	print_output("				default chosen by heuristic dependent on ram and chosen compression\n");
	print_output("\nLRZIP=NOCONFIG environment variable setting can be used to bypass lrzip.conf.\n");
	print_output("TMP environment variable will be used for storage of temporary files when needed.\n");
	print_output("TMPDIR may also be stored in lrzip.conf file.\n");
	print_output("\nIf no filenames or \"-\" is specified, stdin/out will be used.\n");

}

Project: lrzip
File: lrzip.c

static double percentage(i64 num, i64 den)
{
	double d_num, d_den;

	if (den < 100) {
		d_num = num * 100;
		d_den = den;
		if (!d_den)
			d_den = 1;
	} else {
		d_num = num;
		d_den = den / 100;
	}
	return d_num / d_den;
}

Project: lrzip
File: lrzip.c

i64 get_ram(rzip_control *control)
{
	i64 ramsize;
	FILE *meminfo;
	char aux[256];

	ramsize = (i64)sysconf(_SC_PHYS_PAGES) * PAGE_SIZE;
	if (ramsize > 0)
		return ramsize;

	/* Workaround for uclibc which doesn't properly support sysconf */
	if(!(meminfo = fopen("/proc/meminfo", "r")))
		fatal_return(("fopen\n"), -1);

	while(!feof(meminfo) && !fscanf(meminfo, "MemTotal: %"PRId64" kB", &ramsize)) {
		if (unlikely(fgets(aux, sizeof(aux), meminfo) == NULL)) {
			fclose(meminfo);
			fatal_return(("Failed to fgets in get_ram\n"), -1);
		}
	}
	if (fclose(meminfo) == -1)
		fatal_return(("fclose"), -1);
	ramsize *= 1000;

	return ramsize;
}