浏览代码

*** empty log message ***

François Pinard 30 年之前
父节点
当前提交
32841f71ca
共有 2 个文件被更改,包括 1361 次插入8 次删除
  1. 1353 0
      src/buffer.c
  2. 8 8
      src/extract.c

+ 1353 - 0
src/buffer.c

@@ -0,0 +1,1353 @@
+/* Buffer management for tar.
+   Copyright (C) 1988 Free Software Foundation
+
+This file is part of GNU Tar.
+
+GNU Tar 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, or (at your option)
+any later version.
+
+GNU Tar 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 GNU Tar; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/*
+ * Buffer management for tar.
+ *
+ * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+#include <sys/types.h>		/* For non-Berkeley systems */
+#include <signal.h>
+#include <time.h>
+time_t time();
+
+#ifndef NO_MTIO
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#endif
+
+#ifdef BSD42
+#include <sys/file.h>
+#else
+#ifndef V7
+#include <fcntl.h>
+#endif
+#endif
+
+#ifdef	__MSDOS__
+#include <process.h>
+#endif
+
+#ifdef XENIX
+#include <sys/inode.h>
+#endif
+
+#include "tar.h"
+#include "port.h"
+#include "rmt.h"
+#include "regex.h"
+
+/* Either stdout or stderr:  The thing we write messages (standard msgs, not
+   errors) to.  Stdout unless we're writing a pipe, in which case stderr */
+FILE *msg_file = stdout;
+
+#define	STDIN	0		/* Standard input  file descriptor */
+#define	STDOUT	1		/* Standard output file descriptor */
+
+#define	PREAD	0		/* Read  file descriptor from pipe() */
+#define	PWRITE	1		/* Write file descriptor from pipe() */
+
+#define	MAGIC_STAT	105	/* Magic status returned by child, if
+				   it can't exec.  We hope compress/sh
+				   never return this status! */
+
+char *valloc();
+
+void writeerror();
+void readerror();
+
+void ck_pipe();
+void ck_close();
+
+int backspace_output();
+extern void finish_header();
+void flush_archive();
+int isfile();
+int new_volume();
+void verify_volume();
+extern void to_oct();
+
+#ifndef __MSDOS__
+/* Obnoxious test to see if dimwit is trying to dump the archive */
+dev_t ar_dev;
+ino_t ar_ino;
+#endif
+
+/*
+ * The record pointed to by save_rec should not be overlaid
+ * when reading in a new tape block.  Copy it to record_save_area first, and
+ * change the pointer in *save_rec to point to record_save_area.
+ * Saved_recno records the record number at the time of the save.
+ * This is used by annofile() to print the record number of a file's
+ * header record.
+ */
+static union record **save_rec;
+ union record record_save_area;
+static long	    saved_recno;
+
+/*
+ * PID of child program, if f_compress or remote archive access.
+ */
+static int	childpid = 0;
+
+/*
+ * Record number of the start of this block of records
+ */
+long	baserec;
+
+/*
+ * Error recovery stuff
+ */
+static int	r_error_count;
+
+/*
+ * Have we hit EOF yet?
+ */
+static int	eof;
+
+/* JF we're reading, but we just read the last record and its time to update */
+extern time_to_start_writing;
+int file_to_switch_to= -1;	/* If remote update, close archive, and use
+				   this descriptor to write to */
+
+static int volno = 1;		/* JF which volume of a multi-volume tape
+				   we're on */
+
+char *save_name = 0;		/* Name of the file we are currently writing */
+long save_totsize;		/* total size of file we are writing.  Only
+				   valid if save_name is non_zero */
+long save_sizeleft;		/* Where we are in the file we are writing.
+				   Only valid if save_name is non-zero */
+
+int write_archive_to_stdout;
+
+/* Used by fl_read and fl_write to store the real info about saved names */
+static char real_s_name[NAMSIZ];
+static long real_s_totsize;
+static long real_s_sizeleft;
+
+/* Reset the EOF flag (if set), and re-set ar_record, etc */
+
+void
+reset_eof()
+{
+	if(eof) {
+		eof=0;
+		ar_record=ar_block;
+		ar_last=ar_block+blocking;
+		ar_reading=0;
+	}
+}
+
+/*
+ * Return the location of the next available input or output record.
+ * Return NULL for EOF.  Once we have returned NULL, we just keep returning
+ * it, to avoid accidentally going on to the next file on the "tape".
+ */
+union record *
+findrec()
+{
+	if (ar_record == ar_last) {
+		if (eof)
+			return (union record *)NULL;	/* EOF */
+		flush_archive();
+		if (ar_record == ar_last) {
+			eof++;
+			return (union record *)NULL;	/* EOF */
+		}
+	}
+	return ar_record;
+}
+
+
+/*
+ * Indicate that we have used all records up thru the argument.
+ * (should the arg have an off-by-1? XXX FIXME)
+ */
+void
+userec(rec)
+	union record *rec;
+{
+	while(rec >= ar_record)
+		ar_record++;
+	/*
+	 * Do NOT flush the archive here.  If we do, the same
+	 * argument to userec() could mean the next record (if the
+	 * input block is exactly one record long), which is not what
+	 * is intended.
+	 */
+	if (ar_record > ar_last)
+		abort();
+}
+
+
+/*
+ * Return a pointer to the end of the current records buffer.
+ * All the space between findrec() and endofrecs() is available
+ * for filling with data, or taking data from.
+ */
+union record *
+endofrecs()
+{
+	return ar_last;
+}
+
+
+/*
+ * Duplicate a file descriptor into a certain slot.
+ * Equivalent to BSD "dup2" with error reporting.
+ */
+void
+dupto(from, to, msg)
+	int from, to;
+	char *msg;
+{
+	int err;
+
+	if (from != to) {
+		err=close(to);
+		if(err<0 && errno!=EBADF) {
+			msg_perror("Cannot close descriptor %d",to);
+			exit(EX_SYSTEM);
+		}
+		err = dup(from);
+		if (err != to) {
+			msg_perror("cannot dup %s",msg);
+			exit(EX_SYSTEM);
+		}
+		ck_close(from);
+	}
+}
+
+#ifdef __MSDOS__
+void
+child_open()
+{
+	fprintf(stderr,"MS-DOS %s can't use compressed or remote archives\n",tar);
+	exit(EX_ARGSBAD);
+}
+#else
+void
+child_open()
+{
+	int pipe[2];
+	int err = 0;
+
+	int kidpipe[2];
+	int kidchildpid;
+
+#define READ	0
+#define WRITE	1
+
+	ck_pipe(pipe);
+
+	childpid=fork();
+	if(childpid<0) {
+		msg_perror("cannot fork");
+		exit(EX_SYSTEM);
+	}
+	if(childpid>0) {
+		/* We're the parent.  Clean up and be happy */
+		/* This, at least, is easy */
+
+		if(ar_reading) {
+			f_reblock++;
+			archive=pipe[READ];
+			ck_close(pipe[WRITE]);
+		} else {
+			archive = pipe[WRITE];
+			ck_close(pipe[READ]);
+		}
+		return;
+	}
+
+	/* We're the kid */
+	if(ar_reading) {
+		dupto(pipe[WRITE],STDOUT,"(child) pipe to stdout");
+		ck_close(pipe[READ]);
+	} else {
+		dupto(pipe[READ],STDIN,"(child) pipe to stdin");
+		ck_close(pipe[WRITE]);
+	}
+
+	/* We need a child tar only if
+	   1: we're reading/writing stdin/out (to force reblocking)
+	   2: the file is to be accessed by rmt (compress doesn't know how)
+	   3: the file is not a plain file */
+#ifdef NO_REMOTE
+	if(!(ar_file[0]=='-' && ar_file[1]=='\0') && isfile(ar_file))
+#else
+	if(!(ar_file[0]=='-' && ar_file[1]=='\0') && !_remdev(ar_file) && isfile(ar_file))
+#endif
+	{
+		/* We don't need a child tar.  Open the archive */
+		if(ar_reading) {
+			archive=open(ar_file, O_RDONLY|O_BINARY, 0666);
+			if(archive<0) {
+				msg_perror("can't open archive %s",ar_file);
+				exit(EX_BADARCH);
+			}
+			dupto(archive,STDIN,"archive to stdin");
+			/* close(archive); */
+		} else {
+			archive=creat(ar_file,0666);
+			if(archive<0) {
+				msg_perror("can't open archive %s",ar_file);
+				exit(EX_BADARCH);
+			}
+			dupto(archive,STDOUT,"archive to stdout");
+			/* close(archive); */
+		}
+	} else {
+		/* We need a child tar */
+		ck_pipe(kidpipe);
+
+		kidchildpid=fork();
+		if(kidchildpid<0) {
+			msg_perror("child can't fork");
+			exit(EX_SYSTEM);
+		}
+
+		if(kidchildpid>0) {
+			/* About to exec compress:  set up the files */
+			if(ar_reading) {
+				dupto(kidpipe[READ],STDIN,"((child)) pipe to stdin");
+				ck_close(kidpipe[WRITE]);
+				/* dup2(pipe[WRITE],STDOUT); */
+			} else {
+				/* dup2(pipe[READ],STDIN); */
+				dupto(kidpipe[WRITE],STDOUT,"((child)) pipe to stdout");
+				ck_close(kidpipe[READ]);
+			}
+			/* ck_close(pipe[READ]); */
+			/* ck_close(pipe[WRITE]); */
+			/* ck_close(kidpipe[READ]);
+			ck_close(kidpipe[WRITE]); */
+		} else {
+		/* Grandchild.  Do the right thing, namely sit here and
+		   read/write the archive, and feed stuff back to compress */
+			tar="tar (child)";
+			if(ar_reading) {
+				dupto(kidpipe[WRITE],STDOUT,"[child] pipe to stdout");
+				ck_close(kidpipe[READ]);
+			} else {
+				dupto(kidpipe[READ],STDIN,"[child] pipe to stdin");
+				ck_close(kidpipe[WRITE]);
+			}
+
+			if (ar_file[0] == '-' && ar_file[1] == '\0') {
+				if (ar_reading)
+					archive = STDIN;
+				else
+					archive = STDOUT;
+			} else /* This can't happen if (ar_reading==2)
+				archive = rmtopen(ar_file, O_RDWR|O_CREAT|O_BINARY, 0666);
+			else */if(ar_reading)
+				archive = rmtopen(ar_file, O_RDONLY|O_BINARY, 0666);
+			else
+				archive = rmtcreat(ar_file, 0666);
+
+			if (archive < 0) {
+				msg_perror("can't open archive %s",ar_file);
+				exit(EX_BADARCH);
+			}
+
+			if(ar_reading) {
+				for(;;) {
+					char *ptr;
+					int max,count;
+		
+					r_error_count = 0;
+				error_loop:
+					err=rmtread(archive, ar_block->charptr,(int)(blocksize));
+					if(err<0) {
+						readerror();
+						goto error_loop;
+					}
+					if(err==0)
+						break;
+					ptr = ar_block->charptr;
+					max = err;
+					while(max) {
+						count = (max<RECORDSIZE) ? max : RECORDSIZE;
+						err=write(STDOUT,ptr,count);
+						if(err!=count) {
+							if(err<0) {
+								msg_perror("can't write to compress");
+								exit(EX_SYSTEM);
+							} else
+								msg("write to compress short %d bytes",count-err);
+							count = (err<0) ? 0 : err;
+						}
+						ptr+=count;
+						max-=count;
+					}
+				}
+			} else {
+				for(;;) {
+					int n;
+					char *ptr;
+		
+					n=blocksize;
+					ptr = ar_block->charptr;
+					while(n) {
+						err=read(STDIN,ptr,(n<RECORDSIZE) ? n : RECORDSIZE);
+						if(err<=0)
+							break;
+						n-=err;
+						ptr+=err;
+					}
+						/* EOF */
+					if(err==0) {
+						if(f_compress<2)
+							blocksize-=n;
+						else
+							bzero(ar_block->charptr+blocksize-n,n);
+						err=rmtwrite(archive,ar_block->charptr,blocksize);
+						if(err!=(blocksize))
+							writeerror(err);
+						if(f_compress<2)
+							blocksize+=n;
+						break;
+					}
+					if(n) {
+						msg_perror("can't read from compress");
+						exit(EX_SYSTEM);
+					}
+					err=rmtwrite(archive, ar_block->charptr, (int)blocksize);
+					if(err!=blocksize)
+						writeerror(err);
+				}
+			}
+		
+			/* close_archive(); */
+			exit(0);
+		}
+	}
+		/* So we should exec compress (-d) */
+	if(ar_reading)
+		execlp("compress", "compress", "-d", (char *)0);
+	else
+		execlp("compress", "compress", (char *)0);
+	msg_perror("can't exec compress");
+	_exit(EX_SYSTEM);
+}
+
+
+/* return non-zero if p is the name of a directory */
+int
+isfile(p)
+char *p;
+{
+	struct stat stbuf;
+
+	if(stat(p,&stbuf)<0)
+		return 1;
+	if(S_ISREG(stbuf.st_mode))
+		return 1;
+	return 0;
+}
+
+#endif
+
+/*
+ * Open an archive file.  The argument specifies whether we are
+ * reading or writing.
+ */
+/* JF if the arg is 2, open for reading and writing. */
+void
+open_archive(reading)
+	int reading;
+{
+	msg_file = f_exstdout ? stderr : stdout;
+
+	if (blocksize == 0) {
+		msg("invalid value for blocksize");
+		exit(EX_ARGSBAD);
+	}
+
+	if(ar_file==0) {
+		msg("No archive name given, what should I do?");
+		exit(EX_BADARCH);
+	}
+
+	/*NOSTRICT*/
+	if(f_multivol) {
+		ar_block = (union record *) valloc((unsigned)(blocksize+(2*RECORDSIZE)));
+		if(ar_block)
+			ar_block += 2;
+	} else
+		ar_block = (union record *) valloc((unsigned)blocksize);
+	if (!ar_block) {
+		msg("could not allocate memory for blocking factor %d",
+			blocking);
+		exit(EX_ARGSBAD);
+	}
+
+	ar_record = ar_block;
+	ar_last   = ar_block + blocking;
+	ar_reading = reading;
+
+	if (f_compress) {
+		if(reading==2 || f_verify) {
+			msg("cannot update or verify compressed archives");
+			exit(EX_ARGSBAD);
+		}
+		child_open();
+		if(!reading && ar_file[0]=='-' && ar_file[1]=='\0')
+			msg_file = stderr;
+		/* child_open(rem_host, rem_file); */
+	} else if (ar_file[0] == '-' && ar_file[1] == '\0') {
+		f_reblock++;	/* Could be a pipe, be safe */
+		if(f_verify) {
+			msg("can't verify stdin/stdout archive");
+			exit(EX_ARGSBAD);
+		}
+		if(reading==2) {
+			archive=STDIN;
+			msg_file=stderr;
+			write_archive_to_stdout++;
+		} else if (reading)
+			archive = STDIN;
+		else {
+			archive = STDOUT;
+			msg_file = stderr;
+		}
+	} else if (reading==2 || f_verify) {
+		archive = rmtopen(ar_file, O_RDWR|O_CREAT|O_BINARY, 0666);
+	} else if(reading) {
+		archive = rmtopen(ar_file, O_RDONLY|O_BINARY, 0666);
+	} else {
+		archive = rmtcreat(ar_file, 0666);
+	}
+	if (archive < 0) {
+		msg_perror("can't open %s",ar_file);
+		exit(EX_BADARCH);
+	}
+
+#ifndef __MSDOS__
+	if(!_isrmt(archive)) {
+		struct stat tmp_stat;
+
+		fstat(archive,&tmp_stat);
+		if(S_ISREG(tmp_stat.st_mode)) {
+			ar_dev=tmp_stat.st_dev;
+			ar_ino=tmp_stat.st_ino;
+		}
+	}
+#endif
+
+#ifdef	__MSDOS__
+	setmode(archive, O_BINARY);
+#endif
+
+	if (reading) {
+		ar_last = ar_block;		/* Set up for 1st block = # 0 */
+		(void) findrec();		/* Read it in, check for EOF */
+
+		if(f_volhdr) {
+			union record *head;
+#if 0
+			char *ptr;
+
+			if(f_multivol) {
+				ptr=malloc(strlen(f_volhdr)+20);
+				sprintf(ptr,"%s Volume %d",f_volhdr,1);
+			} else
+				ptr=f_volhdr;
+#endif
+			head=findrec();
+			if(!head) {
+				msg("Archive not labelled to match %s",f_volhdr);
+				exit(EX_BADVOL);
+			}
+			if (re_match (label_pattern, head->header.name,
+				      strlen (head->header.name), 0, 0) < 0) {
+				msg ("Volume mismatch!  %s!=%s", f_volhdr,
+				     head->header.name);
+				exit (EX_BADVOL);
+			}
+#if 0			
+			if(strcmp(ptr,head->header.name)) {
+				msg("Volume mismatch!  %s!=%s",ptr,head->header.name);
+				exit(EX_BADVOL);
+			}
+			if(ptr!=f_volhdr)
+				free(ptr);
+#endif
+		}
+	} else if(f_volhdr) {
+		bzero((void *)ar_block,RECORDSIZE);
+		if(f_multivol)
+			sprintf(ar_block->header.name,"%s Volume 1",f_volhdr);
+		else
+			strcpy(ar_block->header.name,f_volhdr);
+		ar_block->header.linkflag = LF_VOLHDR;
+		to_oct(time(0),	1+12, ar_block->header.mtime);
+		finish_header(ar_block);
+		/* ar_record++; */
+	}
+}
+
+
+/*
+ * Remember a union record * as pointing to something that we
+ * need to keep when reading onward in the file.  Only one such
+ * thing can be remembered at once, and it only works when reading
+ * an archive.
+ *
+ * We calculate "offset" then add it because some compilers end up
+ * adding (baserec+ar_record), doing a 9-bit shift of baserec, then
+ * subtracting ar_block from that, shifting it back, losing the top 9 bits.
+ */
+void
+saverec(pointer)
+	union record **pointer;
+{
+	long offset;
+
+	save_rec = pointer;
+	offset = ar_record - ar_block;
+	saved_recno = baserec + offset;
+}
+
+/*
+ * Perform a write to flush the buffer.
+ */
+
+/*send_buffer_to_file();
+  if(new_volume) {
+  	deal_with_new_volume_stuff();
+	send_buffer_to_file();
+  }
+ */
+
+void
+fl_write()
+{
+	int err;
+	int copy_back;
+	static long bytes_written = 0;
+
+	if(tape_length && bytes_written >= tape_length * 1024) {
+		errno = ENOSPC;
+		err = 0;
+	} else
+		err = rmtwrite(archive, ar_block->charptr,(int) blocksize);
+	if(err!=blocksize && !f_multivol)
+		writeerror(err);
+	else if (f_totals)
+	        tot_written += blocksize;
+
+	if(err>0)
+		bytes_written+=err;
+	if (err == blocksize) {
+		if(f_multivol) {
+			if(!save_name) {
+				real_s_name[0]='\0';
+				real_s_totsize=0;
+				real_s_sizeleft = 0;
+				return;
+			}
+#ifdef __MSDOS__
+			if(save_name[1]==':')
+				save_name+=2;
+#endif
+			while(*save_name=='/')
+				save_name++;
+
+			strcpy(real_s_name,save_name);
+			real_s_totsize = save_totsize;
+			real_s_sizeleft = save_sizeleft;
+		}
+		return;
+	}
+
+	/* We're multivol  Panic if we didn't get the right kind of response */
+	/* ENXIO is for the UNIX PC */
+	if(err>0 || (err<0 && errno!=ENOSPC && errno!=EIO && errno!=ENXIO))
+		writeerror(err);
+
+	if(new_volume(0)<0)
+		return;
+	bytes_written=0;
+	if(f_volhdr && real_s_name[0]) {
+		copy_back=2;
+		ar_block-=2;
+	} else if(f_volhdr || real_s_name[0]) {
+		copy_back = 1;
+		ar_block--;
+	} else
+		copy_back = 0;
+	if(f_volhdr) {
+		bzero((void *)ar_block,RECORDSIZE);
+		sprintf(ar_block->header.name,"%s Volume %d",f_volhdr,volno);
+		to_oct(time(0),	1+12, ar_block->header.mtime);
+		ar_block->header.linkflag = LF_VOLHDR;
+		finish_header(ar_block);
+	}
+	if(real_s_name[0]) {
+		int tmp;
+
+		if(f_volhdr)
+			ar_block++;
+		bzero((void *)ar_block,RECORDSIZE);
+		strcpy(ar_block->header.name,real_s_name);
+		ar_block->header.linkflag = LF_MULTIVOL;
+		to_oct((long)real_s_sizeleft,1+12,
+		       ar_block->header.size);
+		to_oct((long)real_s_totsize-real_s_sizeleft,
+		       1+12,ar_block->header.offset);
+		tmp=f_verbose;
+		f_verbose=0;
+		finish_header(ar_block);
+		f_verbose=tmp;
+		if(f_volhdr)
+			ar_block--;
+	}
+
+	err = rmtwrite(archive, ar_block->charptr,(int) blocksize);
+	if(err!=blocksize)
+		writeerror(err);
+	else if (f_totals)
+	        tot_written += blocksize;
+	        
+
+	bytes_written = blocksize;
+	if(copy_back) {
+		ar_block+=copy_back;
+		bcopy((void *)(ar_block+blocking-copy_back),
+		      (void *)ar_record,
+		      copy_back*RECORDSIZE);
+		ar_record+=copy_back;
+
+		if(real_s_sizeleft>=copy_back*RECORDSIZE)
+			real_s_sizeleft-=copy_back*RECORDSIZE;
+		else if((real_s_sizeleft+RECORDSIZE-1)/RECORDSIZE<=copy_back)
+			real_s_name[0] = '\0';
+		else {
+#ifdef __MSDOS__
+			if(save_name[1]==':')
+				save_name+=2;
+#endif
+			while(*save_name=='/')
+				save_name++;
+
+			strcpy(real_s_name,save_name);
+			real_s_sizeleft = save_sizeleft;
+			real_s_totsize=save_totsize;
+		}
+		copy_back = 0;
+	}
+}
+
+/* Handle write errors on the archive.  Write errors are always fatal */
+/* Hitting the end of a volume does not cause a write error unless the write
+*  was the first block of the volume */
+
+void
+writeerror(err)
+int err;
+{
+	if (err < 0) {
+		msg_perror("can't write to %s",ar_file);
+		exit(EX_BADARCH);
+	} else {
+		msg("only wrote %u of %u bytes to %s",err,blocksize,ar_file);
+		exit(EX_BADARCH);
+	}
+}
+
+/*
+ * Handle read errors on the archive.
+ *
+ * If the read should be retried, readerror() returns to the caller.
+ */
+void
+readerror()
+{
+#	define	READ_ERROR_MAX	10
+
+	read_error_flag++;		/* Tell callers */
+
+	msg_perror("read error on %s",ar_file);
+
+	if (baserec == 0) {
+		/* First block of tape.  Probably stupidity error */
+		exit(EX_BADARCH);
+	}
+
+	/*
+	 * Read error in mid archive.  We retry up to READ_ERROR_MAX times
+	 * and then give up on reading the archive.  We set read_error_flag
+	 * for our callers, so they can cope if they want.
+	 */
+	if (r_error_count++ > READ_ERROR_MAX) {
+		msg("Too many errors, quitting.");
+		exit(EX_BADARCH);
+	}
+	return;
+}
+
+
+/*
+ * Perform a read to flush the buffer.
+ */
+void
+fl_read()
+{
+	int err;		/* Result from system call */
+	int left;		/* Bytes left */
+	char *more;		/* Pointer to next byte to read */
+
+	/*
+	 * Clear the count of errors.  This only applies to a single
+	 * call to fl_read.  We leave read_error_flag alone; it is
+	 * only turned off by higher level software.
+	 */
+	r_error_count = 0;	/* Clear error count */
+
+	/*
+	 * If we are about to wipe out a record that
+	 * somebody needs to keep, copy it out to a holding
+	 * area and adjust somebody's pointer to it.
+	 */
+	if (save_rec &&
+	    *save_rec >= ar_record &&
+	    *save_rec < ar_last) {
+		record_save_area = **save_rec;
+		*save_rec = &record_save_area;
+	}
+	if(write_archive_to_stdout && baserec!=0) {
+		err=rmtwrite(1, ar_block->charptr, blocksize);
+		if(err!=blocksize)
+			writeerror(err);
+	}
+	if(f_multivol) {
+		if(save_name) {
+			if(save_name!=real_s_name) {
+#ifdef __MSDOS__
+				if(save_name[1]==':')
+					save_name+=2;
+#endif
+				while(*save_name=='/')
+					save_name++;
+
+				strcpy(real_s_name,save_name);
+				save_name=real_s_name;
+			}
+			real_s_totsize = save_totsize;
+			real_s_sizeleft = save_sizeleft;
+				
+		} else {
+			real_s_name[0]='\0';
+			real_s_totsize=0;
+			real_s_sizeleft = 0;
+		}
+	}
+
+error_loop:
+	err = rmtread(archive, ar_block->charptr, (int)blocksize);
+	if (err == blocksize)
+		return;
+
+	if((err == 0 || (err<0 && errno==ENOSPC)) && f_multivol) {
+		union record *head;
+
+	try_volume:
+		if(new_volume((cmd_mode==CMD_APPEND || cmd_mode==CMD_CAT || cmd_mode==CMD_UPDATE) ? 2 : 1)<0)
+			return;
+	vol_error:
+		err = rmtread(archive, ar_block->charptr,(int) blocksize);
+		if(err < 0) {
+			readerror();
+			goto vol_error;
+		}
+		if(err!=blocksize)
+			goto short_read;
+
+		head=ar_block;
+
+		if(head->header.linkflag==LF_VOLHDR) {
+			if(f_volhdr) {
+#if 0
+				char *ptr;
+
+				ptr=(char *)malloc(strlen(f_volhdr)+20);
+				sprintf(ptr,"%s Volume %d",f_volhdr,volno);
+#endif
+				if (re_match (label_pattern, head->header.name,
+					      strlen (head->header.name),
+					      0, 0) < 0) {
+					msg("Volume mismatch! %s!=%s",f_volhdr,
+					    head->header.name);
+					--volno;
+					goto try_volume;
+				      }
+				    
+#if 0
+ 				if(strcmp(ptr,head->header.name)) {
+					msg("Volume mismatch! %s!=%s",ptr,head->header.name);
+					--volno;
+					free(ptr);
+					goto try_volume;
+				}
+				free(ptr);
+#endif
+			}
+			if(f_verbose)
+				fprintf(msg_file,"Reading %s\n",head->header.name);
+			head++;
+		} else if(f_volhdr) {
+			msg("Warning:  No volume header!");
+		}
+
+		if(real_s_name[0]) {
+			long from_oct();
+
+			if(head->header.linkflag!=LF_MULTIVOL || strcmp(head->header.name,real_s_name)) {
+				msg("%s is not continued on this volume!",real_s_name);
+				--volno;
+				goto try_volume;
+			}
+			if(real_s_totsize!=from_oct(1+12,head->header.size)+from_oct(1+12,head->header.offset)) {
+				msg("%s is the wrong size (%ld!=%ld+%ld)",
+				       head->header.name,save_totsize,
+				       from_oct(1+12,head->header.size),
+				       from_oct(1+12,head->header.offset));
+				--volno;
+				goto try_volume;
+			}
+			if(real_s_totsize-real_s_sizeleft!=from_oct(1+12,head->header.offset)) {
+				msg("This volume is out of sequence");
+				--volno;
+				goto try_volume;
+			}
+			head++;
+		}
+		ar_record=head;
+		return;
+	} else if (err < 0) {
+		readerror();
+		goto error_loop;	/* Try again */
+	}
+
+ short_read:
+	more = ar_block->charptr + err;
+	left = blocksize - err;
+
+again:
+	if (0 == (((unsigned)left) % RECORDSIZE)) {
+		/* FIXME, for size=0, multi vol support */
+		/* On the first block, warn about the problem */
+		if (!f_reblock && baserec == 0 && f_verbose && err > 0) {
+		/*	msg("Blocksize = %d record%s",
+				err / RECORDSIZE, (err > RECORDSIZE)? "s": "");*/
+			msg("Blocksize = %d records", err / RECORDSIZE);
+		}
+		ar_last = ar_block + ((unsigned)(blocksize - left))/RECORDSIZE;
+		return;
+	}
+	if (f_reblock) {
+		/*
+		 * User warned us about this.  Fix up.
+		 */
+		if (left > 0) {
+error2loop:
+			err = rmtread(archive, more, (int)left);
+			if (err < 0) {
+				readerror();
+				goto error2loop;	/* Try again */
+			}
+			if (err == 0) {
+				msg("archive %s EOF not on block boundary",ar_file);
+				exit(EX_BADARCH);
+			}
+			left -= err;
+			more += err;
+			goto again;
+		}
+	} else {
+		msg("only read %d bytes from archive %s",err,ar_file);
+		exit(EX_BADARCH);
+	}
+}
+
+
+/*
+ * Flush the current buffer to/from the archive.
+ */
+void
+flush_archive()
+{
+	int c;
+
+	baserec += ar_last - ar_block;	/* Keep track of block #s */
+	ar_record = ar_block;		/* Restore pointer to start */
+	ar_last = ar_block + blocking;	/* Restore pointer to end */
+
+	if (ar_reading) {
+		if(time_to_start_writing) {
+			time_to_start_writing=0;
+			ar_reading=0;
+
+			if(file_to_switch_to>=0) {
+				if((c=rmtclose(archive))<0)
+					msg_perror("Warning: can't close %s(%d,%d)",ar_file,archive,c);
+
+				archive=file_to_switch_to;
+			} else
+	 			(void)backspace_output();
+			fl_write();
+		} else
+			fl_read();
+	} else {
+		fl_write();
+	}
+}
+
+/* Backspace the archive descriptor by one blocks worth.
+   If its a tape, MTIOCTOP will work.  If its something else,
+   we try to seek on it.  If we can't seek, we lose! */
+int
+backspace_output()
+{
+	long cur;
+	/* int er; */
+	extern char *output_start;
+
+#ifdef MTIOCTOP
+	struct mtop t;
+
+	t.mt_op = MTBSR;
+	t.mt_count = 1;
+	if((rmtioctl(archive,MTIOCTOP,&t))>=0)
+		return 1;
+	if(errno==EIO && (rmtioctl(archive,MTIOCTOP,&t))>=0)
+		return 1;
+#endif
+
+		cur=rmtlseek(archive,0L,1);
+	cur-=blocksize;
+	/* Seek back to the beginning of this block and
+	   start writing there. */
+
+	if(rmtlseek(archive,cur,0)!=cur) {
+		/* Lseek failed.  Try a different method */
+		msg("Couldn't backspace archive file.  It may be unreadable without -i.");
+		/* Replace the first part of the block with nulls */
+		if(ar_block->charptr!=output_start)
+			bzero(ar_block->charptr,output_start-ar_block->charptr);
+		return 2;
+	}
+	return 3;
+}
+
+
+/*
+ * Close the archive file.
+ */
+void
+close_archive()
+{
+	int child;
+	int status;
+	int c;
+
+	if (time_to_start_writing || !ar_reading)
+		flush_archive();
+	if(cmd_mode==CMD_DELETE) {
+		off_t pos;
+
+		pos = rmtlseek(archive,0L,1);
+#ifndef __MSDOS__
+		(void) ftruncate(archive,pos);
+#else
+		(void)rmtwrite(archive,"",0);
+#endif
+	}
+	if(f_verify)
+		verify_volume();
+
+	if((c=rmtclose(archive))<0)
+		msg_perror("Warning: can't close %s(%d,%d)",ar_file,archive,c);
+
+#ifndef	__MSDOS__
+	if (childpid) {
+		/*
+		 * Loop waiting for the right child to die, or for
+		 * no more kids.
+		 */
+		while (((child = wait(&status)) != childpid) && child != -1)
+			;
+
+		if (child != -1) {
+			switch (WTERMSIG(status)) {
+			case 0:
+				/* Child voluntarily terminated  -- but why? */
+				if (WEXITSTATUS(status) == MAGIC_STAT) {
+					exit(EX_SYSTEM);/* Child had trouble */
+				}
+				if (WEXITSTATUS(status) == (SIGPIPE + 128)) {
+					/*
+					 * /bin/sh returns this if its child
+					 * dies with SIGPIPE.  'Sok.
+					 */
+					break;
+				} else if (WEXITSTATUS(status))
+					msg("child returned status %d",
+						WEXITSTATUS(status));
+			case SIGPIPE:
+				break;		/* This is OK. */
+
+			default:
+				msg("child died with signal %d%s",
+				 WTERMSIG(status),
+				 WIFCOREDUMPED(status)? " (core dumped)": "");
+			}
+		}
+	}
+#endif	/* __MSDOS__ */
+}
+
+
+#ifdef DONTDEF
+/*
+ * Message management.
+ *
+ * anno writes a message prefix on stream (eg stdout, stderr).
+ *
+ * The specified prefix is normally output followed by a colon and a space.
+ * However, if other command line options are set, more output can come
+ * out, such as the record # within the archive.
+ *
+ * If the specified prefix is NULL, no output is produced unless the
+ * command line option(s) are set.
+ *
+ * If the third argument is 1, the "saved" record # is used; if 0, the
+ * "current" record # is used.
+ */
+void
+anno(stream, prefix, savedp)
+	FILE	*stream;
+	char	*prefix;
+	int	savedp;
+{
+#	define	MAXANNO	50
+	char	buffer[MAXANNO];	/* Holds annorecment */
+#	define	ANNOWIDTH 13
+	int	space;
+	long	offset;
+	int	save_e;
+
+	save_e=errno;
+	/* Make sure previous output gets out in sequence */
+	if (stream == stderr)
+		fflush(stdout);
+	if (f_sayblock) {
+		if (prefix) {
+			fputs(prefix, stream);
+			putc(' ', stream);
+		}
+		offset = ar_record - ar_block;
+		(void) sprintf(buffer, "rec %d: ",
+			savedp?	saved_recno:
+				baserec + offset);
+		fputs(buffer, stream);
+		space = ANNOWIDTH - strlen(buffer);
+		if (space > 0) {
+			fprintf(stream, "%*s", space, "");
+		}
+	} else if (prefix) {
+		fputs(prefix, stream);
+		fputs(": ", stream);
+	}
+	errno=save_e;
+}
+#endif
+
+/* We've hit the end of the old volume.  Close it and open the next one */
+/* Values for type:  0: writing  1: reading  2: updating */
+int
+new_volume(type)
+int	type;
+{
+	int	c;
+	char	inbuf[80];
+	char	*p;
+	static FILE *read_file = 0;
+	extern int now_verifying;
+	extern char TTY_NAME[];
+	char *getenv();
+
+	if(!read_file && !f_run_script_at_end)
+		read_file = (archive==0) ? fopen(TTY_NAME, "r") : stdin;
+
+	if(now_verifying)
+		return -1;
+	if(f_verify)
+		verify_volume();
+	if((c=rmtclose(archive))<0)
+		msg_perror("Warning: can't close %s(%d,%d)",ar_file,archive,c);
+
+	volno++;
+ tryagain:
+	if (f_run_script_at_end)
+		system(info_script);
+	else for(;;) {
+		fprintf(msg_file,"\007Prepare volume #%d and hit return: ",volno);
+		fflush(msg_file);
+		if(fgets(inbuf,sizeof(inbuf),read_file)==0) {
+ 			fprintf(msg_file,"EOF?  What does that mean?");
+			if(cmd_mode!=CMD_EXTRACT && cmd_mode!=CMD_LIST && cmd_mode!=CMD_DIFF)
+				msg("Warning:  Archive is INCOMPLETE!");
+			exit(EX_BADARCH);
+		}
+		if(inbuf[0]=='\n' || inbuf[0]=='y' || inbuf[0]=='Y')
+			break;
+
+		switch(inbuf[0]) {
+		case '?':
+		{
+			fprintf(msg_file,"\
+ n [name]   Give a new filename for the next (and subsequent) volume(s)\n\
+ q          Abort tar\n\
+ !          Spawn a subshell\n\
+ ?          Print this list\n");
+		}
+			break;
+
+		case 'q':	/* Quit */
+			fprintf(msg_file,"No new volume; exiting.\n");
+			if(cmd_mode!=CMD_EXTRACT && cmd_mode!=CMD_LIST && cmd_mode!=CMD_DIFF)
+				msg("Warning:  Archive is INCOMPLETE!");
+			exit(EX_BADARCH);
+
+		case 'n':	/* Get new file name */
+		{
+			char *q,*r;
+			static char *old_name;
+
+			for(q= &inbuf[1];*q==' ' || *q=='\t';q++)
+				;
+			for(r=q;*r;r++)
+				if(*r=='\n')
+					*r='\0';
+			if(old_name)
+				free(old_name);
+			old_name=p=(char *)malloc((unsigned)(strlen(q)+2));
+			if(p==0) {
+				msg("Can't allocate memory for name");
+				exit(EX_SYSTEM);
+			}
+			(void) strcpy(p,q);
+			ar_file=p;
+		}
+			break;
+
+		case '!':
+#ifdef __MSDOS__
+			spawnl(P_WAIT,getenv("COMSPEC"),"-",0);
+#else
+				/* JF this needs work! */
+			switch(fork()) {
+			case -1:
+				msg_perror("can't fork!");
+				break;
+			case 0:
+				p=getenv("SHELL");
+				if(p==0) p="/bin/sh";
+				execlp(p,"-sh","-i",0);
+				msg_perror("can't exec a shell %s",p);
+				_exit(55);
+			default:
+				wait(0);
+				break;
+			}
+#endif
+			break;
+		}
+	}
+
+	if(type==2 || f_verify)
+		archive=rmtopen(ar_file,O_RDWR|O_CREAT,0666);
+	else if(type==1)
+		archive=rmtopen(ar_file,O_RDONLY,0666);
+	else if(type==0)
+		archive=rmtcreat(ar_file,0666);
+	else
+		archive= -1;
+
+	if(archive<0) {
+		msg_perror("can't open %s",ar_file);
+		goto tryagain;
+	}
+#ifdef __MSDOS__
+	setmode(archive,O_BINARY);
+#endif
+	return 0;
+}
+
+/* this is a useless function that takes a buffer returned by wantbytes
+   and does nothing with it.  If the function called by wantbytes returns
+   an error indicator (non-zero), this function is called for the rest of
+   the file.
+ */
+int
+no_op(size,data)
+int size;
+char *data;
+{
+	return 0;
+}
+
+/* Some other routine wants SIZE bytes in the archive.  For each chunk of
+   the archive, call FUNC with the size of the chunk, and the address of
+   the chunk it can work with.
+ */
+int
+wantbytes(size,func)
+long size;
+int (*func)();
+{
+	char *data;
+	long	data_size;
+
+	while(size) {
+		data = findrec()->charptr;
+		if (data == NULL) {	/* Check it... */
+			msg("Unexpected EOF on archive file");
+			return -1;
+		}
+		data_size = endofrecs()->charptr - data;
+		if(data_size>size)
+			data_size=size;
+		if((*func)(data_size,data))
+			func=no_op;
+		userec((union record *)(data + data_size - 1));
+		size-=data_size;
+	}
+	return 0;
+}

+ 8 - 8
src/extract.c

@@ -217,7 +217,7 @@ extract_archive ()
 	  */
     case LF_SPARSE:
       sp_array_size = 10;
-      sparsearray = (struct sp_array *) malloc (sp_array_size * sizeof (struct sp_array));
+      sparsearray = (struct sp_array *) ck_malloc (sp_array_size * sizeof (struct sp_array));
       for (i = 0; i < SPARSE_IN_HDR; i++)
 	{
 	  sparsearray[i].offset =
@@ -252,7 +252,7 @@ extract_archive ()
 					  * since we've run out of room --
 					  */
 		      sparsearray = (struct sp_array *)
-			realloc (sparsearray,
+			ck_realloc (sparsearray,
 			    2 * sp_array_size * (sizeof (struct sp_array)));
 		      sp_array_size *= 2;
 		    }
@@ -367,8 +367,8 @@ extract_archive ()
 			  * that happen to contain the filename will look
 			  * REAL interesting unless we do this.
 			  */
-	  namelen = strlen (skipcrud + current_file_name);
-	  name = (char *) malloc ((sizeof (char)) * namelen);
+	  namelen = strlen (skipcrud + current_file_name) + 1;
+	  name = (char *) ck_malloc ((sizeof (char)) * namelen);
 	  bcopy (skipcrud + current_file_name, name, namelen);
 	  size = hstat.st_size;
 	  extract_sparse_file (fd, &size, hstat.st_size, name);
@@ -436,7 +436,7 @@ extract_archive ()
 			  skipcrud + current_file_name);
 	    else
 	      msg ("could only write %d of %d bytes to file %s",
-		   written, check, skipcrud + current_file_name);
+		   check, written, skipcrud + current_file_name);
 	    skip_file ((long) (size - written));
 	    break;		/* Still do the close, mod time, chmod, etc */
 	  }
@@ -634,7 +634,7 @@ extract_archive ()
 
     case LF_DIR:
     case LF_DUMPDIR:
-      namelen = strlen (current_file_name) + skipcrud - 1;
+      namelen = strlen (current_file_name + skipcrud) - 1;
     really_dir:
       /* Check for trailing /, and zap as many as we find. */
       while (namelen
@@ -682,8 +682,8 @@ extract_archive ()
 
       if (f_modified)
 	goto set_filestat;
-      tmp = (struct saved_dir_info *) malloc (sizeof (struct saved_dir_info));
-      tmp->path = malloc (strlen (skipcrud + current_file_name) + 1);
+      tmp = (struct saved_dir_info *) ck_malloc (sizeof (struct saved_dir_info));
+      tmp->path = ck_malloc (strlen (skipcrud + current_file_name) + 1);
       strcpy (tmp->path, skipcrud + current_file_name);
       tmp->mode = hstat.st_mode;
       tmp->atime = hstat.st_atime;