windows-nt/Source/XPSP1/NT/base/subsys/posix/programs/pax/buffer.c
2020-09-26 16:20:57 +08:00

791 lines
20 KiB
C

/* $Source: /u/mark/src/pax/RCS/buffer.c,v $
*
* $Revision: 1.2 $
*
* buffer.c - Buffer management functions
*
* DESCRIPTION
*
* These functions handle buffer manipulations for the archiving
* formats. Functions are provided to get memory for buffers,
* flush buffers, read and write buffers and de-allocate buffers.
* Several housekeeping functions are provided as well to get some
* information about how full buffers are, etc.
*
* AUTHOR
*
* Mark H. Colburn, NAPS International (mark@jhereg.mn.org)
*
* Sponsored by The USENIX Association for public distribution.
*
* Copyright (c) 1989 Mark H. Colburn.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice is duplicated in all such
* forms and that any documentation, advertising materials, and other
* materials related to such distribution and use acknowledge that the
* software was developed * by Mark H. Colburn and sponsored by The
* USENIX Association.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Log: buffer.c,v $
* Revision 1.2 89/02/12 10:04:02 mark
* 1.2 release fixes
*
* Revision 1.1 88/12/23 18:02:01 mark
* Initial revision
*
*/
#ifndef lint
static char *ident = "$Id: buffer.c,v 1.2 89/02/12 10:04:02 mark Exp $";
static char *copyright = "Copyright (c) 1989 Mark H. Colburn.\nAll rights reserved.\n";
#endif /* ! lint */
/* Headers */
#include "pax.h"
/* Function Prototypes */
static int ar_write(int, char *, uint);
static void buf_pad(OFFSET);
static int indata(int, OFFSET, char *);
static void outflush(void);
static void buf_use(uint);
static int buf_in_avail(char **, uint *);
static uint buf_out_avail(char **);
/* inentry - install a single archive entry
*
* DESCRIPTION
*
* Inentry reads an archive entry from the archive file and writes it
* out the the named file. If we are in PASS mode during archive
* processing, the pass() function is called, otherwise we will
* extract from the archive file.
*
* Inentry actaully calls indata to process the actual data to the
* file.
*
* PARAMETERS
*
* char *name - name of the file to extract from the archive
* Stat *asb - stat block of the file to be extracted from the
* archive.
*
* RETURNS
*
* Returns zero if successful, -1 otherwise.
*/
int inentry(char *name, Stat *asb)
{
Link *linkp;
int ifd;
int ofd;
#ifdef _POSIX_SOURCE /* Xn */
struct utimbuf tstamp; /* Xn */
#else /* Xn */
time_t tstamp[2];
#endif /* Xn */
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: int inentry() in buffer.c\n");
#endif
if ((ofd = openout(name, asb, linkp = linkfrom(name, asb), 0)) > 0) {
if (asb->sb_size || linkp == (Link *)NULL || linkp->l_size == 0) {
close(indata(ofd, asb->sb_size, name));
} else if ((ifd = open(linkp->l_path->p_name, O_RDONLY)) < 0) {
warn(linkp->l_path->p_name, strerror(errno)); /* Xn */
} else {
passdata(linkp->l_path->p_name, ifd, name, ofd);
close(ifd);
close(ofd);
}
} else {
return (buf_skip((OFFSET) asb->sb_size) >= 0);
}
#ifdef _POSIX_SOURCE
tstamp.actime = (!f_pass && f_access_time) ? asb->sb_atime : time((time_t *) 0); /* Xn */
tstamp.modtime = f_mtime ? asb->sb_mtime : time((time_t *) 0); /* Xn */
(void) utime(name, &tstamp); /* Xn */
#else
tstamp[0] = (!f_pass && f_access_time) ? asb->sb_atime : time((time_t *) 0);
tstamp[1] = f_mtime ? asb->sb_mtime : time((time_t *) 0);
(void) utime(name, tstamp); /* Xn */
#endif
return (0);
}
/* outdata - write archive data
*
* DESCRIPTION
*
* Outdata transfers data from the named file to the archive buffer.
* It knows about the file padding which is required by tar, but no
* by cpio. Outdata continues after file read errors, padding with
* null characters if neccessary. Closes the input file descriptor
* when finished.
*
* PARAMETERS
*
* int fd - file descriptor of file to read data from
* char *name - name of file
* OFFSET size - size of the file
*
*/
void outdata(int fd, char *name, OFFSET size)
{
uint chunk, remaining;
int got;
int oops;
uint avail;
int pad;
char *buf;
remaining = size < 0 ? 0 : (uint)size;
oops = got = 0;
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: void outdata() in buffer.c\n");
#endif
if (pad = (remaining % BLOCKSIZE)) {
pad = (BLOCKSIZE - pad);
}
while (remaining) {
avail = buf_out_avail(&buf);
//printf("avail X%dX, size X%dX\n", avail, size);
remaining -= (chunk = remaining < avail ? remaining : avail);
//printf("chunk X%dX", chunk);
memset(buf, 0, chunk);
if (oops == 0 && (got = read(fd, buf, (unsigned int) chunk)) < 0 && errno != 12) {
oops = -1;
//puts("b warn outdata");
//printf("size X%dX\n", sizeof(buf));
//printf("oops X%dX got X%dX fd X%dX buf X%sX errno X%dX X%dX\n", oops, got, fd, buf, errno, chunk);
warn(name, strerror(errno)); /* Xn */
//puts("a warn outdata");
got = 0;
}
if (got == -1 && errno == 12)
got = 0;
if (got < 0)
got = 0;
if ((uint)got < chunk) {
if (oops == 0) {
oops = -1;
}
warn(name, "Early EOF");
while ((uint)got < chunk) {
buf[got++] = '\0';
}
}
buf_use(chunk);
}
close(fd);
if (ar_format == TAR) {
buf_pad((OFFSET) pad);
}
}
/* write_eot - write the end of archive record(s)
*
* DESCRIPTION
*
* Write out an End-Of-Tape record. We actually zero at least one
* record, through the end of the block. Old tar writes garbage after
* two zeroed records -- and PDtar used to.
*/
void write_eot(void)
{
OFFSET pad;
char header[M_STRLEN + H_STRLEN + 1];
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: void write_eot() in buffer.c\n");
#endif
if (ar_format == TAR) {
/* write out two zero blocks for trailer */
pad = 2 * BLOCKSIZE;
} else {
if (pad = (total + M_STRLEN + H_STRLEN + TRAILZ) % BLOCKSIZE) {
pad = BLOCKSIZE - pad;
}
strcpy(header, M_ASCII);
sprintf(header + M_STRLEN, H_PRINT, 0, 0,
#if 0 /* NIST-PCTS */
0, 0, 0, 1, 0, (time_t) 0, TRAILZ, pad);
#else /* NIST-PCTS */
0, 0, 0, 1, 0, (time_t) 0, TRAILZ, (OFFSET) 0); /* NIST-PCTS */
#endif /* NIST-PCTS */
outwrite(header, M_STRLEN + H_STRLEN);
outwrite(TRAILER, TRAILZ);
}
buf_pad((OFFSET) pad);
outflush();
}
/* outwrite - write archive data
*
* DESCRIPTION
*
* Writes out data in the archive buffer to the archive file. The
* buffer index and the total byte count are incremented by the number
* of data bytes written.
*
* PARAMETERS
*
* char *idx - pointer to data to write
* uint len - length of the data to write
*/
void outwrite(char *idx, uint len)
{
uint have;
uint want;
char *endx;
endx = idx + len;
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: void outwrite() in buffer.c\n");
#endif
while (want = (uint)(endx - idx)) {
if (bufend - bufidx < 0) {
fatal("Buffer overflow in out_write\n"); /* Xn */
}
//puts("b outflush");
if ((have = (uint)(bufend - bufidx)) == 0) {
outflush();
//puts("a outflush");
}
if (have > want) {
have = want;
}
memcpy(bufidx, idx, (int) have);
//puts("a memcpy outwrite");
bufidx += have;
idx += have;
total += have;
}
}
/* passdata - copy data to one file
*
* DESCRIPTION
*
* Copies a file from one place to another. Doesn't believe in input
* file descriptor zero (see description of kludge in openin() comments).
* Closes the provided output file descriptor.
*
* PARAMETERS
*
* char *from - input file name (old file)
* int ifd - input file descriptor
* char *to - output file name (new file)
* int ofd - output file descriptor
*/
void passdata(char *from, int ifd, char *to, int ofd)
{
int got;
int sparse;
char block[BUFSIZ];
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: void passdata() in buffer.c\n");
#endif
if (ifd) {
lseek(ifd, (OFFSET) 0, SEEK_SET); /* Xn */
sparse = 0;
while ((got = read(ifd, block, sizeof(block))) > 0
&& (sparse = ar_write(ofd, block, (uint) got)) >= 0) {
total += got;
}
if (got) {
warn(got < 0 ? from : to, strerror(errno)); /* Xn */
} else if (sparse > 0
&& (lseek(ofd, (OFFSET)(-sparse), SEEK_CUR) < 0 /* Xn */
|| write(ofd, block, (uint) sparse) != sparse)) {
warn(to, strerror(errno)); /* Xn */
}
}
close(ofd);
}
/* buf_allocate - get space for the I/O buffer
*
* DESCRIPTION
*
* buf_allocate allocates an I/O buffer using malloc. The resulting
* buffer is used for all data buffering throughout the program.
* Buf_allocate must be called prior to any use of the buffer or any
* of the buffering calls.
*
* PARAMETERS
*
* int size - size of the I/O buffer to request
*
* ERRORS
*
* If an invalid size is given for a buffer or if a buffer of the
* required size cannot be allocated, then the function prints out an
* error message and returns a non-zero exit status to the calling
* process, terminating the program.
*
*/
void buf_allocate(OFFSET size)
{
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: void buf_allocate() in buffer.c\n");
#endif
if (size <= 0) {
fatal("invalid value for blocksize");
}
if ((bufstart = malloc((unsigned) size)) == (char *)NULL) {
fatal("Cannot allocate I/O buffer");
}
bufend = bufidx = bufstart;
bufend += size;
}
/* buf_skip - skip input archive data
*
* DESCRIPTION
*
* Buf_skip skips past archive data. It is used when the length of
* the archive data is known, and we do not wish to process the data.
*
* PARAMETERS
*
* OFFSET len - number of bytes to skip
*
* RETURNS
*
* Returns zero under normal circumstances, -1 if unreadable data is
* encountered.
*/
int buf_skip(OFFSET len)
{
uint chunk, remaining;
int corrupt = 0;
remaining = len < 0 ? 0 : (uint)len;
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: int buf_skip() in buffer.c\n");
#endif
while (remaining) {
if (bufend - bufidx < 0) {
fatal("Buffer overflow in buf_skip\n"); /* Xn */
}
while ((chunk = (uint)(bufend - bufidx)) == 0) {
corrupt |= ar_read();
}
if (chunk > remaining) {
chunk = remaining;
}
bufidx += chunk;
remaining -= chunk;
total += chunk;
}
return (corrupt);
}
/* buf_read - read a given number of characters from the input archive
*
* DESCRIPTION
*
* Reads len number of characters from the input archive and
* stores them in the buffer pointed at by dst.
*
* PARAMETERS
*
* char *dst - pointer to buffer to store data into
* uint len - length of data to read
*
* RETURNS
*
* Returns zero with valid data, -1 if unreadable portions were
* replaced by null characters.
*/
int buf_read(char *dst, uint len)
{
int have;
int want;
int corrupt = 0;
char *endx = dst + len;
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: int buf_read() in buffer.c\n");
#endif
while (want = (int)(endx - dst)) {
if (bufend - bufidx < 0) {
fatal("Buffer overflow in buf_read\n"); /* Xn */
}
while ((have = (int)(bufend - bufidx)) == 0) {
have = 0;
corrupt |= ar_read();
}
if (have > want) {
have = want;
}
memcpy(dst, bufidx, have);
bufidx += have;
dst += have;
total += have;
}
return (corrupt);
}
/* indata - install data from an archive
*
* DESCRIPTION
*
* Indata writes size bytes of data from the archive buffer to the output
* file specified by fd. The filename which is being written, pointed
* to by name is provided only for diagnostics.
*
* PARAMETERS
*
* int fd - output file descriptor
* OFFSET size - number of bytes to write to output file
* char *name - name of file which corresponds to fd
*
* RETURNS
*
* Returns given file descriptor.
*/
static int indata(int fd, OFFSET size, char *name)
{
uint chunk, remaining;
char *oops;
int sparse;
int corrupt;
char *buf;
uint avail;
remaining = size < 0 ? 0 : (uint)size;
corrupt = sparse = 0;
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: static int indata() in buffer.c\n");
#endif
oops = (char *)NULL;
while (remaining) {
corrupt |= buf_in_avail(&buf, &avail);
remaining -= (chunk = remaining < avail ? remaining : avail);
if (oops == (char *)NULL && (sparse = ar_write(fd, buf, chunk)) < 0) {
oops = strerror(errno); /* Xn */
}
buf_use(chunk);
}
if (corrupt) {
warn(name, "Corrupt archive data");
}
if (oops) {
warn(name, oops);
} else if (sparse > 0 && (lseek(fd, (OFFSET) - 1, SEEK_CUR) < 0 /* Xn */
|| write(fd, "", 1) != 1)) {
warn(name, strerror(errno)); /* Xn */
}
return (fd);
}
/* outflush - flush the output buffer
*
* DESCRIPTION
*
* The output buffer is written, if there is anything in it, to the
* archive file.
*/
void outflush(void)
{
char *buf;
int got;
uint len;
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: void outflush() in buffer.c\n");
#endif
/* if (bufidx - buf > 0) */
for (buf = bufstart; len = (uint)(bufidx - buf);) {
#ifdef _POSIX_SOURCE /* 7/3/90-JPB */
if ((got = write(archivefd, buf, MIN(len, blocksize))) > 0)
buf += got;
if (got <= 0 || got != (int)(MIN(len, blocksize)))
next(AR_WRITE);
#else
if ((got = write(archivefd, buf, MIN(len, blocksize))) > 0) {
buf += got;
} else if (got <= 0) {
next(AR_WRITE);
}
#endif
}
bufend = (bufidx = bufstart) + blocksize;
}
/* ar_read - fill the archive buffer
*
* DESCRIPTION
*
* Remembers mid-buffer read failures and reports them the next time
* through. Replaces unreadable data with null characters. Resets
* the buffer pointers as appropriate.
*
* RETURNS
*
* Returns zero with valid data, -1 otherwise.
*/
int ar_read(void)
{
int got;
static int failed = 0; /* Xn */
bufend = bufidx = bufstart;
/*
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: int ar_read() in buffer.c\n");
#endif
*/
if (!failed) {
if (areof) {
if (total == 0) {
fatal("No input");
} else {
next(AR_READ);
}
}
while (!failed && !areof && (uint)(bufstart + blocksize - bufend) >= blocksize) {
if ((got = read(archivefd, bufend, (unsigned int) blocksize)) > 0) {
bufend += got;
#ifdef _POSIX_SOURCE /* 7/3/90-JPB */
if ((uint)got != blocksize)
++areof;
#endif
} else if (got < 0) {
failed = -1;
warnarch(strerror(errno), (OFFSET) 0 - (bufend - bufidx)); /* Xn */
} else {
++areof;
}
}
}
if (failed && bufend == bufstart) {
failed = 0;
for (got = 0; (uint)got < blocksize; ++got) {
*bufend++ = '\0';
}
return (-1);
}
return (0);
}
/* ar_write - write a filesystem block
*
* DESCRIPTION
*
* Writes len bytes of data data from the specified buffer to the
* specified file. Seeks past sparse blocks.
*
* PARAMETERS
*
* int fd - file to write to
* char *buf - buffer to get data from
* uint len - number of bytes to transfer
*
* RETURNS
*
* Returns 0 if the block was written, the given length for a sparse
* block or -1 if unsuccessful.
*/
int ar_write(int fd, char *buf, uint len)
{
char *bidx;
char *bend;
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: int ar_write() in buffer.c\n");
#endif
bend = (bidx = buf) + len;
while (bidx < bend) {
if (*bidx++) {
return (write(fd, buf, len) == (ssize_t)(len ? 0 : -1));
}
}
return (lseek(fd, (OFFSET) len, SEEK_CUR) < 0 ? -1 : len); /* Xn */
}
/* buf_pad - pad the archive buffer
*
* DESCRIPTION
*
* Buf_pad writes len zero bytes to the archive buffer in order to
* pad it.
*
* PARAMETERS
*
* OFFSET pad - number of zero bytes to pad
*
*/
static void buf_pad(OFFSET pad)
{
int idx;
int have;
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: static void buf_pad() in buffer.c\n");
#endif
while (pad) {
if ((have = (int)(bufend - bufidx)) > pad) {
have = pad;
}
for (idx = 0; idx < have; ++idx) {
*bufidx++ = '\0';
}
total += have;
pad -= have;
if (bufend - bufidx == 0) {
outflush();
}
}
}
/* buf_use - allocate buffer space
*
* DESCRIPTION
*
* Buf_use marks space in the buffer as being used; advancing both the
* buffer index (bufidx) and the total byte count (total).
*
* PARAMETERS
*
* uint len - Amount of space to allocate in the buffer
*/
static void buf_use(uint len)
{
bufidx += len;
total += len;
}
/* buf_in_avail - index available input data within the buffer
*
* DESCRIPTION
*
* Buf_in_avail fills the archive buffer, and points the bufp
* parameter at the start of the data. The lenp parameter is
* modified to contain the number of bytes which were read.
*
* PARAMETERS
*
* char **bufp - pointer to the buffer to read data into
* uint *lenp - pointer to the number of bytes which were read
* (returned to the caller)
*
* RETURNS
*
* Stores a pointer to the data and its length in given locations.
* Returns zero with valid data, -1 if unreadable portions were
* replaced with nulls.
*
* ERRORS
*
* If an error occurs in ar_read, the error code is returned to the
* calling function.
*
*/
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: static void buf_use() in buffer.c\n");
#endif
static int buf_in_avail(char **bufp, uint *lenp)
{
uint have;
int corrupt = 0;
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: static int buf_in_avail() in buffer.c\n");
#endif
while ((have = (uint)(bufend - bufidx)) == 0) {
corrupt |= ar_read();
}
*bufp = bufidx;
*lenp = have;
return (corrupt);
}
/* buf_out_avail - index buffer space for archive output
*
* DESCRIPTION
*
* Stores a buffer pointer at a given location. Returns the number
* of bytes available.
*
* PARAMETERS
*
* char **bufp - pointer to the buffer which is to be stored
*
* RETURNS
*
* The number of bytes which are available in the buffer.
*
*/
static uint buf_out_avail(char **bufp)
{
int have;
#ifdef DF_TRACE_DEBUG
printf("DF_TRACE_DEBUG: static uint buf_out_avail() in buffer.c\n");
#endif
//printf("bufend X%dX bufidx X%dX\n", bufend, bufidx);
if (bufend - bufidx < 0) {
//puts("buf_out_avail fata");
fatal("Buffer overflow in buf_out_avail\n"); /* Xn */
}
if ((have = (int)(bufend - bufidx)) == 0) {
outflush();
}
*bufp = bufidx;
//printf("buf_out_avail rets X%dX\n", have);
return (have);
}