1
0
Files
irix-657m-src/eoe/cmd/cpio/cpio.c
2022-09-29 17:59:04 +03:00

4066 lines
110 KiB
C

/*
* |-----------------------------------------------------------|
* | Copyright (c) 1990 MIPS Computer Systems, Inc. |
* | All Rights Reserved |
* |-----------------------------------------------------------|
* | Restricted Rights Legend |
* | Use, duplication, or disclosure by the Government is |
* | subject to restrictions as set forth in |
* | subparagraph (c)(1)(ii) of the Rights in Technical |
* | Data and Computer Software Clause of DFARS 52.227-7013. |
* | MIPS Computer Systems, Inc. |
* | 950 DeGuigne Drive |
* | Sunnyvale, CA 94086 |
* |-----------------------------------------------------------|
*/
#ident "$Header: /proj/irix6.5.7m/isms/eoe/cmd/cpio/RCS/cpio.c,v 1.65 1999/08/10 19:55:52 tee Exp $"
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
/*******************************************************************
PROPRIETARY NOTICE (Combined)
This source code is unpublished proprietary information
constituting, or derived under license from AT&T's UNIX(r) System V.
In addition, portions of such source code were derived from Berkeley
4.3 BSD under license from the Regents of the University of
California.
Copyright Notice
Notice of copyright on this source code product does not indicate
publication.
(c) 1986,1987,1988,1989 Sun Microsystems, Inc
(c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
All rights reserved.
********************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
#include <sys/mtio.h>
#include <sys/tpsc.h>
#include <sys/resource.h>
#include <sys/param.h> /* for UID_NOBODY */
#include <utime.h>
#include <pwd.h>
#include <grp.h>
#include <signal.h>
#include <ctype.h>
#include <archives.h>
#include <limits.h>
#include <locale.h>
#include <malloc.h>
#include <alloca.h>
#include <pfmt.h>
#include "cpio.h"
#include "newstat.h"
#include "rmt.h"
#include "libgenIO.h"
#include <msgs/uxsgicore.h>
/* Here for now - until POSIX tar/cpio get sync'ed up */
#define NAMSPACE 101 /* The maximum length of the name field */
#define NAMSIZ (NAMSPACE - 1)
#define PREFIXSPACE 156 /* The maximum length of the prefix */
#define PREFIXSIZ (PREFIXSPACE - 1)
#define BIN_MAXUID 0xffff
#define CHR_MAXUID 0777777
#define USTAR_MAXUID 0777777
#define TAR_MAXUID 0777777
extern int g_read(int devtype, int fdes, char *buf, unsigned nbytes);
extern int g_write(int devtype, int fdes, char *buf, unsigned nbytes);
extern int g_init(int *devtype, int *fdes);
static void setup(int, char **);
static void file_in(void);
static void file_out(void);
static void file_pass(void);
static int gethdr(void);
static int getname(void);
static void scan4trail(void);
static void ioerror(int);
static int chgreel(int);
static void rstbuf(void);
static int matched(void);
static ulong cksum(char, int);
static void verbose(char *);
static void rstfiles(int);
static int missdir(char *);
static int creat_hdr(void);
static int creat_lnk(char *, char *);
static int creat_spec(void);
static int creat_tmp(char *);
static void swap(char *, int);
static void write_trail(void);
static void bflush(void);
static int bfill(void);
static int ckname(void);
static void data_in(int);
static void data_out(void);
static int openout(void);
static void data_pass(void);
static int read_hdr(int);
static ulong mklong(short []);
static void mkshort(short [], long);
static void setpasswd(char *);
static void set_tym(char *, time_t, time_t);
static void usage(void);
static void sigint(void);
static void msg(int, ...);
int isqic02(int);
static void write_hdr(void);
static void flush_lnks(void);
int chknomedia(int);
static char * align(int);
static void ckopts(long);
static void getpats(int, char **);
static void chkandfixaudio(int);
static
struct passwd *Curpw_p, /* Current password entry for -t option */
*Rpw_p, /* Password entry for -R option */
*dpasswd;
static
struct group *Curgr_p, /* Current group entry for -t option */
*dgroup;
/* Data structure for buffered I/O. */
static
struct buf_info {
char *b_base_p, /* Pointer to base of buffer */
*b_out_p, /* Position to take bytes from buffer at */
*b_in_p, /* Position to put bytes into buffer at */
*b_end_p; /* Pointer to end of buffer */
long b_cnt, /* Count of unprocessed bytes */
b_size; /* Size of buffer in bytes */
} Buffr;
/* Generic header format */
static
struct gen_hdr {
ino64_t g_ino; /* Inode number of file - expanded for XFS */
ulong g_magic, /* Magic number field */
g_mode, /* Mode of file */
g_uid, /* Uid of file */
g_gid, /* Gid of file */
g_nlink, /* Number of links; also flags bad header for cpio */
g_mtime; /* Modification time */
off64_t g_filesz; /* Length of file - expanded for XFS */
off_t g_filesz2; /* Remainder of file modulo 2**32 - created for XFS */
ulong g_dev, /* File system of file */
g_rdev, /* Major/minor numbers of special files */
g_namesz, /* Length of filename */
g_cksum; /* Checksum of file */
char g_gname[32],
g_uname[32],
g_version[2],
g_tmagic[6],
g_typeflag;
char *g_tname,
*g_prefix,
*g_nam_p; /* Filename */
} Gen, *G_p;
/* Data structure for handling multiply-linked files */
static
char prebuf[PREFIXSPACE],
nambuf[PATH_MAX],
fullnam[HNAMLEN];
#define NLNKHASH 8
#define HASHLNK(x) ((x) & (NLNKHASH-1))
static
struct Lnk {
short L_cnt, /* Number of links encountered */
L_data; /* Data has been encountered if 1 */
struct gen_hdr L_gen; /* gen_hdr information for this file */
struct Lnk *L_nxt_p, /* Next file in list */
*L_bck_p, /* Previous file in list */
*L_lnk_p; /* Next link for this file */
} Lnk_hd[NLNKHASH];
static struct Lnk * add_lnk(struct Lnk **);
static void reclaim( struct Lnk *);
static struct Lnk *find_lnk(void);
static
struct hdr_cpio Hdr;
static
struct stat ArchSt; /* stat(2) information of the archive */
struct stat64 SrcSt, /* stat64(2) information of source file */
DesSt; /* stat64(2) of destination file */
/*
* bin_mag: Used to validate a binary magic number,
* by combining to bytes into an unsigned short.
*/
static
union bin_mag{
unsigned char b_byte[2];
ushort b_half;
} Binmag;
static
union tblock *Thdr_p; /* TAR header pointer */
/*
* swpbuf: Used in swap() to swap bytes within a halfword,
* halfwords within a word, or to reverse the order of the
* bytes within a word. Also used in mklong() and mkshort().
*/
static
union swpbuf {
unsigned char s_byte[4];
ushort s_half[2];
ulong s_word;
} *Swp_p;
static
char Adir, /* Flags object as a directory */
Aspec, /* Flags object as a special file */
Time[50], /* Array to hold date and time */
Ttyname[] = "/dev/tty", /* Controlling console */
T_lname[PATH_MAX], /* Array to hold links name for tar */
*Buf_p, /* Buffer for file system I/O */
*Empty, /* Empty block for TARTYP headers */
*Full_p, /* Pointer to full pathname */
*Efil_p, /* -E pattern file string */
*Eom_p = "Change to part %d and press RETURN key. [q] ",
*Eom_pid = ":32",
*Fullnam_p, /* Full pathname */
*Hdr_p, /* -H header type string */
*IOfil_p, /* -I/-O input/output archive string */
*Lnkend_p, /* Pointer to end of Lnknam_p */
*Lnknam_p, /* Buffer for linking files with -p option */
*Nam_p, /* Array to hold filename */
*Own_p, /* New owner login id string */
*Renam_p, /* Buffer for renaming files */
*Symlnk_p, /* Buffer for holding symbolic link name */
*Over_p, /* Holds temporary filename when overwriting */
*cmpbuf, /* buffer to read file into with Compareflag */
**Pat_pp = 0; /* Pattern strings */
static
int Append = 0, /* Flag set while searching to end of archive */
Archive, /* File descriptor of the archive */
Buf_error = 0, /* I/O error occured during buffer fill */
Def_mode = 0777,/* Default file/directory protection modes */
Device, /* Device type being accessed (used with libgenIO) */
Error_cnt = 0, /* Cumulative count of I/O errors */
Finished = 1, /* Indicates that a file transfer has completed */
Fix_mode = 0, /* Fix the mode on a dir (wrong mode from missdir?) */
Hdrsz = ASCSZ, /* Fixed length portion of the header */
Hdr_type, /* Flag to indicate type of header selected */
Ifile, /* File des. of file being archived */
Ofile, /* File des. of file being extracted from archive */
Onecopy = 0, /* Flags old vs. new link handling */
Pad_val = 0, /* Indicates the number of bytes to pad (if any) */
Volcnt = 1, /* Number of archive volumes processed */
Verbcnt = 0, /* Count of number of dots '.' output */
Eomflag = 0,
Dflag = 0, /* Special flag for libpkg - not documented */
Kflag = 0, /* Don't ignore big files (required for xfs support)*/
Holeflag = 0, /* Deal with holey (xfs) files better by skipping hole */
portwarn, /* have we warned yet that the archive will be non-portable? */
Compareflag = 0; /* Test for differences (compare archive against filesystem) */
mode_t Orig_umask; /* Inherited umask */
static
gid_t Lastgid = -1; /* Used with -t & -v to record current gid */
static
uid_t Lastuid = -1; /* Used with -t & -v to record current uid */
static
rlim64_t Max_filesz; /* Maximum file size from getrlimit64(2) */
static
long Args, /* Mask of selected options */
Max_namesz = APATH, /* Maximum size of pathnames/filenames */
SBlocks; /* Cumulative char count for short reads */
long long Blocks; /* Number of full blocks transferred */
static
int Bufsize = BUFSZ; /* Default block size */
static
FILE *Ef_p, /* File pointer of pattern input file */
*Err_p = stderr, /* File pointer for error reporting */
*Out_p = stdout, /* File pointer for non-archive output */
*Rtty_p, /* Input file pointer for interactive rename */
*Wtty_p; /* Output file pointer for interactive rename */
static
ushort Ftype = S_IFMT; /* File type mask */
static
uid_t Uid; /* Uid of invoker */
static const char
mutex[] = "-%c and -%c are mutually exclusive.",
mutexid[] = ":33",
badaccess[] = "Cannot access \"%s\"",
badaccessid[] = ":34",
badaccarch[] = "Cannot access the archive",
badaccarchid[] = ":35",
badcreate[] = "Cannot create \"%s\"",
badcreateid[] = ":36",
badcreatdir[] = "Cannot create directory for \"%s\"",
badcreatdirid[] = ":37",
badfollow[] = "Cannot follow \"%s\"",
badfollowid[] = ":38",
badread[] = "Read error in \"%s\"",
badreadid[] = ":39",
badreadsym[] = "Cannot read symbolic link \"%s\"",
badreadsymid[] = ":40",
badreadtty[] = "Cannot read tty.",
badreadttyid[] = ":41",
badreminc[] = "Cannot remove incomplete \"%s\"",
badremincid[] = ":42",
badremtmp[] = "Cannot remove temp file \"%s\"",
badremtmpid[] = ":43",
badwrite[] = "Write error in \"%s\"",
badwriteid[] = ":44",
badinit[] = "Error during initialization",
badinitid[] = ":45",
sameage[] = "Existing \"%s\" same age or newer",
sameageid[] = ":46",
badcase[] = "Impossible case.",
badcaseid[] = ":47",
badhdr[] = "Impossible header type.",
badhdrid[] = ":48",
nomem[] = "Out of memory",
nomemid[] = ":49",
badappend[] = "Cannot append to this archive",
badappendid[] = ":50",
badchmod[] = "chmod() failed on \"%s\"",
badchmodid[] = ":51",
badchown[] = "chown() failed on \"%s\"",
badchownid[] = ":52",
badpasswd[] = "Cannot get passwd information for %s",
badpasswdid[] = ":53",
badgroup[] = "Cannot get group information for %s",
badgroupid[] = ":54",
badorig[] = "Cannot recover original \"%s\"",
badorigid[] = ":55";
/*
* main: Call setup() to process options and perform initializations,
* and then select either copy in (-i), copy out (-o), or pass (-p) action.
*/
main(argc, argv)
char **argv;
int argc;
{
int gotname=0, doarch = 0;
int goodhdr = 0, oldgoodhdr = 0;
(void)setlocale(LC_ALL, "");
(void)setcat("uxcore.abi");
(void)setlabel("UX:cpio");
setup(argc, argv);
if (signal(SIGINT, sigint) == SIG_IGN)
(void)signal(SIGINT, SIG_IGN);
switch (Args & (OCi | OCo | OCp)) {
case OCi: /* COPY IN */
Hdr_type = NONE;
while ((goodhdr = gethdr()) != 0) {
file_in();
oldgoodhdr += goodhdr;
}
/* Do not count "extra" "read-ahead" buffered data */
if (Buffr.b_cnt > Bufsize)
Blocks -= (Buffr.b_cnt / Bufsize);
break;
case OCo: /* COPY OUT */
if (Args & OCA)
scan4trail();
while ((gotname = getname()) != 0) {
if (gotname == 1) {
file_out();
doarch++;
}
}
if (doarch > 0)
write_trail();
break;
case OCp: /* PASS */
while ((gotname = getname()) != 0)
if (gotname == 1)
file_pass();
break;
default:
msg(EXT, ":56", "Impossible action.");
}
Blocks = (long long)(Blocks * (double)(Bufsize/BUFSZ));
Blocks += (SBlocks + (BUFSZ-1)) / BUFSZ;
if (oldgoodhdr == 0 && (Args & OCi))
Blocks = 0;
msg(EPOST, ":57", "%lld blocks", Blocks);
if (Error_cnt == 1)
msg(EPOST, ":58", "1 error");
else if (Error_cnt > 1)
msg(EPOST, ":59", "%d error(s)", Error_cnt);
if (oldgoodhdr == 0 && (Args & OCi))
Error_cnt++;
return Error_cnt;
}
/*
* add_lnk: Add a linked file's header to the linked file data structure.
* Either adding it to the end of an existing sub-list or starting
* a new sub-list. Each sub-list saves the links to a given file.
*/
static struct Lnk *
add_lnk(struct Lnk **l_p)
{
register struct Lnk *t1l_p, *t2l_p, *t3l_p;
/*
* Look up the link. If the return value is null, we are creating
* a new link.
*/
t2l_p = find_lnk();
t1l_p = (struct Lnk *)malloc(sizeof(struct Lnk)); /* create new link t1l_p */
if (t1l_p == (struct Lnk *)NULL)
msg(EXT, nomemid, nomem);
t1l_p->L_lnk_p = (struct Lnk *)NULL;
t1l_p->L_gen = *G_p; /* structure copy */
t1l_p->L_gen.g_nam_p = (char *)malloc((unsigned int)G_p->g_namesz);
if (t1l_p->L_gen.g_nam_p == (char *)NULL)
msg(EXT, nomemid, nomem);
(void)strcpy(t1l_p->L_gen.g_nam_p, G_p->g_nam_p);
if (t2l_p == (struct Lnk *)NULL) { /* start new sub-list */
int hash = HASHLNK(G_p->g_ino);
t1l_p->L_nxt_p = &Lnk_hd[hash];
t1l_p->L_bck_p = Lnk_hd[hash].L_bck_p;
Lnk_hd[hash].L_bck_p = t1l_p->L_bck_p->L_nxt_p = t1l_p;
t1l_p->L_lnk_p = (struct Lnk *)NULL;
t1l_p->L_cnt = 1;
t1l_p->L_data = Onecopy ? 0 : 1;
t2l_p = t1l_p;
} else { /* add to existing sub-list */
t2l_p->L_cnt++;
t3l_p = t2l_p;
while (t3l_p->L_lnk_p != (struct Lnk *)NULL) {
t3l_p->L_gen.g_filesz = G_p->g_filesz;
t3l_p = t3l_p->L_lnk_p;
}
t3l_p->L_gen.g_filesz = G_p->g_filesz;
t3l_p->L_lnk_p = t1l_p;
}
*l_p = t2l_p;
return(t1l_p);
}
/*
* Return a pointer to a link structure if one exists corresponding to
* the current generic header.
*/
static struct Lnk *
find_lnk(void)
{
register struct Lnk *t2l_p;
long maj, min;
int hash = HASHLNK(G_p->g_ino);
t2l_p = Lnk_hd[hash].L_nxt_p;
while (t2l_p != &Lnk_hd[hash]) {
/* First check if the inode number are the same ... Then check if
* the major/minor number are the same - if true then hardlink */
if (t2l_p->L_gen.g_ino == G_p->g_ino) {
if (Hdr_type == BIN || Hdr_type == CHR ) {
maj = cpioMAJOR(t2l_p->L_gen.g_dev);
min = cpioMINOR(t2l_p->L_gen.g_dev);
if (maj == major(DEV32TO16(G_p->g_dev)) &&
min == minor(DEV32TO16(G_p->g_dev)) )
break; /* found */
}
else {
maj = (long)major(t2l_p->L_gen.g_dev);
min = (long)minor(t2l_p->L_gen.g_dev);
if( maj == major(G_p->g_dev) &&
min == minor(G_p->g_dev) )
break; /* found */
}
}
t2l_p = t2l_p->L_nxt_p;
}
if (t2l_p != &Lnk_hd[hash])
return t2l_p;
else
return (struct Lnk *)0;
}
/*
* align: Align a section of memory of size bytes on a page boundary and
* return the location. Used to increase I/O performance.
*/
static char *
align(int size)
{
register int pad;
int pagesize;
pagesize = getpagesize();
if ((pad = ((int)sbrk(0) & (pagesize-1))) > 0) {
pad = pagesize - pad;
if (sbrk(pad) == NULL)
return((char *)NULL);
}
return((char *)sbrk(size));
}
static int
bfill(void)
{
register int i = 0, rv;
static int eof = 0;
if (!Dflag) {
while ((Buffr.b_end_p - Buffr.b_in_p) >= Bufsize) {
errno = 0;
if ((rv = g_read(Device, Archive, Buffr.b_in_p, Bufsize)) < 0) {
if (((Buffr.b_end_p - Buffr.b_in_p) >= Bufsize) && (Eomflag == 0)) {
Eomflag = 1;
return(1);
}
if (errno == ENOSPC) {
(void)chgreel(INPUT);
continue;
} else if (Args & OCk) {
if (i++ > MX_SEEKS)
msg(EXT, ":60", "Cannot recover.");
if (rmtlseek(Archive, Bufsize, SEEK_REL) < 0)
msg(EXTN, ":61", "lseek() failed");
Error_cnt++;
Buf_error++;
rv = 0;
continue;
} else
ioerror(INPUT);
} /* (rv = g_read(Device, Archive ... */
Buffr.b_in_p += rv;
Buffr.b_cnt += (long)rv;
if (rv == Bufsize)
Blocks++;
else if (!rv) {
if (!eof) {
eof = 1;
break;
}
return(-1);
} else
SBlocks += rv;
} /* (Buffr.b_end_p - Buffr.b_in_p) <= Bufsize */
}
else {
errno = 0;
if ((rv = g_read(Device, Archive, Buffr.b_in_p, Bufsize)) < 0) {
return(-1);
} /* (rv = g_read(Device, Archive ... */
Buffr.b_in_p += rv;
Buffr.b_cnt += (long)rv;
if (rv == Bufsize)
Blocks++;
else if (!rv) {
if (!eof) {
eof = 1;
return(rv);
}
return(-1);
} else
SBlocks += rv;
}
return(rv);
}
/*
* bflush: Move wr_cnt bytes from data_p into the I/O buffer. When the
* I/O buffer is full, Flushbuf is set and the buffer is written out.
*/
static void
bflush(void)
{
register int rv;
while (Buffr.b_cnt >= Bufsize) {
errno = 0;
if ((rv = g_write(Device, Archive, Buffr.b_out_p, Bufsize)) < 0) {
if (errno == ENOSPC && !Dflag && Device != G_FILE )
rv = chgreel(OUTPUT);
else
ioerror(OUTPUT);
}
Buffr.b_out_p += rv;
Buffr.b_cnt -= (long)rv;
if (rv == Bufsize)
Blocks++;
else if (rv > 0)
SBlocks += rv;
}
rstbuf();
}
/*
* chgreel: Determine if end-of-medium has been reached. If it has,
* close the current medium and prompt the user for the next medium.
*/
static int
chgreel(int dir)
{
register int lastchar, tryagain, askagain, rv;
int tmpdev;
char str[APATH];
if (dir)
msg(EPOST, ":62", "\007End of medium on output.");
else
msg(EPOST, ":63", "\007End of medium on input.");
(void)rmtclose(Archive);
Volcnt++;
for (;;) {
if (Rtty_p == (FILE *)NULL)
if(!(Rtty_p = fopen(Ttyname, "r")))
msg(EXT, badreadttyid, badreadtty); /* tough; give up */
do { /* tryagain */
if (IOfil_p) {
do {
msg(EPOST, Eom_pid, Eom_p, Volcnt);
if (fgets(str, sizeof(str), Rtty_p) == (char *)NULL)
msg(EXT, badreadttyid, badreadtty);
askagain = 0;
switch (*str) {
case '\n':
(void)strcpy(str, IOfil_p);
break;
case 'q':
exit(Error_cnt);
default:
askagain = 1;
}
} while (askagain);
} else {
msg(EPOST, ":64", "To continue, type device/file name when ready.");
if (fgets(str, sizeof(str), Rtty_p) == (char *)NULL)
msg(EXT, badreadttyid, badreadtty);
lastchar = strlen(str) - 1;
if (*(str + lastchar) == '\n') /* remove '\n' */
*(str + lastchar) = '\0';
if (!*str)
exit(Error_cnt);
}
tryagain = 0;
if ((Archive = rmtopen(str, dir)) < 0) {
msg(ERRN, ":65", "Cannot open \"%s\"", str);
tryagain = 1;
}
} while (tryagain);
(void)g_init(&tmpdev, &Archive);
if (tmpdev != Device)
msg(EXT, ":66", "Cannot change media types in mid-stream.");
chkandfixaudio(Archive);
if (dir == INPUT)
break;
else { /* dir == OUTPUT */
errno = 0;
if ((rv = g_write(Device, Archive, Buffr.b_out_p, Bufsize)) == Bufsize)
break;
else
msg(ERR, ":67", "Cannot write on this medium, try another.");
}
} /* ;; */
Eomflag = 0;
return(rv);
}
/*
* ckname: Check filenames against user specified patterns,
* and/or ask the user for new name when -r is used.
*/
static int
ckname(void)
{
register int lastchar;
if (G_p->g_namesz > Max_namesz) {
msg(ERR, ":68", "Name exceeds maximum length - skipped.");
return(F_SKIP);
}
if (Pat_pp && !matched())
return(F_SKIP);
if ((Args & OCr) && !Adir) { /* rename interactively */
(void)pfmt(Wtty_p, MM_NOSTD, ":69:Rename \"%s\"? ", G_p->g_nam_p);
(void)fflush(Wtty_p);
if (fgets(Renam_p, Max_namesz, Rtty_p) == (char *)NULL)
msg(EXT, badreadttyid, badreadtty);
if (feof(Rtty_p))
exit(Error_cnt);
lastchar = strlen(Renam_p) - 1;
if (*(Renam_p + lastchar) == '\n') /* remove trailing '\n' */
*(Renam_p + lastchar) = '\0';
if (*Renam_p == '\0') {
msg(POST, ":70", "%s Skipped.", G_p->g_nam_p);
*G_p->g_nam_p = '\0';
return(F_SKIP);
} else if (strcmp(Renam_p, ".")) {
G_p->g_nam_p = Renam_p;
}
}
VERBOSE((Args & OCt) && !Compareflag && (G_p->g_filesz >= 0), G_p->g_nam_p);
if((Args & OCt) && !Compareflag)
return(F_SKIP);
return(F_EXTR);
}
/*
* ckopts: Check the validity of all command line options.
*/
static void
ckopts(long mask)
{
register int oflag;
register char *t_p;
register long errmsk;
if (mask & OCi)
errmsk = mask & INV_MSK4i;
else if (mask & OCo)
errmsk = mask & INV_MSK4o;
else if (mask & OCp)
errmsk = mask & INV_MSK4p;
else {
msg(ERR, ":71", "One of -i, -o or -p must be specified.");
errmsk = 0;
}
if (errmsk) /* if non-zero, invalid options were specified */
Error_cnt++;
if ((mask & OCa) && (mask & OCm))
msg(ERR, mutexid, mutex, 'a', 'm');
if (Holeflag) {
if(mask & OCc)
msg(ERR, mutexid, mutex, 'c', 'W');
if(mask & OCH)
msg(ERR, mutexid, mutex, 'H', 'W');
}
if ((mask & OCc) && (mask & OCH) &&
((strcmp("odc", Hdr_p) == 0) ||
(strcmp("tar", Hdr_p) == 0) || (strcmp("TAR", Hdr_p) == 0) ||
(strcmp("ustar", Hdr_p) == 0) || (strcmp("USTAR", Hdr_p) == 0)))
msg(ERR, mutexid, mutex, 'c', 'H');
if ((mask & OCc) && (mask & OC6))
msg(ERR, mutexid, mutex, 'c', '6');
if ((mask & OCv) && (mask & OCV))
msg(ERR, mutexid, mutex, 'v', 'V');
if ((mask & OCB) && (mask & OCC))
msg(ERR, mutexid, mutex, 'B', 'C');
if ((mask & OCH) && (mask & OC6))
msg(ERR, mutexid, mutex, 'H', '6');
if ((mask & OCM) && !((mask & OCI) || (mask & OCO)))
msg(ERR, ":72", "-M not meaningful without -O or -I.");
if ((mask & OCA) && !(mask & OCO))
msg(ERR, ":73", "-A requires the -O option.");
if (Kflag && !(mask & OCo))
msg(ERR, ":142", "-K used only with -o option.");
if (Kflag && ((mask & OCc) || (mask & OCH)))
msg(ERR, ":143", "-K used only in binary mode.");
if (Bufsize <= 0)
msg(ERR, ":74", "Illegal size given for -C option.");
if (mask & OCH) {
t_p = Hdr_p;
while (*t_p != NULL) {
if (isupper(*t_p))
*t_p = 'a' + (*t_p - 'A');
t_p++;
}
if (!strcmp("odc", Hdr_p)) {
Hdr_type = CHR;
Max_namesz = CPATH;
Onecopy = 0;
} else if (!strcmp("crc", Hdr_p)) {
Hdr_type = CRC;
Max_namesz = APATH;
Onecopy = 1;
} else if (!strcmp("tar", Hdr_p)) {
if(Args & OCo) {
Hdr_type = USTAR;
Max_namesz = HNAMLEN;
} else {
Hdr_type = TAR;
Max_namesz = TNAMLEN - 1;
}
Onecopy = 0;
} else if (!strcmp("ustar", Hdr_p)) {
Hdr_type = USTAR;
Max_namesz = HNAMLEN;
Onecopy = 0;
} else
msg(ERR, ":75", "Invalid header \"%s\" specified", Hdr_p);
}
if (mask & OCr) {
Rtty_p = fopen(Ttyname, "r");
Wtty_p = fopen(Ttyname, "w");
if (Rtty_p == (FILE *)NULL || Wtty_p == (FILE *)NULL)
msg(ERR, ":76", "Cannot rename, \"%s\" missing", Ttyname);
}
if ((mask & OCE) && (Ef_p = fopen(Efil_p, "r")) == (FILE *)NULL)
msg(ERR, ":77", "Cannot open \"%s\" to read patterns", Efil_p);
if ((mask & OCI) && (Archive = rmtopen(IOfil_p, O_RDONLY)) < 0)
msg(ERR, ":78", "Cannot open \"%s\" for input", IOfil_p);
if (mask & OCO) {
if (mask & OCA) {
if ((Archive = rmtopen(IOfil_p, O_RDWR)) < 0)
msg(ERR, ":79", "Cannot open \"%s\" for append", IOfil_p);
} else {
oflag = (O_WRONLY | O_CREAT | O_TRUNC);
if ((Archive = rmtopen(IOfil_p, oflag, 0666)) < 0)
msg(ERR, ":80", "Cannot open \"%s\" for output", IOfil_p);
}
}
chkandfixaudio(Archive);
if (mask & OCR) {
if (Uid != 0)
msg(ERR, ":81", "R option only valid for super-user.");
if ((Rpw_p = getpwnam(Own_p)) == (struct passwd *)NULL)
msg(ERR, ":82", "Unknown user id: %s", Own_p);
}
if ((mask & OCo) && !(mask & OCO))
Out_p = stderr;
}
/*
* cksum: Calculate the simple checksum of a file (CRC) or header
* (TARTYP (TAR and USTAR)). For -o and the CRC header, the file is opened and
* the checksum is calculated. For -i and the CRC header, the checksum
* is calculated as each block is transferred from the archive I/O buffer
* to the file system I/O buffer. The TARTYP (TAR and USTAR) headers calculate
* the simple checksum of the header (with the checksum field of the
* header initialized to all spaces (\040)).
*/
static ulong
cksum(char hdr, int byt_cnt)
{
register char *crc_p, *end_p;
register int cnt;
register off64_t checksum = 0LL, lcnt, have;
switch (hdr) {
case CRC:
if (Args & OCi) { /* do running checksum */
end_p = Buffr.b_out_p + byt_cnt;
for (crc_p = Buffr.b_out_p; crc_p < end_p; crc_p++)
checksum += (long)*crc_p;
break;
}
/* OCo - do checksum of file */
lcnt = G_p->g_filesz;
while (lcnt > 0) {
have = (lcnt < CPIOBSZ) ? lcnt : CPIOBSZ;
errno = 0;
if (read(Ifile, Buf_p, have) != have) {
msg(ERR, ":83", "Error computing checksum.");
checksum = -1L;
break;
}
end_p = Buf_p + have;
for (crc_p = Buf_p; crc_p < end_p; crc_p++)
checksum += (long)*crc_p;
lcnt -= have;
}
if (lseek(Ifile, 0, SEEK_ABS) < 0)
msg(ERRN, ":84", "Cannot reset file after checksum");
break;
case TARTYP: /* TAR and USTAR */
crc_p = Thdr_p->tbuf.t_cksum;
for (cnt = 0; cnt < TCRCLEN; cnt++) {
*crc_p = '\040';
crc_p++;
}
crc_p = (char *)Thdr_p;
for (cnt = 0; cnt < TARSZ; cnt++) {
checksum += (long)*crc_p;
crc_p++;
}
break;
default:
msg(EXT, badhdrid, badhdr);
} /* hdr */
return (ulong)checksum;
}
/*
* creat_hdr: Fill in the generic header structure with the specific
* header information based on the value of Hdr_type.
*/
static int
creat_hdr(void)
{
register ushort ftype;
static char filname[NAMSPACE];
static char prefix[PREFIXSPACE];
char *cp, *wholename;
int len, Alink;
ftype = (ushort)SrcSt.st_mode & Ftype;
Adir = (ftype == S_IFDIR);
Aspec = (ftype == S_IFBLK || ftype == S_IFCHR || ftype == S_IFIFO);
Alink = (ftype == S_IFLNK);
/* We want to check here if the file is really readable --
If we wait till later it's to late and cpio will have to
pad out the file to the expected length, with null contents.
Complain about types (like UDS that we can't backup/restore.
*/
if (!Adir && !Aspec && !Alink) {
if (access(Gen.g_nam_p, R_OK) < 0) {
msg(ERR, ":65", "Cannot open \"%s\"", Gen.g_nam_p);
return(0);
}
if(ftype != S_IFREG) {
msg(ERR, ":1018", "Cannot backup sockets or unknown file types: \"%s\"", Gen.g_nam_p);
return(0);
}
}
switch (Hdr_type) {
case BIN:
Gen.g_magic = CMN_BIN;
break;
case CHR:
Gen.g_magic = CMN_BIN;
break;
case ASC:
Gen.g_magic = CMN_ASC;
break;
case CRC:
Gen.g_magic = CMN_CRC;
break;
case USTAR:
/* If the length of the fullname is greater than 256,
print out a message and return.
*/
len = strlen(Gen.g_nam_p);
if (len > sizeof(fullnam)) {
msg(ERR, ":85", "%s: file name too long", Gen.g_nam_p);
return(0);
}
memset(prefix,0,sizeof(prefix));
if (len > NAMSIZ) {
memset(filname,0,sizeof(filname));
cp = wholename = Gen.g_nam_p;
cp += (PREFIXSIZ > len) ? len : PREFIXSIZ;
smore:
while ( cp > wholename && *cp != '/' ) /* find a '/' */
cp--;
if ( cp == wholename ) { /* didn't find '/' in prefix side */
msg(WARN, _SGI_MMX_cpio_prefix, "%s: Cannot find \"/\" in prefix string.",Gen.g_nam_p);
return (0);
}
/* Name will fit in 100 char array - Don't count "/" */
if ( strlen(cp + 1) > NAMSIZ ) {
msg(WARN, ":86", "%s: filename is greater than %d",(cp + 1),NAMSIZ);
return (0);
}
if ( *(cp+1) == '\0' ) { /* BSD tar dirs end with '/' */
--cp;
goto smore;
}
strncpy(prefix,wholename,cp - wholename);
strncpy(filname,cp+1, len - (cp - wholename) - 1);
Gen.g_tname=filname;
} else {
Gen.g_tname = Gen.g_nam_p;
}
Gen.g_prefix=prefix;
(void)strcpy(Gen.g_tmagic, "ustar");
(void)strcpy(Gen.g_version, "00");
dpasswd = getpwuid(SrcSt.st_uid);
if (dpasswd == (struct passwd *) NULL)
msg(WARN, badpasswdid, badpasswd, Gen.g_nam_p);
else {
(void)strncpy(&Gen.g_uname[0], dpasswd->pw_name, 32);
Gen.g_uname[31] = 0;
}
dgroup = getgrgid(SrcSt.st_gid);
if (dgroup == (struct group *) NULL) {
msg(WARN, badgroupid, badgroup, Gen.g_nam_p);
Gen.g_gname[0] = 0;
}
else {
(void)strncpy(&Gen.g_gname[0], dgroup->gr_name, 32);
Gen.g_gname[31] = 0;
}
switch(ftype) {
case S_IFDIR:
Gen.g_typeflag = '5';
break;
case S_IFREG:
if (SrcSt.st_nlink > 1)
Gen.g_typeflag = '1';
else
Gen.g_typeflag = '0';
break;
case S_IFLNK:
Gen.g_typeflag = '2';
break;
case S_IFBLK:
Gen.g_typeflag = '4';
break;
case S_IFCHR:
Gen.g_typeflag = '3';
break;
case S_IFIFO:
Gen.g_typeflag = '6';
break;
}
/* FALLTHROUGH*/
case TAR:
T_lname[0] = '\0';
break;
default:
msg(EXT, badhdrid, badhdr);
}
Gen.g_namesz = strlen(Gen.g_nam_p) + 1;
Gen.g_uid = SrcSt.st_uid;
Gen.g_gid = SrcSt.st_gid;
Gen.g_dev = SrcSt.st_dev;
Gen.g_ino = SrcSt.st_ino;
Gen.g_mode = SrcSt.st_mode;
Gen.g_mtime = SrcSt.st_mtime;
Gen.g_nlink = SrcSt.st_nlink;
/* Added for xfs support */
if (SrcSt.st_size >= BIGBLOCK && !Kflag && (Args & OCo)) {
msg(POST, ":141", "Skipping file -> %s\nMust use -K option to archive this file", Gen.g_nam_p);
return(0);
}
if (SrcSt.st_size >= BIGBLOCK && Kflag && (Args & OCo) && !portwarn) {
portwarn = 1;
msg(EPOST, ":144", "Warning: Inclusion of file -> %s will create a non-portable archive", Gen.g_nam_p);
}
if (ftype == S_IFREG || ftype == S_IFLNK) {
if (SrcSt.st_size >= BIGBLOCK) {
Gen.g_filesz = -(SrcSt.st_size/ BIGBLOCK);
Gen.g_filesz2 = SrcSt.st_size% BIGBLOCK;
}
else {
Gen.g_filesz = SrcSt.st_size;
Gen.g_filesz2 = 0L;
}
} else {
/* large dev_t handling with -K in binary mode; see comments
* in read_hdr() and getname() */
if(!Kflag || Hdr_type != BIN || SrcSt.st_rdev != 0xffff)
Gen.g_filesz = 0LL;
Gen.g_filesz2 = 0L;
}
Gen.g_rdev = SrcSt.st_rdev;
return(1);
}
/*
* creat_lnk: Create a link from the existing name1_p to name2_p.
*/
static int
creat_lnk(char *name1_p, char *name2_p)
{
register int cnt = 0;
do {
errno = 0;
if (!link(name1_p, name2_p)) {
cnt = 0;
break;
} else if (errno == EEXIST) {
if (!(Args & OCu) && G_p->g_mtime <= DesSt.st_mtime && (cnt == 0)) {
msg(POST, sameageid, sameage, name2_p);
return(0);
} else if (unlink(name2_p) < 0)
msg(ERRN, ":88", "Cannot unlink \"%s\"", name2_p);
}
cnt++;
} while ((cnt < 2) && !missdir(name2_p));
if (!cnt) {
if (Args & OCv)
(void)pfmt(Err_p, MM_NOSTD, ":89:%s linked to %s\n", name1_p, name2_p);
VERBOSE((Args & (OCv | OCV)), name2_p);
} else if (cnt == 1)
msg(ERRN, badcreatdirid, badcreatdir, name2_p);
else if (cnt == 2)
msg(ERRN, ":90", "Cannot link \"%s\" and \"%s\"", name1_p, name2_p);
return(cnt);
}
/*
* creat_spec:
*/
static int
creat_spec(void)
{
register char *nam_p;
register int cnt, result, rv = 0;
char *curdir;
if (Args & OCp)
nam_p = Fullnam_p;
else
nam_p = G_p->g_nam_p;
result = stat64(nam_p, &DesSt);
if (Adir) {
curdir = strrchr(nam_p, '.');
if (curdir != NULL && curdir[1] == NULL)
return(1);
else {
if (!result && (Args & OCd)) {
/* this dir already exists. if we've just
* created it with missdir(), the permissions
* may not be right. now that we know what
* they should be, fix them.
*/
Fix_mode = 1;
rstfiles(U_KEEP);
Fix_mode = 0;
return(1);
}
if (!result || !(Args & OCd) || !strcmp(nam_p, ".") || !strcmp(nam_p, ".."))
return(1);
}
} else if (!result && creat_tmp(nam_p) < 0)
return(0);
cnt = 0;
do {
(void)umask(Orig_umask);
if (Adir)
result = mkdir(nam_p, (int)G_p->g_mode);
else if (Aspec)
result = mknod(nam_p, (int)G_p->g_mode, (int)G_p->g_rdev);
if (result >= 0) {
cnt = 0;
break;
}
cnt++;
} while (cnt < 2 && !missdir(nam_p));
(void)umask(0);
switch (cnt) {
case 0:
rv = 1;
rstfiles(U_OVER);
break;
case 1:
msg(ERRN, badcreatdirid, badcreatdir, nam_p);
if (*Over_p == '\0')
rstfiles(U_KEEP);
break;
case 2:
if (Adir)
msg(ERRN, ":91", "Cannot create directory \"%s\"", nam_p);
else if (Aspec)
msg(ERRN, ":92", "mknod() failed for \"%s\"", nam_p);
if (*Over_p == '\0')
rstfiles(U_KEEP);
break;
default:
msg(EXT, badcaseid, badcase);
}
return(rv);
}
/* returns count of extents if file is holey, else 0 */
getbmap(int fd)
{
int cnt = 0, hole = 0, i;
struct getbmap bm[512]; /* enough for more than all but the largest
files in a single syscall */
bm[0].bmv_offset = 0;
bm[0].bmv_length = -1;
bm[0].bmv_count = sizeof(bm)/sizeof(bm[0]);
for (;;) {
if (fcntl(fd, F_GETBMAP, bm) < 0) {
if(errno != EINVAL)
perror("getbmap");
/* else filesystem doesn't support holes/fcntl (like EFS),
* don't complain for that case... */
break;
}
cnt += bm[0].bmv_entries;
for (i= 1;bm[0].bmv_entries-- > 0; i++) {
if (bm[i].bmv_block == -1)
hole++;
}
if(i < sizeof(bm)/sizeof(bm[0]))
break;
}
return hole ? cnt : 0;
}
/*
* creat_tmp:
*/
static int
creat_tmp(char *nam_p)
{
register char *t_p;
if ((Args & OCp) && G_p->g_ino == DesSt.st_ino && G_p->g_dev == DesSt.st_dev) {
msg(ERR, ":93", "Attempt to pass a file to itself.");
return(-1);
}
if (G_p->g_mtime <= DesSt.st_mtime && !(Args & OCu)) {
msg(POST, sameageid, sameage, nam_p);
return(-1);
}
if (Uid && Aspec) {
msg(ERR, ":94", "Cannot overwrite \"%s\"", nam_p);
return(-1);
}
(void)strcpy(Over_p, nam_p);
t_p = Over_p + strlen(Over_p);
while (t_p != Over_p) {
if (*(t_p - 1) == '/')
break;
t_p--;
}
(void)strcpy(t_p, "XXXXXX");
(void)mktemp(Over_p);
if (*Over_p == '\0') {
msg(ERR, ":95", "Cannot get temporary file name.");
return(-1);
}
if (rename(nam_p, Over_p) < 0) {
msg(ERRN, ":96", "Cannot create temporary file");
return(-1);
}
return(1);
}
/*
* data_in: If proc_mode == P_PROC, bread() the file's data from the archive
* and write(2) it to the open fdes gotten from openout(). If proc_mode ==
* P_SKIP, or becomes P_SKIP (due to errors etc), bread(2) the file's data
* and ignore it. If the user specified any of the "swap" options (b, s or S),
* and the length of the file is not appropriate for that action, do not
* perform the "swap", otherwise perform the action on a buffer by buffer basis.
* If the CRC header was selected, calculate a running checksum as each buffer
* is processed.
*/
static void
data_in(int proc_mode)
{
register char *nam_p;
register int cnt, pad;
register off64_t filesz;
register long cksumval = 0L;
register int rv, swapfile = 0;
int bigflag = 0;
off64_t tempsz;
int cfile=-1, same=1, samex=1, islnk=0;
struct stat st;
char *lnkbuf;
int chunks = 0, chunki;
struct holemap *hm = 0;
off64_t offs;
nam_p = G_p->g_nam_p;
if ((G_p->g_mode & Ftype) == S_IFLNK && proc_mode != P_SKIP) {
if(Compareflag) {
if(lstat(nam_p, &st) || (st.st_mode&Ftype) != S_IFLNK)
same = 0;
islnk = 1;
FILL(Gen.g_filesz);
lnkbuf = Buffr.b_out_p;
}
else {
proc_mode = P_SKIP;
VERBOSE((Args & (OCv | OCV)), nam_p);
}
}
if (Args & (OCb | OCs | OCS)) { /* verfify that swapping is possible */
swapfile = 1;
if (Args & (OCs | OCb) && G_p->g_filesz % 2) {
msg(ERR, ":98", "Cannot swap bytes of \"%s\", odd number of bytes", nam_p);
swapfile = 0;
}
if (Args & (OCS | OCb) && G_p->g_filesz % 4) {
msg(ERR, ":99", "Cannot swap halfwords of \"%s\", odd number of halfwords", nam_p);
swapfile = 0;
}
}
if(S_ISREG(G_p->g_mode) && G_p->g_rdev==1) { /* see bug 511930
* for why we explictly check for 1, rather than non-zero */
/* it's a holey file */
struct holemap h;
FILL(sizeof(*hm)); /* read first struct get chunk count */
memcpy(&h, Buffr.b_out_p, sizeof(h));
if (!h.offset && h.count) { /* if this is REALLY a holey file,
* h.count will be the number of holemap structs (chunks)
* and h.offset will be zero. This check in necessary
* because unfortunately some implementations of cpio
* leave garbage in their data structures instead of
* zeroing them. See bugs 511930 and 525101. */
chunks = h.count;
Buffr.b_out_p += sizeof(h);
Buffr.b_cnt -= (long)sizeof(h);
hm = alloca(sizeof(*hm)*chunks);
FILL(sizeof(*hm)*chunks);
memcpy(hm, Buffr.b_out_p, sizeof(*hm)*chunks);
Buffr.b_out_p += sizeof(*hm)*chunks;
Buffr.b_cnt -= (long)sizeof(*hm)*chunks;
chunki = 0;
offs = 0;
}
}
filesz = G_p->g_filesz;
if ((int)filesz < 0) {
filesz = tempsz = BIGBLOCK * -(int)filesz;
bigflag++;
}
if(proc_mode == P_CMP && !islnk) {
if((cfile = open(nam_p, O_RDONLY)) == -1)
same = 0;
else if(!bigflag && (fstat(cfile, &st) || st.st_size != filesz))
same = 0;
}
while (filesz > 0) {
if(hm && chunki<chunks) {
if(offs >= hm[chunki].offset && hm[chunki].count < 0) {
if(proc_mode == P_PROC) {
/* hole, seek over it */
if((offs=lseek(Ofile, -hm[chunki].count, SEEK_CUR)) == -1)
msg(ERRN, ":1015", "\"%s\": lseek error skipping over hole",
nam_p);
}
else if(proc_mode == P_CMP) {
/* have to seek on compare file; or should we
* compare block maps? Maybe someday */
if(same && !islnk &&
lseek(cfile, -hm[chunki].count, SEEK_CUR) == -1)
same = 0;
offs -= hm[chunki].count;
}
else
offs -= hm[chunki].count;
filesz += hm[chunki].count;
chunki++;
continue;
}
cnt = (unsigned)(filesz > CPIOBSZ) ? CPIOBSZ : filesz;
if(cnt > hm[chunki].count && hm[chunki].count>0LL)
cnt = hm[chunki].count;
}
else
cnt = (int)(filesz > CPIOBSZ) ? CPIOBSZ : filesz;
FILL(cnt);
if (proc_mode != P_SKIP) {
if (Hdr_type == CRC)
cksumval += cksum(CRC, cnt);
if (swapfile)
swap(Buffr.b_out_p, cnt);
errno = 0;
if(proc_mode == P_CMP) {
/* compare contents */
if(same && !islnk) {
if(read(cfile, cmpbuf, cnt) != cnt)
same = 0;
else if(memcmp(cmpbuf, Buffr.b_out_p, cnt))
same = 0;
}
}
else {
rv = write(Ofile, Buffr.b_out_p, cnt);
if (rv < cnt) {
if (rv < 0)
msg(ERRN, badwriteid, badwrite, nam_p);
else
msg(EXTN, badwriteid, badwrite, nam_p);
proc_mode = P_SKIP;
(void)close(Ofile);
rstfiles(U_KEEP);
}
}
}
Buffr.b_out_p += cnt;
Buffr.b_cnt -= (long)cnt;
filesz -= (long)cnt;
if(hm) {
if(hm[chunki].count>0LL && (!(hm[chunki].count -= (off64_t)cnt)))
chunki++; /* next chunk */
offs += (off64_t)cnt;
}
} /* filesz */
pad = (Pad_val + 1 - (G_p->g_filesz & Pad_val)) & Pad_val;
if (pad != 0) {
FILL(pad);
Buffr.b_out_p += pad;
Buffr.b_cnt -= pad;
}
if (bigflag) {
gethdr();
G_p = &Gen;
/* += because with holey files, filesz may not be zero at this point */
filesz += G_p->g_filesz;
/* have to do the fstat here for very large files... */
if(fstat(cfile, &st) || st.st_size != (G_p->g_filesz+tempsz))
same = 0;
G_p->g_filesz += tempsz;
VERBOSE((Args & OCt) && !Compareflag, G_p->g_nam_p);
while (filesz > 0) {
if(hm && chunki<chunks) {
if(offs >= hm[chunki].offset && hm[chunki].count < 0) {
/* hole, seek over it */
if(proc_mode == P_PROC) {
if((offs=lseek(Ofile, -hm[chunki].count, SEEK_CUR)) == -1)
msg(ERRN, ":1015", "\"%s\": lseek error skipping over hole",
nam_p);
}
else if(proc_mode == P_CMP) {
/* have to seek on compare file; or should we
* compare block maps? Maybe someday */
if(same && !islnk &&
lseek(cfile, -hm[chunki].count, SEEK_CUR) == -1)
same = 0;
offs -= hm[chunki].count;
}
else
offs -= hm[chunki].count;
filesz += hm[chunki].count;
chunki++;
continue;
}
cnt = (unsigned)(filesz > CPIOBSZ) ? CPIOBSZ : filesz;
if(cnt > hm[chunki].count && hm[chunki].count>0LL)
cnt = hm[chunki].count;
}
else
cnt = (int)(filesz > CPIOBSZ) ? CPIOBSZ : filesz;
FILL(cnt);
if (proc_mode != P_SKIP) {
if (Hdr_type == CRC)
cksumval += cksum(CRC, cnt);
if (swapfile)
swap(Buffr.b_out_p, cnt);
errno = 0;
if(proc_mode == P_CMP) {
/* compare contents */
if(same) {
if(read(cfile, cmpbuf, cnt) != cnt)
same = 0;
else if(memcmp(cmpbuf, Buffr.b_out_p, cnt))
same = 0;
}
}
else {
rv = write(Ofile, Buffr.b_out_p, cnt);
if (rv < cnt) {
if (rv < 0)
msg(ERRN, badwriteid, badwrite, nam_p);
else
msg(EXTN, badwriteid, badwrite, nam_p);
proc_mode = P_SKIP;
(void)close(Ofile);
rstfiles(U_KEEP);
}
}
}
Buffr.b_out_p += cnt;
Buffr.b_cnt -= (long)cnt;
filesz -= (long)cnt;
if(hm) {
if(hm[chunki].count>0LL && (!(hm[chunki].count -= (off64_t)cnt)))
chunki++; /* next chunk */
offs += (off64_t)cnt;
}
} /* filesz */
pad = (Pad_val + 1 - (G_p->g_filesz & Pad_val)) & Pad_val;
if (pad != 0) {
FILL(pad);
Buffr.b_out_p += pad;
Buffr.b_cnt -= pad;
}
}
if (proc_mode == P_CMP) {
char *sptr;
if(cfile != -1)
(void)close(cfile);
if(Compareflag > 1) {
if(Gen.g_mode != st.st_mode ||
Gen.g_uid != st.st_uid || Gen.g_gid != st.st_gid)
samex = 0;
if(islnk) {
size_t l;
if(Hdr_type == USTAR || Hdr_type == TAR) {
lnkbuf = Thdr_p->tbuf.t_linkname;
l = strlen(Thdr_p->tbuf.t_linkname);
if(l > sizeof(Thdr_p->tbuf.t_linkname))
l = sizeof(Thdr_p->tbuf.t_linkname); /* no null */
}
else
l = G_p->g_filesz;
if(readlink(nam_p, cmpbuf, PATH_MAX) != l)
samex = 0;
else if(strncmp(cmpbuf, lnkbuf, l))
samex = 0;
}
}
printf("%c%s", same ? '=' : '!', Compareflag>1 ? (samex?"= ":"! ") : " ");
sptr = Buffr.b_out_p;
Buffr.b_out_p = lnkbuf; /* need this for islnk case for link targ */
verbose(nam_p);
Buffr.b_out_p = sptr;
}
else if (proc_mode == P_PROC) {
(void)close(Ofile);
if (Hdr_type == CRC && G_p->g_cksum != cksumval) {
msg(ERR, ":100", "\"%s\" - checksum error", nam_p);
rstfiles(U_KEEP);
} else
rstfiles(U_OVER);
VERBOSE(Args&(OCv|OCV), nam_p);
}
Finished = 1;
}
/*
* data_out: open(2) the file to be archived, compute the checksum
* of it's data if the CRC header was specified and write the header.
* read(2) each block of data and bwrite() it to the archive. For TARTYP (TAR
* and USTAR) archives, pad the data with NULLs to the next 512 byte boundary.
*/
static void
data_out(void)
{
register char *nam_p;
register int cnt, rv, pad;
register off64_t filesz, offs;
int lnksz, bigflag = 0, firsterr=1, chunks = 0, chunki;
struct holemap *hm = 0;
nam_p = G_p->g_nam_p;
if (Aspec) {
write_hdr();
rstfiles(U_KEEP);
VERBOSE((Args & (OCv | OCV)), nam_p);
return;
}
if ((G_p->g_mode & Ftype) == S_IFLNK && (Hdr_type != USTAR && Hdr_type != TAR)) { /* symbolic link */
write_hdr();
FLUSH(G_p->g_filesz+1);
errno = 0;
if (readlink(nam_p, Buffr.b_in_p, G_p->g_filesz) < 0) {
msg(ERRN, badreadsymid, badreadsym, nam_p);
return;
}
Buffr.b_in_p[G_p->g_filesz] = '\0'; /* be sure... */
Buffr.b_in_p += G_p->g_filesz;
Buffr.b_cnt += G_p->g_filesz;
pad = (Pad_val + 1 - (G_p->g_filesz & Pad_val)) & Pad_val;
if (pad != 0) {
FLUSH(pad);
(void)memcpy(Buffr.b_in_p, Empty, pad);
Buffr.b_in_p += pad;
Buffr.b_cnt += pad;
}
VERBOSE((Args & (OCv | OCV)), nam_p);
return;
} else if ((G_p->g_mode & Ftype) == S_IFLNK && (Hdr_type == USTAR || Hdr_type == TAR)) {
if ((lnksz=readlink(nam_p, T_lname, G_p->g_filesz)) < 0 || lnksz > NAMSIZ) {
msg(ERRN, ":86", "%s: filename is greater than %d", T_lname, NAMSIZ);
return;
}
T_lname[G_p->g_filesz] = '\0';
G_p->g_filesz = 0LL;
write_hdr();
VERBOSE((Args & (OCv | OCV)), nam_p);
return;
}
/* OLSON: this should be reconsidered for a possible new fcntl or
* use of a pad field in stat in the future; this is somewhat of a
* hack that can't be counted on "forever"; 4/97 */
/* We open for writing as a mechanism for detecting swap files. If
* the open returns EBUSY, and it's a regular file, then this is a swap
* file. Opening it for writing is safe, since we won't actually write to
* it, but does mean we have some small additional risk of a bug in
* cpio trashing a file... Very IRIX specific... */
if ((Ifile = open(nam_p, O_RDWR)) < 0) {
struct stat st;
if(errno == EBUSY && !stat(nam_p, &st) && S_ISREG(st.st_mode)) {
msg(ERR, ":1013", "\"%s\" skipped because it appears to be a swapfile", nam_p);
return;
}
else if ((Ifile = open(nam_p, O_RDONLY)) < 0) {
msg(ERR, ":101", "\"%s\" ?", nam_p);
return;
}
}
if (Hdr_type == CRC && ((G_p->g_cksum = cksum(CRC, 0)) == (ulong)-1)) {
msg(POST, ":102", "\"%s\" skipped", nam_p);
(void)close(Ifile);
return;
}
if(Holeflag) {
struct getbmap *bm;
chunks = getbmap(Ifile);
if(chunks) { /* has holes */
if((sizeof(*bm)*(chunks+1)) > Buffr.b_size) {
(void)close(Ifile);
msg(ERR, ":1014", "\"%s\" skipped because holey file map too"
" large. Use at least -C %u", nam_p, sizeof(*bm)*(chunks+1));
/*NOTREACHED*/
}
if(!portwarn) {
portwarn = 1;
msg(EPOST, ":144", "Warning: Inclusion of file -> %s will create a non-portable archive", Gen.g_nam_p);
}
bm = alloca(sizeof(*bm)*(chunks+1));
bm[0].bmv_offset = 0;
bm[0].bmv_length = -1;
bm[0].bmv_count = sizeof(*bm)*(chunks+1);
if (fcntl(Ifile, F_GETBMAP, bm) < 0) {
msg(ERR, ":1012", "\"%s\" skipped due to error in processing information about holes", nam_p);
/* this shouldn't ever happen */
(void)close(Ifile);
return;
}
/* this is the way we flag a holey file; it's a regular file
* and has a non-zero g_rdev/h_rdev; see check in data_in() */
G_p->g_rdev = 1;
hm = alloca(sizeof(*hm)*(chunks+1));
hm[0].offset = 0; /* unused */
hm[0].count = chunks; /* set number of holemap structs */
for(chunki=1; chunki<=chunks; chunki++) {
hm[chunki].offset = (++bm)->bmv_offset * 512;
hm[chunki].count = bm->bmv_length * ((bm->bmv_block==-1)
? -512 : 512);
}
}
}
write_hdr();
filesz = G_p->g_filesz;
if (filesz < 0) {
filesz = BIGBLOCK * -filesz;
bigflag++;
}
if(hm) {
FLUSH(sizeof(*hm)*(chunks+1));
memcpy(Buffr.b_in_p, hm, sizeof(*hm)*(chunks+1));
Buffr.b_in_p += sizeof(*hm)*(chunks+1);
Buffr.b_cnt += (long)sizeof(*hm)*(chunks+1);
chunki = 1;
offs = 0;
}
while (filesz > 0) {
if(hm && chunki < chunks) {
if(offs >= hm[chunki].offset && hm[chunki].count < 0) {
if((filesz + hm[chunki].count) < 0)
break; /* get the rest in the bigflag chunk */
/* hole, seek over it */
if((offs=lseek(Ifile, -hm[chunki].count, SEEK_CUR)) == -1)
msg(ERRN, ":1015", "\"%s\": lseek error skipping over hole",
nam_p);
filesz += hm[chunki].count;
chunki++;
continue;
}
cnt = (unsigned)(filesz > CPIOBSZ) ? CPIOBSZ : filesz;
if(cnt > hm[chunki].count)
cnt = hm[chunki].count;
}
else
cnt = (unsigned)(filesz > CPIOBSZ) ? CPIOBSZ : filesz;
FLUSH(cnt);
errno = 0;
if ((rv = read(Ifile, Buffr.b_in_p, (unsigned)cnt)) < 0) {
if(firsterr) {
msg(ERRN, badreadid, badread, nam_p);
firsterr=0;
}
/* need to pad to original size, same as tar, or following
* files in archive will be "lost"; this was handled wrong
* prior to irix 6.5; zero so it doesn't have random earlier
* data in it; this can happen when trying to backup a swap
* file, for example (before the swap file changes). */
memset(Buffr.b_in_p, 0, (size_t)cnt);
rv = cnt;
}
Buffr.b_in_p += rv;
Buffr.b_cnt += (long)rv;
filesz -= (long)rv;
if(hm) {
if(!(hm[chunki].count -= (off64_t)rv))
chunki++;
offs += (off64_t)rv;
}
}
pad = (Pad_val + 1 - (G_p->g_filesz & Pad_val)) & Pad_val;
if (pad != 0) {
FLUSH(pad);
(void)memcpy(Buffr.b_in_p, Empty, pad);
Buffr.b_in_p += pad;
Buffr.b_cnt += pad;
}
if (bigflag) {
G_p->g_filesz = G_p->g_filesz2;
G_p->g_filesz2 = 0LL;
filesz += G_p->g_filesz;
write_hdr();
while (filesz > 0) {
if(hm && chunki < chunks) {
if(offs >= hm[chunki].offset && hm[chunki].count < 0) {
/* hole, seek over it */
if((offs=lseek(Ifile, -hm[chunki].count, SEEK_CUR)) == -1)
msg(ERRN, ":1015", "\"%s\": lseek error skipping over hole",
nam_p);
filesz += hm[chunki].count;
chunki++;
continue;
}
cnt = (unsigned)(filesz > CPIOBSZ) ? CPIOBSZ : filesz;
if(cnt > hm[chunki].count)
cnt = hm[chunki].count;
}
else
cnt = (unsigned)(filesz > CPIOBSZ) ? CPIOBSZ : filesz;
FLUSH(cnt);
errno = 0;
if ((rv = read(Ifile, Buffr.b_in_p, (unsigned)cnt)) < 0) {
msg(ERRN, badreadid, badread, nam_p);
break;
}
Buffr.b_in_p += rv;
Buffr.b_cnt += (long)rv;
filesz -= (long)rv;
if(hm) {
if(!(hm[chunki].count -= (off64_t)rv))
chunki++;
offs += (off64_t)rv;
}
}
pad = (Pad_val + 1 - (G_p->g_filesz & Pad_val)) & Pad_val;
if (pad != 0 ) {
FLUSH(pad);
(void)memcpy(Buffr.b_in_p, Empty, pad);
Buffr.b_in_p += pad;
Buffr.b_cnt += pad;
}
}
(void)close(Ifile);
rstfiles(U_KEEP);
VERBOSE((Args & (OCv | OCV)), nam_p);
}
/*
* data_pass: If not a special file (Aspec), open(2) the file to be
* transferred, read(2) each block of data and write(2) it to the output file
* Ofile, which was opened in file_pass().
*/
static void
data_pass(void)
{
register int cnt, done = 1;
register off64_t filesz;
if (Aspec) {
(void)close(Ofile);
rstfiles(U_KEEP);
VERBOSE((Args & (OCv | OCV)), Nam_p);
return;
}
if ((Ifile = open(Nam_p, 0)) < 0) {
msg(ERRN, ":103", "Cannot open \"%s\", skipped", Nam_p);
(void)close(Ofile);
rstfiles(U_KEEP);
return;
}
filesz = G_p->g_filesz;
if (filesz < 0)
filesz = BIGBLOCK * -filesz + G_p->g_filesz2;
while (filesz > 0) {
cnt = (unsigned)(filesz > CPIOBSZ) ? CPIOBSZ : filesz;
errno = 0;
if (read(Ifile, Buf_p, (unsigned)cnt) < 0) {
msg(ERRN, badreadid, badread, Nam_p);
done = 0;
break;
}
errno = 0;
if (write(Ofile, Buf_p, (unsigned)cnt) < 0) {
msg(ERRN, badwriteid, badwrite, Fullnam_p);
done = 0;
break;
}
Blocks += ((cnt + (BUFSZ - 1)) / BUFSZ);
filesz -= (long)cnt;
}
(void)close(Ifile);
(void)close(Ofile);
if (done)
rstfiles(U_OVER);
else
rstfiles(U_KEEP);
VERBOSE((Args & (OCv | OCV)), Fullnam_p);
Finished = 1;
}
/* compare filetype. Maybe with Compareflag>1 also check owner and perms
* at some point
*/
void
compare_spec(void)
{
struct stat st;
int same = 1, samex = 1;;
if(stat(G_p->g_nam_p, &st) == -1)
same = 0;
else if((Gen.g_mode & Ftype) != (st.st_mode & Ftype))
same = 0;
else if(Compareflag > 1 && (Gen.g_mode != st.st_mode ||
Gen.g_uid != st.st_uid || Gen.g_gid != st.st_gid))
samex = 0;
printf("%c%s", same ? '=' : '!', Compareflag>1 ? (samex?"= ":"! ") : " ");
verbose(G_p->g_nam_p);
}
/*
* file_in: Process an object from the archive. If a TARTYP (TAR or USTAR)
* archive and g_nlink == 1, link this file to the file name in t_linkname
* and return. Handle linked files in one of two ways. If Onecopy == 0, this
* is an old style (binary or -c) archive, create and extract the data for the
* first link found, link all subsequent links to this file and skip their data.
* If Oncecopy == 1, save links until all have been processed, and then
* process the links first to last checking their names against the patterns
* and/or asking the user to rename them. The first link that is accepted
* for xtraction is created and the data is read from the archive.
* All subsequent links that are accepted are linked to this file.
*/
static void
file_in(void)
{
register struct Lnk *l_p, *tl_p;
int lnkem = 0, cleanup = 0;
int proc_file;
struct Lnk *ttl_p;
G_p = &Gen;
/* TAR and USTAR */
if ((Hdr_type == USTAR || Hdr_type == TAR) && G_p->g_nlink == 1 && !(Args & OCt)) {
(void)creat_lnk(Thdr_p->tbuf.t_linkname, G_p->g_nam_p);
return;
}
if (Adir) {
if (ckname() != F_SKIP) {
if(Compareflag)
compare_spec();
else if(creat_spec() > 0)
VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
}
return;
}
if (G_p->g_nlink == 1 || (Hdr_type == TAR || Hdr_type == USTAR)) {
if (Aspec) {
if (ckname() != F_SKIP) {
if(Compareflag)
compare_spec();
else if(creat_spec() > 0)
VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
}
} else {
if (ckname() == F_SKIP)
data_in(P_SKIP);
else if(Compareflag)
data_in(P_CMP);
else
data_in((Ofile = openout()) < 0 ? P_SKIP : P_PROC);
}
return;
}
tl_p = add_lnk(&ttl_p);
l_p = ttl_p;
if (l_p->L_cnt == l_p->L_gen.g_nlink)
cleanup = 1;
if (!Onecopy) {
lnkem = (tl_p != l_p) ? 1 : 0;
G_p = &tl_p->L_gen;
if (ckname() == F_SKIP) {
data_in(P_SKIP);
}
else {
if (!lnkem) {
if (Aspec) {
if(Compareflag)
compare_spec();
else if (creat_spec() > 0)
VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
} else if(Compareflag)
data_in(P_CMP);
else if ((Ofile = openout()) < 0) {
data_in(P_SKIP);
reclaim(l_p);
return;
} else
data_in(P_PROC);
} else {
(void)strcpy(Lnkend_p, l_p->L_gen.g_nam_p);
(void)strcpy(Full_p, tl_p->L_gen.g_nam_p);
if(!Compareflag) {
(void)creat_lnk(Lnkend_p, Full_p);
data_in(P_SKIP);
} else if(Aspec) {
compare_spec();
data_in(P_SKIP);
} else
data_in(P_CMP);
l_p->L_lnk_p = (struct Lnk *)NULL;
free(tl_p->L_gen.g_nam_p);
free(tl_p);
}
}
} else { /* Onecopy */
if (tl_p->L_gen.g_filesz)
cleanup = 1;
if (!cleanup)
return; /* don't do anything yet */
tl_p = l_p;
while (tl_p != (struct Lnk *)NULL) {
G_p = &tl_p->L_gen;
if ((proc_file = ckname()) != F_SKIP) {
if (l_p->L_data) {
(void)creat_lnk(l_p->L_gen.g_nam_p, G_p->g_nam_p);
} else if (Aspec) {
if(Compareflag)
compare_spec();
else
(void)creat_spec();
l_p->L_data = 1;
VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
} else if(Compareflag)
data_in(P_CMP);
else if ((Ofile = openout()) < 0) {
proc_file = F_SKIP;
} else {
data_in(P_PROC);
l_p->L_data = 1;
}
} /* (proc_file = ckname()) != F_SKIP */
tl_p = tl_p->L_lnk_p;
if (proc_file == F_SKIP && !cleanup) {
tl_p->L_nxt_p = l_p->L_nxt_p;
tl_p->L_bck_p = l_p->L_bck_p;
l_p->L_bck_p->L_nxt_p = tl_p;
l_p->L_nxt_p->L_bck_p = tl_p;
free(l_p->L_gen.g_nam_p);
free(l_p);
}
} /* tl_p->L_lnk_p != (struct Lnk *)NULL */
if (l_p->L_data == 0) {
data_in(P_SKIP);
}
}
if (cleanup)
reclaim(l_p);
}
/*
* file_out: If the current file is not a special file (!Aspec) and it
* is identical to the archive, skip it (do not archive the archive if it
* is a regular file). If creating a TARTYP (TAR or USTAR) archive, the first
* time a link to a file is encountered, write the header and file out normally.
* Subsequent links to this file put this file name in their t_linkname field.
* Otherwise, links are handled in one of two ways, for the old headers
* (i.e. binary and -c), linked files are written out as they are encountered.
* For the new headers (ASC and CRC), links are saved up until all the links
* to each file are found. For a file with n links, write n - 1 headers with
* g_filesz set to 0, write the final (nth) header with the correct g_filesz
* value and write the data for the file to the archive.
*/
static void
file_out(void)
{
register struct Lnk *l_p, *tl_p;
register int cleanup = 0;
struct Lnk *ttl_p;
G_p = &Gen;
if (!Aspec && !ArchSt.st_rdev && IDENT(SrcSt, ArchSt)) {
/* only skip if archive isn't a device; this extra check
* keeps us from skipping files that happen to have a
* a faked inode/dev from new_stat64() that match the real
* device we are archiving to; that happens a lot more with the
* hwgraph, because the major number is 0. Also *report* when
* we skip something; it should never be silent! Bug #461672 */
msg(POST, ":102", "\"%s\" skipped",G_p->g_nam_p);
return; /* do not archive the archive if it's a regular file */
}
if ((Hdr_type == USTAR) || (Hdr_type == TAR)) { /* TAR and USTAR */
if (Adir) {
write_hdr();
VERBOSE((Args & (OCv|OCV)), G_p->g_nam_p);
return;
}
if (G_p->g_nlink == 1) {
data_out();
return;
}
tl_p = add_lnk(&ttl_p);
l_p = ttl_p;
if (tl_p == l_p) { /* first link to this file encountered */
data_out();
return;
}
(void)strncpy(T_lname, l_p->L_gen.g_nam_p, l_p->L_gen.g_namesz);
write_hdr();
VERBOSE((Args & (OCv | OCV)), tl_p->L_gen.g_nam_p);
free(tl_p->L_gen.g_nam_p);
free(tl_p);
return;
}
if (Adir) {
write_hdr();
VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
return;
}
if (G_p->g_nlink == 1) { /* symlink */
data_out();
return;
} else { /* other hardlink, reg. file, ... */
tl_p = add_lnk(&ttl_p);
l_p = ttl_p;
if (l_p->L_cnt == l_p->L_gen.g_nlink)
cleanup = 1;
else if (Onecopy)
return; /* don't process data yet */
}
if (Onecopy) {
tl_p = l_p;
while (tl_p->L_lnk_p != (struct Lnk *)NULL) {
G_p = &tl_p->L_gen;
if (Hdr_type == CRC){
if ((Ifile = open(G_p->g_nam_p, O_RDONLY)) < 0) {
msg(ERR, ":101", "\"%s\" ?", G_p->g_nam_p);
return;
}
if ((G_p->g_cksum = cksum(CRC, 0)) == (ulong)-1) {
msg(POST, ":102", "\"%s\" skipped",G_p->g_nam_p);
(void)close(Ifile);
return;
}
(void)close(Ifile);
}
G_p->g_filesz = 0LL;
write_hdr();
VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
tl_p = tl_p->L_lnk_p;
}
G_p = &tl_p->L_gen;
}
data_out();
if (cleanup)
reclaim(l_p);
}
/*
* file_pass: If the -l option is set (link files when possible), and the
* source and destination file systems are the same, link the source file
* (G_p->g_nam_p) to the destination file (Fullnam) and return. If not a
* linked file, transfer the data. Otherwise, the first link to a file
* encountered is transferred normally and subsequent links are linked to it.
* It should be noted that when using the "pass" option that an internal file
* structure is used to pass the device number (structure "Gen", device number
* types are ulongs) thus, this is the reason "pass" will do the "right"
* thing when passing major/minor numbers for block or character devices.
*/
static void
file_pass(void)
{
register struct Lnk *l_p, *tl_p;
struct Lnk *ttl_p;
char *save_name;
G_p = &Gen;
if (Adir && !(Args & OCd)) {
msg(ERR, ":104", "Use -d option to copy \"%s\"", G_p->g_nam_p);
return;
}
save_name = G_p->g_nam_p;
while (*(G_p->g_nam_p) == '/')
G_p->g_nam_p++;
(void)strcpy(Full_p, G_p->g_nam_p);
if ((Args & OCl) && !Adir && creat_lnk(G_p->g_nam_p, Fullnam_p) == 0)
return;
if ((G_p->g_mode & Ftype) == S_IFLNK && !(Args & OCL)) {
errno = 0;
if (readlink(save_name, Symlnk_p, G_p->g_filesz) < 0) {
msg(ERRN, badreadsymid, badreadsym, save_name);
return;
}
errno = 0;
(void)missdir(Fullnam_p);
*(Symlnk_p + G_p->g_filesz) = '\0';
if (symlink(Symlnk_p, Fullnam_p) < 0) {
if (errno == EEXIST) {
if (openout() < 0) {
return;
}
} else {
msg(ERRN, badcreateid, badcreate, Fullnam_p);
return;
}
}
rstfiles(U_OVER);
VERBOSE((Args & (OCv | OCV)), Fullnam_p);
return;
}
if (!Adir && G_p->g_nlink > 1) {
tl_p = add_lnk(&ttl_p);
l_p = ttl_p;
if (tl_p == l_p) /* was not found */
G_p = &tl_p->L_gen;
else { /* found */
(void)strcpy(Lnkend_p, l_p->L_gen.g_nam_p);
(void)strcpy(Full_p, tl_p->L_gen.g_nam_p);
(void)creat_lnk(Lnknam_p, Fullnam_p);
l_p->L_lnk_p = (struct Lnk *)NULL;
free(tl_p->L_gen.g_nam_p);
free(tl_p);
if (l_p->L_cnt == G_p->g_nlink)
reclaim(l_p);
return;
}
}
if (Adir || Aspec) {
if (creat_spec() > 0)
VERBOSE((Args & (OCv | OCV)), Fullnam_p);
} else if ((Ofile = openout()) > 0)
data_pass();
}
/*
* flush_lnks: With new linked file handling, linked files are not archived
* until all links have been collected. When the end of the list of filenames
* to archive has been reached, all files that did not encounter all their links
* are written out with actual (encountered) link counts. A file with n links
* (that are archived) will be represented by n headers (one for each link (the
* first n - 1 have g_filesz set to 0)) followed by the data for the file.
*/
static void
flush_lnks(void)
{
register struct Lnk *l_p, *tl_p;
off64_t tfsize;
int i;
for (i = 0; i < NLNKHASH; i++) {
l_p = Lnk_hd[i].L_nxt_p;
while (l_p != &Lnk_hd[i]) {
(void)strcpy(Gen.g_nam_p, l_p->L_gen.g_nam_p);
if (stat64(Gen.g_nam_p, &SrcSt) == 0) { /* check if file exists */
tl_p = l_p;
(void)creat_hdr();
Gen.g_nlink = l_p->L_cnt; /* "actual" link count */
tfsize = Gen.g_filesz;
Gen.g_filesz = 0LL;
G_p = &Gen;
while (tl_p != (struct Lnk *)NULL) {
Gen.g_nam_p = tl_p->L_gen.g_nam_p;
Gen.g_namesz = tl_p->L_gen.g_namesz;
if (tl_p->L_lnk_p == (struct Lnk *)NULL) {
Gen.g_filesz = tfsize;
data_out();
break;
}
write_hdr(); /* archive header only */
VERBOSE((Args & (OCv | OCV)), Gen.g_nam_p);
tl_p = tl_p->L_lnk_p;
}
Gen.g_nam_p = Nam_p;
} else /* stat64(Gen.g_nam_p, &SrcSt) == 0 */
msg(ERR, ":105", "\"%s\" has disapeared", Gen.g_nam_p);
tl_p = l_p;
l_p = l_p->L_nxt_p;
reclaim(tl_p);
} /* l_p != &Lnk_hd */
}
}
/*
* gethdr: Get a header from the archive, validate it and check for the trailer.
* Any user specified Hdr_type is ignored (set to NONE in main). Hdr_type is
* set appropriately after a valid header is found. Unless the -k option is
* set a corrupted header causes an exit with an error. I/O errors during
* examination of any part of the header cause gethdr to throw away any current
* data and start over. Other errors during examination of any part of the
* header cause gethdr to advance one byte and continue the examination.
*/
static int
gethdr(void)
{
register ushort ftype;
register int hit = NONE, cnt = 0;
int goodhdr, hsize, offset;
int bswap = 0;
char *preptr;
Gen.g_nam_p = Nam_p;
do { /* hit == NONE && (Args & OCk) && Buffr.b_cnt > 0 */
FILL(Hdrsz);
switch (Hdr_type) {
case NONE:
case BIN:
Binmag.b_byte[0] = Buffr.b_out_p[0];
Binmag.b_byte[1] = Buffr.b_out_p[1];
if ((Binmag.b_half == CMN_BIN) ||
(Binmag.b_half == CMN_BBS)) {
hit = read_hdr(BIN);
if (Hdr_type == NONE)
bswap = 1;
hsize = (int)(HDRSZ + Gen.g_namesz);
break;
}
if (Hdr_type != NONE)
break;
/*FALLTHROUGH*/
case CHR:
if (!strncmp(Buffr.b_out_p, CMS_CHR, CMS_LEN)) {
hit = read_hdr(CHR);
hsize = (int)(CHRSZ + Gen.g_namesz);
break;
}
if (Hdr_type != NONE)
break;
/*FALLTHROUGH*/
case ASC:
if (!strncmp(Buffr.b_out_p, CMS_ASC, CMS_LEN)) {
hit = read_hdr(ASC);
hsize = (int)(ASCSZ + Gen.g_namesz);
break;
}
if (Hdr_type != NONE)
break;
/*FALLTHROUGH*/
case CRC:
if (!strncmp(Buffr.b_out_p, CMS_CRC, CMS_LEN)) {
hit = read_hdr(CRC);
hsize = (int)(ASCSZ + Gen.g_namesz);
break;
}
if (Hdr_type != NONE)
break;
/*FALLTHROUGH*/
case USTAR:
Hdrsz = TARSZ;
FILL(Hdrsz);
if ((hit = read_hdr(USTAR)) == NONE) {
Hdrsz = ASCSZ;
break;
}
hit = USTAR;
hsize = TARSZ;
break;
/*FALLTHROUGH*/
case TAR:
Hdrsz = TARSZ;
FILL(Hdrsz);
if ((hit = read_hdr(TAR)) == NONE) {
Hdrsz = ASCSZ;
break;
}
hit = TAR;
hsize = TARSZ;
break;
default:
msg(EXT, badhdrid, badhdr);
} /* Hdr_type */
Gen.g_nam_p = &nambuf[0];
if (hit != NONE) {
FILL(hsize);
goodhdr = 1;
if (Gen.g_namesz < 1)
goodhdr = 0;
if ((hit == USTAR) || (hit == TAR)) { /* TAR and USTAR */
if (*Gen.g_nam_p == '\0') { /* tar trailer */
goodhdr = 1;
}
else {
G_p = &Gen;
if (G_p->g_cksum != cksum(TARTYP, 0)) {
goodhdr = 0;
if (!strncmp(Buffr.b_out_p,"7070",4))
msg(EXT, _SGI_MMX_cpio_bswap, "Byte swapped data - re-try with correct device");
msg(ERR, ":106", "Bad header - checksum error.");
}
}
} else { /* binary, -c, ASC and CRC */
if (Gen.g_nlink <= (ulong)0)
goodhdr = 0;
if (*(Buffr.b_out_p + hsize - 1) != '\0')
goodhdr = 0;
}
if (!goodhdr) {
hit = NONE;
if (!(Args & OCk))
break;
msg(ERR, ":107", "Corrupt header, file(s) may be lost.");
} else {
FILL(hsize);
}
} /* hit != NONE */
if (hit == NONE) {
Buffr.b_out_p++;
Buffr.b_cnt--;
if (!(Args & OCk))
break;
if (!cnt++)
msg(ERR, ":108", "Searching for magic number/header.");
}
} while (hit == NONE);
if (hit == NONE) {
if (Hdr_type == NONE)
msg(EXT, ":109", "Not a cpio file, bad header.");
else
msg(EXT, ":110", "Bad magic number/header.");
} else if (cnt > 0) {
if(Args & OCv)
fprintf(stderr, "(%d blocks) ", cnt);
msg(EPOST, ":111", "Re-synchronized on magic number/header.");
}
if (Hdr_type == NONE) {
Hdr_type = hit;
switch (Hdr_type) {
case BIN:
if(bswap)
Args |= BSM;
Hdrsz = HDRSZ;
Max_namesz = CPATH;
Pad_val = HALFWD;
Onecopy = 0;
break;
case CHR:
Hdrsz = CHRSZ;
Max_namesz = CPATH;
Pad_val = 0;
Onecopy = 0;
break;
case ASC:
case CRC:
Hdrsz = ASCSZ;
Max_namesz = APATH;
Pad_val = FULLWD;
Onecopy = 1;
break;
case USTAR:
Hdrsz = TARSZ;
Max_namesz = HNAMLEN;
Pad_val = FULLBK;
Onecopy = 0;
break;
case TAR:
Hdrsz = TARSZ;
Max_namesz = TNAMLEN - 1;
Pad_val = FULLBK;
Onecopy = 0;
break;
default:
msg(EXT, badhdrid, badhdr);
} /* Hdr_type */
} /* Hdr_type == NONE */
if ((Hdr_type == USTAR) || (Hdr_type == TAR)) { /* TAR and USTAR */
Gen.g_namesz = 0;
if (Gen.g_nam_p[0] == '\0')
return(0);
else {
preptr = &prebuf[0];
if (*preptr != (char) NULL) {
(void)strcpy(&fullnam[0], &prebuf[0]);
strcat(&fullnam[0],"/");
strcat(&fullnam[0],&nambuf[0]);
Gen.g_nam_p = &fullnam[0];
} else
Gen.g_nam_p = &nambuf[0];
}
} else {
(void)memcpy(Gen.g_nam_p, Buffr.b_out_p + Hdrsz, Gen.g_namesz);
if (!strcmp(Gen.g_nam_p, "TRAILER!!!"))
return(0);
}
offset = ((hsize + Pad_val) & ~Pad_val);
Buffr.b_out_p += offset;
Buffr.b_cnt -= (long)offset;
ftype = (ushort)Gen.g_mode & Ftype;
Adir = (ftype == S_IFDIR);
Aspec = (ftype == S_IFBLK || ftype == S_IFCHR || ftype == S_IFIFO);
return(1);
}
/*
* getname: Get file names for inclusion in the archive. When end of file
* on the input stream of file names is reached, flush the link buffer out.
* For each filename, remove leading "./"s and multiple "/"s, and remove
* any trailing newline "\n". Finally, verify the existance of the file,
* and call creat_hdr() to fill in the gen_hdr structure.
*/
static int
getname(void)
{
register int goodfile = 0, lastchar, namelen;
while (!goodfile) {
Gen.g_nam_p = Nam_p;
if (fgets(Gen.g_nam_p, PATH_MAX, stdin) == (char *)NULL) {
if (Onecopy && !(Args &OCp))
flush_lnks();
return(0);
}
namelen = strlen(Gen.g_nam_p);
if (namelen > Max_namesz) {
*(Gen.g_nam_p + namelen - 1) = '\0';
msg(ERR, _SGI_MMX_cpio_maxlen, "Name %s exceeds maximum length (%d) - skipped.",Gen.g_nam_p, Max_namesz);
return(2);
}
while (*Gen.g_nam_p == '.' && Gen.g_nam_p[1] == '/') {
Gen.g_nam_p += 2;
while (*Gen.g_nam_p == '/')
Gen.g_nam_p++;
}
lastchar = strlen(Gen.g_nam_p) - 1;
if (*(Gen.g_nam_p + lastchar) == '\n')
*(Gen.g_nam_p + lastchar) = '\0';
/*
Since we cannot return to the old version of stat (16 bit
dev and rdev field) - lets make these look like the old version
in a 32 bit field iff the options being used are the binary
(default) or the -Hodc is being used.
If the format is binary or -Hodc change the dev, and rdev fields
to reflect 16 bits instead of the new 32 bits. Difference between
old major/minor (8/8 bits) and new major/minor (14/18 bits).
If it OVER_FLOW's then make it max bits for old major/minor.
*/
if (!lstat64(Gen.g_nam_p, &SrcSt)) {
goodfile = 1;
if ((SrcSt.st_mode & Ftype) == S_IFLNK && (Args & OCL)) {
errno = 0;
if (stat64(Gen.g_nam_p, &SrcSt) < 0) {
msg(ERRN, badfollowid, badfollow, Gen.g_nam_p);
goodfile = 0;
}
}
if ( (Hdr_type == BIN || Hdr_type == CHR) && !(Args & OCp) ) {
if (new_stat64(&SrcSt) < 0)
msg(EXT, nomemid, nomem);
if ( BLKORCHR(SrcSt.st_mode) )
if ( OVER_FLOW(SrcSt.st_rdev) ) {
if(Hdr_type == BIN && Kflag) {
/* only for rdev; Olson, 4/97
* this makes us able to backup all
* the possible dev_t's with -K, which
* uses binary mode, no expanded rdev */
if(!portwarn) {
portwarn = 1;
msg(EPOST, ":144", "Warning: Inclusion of file -> %s will create a non-portable archive", Gen.g_nam_p);
}
Gen.g_filesz = SrcSt.st_rdev;
SrcSt.st_rdev = 0xffff; /* flag it */
}
else {
msg(WARN, _SGI_MMX_cpio_expdev, "Old format cannot support expanded device types on \"%s\" (device %d,%d)", Gen.g_nam_p, SrcSt.st_rdev >> NBITSMINOR, SrcSt.st_rdev & MAXMIN);
SrcSt.st_rdev &= 0x00007fff;
}
}
else {
SrcSt.st_rdev = DEV32TO16(SrcSt.st_rdev);
}
}
} else {
msg(ERRN, badaccessid, badaccess, Gen.g_nam_p);
}
}
if (creat_hdr())
return(1);
return(2);
}
/*
* getpats: Save any filenames/patterns specified as arguments.
* Read additional filenames/patterns from the file specified by the
* user. The filenames/patterns must occur one per line.
*/
static void
getpats(int largc, char **largv)
{
register char **t_pp;
register int len;
register unsigned numpat = largc, maxpat = largc + 2;
if ((Pat_pp = (char **)malloc(maxpat * sizeof(char *))) == (char **)NULL)
msg(EXT, nomemid, nomem);
t_pp = Pat_pp;
while (*largv) {
if ((*t_pp = (char *)malloc((unsigned int)strlen(*largv) + 1)) == (char *)NULL)
msg(EXT, nomemid, nomem);
(void)strcpy(*t_pp, *largv);
t_pp++;
largv++;
}
while (fgets(Nam_p, Max_namesz, Ef_p) != (char *)NULL) {
if (numpat == maxpat - 1) {
maxpat += 10;
if ((Pat_pp = (char **)realloc((char *)Pat_pp, maxpat * sizeof(char *))) == (char **)NULL)
msg(EXT, nomemid, nomem);
t_pp = Pat_pp + numpat;
}
len = strlen(Nam_p); /* includes the \n */
*(Nam_p + len - 1) = '\0'; /* remove the \n */
*t_pp = (char *)malloc((unsigned int)len);
if(*t_pp == (char *) NULL)
msg(EXT, nomemid, nomem);
(void)strcpy(*t_pp, Nam_p);
t_pp++;
numpat++;
}
*t_pp = (char *)NULL;
}
static void
ioerror(int dir)
{
register int t_errno;
register int archtype;
t_errno = errno;
errno = 0;
if (rmtfstat(Archive, &ArchSt) < 0)
msg(EXTN, badaccarchid, badaccarch);
errno = t_errno;
archtype = (int)ArchSt.st_mode & Ftype;
if ((archtype != S_IFCHR) && (archtype != S_IFBLK)) {
if (dir) {
if (errno == EFBIG)
msg(EXT, ":113", "ulimit reached for output file.");
else if (errno == ENOSPC)
msg(EXT, ":114", "No space left for output file.");
else
msg(EXTN, ":115", "I/O error - cannot continue");
} else
msg(EXT, ":116", "Unexpected end-of-file encountered.");
} else {
if (chknomedia(Archive)) {
msg(EXT, _SGI_MMX_cpio_dooropen, "\007No tape in drive or drive door open");
}
if (dir)
msg(EXTN, ":117", "\007I/O error on output");
else
msg(EXTN, ":118", "\007I/O error on input");
}
}
/*
* matched: Determine if a filename matches the specified pattern(s). If the
* pattern is matched (the first return), return 0 if -f was specified, else
* return 1. If the pattern is not matched (the second return), return 0 if
* -f was not specified, else return 1.
*/
static int
matched(void)
{
register char *str_p = G_p->g_nam_p;
register char **pat_pp = Pat_pp;
while (*pat_pp) {
if ((**pat_pp == '!' && !gmatch(str_p, *pat_pp + 1)) || gmatch(str_p, *pat_pp))
return(!(Args & OCf)); /* matched */
pat_pp++;
}
return(Args & OCf); /* not matched */
}
/*
* missdir: Create missing directories for files.
* (Possible future performance enhancement, if missdir is called, we know
* that at least the very last directory of the path does not exist, therefore,
* scan the path from the end
*/
static int
missdir(char *nam_p)
{
register char *c_p;
register int cnt = 2;
if (*(c_p = nam_p) == '/') /* skip over 'root slash' */
c_p++;
for (; *c_p; ++c_p) {
if (*c_p == '/') {
*c_p = '\0';
if (stat64(nam_p, &DesSt) < 0) {
if (Args & OCd) {
(void)umask(Orig_umask);
cnt = mkdir(nam_p, Def_mode);
(void)umask(0);
if (cnt != 0) {
*c_p = '/';
return(cnt);
}
} else {
msg(ERR, ":119", "Missing -d option.");
*c_p = '/';
return(-1);
}
}
*c_p = '/';
}
}
if (cnt == 2) /* the file already exists */
cnt = 0;
return(cnt);
}
/*
* mklong: Convert two shorts into one long. For VAX, Interdata ...
*/
static ulong
mklong(short v[])
{
union swpbuf swp_b;
swp_b.s_word = 1;
if (swp_b.s_byte[0]) {
swp_b.s_half[0] = v[1];
swp_b.s_half[1] = v[0];
} else {
swp_b.s_half[0] = v[0];
swp_b.s_half[1] = v[1];
}
return swp_b.s_word;
}
/*
* mkshort: Convert a long into 2 shorts, for VAX, Interdata ...
*/
static void
mkshort(short sval[], long v)
{
union swpbuf *swp_p, swp_b;
swp_p = (union swpbuf *)sval;
swp_b.s_word = 1;
if (swp_b.s_byte[0]) {
swp_b.s_word = v;
swp_p->s_half[0] = swp_b.s_half[1];
swp_p->s_half[1] = swp_b.s_half[0];
} else {
swp_b.s_word = v;
swp_p->s_half[0] = swp_b.s_half[0];
swp_p->s_half[1] = swp_b.s_half[1];
}
}
/*
* msg: Print either a message (no error) (POST), an error message with or
* without the errno (ERRN or ERR), or print an error message with or without
* the errno and exit (EXTN or EXT).
*/
static void
msg(int severity,...)
{
register char *fmt_p, *fmt_pid;
register FILE *file_p;
va_list v_Args;
int Errno = errno;
if ((Args & OCV) && Verbcnt) { /* clear current line of dots */
(void)fputc('\n', Out_p);
Verbcnt = 0;
}
va_start(v_Args,severity);
if (severity == POST)
file_p = Out_p;
else
if (severity == EPOST)
file_p = Err_p;
else {
file_p = Err_p;
Error_cnt++;
}
fmt_pid = va_arg(v_Args, char *);
fmt_p = va_arg(v_Args, char *);
(void)fflush(Out_p);
(void)fflush(Err_p);
if ((severity != POST) && (severity != EPOST))
(void)pfmt(file_p, severity == WARN?MM_WARNING:MM_ERROR, NULL);
(void)vfprintf(file_p, fmt_pid ? gettxt(fmt_pid, fmt_p) : fmt_p, v_Args);
if (severity == ERRN || severity == EXTN) {
(void)pfmt(file_p, MM_NOSTD, ":120:, Errno %d, %s\n", Errno,
strerror(Errno));
} else
(void)fprintf(file_p, "\n");
(void)fflush(file_p);
va_end(v_Args);
if (severity == EXT || severity == EXTN) {
if (Error_cnt == 1)
(void)pfmt(file_p, MM_NOSTD, ":121:1 error\n");
else
(void)pfmt(file_p, MM_NOSTD, ":122:%d errors\n", Error_cnt);
exit(Error_cnt);
}
}
/*
* openout: Open files for output and set all necessary information.
* If the u option is set (unconditionally overwrite existing files),
* and the current file exists, get a temporary file name from mktemp(3C),
* link the temporary file to the existing file, and remove the existing file.
* Finally either creat(2), mkdir(2) or mknod(2) as appropriate.
*
*/
static int
openout(void)
{
register char *nam_p;
register int cnt, result;
if (Args & OCp)
nam_p = Fullnam_p;
else
nam_p = G_p->g_nam_p;
if (Max_filesz < G_p->g_filesz && G_p->g_filesz >= 0) {
msg(ERR, ":123", "Skipping \"%s\": exceeds rlimit by %lld bytes ",
nam_p, G_p->g_filesz - Max_filesz);
return(-1);
}
if (!lstat64(nam_p, &DesSt) && creat_tmp(nam_p) < 0)
return(-1);
cnt = 0;
do {
(void)umask(Orig_umask);
errno = 0;
if ((G_p->g_mode & Ftype) == S_IFLNK) {
if ((!(Args & OCp)) && !(Hdr_type == USTAR)) {
FILL(Gen.g_filesz);
(void)strncpy(Symlnk_p, Buffr.b_out_p, G_p->g_filesz);
*(Symlnk_p + G_p->g_filesz) = '\0';
} else if ((!(Args & OCp)) && (Hdr_type == USTAR)) {
(void)strcpy(Symlnk_p, &Thdr_p->tbuf.t_linkname[0]);
}
if ((result = symlink(Symlnk_p, nam_p)) >= 0) {
cnt = 0;
if (Over_p != NULL) {
(void)unlink(Over_p);
*Over_p = '\0';
}
break;
}
} else if ((result = creat(nam_p, (int)G_p->g_mode)) >= 0) {
cnt = 0;
break;
}
cnt++;
} while (cnt < 2 && !missdir(nam_p));
(void)umask(0);
switch (cnt) {
case 0:
if ((Args & OCi) && (Hdr_type == USTAR))
setpasswd(nam_p);
if ((G_p->g_mode & Ftype) == S_IFLNK) {
if (!Uid) {
if (Args & OCR) {
if (lchown(nam_p, Rpw_p->pw_uid, Rpw_p->pw_gid) < 0)
msg(ERRN, badchownid, badchown, nam_p);
} else {
if (lchown(nam_p, (int)G_p->g_uid, (int)G_p->g_gid) < 0)
msg(ERRN, badchownid, badchown, nam_p);
}
}
break;
}
if (!Uid && chown(nam_p, (int)G_p->g_uid, (int)G_p->g_gid) < 0)
msg(ERRN, badchownid, badchown, nam_p);
break;
case 1:
msg(ERRN, badcreatdirid, badcreatdir, nam_p);
break;
case 2:
msg(ERRN, badcreateid, badcreate, nam_p);
break;
default:
msg(EXT, badcaseid, badcase);
}
Finished = 0;
return(result);
}
/*
* read_hdr: Transfer headers from the selected format
* in the archive I/O buffer to the generic structure.
*/
static int
read_hdr(int hdr)
{
register int rv = NONE;
major_t maj, rmaj;
minor_t min, rmin;
char tmpnull;
if (Buffr.b_end_p != (Buffr.b_out_p + Hdrsz)) {
tmpnull = *(Buffr.b_out_p + Hdrsz);
*(Buffr.b_out_p + Hdrsz) = '\0';
}
switch (hdr) {
case BIN:
(void)memcpy(&Hdr, Buffr.b_out_p, HDRSZ);
if ((Hdr.h_magic == (short)CMN_BBS) && !(Args & OCs))
msg(EXT, _SGI_MMX_cpio_bswap, "Byte swapped data - re-try with correct device");
if (Hdr.h_magic == (short)CMN_BBS)
swap((char *)&Hdr,HDRSZ);
Gen.g_magic = Hdr.h_magic;
Gen.g_mode = Hdr.h_mode;
Gen.g_uid = Hdr.h_uid;
Gen.g_gid = Hdr.h_gid;
Gen.g_nlink = Hdr.h_nlink;
Gen.g_mtime = mklong(Hdr.h_mtime);
Gen.g_ino = Hdr.h_ino;
if (Hdr.h_dev <= 0x7fff) {
maj = cpioMAJOR(Hdr.h_dev);
min = cpioMINOR(Hdr.h_dev);
Gen.g_dev = makedev(maj, min);
/* only for rdev, not dev; Olson, 4/97; this makes us able to
* backup all the possible dev_t's with -K, which requires
* binary mode, so no expanded rdev; this is supposed to be
* an illegal value for binary mode rdev, so we shouldn't see it
* in any non-irix archives... */
if((ushort)Hdr.h_rdev == 0xffff)
Gen.g_rdev = mklong(Hdr.h_filesize);
else {
rmaj = cpioMAJOR(Hdr.h_rdev);
rmin = cpioMINOR(Hdr.h_rdev);
Gen.g_rdev = makedev(rmaj,rmin);
}
}
else {
Gen.g_dev = Hdr.h_dev;
if((ushort)Hdr.h_rdev == 0xffff) /* see comments above */
Gen.g_rdev = mklong(Hdr.h_filesize);
else
Gen.g_rdev = Hdr.h_rdev;
}
Gen.g_cksum = 0L;
Gen.g_filesz = mklong(Hdr.h_filesize);
Gen.g_namesz = Hdr.h_namesize;
rv = BIN;
break;
case CHR:
if (sscanf(Buffr.b_out_p, "%6lo%6lo%6llo%6lo%6lo%6lo%6lo%6lo%11lo%6o%11llo",
&Gen.g_magic, &Gen.g_dev, &Gen.g_ino, &Gen.g_mode, &Gen.g_uid, &Gen.g_gid,
&Gen.g_nlink, &Gen.g_rdev, &Gen.g_mtime, &Gen.g_namesz, &Gen.g_filesz) == CHR_CNT) {
rv = CHR;
maj = cpioMAJOR(Gen.g_dev);
rmaj = cpioMAJOR(Gen.g_rdev);
min = cpioMINOR(Gen.g_dev);
rmin = cpioMINOR(Gen.g_rdev);
Gen.g_dev = makedev(maj, min);
Gen.g_rdev = makedev(rmaj,rmin);
}
break;
case ASC:
case CRC:
if (sscanf(Buffr.b_out_p, "%6lx%8llx%8lx%8lx%8lx%8lx%8lx%8llx%8x%8x%8x%8x%8x%8lx",
&Gen.g_magic, &Gen.g_ino, &Gen.g_mode, &Gen.g_uid, &Gen.g_gid, &Gen.g_nlink, &Gen.g_mtime,
&Gen.g_filesz, &maj, &min, &rmaj, &rmin, &Gen.g_namesz, &Gen.g_cksum) == ASC_CNT) {
Gen.g_dev = makedev(maj, min);
Gen.g_rdev = makedev(rmaj, rmin);
rv = hdr;
}
break;
case USTAR: /* TAR and USTAR */
if (*Buffr.b_out_p == '\0') {
*Gen.g_nam_p = '\0';
nambuf[0] = '\0';
} else {
Thdr_p = (union tblock *)Buffr.b_out_p;
prebuf[0] = Gen.g_nam_p[0] = '\0';
(void)sscanf(Thdr_p->tbuf.t_name, "%100s", nambuf);
(void)sscanf(Thdr_p->tbuf.t_mode, "%8lo", &Gen.g_mode);
(void)sscanf(Thdr_p->tbuf.t_uid, "%8lo", &Gen.g_uid);
(void)sscanf(Thdr_p->tbuf.t_gid, "%8lo", &Gen.g_gid);
(void)sscanf(Thdr_p->tbuf.t_size, "%12llo", &Gen.g_filesz);
(void)sscanf(Thdr_p->tbuf.t_mtime, "%12lo", &Gen.g_mtime);
(void)sscanf(Thdr_p->tbuf.t_cksum, "%8lo", &Gen.g_cksum);
if (Thdr_p->tbuf.t_linkname[0] != (char)NULL)
Gen.g_nlink = 1;
else
Gen.g_nlink = 0;
switch (Thdr_p->tbuf.t_typeflag) {
case '0' :
case '1' : /* hard link */
Gen.g_mode |= S_IFREG;
Aspec = 0;
Adir = 0;
break;
case '2' : /* symlink */
Gen.g_nlink = 2;
Gen.g_mode |= S_IFLNK;
Aspec = 0;
Adir = 0;
break;
case '3' :
Gen.g_mode |= S_IFCHR;
Aspec = 1;
break;
case '4' :
Gen.g_mode |= S_IFBLK;
Aspec = 1;
break;
case '5' :
Gen.g_mode |= S_IFDIR;
Adir = 1;
break;
case '6' :
Gen.g_mode |= S_IFIFO;
Aspec = 1;
break;
}
/*
* Do not need to be filled here used only to write out
* not read - also cause a core dump (#198374) because
* it is not properly aligned (char vs. long).
*
* (void)sscanf(Thdr_p->tbuf.t_magic, "%8lo", Gen.g_tmagic);
* (void)sscanf(Thdr_p->tbuf.t_version, "%8lo", Gen.g_version);
*/
(void)sscanf(Thdr_p->tbuf.t_uname, "%31s", Gen.g_uname);
(void)sscanf(Thdr_p->tbuf.t_gname, "%31s", Gen.g_gname);
(void)sscanf(Thdr_p->tbuf.t_devmajor, "%8lo", &Gen.g_dev);
(void)sscanf(Thdr_p->tbuf.t_devminor, "%8lo", &Gen.g_rdev);
(void)sscanf(Thdr_p->tbuf.t_prefix, "%155s", prebuf);
Gen.g_namesz = strlen(Gen.g_nam_p) + 1;
Gen.g_rdev = makedev(Gen.g_dev, Gen.g_rdev);
/*
* I don't know why this is here??? Since no st_dev information
* is passed to any tar structure and "maj" and "min" are not
* filled during the sscanf.
*/
Gen.g_dev = makedev(maj, min);
}
rv = USTAR;
break;
case TAR:
if (*Buffr.b_out_p == '\0')
*Gen.g_nam_p = '\0';
else {
Thdr_p = (union tblock *)Buffr.b_out_p;
Gen.g_nam_p[0] = '\0';
(void)sscanf(Thdr_p->tbuf.t_mode, "%lo", &Gen.g_mode);
(void)sscanf(Thdr_p->tbuf.t_uid, "%lo", &Gen.g_uid);
(void)sscanf(Thdr_p->tbuf.t_gid, "%lo", &Gen.g_gid);
(void)sscanf(Thdr_p->tbuf.t_size, "%llo", &Gen.g_filesz);
(void)sscanf(Thdr_p->tbuf.t_mtime, "%lo", &Gen.g_mtime);
(void)sscanf(Thdr_p->tbuf.t_cksum, "%lo", &Gen.g_cksum);
if (Thdr_p->tbuf.t_typeflag == '1')
Gen.g_nlink = 1;
else
Gen.g_nlink = 0;
(void)sscanf(Thdr_p->tbuf.t_name, "%s", Gen.g_nam_p);
Gen.g_namesz = strlen(Gen.g_nam_p) + 1;
}
rv = TAR;
break;
default:
msg(EXT, badhdrid, badhdr);
}
if (Buffr.b_end_p != (Buffr.b_out_p + Hdrsz))
*(Buffr.b_out_p + Hdrsz) = tmpnull;
return(rv);
}
/*
* reclaim: Reclaim linked file structure storage.
*/
static void
reclaim( struct Lnk *l_p)
{
register struct Lnk *tl_p;
l_p->L_bck_p->L_nxt_p = l_p->L_nxt_p;
l_p->L_nxt_p->L_bck_p = l_p->L_bck_p;
while (l_p != (struct Lnk *)NULL) {
tl_p = l_p->L_lnk_p;
free(l_p->L_gen.g_nam_p);
free(l_p);
l_p = tl_p;
}
}
/*
* rstbuf: Reset the I/O buffer, move incomplete potential headers to
* the front of the buffer and force bread() to refill the buffer. The
* return value from bread() is returned (to identify I/O errors). On the
* 3B2, reads must begin on a word boundary, therefore, with the -i option,
* any remaining bytes in the buffer must be moved to the base of the buffer
* in such a way that the destination locations of subsequent reads are
* word aligned.
*/
static void
rstbuf(void)
{
register int pad;
if ((Args & OCi) || Append) {
if (Buffr.b_out_p != Buffr.b_base_p) {
pad = ((Buffr.b_cnt + FULLWD) & ~FULLWD);
Buffr.b_in_p = Buffr.b_base_p + pad;
pad -= Buffr.b_cnt;
(void)memcpy(Buffr.b_base_p + pad, Buffr.b_out_p, (int)Buffr.b_cnt);
Buffr.b_out_p = Buffr.b_base_p + pad;
}
if (bfill() < 0)
msg(EXT, ":124", "Unexpected end-of-archive encountered.");
} else { /* OCo */
(void)memcpy(Buffr.b_base_p, Buffr.b_out_p, (int)Buffr.b_cnt);
Buffr.b_out_p = Buffr.b_base_p;
Buffr.b_in_p = Buffr.b_base_p + Buffr.b_cnt;
}
}
static void
setpasswd(char *nam)
{
if ((dpasswd = getpwnam(&Gen.g_uname[0])) == (struct passwd *)NULL) {
msg(WARN, badpasswdid, badpasswd, &Gen.g_uname[0]);
msg(WARN, ":125", "%s: owner not changed", nam);
} else
Gen.g_uid = dpasswd->pw_uid;
if ((dgroup = getgrnam(&Gen.g_gname[0])) == (struct group *)NULL) {
msg(WARN, badgroupid, badgroup, &Gen.g_gname[0]);
msg(WARN, ":126", "%s: group not changed", nam);
} else
Gen.g_gid = dgroup->gr_gid;
G_p = &Gen;
}
/*
* rstfiles: Perform final changes to the file. If the -u option is set,
* and overwrite == U_OVER, remove the temporary file, else if overwrite
* == U_KEEP, unlink the current file, and restore the existing version
* of the file. In addition, where appropriate, set the access or modification
* times, change the owner and change the modes of the file.
*/
static void
rstfiles(int over)
{
register char *inam_p, *onam_p, *nam_p;
if (Args & OCp)
nam_p = Fullnam_p;
else
if (Gen.g_nlink > (ulong)0)
nam_p = G_p->g_nam_p;
else
nam_p = Gen.g_nam_p;
if ((Args & OCi) && (Hdr_type == USTAR))
setpasswd(nam_p);
if (over == U_KEEP && *Over_p != '\0') {
msg(POST, ":127", "Restoring existing \"%s\"", nam_p);
(void)unlink(nam_p);
if (rename(Over_p, nam_p) < 0)
msg(EXTN, badorigid, badorig, nam_p);
*Over_p = '\0';
return;
} else if (over == U_OVER && *Over_p != '\0') {
if (unlink(Over_p) < 0)
msg(ERRN, badremtmpid, badremtmp, Over_p);
*Over_p = '\0';
}
if (Args & OCp) {
inam_p = Nam_p;
onam_p = Fullnam_p;
} else /* OCi only uses onam_p, OCo only uses inam_p */
inam_p = onam_p = G_p->g_nam_p;
if ((Args & OCm) && !S_ISLNK(G_p->g_mode))
set_tym(onam_p, (time_t)G_p->g_mtime, (time_t)G_p->g_mtime);
if (Uid == 0) {
if (!(Args & OCo)) {
if (!S_ISLNK(G_p->g_mode) && chmod(onam_p, (int)G_p->g_mode) < 0)
msg(ERRN, badchmodid, badchmod, onam_p);
}
}
#ifdef sgi
else if (Adir && Fix_mode) {
if (!S_ISLNK(G_p->g_mode) && chmod(onam_p, (int)(G_p->g_mode&(~Orig_umask))) < 0)
msg(ERRN, badchmodid, badchmod, onam_p);
}
#endif
if (Args & OCa)
set_tym(inam_p, SrcSt.st_atime, SrcSt.st_mtime);
if ((Args & OCR) && chown(onam_p, Rpw_p->pw_uid, Rpw_p->pw_gid) < 0)
msg(ERRN, badchownid, badchown, onam_p);
if ((Args & OCp) && !(Args & OCR)) {
if (!Uid) {
if (!S_ISLNK(G_p->g_mode) && chown(onam_p, (uid_t)G_p->g_uid, (uid_t)G_p->g_gid) < 0)
msg(ERRN, badchownid, badchown, onam_p);
else if (lchown(onam_p, (uid_t)G_p->g_uid, (uid_t)G_p->g_gid) < 0)
msg(ERRN, badchownid, badchown, onam_p);
}
} else { /* OCi only uses onam_p, OCo only uses inam_p */
if (!(Args & OCR)) {
if ((Args & OCi) && !Uid && (chown(inam_p, (uid_t)G_p->g_uid, (uid_t)G_p->g_gid) < 0))
msg(ERRN, badchownid, badchown, onam_p);
}
}
}
/*
* scan4trail: Scan the archive looking for the trailer.
* When found, back the archive up over the trailer and overwrite
* the trailer with the files to be added to the archive.
*/
static void
scan4trail(void)
{
register int rv;
register long off1, off2;
Append = 1;
Hdr_type = NONE;
G_p = (struct gen_hdr *)NULL;
while (gethdr()) {
G_p = &Gen;
data_in(P_SKIP);
}
off1 = Buffr.b_cnt;
off2 = Bufsize - (Buffr.b_cnt % Bufsize);
Buffr.b_out_p = Buffr.b_in_p = Buffr.b_base_p;
Buffr.b_cnt = 0L;
if (rmtlseek(Archive, -(off1 + off2), SEEK_REL) < 0)
msg(EXTN, badappendid, badappend);
if ((rv = g_read(Device, Archive, Buffr.b_in_p, Bufsize)) < 0)
msg(EXTN, badappendid, badappend);
if (rmtlseek(Archive, -rv, SEEK_REL) < 0)
msg(EXTN, badappendid, badappend);
Buffr.b_cnt = off2;
Buffr.b_in_p = Buffr.b_base_p + Buffr.b_cnt;
Append = 0;
}
/*
* setup: Perform setup and initialization functions. Parse the options
* using getopt(3C), call ckopts to check the options and initialize various
* structures and pointers. Specifically, for the -i option, save any
* patterns, for the -o option, check (via stat64(2)) the archive, and for
* the -p option, validate the destination directory.
*/
static void
setup(int largc, char **largv)
{
extern int optind;
extern char *optarg;
register char *opts_p = "abcdfiklmoprstuvABC:DE:H:I:KTWLM:O:R:SV6",
*dupl_p = "Only one occurrence of -%c allowed",
*dupl_pid = ":128";
register int option;
int blk_cnt;
int i;
struct rlimit64 rl;
Uid = getuid();
Orig_umask = umask(0);
Hdr_type = BIN;
Efil_p = Hdr_p = Own_p = IOfil_p = NULL;
if((largv[1] != NULL) && (*largv[1] != '-'))
usage();
while ((option = getopt(largc, largv, opts_p)) != EOF) {
switch (option) {
case 'a': /* reset access time */
Args |= OCa;
break;
case 'b': /* swap bytes and halfwords */
Args |= OCb;
break;
case 'c': /* select character header */
Args |= OCc;
Hdr_type = ASC;
Onecopy = 1;
break;
case 'd': /* create directories as needed */
Args |= OCd;
break;
case 'f': /* select files not in patterns */
Args |= OCf;
break;
case 'i': /* "copy in" */
Args |= OCi;
Archive = 0;
break;
case 'k': /* retry after I/O errors */
Args |= OCk;
break;
case 'l': /* link files when possible */
Args |= OCl;
break;
case 'm': /* retain modification time */
Args |= OCm;
break;
case 'o': /* "copy out" */
Args |= OCo;
Archive = 1;
break;
case 'p': /* "pass" */
Args |= OCp;
Hdr_type = ASC; /* for expanded dev_t stuff */
break;
case 'r': /* rename files interactively */
Args |= OCr;
break;
case 's': /* swap bytes */
Args |= OCs;
break;
case 't': /* table of contents */
Args |= OCt;
break;
case 'u': /* copy unconditionally */
Args |= OCu;
break;
case 'v': /* verbose - print file names */
Args |= OCv;
break;
case 'A': /* append to existing archive */
Args |= OCA;
break;
case 'B': /* set block size to 5120 bytes */
Args |= OCB;
Bufsize = 5120;
break;
case 'C': /* set arbitrary block size */
if (Args & OCC)
msg(ERR, dupl_pid, dupl_p, 'C');
else {
Args |= OCC;
Bufsize = atoi(optarg);
}
break;
case 'D': /* undocumented flag for libpkg */
Dflag = 1;
break;
case 'E': /* alternate file for pattern input */
if (Args & OCE)
msg(ERR, dupl_pid, dupl_p, 'E');
else {
Args |= OCE;
Efil_p = optarg;
}
break;
case 'H': /* select header type */
if (Args & OCH)
msg(ERR, dupl_pid, dupl_p, 'H');
else {
Args |= OCH;
Hdr_p = optarg;
}
break;
case 'I': /* alternate file for archive input */
if (Args & OCI)
msg(ERR, dupl_pid, dupl_p, 'I');
else {
Args |= OCI;
IOfil_p = optarg;
}
break;
case 'K': /* Don't ignore big files (used by xfs) */
Kflag = 1;
break;
case 'L': /* follow symbolic links */
Args |= OCL;
break;
case 'M': /* specify new end-of-media message */
if (Args & OCM)
msg(ERR, dupl_pid, dupl_p, 'M');
else {
Args |= OCM;
Eom_p = optarg;
Eom_pid = (char *)NULL;
}
break;
case 'O': /* alternate file for archive output */
if (Args & OCO)
msg(ERR, dupl_pid, dupl_p, 'O');
else {
Args |= OCO;
IOfil_p = optarg;
}
break;
case 'R': /* change owner/group of files */
if (Args & OCR)
msg(ERR, dupl_pid, dupl_p, 'R');
else {
Args |= OCR;
Own_p = optarg;
}
break;
case 'S': /* swap halfwords */
Args |= OCS;
break;
case 'V': /* print a dot '.' for each file */
Args |= OCV;
break;
case 'T': /* Test for differences (compare archive against filesystem) */
Compareflag++;
Args |= OCi|OCt; /* implies -it */
break;
case 'W': /* Deal with holey (xfs) files better by skipping hole */
Holeflag = 1;
break;
case '6': /* for old, sixth-edition files */
Args |= OC6;
Ftype = SIXTH;
break;
default:
Error_cnt++;
} /* option */
} /* (option = getopt(largc, largv, opts_p)) != EOF */
largc -= optind;
largv += optind;
ckopts(Args);
if (!Error_cnt) {
if ((Buf_p = align(CPIOBSZ)) == (char *)-1)
msg(EXTN, nomemid, nomem);
if ((Empty = align(TARSZ)) == (char *)-1)
msg(EXTN, nomemid, nomem);
if ((Args & OCr) && (Renam_p = (char *)malloc(APATH)) == (char *)NULL)
msg(EXTN, nomemid, nomem);
if ((Symlnk_p = (char *)malloc(APATH)) == (char *)NULL)
msg(EXTN, nomemid, nomem);
if ((Over_p = (char *)malloc(APATH)) == (char *)NULL)
msg(EXTN, nomemid, nomem);
if ((Nam_p = (char *)malloc(APATH)) == (char *)NULL)
msg(EXTN, nomemid, nomem);
if ((Fullnam_p = (char *)malloc(APATH)) == (char *)NULL)
msg(EXTN, nomemid, nomem);
if ((Lnknam_p = (char *)malloc(APATH)) == (char *)NULL)
msg(EXTN, nomemid, nomem);
Gen.g_nam_p = Nam_p;
if (Args & OCi) {
if (largc > 0) /* save patterns for -i option, if any */
Pat_pp = largv;
if (Args & OCE)
getpats(largc, largv);
} else if (Args & OCo) {
if (largc != 0) /* error if arguments left with -o */
Error_cnt++;
else if (rmtfstat(Archive, &ArchSt) < 0)
msg(ERRN, badaccarchid, badaccarch);
switch (Hdr_type) {
case BIN:
Hdrsz = HDRSZ;
Pad_val = HALFWD;
break;
case CHR:
Hdrsz = CHRSZ;
Pad_val = 0;
break;
case ASC:
case CRC:
Hdrsz = ASCSZ;
Pad_val = FULLWD;
break;
case TAR:
/* FALLTHROUGH */
case USTAR: /* TAR and USTAR */
Hdrsz = TARSZ;
Pad_val = FULLBK;
break;
default:
msg(EXT, badhdrid, badhdr);
}
} else { /* directory must be specified */
if (largc != 1)
Error_cnt++;
else if (access(*largv, 2) < 0)
msg(ERRN, badaccessid, badaccess, *largv);
}
}
if (Error_cnt)
usage(); /* exits! */
if (Args & (OCi | OCo)) {
long bsize;
if (!Dflag) {
if (Args & (OCB | OCC)) {
if (g_init(&Device, &Archive) < 0)
msg(EXTN, badinitid, badinit);
} else {
if ((Bufsize = g_init(&Device, &Archive)) < 0)
msg(EXTN, badinitid, badinit);
}
}
blk_cnt = _20K / Bufsize;
blk_cnt = (blk_cnt >= MX_BUFS) ? blk_cnt : MX_BUFS;
while (blk_cnt > 1) {
bsize = Bufsize * blk_cnt;
if(bsize < (2*CPIOBSZ))
bsize = 2*CPIOBSZ;
if ((Buffr.b_base_p = align(bsize)) != (char *)-1) {
Buffr.b_out_p = Buffr.b_in_p = Buffr.b_base_p;
Buffr.b_cnt = 0L;
Buffr.b_size = bsize;
Buffr.b_end_p = Buffr.b_base_p + Buffr.b_size;
break;
}
blk_cnt--;
}
if (blk_cnt < 2)
msg(EXT, nomemid, nomem);
if(Compareflag) {
cmpbuf = align(bsize);
if(cmpbuf == (char *)-1)
msg(EXT, nomemid, nomem);
}
}
if (Args & OCp) { /* get destination directory */
(void)strcpy(Fullnam_p, *largv);
if (stat64(Fullnam_p, &DesSt) < 0)
msg(EXTN, badaccessid, badaccess, Fullnam_p);
if ((DesSt.st_mode & Ftype) != S_IFDIR)
msg(EXT, ":130", "\"%s\" is not a directory", Fullnam_p);
}
Full_p = Fullnam_p + strlen(Fullnam_p) - 1;
if (*Full_p != '/') {
Full_p++;
*Full_p = '/';
}
Full_p++;
*Full_p = '\0';
(void)strcpy(Lnknam_p, Fullnam_p);
Lnkend_p = Lnknam_p + strlen(Lnknam_p);
getrlimit64(RLIMIT_FSIZE, &rl);
Max_filesz = rl.rlim_cur;
for (i = 0; i < NLNKHASH; i++) {
Lnk_hd[i].L_nxt_p = Lnk_hd[i].L_bck_p = &Lnk_hd[i];
Lnk_hd[i].L_lnk_p = (struct Lnk *)NULL;
}
}
/*
* set_tym: Set the access and/or modification times for a file.
*/
static void
set_tym(char *nam_p, time_t atime, time_t mtime)
{
struct utimbuf timev;
timev.actime = atime;
timev.modtime = mtime;
if (utime(nam_p, &timev) < 0) {
if (Args & OCa)
if (errno == ENOSYS)
msg(WARN, _SGI_MMX_cpio_settym, "Cannot reset time on \"%s\" : Operation not supported", nam_p);
else
msg(WARN, ":131", "Cannot reset access time for \"%s\"", nam_p);
else
msg(WARN, ":132", "Cannot reset modification time for \"%s\"", nam_p);
}
}
/*
* sigint: Catch interrupts. If an interrupt occurs during the extraction
* of a file from the archive with the -u option set, and the filename did
* exist, remove the current file and restore the original file. Then exit.
*/
static void
sigint(void)
{
register char *nam_p;
(void)signal(SIGINT, SIG_IGN); /* block further signals */
if (!Finished) {
if (Args & OCi)
nam_p = G_p->g_nam_p;
else /* OCp */
nam_p = Fullnam_p;
if (*Over_p != '\0') { /* There is a temp file */
if (unlink(nam_p))
msg(ERRN, badremincid, badreminc, nam_p);
if (rename(Over_p, nam_p) < 0)
msg(ERRN, badorigid, badorig, nam_p);
} else if (unlink(nam_p))
msg(ERRN, badremincid, badreminc, nam_p);
}
exit(Error_cnt);
}
/*
* swap: Swap bytes (-s), halfwords (-S) or or both halfwords and bytes (-b).
*/
static void
swap(char *buf_p, int cnt)
{
register unsigned char tbyte;
register int tcnt;
register int rcnt;
register ushort thalf;
rcnt = cnt % 4;
cnt /= 4;
if (Args & (OCb | OCs | BSM)) {
tcnt = cnt;
Swp_p = (union swpbuf *)buf_p;
while (tcnt-- > 0) {
tbyte = Swp_p->s_byte[0];
Swp_p->s_byte[0] = Swp_p->s_byte[1];
Swp_p->s_byte[1] = tbyte;
tbyte = Swp_p->s_byte[2];
Swp_p->s_byte[2] = Swp_p->s_byte[3];
Swp_p->s_byte[3] = tbyte;
Swp_p++;
}
if (rcnt >= 2) {
tbyte = Swp_p->s_byte[0];
Swp_p->s_byte[0] = Swp_p->s_byte[1];
Swp_p->s_byte[1] = tbyte;
tbyte = Swp_p->s_byte[2];
}
}
if (Args & (OCb | OCS)) {
tcnt = cnt;
Swp_p = (union swpbuf *)buf_p;
while (tcnt-- > 0) {
thalf = Swp_p->s_half[0];
Swp_p->s_half[0] = Swp_p->s_half[1];
Swp_p->s_half[1] = thalf;
Swp_p++;
}
}
}
/*
* usage: Print the usage message on stderr and exit.
*/
static void
usage(void)
{
(void)fflush(stdout);
(void)pfmt(stderr, MM_ACTION, ":133:Usage:\n");
(void)pfmt(stderr, MM_NOSTD, ":134:\tcpio -i[bcdfkmrstuvBSVT6] [-C size]");
(void)pfmt(stderr, MM_NOSTD, ":135:[-E file] [-H hdr] [[-I file] [-M msg]] ");
(void)pfmt(stderr, MM_NOSTD, ":136:[-R id] [patterns]\n");
(void)pfmt(stderr, MM_NOSTD, ":137:\tcpio -o[acvABKLVW] [-C size] ");
(void)pfmt(stderr, MM_NOSTD, ":138:[-H hdr] [[-M msg] [-O file]]\n");
(void)pfmt(stderr, MM_NOSTD, ":139:\tcpio -p[adlmuvLV] [-R id] directory\n");
(void)fflush(stderr);
exit(Error_cnt);
}
/*
* verbose: For each file, print either the filename (-v) or a dot (-V).
* If the -t option (table of contents) is set, print either the filename,
* or if the -v option is also set, print an "ls -l"-like listing.
*/
static void
verbose(char *nam_p)
{
register int i, j, temp;
mode_t mode;
char modestr[11];
for (i = 0; i < 10; i++)
modestr[i] = '-';
modestr[i] = '\0';
if ((Args & OCt) && (Args & OCv)) {
mode = (mode_t)Gen.g_mode;
for (i = 0; i < 3; i++) {
temp = (int)(mode >> (6 - (i * 3)));
j = (i * 3) + 1;
if (S_IROTH & temp)
modestr[j] = 'r';
if (S_IWOTH & temp)
modestr[j + 1] = 'w';
if (S_IXOTH & temp)
modestr[j + 2] = 'x';
}
temp = (int)Gen.g_mode & Ftype;
switch (temp) {
case (S_IFIFO):
modestr[0] = 'p';
break;
case (S_IFCHR):
modestr[0] = 'c';
break;
case (S_IFDIR):
modestr[0] = 'd';
break;
case (S_IFBLK):
modestr[0] = 'b';
break;
case (S_IFREG): /* was initialized to '-' */
if(Gen.g_rdev==1)
modestr[0] = 'H'; /* holey file */
break;
case (S_IFLNK):
modestr[0] = 'l';
break;
default:
msg(ERR, ":140", "Impossible file type");
}
if ((S_ISUID & Gen.g_mode) == S_ISUID)
modestr[3] = 's';
if ((S_ISVTX & Gen.g_mode) == S_ISVTX)
modestr[9] = 't';
if ((S_ISGID & G_p->g_mode) == S_ISGID && modestr[6] == 'x')
modestr[6] = 's';
else if ((S_ENFMT & Gen.g_mode) == S_ENFMT && modestr[6] != 'x')
modestr[6] = 'l';
if ((Hdr_type == USTAR || Hdr_type == TAR) && Gen.g_nlink == 0)
(void)printf("%s%4d ", modestr, Gen.g_nlink+1);
else
(void)printf("%s%4d ", modestr, Gen.g_nlink);
if (Lastuid == (int)Gen.g_uid)
(void)printf("%-9s", Curpw_p->pw_name);
else {
setpwent();
if (Curpw_p = getpwuid((int)Gen.g_uid)) {
(void)printf("%-9s", Curpw_p->pw_name);
Lastuid = (int)Gen.g_uid;
} else {
(void)printf("%-9d", Gen.g_uid);
Lastuid = -1;
}
}
if (Lastgid == (int)Gen.g_gid)
(void)printf("%-9s", Curgr_p->gr_name);
else {
setgrent();
if (Curgr_p = getgrgid((int)Gen.g_gid)) {
(void)printf("%-9s", Curgr_p->gr_name);
Lastgid = (int)Gen.g_gid;
} else {
(void)printf("%-9d", Gen.g_gid);
Lastgid = -1;
}
}
if (!Aspec || ((Gen.g_mode & Ftype) == S_IFIFO))
(void)printf("%13lld ", Gen.g_filesz);
else
(void)printf("%3d,%3d ", major(Gen.g_rdev), minor(Gen.g_rdev));
(void)cftime(Time, gettxt(FORMATID, FORMAT), (time_t *)&Gen.g_mtime);
(void)printf("%s %s", Time, nam_p);
if ((Gen.g_mode & Ftype) == S_IFLNK) {
if (Hdr_type == USTAR || Hdr_type == TAR)
(void)strcpy(Symlnk_p, Thdr_p->tbuf.t_linkname);
else {
if(!Compareflag) /* else Buffr.b_out_p setup right */
FILL(Gen.g_filesz);
(void)strncpy(Symlnk_p, Buffr.b_out_p, Gen.g_filesz);
*(Symlnk_p + Gen.g_filesz) = '\0';
}
(void)printf(" -> %s", Symlnk_p);
}
(void)printf("\n");
} else if ((Args & OCt) || (Args & OCv)) {
(void)fputs(nam_p, Out_p);
(void)fputc('\n', Out_p);
} else { /* OCV */
(void)fputc('.', Out_p);
if (Verbcnt++ >= 49) { /* start a new line of dots */
Verbcnt = 0;
(void)fputc('\n', Out_p);
}
}
(void)fflush(Out_p);
}
/*
* write_hdr: Transfer header information for the generic structure
* into the format for the selected header and bwrite() the header.
*/
static void
write_hdr(void)
{
static int uid_overflow_warning = 0;
register int cnt, pad;
switch (Hdr_type) {
case BIN:
case CHR:
case ASC:
case CRC:
cnt = Hdrsz + (int)G_p->g_namesz;
break;
case TAR:
/*FALLTHROUGH*/
case USTAR: /* TAR and USTAR */
cnt = TARSZ;
break;
default:
msg(EXT, badhdrid, badhdr);
}
FLUSH(cnt);
switch (Hdr_type) {
case BIN:
if ((G_p->g_uid > BIN_MAXUID || G_p->g_gid > BIN_MAXUID) &&
!uid_overflow_warning) {
uid_overflow_warning = BIN_MAXUID;
}
Hdr.h_magic = (short)G_p->g_magic;
Hdr.h_dev = (short)G_p->g_dev;
Hdr.h_ino = (ushort)G_p->g_ino;
Hdr.h_mode = (ushort)G_p->g_mode;
Hdr.h_uid = (ushort)(G_p->g_uid > BIN_MAXUID ? UID_NOBODY : G_p->g_uid);
Hdr.h_gid = (ushort)(G_p->g_gid > BIN_MAXUID ? GID_NOBODY : G_p->g_gid);
Hdr.h_nlink = (short)G_p->g_nlink;
Hdr.h_rdev = (short)G_p->g_rdev;
mkshort(Hdr.h_mtime, (long)G_p->g_mtime);
Hdr.h_namesize = (short)G_p->g_namesz;
mkshort(Hdr.h_filesize, (long)G_p->g_filesz);
(void)strcpy(Hdr.h_name, G_p->g_nam_p);
(void)memcpy(Buffr.b_in_p, &Hdr, cnt);
break;
case CHR:
if ((G_p->g_uid > CHR_MAXUID || G_p->g_gid > CHR_MAXUID) &&
!uid_overflow_warning) {
uid_overflow_warning = CHR_MAXUID;
}
(void)sprintf(Buffr.b_in_p, "%.6lo%.6lo%.6llo%.6lo%.6lo%.6lo%.6lo%.6lo%.11lo%.6lo%.11llo%s",
G_p->g_magic, G_p->g_dev, G_p->g_ino, G_p->g_mode,
(G_p->g_uid > CHR_MAXUID ? UID_NOBODY : G_p->g_uid),
(G_p->g_gid > CHR_MAXUID ? GID_NOBODY : G_p->g_gid),
G_p->g_nlink, G_p->g_rdev, G_p->g_mtime, G_p->g_namesz, G_p->g_filesz, G_p->g_nam_p);
break;
case ASC:
case CRC:
(void)sprintf(Buffr.b_in_p, "%.6lx%.8llx%.8lx%.8lx%.8lx%.8lx%.8lx%.8llx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%s",
G_p->g_magic, G_p->g_ino, G_p->g_mode, G_p->g_uid, G_p->g_gid, G_p->g_nlink, G_p->g_mtime,
G_p->g_filesz, major(G_p->g_dev), minor(G_p->g_dev), major(G_p->g_rdev), minor(G_p->g_rdev),
G_p->g_namesz, G_p->g_cksum, G_p->g_nam_p);
break;
case USTAR: /* USTAR */
/* N.B. We don't print a warning when the uid/gid overflows.
* Since we're saving the uname/gname too, chances are that we'll
* still be able to do the right thing at extraction time.
*/
Thdr_p = (union tblock *)Buffr.b_in_p;
(void)memcpy(Thdr_p, Empty, TARSZ);
(void)strncpy(Thdr_p->tbuf.t_name, G_p->g_tname, strlen(G_p->g_tname));
(void)sprintf(Thdr_p->tbuf.t_mode, "%07o", (G_p->g_mode & 0xfffL));
(void)sprintf(Thdr_p->tbuf.t_uid, "%07o",
(G_p->g_uid > USTAR_MAXUID ? UID_NOBODY : G_p->g_uid));
(void)sprintf(Thdr_p->tbuf.t_gid, "%07o",
(G_p->g_gid > USTAR_MAXUID ? GID_NOBODY : G_p->g_gid));
#ifndef sgi
(void)sprintf(Thdr_p->tbuf.t_size, "%011lo", G_p->g_filesz);
#endif
(void)sprintf(Thdr_p->tbuf.t_mtime, "%011lo", G_p->g_mtime);
Thdr_p->tbuf.t_typeflag = G_p->g_typeflag;
#ifdef sgi
if (T_lname[0] != '\0') {
if (strlen(T_lname) > NAMSIZ) {
msg(EPOST, ":86",
"%s: filename is greater than %d",
T_lname, NAMSIZ);
/*
* Ensure that we don't write a record.
*/
G_p->g_filesz = 0LL;
return;
}
(void)sprintf(Thdr_p->tbuf.t_size, "%011o",
0L);
Thdr_p->tbuf.t_typeflag = S_ISLNK(G_p->g_mode) ? '2' : '1';
} else {
if (Thdr_p->tbuf.t_typeflag == '1')
Thdr_p->tbuf.t_typeflag = '0';
(void)sprintf(Thdr_p->tbuf.t_size, "%011llo",
G_p->g_filesz);
}
#endif
if (T_lname[0] != '\0') {
int n = strlen(T_lname)+1; /* including \0 ! */
if(n > sizeof(Thdr_p->tbuf.t_linkname))
n = sizeof(Thdr_p->tbuf.t_linkname);
(void)strncpy(Thdr_p->tbuf.t_linkname, T_lname, n);
}
(void)sprintf(Thdr_p->tbuf.t_magic, "%s", TMAGIC);
(void)sprintf(Thdr_p->tbuf.t_version, "%2s", TVERSION);
(void)sprintf(Thdr_p->tbuf.t_uname, "%s", G_p->g_uname);
(void)sprintf(Thdr_p->tbuf.t_gname, "%s", G_p->g_gname);
if (Aspec) {
(void)sprintf(Thdr_p->tbuf.t_devmajor, "%07o", major(G_p->g_rdev));
(void)sprintf(Thdr_p->tbuf.t_devminor, "%07o", minor(G_p->g_rdev));
} else {
(void)sprintf(Thdr_p->tbuf.t_devmajor, "%07o", major(G_p->g_dev));
(void)sprintf(Thdr_p->tbuf.t_devminor, "%07o", minor(G_p->g_dev));
}
if (Gen.g_prefix != (char *) NULL)
(void)sprintf(Thdr_p->tbuf.t_prefix, "%s", Gen.g_prefix);
(void)sprintf(Thdr_p->tbuf.t_cksum, "%07o", (int)cksum(TARTYP, 0));
break;
case TAR:
if ((G_p->g_uid > TAR_MAXUID || G_p->g_gid > TAR_MAXUID) &&
!uid_overflow_warning) {
uid_overflow_warning = TAR_MAXUID;
}
Thdr_p = (union tblock *)Buffr.b_in_p;
(void)memcpy(Thdr_p, Empty, TARSZ);
(void)strncpy(Thdr_p->tbuf.t_name, G_p->g_nam_p, G_p->g_namesz);
(void)sprintf(Thdr_p->tbuf.t_mode, "%07o ", G_p->g_mode);
(void)sprintf(Thdr_p->tbuf.t_uid, "%07o ",
(G_p->g_uid > TAR_MAXUID ? UID_NOBODY : G_p->g_uid));
(void)sprintf(Thdr_p->tbuf.t_gid, "%07o ",
(G_p->g_gid > TAR_MAXUID ? UID_NOBODY : G_p->g_gid));
(void)sprintf(Thdr_p->tbuf.t_size, "%011llo ", G_p->g_filesz);
(void)sprintf(Thdr_p->tbuf.t_mtime, "%011o ", G_p->g_mtime);
if (T_lname[0] != '\0')
Thdr_p->tbuf.t_typeflag = S_ISLNK(G_p->g_mode) ? '2' : '1';
else
Thdr_p->tbuf.t_typeflag = '\0';
(void)strncpy(Thdr_p->tbuf.t_linkname, T_lname, strlen(T_lname));
break;
default:
msg(EXT, ":48", "Impossible header type.");
} /* Hdr_type */
if (uid_overflow_warning > 0) {
msg(WARN, _SGI_MMX_cpio_uid_overflow,
"uid or gid > %d, using \"nobody\" instead", uid_overflow_warning);
/* only print this warning once */
uid_overflow_warning = -1;
}
Buffr.b_in_p += cnt;
Buffr.b_cnt += cnt;
pad = ((cnt + Pad_val) & ~Pad_val) - cnt;
if (pad != 0) {
FLUSH(pad);
(void)memcpy(Buffr.b_in_p, Empty, pad);
Buffr.b_in_p += pad;
Buffr.b_cnt += pad;
}
}
/*
* write_trail: Create the appropriate trailer for the selected header type
* and bwrite the trailer. Pad the buffer with nulls out to the next Bufsize
* boundary, and force a write. If the write completes, or if the trailer is
* completely written (but not all of the padding nulls (as can happen on end
* of medium)) return. Otherwise, the trailer was not completely written out,
* so re-pad the buffer with nulls and try again.
*/
static void
write_trail(void)
{
register int cnt, need;
switch (Hdr_type) {
case BIN:
case CHR:
case ASC:
case CRC:
Gen.g_mode = Gen.g_uid = Gen.g_gid = 0;
Gen.g_nlink = 1;
Gen.g_mtime = Gen.g_dev = 0;
Gen.g_rdev = Gen.g_cksum = 0;
Gen.g_filesz = Gen.g_ino = 0LL;
Gen.g_namesz = strlen("TRAILER!!!") + 1;
(void)strcpy(Gen.g_nam_p, "TRAILER!!!");
G_p = &Gen;
write_hdr();
break;
case TAR:
/*FALLTHROUGH*/
case USTAR: /* TAR and USTAR */
for (cnt = 0; cnt < 3; cnt++) {
FLUSH(TARSZ);
(void)memcpy(Buffr.b_in_p, Empty, TARSZ);
Buffr.b_in_p += TARSZ;
Buffr.b_cnt += TARSZ;
}
break;
default:
msg(EXT, badhdrid, badhdr);
}
need = Bufsize - (Buffr.b_cnt % Bufsize);
if(need == Bufsize)
need = 0;
while (Buffr.b_cnt > 0) {
while (need > 0) {
cnt = (need < TARSZ) ? need : TARSZ;
need -= cnt;
FLUSH(cnt);
(void)memcpy(Buffr.b_in_p, Empty, cnt);
Buffr.b_in_p += cnt;
Buffr.b_cnt += cnt;
}
bflush();
}
}
/* check to see if it's a drive, and is in audio mode; if it is, then
* try to fix it, and also notify them. Otherwise we can write in
* audio mode, but they won't be able to get the data back
*/
static void
chkandfixaudio(int mt)
{
static struct mtop mtop = {MTAUD, 0};
struct mtget mtget;
unsigned status;
if(rmtioctl(mt, MTIOCGET, &mtget))
return;
status = mtget.mt_dsreg | ((unsigned)mtget.mt_erreg<<16);
if(status & CT_AUDIO) {
msg(EPOST, ":1016", "Warning: drive was in audio mode, turning audio mode off");
if(rmtioctl(mt, MTIOCTOP, &mtop) < 0)
msg(EPOST, ":1017", "Warning: unable to disable audio mode. Tape may not be readable");
}
}
/*
* determine if we there is a tape in the drive, called after an i/o error.
*/
int
chknomedia(int fd)
{
struct mtget mt_status;
int saverr = errno;
if(rmtioctl(fd, MTIOCGET, (char *)&mt_status) == 0 &&
!(mt_status.mt_dposn & MT_ONL))
return 1;
errno = saverr; /* for perror to follow */
return 0;
}