📄 buf_subs.c
字号:
/*- * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)buf_subs.c 8.2 (Berkeley) 4/18/94";#endif /* not lint */#include <sys/types.h>#include <sys/time.h>#include <sys/stat.h>#include <sys/param.h>#include <stdio.h>#include <ctype.h>#include <errno.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include "pax.h"#include "extern.h"/* * routines which implement archive and file buffering */#define MINFBSZ 512 /* default block size for hole detect */#define MAXFLT 10 /* default media read error limit *//* * Need to change bufmem to dynamic allocation when the upper * limit on blocking size is removed (though that will violate pax spec) * MAXBLK define and tests will also need to be updated. */static char bufmem[MAXBLK+BLKMULT]; /* i/o buffer + pushback id space */static char *buf; /* normal start of i/o buffer */static char *bufend; /* end or last char in i/o buffer */static char *bufpt; /* read/write point in i/o buffer */int blksz = MAXBLK; /* block input/output size in bytes */int wrblksz; /* user spec output size in bytes */int maxflt = MAXFLT; /* MAX consecutive media errors */int rdblksz; /* first read blksize (tapes only) */off_t wrlimit; /* # of bytes written per archive vol */off_t wrcnt; /* # of bytes written on current vol */off_t rdcnt; /* # of bytes read on current vol *//* * wr_start() * set up the buffering system to operate in a write mode * Return: * 0 if ok, -1 if the user specified write block size violates pax spec */#if __STDC__intwr_start(void)#elseintwr_start()#endif{ buf = &(bufmem[BLKMULT]); /* * Check to make sure the write block size meets pax specs. If the user * does not specify a blocksize, we use the format default blocksize. * We must be picky on writes, so we do not allow the user to create an * archive that might be hard to read elsewhere. If all ok, we then * open the first archive volume */ if (!wrblksz) wrblksz = frmt->bsz; if (wrblksz > MAXBLK) { warn(1, "Write block size of %d too large, maximium is: %d", wrblksz, MAXBLK); return(-1); } if (wrblksz % BLKMULT) { warn(1, "Write block size of %d is not a %d byte multiple", wrblksz, BLKMULT); return(-1); } /* * we only allow wrblksz to be used with all archive operations */ blksz = rdblksz = wrblksz; if ((ar_open(arcname) < 0) && (ar_next() < 0)) return(-1); wrcnt = 0; bufend = buf + wrblksz; bufpt = buf; return(0);}/* * rd_start() * set up buffering system to read an archive * Return: * 0 if ok, -1 otherwise */#if __STDC__intrd_start(void)#elseintrd_start()#endif{ /* * leave space for the header pushback (see get_arc()). If we are * going to append and user specified a write block size, check it * right away */ buf = &(bufmem[BLKMULT]); if ((act == APPND) && wrblksz) { if (wrblksz > MAXBLK) { warn(1,"Write block size %d too large, maximium is: %d", wrblksz, MAXBLK); return(-1); } if (wrblksz % BLKMULT) { warn(1, "Write block size %d is not a %d byte multiple", wrblksz, BLKMULT); return(-1); } } /* * open the archive */ if ((ar_open(arcname) < 0) && (ar_next() < 0)) return(-1); bufend = buf + rdblksz; bufpt = bufend; rdcnt = 0; return(0);}/* * cp_start() * set up buffer system for copying within the file system */#if __STDC__voidcp_start(void)#elsevoidcp_start()#endif{ buf = &(bufmem[BLKMULT]); rdblksz = blksz = MAXBLK;}/* * appnd_start() * Set up the buffering system to append new members to an archive that * was just read. The last block(s) of an archive may contain a format * specific trailer. To append a new member, this trailer has to be * removed from the archive. The first byte of the trailer is replaced by * the start of the header of the first file added to the archive. The * format specific end read function tells us how many bytes to move * backwards in the archive to be positioned BEFORE the trailer. Two * different postions have to be adjusted, the O.S. file offset (e.g. the * position of the tape head) and the write point within the data we have * stored in the read (soon to become write) buffer. We may have to move * back several records (the number depends on the size of the archive * record and the size of the format trailer) to read up the record where * the first byte of the trailer is recorded. Trailers may span (and * overlap) record boundries. * We first calculate which record has the first byte of the trailer. We * move the OS file offset back to the start of this record and read it * up. We set the buffer write pointer to be at this byte (the byte where * the trailer starts). We then move the OS file pointer back to the * start of this record so a flush of this buffer will replace the record * in the archive. * A major problem is rewriting this last record. For archives stored * on disk files, this is trival. However, many devices are really picky * about the conditions under which they will allow a write to occur. * Often devices restrict the conditions where writes can be made writes, * so it may not be feasable to append archives stored on all types of * devices. * Return: * 0 for success, -1 for failure */#if __STDC__intappnd_start(off_t skcnt)#elseintappnd_start(skcnt) off_t skcnt;#endif{ register int res; off_t cnt; if (exit_val != 0) { warn(0, "Cannot append to an archive that may have flaws."); return(-1); } /* * if the user did not specify a write blocksize, inherit the size used * in the last archive volume read. (If a is set we still use rdblksz * until next volume, cannot shift sizes within a single volume). */ if (!wrblksz) wrblksz = blksz = rdblksz; else blksz = rdblksz; /* * make sure that this volume allows appends */ if (ar_app_ok() < 0) return(-1); /* * Calculate bytes to move back and move in front of record where we * need to start writing from. Remember we have to add in any padding * that might be in the buffer after the trailer in the last block. We * travel skcnt + padding ROUNDED UP to blksize. */ skcnt += bufend - bufpt; if ((cnt = (skcnt/blksz) * blksz) < skcnt) cnt += blksz; if (ar_rev((off_t)cnt) < 0) goto out; /* * We may have gone too far if there is valid data in the block we are * now in front of, read up the block and position the pointer after * the valid data. */ if ((cnt -= skcnt) > 0) { /* * watch out for stupid tape drives. ar_rev() will set rdblksz * to be real physical blocksize so we must loop until we get * the old rdblksz (now in blksz). If ar_rev() fouls up the * determination of the physical block size, we will fail. */ bufpt = buf; bufend = buf + blksz; while (bufpt < bufend) { if ((res = ar_read(bufpt, rdblksz)) <= 0) goto out; bufpt += res; } if (ar_rev((off_t)(bufpt - buf)) < 0) goto out; bufpt = buf + cnt; bufend = buf + blksz; } else { /* * buffer is empty */ bufend = buf + blksz; bufpt = buf; } rdblksz = blksz; rdcnt -= skcnt; wrcnt = 0; /* * At this point we are ready to write. If the device requires special * handling to write at a point were previously recorded data resides, * that is handled in ar_set_wr(). From now on we operate under normal * ARCHIVE mode (write) conditions */ if (ar_set_wr() < 0) return(-1); act = ARCHIVE; return(0); out: warn(1, "Unable to rewrite archive trailer, cannot append."); return(-1);} /* * rd_sync() * A read error occurred on this archive volume. Resync the buffer and * try to reset the device (if possible) so we can continue to read. Keep * trying to do this until we get a valid read, or we reach the limit on * consecutive read faults (at which point we give up). The user can * adjust the read error limit through a command line option. * Returns: * 0 on success, and -1 on failure */#if __STDC__intrd_sync(void)#elseintrd_sync()#endif{ register int errcnt = 0; register int res; /* * if the user says bail out on first fault, we are out of here... */ if (maxflt == 0) return(-1); if (act == APPND) { warn(1, "Unable to append when there are archive read errors."); return(-1); } /* * poke at device and try to get past media error */ if (ar_rdsync() < 0) { if (ar_next() < 0) return(-1); else rdcnt = 0; } for (;;) { if ((res = ar_read(buf, blksz)) > 0) { /* * All right! got some data, fill that buffer */ bufpt = buf; bufend = buf + res; rdcnt += res; return(0); } /* * Oh well, yet another failed read... * if error limit reached, ditch. o.w. poke device to move past * bad media and try again. if media is badly damaged, we ask * the poor (and upset user at this point) for the next archive * volume. remember the goal on reads is to get the most we * can extract out of the archive. */ if ((maxflt > 0) && (++errcnt > maxflt)) warn(0,"Archive read error limit (%d) reached",maxflt); else if (ar_rdsync() == 0) continue; if (ar_next() < 0) break; rdcnt = 0; errcnt = 0; } return(-1);}/* * pback() * push the data used during the archive id phase back into the I/O * buffer. This is required as we cannot be sure that the header does NOT * overlap a block boundry (as in the case we are trying to recover a * flawed archived). This was not designed to be used for any other * purpose. (What software engineering, HA!) * WARNING: do not even THINK of pback greater than BLKMULT, unless the * pback space is increased. */#if __STDC__voidpback(char *pt, int cnt)#elsevoidpback(pt, cnt) char *pt; int cnt;#endif{ bufpt -= cnt; bcopy(pt, bufpt, cnt); return;}/* * rd_skip() * skip foward in the archive during a archive read. Used to get quickly * past file data and padding for files the user did NOT select. * Return: * 0 if ok, -1 failure, and 1 when EOF on the archive volume was detected. */#if __STDC__intrd_skip(off_t skcnt)#elseintrd_skip(skcnt) off_t skcnt;#endif{ off_t res; off_t cnt; off_t skipped = 0; /* * consume what data we have in the buffer. If we have to move foward * whole records, we call the low level skip function to see if we can * move within the archive without doing the expensive reads on data we * do not want. */ if (skcnt == 0) return(0); res = MIN((bufend - bufpt), skcnt); bufpt += res; skcnt -= res; /* * if skcnt is now 0, then no additional i/o is needed */ if (skcnt == 0) return(0); /* * We have to read more, calculate complete and partial record reads * based on rdblksz. we skip over "cnt" complete records */ res = skcnt%rdblksz; cnt = (skcnt/rdblksz) * rdblksz; /* * if the skip fails, we will have to resync. ar_fow will tell us * how much it can skip over. We will have to read the rest. */ if (ar_fow(cnt, &skipped) < 0) return(-1); res += cnt - skipped; rdcnt += skipped; /* * what is left we have to read (which may be the whole thing if * ar_fow() told us the device can only read to skip records); */ while (res > 0L) { cnt = bufend - bufpt; /* * if the read fails, we will have to resync */ if ((cnt <= 0) && ((cnt = buf_fill()) < 0)) return(-1); if (cnt == 0) return(1); cnt = MIN(cnt, res); bufpt += cnt; res -= cnt; } return(0);}/* * wr_fin() * flush out any data (and pad if required) the last block. We always pad * with zero (even though we do not have to). Padding with 0 makes it a * lot easier to recover if the archive is damaged. zero paddding SHOULD * BE a requirement.... */#if __STDC__voidwr_fin(void)#elsevoidwr_fin()#endif{ if (bufpt > buf) { bzero(bufpt, bufend - bufpt); bufpt = bufend; (void)buf_flush(blksz); }}/* * wr_rdbuf() * fill the write buffer from data passed to it in a buffer (usually used * by format specific write routines to pass a file header). On failure we * punt. We do not allow the user to continue to write flawed archives. * We assume these headers are not very large (the memory copy we use is * a bit expensive). * Return: * 0 if buffer was filled ok, -1 o.w. (buffer flush failure) */#if __STDC__intwr_rdbuf(register char *out, register int outcnt)#elseintwr_rdbuf(out, outcnt) register char *out; register int outcnt;#endif{ register int cnt; /* * while there is data to copy copy into the write buffer. when the * write buffer fills, flush it to the archive and continue */ while (outcnt > 0) { cnt = bufend - bufpt; if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) return(-1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -