/*
 *  qemubuilder: pbuilder with qemu
 *  Basic file operations code.
 *
 *  Copyright (C) 2009 Junichi Uekawa
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 *
 */

/**
   copy file contents to temporary filesystem, so that it can be used
   inside qemu.

   return -1
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <alloca.h>
#include <errno.h>
#include "file.h"
#include "log.h"

/**
 * Copy file from orig to dest.
 *
 * returns 0 on success, -1 on failure.
 */
int copy_file(const char *orig, const char *dest) {
	const int buffer_size = 4096;
	char *buf = malloc(buffer_size);
	int ret = -1;
	int fin = -1;
	int fout = -1;
	size_t count;
	struct stat st;

	if (!buf) {
		log_printf(log_error, "Out of memory");
		goto out;
	}

	if (-1 == (fin = open(orig, O_RDONLY))) {
		log_perror("file copy: open for read");
		goto out;
	}
	if (-1 == (fout = creat(dest, 0666))) {
		log_perror("file copy: open for write");
		goto out;
	}
	while ((count = read(fin, buf, buffer_size)) > 0) {
		if (-1 == write(fout, buf, count)) { /* error */
			log_perror("file copy: write");
			log_printf(log_error, "%i", fin);
			log_printf(log_error, "%i %i", fout, (int)count);
			goto out;
		}
	}
	if (count == -1) {
		log_perror("file copy: read ");
		goto out;
	}
	if (-1 == close(fin) || -1 == close(fout)) {
		log_perror("file copy: close ");
		goto out;
	}

	if (0 > stat(orig, &st)) {
		log_printf(
			log_error, "Error calling stat '%s': %s", orig, strerror(errno));
		goto out;
	}

	if (chmod(dest, st.st_mode)) {
		log_printf(log_error,
				   "Error calling chmod('%s' 0%llo): %s",
				   orig,
				   (unsigned long long)st.st_mode,
				   strerror(errno));
		goto out;
	}

	ret = 0;
out:
	free(buf);
	return ret;
}

/**
   Create sparse file of specified size.

   returns 0 on success, 1 on fail.
 */
int create_sparse_file(const char *filename, unsigned long int size) {
	int fd = creat(filename, 0660);
	if (-1 == fd) {
		log_perror("creat");
		log_printf(log_error, "Could not create %s", filename);
		return 1;
	}

	const off_t seeksize = 1 << 30; /* try with 30-bit seeks (1GB / seek) */
	assert(size > 1);
	size--;
	if (-1 == lseek(fd, 0, SEEK_SET)) {
		log_perror("initial lseek");
		return 1;
	}

	while (size > seeksize) {
		if (-1 == lseek(fd, seeksize, SEEK_CUR)) {
			log_perror("intermediate lseek");
			return 1;
		}
		size -= seeksize;
	}
	if (-1 == lseek(fd, size - 1, SEEK_CUR)) {
		log_perror("final lseek");
		return 1;
	}

	if (-1 ==
		write(fd, "", 1)) /* A string consists of \0, write 0 to end of file */
	{
		log_perror("write");
		return 1;
	}

	if (-1 == close(fd)) {
		log_perror("close");
		return 1;
	}
	return 0;
}

/* mknod with prepended pathname
   return -1 on failure.
 */
int mknod_inside_chroot(const char *chroot,
						const char *pathname,
						mode_t mode,
						dev_t dev) {
	char *p = alloca(strlen(chroot) + strlen(pathname) + 2);
	int ret;

	if (!p) {
		log_printf(log_error, "error on alloca");
		return -1;
	}

	if (-1 == sprintf(p, "%s/%s", chroot, pathname)) {
		log_printf(log_error, "error on sprintf");
		return -1;
	}

	ret = mknod(p, mode, dev);

	if (ret == -1) {
		/* output the error message for debug, but ignore it here. */
		log_perror(p);
	}

	return ret;
}
