root/usr/src/cmd/backup/dump/dumponline.c
/*
 * 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.
 */

#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 */
};

char    *archivefile;
char    *tape;

int     active;
int     doingactive;
int     doposition;
int     pipeout;
int     tapeout;
int     to;

struct fs       *sblock;
union u_spcl    u_spcl;

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;

static void onsigbus(int);

#endif  /* ENABLE_MMAP */

#ifdef DEBUG
extern int xflag;
#endif

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

void
allocino(void)
{
        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
freeino(void)
{
        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_t ino)
{
        last = ilist.id_next;
        while (last && last->id_inumber < ino)
                last = last->id_next;
}

char *
unrawname(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(char *devname, char *dirname)
{
        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(int fd, off_t offset, off_t bytes, int fetch)
{
        /*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
unmapfile(void)
{
        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
activepass(void)
{
        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++;
}