/*
 * Punch out every other block in a file.
 * Copyright (C) 2016 Oracle.
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "global.h"

void usage(char *cmd)
{
	printf("Usage: %s [-o offset] [-i interval] [-s size] file\n", cmd);
	printf("Punches every other block in the file by default,\n");
	printf("or 'size' blocks every 'interval' blocks starting at\n");
	printf("'offset'.  Units are in fstatfs blocks.\n");
	exit(1);
}

int main(int argc, char *argv[])
{
	struct stat	s;
	struct statfs	sf;
	off_t		offset;
	off_t		start_offset = 0;
	int		fd;
	blksize_t	blksz;
	off_t		sz;
	int		mode;
	int		error;
	int		c;
	int		size = 1;	/* punch $SIZE blocks ... */
	int		interval = 2;	/* every $INTERVAL blocks */

	while ((c = getopt(argc, argv, "i:o:s:")) != EOF) {
		switch (c) {
		case 'i':
			interval = atoi(optarg);
			break;
		case 'o':
			errno = 0;
			start_offset = strtoull(optarg, NULL, 0);
			if (errno) {
				fprintf(stderr, "invalid offset '%s'\n", optarg);
				return 1;
			}
			break;
		case 's':
			size = atoi(optarg);
			break;
		default:
			usage(argv[0]);
		}
	}

	if (interval <= 0) {
		printf("interval must be > 0\n");
		usage(argv[0]);
	}

	if (size <= 0) {
		printf("size must be > 0\n");
		usage(argv[0]);
	}

	if (optind != argc - 1)
		usage(argv[0]);

	fd = open(argv[optind], O_WRONLY);
	if (fd < 0)
		goto err;

	error = fstat(fd, &s);
	if (error)
		goto err;

	error = fstatfs(fd, &sf);
	if (error)
		goto err;

	sz = s.st_size;
	blksz = sf.f_bsize;

	mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
	for (offset = start_offset * blksz;
	     offset < sz;
	     offset += blksz * interval) {
		error = fallocate(fd, mode, offset, blksz * size);
		if (error)
			goto err;
	}

	error = fsync(fd);
	if (error)
		goto err;

	error = close(fd);
	if (error)
		goto err;
	return 0;
err:
	perror(argv[optind]);
	return 2;
}
