Home | History | Annotate | Line # | Download | only in vndcompress
vndcompress.c revision 1.8
      1  1.8  riastrad /*	$NetBSD: vndcompress.c,v 1.8 2013/05/03 23:28:15 riastradh Exp $	*/
      2  1.1   hubertf 
      3  1.8  riastrad /*-
      4  1.8  riastrad  * Copyright (c) 2013 The NetBSD Foundation, Inc.
      5  1.1   hubertf  * All rights reserved.
      6  1.1   hubertf  *
      7  1.8  riastrad  * This code is derived from software contributed to The NetBSD Foundation
      8  1.8  riastrad  * by Taylor R. Campbell.
      9  1.8  riastrad  *
     10  1.1   hubertf  * Redistribution and use in source and binary forms, with or without
     11  1.1   hubertf  * modification, are permitted provided that the following conditions
     12  1.1   hubertf  * are met:
     13  1.1   hubertf  * 1. Redistributions of source code must retain the above copyright
     14  1.1   hubertf  *    notice, this list of conditions and the following disclaimer.
     15  1.1   hubertf  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.1   hubertf  *    notice, this list of conditions and the following disclaimer in the
     17  1.1   hubertf  *    documentation and/or other materials provided with the distribution.
     18  1.1   hubertf  *
     19  1.8  riastrad  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  1.8  riastrad  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  1.1   hubertf  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  1.1   hubertf  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  1.1   hubertf  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  1.1   hubertf  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  1.1   hubertf  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  1.1   hubertf  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  1.1   hubertf  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  1.1   hubertf  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  1.1   hubertf  * POSSIBILITY OF SUCH DAMAGE.
     30  1.1   hubertf  */
     31  1.8  riastrad 
     32  1.8  riastrad #include <sys/cdefs.h>
     33  1.8  riastrad __RCSID("$NetBSD: vndcompress.c,v 1.8 2013/05/03 23:28:15 riastradh Exp $");
     34  1.8  riastrad 
     35  1.8  riastrad #include <sys/endian.h>
     36  1.8  riastrad 
     37  1.8  riastrad #include <assert.h>
     38  1.1   hubertf #include <err.h>
     39  1.8  riastrad #include <errno.h>
     40  1.1   hubertf #include <fcntl.h>
     41  1.5     lukem #include <inttypes.h>
     42  1.8  riastrad #include <limits.h>
     43  1.8  riastrad #include <signal.h>
     44  1.8  riastrad #include <stdbool.h>
     45  1.8  riastrad #include <stdint.h>
     46  1.1   hubertf #include <stdio.h>
     47  1.1   hubertf #include <stdlib.h>
     48  1.1   hubertf #include <string.h>
     49  1.1   hubertf #include <unistd.h>
     50  1.1   hubertf #include <zlib.h>
     51  1.1   hubertf 
     52  1.8  riastrad /* XXX Seems to be missing from <stdio.h>...  */
     53  1.8  riastrad int	snprintf_ss(char *restrict, size_t, const char *restrict, ...)
     54  1.8  riastrad 	    __printflike(3, 4);
     55  1.8  riastrad int	vsnprintf_ss(char *restrict, size_t, const char *restrict, va_list);
     56  1.8  riastrad 
     57  1.8  riastrad #include "common.h"
     58  1.8  riastrad 
     59  1.8  riastrad /*
     60  1.8  riastrad  * XXX Switch to control bug-for-bug byte-for-byte compatibility with
     61  1.8  riastrad  * NetBSD's vndcompress.
     62  1.8  riastrad  */
     63  1.8  riastrad #define	VNDCOMPRESS_COMPAT	0
     64  1.8  riastrad 
     65  1.8  riastrad __CTASSERT(sizeof(struct cloop2_header) == CLOOP2_OFFSET_TABLE_OFFSET);
     66  1.8  riastrad 
     67  1.8  riastrad struct compress_state {
     68  1.8  riastrad 	uint64_t	size;		/* uncompressed size */
     69  1.8  riastrad 	uint64_t	offset;		/* output byte offset */
     70  1.8  riastrad 	uint32_t	blocksize;	/* bytes per block */
     71  1.8  riastrad 	uint32_t	blkno;		/* input block number */
     72  1.8  riastrad 	uint32_t	n_full_blocks;	/* floor(size/blocksize) */
     73  1.8  riastrad 	uint32_t	n_blocks;	/* ceiling(size/blocksize) */
     74  1.8  riastrad 	uint32_t	n_offsets;	/* n_blocks + 1 */
     75  1.8  riastrad 	uint32_t	end_block;	/* last block to transfer */
     76  1.8  riastrad 	uint32_t	checkpoint_blocks;	/* blocks before checkpoint */
     77  1.8  riastrad 	int		image_fd;
     78  1.8  riastrad 	int		cloop2_fd;
     79  1.8  riastrad 	uint64_t	*offset_table;
     80  1.8  riastrad 	uint32_t	n_checkpointed_blocks;
     81  1.8  riastrad 	volatile sig_atomic_t
     82  1.8  riastrad 			initialized;	/* everything above initialized?  */
     83  1.8  riastrad };
     84  1.8  riastrad 
     85  1.8  riastrad /* Global compression state for SIGINFO handler.  */
     86  1.8  riastrad static struct compress_state	global_state;
     87  1.8  riastrad 
     88  1.8  riastrad struct sigdesc {
     89  1.8  riastrad 	int sd_signo;
     90  1.8  riastrad 	const char *sd_name;
     91  1.8  riastrad };
     92  1.1   hubertf 
     93  1.8  riastrad static const struct sigdesc info_signals[] = {
     94  1.8  riastrad 	{ SIGINFO, "SIGINFO" },
     95  1.8  riastrad 	{ SIGUSR1, "SIGUSR1" },
     96  1.1   hubertf };
     97  1.1   hubertf 
     98  1.8  riastrad static const struct sigdesc checkpoint_signals[] = {
     99  1.8  riastrad 	{ SIGUSR2, "SIGUSR2" },
    100  1.8  riastrad };
    101  1.8  riastrad 
    102  1.8  riastrad static void	init_signals(void);
    103  1.8  riastrad static void	init_signal_handler(int, const struct sigdesc *, size_t,
    104  1.8  riastrad 		    void (*)(int));
    105  1.8  riastrad static void	info_signal_handler(int);
    106  1.8  riastrad static void	checkpoint_signal_handler(int);
    107  1.8  riastrad static void	block_signals(sigset_t *);
    108  1.8  riastrad static void	restore_sigmask(const sigset_t *);
    109  1.8  riastrad static void	compress_progress(struct compress_state *);
    110  1.8  riastrad static void	compress_init(int, char **, const struct options *,
    111  1.8  riastrad 		    struct compress_state *);
    112  1.8  riastrad static bool	compress_restart(struct compress_state *);
    113  1.8  riastrad static uint32_t	compress_block(int, int, uint32_t, uint32_t, uint32_t, void *,
    114  1.8  riastrad 		    void *);
    115  1.8  riastrad static void	compress_maybe_checkpoint(struct compress_state *);
    116  1.8  riastrad static void	compress_checkpoint(struct compress_state *);
    117  1.8  riastrad static void	compress_exit(struct compress_state *);
    118  1.8  riastrad static ssize_t	read_block(int, void *, size_t);
    119  1.8  riastrad static void	err_ss(int, const char *);
    120  1.8  riastrad static void	errx_ss(int, const char *, ...) __printflike(2, 3);
    121  1.8  riastrad static void	warn_ss(const char *);
    122  1.8  riastrad static void	warnx_ss(const char *, ...) __printflike(1, 2);
    123  1.8  riastrad static void	vwarnx_ss(const char *, va_list);
    124  1.8  riastrad 
    125  1.1   hubertf /*
    126  1.8  riastrad  * Compression entry point.
    127  1.1   hubertf  */
    128  1.8  riastrad int
    129  1.8  riastrad vndcompress(int argc, char **argv, const struct options *O)
    130  1.8  riastrad {
    131  1.8  riastrad 	struct compress_state *const S = &global_state;
    132  1.8  riastrad 
    133  1.8  riastrad 	/* Paranoia.  The other fields either have no sentinel or use zero.  */
    134  1.8  riastrad 	S->image_fd = -1;
    135  1.8  riastrad 	S->cloop2_fd = -1;
    136  1.8  riastrad 
    137  1.8  riastrad 	/* Set up signal handlers so we can handle SIGINFO ASAP.  */
    138  1.8  riastrad 	init_signals();
    139  1.8  riastrad 
    140  1.8  riastrad 	/*
    141  1.8  riastrad 	 * Parse the arguments to initialize our state.
    142  1.8  riastrad 	 */
    143  1.8  riastrad 	compress_init(argc, argv, O, S);
    144  1.8  riastrad 	assert(MIN_BLOCKSIZE <= S->blocksize);
    145  1.8  riastrad 	assert(S->blocksize <= MAX_BLOCKSIZE);
    146  1.8  riastrad 	assert(S->offset_table != NULL);
    147  1.8  riastrad 	assert(S->n_offsets > 0);
    148  1.8  riastrad 	assert(S->offset_table[0] == htobe64(sizeof(struct cloop2_header) +
    149  1.8  riastrad 		(S->n_offsets * sizeof(uint64_t))));
    150  1.8  riastrad 
    151  1.8  riastrad 	/*
    152  1.8  riastrad 	 * Allocate compression buffers.
    153  1.8  riastrad 	 *
    154  1.8  riastrad 	 * Compression may actually expand.  From an overabundance of
    155  1.8  riastrad 	 * caution, assume it can expand by at most double.
    156  1.8  riastrad 	 *
    157  1.8  riastrad 	 * XXX Check and consider tightening this assumption.
    158  1.8  riastrad 	 */
    159  1.8  riastrad 	__CTASSERT(MAX_BLOCKSIZE <= SIZE_MAX);
    160  1.8  riastrad 	void *const uncompbuf = malloc(S->blocksize);
    161  1.8  riastrad 	if (uncompbuf == NULL)
    162  1.8  riastrad 		err(1, "malloc uncompressed buffer");
    163  1.8  riastrad 
    164  1.8  riastrad 	/* XXX compression ratio bound */
    165  1.8  riastrad 	__CTASSERT(MAX_BLOCKSIZE <= (SIZE_MAX / 2));
    166  1.8  riastrad 	void *const compbuf = malloc(2 * (size_t)S->blocksize);
    167  1.8  riastrad 	if (compbuf == NULL)
    168  1.8  riastrad 		err(1, "malloc compressed buffer");
    169  1.8  riastrad 
    170  1.8  riastrad 	/*
    171  1.8  riastrad 	 * Compress the blocks.  S->blkno specifies the input block
    172  1.8  riastrad 	 * we're about to transfer.  S->offset is the current output
    173  1.8  riastrad 	 * offset.
    174  1.8  riastrad 	 */
    175  1.8  riastrad 	while (S->blkno < S->n_blocks) {
    176  1.8  riastrad 		/* Report any progress.  */
    177  1.8  riastrad 		compress_progress(S);
    178  1.8  riastrad 
    179  1.8  riastrad 		/* Stop if we've done the requested partial transfer.  */
    180  1.8  riastrad 		if ((0 < S->end_block) && (S->end_block <= S->blkno))
    181  1.8  riastrad 			goto out;
    182  1.8  riastrad 
    183  1.8  riastrad 		/* Checkpoint if appropriate.  */
    184  1.8  riastrad 		compress_maybe_checkpoint(S);
    185  1.8  riastrad 
    186  1.8  riastrad 		/* Choose read size: partial if last block, full if not.  */
    187  1.8  riastrad 		const uint32_t readsize = (S->blkno == S->n_full_blocks?
    188  1.8  riastrad 		    (S->size % S->blocksize) : S->blocksize);
    189  1.8  riastrad 		assert(readsize > 0);
    190  1.8  riastrad 		assert(readsize <= S->blocksize);
    191  1.8  riastrad 
    192  1.8  riastrad 		/* Fail noisily if we might be about to overflow.  */
    193  1.8  riastrad 		/* XXX compression ratio bound */
    194  1.8  riastrad 		__CTASSERT(MAX_BLOCKSIZE <= (UINTMAX_MAX / 2));
    195  1.8  riastrad 		assert(S->offset <= MIN(UINT64_MAX, OFF_MAX));
    196  1.8  riastrad 		if ((2 * (uintmax_t)readsize) >
    197  1.8  riastrad 		    (MIN(UINT64_MAX, OFF_MAX) - S->offset))
    198  1.8  riastrad 			errx(1, "blkno %"PRIu32" may overflow: %ju + 2*%ju",
    199  1.8  riastrad 			    S->blkno, (uintmax_t)S->offset,
    200  1.8  riastrad 			    (uintmax_t)readsize);
    201  1.8  riastrad 
    202  1.8  riastrad 		/* Process the block.  */
    203  1.8  riastrad 		const uint32_t complen =
    204  1.8  riastrad 		    compress_block(S->image_fd, S->cloop2_fd, S->blkno,
    205  1.8  riastrad 			S->blocksize, readsize, uncompbuf, compbuf);
    206  1.8  riastrad 
    207  1.8  riastrad 		/*
    208  1.8  riastrad 		 * Signal-atomically update the state to reflect
    209  1.8  riastrad 		 * (a) what block number we are now at,
    210  1.8  riastrad 		 * (b) how far we are now in the output file, and
    211  1.8  riastrad 		 * (c) where the last block ended.
    212  1.8  riastrad 		 */
    213  1.8  riastrad 		assert(S->blkno <= (UINT32_MAX - 1));
    214  1.8  riastrad 		assert(complen <= (MIN(UINT64_MAX, OFF_MAX) - S->offset));
    215  1.8  riastrad 		assert((S->blkno + 1) < S->n_offsets);
    216  1.8  riastrad 	    {
    217  1.8  riastrad 		sigset_t old_sigmask;
    218  1.8  riastrad 		block_signals(&old_sigmask);
    219  1.8  riastrad 		S->blkno += 1;					/* (a) */
    220  1.8  riastrad 		S->offset += complen;				/* (b) */
    221  1.8  riastrad 		S->offset_table[S->blkno] = htobe64(S->offset);	/* (c) */
    222  1.8  riastrad 		restore_sigmask(&old_sigmask);
    223  1.8  riastrad 	    }
    224  1.8  riastrad 	}
    225  1.8  riastrad 
    226  1.8  riastrad 	/* Make sure we're all done. */
    227  1.8  riastrad 	assert(S->blkno == S->n_blocks);
    228  1.8  riastrad 	assert((S->blkno + 1) == S->n_offsets);
    229  1.8  riastrad 
    230  1.8  riastrad 	/* Pad to the disk block size.  */
    231  1.8  riastrad 	const uint32_t n_extra = (S->offset % DEV_BSIZE);
    232  1.8  riastrad 	if (n_extra != 0) {
    233  1.8  riastrad 		const uint32_t n_padding = (DEV_BSIZE - n_extra);
    234  1.8  riastrad 		/* Reuse compbuf -- guaranteed to be large enough.  */
    235  1.8  riastrad 		(void)memset(compbuf, 0, n_padding);
    236  1.8  riastrad 		const ssize_t n_written = write(S->cloop2_fd, compbuf,
    237  1.8  riastrad 		    n_padding);
    238  1.8  riastrad 		if (n_written == -1)
    239  1.8  riastrad 			err(1, "write final padding failed");
    240  1.8  riastrad 		assert(n_written >= 0);
    241  1.8  riastrad 		if ((size_t)n_written != n_padding)
    242  1.8  riastrad 			errx(1, "partial write of final padding bytes"
    243  1.8  riastrad 			    ": %zd <= %"PRIu32,
    244  1.8  riastrad 			    n_written, n_padding);
    245  1.8  riastrad 
    246  1.8  riastrad 		/* Account for the extra bytes in the output file.  */
    247  1.8  riastrad 		assert(n_padding <= (MIN(UINT64_MAX, OFF_MAX) - S->offset));
    248  1.8  riastrad 	    {
    249  1.8  riastrad 		sigset_t old_sigmask;
    250  1.8  riastrad 		block_signals(&old_sigmask);
    251  1.8  riastrad 		S->offset += n_padding;
    252  1.8  riastrad 		restore_sigmask(&old_sigmask);
    253  1.8  riastrad 	    }
    254  1.8  riastrad 	}
    255  1.8  riastrad 
    256  1.8  riastrad out:
    257  1.8  riastrad 	/* Commit the offset table.  */
    258  1.8  riastrad 	assert(S->offset <= OFF_MAX);
    259  1.8  riastrad 	assert((off_t)S->offset == lseek(S->cloop2_fd, 0, SEEK_CUR));
    260  1.8  riastrad 	compress_checkpoint(S);
    261  1.8  riastrad 
    262  1.8  riastrad 	/*
    263  1.8  riastrad 	 * Free the compression buffers and finalize the compression.
    264  1.8  riastrad 	 */
    265  1.8  riastrad 	free(compbuf);
    266  1.8  riastrad 	free(uncompbuf);
    267  1.8  riastrad 	compress_exit(S);
    268  1.1   hubertf 
    269  1.8  riastrad 	return 0;
    270  1.8  riastrad }
    271  1.1   hubertf 
    272  1.1   hubertf /*
    273  1.8  riastrad  * Signal cruft.
    274  1.1   hubertf  */
    275  1.8  riastrad 
    276  1.8  riastrad static void
    277  1.8  riastrad init_signals(void)
    278  1.8  riastrad {
    279  1.8  riastrad 
    280  1.8  riastrad 	init_signal_handler(SA_RESTART, info_signals,
    281  1.8  riastrad 	    __arraycount(info_signals), &info_signal_handler);
    282  1.8  riastrad 	init_signal_handler(SA_RESTART, checkpoint_signals,
    283  1.8  riastrad 	    __arraycount(checkpoint_signals), &checkpoint_signal_handler);
    284  1.8  riastrad }
    285  1.8  riastrad 
    286  1.8  riastrad static void
    287  1.8  riastrad init_signal_handler(int flags, const struct sigdesc *signals, size_t n,
    288  1.8  riastrad     void (*handler)(int))
    289  1.1   hubertf {
    290  1.8  riastrad 	static const struct sigaction zero_sa;
    291  1.8  riastrad 	struct sigaction sa = zero_sa;
    292  1.8  riastrad 	size_t i;
    293  1.8  riastrad 
    294  1.8  riastrad 	(void)sigemptyset(&sa.sa_mask);
    295  1.8  riastrad 	for (i = 0; i < n; i++)
    296  1.8  riastrad 		(void)sigaddset(&sa.sa_mask, signals[i].sd_signo);
    297  1.8  riastrad 	sa.sa_flags = flags;
    298  1.8  riastrad 	sa.sa_handler = handler;
    299  1.8  riastrad 	for (i = 0; i < n; i++)
    300  1.8  riastrad 		if (sigaction(signals[i].sd_signo, &sa, NULL) == -1)
    301  1.8  riastrad 			err(1, "sigaction(%s)", signals[i].sd_name);
    302  1.8  riastrad }
    303  1.8  riastrad 
    304  1.8  riastrad static void
    305  1.8  riastrad info_signal_handler(int signo __unused)
    306  1.8  riastrad {
    307  1.8  riastrad 	/* Save errno.  */
    308  1.8  riastrad 	const int error = errno;
    309  1.8  riastrad 	struct compress_state *const S = &global_state;
    310  1.8  riastrad 	char buf[128];
    311  1.8  riastrad 
    312  1.8  riastrad 	/* Bail if the state is not yet initialized.  */
    313  1.8  riastrad 	if (!S->initialized) {
    314  1.8  riastrad 		warnx_ss("initializing");
    315  1.8  riastrad 		goto out;
    316  1.8  riastrad 	}
    317  1.8  riastrad 
    318  1.8  riastrad 	/* Carefully calculate our I/O position.  */
    319  1.8  riastrad 	assert(S->blocksize > 0);
    320  1.8  riastrad 	__CTASSERT(MAX_N_BLOCKS <= (UINT64_MAX / MAX_BLOCKSIZE));
    321  1.8  riastrad 	const uint64_t nread = ((uint64_t)S->blkno * (uint64_t)S->blocksize);
    322  1.8  riastrad 
    323  1.8  riastrad 	assert(S->n_blocks > 0);
    324  1.8  riastrad 	__CTASSERT(MAX_N_BLOCKS <= ((UINT64_MAX / sizeof(uint64_t)) -
    325  1.8  riastrad 		CLOOP2_OFFSET_TABLE_OFFSET));
    326  1.8  riastrad 	const uint64_t nwritten = (S->offset <= (CLOOP2_OFFSET_TABLE_OFFSET +
    327  1.8  riastrad 		(S->n_blocks * sizeof(uint64_t)))?
    328  1.8  riastrad 	    0 : S->offset);
    329  1.8  riastrad 
    330  1.8  riastrad 	/* snprintf_ss can't do floating-point, so do fixed-point instead.  */
    331  1.8  riastrad 	const uint64_t ratio_percent =
    332  1.8  riastrad 	    (nread > 0?
    333  1.8  riastrad 		((nwritten >= (UINT64_MAX / 100)) ?
    334  1.8  riastrad 		    ((nwritten / nread) * 100) : ((nwritten * 100) / nread))
    335  1.8  riastrad 		: 0);
    336  1.8  riastrad 
    337  1.8  riastrad 	/* Format the status.  */
    338  1.8  riastrad 	assert(S->n_checkpointed_blocks <= (UINT64_MAX / S->blocksize));
    339  1.8  riastrad 	const int n = snprintf_ss(buf, sizeof(buf),
    340  1.8  riastrad 	    "vndcompress: read %"PRIu64" bytes, wrote %"PRIu64" bytes, "
    341  1.8  riastrad 	    "compression ratio %"PRIu64"%% (checkpointed %"PRIu64" bytes)\n",
    342  1.8  riastrad 	    nread, nwritten, ratio_percent,
    343  1.8  riastrad 	    ((uint64_t)S->n_checkpointed_blocks * (uint64_t)S->blocksize));
    344  1.8  riastrad 	if (n < 0) {
    345  1.8  riastrad 		const char msg[] = "vndcompress: can't format info\n";
    346  1.8  riastrad 		(void)write(STDERR_FILENO, msg, __arraycount(msg));
    347  1.1   hubertf 	} else {
    348  1.8  riastrad 		__CTASSERT(INT_MAX <= SIZE_MAX);
    349  1.8  riastrad 		(void)write(STDERR_FILENO, buf, (size_t)n);
    350  1.8  riastrad 	}
    351  1.8  riastrad 
    352  1.8  riastrad out:
    353  1.8  riastrad 	/* Restore errno.  */
    354  1.8  riastrad 	errno = error;
    355  1.8  riastrad }
    356  1.8  riastrad 
    357  1.8  riastrad static void
    358  1.8  riastrad checkpoint_signal_handler(int signo __unused)
    359  1.8  riastrad {
    360  1.8  riastrad 	/* Save errno.  */
    361  1.8  riastrad 	const int error = errno;
    362  1.8  riastrad 	struct compress_state *const S = &global_state;
    363  1.8  riastrad 
    364  1.8  riastrad 	/* Bail if the state is not yet initialized.  */
    365  1.8  riastrad 	if (!S->initialized) {
    366  1.8  riastrad 		warnx_ss("nothing to checkpoint yet");
    367  1.8  riastrad 		goto out;
    368  1.1   hubertf 	}
    369  1.8  riastrad 
    370  1.8  riastrad 	assert(S->image_fd >= 0);
    371  1.8  riastrad 	assert(S->cloop2_fd >= 0);
    372  1.8  riastrad 
    373  1.8  riastrad 	/* Take a checkpoint.  */
    374  1.8  riastrad 	assert(S->blocksize > 0);
    375  1.8  riastrad 	assert(S->blkno <= (UINT64_MAX / S->blocksize));
    376  1.8  riastrad 	warnx_ss("checkpointing %"PRIu64" bytes",
    377  1.8  riastrad 	    ((uint64_t)S->blkno * (uint64_t)S->blocksize));
    378  1.8  riastrad 	compress_checkpoint(S);
    379  1.8  riastrad 
    380  1.8  riastrad out:
    381  1.8  riastrad 	/* Restore errno.  */
    382  1.8  riastrad 	errno = error;
    383  1.8  riastrad }
    384  1.8  riastrad 
    385  1.8  riastrad static void
    386  1.8  riastrad block_signals(sigset_t *old_sigmask)
    387  1.8  riastrad {
    388  1.8  riastrad 	sigset_t block;
    389  1.8  riastrad 
    390  1.8  riastrad 	(void)sigfillset(&block);
    391  1.8  riastrad 	(void)sigprocmask(SIG_BLOCK, &block, old_sigmask);
    392  1.8  riastrad }
    393  1.8  riastrad 
    394  1.8  riastrad static void
    395  1.8  riastrad restore_sigmask(const sigset_t *sigmask)
    396  1.8  riastrad {
    397  1.8  riastrad 
    398  1.8  riastrad 	(void)sigprocmask(SIG_SETMASK, sigmask, NULL);
    399  1.8  riastrad }
    400  1.8  riastrad 
    401  1.8  riastrad /*
    402  1.8  riastrad  * Report progress.
    403  1.8  riastrad  *
    404  1.8  riastrad  * XXX Should do a progress bar here.
    405  1.8  riastrad  */
    406  1.8  riastrad static void
    407  1.8  riastrad compress_progress(struct compress_state *S __unused)
    408  1.8  riastrad {
    409  1.1   hubertf }
    410  1.1   hubertf 
    411  1.1   hubertf /*
    412  1.8  riastrad  * Parse arguments, open the files, and initialize the state.
    413  1.1   hubertf  */
    414  1.7     joerg static void
    415  1.8  riastrad compress_init(int argc, char **argv, const struct options *O,
    416  1.8  riastrad     struct compress_state *S)
    417  1.8  riastrad {
    418  1.8  riastrad 	uint32_t i;
    419  1.8  riastrad 
    420  1.8  riastrad 	if (!((argc == 2) || (argc == 3)))
    421  1.8  riastrad 		usage();
    422  1.8  riastrad 
    423  1.8  riastrad 	const char *const image_pathname = argv[0];
    424  1.8  riastrad 	const char *const cloop2_pathname = argv[1];
    425  1.8  riastrad 
    426  1.8  riastrad 	/* Grab the block size either from `-s' or from the last argument.  */
    427  1.8  riastrad 	__CTASSERT(0 < DEV_BSIZE);
    428  1.8  riastrad 	__CTASSERT((MIN_BLOCKSIZE % DEV_BSIZE) == 0);
    429  1.8  riastrad 	__CTASSERT(MIN_BLOCKSIZE <= DEF_BLOCKSIZE);
    430  1.8  riastrad 	__CTASSERT((DEF_BLOCKSIZE % DEV_BSIZE) == 0);
    431  1.8  riastrad 	__CTASSERT(DEF_BLOCKSIZE <= MAX_BLOCKSIZE);
    432  1.8  riastrad 	__CTASSERT((MAX_BLOCKSIZE % DEV_BSIZE) == 0);
    433  1.8  riastrad 	if (ISSET(O->flags, FLAG_s)) {
    434  1.8  riastrad 		if (argc == 3) {
    435  1.8  riastrad 			warnx("use -s or the extra argument, not both");
    436  1.8  riastrad 			usage();
    437  1.8  riastrad 		}
    438  1.8  riastrad 		S->blocksize = O->blocksize;
    439  1.8  riastrad 	} else {
    440  1.8  riastrad 		S->blocksize = (argc == 2? DEF_BLOCKSIZE :
    441  1.8  riastrad 		    strsuftoll("block size", argv[2], MIN_BLOCKSIZE,
    442  1.8  riastrad 			MAX_BLOCKSIZE));
    443  1.8  riastrad 	}
    444  1.8  riastrad 
    445  1.8  riastrad 	/* Sanity-check the blocksize.  (strsuftoll guarantees bounds.)  */
    446  1.8  riastrad 	__CTASSERT(DEV_BSIZE <= UINT32_MAX);
    447  1.8  riastrad 	if ((S->blocksize % DEV_BSIZE) != 0)
    448  1.8  riastrad 		errx(1, "bad blocksize: %"PRIu32
    449  1.8  riastrad 		    " (not a multiple of %"PRIu32")",
    450  1.8  riastrad 		    S->blocksize, (uint32_t)DEV_BSIZE);
    451  1.8  riastrad 	assert(MIN_BLOCKSIZE <= S->blocksize);
    452  1.8  riastrad 	assert((S->blocksize % DEV_BSIZE) == 0);
    453  1.8  riastrad 	assert(S->blocksize <= MAX_BLOCKSIZE);
    454  1.8  riastrad 
    455  1.8  riastrad 	/* Grab the end block number if we have one.  */
    456  1.8  riastrad 	S->end_block = (ISSET(O->flags, FLAG_p)? O->end_block : 0);
    457  1.8  riastrad 
    458  1.8  riastrad 	/* Grab the checkpoint block count, if we have one.  */
    459  1.8  riastrad 	S->checkpoint_blocks =
    460  1.8  riastrad 	    (ISSET(O->flags, FLAG_k)? O->checkpoint_blocks : 0);
    461  1.8  riastrad 
    462  1.8  riastrad 	/* Open the input image file and the output cloop2 file.  */
    463  1.8  riastrad 	S->image_fd = open(image_pathname, O_RDONLY);
    464  1.8  riastrad 	if (S->image_fd == -1)
    465  1.8  riastrad 		err(1, "open(%s)", image_pathname);
    466  1.8  riastrad 
    467  1.8  riastrad 	int oflags;
    468  1.8  riastrad 	if (!ISSET(O->flags, FLAG_r))
    469  1.8  riastrad 		oflags = (O_WRONLY | O_TRUNC | O_CREAT); /* XXX O_EXCL?  */
    470  1.8  riastrad 	else if (!ISSET(O->flags, FLAG_R))
    471  1.8  riastrad 		oflags = (O_RDWR | O_CREAT);
    472  1.8  riastrad 	else
    473  1.8  riastrad 		oflags = O_RDWR;
    474  1.8  riastrad 	S->cloop2_fd = open(cloop2_pathname, oflags, 0777);
    475  1.8  riastrad 	if (S->cloop2_fd == -1)
    476  1.8  riastrad 		err(1, "open(%s)", cloop2_pathname);
    477  1.8  riastrad 
    478  1.8  riastrad 	/* Find the size of the input image.  */
    479  1.8  riastrad 	if (ISSET(O->flags, FLAG_l)) {
    480  1.8  riastrad 		S->size = O->length;
    481  1.8  riastrad 	} else {
    482  1.8  riastrad 		static const struct stat zero_st;
    483  1.8  riastrad 		struct stat st = zero_st;
    484  1.8  riastrad 		if (fstat(S->image_fd, &st) == -1)
    485  1.8  riastrad 			err(1, "stat(%s)", image_pathname);
    486  1.8  riastrad 		if (st.st_size <= 0)
    487  1.8  riastrad 			errx(1, "unknown image size");
    488  1.8  riastrad 		assert(st.st_size >= 0);
    489  1.8  riastrad 		__CTASSERT(OFF_MAX <= UINT64_MAX);
    490  1.8  riastrad 		assert(__type_fit(uint64_t, st.st_size));
    491  1.8  riastrad 		S->size = st.st_size;
    492  1.8  riastrad 	}
    493  1.8  riastrad 	assert(S->size <= OFF_MAX);
    494  1.8  riastrad 
    495  1.8  riastrad 	/* Find number of full blocks and whether there's a partial block.  */
    496  1.8  riastrad 	S->n_full_blocks = (S->size / S->blocksize);
    497  1.8  riastrad 	assert(S->n_full_blocks <=
    498  1.8  riastrad 	    (UINT32_MAX - ((S->size % S->blocksize) > 0)));
    499  1.8  riastrad 	S->n_blocks = (S->n_full_blocks + ((S->size % S->blocksize) > 0));
    500  1.8  riastrad 	assert(S->n_full_blocks <= S->n_blocks);
    501  1.8  riastrad 
    502  1.8  riastrad 	if (S->n_blocks > MAX_N_BLOCKS)
    503  1.8  riastrad 		errx(1, "image too large for block size %"PRIu32": %"PRIu64,
    504  1.8  riastrad 		    S->blocksize, S->size);
    505  1.8  riastrad 	assert(S->n_blocks <= MAX_N_BLOCKS);
    506  1.8  riastrad 
    507  1.8  riastrad 	/* Allocate an offset table for the blocks; one extra for the end.  */
    508  1.8  riastrad 	__CTASSERT(MAX_N_BLOCKS <= (UINT32_MAX - 1));
    509  1.8  riastrad 	S->n_offsets = (S->n_blocks + 1);
    510  1.8  riastrad 	__CTASSERT(MAX_N_OFFSETS == (MAX_N_BLOCKS + 1));
    511  1.8  riastrad 	__CTASSERT(MAX_N_OFFSETS <= (SIZE_MAX / sizeof(uint64_t)));
    512  1.8  riastrad 	S->offset_table = malloc(S->n_offsets * sizeof(uint64_t));
    513  1.8  riastrad 	if (S->offset_table == NULL)
    514  1.8  riastrad 		err(1, "malloc offset table");
    515  1.8  riastrad 
    516  1.8  riastrad 	/* Attempt to restart a partial transfer if requested.  */
    517  1.8  riastrad 	if (ISSET(O->flags, FLAG_r)) {
    518  1.8  riastrad 		if (compress_restart(S)) {
    519  1.8  riastrad 			/*
    520  1.8  riastrad 			 * Restart succeeded.  Truncate the output
    521  1.8  riastrad 			 * here, in case any garbage got appended.  We
    522  1.8  riastrad 			 * are committed to making progress at this
    523  1.8  riastrad 			 * point.  If the ftruncate fails, we don't
    524  1.8  riastrad 			 * lose anything valuable -- this is the last
    525  1.8  riastrad 			 * point at which we can restart anyway.
    526  1.8  riastrad 			 */
    527  1.8  riastrad 			if (ftruncate(S->cloop2_fd, S->offset) == -1)
    528  1.8  riastrad 				err(1, "ftruncate failed");
    529  1.8  riastrad 
    530  1.8  riastrad 			/* All set!  No more initialization to do.  */
    531  1.8  riastrad 			return;
    532  1.8  riastrad 		} else {
    533  1.8  riastrad 			/* Restart failed.  Barf now if requested.  */
    534  1.8  riastrad 			if (ISSET(O->flags, FLAG_R))
    535  1.8  riastrad 				errx(1, "restart failed, aborting");
    536  1.8  riastrad 
    537  1.8  riastrad 			/* Otherwise, truncate and start at the top.  */
    538  1.8  riastrad 			if (ftruncate(S->cloop2_fd, 0) == -1)
    539  1.8  riastrad 				err(1, "truncate failed");
    540  1.8  riastrad 			if (lseek(S->cloop2_fd, 0, SEEK_SET) == -1)
    541  1.8  riastrad 				err(1, "lseek to cloop2 beginning failed");
    542  1.8  riastrad 			if (lseek(S->image_fd, 0, SEEK_SET) == -1)
    543  1.8  riastrad 				err(1, "lseek to image beginning failed");
    544  1.8  riastrad 		}
    545  1.8  riastrad 	}
    546  1.8  riastrad 
    547  1.1   hubertf 	/*
    548  1.8  riastrad 	 * Initialize the offset table to all ones (except for the
    549  1.8  riastrad 	 * fixed first offset) so that we can easily detect where we
    550  1.8  riastrad 	 * were interrupted if we want to restart.
    551  1.1   hubertf 	 */
    552  1.8  riastrad 	__CTASSERT(MAX_N_OFFSETS <= UINT32_MAX);
    553  1.8  riastrad 	assert(S->n_offsets > 0);
    554  1.8  riastrad 	S->offset_table[0] = htobe64(sizeof(struct cloop2_header) +
    555  1.8  riastrad 	    (S->n_offsets * sizeof(uint64_t)));
    556  1.8  riastrad 	for (i = 1; i < S->n_offsets; i++)
    557  1.8  riastrad 		S->offset_table[i] = ~(uint64_t)0;
    558  1.8  riastrad 
    559  1.8  riastrad 	/* Write a bogus (zero) header for now, until we checkpoint.  */
    560  1.8  riastrad 	static const struct cloop2_header zero_header;
    561  1.8  riastrad 	const ssize_t h_written = write(S->cloop2_fd, &zero_header,
    562  1.8  riastrad 	    sizeof(zero_header));
    563  1.8  riastrad 	if (h_written == -1)
    564  1.8  riastrad 		err(1, "write header");
    565  1.8  riastrad 	assert(h_written >= 0);
    566  1.8  riastrad 	if ((size_t)h_written != sizeof(zero_header))
    567  1.8  riastrad 		errx(1, "partial write of header: %zd <= %zu", h_written,
    568  1.8  riastrad 		    sizeof(zero_header));
    569  1.8  riastrad 
    570  1.8  riastrad 	/* Write the initial (empty) offset table.  */
    571  1.8  riastrad 	const ssize_t ot_written = write(S->cloop2_fd, S->offset_table,
    572  1.8  riastrad 	    (S->n_offsets * sizeof(uint64_t)));
    573  1.8  riastrad 	if (ot_written == -1)
    574  1.8  riastrad 		err(1, "write initial offset table");
    575  1.8  riastrad 	assert(ot_written >= 0);
    576  1.8  riastrad 	if ((size_t)ot_written != (S->n_offsets * sizeof(uint64_t)))
    577  1.8  riastrad 		errx(1, "partial write of initial offset bytes: %zd <= %zu",
    578  1.8  riastrad 		    ot_written, (size_t)(S->n_offsets * sizeof(uint64_t)));
    579  1.8  riastrad 
    580  1.8  riastrad 	/* Start at the beginning of the image.  */
    581  1.8  riastrad 	S->blkno = 0;
    582  1.8  riastrad 	S->offset = (sizeof(struct cloop2_header) +
    583  1.8  riastrad 	    (S->n_offsets * sizeof(uint64_t)));
    584  1.8  riastrad 	S->n_checkpointed_blocks = 0;
    585  1.8  riastrad 
    586  1.8  riastrad 	/* Good to go and ready for interruption by a signal.  */
    587  1.8  riastrad 	S->initialized = 1;
    588  1.8  riastrad }
    589  1.8  riastrad 
    590  1.8  riastrad /*
    591  1.8  riastrad  * Try to recover state from an existing output file.
    592  1.8  riastrad  *
    593  1.8  riastrad  * On success, fill S->offset_table with what's in the file, set
    594  1.8  riastrad  * S->blkno and S->offset to reflect our position, and seek to the
    595  1.8  riastrad  * respective positions in the input and output files.
    596  1.8  riastrad  *
    597  1.8  riastrad  * On failure, return false.  May clobber S->offset_table, S->blkno,
    598  1.8  riastrad  * S->offset, and the file pointers.
    599  1.8  riastrad  */
    600  1.8  riastrad static bool
    601  1.8  riastrad compress_restart(struct compress_state *S)
    602  1.8  riastrad {
    603  1.8  riastrad 
    604  1.8  riastrad 	/* Read in the header.  */
    605  1.8  riastrad 	static const struct cloop2_header zero_header;
    606  1.8  riastrad 	struct cloop2_header header = zero_header;
    607  1.8  riastrad 
    608  1.8  riastrad 	const ssize_t h_read = read_block(S->cloop2_fd, &header,
    609  1.8  riastrad 	    sizeof(header));
    610  1.8  riastrad 	if (h_read == -1) {
    611  1.8  riastrad 		warn("failed to read header");
    612  1.8  riastrad 		return false;
    613  1.8  riastrad 	}
    614  1.8  riastrad 	assert(h_read >= 0);
    615  1.8  riastrad 	if ((size_t)h_read != sizeof(header)) {
    616  1.8  riastrad 		warnx("partial read of header");
    617  1.8  riastrad 		return false;
    618  1.8  riastrad 	}
    619  1.8  riastrad 
    620  1.8  riastrad 	/* Check that the header looks like a header.  */
    621  1.8  riastrad 	__CTASSERT(sizeof(cloop2_magic) <= sizeof(header.cl2h_magic));
    622  1.8  riastrad 	if (memcmp(header.cl2h_magic, cloop2_magic, sizeof(cloop2_magic))
    623  1.8  riastrad 	    != 0) {
    624  1.8  riastrad 		warnx("bad cloop2 shell script magic");
    625  1.8  riastrad 		return false;
    626  1.8  riastrad 	}
    627  1.8  riastrad 
    628  1.8  riastrad 	/* Check the header parameters.  */
    629  1.8  riastrad 	if (be32toh(header.cl2h_blocksize) != S->blocksize) {
    630  1.8  riastrad 		warnx("mismatched block size: %"PRIu32
    631  1.8  riastrad 		    " (expected %"PRIu32")",
    632  1.8  riastrad 		    be32toh(header.cl2h_blocksize), S->blocksize);
    633  1.8  riastrad 		return false;
    634  1.8  riastrad 	}
    635  1.8  riastrad 	if (be32toh(header.cl2h_n_blocks) != S->n_blocks) {
    636  1.8  riastrad 		warnx("mismatched number of blocks: %"PRIu32
    637  1.8  riastrad 		    " (expected %"PRIu32")",
    638  1.8  riastrad 		    be32toh(header.cl2h_n_blocks), S->n_blocks);
    639  1.8  riastrad 		return false;
    640  1.8  riastrad 	}
    641  1.8  riastrad 
    642  1.8  riastrad 	/* Read in the partial offset table.  */
    643  1.8  riastrad 	const ssize_t ot_read = read_block(S->cloop2_fd, S->offset_table,
    644  1.8  riastrad 	    (S->n_offsets * sizeof(uint64_t)));
    645  1.8  riastrad 	if (ot_read == -1) {
    646  1.8  riastrad 		warn("failed to read offset table");
    647  1.8  riastrad 		return false;
    648  1.8  riastrad 	}
    649  1.8  riastrad 	assert(ot_read >= 0);
    650  1.8  riastrad 	if ((size_t)ot_read != (S->n_offsets * sizeof(uint64_t))) {
    651  1.8  riastrad 		warnx("partial read of offset table");
    652  1.8  riastrad 		return false;
    653  1.8  riastrad 	}
    654  1.8  riastrad 
    655  1.8  riastrad 	if (be64toh(S->offset_table[0]) != (sizeof(struct cloop2_header) +
    656  1.8  riastrad 		(S->n_offsets * sizeof(uint64_t)))) {
    657  1.8  riastrad 		warnx("first offset is not %"PRIu64": %"PRIu64,
    658  1.8  riastrad 		    ((uint64_t)S->n_offsets * sizeof(uint64_t)),
    659  1.8  riastrad 		    be64toh(S->offset_table[0]));
    660  1.8  riastrad 		return false;
    661  1.8  riastrad 	}
    662  1.8  riastrad 
    663  1.8  riastrad 	/* Find where we left off.  */
    664  1.8  riastrad 	__CTASSERT(MAX_N_OFFSETS <= UINT32_MAX);
    665  1.8  riastrad 	uint32_t blkno = 0;
    666  1.8  riastrad 	for (blkno = 0; blkno < S->n_blocks; blkno++) {
    667  1.8  riastrad 		if (S->offset_table[blkno] == ~(uint64_t)0)
    668  1.8  riastrad 			break;
    669  1.8  riastrad 		if (0 < blkno) {
    670  1.8  riastrad 			const uint64_t start =
    671  1.8  riastrad 			    be64toh(S->offset_table[blkno - 1]);
    672  1.8  riastrad 			const uint64_t end = be64toh(S->offset_table[blkno]);
    673  1.8  riastrad 			if (end <= start) {
    674  1.8  riastrad 				warnx("bad offset table: 0x%"PRIx64
    675  1.8  riastrad 				    ", 0x%"PRIx64, start, end);
    676  1.8  riastrad 				return false;
    677  1.8  riastrad 			}
    678  1.8  riastrad 			/* XXX compression ratio bound */
    679  1.8  riastrad 			__CTASSERT(MAX_BLOCKSIZE <= (SIZE_MAX / 2));
    680  1.8  riastrad 			if ((2 * (size_t)S->blocksize) <= (end - start)) {
    681  1.8  riastrad 				warnx("block %"PRIu32" too large:"
    682  1.8  riastrad 				    " %"PRIu64" bytes",
    683  1.8  riastrad 				    blkno, (end - start));
    684  1.8  riastrad 				return false;
    685  1.8  riastrad 			}
    686  1.8  riastrad 		}
    687  1.8  riastrad 	}
    688  1.8  riastrad 
    689  1.8  riastrad 	if (blkno == 0) {
    690  1.8  riastrad 		warnx("no blocks were written; nothing to restart");
    691  1.8  riastrad 		return false;
    692  1.8  riastrad 	}
    693  1.8  riastrad 
    694  1.8  riastrad 	/* Make sure the rest of the offset table is all ones.  */
    695  1.8  riastrad 	if (blkno < S->n_blocks) {
    696  1.8  riastrad 		uint32_t nblkno;
    697  1.8  riastrad 
    698  1.8  riastrad 		for (nblkno = blkno; nblkno < S->n_blocks; nblkno++) {
    699  1.8  riastrad 			if (S->offset_table[nblkno] != ~(uint64_t)0) {
    700  1.8  riastrad 				warnx("bad partial offset table entry"
    701  1.8  riastrad 				    " at %"PRIu32": %"PRIu64,
    702  1.8  riastrad 				    nblkno,
    703  1.8  riastrad 				    be64toh(S->offset_table[nblkno]));
    704  1.8  riastrad 				return false;
    705  1.8  riastrad 			}
    706  1.8  riastrad 		}
    707  1.1   hubertf 	}
    708  1.8  riastrad 
    709  1.1   hubertf 	/*
    710  1.8  riastrad 	 * XXX Consider decompressing some number of blocks to make
    711  1.8  riastrad 	 * sure they match.
    712  1.1   hubertf 	 */
    713  1.8  riastrad 
    714  1.8  riastrad 	/* Back up by one.  */
    715  1.8  riastrad 	assert(1 <= blkno);
    716  1.8  riastrad 	blkno -= 1;
    717  1.8  riastrad 
    718  1.8  riastrad 	/* Seek to the input position.  */
    719  1.8  riastrad 	assert(S->size <= OFF_MAX);
    720  1.8  riastrad 	assert(blkno <= (S->size / S->blocksize));
    721  1.8  riastrad 	const off_t restart_position = ((off_t)blkno * (off_t)S->blocksize);
    722  1.8  riastrad 	assert(0 <= restart_position);
    723  1.8  riastrad 	assert(restart_position <= (off_t)S->size);
    724  1.8  riastrad 	if (lseek(S->image_fd, restart_position, SEEK_SET) == -1) {
    725  1.8  riastrad 		if (errno != ESPIPE) {
    726  1.8  riastrad 			warn("lseek input image failed");
    727  1.8  riastrad 			return false;
    728  1.8  riastrad 		}
    729  1.8  riastrad 
    730  1.8  riastrad 		/* Try read instead of lseek for a pipe/socket/fifo.  */
    731  1.8  riastrad 		void *const buffer = malloc(0x10000);
    732  1.8  riastrad 		if (buffer == NULL)
    733  1.8  riastrad 			err(1, "malloc temporary buffer");
    734  1.8  riastrad 		off_t left = restart_position;
    735  1.8  riastrad 		while (left > 0) {
    736  1.8  riastrad 			const size_t size = MIN(0x10000, left);
    737  1.8  riastrad 			const ssize_t n_read = read_block(S->image_fd, buffer,
    738  1.8  riastrad 			    size);
    739  1.8  riastrad 			if (n_read == -1) {
    740  1.8  riastrad 				free(buffer);
    741  1.8  riastrad 				warn("read of input image failed");
    742  1.8  riastrad 				return false;
    743  1.8  riastrad 			}
    744  1.8  riastrad 			assert(n_read >= 0);
    745  1.8  riastrad 			if ((size_t)n_read != size) {
    746  1.8  riastrad 				free(buffer);
    747  1.8  riastrad 				warnx("partial read of input image");
    748  1.8  riastrad 				return false;
    749  1.1   hubertf 			}
    750  1.8  riastrad 			assert((off_t)size <= left);
    751  1.8  riastrad 			left -= size;
    752  1.1   hubertf 		}
    753  1.8  riastrad 		free(buffer);
    754  1.8  riastrad 	}
    755  1.8  riastrad 
    756  1.8  riastrad 	/* Seek to the output position.  */
    757  1.8  riastrad 	const uint64_t offset = be64toh(S->offset_table[blkno]);
    758  1.8  riastrad 	assert(offset <= OFF_MAX);
    759  1.8  riastrad 	if (lseek(S->cloop2_fd, offset, SEEK_SET) == -1) {
    760  1.8  riastrad 		warn("lseek output cloop2 to %"PRIx64" failed",
    761  1.8  riastrad 		    S->offset);
    762  1.8  riastrad 		return false;
    763  1.1   hubertf 	}
    764  1.8  riastrad 
    765  1.8  riastrad 	/* Start where we left off.  */
    766  1.8  riastrad 	S->blkno = blkno;
    767  1.8  riastrad 	S->offset = offset;
    768  1.8  riastrad 	S->n_checkpointed_blocks = blkno;
    769  1.8  riastrad 
    770  1.8  riastrad 	/* Good to go and ready for interruption by a signal.  */
    771  1.8  riastrad 	S->initialized = 1;
    772  1.8  riastrad 
    773  1.8  riastrad 	/* Success!  */
    774  1.8  riastrad 	return true;
    775  1.8  riastrad }
    776  1.8  riastrad 
    777  1.8  riastrad /*
    778  1.8  riastrad  * Read a single block, compress it, and write the compressed block.
    779  1.8  riastrad  * Return the size of the compressed block.
    780  1.8  riastrad  */
    781  1.8  riastrad static uint32_t
    782  1.8  riastrad compress_block(int in_fd, int out_fd, uint32_t blkno, uint32_t blocksize,
    783  1.8  riastrad     uint32_t readsize, void *uncompbuf, void *compbuf)
    784  1.8  riastrad {
    785  1.8  riastrad 
    786  1.8  riastrad 	assert(readsize <= blocksize);
    787  1.8  riastrad 	assert(blocksize <= MAX_BLOCKSIZE);
    788  1.8  riastrad 
    789  1.8  riastrad 	/* Read the uncompressed block.  */
    790  1.8  riastrad 	const ssize_t n_read = read_block(in_fd, uncompbuf, readsize);
    791  1.8  riastrad 	if (n_read == -1)
    792  1.8  riastrad 		err(1, "read block %"PRIu32, blkno);
    793  1.8  riastrad 	assert(n_read >= 0);
    794  1.8  riastrad 	assert((uintmax_t)n_read <= (uintmax_t)readsize);
    795  1.8  riastrad 	if (n_read < readsize)
    796  1.8  riastrad 		errx(1, "partial read of block %"PRIu32": %zd <= %"PRIu32,
    797  1.8  riastrad 		    blkno, n_read, readsize);
    798  1.8  riastrad 
    799  1.8  riastrad 	/* Compress the block.  */
    800  1.8  riastrad 	/* XXX compression ratio bound */
    801  1.8  riastrad 	__CTASSERT(MAX_BLOCKSIZE <= (ULONG_MAX / 2));
    802  1.8  riastrad 	const unsigned long uncomplen =
    803  1.8  riastrad 	    (VNDCOMPRESS_COMPAT? blocksize : readsize); /* XXX */
    804  1.8  riastrad 	unsigned long complen = (uncomplen * 2);
    805  1.8  riastrad 	const int zerror = compress2(compbuf, &complen, uncompbuf, uncomplen,
    806  1.8  riastrad 	    Z_BEST_COMPRESSION);
    807  1.8  riastrad 	if (zerror != Z_OK)
    808  1.8  riastrad 		errx(1, "compressed failed at block %"PRIu32" (%d): %s", blkno,
    809  1.8  riastrad 		    zerror, zError(zerror));
    810  1.8  riastrad 	assert(complen <= (uncomplen * 2));
    811  1.8  riastrad 
    812  1.8  riastrad 	/* Write the compressed block.  */
    813  1.8  riastrad 	const ssize_t n_written = write(out_fd, compbuf, complen);
    814  1.8  riastrad 	if (n_written == -1)
    815  1.8  riastrad 		err(1, "write block %"PRIu32, blkno);
    816  1.8  riastrad 	assert(n_written >= 0);
    817  1.8  riastrad 	if ((uint32_t)n_written != complen)
    818  1.8  riastrad 		errx(1, "partial write of block %"PRIu32": %zd <= %zu", blkno,
    819  1.8  riastrad 		    n_written, complen);
    820  1.8  riastrad 
    821  1.8  riastrad 	return n_written;
    822  1.1   hubertf }
    823  1.1   hubertf 
    824  1.1   hubertf /*
    825  1.8  riastrad  * Checkpoint if appropriate.
    826  1.1   hubertf  */
    827  1.8  riastrad static void
    828  1.8  riastrad compress_maybe_checkpoint(struct compress_state *S)
    829  1.1   hubertf {
    830  1.1   hubertf 
    831  1.8  riastrad 	if ((0 < S->checkpoint_blocks) && (0 < S->blkno) &&
    832  1.8  riastrad 	    ((S->blkno % S->checkpoint_blocks) == 0)) {
    833  1.8  riastrad 		assert(S->offset <= OFF_MAX);
    834  1.8  riastrad 		assert((off_t)S->offset == lseek(S->cloop2_fd, 0, SEEK_CUR));
    835  1.8  riastrad 		compress_checkpoint(S);
    836  1.1   hubertf 	}
    837  1.1   hubertf }
    838  1.1   hubertf 
    839  1.1   hubertf /*
    840  1.8  riastrad  * Write the prefix of the offset table that we have filled so far.
    841  1.8  riastrad  *
    842  1.8  riastrad  * We fsync the data blocks we have written, and then write the offset
    843  1.8  riastrad  * table, and then fsync the offset table and file metadata.  This
    844  1.8  riastrad  * should help to avoid offset tables that point at garbage data.
    845  1.8  riastrad  *
    846  1.8  riastrad  * This may be called from a signal handler, so it must not use stdio,
    847  1.8  riastrad  * malloc, &c. -- it may only (a) handle signal-safe state in S, and
    848  1.8  riastrad  * (b) do file descriptor I/O / fsync.
    849  1.8  riastrad  *
    850  1.8  riastrad  * XXX This requires further thought and heavy testing to be sure.
    851  1.8  riastrad  *
    852  1.8  riastrad  * XXX Should have an option to suppress fsync.
    853  1.8  riastrad  *
    854  1.8  riastrad  * XXX Should have an option to fail on fsync failures.
    855  1.8  riastrad  *
    856  1.8  riastrad  * XXX Would be nice if we could just do a barrier rather than an
    857  1.8  riastrad  * fsync.
    858  1.8  riastrad  *
    859  1.8  riastrad  * XXX How might we automatically test the fsyncs?
    860  1.1   hubertf  */
    861  1.7     joerg static void
    862  1.8  riastrad compress_checkpoint(struct compress_state *S)
    863  1.1   hubertf {
    864  1.8  riastrad 
    865  1.8  riastrad 	assert(S->blkno < S->n_offsets);
    866  1.8  riastrad 	const uint32_t n_offsets = (S->blkno + 1);
    867  1.8  riastrad 	assert(n_offsets <= S->n_offsets);
    868  1.8  riastrad 
    869  1.8  riastrad 	assert(S->offset <= OFF_MAX);
    870  1.8  riastrad 	assert((off_t)S->offset <= lseek(S->cloop2_fd, 0, SEEK_CUR));
    871  1.8  riastrad 
    872  1.8  riastrad 	/* Make sure the data hits the disk before we say it's ready.  */
    873  1.8  riastrad 	if (fsync_range(S->cloop2_fd, (FFILESYNC | FDISKSYNC), 0, S->offset)
    874  1.8  riastrad 	    == -1)
    875  1.8  riastrad 		warn_ss("fsync of output failed");
    876  1.8  riastrad 
    877  1.8  riastrad 	/* Say the data blocks are ready.  */
    878  1.8  riastrad 	const ssize_t n_written = pwrite(S->cloop2_fd, S->offset_table,
    879  1.8  riastrad 	    (n_offsets * sizeof(uint64_t)), CLOOP2_OFFSET_TABLE_OFFSET);
    880  1.8  riastrad 	if (n_written == -1)
    881  1.8  riastrad 		err_ss(1, "write partial offset table");
    882  1.8  riastrad 	assert(n_written >= 0);
    883  1.8  riastrad 	if ((size_t)n_written != (n_offsets * sizeof(uint64_t)))
    884  1.8  riastrad 		errx_ss(1, "partial write of partial offset table: %zd <= %zu",
    885  1.8  riastrad 		    n_written, (size_t)(n_offsets * sizeof(uint64_t)));
    886  1.8  riastrad 
    887  1.1   hubertf 	/*
    888  1.8  riastrad 	 * If this is the first checkpoint, initialize the header.
    889  1.8  riastrad 	 * Signal handler can race with main code here, but it is
    890  1.8  riastrad 	 * harmless -- just an extra fsync and write of the header,
    891  1.8  riastrad 	 * which are both idempotent.
    892  1.1   hubertf 	 */
    893  1.8  riastrad 	if (S->n_checkpointed_blocks == 0) {
    894  1.8  riastrad 		static const struct cloop2_header zero_header;
    895  1.8  riastrad 		struct cloop2_header header = zero_header;
    896  1.8  riastrad 
    897  1.8  riastrad 		/* Force the offset table to disk before we set the header.  */
    898  1.8  riastrad 		if (fsync_range(S->cloop2_fd, (FFILESYNC | FDISKSYNC),
    899  1.8  riastrad 			0,
    900  1.8  riastrad 			(CLOOP2_OFFSET_TABLE_OFFSET
    901  1.8  riastrad 			    + (n_offsets * (sizeof(uint64_t)))))
    902  1.8  riastrad 		    == -1)
    903  1.8  riastrad 			warn_ss("fsync of offset table failed");
    904  1.8  riastrad 
    905  1.8  riastrad 		/* Subsequent writes will preserve a valid state.  */
    906  1.8  riastrad 
    907  1.8  riastrad 		/* Format the header.  */
    908  1.8  riastrad 		__CTASSERT(sizeof(cloop2_magic) <= sizeof(header.cl2h_magic));
    909  1.8  riastrad 		(void)memcpy(header.cl2h_magic, cloop2_magic,
    910  1.8  riastrad 		    sizeof(cloop2_magic));
    911  1.8  riastrad 		header.cl2h_blocksize = htobe32(S->blocksize);
    912  1.8  riastrad 		header.cl2h_n_blocks = htobe32(S->n_blocks);
    913  1.8  riastrad 
    914  1.8  riastrad 		/* Write the header.  */
    915  1.8  riastrad 		const ssize_t h_written = pwrite(S->cloop2_fd, &header,
    916  1.8  riastrad 		    sizeof(header), 0);
    917  1.8  riastrad 		if (h_written == -1)
    918  1.8  riastrad 			err_ss(1, "write header");
    919  1.8  riastrad 		assert(h_written >= 0);
    920  1.8  riastrad 		if ((size_t)h_written != sizeof(header))
    921  1.8  riastrad 			errx_ss(1, "partial write of header: %zd <= %zu",
    922  1.8  riastrad 			    h_written, sizeof(header));
    923  1.8  riastrad 	}
    924  1.8  riastrad 
    925  1.8  riastrad 	/* Record how many blocks we've checkpointed.  */
    926  1.8  riastrad     {
    927  1.8  riastrad 	sigset_t old_sigmask;
    928  1.8  riastrad 	block_signals(&old_sigmask);
    929  1.8  riastrad 	S->n_checkpointed_blocks = S->blkno;
    930  1.8  riastrad 	restore_sigmask(&old_sigmask);
    931  1.8  riastrad     }
    932  1.8  riastrad }
    933  1.8  riastrad 
    934  1.8  riastrad /*
    935  1.8  riastrad  * Release everything we allocated in compress_init.
    936  1.8  riastrad  */
    937  1.8  riastrad static void
    938  1.8  riastrad compress_exit(struct compress_state *S)
    939  1.8  riastrad {
    940  1.8  riastrad 
    941  1.8  riastrad 	/* Done with the offset table.  Free it.  */
    942  1.8  riastrad 	free(S->offset_table);
    943  1.8  riastrad 
    944  1.8  riastrad 	/* Done with the files.  Close them.  */
    945  1.8  riastrad 	if (close(S->cloop2_fd) == -1)
    946  1.8  riastrad 		warn("close(cloop2 fd)");
    947  1.8  riastrad 	if (close(S->image_fd) == -1)
    948  1.8  riastrad 		warn("close(image fd)");
    949  1.1   hubertf }
    950  1.1   hubertf 
    951  1.1   hubertf /*
    952  1.8  riastrad  * Read, returning partial data only at end of file.
    953  1.1   hubertf  */
    954  1.8  riastrad static ssize_t
    955  1.8  riastrad read_block(int fd, void *buffer, size_t n)
    956  1.1   hubertf {
    957  1.8  riastrad 	char *p = buffer, *const end __unused = (p + n);
    958  1.8  riastrad 	size_t total_read = 0;
    959  1.8  riastrad 
    960  1.8  riastrad 	while (n > 0) {
    961  1.8  riastrad 		const ssize_t n_read = read(fd, p, n);
    962  1.8  riastrad 		if (n_read == -1)
    963  1.8  riastrad 			return -1;
    964  1.8  riastrad 		assert(n_read >= 0);
    965  1.8  riastrad 		if (n_read == 0)
    966  1.1   hubertf 			break;
    967  1.8  riastrad 
    968  1.8  riastrad 		assert((size_t)n_read <= n);
    969  1.8  riastrad 		n -= (size_t)n_read;
    970  1.8  riastrad 
    971  1.8  riastrad 		assert(p <= end);
    972  1.8  riastrad 		assert(n_read <= (end - p));
    973  1.8  riastrad 		p += (size_t)n_read;
    974  1.8  riastrad 
    975  1.8  riastrad 		assert((size_t)n_read <= (SIZE_MAX - total_read));
    976  1.8  riastrad 		total_read += (size_t)n_read;
    977  1.1   hubertf 	}
    978  1.1   hubertf 
    979  1.8  riastrad 	return total_read;
    980  1.8  riastrad }
    981  1.8  riastrad 
    982  1.8  riastrad /*
    983  1.8  riastrad  * Signal-safe err/warn utilities.  The errno varieties are limited to
    984  1.8  riastrad  * having no format arguments for reasons of laziness.
    985  1.8  riastrad  */
    986  1.8  riastrad 
    987  1.8  riastrad static void
    988  1.8  riastrad err_ss(int exit_value, const char *msg)
    989  1.8  riastrad {
    990  1.8  riastrad 	warn_ss(msg);
    991  1.8  riastrad 	_Exit(exit_value);
    992  1.8  riastrad }
    993  1.8  riastrad 
    994  1.8  riastrad static void
    995  1.8  riastrad errx_ss(int exit_value, const char *format, ...)
    996  1.8  riastrad {
    997  1.8  riastrad 	va_list va;
    998  1.8  riastrad 
    999  1.8  riastrad 	va_start(va, format);
   1000  1.8  riastrad 	vwarnx_ss(format, va);
   1001  1.8  riastrad 	va_end(va);
   1002  1.8  riastrad 	_Exit(exit_value);
   1003  1.8  riastrad }
   1004  1.8  riastrad 
   1005  1.8  riastrad static void
   1006  1.8  riastrad warn_ss(const char *msg)
   1007  1.8  riastrad {
   1008  1.8  riastrad 	int error = errno;
   1009  1.8  riastrad 
   1010  1.8  riastrad 	warnx_ss("%s: %s", msg, strerror(error));
   1011  1.8  riastrad 
   1012  1.8  riastrad 	errno = error;
   1013  1.8  riastrad }
   1014  1.8  riastrad 
   1015  1.8  riastrad static void
   1016  1.8  riastrad warnx_ss(const char *format, ...)
   1017  1.8  riastrad {
   1018  1.8  riastrad 	va_list va;
   1019  1.8  riastrad 
   1020  1.8  riastrad 	va_start(va, format);
   1021  1.8  riastrad 	vwarnx_ss(format, va);
   1022  1.8  riastrad 	va_end(va);
   1023  1.8  riastrad }
   1024  1.8  riastrad 
   1025  1.8  riastrad static void
   1026  1.8  riastrad vwarnx_ss(const char *format, va_list va)
   1027  1.8  riastrad {
   1028  1.8  riastrad 	char buf[128];
   1029  1.8  riastrad 
   1030  1.8  riastrad 	(void)strlcpy(buf, getprogname(), sizeof(buf));
   1031  1.8  riastrad 	(void)strlcat(buf, ": ", sizeof(buf));
   1032  1.8  riastrad 
   1033  1.8  riastrad 	const int n = vsnprintf_ss(&buf[strlen(buf)], (sizeof(buf) -
   1034  1.8  riastrad 		strlen(buf)), format, va);
   1035  1.8  riastrad 	if (n <= 0) {
   1036  1.8  riastrad 		const char fallback[] =
   1037  1.8  riastrad 		    "vndcompress: Help!  I'm trapped in a signal handler!\n";
   1038  1.8  riastrad 		(void)write(STDERR_FILENO, fallback, __arraycount(fallback));
   1039  1.8  riastrad 	} else {
   1040  1.8  riastrad 		(void)strlcat(buf, "\n", sizeof(buf));
   1041  1.8  riastrad 		(void)write(STDERR_FILENO, buf, strlen(buf));
   1042  1.1   hubertf 	}
   1043  1.1   hubertf }
   1044