/* uncompress the given file and remove the original */
static off_t
file_uncompress(char *file, char *outfile, size_t outsize)
{
	struct stat isb, osb;
	off_t size;
	ssize_t rbytes;
	unsigned char header1[4];
	enum filetype method;
	int fd, ofd, zfd = -1;
#ifndef SMALL
	ssize_t rv;
	time_t timestamp = 0;
	char name[PATH_MAX + 1];
#endif

	/* gather the old name info */

	fd = open(file, O_RDONLY);
	if (fd < 0) {
		maybe_warn("can't open %s", file);
		goto lose;
	}

	strlcpy(outfile, file, outsize);
	if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
		maybe_warnx("%s: unknown suffix -- ignored", file);
		goto lose;
	}

	rbytes = read(fd, header1, sizeof header1);
	if (rbytes != sizeof header1) {
		/* we don't want to fail here. */
#ifndef SMALL
		if (fflag)
			goto lose;
#endif
		if (rbytes == -1)
			maybe_warn("can't read %s", file);
		else
			goto unexpected_EOF;
		goto lose;
	}

	method = file_gettype(header1);
#ifndef SMALL
	if (fflag == 0 && method == FT_UNKNOWN) {
		maybe_warnx("%s: not in gzip format", file);
		goto lose;
	}

#endif

#ifndef SMALL
	if (method == FT_GZIP && Nflag) {
		unsigned char ts[4];	/* timestamp */

		rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
		if (rv >= 0 && rv < (ssize_t)(sizeof ts))
			goto unexpected_EOF;
		if (rv == -1) {
			if (!fflag)
				maybe_warn("can't read %s", file);
			goto lose;
		}
		timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];

		if (header1[3] & ORIG_NAME) {
			rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME);
			if (rbytes < 0) {
				maybe_warn("can't read %s", file);
				goto lose;
			}
			if (name[0] != '\0') {
				char *dp, *nf;

				/* Make sure that name is NUL-terminated */
				name[rbytes] = '\0';

				/* strip saved directory name */
				nf = strrchr(name, '/');
				if (nf == NULL)
					nf = name;
				else
					nf++;

				/* preserve original directory name */
				dp = strrchr(file, '/');
				if (dp == NULL)
					dp = file;
				else
					dp++;
				snprintf(outfile, outsize, "%.*s%.*s",
						(int) (dp - file), 
						file, (int) rbytes, nf);
			}
		}
	}
#endif
	lseek(fd, 0, SEEK_SET);

	if (cflag == 0 || lflag) {
		if (fstat(fd, &isb) != 0)
			goto lose;
#ifndef SMALL
		if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
			maybe_warnx("%s has %d other links -- skipping",
			    file, isb.st_nlink - 1);
			goto lose;
		}
		if (nflag == 0 && timestamp)
			isb.st_mtime = timestamp;
		if (check_outfile(outfile) == 0)
			goto lose;
#endif
	}

	if (cflag == 0 && lflag == 0) {
		zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
		if (zfd == STDOUT_FILENO) {
			/* We won't close STDOUT_FILENO later... */
			zfd = dup(zfd);
			close(STDOUT_FILENO);
		}
		if (zfd == -1) {
			maybe_warn("can't open %s", outfile);
			goto lose;
		}
	} else
		zfd = STDOUT_FILENO;

	switch (method) {
#ifndef NO_BZIP2_SUPPORT
	case FT_BZIP2:
		/* XXX */
		if (lflag) {
			maybe_warnx("no -l with bzip2 files");
			goto lose;
		}

		size = unbzip2(fd, zfd, NULL, 0, NULL);
		break;
#endif

#ifndef NO_COMPRESS_SUPPORT
	case FT_Z: {
		FILE *in, *out;

		/* XXX */
		if (lflag) {
			maybe_warnx("no -l with Lempel-Ziv files");
			goto lose;
		}

		if ((in = zdopen(fd)) == NULL) {
			maybe_warn("zdopen for read: %s", file);
			goto lose;
		}

		out = fdopen(dup(zfd), "w");
		if (out == NULL) {
			maybe_warn("fdopen for write: %s", outfile);
			fclose(in);
			goto lose;
		}

		size = zuncompress(in, out, NULL, 0, NULL);
		/* need to fclose() if ferror() is true... */
		if (ferror(in) | fclose(in)) {
			maybe_warn("failed infile fclose");
			unlink(outfile);
			(void)fclose(out);
		}
		if (fclose(out) != 0) {
			maybe_warn("failed outfile fclose");
			unlink(outfile);
			goto lose;
		}
		break;
	}
#endif

#ifndef NO_PACK_SUPPORT
	case FT_PACK:
		if (lflag) {
			maybe_warnx("no -l with packed files");
			goto lose;
		}

		size = unpack(fd, zfd, NULL, 0, NULL);
		break;
#endif

#ifndef NO_XZ_SUPPORT
	case FT_XZ:
		if (lflag) {
			maybe_warnx("no -l with xz files");
			goto lose;
		}

		size = unxz(fd, zfd, NULL, 0, NULL);
		break;
#endif

#ifndef SMALL
	case FT_UNKNOWN:
		if (lflag) {
			maybe_warnx("no -l for unknown filetypes");
			goto lose;
		}
		size = cat_fd(NULL, 0, NULL, fd);
		break;
#endif
	default:
		if (lflag) {
			print_list(fd, isb.st_size, outfile, isb.st_mtime);
			close(fd);
			return -1;	/* XXX */
		}

		size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
		break;
	}

	if (close(fd) != 0)
		maybe_warn("couldn't close input");
	if (zfd != STDOUT_FILENO && close(zfd) != 0)
		maybe_warn("couldn't close output");

	if (size == -1) {
		if (cflag == 0)
			unlink(outfile);
		maybe_warnx("%s: uncompress failed", file);
		return -1;
	}

	/* if testing, or we uncompressed to stdout, this is all we need */
#ifndef SMALL
	if (tflag)
		return size;
#endif
	/* if we are uncompressing to stdin, don't remove the file. */
	if (cflag)
		return size;

	/*
	 * if we create a file...
	 */
	/*
	 * if we can't stat the file don't remove the file.
	 */

	ofd = open(outfile, O_RDWR, 0);
	if (ofd == -1) {
		maybe_warn("couldn't open (leaving original): %s",
			   outfile);
		return -1;
	}
	if (fstat(ofd, &osb) != 0) {
		maybe_warn("couldn't stat (leaving original): %s",
			   outfile);
		close(ofd);
		return -1;
	}
	if (osb.st_size != size) {
		maybe_warnx("stat gave different size: %" PRIdOFF
				" != %" PRIdOFF " (leaving original)",
				size, osb.st_size);
		close(ofd);
		unlink(outfile);
		return -1;
	}
	unlink_input(file, &isb);
#ifndef SMALL
	copymodes(ofd, &isb, outfile);
#endif
	close(ofd);
	return size;

    unexpected_EOF:
	maybe_warnx("%s: unexpected end of file", file);
    lose:
	if (fd != -1)
		close(fd);
	if (zfd != -1 && zfd != STDOUT_FILENO)
		close(fd);
	return -1;
}
static void
handle_stdin(void)
{
	unsigned char header1[4];
	off_t usize, gsize;
	enum filetype method;
	ssize_t bytes_read;
#ifndef NO_COMPRESS_SUPPORT
	FILE *in;
#endif

#ifndef SMALL
	if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
		maybe_warnx("standard input is a terminal -- ignoring");
		return;
	}
#endif

	if (lflag) {
		struct stat isb;

		/* XXX could read the whole file, etc. */
		if (fstat(STDIN_FILENO, &isb) < 0) {
			maybe_warn("fstat");
			return;
		}
		print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime);
		return;
	}

	bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
	if (bytes_read == -1) {
		maybe_warn("can't read stdin");
		return;
	} else if (bytes_read != sizeof(header1)) {
		maybe_warnx("(stdin): unexpected end of file");
		return;
	}

	method = file_gettype(header1);
	switch (method) {
	default:
#ifndef SMALL
		if (fflag == 0) {
			maybe_warnx("unknown compression format");
			return;
		}
		usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
		break;
#endif
	case FT_GZIP:
		usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO, 
			      (char *)header1, sizeof header1, &gsize, "(stdin)");
		break;
#ifndef NO_BZIP2_SUPPORT
	case FT_BZIP2:
		usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
				(char *)header1, sizeof header1, &gsize);
		break;
#endif
#ifndef NO_COMPRESS_SUPPORT
	case FT_Z:
		if ((in = zdopen(STDIN_FILENO)) == NULL) {
			maybe_warnx("zopen of stdin");
			return;
		}

		usize = zuncompress(in, stdout, (char *)header1,
		    sizeof header1, &gsize);
		fclose(in);
		break;
#endif
#ifndef NO_PACK_SUPPORT
	case FT_PACK:
		usize = unpack(STDIN_FILENO, STDOUT_FILENO,
			       (char *)header1, sizeof header1, &gsize);
		break;
#endif
#ifndef NO_XZ_SUPPORT
	case FT_XZ:
		usize = unxz(STDIN_FILENO, STDOUT_FILENO,
			     (char *)header1, sizeof header1, &gsize);
		break;
#endif
	}

#ifndef SMALL
        if (vflag && !tflag && usize != -1 && gsize != -1)
		print_verbage(NULL, NULL, usize, gsize);
	if (vflag && tflag)
		print_test("(stdin)", usize != -1);
#endif 

}
예제 #3
0
int main(int argc, char **argv)
{
	if (argc != 1) {
		die("This program expects no arguments.");
	}

	int r;
	ssize_t rss;
	const char *fifo_path = NULL, *fifo_ctl_path = "fifo-control";
	int pipe_rd_fd = -1, pipe_ctl_rd_fd = -1, out_fd = STDOUT_FILENO;
	pid_t server_pid = -1;

	r = mkfifo(fifo_ctl_path, 0600);
	if (r < 0 && errno != EEXIST) {
		log("Failed to mkfifo() control FIFO \"%s\": %m", fifo_ctl_path);
		r = 1;
		goto cleanup;
	}

	/* blocks until server appear, servers open() and block on writing, we unblock and continue */
	pipe_ctl_rd_fd = open(fifo_ctl_path, O_RDONLY);
	if (pipe_ctl_rd_fd < 0) {
		log("Failed to open() read end on control FIFO \"%s\": %m", fifo_ctl_path);
		r = 1;
		goto cleanup;
	}

	/* atomic read() */
	rss = read(pipe_ctl_rd_fd, &server_pid, sizeof(server_pid));
	if (rss != sizeof(server_pid)) {
		log("Failed to read() %zu bytes from control FIFO \"%s\", read %zd bytes: %m",
		    sizeof(server_pid), fifo_ctl_path, rss);
		r = 1;
		goto cleanup;
	}

	/* here server waits for us with a timeout... */
	pipe_rd_fd = open_rdonly_no_wait((fifo_path = snprintf_static("fifo-%d", server_pid)), 0);
	if (pipe_rd_fd < 0) {
		log("Failed to open() read end on \"%s\": %m", fifo_path);
		r = 1;
		goto cleanup;
	}

	/* read while we can */
	r = cat_fd(pipe_rd_fd, out_fd, pipe_buffer_size(pipe_rd_fd), 0);
	switch (r) {
	case RESULT_READ_NIL:
	case RESULT_OK:
		r = 0;
		break;

	default:
		r = 1;
		break;
	}

cleanup:
	close(pipe_rd_fd);
	close(pipe_ctl_rd_fd);
	return r;
}