home *** CD-ROM | disk | FTP | other *** search
- /* Written 9:18 am Jun 18, 1986 by sources-request@mirror.UUCP in mirror:mod.sources */
- /* ---------- "v06i004: vol: create volume header" ---------- */
- Submitted by: cca!harvard!clyde!infopro!bty!yost (Brian Host)
- Mod.sources: Volume 6, Issue 4
- Archive-name: vol
-
- [ There was some discussion on the net a few weeks ago about the
- abilities of cpio and tar to handle multi-volume dumps. With
- this front-end, both programs can. Synchronicity. --r$]
-
- ----- Cut here -------------------------------------------------
- #!/bin/sh
- # shar: Shell Archiver
- # Run the following text with /bin/sh to create:
- # README
- # vol.1
- # vol.c
- # voltar
- echo x - extracting README
- sed 's/^X//' << 'SHAR_EOF' > README
- X
- XThis distribution contains the first release of the `vol' command,
- Xwhich creates volume header files for multivolume `tar' archives.
- XA man page is included.
- X
- XThe following items may be of interest for porting:
- X
- X 1. It was compiled undex XENIX 3.0 originally.
- X 2. My compiler has the 8-character limit on names.
- X 3. I used upper and lowercase in names.
- X 4. It reads directories and uses stat(2) and time(2).
- X
- XHere's my copy of /etc/default/vol:
- X# tape/disk volume sizes
- XVOLSIZE=1400
- XDISKSIZE=1400
- XTAPESIZE=60000
- X
- XI'd be very interested in hearing any comments, and of course I'd
- Xappreciate receiving any modifications, bug fixes, ports, etc.
- X
- XBrian Yost {clyde,topaz}!infopro!bty!yost 14 June 1986
- SHAR_EOF
- echo x - extracting vol.1
- sed 's/^X//' << 'SHAR_EOF' > vol.1
- X.nh
- X.TH VOL 1
- X.SH NAME
- Xvol \- create volume header files for
- X.IR tar (1)
- X.SH SYNOPSIS
- X.B vol
- X[ -ptd ] [ -s
- X.B "maxblocks"
- X] pathname...
- X.SH DESCRIPTION
- XBecause multivolume
- X.IR tar (1)
- Xarchives are sequential in nature and cannot be accessed individually,
- Xa number of difficulties can arise.
- XThe most common problem is that one must search through ten floppies
- Xin order to retrieve a file from the eleventh.
- XSearching through two cartridge tapes to get at something on the third
- Xis no fun, either,
- Xespecially when it takes 30 minutes or more per tape.
- XA much more devastating problem occurs when the third of fifteen floppy
- Xdisks gets trashed or lost,
- Xrendering disks 4 through 15 effectively inaccessible.
- X.PP
- XThe use of
- X.I vol
- Xeliminates these problems by keeping the amount of data written
- Xto each volume within the capacity of the medium being used.
- X.I Vol
- Xtakes a maximum capacity value (in blocks) and a list of pathnames,
- Xand creates a sequence of volume header files of the form
- X.nf
- X.sp 1
- X Vol_1ofN, Vol_2ofN, ..., Vol_NofN
- X.sp 1
- X.fi
- XEach of these files contains
- X1) its own name,
- X2) a subset of the pathnames specified, and
- X3) a dummy file `TOTAL-NNNNN-Blocks-MM/DD/YY'.
- XThe total space in blocks required by this subset of files should
- Xbe some value NNNNN which is less than or equal to the maximum
- Xcapacity value. Here's a sample volume header file for a
- Xdiskette that has a 1400-block capacity:
- X.nf
- X.sp 1
- X $ cat Vol_1of3
- X ./Vol_1of3
- X /usr2/yost/c
- X /usr2/yost/news
- X /usr2/yost/calendar
- X TOTAL-1387-Blocks-06/02/86
- X $
- X.sp 1
- X.fi
- X.PP
- XThese volume header files may then be used in conjunction with the
- Xcommand evaluation capability of
- X.IR sh (1),
- Xas in the following examples:
- X.nf
- X.sp 1
- X $ tar cv `cat Vol_1of3`
- X $ ls -ls `cat Vol_1of3`
- X $ rm -rf `cat Vol_1of3`
- X.sp 1
- X.fi
- X.PP
- X.I Vol
- Xruns
- X.IR du (1)
- Xon each pathname supplied on the command line to determine how
- Xmuch space it requires.
- XIf any pathname is larger than the maximum capacity,
- Xand it is a directory,
- X.I vol
- Xprocesses each of the files within that directory separately,
- Xrather than the directory as a whole.
- XFor example, if the directory /usr is too big to fit on a single magtape,
- X.I vol
- Xwill split it up into tape-sized chunks.
- XIf a regular file is larger than the maximum capacity,
- X.I vol
- Xwill print an error message and skip the file.
- X.PP
- XOnce all of the pathnames on the command line have been processed
- Xin this way,
- X.I vol
- Xbegins arranging them into volume header files.
- XThe default algorithm takes great liberties in rearranging the
- Xsequence of the pathnames in order to minimize the amount of
- Xwasted space on each volume.
- XThe -p option tells
- X.I vol
- Xto preserve the original sequence of pathnames,
- Xregardless of space considerations.
- X.PP
- XThe maximum capacity value defaults to the value of `VOLSIZE' in
- Xthe file /etc/default/vol,
- Xor the environment variable `VOLSIZE'.
- XThe -t option to use `TAPESIZE' rather than `VOLSIZE';
- Xsimilarly, -d means use `TAPESIZE'.
- XCommand-line options take priority over environment variables,
- Xand variables take priority over the values in /etc/default/vol.
- X.PP
- X.I Vol
- Xwill run much faster if you do some of its work for it.
- XFor example, if you wish to split up /usr into magtape-sized
- Xvolumes, and you know it will take more than one, use the
- Xcommand
- X.nf
- X
- X $ vol -t /usr/*
- X
- Xrather than
- X
- X $ vol -t /usr
- X
- X.fi
- XThis will save
- X.I vol
- Xthe trouble of doing the initial
- X.IR du (1)
- Xon /usr, only to find out that it's too big.
- X.SH DIAGNOSTICS
- X.I Vol
- Xexits with status 1 if any errors are encountered.
- X.SH FILES
- X/etc/default/vol /tmp/vol* ./Vol_*of*
- X.SH "SEE ALSO"
- Xtar(1)
- Xdu(1)
- Xsh(1)
- X.SH BUGS
- X.I Vol
- Xneglects to consider the size of the header files in its calculations.
- X.PP
- XMultiple links to a file are treated as separate files.
- X.SH AUTHOR
- XBrian Yost (bty!yost)
- SHAR_EOF
- echo x - extracting vol.c
- sed 's/^X//' << 'SHAR_EOF' > vol.c
- X
- X/*
- X** vol.c - create volume header files for `tar'
- X**
- X** I hereby release this source code into the public domain. Anyone
- X** may use, copy, or modify this source code, as long as:
- X**
- X** 1) it is not sold for profit;
- X** 2) modifications are clearly identified with the date,
- X** author's name, and e-mail address; and
- X** 3) the entire text of this comment remains intact.
- X**
- X** I would also appreciate it if ports, modifications and/or flames
- X** were sent to me at the address below.
- X**
- X** Brian Yost {topaz,clyde}!infopro!bty!yost 14 June 1986
- X*/
- X
- X#include <stdio.h>
- X#include <string.h>
- X#include <signal.h>
- X#include <time.h>
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <sys/dir.h>
- X
- X#define OKAY 0
- X#define ERROR 1
- X#define TOLERANCE 10 /* how close to actually come to limit */
- X#define MAX_HEADER 5000L /* maximum size of header file (bytes) */
- X#define UNDER_10 "./Vol_%dof%d"
- X#define OVER_9 "./Vol_%02dof%02d"
- X
- X#define SIGNAL(S,A) if (signal((S),SIG_IGN) != SIG_IGN) signal((S),(A))
- X
- Xtypedef enum {FALSE, TRUE} boolean;
- X
- Xchar *ProgName;
- Xchar *Default = "/etc/default/vol";
- Xchar *Usage = "Usage: %s [-ptd] [-s maxblocks] pathname...\n";
- Xchar *Size = "VOLSIZE";
- X
- Xboolean Preserve = FALSE; /* ok to rearrange file sequence */
- X
- XFILE *TempFile;
- Xchar *TempName = "/tmp/volXXXXXX";
- X
- Xlong MaxBlocks = 0L;
- Xlong N_Records = 0L;
- X
- Xextern int CreateVol(); /* top level */
- Xextern void GetSize(); /* determines size of a pathname */
- Xextern void ProcessDir(); /* run GetSize() on directory contents */
- Xextern void AddList(); /* adds to the list of processed pathnames */
- Xextern char *GetDate(); /* returns today's date in MM/DD/YY format */
- Xextern int rm_temps(); /* interrupt handler */
- X
- Xmain(ac, av)
- Xint ac;
- Xchar *av[];
- X{
- X int status, option;
- X FILE *deflt;
- X extern char *optarg;
- X extern int optind;
- X extern long atol();
- X extern char *getenv(), *fgets();
- X
- X ProgName = av[0];
- X while ((option = getopt(ac, av, "tds:ph")) != EOF)
- X switch (option) {
- X case 't': /* tape */
- X Size = "TAPESIZE";
- X break;
- X case 'd': /* disk */
- X Size = "DISKSIZE";
- X break;
- X case 's': /* size */
- X MaxBlocks = atol(optarg);
- X break;
- X case 'p': /* preserve file sequence */
- X Preserve = TRUE;
- X break;
- X case 'h': /* help */
- X case '?': /* error */
- X default:
- X fprintf(stderr, Usage, ProgName);
- X exit(ERROR);
- X break;
- X }
- X
- X if (ac - optind < 1) { /* not enough args */
- X fprintf(stderr, Usage, ProgName);
- X exit(ERROR);
- X }
- X
- X /* Look for environment variable */
- X if (!MaxBlocks) {
- X char *s;
- X
- X if ((s = getenv(Size)) != NULL)
- X MaxBlocks = atol(s);
- X }
- X
- X /* Check DEFAULT file */
- X if (!MaxBlocks)
- X if ((deflt = fopen(Default, "r")) != NULL) {
- X int len = strlen(Size);
- X char buf[BUFSIZ];
- X
- X while (fgets(buf, BUFSIZ, deflt))
- X if (strncmp(buf, Size, len) == 0)
- X if (buf[len] == '=')
- X if (MaxBlocks = atol(buf+len+1))
- X break;
- X fclose(deflt);
- X }
- X
- X
- X if (!MaxBlocks) { /* just give up */
- X fprintf(stderr, "%s: you must specify block size\n", ProgName);
- X fprintf(stderr, Usage, ProgName);
- X exit(ERROR);
- X } else if (MaxBlocks < 0L) { /* no way */
- X fprintf(stderr, "%s: invalid block size %ld\n",
- X ProgName, MaxBlocks);
- X exit(ERROR);
- X }
- X printf("Volume size = %ld blocks\n", MaxBlocks);
- X
- X SIGNAL(SIGHUP, rm_temps);
- X SIGNAL(SIGINT, rm_temps);
- X SIGNAL(SIGQUIT, rm_temps);
- X SIGNAL(SIGTERM, rm_temps);
- X
- X mktemp(TempName);
- X if ((TempFile = fopen(TempName, "w+")) == NULL) {
- X fprintf(stderr, "%s: can't open %s\n", ProgName, TempName);
- X exit(ERROR);
- X } else {
- X setbuf(TempFile, (char*)NULL);
- X status = CreateVol(ac-optind, av+optind);
- X fclose(TempFile);
- X exit(status);
- X }
- X}
- X
- Xint
- XCreateVol(n, pathname)
- Xint n;
- Xchar *pathname[];
- X{
- X int i, c, volume;
- X long total, grandtotal, blocks, filepos, filesize;
- X char volfile[BUFSIZ], oldfile[BUFSIZ], name[BUFSIZ], buf[BUFSIZ];
- X FILE *vol, *old;
- X extern long ftell();
- X
- X for (i = 0; i < n; i++)
- X GetSize(pathname[i]);
- X
- X if (Preserve != TRUE) {
- X /*
- X ** Since we only make one pass through the list, sort
- X ** it by blocksize descending. This should get us as
- X ** close to MaxBlocks as possible.
- X */
- X sprintf(buf, "sort -n -r %s -o %s", TempName, TempName);
- X if (system(buf) != 0)
- X fprintf(stderr, "%s: error sorting %s\n",
- X ProgName, TempName);
- X }
- X unlink(TempName); /* (after last close) */
- X
- X /*
- X ** Move items from the list into the volume header files
- X */
- X volume = 0;
- X grandtotal = 0L;
- X while (N_Records) {
- X volume++;
- X total = 0L;
- X
- X sprintf(volfile, "%s-%d", TempName, volume);
- X if ((vol = fopen(volfile, "w")) == NULL) {
- X fprintf(stderr, "%s: fatal error -- can't open %s\n",
- X ProgName, volfile);
- X return ERROR;
- X }
- X
- X rewind(TempFile);
- X filepos = ftell(TempFile);
- X filesize = 0L;
- X while (fgets(buf, BUFSIZ, TempFile)) {
- X if (sscanf(buf, "%ld %s", &blocks, name) != 2) {
- X fprintf(stderr, "%s: error reading %s\n",
- X ProgName, TempName);
- X return ERROR;
- X }
- X
- X if (blocks >= 0L) /* hasn't been deleted */
- X if ((total + blocks) <= (MaxBlocks - TOLERANCE)
- X && (filesize + strlen(name)+1) < MAX_HEADER) {
- X total += blocks;
- X filesize += strlen(name) + 1;
- X fprintf(vol, "%s\n", name);
- X
- X /* delete from tempfile */
- X fseek(TempFile, filepos, 0);
- X fprintf(TempFile, " -1 ");
- X fgets(buf, BUFSIZ, TempFile);
- X N_Records--;
- X } else if (Preserve) /* can't rearrange files */
- X break;
- X
- X filepos = ftell(TempFile);
- X }
- X
- X fprintf(vol, "TOTAL-%ld-blocks-%s\n", total, GetDate());
- X fclose(vol);
- X printf("Volume %d: %ld blocks\n", volume, total);
- X grandtotal += total;
- X }
- X
- X /*
- X ** Now we know how many were needed altogether, so
- X ** we can create the proper volume header file names.
- X */
- X for (i = 1; i <= volume; i++) {
- X sprintf(oldfile, "%s-%d", TempName, i);
- X if (volume < 10)
- X sprintf(volfile, UNDER_10, i, volume);
- X else
- X sprintf(volfile, OVER_9, i, volume);
- X
- X if ((vol = fopen(volfile, "w")) == NULL) {
- X fprintf(stderr, "%s: fatal error -- can't open %s\n",
- X ProgName, volfile);
- X return ERROR;
- X } else if ((old = fopen(oldfile, "r")) == NULL) {
- X fprintf(stderr, "%s: fatal error -- can't open %s\n",
- X ProgName, volfile);
- X return ERROR;
- X }
- X
- X fprintf(vol, "%s\n", volfile);
- X while ((c = getc(old)) != EOF)
- X putc(c, vol);
- X
- X fclose(old);
- X fclose(vol);
- X unlink(oldfile);
- X }
- X
- X printf("Total %ld blocks in %d volume%c\n",
- X grandtotal, volume, (volume == 1? ' ' : 's'));
- X
- X return OKAY;
- X}
- X
- Xvoid
- XGetSize(pathname)
- Xchar *pathname;
- X{
- X struct stat stat_buf;
- X char du_pipe[BUFSIZ], buf[BUFSIZ];
- X FILE *du, *popen();
- X
- X if (stat(pathname, &stat_buf) != 0) {
- X fprintf(stderr, "%s: can't stat %s\n",
- X ProgName, pathname);
- X return;
- X } else if ((stat_buf.st_mode & S_IFDIR) == 0
- X && (stat_buf.st_mode & S_IFREG) == 0) {
- X fprintf(stderr, "%s: %s not a file or directory\n",
- X ProgName, pathname);
- X return;
- X }
- X
- X sprintf(du_pipe, "du -s %s", pathname);
- X if ((du = popen(du_pipe, "r")) == NULL)
- X fprintf(stderr, "%s: can't popen du\n", ProgName);
- X else {
- X if (fgets(buf, BUFSIZ, du)) {
- X pclose(du); /* before recursion */
- X if (atol(buf) > (MaxBlocks - TOLERANCE))
- X if (stat_buf.st_mode & S_IFDIR)
- X ProcessDir(pathname);
- X else
- X fprintf(stderr, "%s: %s too big\n",
- X ProgName, pathname);
- X else
- X AddList(buf);
- X } else {
- X pclose(du);
- X fprintf(stderr, "%s: du not cooperating\n",
- X ProgName);
- X }
- X }
- X
- X return;
- X}
- X
- Xvoid
- XProcessDir(pathname)
- Xchar *pathname;
- X{
- X FILE *dir;
- X char buf[BUFSIZ];
- X struct direct dir_buf;
- X
- X if ((dir = fopen(pathname, "r")) == NULL)
- X fprintf(stderr, "%s: can't read directory %s\n",
- X ProgName, pathname);
- X else {
- X while (fread((char*)&dir_buf, sizeof(dir_buf), 1, dir) == 1)
- X if (dir_buf.d_ino
- X && strcmp(dir_buf.d_name, ".") != 0
- X && strcmp(dir_buf.d_name, "..") != 0) {
- X if (pathname[strlen(pathname)-1] == '/')
- X sprintf(buf, "%s%.14s",
- X pathname, dir_buf.d_name);
- X else
- X sprintf(buf, "%s/%.14s",
- X pathname, dir_buf.d_name);
- X GetSize(buf);
- X }
- X fclose(dir);
- X }
- X return;
- X}
- X
- Xvoid
- XAddList(buf)
- Xchar *buf;
- X{
- X long blocks;
- X char pathname[BUFSIZ];
- X
- X if (sscanf(buf, "%ld %s", &blocks, pathname) != 2)
- X fprintf(stderr, "%s: unexpected data from du - \"%s\"\n",
- X ProgName, buf);
- X else if (blocks >= 0) {
- X fprintf(TempFile, "%07ld %s\n", blocks, pathname);
- X N_Records++;
- X }
- X
- X return;
- X}
- X
- Xchar *
- XGetDate()
- X{
- X long now, time();
- X struct tm *tm, *localtime();
- X static char date[9];
- X
- X if (!(*date)) {
- X now = time((long *)0);
- X tm = localtime(&now);
- X sprintf(date, "%02d/%02d/%02d",
- X tm->tm_mon+1, tm->tm_mday, tm->tm_year);
- X }
- X return date;
- X}
- X
- X
- Xint
- Xrm_temps(signum)
- Xint signum;
- X{
- X char buf[BUFSIZ];
- X
- X signal(signum, SIG_IGN);
- X sprintf(buf, "rm -f %s*", TempName);
- X system(buf);
- X
- X switch (signum) {
- X case SIGHUP:
- X fprintf(stderr, "%s: line gone\n", ProgName);
- X exit(ERROR);
- X break;
- X case SIGINT:
- X fprintf(stderr, "%s: terminated by user\n", ProgName);
- X exit(ERROR);
- X break;
- X case SIGQUIT:
- X signal(SIGQUIT, SIG_DFL);
- X kill(getpid(), SIGQUIT);
- X break;
- X case SIGTERM:
- X fprintf(stderr, "%s: killed\n", ProgName);
- X exit(ERROR);
- X break;
- X default:
- X return;
- X }
- X}
- SHAR_EOF
- echo x - extracting voltar.sh
- sed 's/^X//' << 'SHAR_EOF' > voltar.sh
- X: voltar.sh
- X#
- X# Run vol to create volume header files.
- X# Run tar with the volume header files.
- X#
- X
- XBEEP=""
- X
- XTAPECMD="tar cvbf 126 /dev/rct"
- XDISKCMD="tar cv"
- X
- XTAR_CMD=$DISKCMD
- XOPTIONS="-d"
- XPROMPT="floppy diskette"
- X
- XFILES=
- X
- Xif test $# -eq 0
- Xthen echo "Usage: voltar [ -dt ] pathnames..."; exit
- Xfi
- X
- Xfor i
- Xdo
- X case $i in
- X
- X -d) TAR_CMD=$DISKCMD
- X OPTIONS="-d"
- X PROMPT="floppy diskette"
- X ;;
- X
- X -t) TAR_CMD=$TAPECMD
- X OPTIONS="-t"
- X PROMPT="magnetic tape"
- X ;;
- X
- X *) FILES="$FILES $i"
- X ;;
- X esac
- Xdone
- X
- Xrm -f Vol_*
- Xvol $OPTIONS $FILES
- X
- Xfor i in Vol_*
- Xdo
- X echo $BEEP
- X echo -n "$i: Insert $PROMPT, then type <return>: "
- X read line
- X $TAR_CMD `cat $i`
- Xdone
- SHAR_EOF
- exit 0
- /* End of text from mirror:mod.sources */
-