/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1991,1996,1998 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include "dump.h"
#include <math.h>
#include <limits.h>

/*
 * Uncomment if using mmap'ing of files for pre-fetch.
 * #define ENABLE_MMAP 1
 */

struct inodesc {
	ino_t	id_inumber;		/* inode number */
	long	id_gen;			/* generation number */
	struct inodesc *id_next;	/* next on linked list */
};

static struct inodesc	ilist;		/* list of used inodesc structs */
static struct inodesc	*last;		/* last inodesc init'd or matched */
static struct inodesc	*freeinodesc;	/* free list of inodesc structs */
static struct inodesc	**ialloc;	/* allocated chunks, for freeing */
static int		nchunks;	/* number of allocations */

#ifdef ENABLE_MMAP /* XXX part of mmap support */
/*
 * If an mmap'ed file is truncated as it is being dumped or
 * faulted in, we are delivered a SIGBUS.
 */
static jmp_buf	truncate_buf;
static void	(*savebus)();
static int	incopy;

#ifdef __STDC__
static void onsigbus(int);
#else
static void onsigbus();
#endif

#endif	/* ENABLE_MMAP */

#ifdef DEBUG
extern int xflag;
#endif

#ifdef ENABLE_MMAP /* XXX part of mmap support */
static void
onsigbus(sig)
	int	sig;
{
	if (!incopy) {
		dumpabort();
		/*NOTREACHED*/
	}
	incopy = 0;
	longjmp(truncate_buf, 1);
	/*NOTREACHED*/
}
#endif	/* ENABLE_MMAP */

void
#ifdef __STDC__
allocino(void)
#else
allocino()
#endif
{
	ino_t maxino;
	size_t nused;

	maxino = (unsigned)(sblock->fs_ipg * sblock->fs_ncg);
	if (maxino > ULONG_MAX) {
		msg(gettext("allocino: filesystem too large\n"));
		dumpabort();
		/*NOTREACHED*/
	}
	/* LINTED maxino guaranteed to fit into a size_t by above test */
	nused =  maxino - sblock->fs_cstotal.cs_nifree;
	freeinodesc = (struct inodesc *)xcalloc(nused, sizeof (*freeinodesc));
	if (freeinodesc == (struct inodesc *)0) {
		msg(gettext("%s: out of memory\n"), "allocino");
		dumpabort();
		/*NOTREACHED*/
	}
	last = &ilist;
	ialloc =
	    (struct inodesc **)xmalloc(2*sizeof (*ialloc));
	ialloc[0] = freeinodesc;
	ialloc[1] = (struct inodesc *)0;
	nchunks = 1;
}

void
#ifdef __STDC__
freeino(void)
#else
freeino()
#endif
{
	int i;

	if (ialloc == (struct inodesc **)0)
		return;
	for (i = 0; i < nchunks; i++)
		if (ialloc[i] != 0)
			free(ialloc[i]);
	free(ialloc);
	ialloc = (struct inodesc **)0;
}

void
resetino(ino)
	ino_t	ino;
{
	last = ilist.id_next;
	while (last && last->id_inumber < ino)
		last = last->id_next;
}

char *
unrawname(cp)
	char *cp;
{
	char *dp;
	extern char *getfullblkname();

	dp = getfullblkname(cp);
	if (dp == 0)
		return (0);
	if (*dp == '\0') {
		free(dp);
		return (0);
	}
	if (dp == cp)		/* caller wants to always free() dp */
		dp = strdup(cp);

	return (dp);
}

/*
 * Determine if specified device is mounted at
 * specified mount point.  Returns 1 if mounted,
 * 0 if not mounted, -1 on error.
 */
int
lf_ismounted(devname, dirname)
	char	*devname;	/* name of device (raw or block) */
	char	*dirname;	/* name of f/s mount point */
{
	struct stat64 st;
	char	*blockname;	/* name of block device */
	dev_t	dev;
	int	saverr;

	if ((blockname = unrawname(devname)) == NULL) {
		msg(gettext("Cannot obtain block name from `%s'\n"), devname);
		return (-1);
	}
	if (stat64(blockname, &st) < 0) {
		saverr = errno;
		msg(gettext("Cannot obtain status of device `%s': %s\n"),
		    blockname, strerror(saverr));
		free(blockname);
		return (-1);
	}
	free(blockname);
	dev = st.st_rdev;
	if (stat64(dirname, &st) < 0) {
		saverr = errno;
		msg(gettext("Cannot obtain status of device `%s': %s\n"),
		    dirname, strerror(saverr));
		return (-1);
	}
	if (dev == st.st_dev)
		return (1);
	return (0);
}

#ifdef ENABLE_MMAP /* XXX mapped-file support */
#define	MINMAPSIZE	1024*1024
#define	MAXMAPSIZE	1024*1024*32

static caddr_t	mapbase;	/* base of mapped data */
static caddr_t	mapend;		/* last byte of mapped data */
static size_t	mapsize;	/* amount of mapped data */
/*
 * Map a file prior to dumping and start faulting in its
 * pages.  Stop if we catch a signal indicating our turn
 * to dump has arrived.  If the file is truncated out from
 * under us, immediately return.
 * NB:  the base of the mapped data may not coincide
 * exactly to the requested offset, due to alignment
 * constraints.
 */
caddr_t
mapfile(fd, offset, bytes, fetch)
	int	fd;
	off_t	offset;		/* offset within file */
	off_t	bytes;		/* number of bytes to map */
	int	fetch;		/* start faulting in pages */
{
	/*LINTED [c used during pre-fetch faulting]*/
	volatile char c, *p;
	int stride = (int)sysconf(_SC_PAGESIZE);
	extern int caught;		/* pre-fetch until set */
	caddr_t	mapstart;		/* beginning of file's mapped data */
	off_t	mapoffset;		/* page-aligned offset */
	int	saverr;

	mapbase = mapend = (caddr_t)0;

	if (bytes == 0)
		return ((caddr_t)0);
	/*
	 * mmap the file for reading
	 */
	mapoffset = offset & ~(stride - 1);
	/* LINTED: "bytes" will always fit into a size_t */
	mapsize = bytes + (offset - mapoffset);
	if (mapsize > MAXMAPSIZE)
		mapsize = MAXMAPSIZE;
	while ((mapbase = mmap((caddr_t)0, mapsize, PROT_READ,
	    MAP_SHARED, fd, mapoffset)) == (caddr_t)-1 &&
	    errno == ENOMEM && mapsize >= MINMAPSIZE) {
		/*
		 * Due to address space limitations, we
		 * may not be able to map as much as we want.
		 */
		mapsize /= 2;	/* exponential back-off */
	}

	if (mapbase == (caddr_t)-1) {
		saverr = errno;
		msg(gettext("Cannot map file at inode `%lu' into memory: %s\n"),
			ino, strerror(saverr));
		/* XXX why not call dumpailing() here? */
		if (!query(gettext(
	    "Do you want to attempt to continue? (\"yes\" or \"no\") "))) {
			dumpabort();
			/*NOTREACHED*/
		}
		mapbase = (caddr_t)0;
		return ((caddr_t)0);
	}

	(void) madvise(mapbase, mapsize, MADV_SEQUENTIAL);
	mapstart = mapbase + (offset - mapoffset);
	mapend = mapbase + (mapsize - 1);

	if (!fetch)
		return (mapstart);

	if (setjmp(truncate_buf) == 0) {
		savebus = signal(SIGBUS, onsigbus);
		/*
		 * Touch each page to pre-fetch by faulting.  At least
		 * one of c or *p must be declared volatile, lest the
		 * optimizer eliminate the assignment in the loop.
		 */
		incopy = 1;
		for (p = mapbase; !caught && p <= mapend; p += stride) {
			/* LINTED: c is used for its side-effects */
			c = *p;
		}
		incopy = 0;
	}
#ifdef DEBUG
	else
		/* XGETTEXT:  #ifdef DEBUG only */
		msg(gettext(
			"FILE TRUNCATED (fault): Interrupting pre-fetch\n"));
#endif
	(void) signal(SIGBUS, savebus);
	return (mapstart);
}

void
#ifdef __STDC__
unmapfile(void)
#else
unmapfile()
#endif
{
	if (mapbase) {
		/* XXX we're unmapping it, so what does this gain us? */
		(void) msync(mapbase, mapsize, MS_ASYNC|MS_INVALIDATE);
		(void) munmap(mapbase, mapsize);
		mapbase = (caddr_t)0;
	}
}
#endif	/* ENABLE_MMAP */

void
#ifdef __STDC__
activepass(void)
#else
activepass()
#endif
{
	static int passno = 1;			/* active file pass number */
	char *ext, *old;
	char buf[3000];
	static char defext[] = ".retry";

	if (pipeout) {
		msg(gettext("Cannot re-dump active files to `%s'\n"), tape);
		dumpabort();
		/*NOTREACHED*/
	}

	if (active > 1)
		(void) snprintf(buf, sizeof (buf), gettext(
		    "%d files were active and will be re-dumped\n"), active);
	else
		(void) snprintf(buf, sizeof (buf), gettext(
		    "1 file was active and will be re-dumped\n"));
	msg(buf);

	doingactive++;
	active = 0;
	reset();			/* reset tape params */
	spcl.c_ddate = spcl.c_date;	/* chain with last dump/pass */

	/*
	 * If archiving, create a new
	 * archive file.
	 */
	if (archivefile) {
		old = archivefile;

		ext = strstr(old, defext);
		if (ext != (char *)NULL)
			*ext = '\0'; /* just want the base name */

		/* The two is for the trailing \0 and rounding up log10() */
		archivefile = xmalloc(strlen(old) + strlen(defext) +
		    (int)log10((double)passno) + 2);

		/* Always fits */
		(void) sprintf(archivefile, "%s%s%d", old, defext, passno);
		free(old);
	}

	if (tapeout) {
		if (isrewind(to)) {
			/*
			 * A "rewind" tape device.  When we do
			 * the close, we will lose our position.
			 * Be nice and switch volumes.
			 */
			(void) snprintf(buf, sizeof (buf), gettext(
		"Warning - cannot dump active files to rewind device `%s'\n"),
				tape);
			msg(buf);
			close_rewind();
			changevol();
		} else {
			trewind();
			doposition = 0;
			filenum++;
		}
	} else {
		/*
		 * Not a tape.  Do a volume switch.
		 * This will advance to the next file
		 * if using a sequence of files, next
		 * diskette if using diskettes, or
		 * let the user move the old file out
		 * of the way.
		 */
		close_rewind();
		changevol();	/* switch files */
	}
	(void) snprintf(buf, sizeof (buf), gettext(
	    "Dumping active files (retry pass %d) to `%s'\n"), passno, tape);
	msg(buf);
	passno++;
}