/* 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 1, or (at your option)
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.
* @(#) buffer.c 1.28 11/6/87 - gnu
#include <sys/types.h> /* For non-Berkeley systems */
#if !defined(USG) || defined(HAVE_MTIO)
/* 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 */
#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() */
extern char *index(), *strcat();
* V7 doesn't have a #define for this.
#define MAGIC_STAT 105 /* Magic status returned by child, if
it can't exec. We hope compress/sh
never return this status! */
extern void finish_header();
/* Obnoxious test to see if dimwit is trying to dump the archive */
* 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
static union record
**save_rec
;
union record record_save_area
;
* PID of child program, if f_compress or remote archive access.
* Record number of the start of this block of records
static int r_error_count
;
/* 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
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 */
ar_last
=ar_block
+blocking
;
* 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".
if (ar_record
== ar_last
) {
return (union record
*)NULL
; /* EOF */
if (ar_record
== ar_last
) {
return (union record
*)NULL
; /* EOF */
* Indicate that we have used all records up thru the argument.
* (should the arg have an off-by-1? XXX FIXME)
* 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
* 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.
* Duplicate a file descriptor into a certain slot.
* Equivalent to BSD "dup2" with error reporting.
if(err
<0 && errno
!=EBADF
) {
msg_perror("Cannot close descriptor %d",to
);
msg_perror("cannot dup %s",msg
);
fprintf(stderr
,"MSDOS %s can't use compressed or remote archives\n",tar
);
msg_perror("cannot fork");
/* We're the parent. Clean up and be happy */
/* This, at least, is easy */
dupto(pipe
[WRITE
],STDOUT
,"(child) pipe to stdout");
dupto(pipe
[READ
],STDIN
,"(child) pipe to stdin");
/* 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 */
if(!(ar_file
[0]=='-' && ar_file
[1]=='\0') && isfile(ar_file
))
if(!(ar_file
[0]=='-' && ar_file
[1]=='\0') && !_remdev(ar_file
) && isfile(ar_file
))
/* We don't need a child tar. Open the archive */
archive
=open(ar_file
, O_RDONLY
|O_BINARY
, 0666);
msg_perror("can't open archive %s",ar_file
);
dupto(archive
,STDIN
,"archive to stdin");
archive
=creat(ar_file
,0666);
msg_perror("can't open archive %s",ar_file
);
dupto(archive
,STDOUT
,"archive to stdout");
/* We need a child tar */
msg_perror("child can't fork");
/* About to exec compress: set up the files */
dupto(kidpipe
[READ
],STDIN
,"((child)) pipe to stdin");
ck_close(kidpipe
[WRITE
]);
/* dup2(pipe[WRITE],STDOUT); */
/* dup2(pipe[READ],STDIN); */
dupto(kidpipe
[WRITE
],STDOUT
,"((child)) pipe to stdout");
/* ck_close(pipe[READ]); */
/* ck_close(pipe[WRITE]); */
/* ck_close(kidpipe[READ]);
ck_close(kidpipe[WRITE]); */
/* Grandchild. Do the right thing, namely sit here and
read/write the archive, and feed stuff back to compress */
dupto(kidpipe
[WRITE
],STDOUT
,"[child] pipe to stdout");
dupto(kidpipe
[READ
],STDIN
,"[child] pipe to stdin");
ck_close(kidpipe
[WRITE
]);
if (ar_file
[0] == '-' && ar_file
[1] == '\0') {
} else /* This can't happen if (ar_reading==2)
archive = rmtopen(ar_file, O_RDWR|O_CREAT|O_BINARY, 0666);
archive
= rmtopen(ar_file
, O_RDONLY
|O_BINARY
, 0666);
archive
= rmtcreat(ar_file
, 0666);
msg_perror("can't open archive %s",ar_file
);
err
=rmtread(archive
, ar_block
->charptr
,(int)(blocksize
));
count
= (max
<RECORDSIZE
) ? max
: RECORDSIZE
;
err
=write(STDOUT
,ptr
,count
);
msg_perror("can't write to compress");
msg("write to compress short %d bytes",count
-err
);
count
= (err
<0) ? 0 : err
;
err
=read(STDIN
,ptr
,(n
<RECORDSIZE
) ? n
: RECORDSIZE
);
bzero(ar_block
->charptr
+blocksize
-n
,n
);
err
=rmtwrite(archive
,ar_block
->charptr
,blocksize
);
msg_perror("can't read from compress");
err
=rmtwrite(archive
, ar_block
->charptr
, (int)blocksize
);
/* So we should exec compress (-d) */
execlp("compress", "compress", "-d", (char *)0);
execlp("compress", "compress", (char *)0);
msg_perror("can't exec compress");
/* return non-zero if p is the name of a directory */
if((stbuf
.st_mode
&S_IFMT
)==S_IFREG
)
* Open an archive file. The argument specifies whether we are
/* JF if the arg is 2, open for reading and writing. */
msg_file
= f_exstdout
? stderr
: stdout
;
msg("invalid value for blocksize");
msg("No archive name given, what should I do?");
ar_block
= (union record
*) valloc((unsigned)(blocksize
+(2*RECORDSIZE
)));
ar_block
= (union record
*) valloc((unsigned)blocksize
);
msg("could not allocate memory for blocking factor %d",
ar_last
= ar_block
+ blocking
;
if(reading
==2 || f_verify
) {
msg("cannot update or verify compressed archives");
if(!reading
&& ar_file
[0]=='-' && ar_file
[1]=='\0')
/* child_open(rem_host, rem_file); */
} else if (ar_file
[0] == '-' && ar_file
[1] == '\0') {
f_reblock
++; /* Could be a pipe, be safe */
msg("can't verify stdin/stdout archive");
write_archive_to_stdout
++;
} else if (reading
==2 || f_verify
) {
archive
= rmtopen(ar_file
, O_RDWR
|O_CREAT
|O_BINARY
, 0666);
archive
= rmtopen(ar_file
, O_RDONLY
|O_BINARY
, 0666);
archive
= rmtcreat(ar_file
, 0666);
fstat(archive
,&tmp_stat
);
if((tmp_stat
.st_mode
&S_IFMT
)==S_IFREG
) {
msg_perror("can't open %s",ar_file
);
setmode(archive
, O_BINARY
);
ar_last
= ar_block
; /* Set up for 1st block = # 0 */
(void) findrec(); /* Read it in, check for EOF */
ptr
=malloc(strlen(f_volhdr
)+20);
sprintf(ptr
,"%s Volume %d",f_volhdr
,1);
msg("Archive not labelled to match %s",f_volhdr
);
if (re_match (label_pattern
, head
->header
.name
,
strlen (head
->header
.name
), 0, 0) < 0) {
msg ("Volume mismatch! %s!=%s", f_volhdr
,
if(strcmp(ptr
,head
->header
.name
)) {
msg("Volume mismatch! %s!=%s",ptr
,head
->header
.name
);
bzero((void *)ar_block
,RECORDSIZE
);
sprintf(ar_block
->header
.name
,"%s Volume 1",f_volhdr
);
strcpy(ar_block
->header
.name
,f_volhdr
);
ar_block
->header
.linkflag
= LF_VOLHDR
;
to_oct(time(0), 1+12, ar_block
->header
.mtime
);
* 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
* 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.
offset
= ar_record
- ar_block
;
saved_recno
= baserec
+ offset
;
* Perform a write to flush the buffer.
deal_with_new_volume_stuff();
static long bytes_written
= 0;
if(tape_length
&& bytes_written
>= tape_length
* 1024) {
err
= rmtwrite(archive
, ar_block
->charptr
,(int) blocksize
);
if(err
!=blocksize
&& !f_multivol
)
tot_written
+= blocksize
;
strcpy(real_s_name
,save_name
);
real_s_totsize
= save_totsize
;
real_s_sizeleft
= save_sizeleft
;
/* 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
))
if(f_volhdr
&& real_s_name
[0]) {
} else if(f_volhdr
|| real_s_name
[0]) {
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
;
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,
to_oct((long)real_s_totsize
-real_s_sizeleft
,
1+12,ar_block
->header
.offset
);
err
= rmtwrite(archive
, ar_block
->charptr
,(int) blocksize
);
tot_written
+= blocksize
;
bytes_written
= blocksize
;
bcopy((void *)(ar_block
+blocking
-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
)
strcpy(real_s_name
,save_name
);
real_s_sizeleft
= save_sizeleft
;
real_s_totsize
=save_totsize
;
/* 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 */
msg_perror("can't write to %s",ar_file
);
msg("only wrote %u of %u bytes to %s",err
,blocksize
,ar_file
);
* Handle read errors on the archive.
* If the read should be retried, readerror() returns to the caller.
# define READ_ERROR_MAX 10
read_error_flag
++; /* Tell callers */
msg_perror("read error on %s",ar_file
);
/* First block of tape. Probably stupidity error */
* 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.");
* Perform a read to flush the buffer.
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.
*save_rec
>= ar_record
&&
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(save_name
!=real_s_name
) {
strcpy(real_s_name
,save_name
);
real_s_totsize
= save_totsize
;
real_s_sizeleft
= save_sizeleft
;
err
= rmtread(archive
, ar_block
->charptr
, (int)blocksize
);
if((err
== 0 || (err
<0 && errno
==ENOSPC
)) && f_multivol
) {
if(new_volume((cmd_mode
==CMD_APPEND
|| cmd_mode
==CMD_CAT
|| cmd_mode
==CMD_UPDATE
) ? 2 : 1)<0)
err
= rmtread(archive
, ar_block
->charptr
,(int) blocksize
);
if(head
->header
.linkflag
==LF_VOLHDR
) {
ptr
=(char *)malloc(strlen(f_volhdr
)+20);
sprintf(ptr
,"%s Volume %d",f_volhdr
,volno
);
if (re_match (label_pattern
, head
->header
.name
,
strlen (head
->header
.name
),
msg("Volume mismatch! %s!=%s",f_volhdr
,
if(strcmp(ptr
,head
->header
.name
)) {
msg("Volume mismatch! %s!=%s",ptr
,head
->header
.name
);
fprintf(msg_file
,"Reading %s",head
->header
.name
);
msg("Warning: No volume header!");
if(head
->header
.linkflag
!=LF_MULTIVOL
|| strcmp(head
->header
.name
,real_s_name
)) {
msg("%s is not continued on this volume!",real_s_name
);
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
));
if(real_s_totsize
-real_s_sizeleft
!=from_oct(1+12,head
->header
.offset
)) {
msg("This volume is out of sequence");
goto error_loop
; /* Try again */
more
= ar_block
->charptr
+ err
;
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
;
* User warned us about this. Fix up.
err
= rmtread(archive
, more
, (int)left
);
goto error2loop
; /* Try again */
msg("archive %s EOF not on block boundary",ar_file
);
msg("only read %d bytes from archive %s",err
,ar_file
);
* Flush the current buffer to/from the archive.
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(time_to_start_writing
) {
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
;
(void)backspace_output();
/* 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! */
extern char *output_start
;
if((rmtioctl(archive
,MTIOCTOP
,&t
))>=0)
if(errno
==EIO
&& (rmtioctl(archive
,MTIOCTOP
,&t
))>=0)
cur
=rmtlseek(archive
,0L,1);
/* Seek back to the beginning of this block and
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
);
* Close the archive file.
if (time_to_start_writing
|| !ar_reading
)
if(cmd_mode
==CMD_DELETE
) {
pos
= rmtlseek(archive
,0L,1);
/* FIXME does ftruncate really take an INT?! */
(void) ftruncate(archive
,(int)pos
);
(void)rmtwrite(archive
,"",0);
if((c
=rmtclose(archive
))<0)
msg_perror("Warning: can't close %s(%d,%d)",ar_file
,archive
,c
);
* Loop waiting for the right child to die, or for
while (((child
= wait(&status
)) != childpid
) && child
!= -1)
switch (TERM_SIGNAL(status
)) {
/* Child voluntarily terminated -- but why? */
if (TERM_VALUE(status
) == MAGIC_STAT
) {
exit(EX_SYSTEM
);/* Child had trouble */
if (TERM_VALUE(status
) == (SIGPIPE
+ 128)) {
* /bin/sh returns this if its child
* dies with SIGPIPE. 'Sok.
} else if (TERM_VALUE(status
))
msg("child returned status %d",
msg("child died with signal %d%s",
TERM_COREDUMP(status
)? " (core dumped)": "");
* 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.
anno(stream
, prefix
, savedp
)
char buffer
[MAXANNO
]; /* Holds annorecment */
/* Make sure previous output gets out in sequence */
offset
= ar_record
- ar_block
;
(void) sprintf(buffer
, "rec %d: ",
space
= ANNOWIDTH
- strlen(buffer
);
fprintf(stream
, "%*s", space
, "");
/* 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 */
static FILE *read_file
= 0;
extern int now_verifying
;
if(!read_file
&& !f_run_script_at_end
)
read_file
= (archive
==0) ? fopen(TTY_NAME
, "r") : stdin
;
if((c
=rmtclose(archive
))<0)
msg_perror("Warning: can't close %s(%d,%d)",ar_file
,archive
,c
);
fprintf(msg_file
,"\007Prepare volume #%d and hit return: ",volno
);
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!");
if(inbuf
[0]=='\n' || inbuf
[0]=='y' || inbuf
[0]=='Y')
n [name] Give a new filename for the next (and subsequent) volume(s)\n\
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!");
case 'n': /* Get new file name */
for(q
= &inbuf
[1];*q
==' ' || *q
=='\t';q
++)
old_name
=p
=(char *)malloc((unsigned)(strlen(q
)+2));
msg("Can't allocate memory for name");
spawnl(P_WAIT
,getenv("COMSPEC"),"-",0);
/* JF this needs work! */
msg_perror("can't fork!");
msg_perror("can't exec a shell %s",p
);
archive
=rmtopen(ar_file
,O_RDWR
|O_CREAT
,0666);
archive
=rmtopen(ar_file
,O_RDONLY
,0666);
archive
=rmtcreat(ar_file
,0666);
msg_perror("can't open %s",ar_file
);
setmode(archive
,O_BINARY
);
/* 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
/* 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.
data
= findrec()->charptr
;
if (data
== NULL
) { /* Check it... */
msg("Unexpected EOF on archive file");
data_size
= endofrecs()->charptr
- data
;
if((*func
)(data_size
,data
))
userec((union record
*)(data
+ data_size
- 1));