/*
 * dump.c --- dump the contents of an inode out to a file
 * 
 * Copyright (C) 1994 Theodore Ts'o.  This file may be redistributed
 * under the terms of the GNU Public License.
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "debugfs.h"

struct dump_block_struct {
	int		fd;
	char		*buf;
	errcode_t	errcode;
};

int dump_block(ext2_filsys fs, blk_t *blocknr, int blockcnt, void
	       *private)
{
	ssize_t nbytes;
	
	struct dump_block_struct *rec = (struct dump_block_struct *) private;
	
	if (blockcnt < 0)
		return 0;

	if (*blocknr) {
		rec->errcode = io_channel_read_blk(fs->io, *blocknr,
						   1, rec->buf);
		if (rec->errcode)
			return BLOCK_ABORT;
	} else
		memset(rec->buf, 0, fs->blocksize);

retry_write:
	nbytes = write(rec->fd, rec->buf, fs->blocksize);
	if (nbytes == -1) {
		if (errno == EINTR)
			goto retry_write;
		rec->errcode = errno;
		return BLOCK_ABORT;
	}
	if (nbytes != fs->blocksize) {
		/* XXX not quite right, but good enough */
		rec->errcode = EXT2_ET_SHORT_WRITE;
		return BLOCK_ABORT;
	}
	return 0;
}

void dump_file(char *cmdname, ino_t inode, int fd, char *outname)
{
	errcode_t retval;
	struct dump_block_struct rec;

	rec.fd = fd;
	rec.errcode = 0;
	rec.buf = malloc(fs->blocksize);

	if (rec.buf == 0) {
		com_err(cmdname, ENOMEM, "while allocating block buffer for dump_inode");
		return;
	}
	
	retval = ext2fs_block_iterate(fs, inode, 0, NULL,
				      dump_block, &rec);
	if (retval) {
		com_err(cmdname, retval, "while iterating over blocks in %s",
			outname);
		goto cleanup;
	}
	if (rec.errcode) {
		com_err(cmdname, retval, "in dump_block while dumping %s",
			outname);
		goto cleanup;
	}
	
cleanup:
	free(rec.buf);
	return;
}

void do_dump(int argc, char **argv)
{
	ino_t	inode;
	int	fd;

	if (argc != 3) {
		com_err(argv[0], 0, "Usage: dump_inode <file> <output_file>");
		return;
	}

	if (check_fs_open(argv[0]))
		return;

	inode = string_to_inode(argv[1]);
	if (!inode) 
		return;

	fd = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0666);
	if (fd < 0) {
		com_err(argv[0], errno, "while opening %s for dump_inode",
			argv[2]);
		return;
	}

	dump_file(argv[0], inode, fd, argv[2]);

	close(fd);
	return;
}

void do_cat(int argc, char **argv)
{
	ino_t	inode;

	if (argc != 2) {
		com_err(argv[0], 0, "Usage: cat <file>");
		return;
	}

	if (check_fs_open(argv[0]))
		return;

	inode = string_to_inode(argv[1]);
	if (!inode) 
		return;

	dump_file(argv[0], inode, 0, argv[2]);

	return;
}

