📄 tar.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[] = "@(#)tar.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 <string.h>#include <stdio.h>#include <ctype.h>#include <unistd.h>#include <stdlib.h>#include "pax.h"#include "extern.h"#include "tar.h"/* * Routines for reading, writing and header identify of various versions of tar */static u_long tar_chksm __P((register char *, register int));static char *name_split __P((register char *, register int));static int ul_oct __P((u_long, register char *, register int, int));#ifndef NET2_STATstatic int uqd_oct __P((u_quad_t, register char *, register int, int));#endif/* * Routines common to all versions of tar */static int tar_nodir; /* do not write dirs under old tar *//* * tar_endwr() * add the tar trailer of two null blocks * Return: * 0 if ok, -1 otherwise (what wr_skip returns) */#if __STDC__inttar_endwr(void)#elseinttar_endwr()#endif{ return(wr_skip((off_t)(NULLCNT*BLKMULT)));}/* * tar_endrd() * no cleanup needed here, just return size of trailer (for append) * Return: * size of trailer (2 * BLKMULT) */#if __STDC__off_ttar_endrd(void)#elseoff_ttar_endrd()#endif{ return((off_t)(NULLCNT*BLKMULT));}/* * tar_trail() * Called to determine if a header block is a valid trailer. We are passed * the block, the in_sync flag (which tells us we are in resync mode; * looking for a valid header), and cnt (which starts at zero) which is * used to count the number of empty blocks we have seen so far. * Return: * 0 if a valid trailer, -1 if not a valid trailer, or 1 if the block * could never contain a header. */#if __STDC__inttar_trail(register char *buf, register int in_resync, register int *cnt)#elseinttar_trail(buf, in_resync, cnt) register char *buf; register int in_resync; register int *cnt;#endif{ register int i; /* * look for all zero, trailer is two consecutive blocks of zero */ for (i = 0; i < BLKMULT; ++i) { if (buf[i] != '\0') break; } /* * if not all zero it is not a trailer, but MIGHT be a header. */ if (i != BLKMULT) return(-1); /* * When given a zero block, we must be careful! * If we are not in resync mode, check for the trailer. Have to watch * out that we do not mis-identify file data as the trailer, so we do * NOT try to id a trailer during resync mode. During resync mode we * might as well throw this block out since a valid header can NEVER be * a block of all 0 (we must have a valid file name). */ if (!in_resync && (++*cnt >= NULLCNT)) return(0); return(1);}/* * ul_oct() * convert an unsigned long to an octal string. many oddball field * termination characters are used by the various versions of tar in the * different fields. term selects which kind to use. str is BLANK padded * at the front to len. we are unable to use only one format as many old * tar readers are very cranky about this. * Return: * 0 if the number fit into the string, -1 otherwise */#if __STDC__static intul_oct(u_long val, register char *str, register int len, int term)#elsestatic intul_oct(val, str, len, term) u_long val; register char *str; register int len; int term;#endif{ register char *pt; /* * term selects the appropriate character(s) for the end of the string */ pt = str + len - 1; switch(term) { case 3: *pt-- = '\0'; break; case 2: *pt-- = ' '; *pt-- = '\0'; break; case 1: *pt-- = ' '; break; case 0: default: *pt-- = '\0'; *pt-- = ' '; break; } /* * convert and blank pad if there is space */ while (pt >= str) { *pt-- = '0' + (char)(val & 0x7); if ((val = val >> 3) == (u_long)0) break; } while (pt >= str) *pt-- = ' '; if (val != (u_long)0) return(-1); return(0);}#ifndef NET2_STAT/* * uqd_oct() * convert an u_quad_t to an octal string. one of many oddball field * termination characters are used by the various versions of tar in the * different fields. term selects which kind to use. str is BLANK padded * at the front to len. we are unable to use only one format as many old * tar readers are very cranky about this. * Return: * 0 if the number fit into the string, -1 otherwise */#if __STDC__static intuqd_oct(u_quad_t val, register char *str, register int len, int term)#elsestatic intuqd_oct(val, str, len, term) u_quad_t val; register char *str; register int len; int term;#endif{ register char *pt; /* * term selects the appropriate character(s) for the end of the string */ pt = str + len - 1; switch(term) { case 3: *pt-- = '\0'; break; case 2: *pt-- = ' '; *pt-- = '\0'; break; case 1: *pt-- = ' '; break; case 0: default: *pt-- = '\0'; *pt-- = ' '; break; } /* * convert and blank pad if there is space */ while (pt >= str) { *pt-- = '0' + (char)(val & 0x7); if ((val = val >> 3) == 0) break; } while (pt >= str) *pt-- = ' '; if (val != (u_quad_t)0) return(-1); return(0);}#endif/* * tar_chksm() * calculate the checksum for a tar block counting the checksum field as * all blanks (BLNKSUM is that value pre-calculated, the sume of 8 blanks). * NOTE: we use len to short circuit summing 0's on write since we ALWAYS * pad headers with 0. * Return: * unsigned long checksum */#if __STDC__static u_longtar_chksm(register char *blk, register int len)#elsestatic u_longtar_chksm(blk, len) register char *blk; register int len;#endif{ register char *stop; register char *pt; u_long chksm = BLNKSUM; /* inital value is checksum field sum */ /* * add the part of the block before the checksum field */ pt = blk; stop = blk + CHK_OFFSET; while (pt < stop) chksm += (u_long)(*pt++ & 0xff); /* * move past the checksum field and keep going, spec counts the * checksum field as the sum of 8 blanks (which is pre-computed as * BLNKSUM). * ASSUMED: len is greater than CHK_OFFSET. (len is where our 0 padding * starts, no point in summing zero's) */ pt += CHK_LEN; stop = blk + len; while (pt < stop) chksm += (u_long)(*pt++ & 0xff); return(chksm);}/* * Routines for old BSD style tar (also made portable to sysV tar) *//* * tar_id() * determine if a block given to us is a valid tar header (and not a USTAR * header). We have to be on the lookout for those pesky blocks of all * zero's. * Return: * 0 if a tar header, -1 otherwise */#if __STDC__inttar_id(register char *blk, int size)#elseinttar_id(blk, size) register char *blk; int size;#endif{ register HD_TAR *hd; register HD_USTAR *uhd; if (size < BLKMULT) return(-1); hd = (HD_TAR *)blk; uhd = (HD_USTAR *)blk; /* * check for block of zero's first, a simple and fast test, then make * sure this is not a ustar header by looking for the ustar magic * cookie. We should use TMAGLEN, but some USTAR archive programs are * wrong and create archives missing the \0. Last we check the * checksum. If this is ok we have to assume it is a valid header. */ if (hd->name[0] == '\0') return(-1); if (strncmp(uhd->magic, TMAGIC, TMAGLEN - 1) == 0) return(-1); if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT)) return(-1); return(0);}/* * tar_opt() * handle tar format specific -o options * Return: * 0 if ok -1 otherwise */#if __STDC__inttar_opt(void)#elseinttar_opt()#endif{ OPLIST *opt; while ((opt = opt_next()) != NULL) { if (strcmp(opt->name, TAR_OPTION) || strcmp(opt->value, TAR_NODIR)) { warn(1, "Unknown tar format -o option/value pair %s=%s", opt->name, opt->value); warn(1,"%s=%s is the only supported tar format option", TAR_OPTION, TAR_NODIR); return(-1); } /* * we only support one option, and only when writing */ if ((act != APPND) && (act != ARCHIVE)) { warn(1, "%s=%s is only supported when writing.", opt->name, opt->value); return(-1); } tar_nodir = 1; } return(0);}/* * tar_rd() * extract the values out of block already determined to be a tar header. * store the values in the ARCHD parameter. * Return: * 0 */#if __STDC__inttar_rd(register ARCHD *arcn, register char *buf)#elseinttar_rd(arcn, buf) register ARCHD *arcn; register char *buf;#endif{ register HD_TAR *hd; register char *pt; /* * we only get proper sized buffers passed to us */ if (tar_id(buf, BLKMULT) < 0) return(-1); arcn->org_name = arcn->name; arcn->sb.st_nlink = 1; arcn->pat = NULL; /* * copy out the name and values in the stat buffer */ hd = (HD_TAR *)buf; arcn->nlen = l_strncpy(arcn->name, hd->name, sizeof(hd->name)); arcn->name[arcn->nlen] = '\0'; arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode,sizeof(hd->mode),OCT) & 0xfff); arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); arcn->sb.st_size = (size_t)asc_ul(hd->size, sizeof(hd->size), OCT); arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT); arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; /* * have to look at the last character, it may be a '/' and that is used * to encode this as a directory */ pt = &(arcn->name[arcn->nlen - 1]); arcn->pad = 0; arcn->skip = 0; switch(hd->linkflag) { case SYMTYPE: /* * symbolic link, need to get the link name and set the type in * the st_mode so -v printing will look correct. */ arcn->type = PAX_SLK; arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname, sizeof(hd->linkname)); arcn->ln_name[arcn->ln_nlen] = '\0'; arcn->sb.st_mode |= S_IFLNK; break; case LNKTYPE: /* * hard link, need to get the link name, set the type in the * st_mode and st_nlink so -v printing will look better. */ arcn->type = PAX_HLK; arcn->sb.st_nlink = 2; arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname, sizeof(hd->linkname)); arcn->ln_name[arcn->ln_nlen] = '\0'; /* * no idea of what type this thing really points at, but * we set something for printing only. */ arcn->sb.st_mode |= S_IFREG; break; case AREGTYPE: case REGTYPE: default: /* * If we have a trailing / this is a directory and NOT a file. */ arcn->ln_name[0] = '\0'; arcn->ln_nlen = 0; if (*pt == '/') { /* * it is a directory, set the mode for -v printing */ arcn->type = PAX_DIR; arcn->sb.st_mode |= S_IFDIR; arcn->sb.st_nlink = 2; } else { /* * have a file that will be followed by data. Set the * skip value to the size field and caluculate the size * of the padding. */ arcn->type = PAX_REG; arcn->sb.st_mode |= S_IFREG; arcn->pad = TAR_PAD(arcn->sb.st_size); arcn->skip = arcn->sb.st_size; } break; } /* * strip off any trailing slash. */ if (*pt == '/') { *pt = '\0'; --arcn->nlen; } return(0);}/* * tar_wr() * write a tar header for the file specified in the ARCHD to the archive. * Have to check for file types that cannot be stored and file names that * are too long. Be careful of the term (last arg) to ul_oct, each field * of tar has it own spec for the termination character(s). * ASSUMED: space after header in header block is zero filled * Return: * 0 if file has data to be written after the header, 1 if file has NO * data to write after the header, -1 if archive write failed */#if __STDC__inttar_wr(register ARCHD *arcn)#elseinttar_wr(arcn) register ARCHD *arcn;#endif{ register HD_TAR *hd; int len; char hdblk[sizeof(HD_TAR)]; /* * check for those file system types which tar cannot store */ switch(arcn->type) { case PAX_DIR: /* * user asked that dirs not be written to the archive */ if (tar_nodir) return(1); break; case PAX_CHR: warn(1, "Tar cannot archive a character device %s", arcn->org_name); return(1); case PAX_BLK: warn(1, "Tar cannot archive a block device %s", arcn->org_name); return(1); case PAX_SCK: warn(1, "Tar cannot archive a socket %s", arcn->org_name); return(1); case PAX_FIF: warn(1, "Tar cannot archive a fifo %s", arcn->org_name); return(1); case PAX_SLK: case PAX_HLK: case PAX_HRG: if (arcn->ln_nlen > sizeof(hd->linkname)) { warn(1,"Link name too long for tar %s", arcn->ln_name); return(1); } break; case PAX_REG: case PAX_CTG:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -