/* @(#)star.c 1.122 02/05/17 Copyright 1985, 88-90, 92-96, 98, 99, 2000-2002 J. Schilling */ #ifndef lint static char sccsid[] = "@(#)star.c 1.122 02/05/17 Copyright 1985, 88-90, 92-96, 98, 99, 2000-2002 J. Schilling"; #endif /* * Copyright (c) 1985, 88-90, 92-96, 98, 99, 2000-2002 J. Schilling */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; see the file COPYING. If not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include "star.h" #include "diff.h" #include #include #include #define __XDEV__ /* Needed to activate _dev_init() */ #include #include #include #include "starsubs.h" #include "fifo.h" EXPORT int main __PR((int ac, char** av)); LOCAL void getdir __PR((int *acp, char *const **avp, const char **dirp)); LOCAL char *dogetwdir __PR((void)); LOCAL BOOL dochdir __PR((const char *dir, BOOL doexit)); LOCAL void openlist __PR((void)); LOCAL void susage __PR((int ret)); LOCAL void usage __PR((int ret)); LOCAL void xusage __PR((int ret)); LOCAL void dusage __PR((int ret)); LOCAL void husage __PR((int ret)); LOCAL void gargs __PR((int ac, char *const* av)); LOCAL Llong number __PR((char* arg, int* retp)); LOCAL int getnum __PR((char* arg, long* valp)); /*LOCAL int getllnum __PR((char* arg, Llong* valp));*/ LOCAL int getlldefault __PR((char* arg, Llong* valp, int mult)); LOCAL int getbnum __PR((char* arg, Llong* valp)); LOCAL int getknum __PR((char* arg, Llong* valp)); LOCAL int addtarfile __PR((const char *tarfile)); EXPORT const char *filename __PR((const char *name)); LOCAL BOOL nameprefix __PR((const char *patp, const char *name)); LOCAL int namefound __PR((const char* name)); EXPORT BOOL match __PR((const char* name)); LOCAL int addpattern __PR((const char* pattern)); LOCAL int addarg __PR((const char* pattern)); LOCAL void closepattern __PR((void)); LOCAL void printpattern __PR((void)); LOCAL int add_diffopt __PR((char* optstr, long* flagp)); LOCAL int gethdr __PR((char* optstr, long* typep)); #ifdef USED LOCAL int addfile __PR((char* optstr, long* dummy)); #endif LOCAL void exsig __PR((int sig)); LOCAL void sighup __PR((int sig)); LOCAL void sigintr __PR((int sig)); LOCAL void sigquit __PR((int sig)); LOCAL void getstamp __PR((void)); EXPORT void *__malloc __PR((size_t size, char *msg)); EXPORT void *__realloc __PR((void *ptr, size_t size, char *msg)); EXPORT char *__savestr __PR((char *s)); LOCAL void docompat __PR((int *pac, char *const **pav)); #if defined(SIGDEFER) || defined(SVR4) #define signal sigset #endif #define QIC_24_TSIZE 122880 /* 61440 kBytes */ #define QIC_120_TSIZE 256000 /* 128000 kBytes */ #define QIC_150_TSIZE 307200 /* 153600 kBytes */ #define QIC_250_TSIZE 512000 /* 256000 kBytes (XXX not verified)*/ #define TSIZE(s) ((s)*TBLOCK) #define SECOND (1) #define MINUTE (60 * SECOND) #define HOUR (60 * MINUTE) #define DAY (24 * HOUR) #define YEAR (365 * DAY) #define LEAPYEAR (366 * DAY) char strvers[] = "1.4.3"; struct star_stats xstats; #define NPAT 100 EXPORT BOOL havepat = FALSE; LOCAL int npat = 0; LOCAL int narg = 0; LOCAL int maxplen = 0; LOCAL int *aux[NPAT]; LOCAL int alt[NPAT]; LOCAL int *state; LOCAL const Uchar *pat[NPAT]; LOCAL const char *dirs[NPAT]; #define NTARFILE 100 FILE *tarf; FILE *listf; FILE *tty; FILE *vpr; const char *tarfiles[NTARFILE]; int ntarfiles; int tarfindex; char *newvol_script; char *listfile; char *stampfile; const char *wdir; const char *currdir; const char *dir_flags = NULL; char *volhdr; dev_t tape_dev; ino_t tape_ino; BOOL tape_isreg = FALSE; #ifdef FIFO BOOL use_fifo = TRUE; #else BOOL use_fifo = FALSE; #endif BOOL shmflag = FALSE; long fs; long bs; int nblocks = 20; int uid; dev_t curfs = NODEV; /* * Change default header format into XUSTAR in 2004 (see below in gargs()) */ long hdrtype = H_XSTAR; /* default header format */ long chdrtype= H_UNDEF; /* command line hdrtype */ int version = 0; int swapflg = -1; BOOL debug = FALSE; BOOL showtime= FALSE; BOOL no_stats= FALSE; BOOL do_fifostats= FALSE; BOOL numeric = FALSE; int verbose = 0; BOOL silent = FALSE; BOOL prblockno = FALSE; BOOL tpath = FALSE; BOOL cflag = FALSE; BOOL uflag = FALSE; BOOL rflag = FALSE; BOOL xflag = FALSE; BOOL tflag = FALSE; BOOL nflag = FALSE; BOOL diff_flag = FALSE; BOOL zflag = FALSE; BOOL bzflag = FALSE; BOOL multblk = FALSE; BOOL ignoreerr = FALSE; BOOL nodir = FALSE; BOOL nomtime = FALSE; BOOL nochown = FALSE; BOOL acctime = FALSE; BOOL pflag = FALSE; BOOL dirmode = FALSE; BOOL nolinkerr = FALSE; BOOL follow = FALSE; BOOL nodesc = FALSE; BOOL nomount = FALSE; BOOL interactive = FALSE; BOOL signedcksum = FALSE; BOOL partial = FALSE; BOOL nospec = FALSE; int Fflag = 0; BOOL uncond = FALSE; BOOL xdir = FALSE; BOOL keep_old= FALSE; BOOL refresh_old= FALSE; BOOL abs_path= FALSE; BOOL notpat = FALSE; BOOL force_hole = FALSE; BOOL sparse = FALSE; BOOL to_stdout = FALSE; BOOL wready = FALSE; BOOL force_remove = FALSE; BOOL ask_remove = FALSE; BOOL remove_first = FALSE; BOOL remove_recursive = FALSE; BOOL nullout = FALSE; Ullong maxsize = 0; time_t Newer = 0; Ullong tsize = 0; long diffopts= 0L; BOOL nowarn = FALSE; BOOL Ctime = FALSE; BOOL nodump = FALSE; BOOL listnew = FALSE; BOOL listnewf= FALSE; BOOL hpdev = FALSE; BOOL modebits= FALSE; BOOL copylinks= FALSE; BOOL hardlinks= FALSE; BOOL symlinks= FALSE; BOOL doacl = FALSE; BOOL dofflags= FALSE; BOOL link_dirs= FALSE; BOOL dodump = FALSE; BOOL dometa = FALSE; BOOL tcompat = FALSE; /* Tar compatibility (av[0] is tar/ustar) */ BOOL fcompat = FALSE; /* Archive file compatibility was requested */ int intr = 0; /* * Achtung: Optionen wie f= sind problematisch denn dadurch dass -ffilename geht, * werden wird bei Falschschreibung von -fifo evt. eine Datei angelegt wird. */ char *opts = "C*,help,xhelp,version,debug,time,no_statistics,no-statistics,fifostats,numeric,v+,block-number,tpath,c,u,r,x,t,n,diff,diffopts&,H&,force_hole,force-hole,sparse,to_stdout,to-stdout,wready,force_remove,force-remove,ask_remove,ask-remove,remove_first,remove-first,remove_recursive,remove-recursive,nullout,onull,fifo,no_fifo,no-fifo,shm,fs&,VOLHDR*,list*,new-volume-script*,file&,f&,T,z,bz,bs&,blocks&,b&,B,pattern&,pat&,i,d,m,o,nochown,a,atime,p,dirmode,l,h,L,D,dodesc,M,I,w,O,signed_checksum,signed-checksum,P,S,F+,U,xdir,k,keep_old_files,keep-old-files,refresh_old_files,refresh-old-files,refresh,/,not,V,maxsize&,newer*,ctime,nodump,tsize&,qic24,qic120,qic150,qic250,nowarn,newest_file,newest-file,newest,hpdev,modebits,copylinks,hardlinks,symlinks,acl,xfflags,link-dirs,dump,meta,silent"; EXPORT int main(ac, av) int ac; char **av; { int cac = ac; char *const *cav = av; save_args(ac, av); docompat(&cac, &cav); gargs(cac, cav); --cac,cav++; if (signal(SIGHUP, SIG_IGN) != SIG_IGN) (void) signal(SIGHUP, sighup); if (signal(SIGINT, SIG_IGN) != SIG_IGN) (void) signal(SIGINT, sigintr); if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) (void) signal(SIGQUIT, sigquit); #ifdef SIGINFO /* * Be polite to *BSD users. * They copied our idea and implemented intermediate status * printing in 'dd' in 1990. */ if (signal(SIGINFO, SIG_IGN) != SIG_IGN) (void) signal(SIGINFO, sigquit); #endif file_raise((FILE *)NULL, FALSE); initbuf(nblocks); (void)openremote(); /* This needs super user privilleges */ if (geteuid() != getuid()) { /* AIX does not like to do this */ /* If we are not root */ #ifdef HAVE_SETREUID if (setreuid(-1, getuid()) < 0) #else #ifdef HAVE_SETEUID if (seteuid(getuid()) < 0) #else if (setuid(getuid()) < 0) #endif #endif comerr("Panic cannot set back effective uid.\n"); } /* * WARNING: We now are no more able to open a new remote connection * unless we have been called by root. * It you like to do a remote multi-tape backup to different hosts * and do not call star from root, you are lost. */ opentape(); uid = geteuid(); if (stampfile) getstamp(); setprops(chdrtype); /* Set up properties for archive format */ dev_init(debug); /* Init device macro handling */ xbinit(); /* Initialize buffer for extended headers */ #ifdef FIFO if (use_fifo) runfifo(); #endif if (dir_flags) wdir = dogetwdir(); if (xflag || tflag || diff_flag) { if (listfile) { openlist(); hash_build(listf, 1000); if((currdir = dir_flags) != NULL) dochdir(currdir, TRUE); } else { for (;;--cac,cav++) { if (dir_flags) getdir(&cac, &cav, &currdir); if (getfiles(&cac, &cav, opts) == 0) break; addarg(cav[0]); } closepattern(); } if (tflag) { list(); } else { /* * xflag || diff_flag * First change dir to the one or last -C arg * in case there is no pattern in list. */ if((currdir = dir_flags) != NULL) dochdir(currdir, TRUE); if (xflag) extract(volhdr); else diff(); } } closepattern(); if (uflag || rflag) { skipall(); syncbuf(); backtape(); } if (cflag) { put_volhdr(volhdr); if (listfile) { openlist(); if((currdir = dir_flags) != NULL) dochdir(currdir, TRUE); createlist(); } else { const char *cdir = NULL; for (;;--cac,cav++) { if (dir_flags) getdir(&cac, &cav, &currdir); if (currdir && cdir != currdir) { if (!(dochdir(wdir, FALSE) && dochdir(currdir, FALSE))) break; cdir = currdir; } if (getfiles(&cac, &cav, opts) == 0) break; if (intr) break; curfs = NODEV; create(cav[0]); } } weof(); buf_drain(); } if (!nolinkerr) checklinks(); if (!use_fifo) closetape(); #ifdef FIFO if (use_fifo) fifo_exit(0); #endif while (wait(0) >= 0) { ; } prstats(); if (checkerrs()) { if (!nowarn && !no_stats) { errmsgno(EX_BAD, "Processed all possible files, despite earlier errors.\n"); } exit(-2); } #ifdef FIFO /* * Fetch errno from FIFO if available. */ exit(fifo_errno()); #endif exit(0); /* NOTREACHED */ return(0); /* keep lint happy */ } LOCAL void getdir(acp, avp, dirp) int *acp; char *const **avp; const char **dirp; { /* * Skip all other flags. */ getfiles(acp, avp, &opts[3]); if (debug) /* temporary */ errmsgno(EX_BAD, "Flag/File: '%s'.\n", *avp[0]); if (getargs(acp, avp, "C*", dirp) < 0) { /* * Skip all other flags. */ if (getfiles(acp, avp, &opts[3]) < 0) { errmsgno(EX_BAD, "Badly placed Option: %s.\n", *avp[0]); susage(EX_BAD); } } if (debug) /* temporary */ errmsgno(EX_BAD, "Dir: '%s'.\n", *dirp); } #include #include #include LOCAL char * dogetwdir() { char dir[PATH_MAX+1]; char *ndir; /* XXX MAXPATHNAME vs. PATH_MAX ??? */ if (getcwd(dir, PATH_MAX) == NULL) comerr("Cannot get working directory\n"); ndir = __malloc(strlen(dir)+1, "working dir"); strcpy(ndir, dir); return (ndir); } LOCAL BOOL dochdir(dir, doexit) const char *dir; BOOL doexit; { if (debug) /* temporary */ error("dochdir(%s) = ", dir); if (chdir(dir) < 0) { int ex = geterrno(); if (debug) /* temporary */ error("%d\n", ex); errmsg("Cannot change directory to '%s'.\n", dir); if (doexit) exit(ex); return (FALSE); } if (debug) /* temporary */ error("%d\n", 0); return (TRUE); } LOCAL void openlist() { if (streql(listfile, "-")) { listf = stdin; listfile = "stdin"; } else if ((listf = fileopen(listfile, "r")) == (FILE *)NULL) comerr("Cannot open '%s'.\n", listfile); } /* * Short usage */ LOCAL void susage(ret) int ret; { error("Usage:\t%s cmd [options] file1 ... filen\n", get_progname()); error("\nUse\t%s -help\n", get_progname()); error("and\t%s -xhelp\n", get_progname()); error("to get a list of valid cmds and options.\n"); error("\nUse\t%s H=help\n", get_progname()); error("to get a list of valid archive header formats.\n"); error("\nUse\t%s diffopts=help\n", get_progname()); error("to get a list of valid diff options.\n"); exit(ret); /* NOTREACHED */ } LOCAL void usage(ret) int ret; { error("Usage:\t%s cmd [options] file1 ... filen\n", get_progname()); error("Cmd:\n"); error("\t-c/-u/-r\tcreate/update/replace named files to tape\n"); error("\t-x/-t/-n\textract/list/trace named files from tape\n"); error("\t-diff\t\tdiff archive against file system (see -xhelp)\n"); error("Options:\n"); error("\t-help\t\tprint this help\n"); error("\t-xhelp\t\tprint extended help\n"); error("\t-version\tprint version information and exit\n"); error("\tblocks=#,b=#\tset blocking factor to #x512 Bytes (default 20)\n"); error("\tfile=nm,f=nm\tuse 'nm' as tape instead of stdin/stdout\n"); error("\t-T\t\tuse $TAPE as tape instead of stdin/stdout\n"); #ifdef FIFO error("\t-fifo/-no-fifo\tuse/don't use a fifo to optimize data flow from/to tape\n"); #if defined(USE_MMAP) && defined(USE_USGSHM) error("\t-shm\t\tuse SysV shared memory for fifo\n"); #endif #endif error("\t-v\t\tincrement verbose level\n"); error("\t-block-number\tprint the block numbers where the TAR headers start\n"); error("\t-tpath\t\tuse with -t to list path names only\n"); error("\tH=header\tgenerate 'header' type archive (see H=help)\n"); error("\tC=dir\t\tperform a chdir to 'dir' before storing next file\n"); error("\t-z\t\tpipe input/output through gzip, does not work on tapes\n"); error("\t-bz\t\tpipe input/output through bzip2, does not work on tapes\n"); error("\t-B\t\tperform multiple reads (needed on pipes)\n"); error("\t-i\t\tignore checksum errors\n"); error("\t-d\t\tdo not store/create directories\n"); error("\t-m\t\tdo not restore access and modification time\n"); error("\t-o,-nochown\tdo not restore owner and group\n"); error("\t-a,-atime\treset access time after storing file\n"); error("\t-p\t\trestore filemodes of directories\n"); error("\t-l\t\tdo not print a message if not all links are dumped\n"); error("\t-h,-L\t\tfollow symbolic links as if they were files\n"); error("\t-D\t\tdo not descend directories\n"); error("\t-M\t\tdo not descend mounting points\n"); error("\t-I,-w\t\tdo interactive creation/extraction/renaming\n"); error("\t-O\t\tbe compatible to old tar (except for checksum bug)\n"); error("\t-P\t\tlast record may be partial (useful on cartridge tapes)\n"); error("\t-S\t\tdo not store/create special files\n"); error("\t-F,-FF,-FFF,...\tdo not store/create SCCS/RCS, core and object files\n"); error("\t-U\t\trestore files unconditionally\n"); exit(ret); /* NOTREACHED */ } LOCAL void xusage(ret) int ret; { error("Usage:\t%s cmd [options] file1 ... filen\n", get_progname()); error("Extended options:\n"); error("\tdiffopts=optlst\tcomma separated list of diffopts (see diffopts=help)\n"); error("\t-debug\t\tprint additional debug messages\n"); error("\t-silent\t\tno not print informational messages\n"); error("\t-not,-V\t\tuse those files which do not match pattern\n"); error("\tVOLHDR=name\tuse name to generate a volume header\n"); error("\t-xdir\t\textract dir even if the current is never\n"); error("\t-dirmode\t\twrite directories after the files they contain\n"); error("\t-link-dirs\tlook for hard linked directories in create mode\n"); error("\t-dump\t\texperimental option for incremental dumps (more ino metadata)\n"); error("\t-meta\t\texperimental option to use inode metadata only\n"); error("\t-keep-old-files,-k\tkeep existing files\n"); error("\t-refresh-old-files\trefresh existing files, don't create new files\n"); error("\t-refresh\trefresh existing files, don't create new files\n"); error("\t-/\t\tdon't strip leading '/'s from file names\n"); error("\tlist=name\tread filenames from named file\n"); error("\t-dodesc\t\tdo descend directories found in a list= file\n"); error("\tpattern=p,pat=p\tset matching pattern\n"); error("\tmaxsize=#\tdo not store file if bigger than # (default mult is kB)\n"); error("\tnewer=name\tstore only files which are newer than 'name'\n"); error("\tnew-volume-script=script\tcall 'scipt' at end of each volume\n"); error("\t-ctime\t\tuse ctime for newer= option\n"); error("\t-nodump\t\tdo not dump files that have the nodump flag set\n"); error("\t-acl\t\thandle access control lists\n"); error("\t-xfflags\t\thandle extended file flags\n"); error("\tbs=#\t\tset (output) block size to #\n"); #ifdef FIFO error("\tfs=#\t\tset fifo size to #\n"); #endif error("\ttsize=#\t\tset tape volume size to # (default multiplier is 512)\n"); error("\t-qic24\t\tset tape volume size to %d kBytes\n", TSIZE(QIC_24_TSIZE)/1024); error("\t-qic120\t\tset tape volume size to %d kBytes\n", TSIZE(QIC_120_TSIZE)/1024); error("\t-qic150\t\tset tape volume size to %d kBytes\n", TSIZE(QIC_150_TSIZE)/1024); error("\t-qic250\t\tset tape volume size to %d kBytes\n", TSIZE(QIC_250_TSIZE)/1024); error("\t-nowarn\t\tdo not print warning messages\n"); error("\t-time\t\tprint timing info\n"); error("\t-no-statistics\tdo not print statistics\n"); #ifdef FIFO error("\t-fifostats\tprint fifo statistics\n"); #endif error("\t-numeric\tdon't use user/group name from tape\n"); error("\t-newest\t\tfind newest file on tape\n"); error("\t-newest-file\tfind newest regular file on tape\n"); error("\t-hpdev\t\tuse HP's non POSIX compliant method to store dev numbers\n"); error("\t-modebits\tinclude all 16 bits from stat.st_mode, this violates POSIX-1003.1\n"); error("\t-copylinks\tCopy hard and symlinks rather than linking\n"); error("\t-hardlinks\tExtract symlinks as hardlinks\n"); error("\t-symlinks\tExtract hardlinks as symlinks\n"); error("\t-signed-checksum\tuse signed chars to calculate checksum\n"); error("\t-sparse\t\thandle file with holes effectively on store/create\n"); error("\t-force-hole\ttry to extract all files with holes\n"); error("\t-to-stdout\textract files to stdout\n"); error("\t-wready\t\twait for tape drive to become ready\n"); error("\t-force-remove\tforce to remove non writable files on extraction\n"); error("\t-ask-remove\task to remove non writable files on extraction\n"); error("\t-remove-first\tremove files before extraction\n"); error("\t-remove-recursive\tremove files recursive\n"); error("\t-onull,-nullout\tsimulate creating an achive to compute the size\n"); exit(ret); /* NOTREACHED */ } LOCAL void dusage(ret) int ret; { error("Diff options:\n"); error("\tnot\t\tif this option is present, exclude listed options\n"); error("\t!\t\tif this option is present, exclude listed options\n"); error("\tall\t\tcompare everything\n"); error("\tperm\t\tcompare file permissions\n"); error("\tmode\t\tcompare file permissions\n"); error("\ttype\t\tcompare file type\n"); error("\tnlink\t\tcompare linkcount (not supported)\n"); error("\tuid\t\tcompare owner of file\n"); error("\tgid\t\tcompare group of file\n"); error("\tuname\t\tcompare name of owner of file\n"); error("\tgname\t\tcompare name of group of file\n"); error("\tid\t\tcompare owner, group, ownername and groupname of file\n"); error("\tsize\t\tcompare file size\n"); error("\tdata\t\tcompare content of file\n"); error("\tcont\t\tcompare content of file\n"); error("\trdev\t\tcompare rdev of device node\n"); error("\thardlink\tcompare target of hardlink\n"); error("\tsymlink\t\tcompare target of symlink\n"); error("\tatime\t\tcompare access time of file (only star)\n"); error("\tmtime\t\tcompare modification time of file\n"); error("\tctime\t\tcompare creation time of file (only star)\n"); error("\ttimes\t\tcompare all times of file\n"); error("\n"); error("Default is to compare everything except atime.\n"); exit(ret); /* NOTREACHED */ } LOCAL void husage(ret) int ret; { error("Header types:\n"); error("\ttar\t\told tar format\n"); error("\tstar\t\told star format from 1985\n"); error("\tgnutar\t\tgnu tar format (violates POSIX, use with care)\n"); error("\tustar\t\tstandard tar (ieee POSIX 1003.1-1988) format\n"); error("\txstar\t\textended standard tar format (star 1994)\n"); error("\txustar\t\textended standard tar format without tar signature\n"); error("\texustar\t\textended standard tar format without tar signature (always x-header)\n"); error("\tpax\t\textended (ieee POSIX 1003.1-2001) standard tar format\n"); error("\tsuntar\t\tSun's extended pre-POSIX.1-2001 Solaris 7/8 tar format\n"); exit(ret); /* NOTREACHED */ } LOCAL void gargs(ac, av) int ac; char *const *av; { BOOL help = FALSE; BOOL xhelp = FALSE; BOOL prvers = FALSE; BOOL oldtar = FALSE; BOOL no_fifo = FALSE; BOOL usetape = FALSE; BOOL xlinkerr= FALSE; BOOL dodesc = FALSE; BOOL qic24 = FALSE; BOOL qic120 = FALSE; BOOL qic150 = FALSE; BOOL qic250 = FALSE; const char *p; Llong llbs = 0; /*char *opts = "C*,help,xhelp,version,debug,time,no_statistics,no-statistics,fifostats,numeric,v+,block-number,tpath,c,u,r,x,t,n,diff,diffopts&,H&,force_hole,force-hole,sparse,to_stdout,to-stdout,wready,force_remove,force-remove,ask_remove,ask-remove,remove_first,remove-first,remove_recursive,remove-recursive,nullout,onull,fifo,no_fifo,no-fifo,shm,fs&,VOLHDR*,list*,new-volume-script*,file&,f&,T,z,bz,bs&,blocks&,b&,B,pattern&,pat&,i,d,m,o,nochown,a,atime,p,dirmode,l,h,L,D,dodesc,M,I,w,O,signed_checksum,signed-checksum,P,S,F+,U,xdir,k,keep_old_files,keep-old-files,refresh_old_files,refresh-old-files,refresh,/,not,V,maxsize&,newer*,ctime,nodump,tsize&,qic24,qic120,qic150,qic250,nowarn,newest_file,newest-file,newest,hpdev,modebits,copylinks,hardlinks,symlinks,acl,xfflags,link-dirs,dump,meta,silent";*/ p = filename(av[0]); if (streql(p, "ustar")) { /* * If we are called as "ustar" we are as POSIX-1003.1-1988 * compliant as possible. There are no enhancements at all. */ hdrtype = H_USTAR; } else if (streql(p, "tar")) { /* * If we are called as "tar" we are mostly POSIX compliant * and use POSIX-1003.1-2001 extensions. The differences of the * base format compared to POSIX-1003.1-1988 can only be * regocnised by star. Even the checsum bug of the "pax" * reference implementation is not hit by the fingerprint * used to allow star to discriminate XUSTAR from USTAR. */ hdrtype = H_XUSTAR; } /* * Current default archive format in all other cases is XSTAR (see * above). This will not change until 2004 (then the new XUSTAR format * is recognised by star for at least 5 years and we may asume that * all star installations will properly handle it. * XSTAR is USTAR with extensions similar to GNU tar. */ --ac,++av; if (getallargs(&ac, &av, opts, &dir_flags, &help, &xhelp, &prvers, &debug, &showtime, &no_stats, &no_stats, &do_fifostats, &numeric, &verbose, &prblockno, &tpath, #ifndef lint &cflag, &uflag, &rflag, &xflag, &tflag, &nflag, &diff_flag, add_diffopt, &diffopts, gethdr, &chdrtype, &force_hole, &force_hole, &sparse, &to_stdout, &to_stdout, &wready, &force_remove, &force_remove, &ask_remove, &ask_remove, &remove_first, &remove_first, &remove_recursive, &remove_recursive, &nullout, &nullout, &use_fifo, &no_fifo, &no_fifo, &shmflag, getnum, &fs, &volhdr, &listfile, &newvol_script, addtarfile, NULL, addtarfile, NULL, &usetape, &zflag, &bzflag, getnum, &bs, getbnum, &llbs, getbnum, &llbs, &multblk, addpattern, NULL, addpattern, NULL, &ignoreerr, &nodir, &nomtime, &nochown, &nochown, &acctime, &acctime, &pflag, &dirmode, &xlinkerr, &follow, &follow, &nodesc, &dodesc, &nomount, &interactive, &interactive, &oldtar, &signedcksum, &signedcksum, &partial, &nospec, &Fflag, &uncond, &xdir, &keep_old, &keep_old, &keep_old, &refresh_old, &refresh_old, &refresh_old, &abs_path, ¬pat, ¬pat, getknum, &maxsize, &stampfile, &Ctime, &nodump, getbnum, &tsize, &qic24, &qic120, &qic150, &qic250, &nowarn, #endif /* lint */ &listnewf, &listnewf, &listnew, &hpdev, &modebits, ©links, &hardlinks, &symlinks, &doacl, &dofflags, &link_dirs, &dodump, /* Experimental */ &dometa, /* Experimental */ &silent) < 0) { errmsgno(EX_BAD, "Bad Option: %s.\n", av[0]); susage(EX_BAD); } if (help) usage(0); if (xhelp) xusage(0); if (prvers) { printf("star %s (%s-%s-%s)\n\n", strvers, HOST_CPU, HOST_VENDOR, HOST_OS); printf("Copyright (C) 1985, 88-90, 92-96, 98, 99, 2000-2002 Jörg Schilling\n"); printf("This is free software; see the source for copying conditions. There is NO\n"); printf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); exit(0); } if ((xflag + cflag + uflag + rflag + tflag + nflag + diff_flag) > 1) { errmsgno(EX_BAD, "Too many commands, only one of -x -c -u -r -t -n or -diff is allowed.\n"); susage(EX_BAD); } if (!(xflag | cflag | uflag | rflag | tflag | nflag | diff_flag)) { errmsgno(EX_BAD, "Missing command, must specify -x -c -u -r -t -n or -diff.\n"); susage(EX_BAD); } if (uflag || rflag) { cflag = TRUE; no_fifo = TRUE; /* Until we are able to reverse the FIFO */ } if (nullout && !cflag) { errmsgno(EX_BAD, "-nullout only makes sense in create mode.\n"); susage(EX_BAD); } if (no_fifo || nullout) use_fifo = FALSE; #ifndef FIFO if (use_fifo) { errmsgno(EX_BAD, "Fifo not configured in.\n"); susage(EX_BAD); } #endif /*#define TAR_COMPAT*/ #ifdef TAR_COMPAT nolinkerr = xlinkerr ^ tcompat; #else nolinkerr = xlinkerr; #endif if (dodump) chdrtype = H_EXUSTAR; if (oldtar) chdrtype = H_OTAR; if (chdrtype != H_UNDEF) { if (H_TYPE(chdrtype) == H_OTAR) oldtar = TRUE; /* XXX hack */ } if (cflag) { if (chdrtype != H_UNDEF) hdrtype = chdrtype; chdrtype = hdrtype; /* wegen setprops in main() */ /* * hdrtype und chdrtype * bei uflag, rflag sowie xflag, tflag, nflag, diff_flag * in get_tcb vergleichen ! */ } if (diff_flag) { if (diffopts == 0) diffopts = D_DEFLT; } else if (diffopts != 0) { errmsgno(EX_BAD, "diffopts= only makes sense with -diff\n"); susage(EX_BAD); } if (fs == 0L) { char *ep = getenv("STAR_FIFO_SIZE"); if (ep) { if (getnum(ep, &fs) != 1) { comerrno(EX_BAD, "Bad fifo size environment '%s'.\n", ep); } } } if (llbs != 0 && bs != 0) { errmsgno(EX_BAD, "Only one of blocks= b= bs=.\n"); susage(EX_BAD); } if (llbs != 0) { bs = llbs; if (bs != llbs) { errmsgno(EX_BAD, "Blocksize used with blocks= or b= too large.\n"); susage(EX_BAD); } } if (bs % TBLOCK) { errmsgno(EX_BAD, "Invalid block size %ld.\n", bs); susage(EX_BAD); } if (bs) nblocks = bs / TBLOCK; if (nblocks <= 0) { errmsgno(EX_BAD, "Invalid block size %d blocks.\n", nblocks); susage(EX_BAD); } bs = nblocks * TBLOCK; if (debug) { errmsgno(EX_BAD, "Block size %d blocks (%ld bytes).\n", nblocks, bs); } if (tsize > 0) { if (tsize % TBLOCK) { errmsgno(EX_BAD, "Invalid tape size %llu.\n", tsize); susage(EX_BAD); } tsize /= TBLOCK; } if (tsize > 0 && tsize < 3) { errmsgno(EX_BAD, "Tape size must be at least 3 blocks.\n"); susage(EX_BAD); } if (tsize == 0) { if (qic24) tsize = QIC_24_TSIZE; if (qic120) tsize = QIC_120_TSIZE; if (qic150) tsize = QIC_150_TSIZE; if (qic250) tsize = QIC_250_TSIZE; } if (listfile != NULL && !dodesc) nodesc = TRUE; if (oldtar) nospec = TRUE; if (!tarfiles[0]) { if (usetape) { tarfiles[0] = getenv("TAPE"); } if (!tarfiles[0]) tarfiles[0] = "-"; ntarfiles++; } /* * XXX This is a place that should be checked every time, when * XXX possible interactivity is modified. */ if (interactive || ask_remove || (tsize > 0 && !newvol_script)) { #ifdef JOS tty = stderr; #else if ((tty = fileopen("/dev/tty", "r")) == (FILE *)NULL) comerr("Cannot open '/dev/tty'.\n"); #endif } if (nflag) { xflag = TRUE; interactive = TRUE; if (verbose == 0 && !tpath) verbose = 1; } if (to_stdout) { force_hole = FALSE; } if (keep_old && refresh_old) { errmsgno(EX_BAD, "Cannot use -keep-old-files and -refresh-old-files together.\n"); susage(EX_BAD); } if ((copylinks + hardlinks + symlinks) > 1) { errmsgno(EX_BAD, "Only one of -copylinks -hardlinks -symlinks.\n"); susage(EX_BAD); } /* * keep compatibility for some time. */ if (pflag) dirmode = TRUE; /* * -acl includes -p */ if (doacl) pflag = TRUE; } LOCAL Llong number(arg, retp) register char *arg; int *retp; { Llong val = (Llong)0; if (*retp != 1) return (val); if (*arg == '\0') { *retp = -1; } else if (*(arg = astoll(arg, &val))) { if (*arg == 'p' || *arg == 'P') { val *= (1024*1024); val *= (1024*1024*1024); arg++; } else if (*arg == 't' || *arg == 'T') { val *= (1024*1024); val *= (1024*1024); arg++; } else if (*arg == 'g' || *arg == 'G') { val *= (1024*1024*1024); arg++; } else if (*arg == 'm' || *arg == 'M') { val *= (1024*1024); arg++; } else if (*arg == 'k' || *arg == 'K') { val *= 1024; arg++; } else if (*arg == 'b' || *arg == 'B') { val *= 512; arg++; } else if (*arg == 'w' || *arg == 'W') { val *= 2; arg++; } else if (*arg == '.') { /* 1x multiplier */ arg++; } if (*arg == '*' || *arg == 'x') val *= number(++arg, retp); else if (*arg != '\0') *retp = -1; } return (val); } LOCAL int getnum(arg, valp) char *arg; long *valp; { Llong llval; int ret = 1; llval = number(arg, &ret); *valp = llval; if (*valp != llval) { errmsgno(EX_BAD, "Value %lld is too large for data type 'long'.\n", llval); ret = -1; } return (ret); } /* * not yet needed LOCAL int getllnum(arg, valp) char *arg; Llong *valp; { int ret = 1; *valp = number(arg, &ret); return (ret); } */ LOCAL int getlldefault(arg, valp, mult) char *arg; Llong *valp; int mult; { int ret = 1; int len = strlen(arg); if (len > 0) { len = (Uchar)arg[len-1]; if (!isdigit(len)) mult = 1; } *valp = number(arg, &ret); if (ret == 1) *valp *= mult; return (ret); } LOCAL int getbnum(arg, valp) char *arg; Llong *valp; { return (getlldefault(arg, valp, 512)); } LOCAL int getknum(arg, valp) char *arg; Llong *valp; { return (getlldefault(arg, valp, 1024)); } LOCAL int addtarfile(tarfile) const char *tarfile; { /* if (debug)*/ /* error("Add tar file '%s'.\n", tarfile);*/ if (ntarfiles >= NTARFILE) comerrno(EX_BAD, "Too many tar files (max is %d).\n", NTARFILE); if (ntarfiles > 0 && (streql(tarfile, "-") || streql(tarfiles[0], "-"))) comerrno(EX_BAD, "Cannot handle multi volume archives from/to stdin/stdout.\n"); tarfiles[ntarfiles] = tarfile; ntarfiles++; return (TRUE); } EXPORT const char * filename(name) const char *name; { char *p; if ((p = strrchr(name, '/')) == NULL) return (name); return (++p); } LOCAL BOOL nameprefix(patp, name) register const char *patp; register const char *name; { while (*patp) { if (*patp++ != *name++) return (FALSE); } if (*name) { return (*name == '/'); /* Directory tree match */ } return (TRUE); /* Names are equal */ } LOCAL int namefound(name) const char *name; { register int i; for (i=npat; i < narg; i++) { if (nameprefix((const char *)pat[i], name)) { return (i); } } return (-1); } EXPORT BOOL match(name) const char *name; { register int i; char *ret = NULL; if (!cflag && narg > 0) { if ((i = namefound(name)) < 0) return (FALSE); if (npat == 0) goto found; } for (i=0; i < npat; i++) { ret = (char *)patmatch(pat[i], aux[i], (const Uchar *)name, 0, strlen(name), alt[i], state); if (ret != NULL && *ret == '\0') break; } if (notpat ^ (ret != NULL && *ret == '\0')) { found: if (!(xflag || diff_flag)) /* Chdir only on -x or -diff */ return (TRUE); if (dirs[i] != NULL && currdir != dirs[i]) { currdir = dirs[i]; dochdir(wdir, TRUE); dochdir(currdir, TRUE); } return TRUE; } return FALSE; } LOCAL int addpattern(pattern) const char *pattern; { int plen; /* if (debug)*/ /* error("Add pattern '%s'.\n", pattern);*/ if (npat >= NPAT) comerrno(EX_BAD, "Too many patterns (max is %d).\n", NPAT); plen = strlen(pattern); pat[npat] = (const Uchar *)pattern; if (plen > maxplen) maxplen = plen; aux[npat] = __malloc(plen*sizeof(int), "compiled pattern"); if ((alt[npat] = patcompile((const Uchar *)pattern, plen, aux[npat])) == 0) comerrno(EX_BAD, "Bad pattern: '%s'.\n", pattern); dirs[npat] = currdir; npat++; return (TRUE); } LOCAL int addarg(pattern) const char *pattern; { if (narg == 0) narg = npat; /* if (debug)*/ /* error("Add arg '%s'.\n", pattern);*/ if (narg >= NPAT) comerrno(EX_BAD, "Too many patterns (max is %d).\n", NPAT); pat[narg] = (const Uchar *)pattern; dirs[narg] = currdir; narg++; return (TRUE); } /* * Close pattern list: insert useful default directories. */ LOCAL void closepattern() { register int i; if (debug) /* temporary */ printpattern(); for (i=0; i < npat; i++) { if (dirs[i] != NULL) break; } while (--i >= 0) dirs[i] = wdir; if (debug) /* temporary */ printpattern(); if (npat > 0 || narg > 0) havepat = TRUE; if (npat > 0) { state = __malloc((maxplen+1)*sizeof(int), "pattern state"); } } LOCAL void printpattern() { register int i; error("npat: %d narg: %d\n", npat, narg); for (i=0; i < npat; i++) { error("pat %s dir %s\n", pat[i], dirs[i]); } for (i=npat; i < narg; i++) { error("arg %s dir %s\n", pat[i], dirs[i]); } } LOCAL int add_diffopt(optstr, flagp) char *optstr; long *flagp; { char *ep; char *np; int optlen; long optflags = 0; BOOL not = FALSE; while (*optstr) { if ((ep = strchr(optstr, ',')) != NULL) { optlen = ep - optstr; np = &ep[1]; } else { optlen = strlen(optstr); np = &optstr[optlen]; } if (optstr[0] == '!') { optstr++; optlen--; not = TRUE; } if (strncmp(optstr, "not", optlen) == 0 || strncmp(optstr, "!", optlen) == 0) { not = TRUE; } else if (strncmp(optstr, "all", optlen) == 0) { optflags |= D_ALL; } else if (strncmp(optstr, "perm", optlen) == 0) { optflags |= D_PERM; } else if (strncmp(optstr, "mode", optlen) == 0) { optflags |= D_PERM; } else if (strncmp(optstr, "type", optlen) == 0) { optflags |= D_TYPE; } else if (strncmp(optstr, "nlink", optlen) == 0) { optflags |= D_NLINK; errmsgno(EX_BAD, "nlink not supported\n"); dusage(EX_BAD); } else if (strncmp(optstr, "uid", optlen) == 0) { optflags |= D_UID; } else if (strncmp(optstr, "gid", optlen) == 0) { optflags |= D_GID; } else if (strncmp(optstr, "uname", optlen) == 0) { optflags |= D_UNAME; } else if (strncmp(optstr, "gname", optlen) == 0) { optflags |= D_GNAME; } else if (strncmp(optstr, "id", optlen) == 0) { optflags |= D_ID; } else if (strncmp(optstr, "size", optlen) == 0) { optflags |= D_SIZE; } else if (strncmp(optstr, "data", optlen) == 0) { optflags |= D_DATA; } else if (strncmp(optstr, "cont", optlen) == 0) { optflags |= D_DATA; } else if (strncmp(optstr, "rdev", optlen) == 0) { optflags |= D_RDEV; } else if (strncmp(optstr, "hardlink", optlen) == 0) { optflags |= D_HLINK; } else if (strncmp(optstr, "symlink", optlen) == 0) { optflags |= D_SLINK; } else if (strncmp(optstr, "sparse", optlen) == 0) { optflags |= D_SPARS; } else if (strncmp(optstr, "atime", optlen) == 0) { optflags |= D_ATIME; } else if (strncmp(optstr, "mtime", optlen) == 0) { optflags |= D_MTIME; } else if (strncmp(optstr, "ctime", optlen) == 0) { optflags |= D_CTIME; } else if (strncmp(optstr, "times", optlen) == 0) { optflags |= D_TIMES; } else if (strncmp(optstr, "help", optlen) == 0) { dusage(0); } else { error("Illegal diffopt.\n"); dusage(EX_BAD); return (-1); } optstr = np; } if (not) { *flagp = ~optflags; } else { *flagp = optflags; } return (TRUE); } LOCAL int gethdr(optstr, typep) char *optstr; long *typep; { BOOL swapped = FALSE; long type = H_UNDEF; if (*optstr == 'S') { swapped = TRUE; optstr++; } if (streql(optstr, "tar")) { type = H_OTAR; } else if (streql(optstr, "star")) { type = H_STAR; } else if (streql(optstr, "gnutar")) { type = H_GNUTAR; } else if (streql(optstr, "ustar")) { type = H_USTAR; } else if (streql(optstr, "xstar")) { type = H_XSTAR; } else if (streql(optstr, "xustar")) { type = H_XUSTAR; } else if (streql(optstr, "exustar")) { type = H_EXUSTAR; } else if (streql(optstr, "pax")) { type = H_PAX; } else if (streql(optstr, "suntar")) { type = H_SUNTAR; } else if (streql(optstr, "help")) { husage(0); } else { error("Illegal header type '%s'.\n", optstr); husage(EX_BAD); return (-1); } if (swapped) *typep = H_SWAPPED(type); else *typep = type; return (TRUE); } #ifdef USED /* * Add archive file. * May currently not be activated: * If the option string ends with ",&", the -C option will not work * anymore. */ LOCAL int addfile(optstr, dummy) char *optstr; long *dummy; { char *p; /* error("got_it: %s\n", optstr);*/ if (!strchr("01234567", optstr[0])) return (NOTAFILE);/* Tell getargs that this may be a flag */ for (p = &optstr[1]; *p; p++) { if (*p != 'l' && *p != 'm' && *p != 'h') return (BADFLAG); } /* error("is_tape: %s\n", optstr);*/ comerrno(EX_BAD, "Options [0-7][lmh] currently not supported.\n"); /* * The tape device should be determined from the defaults file * in the near future. * Search for /etc/opt/schily/star, /etc/default/star, /etc/default/tar */ return (1); /* Success */ } #endif LOCAL void exsig(sig) int sig; { (void) signal(sig, SIG_DFL); kill(getpid(), sig); } /* ARGSUSED */ LOCAL void sighup(sig) int sig; { (void) signal(SIGHUP, sighup); prstats(); intr++; if (!cflag) exsig(sig); } /* ARGSUSED */ LOCAL void sigintr(sig) int sig; { (void) signal(SIGINT, sigintr); prstats(); intr++; if (!cflag) exsig(sig); } /* ARGSUSED */ LOCAL void sigquit(sig) int sig; { /* * sig may be either SIGQUIT or SIGINFO (*BSD only). */ (void) signal(sig, sigquit); prstats(); } LOCAL void getstamp() { FINFO finfo; BOOL ofollow = follow; follow = TRUE; if (!getinfo(stampfile, &finfo)) comerr("Cannot stat '%s'.\n", stampfile); follow = ofollow; Newer = finfo.f_mtime; } EXPORT void * __malloc(size, msg) size_t size; char *msg; { void *ret; ret = malloc(size); if (ret == NULL) { comerr("Cannot allocate memory for %s.\n", msg); /* NOTREACHED */ } return (ret); } EXPORT void * __realloc(ptr, size, msg) void *ptr; size_t size; char *msg; { void *ret; ret = realloc(ptr, size); if (ret == NULL) { comerr("Cannot realloc memory for %s.\n", msg); /* NOTREACHED */ } return (ret); } EXPORT char * __savestr(s) char *s; { char *ret = __malloc(strlen(s)+1, "saved string"); strcpy(ret, s); return (ret); } /* * Convert old tar type syntax into the new UNIX option syntax. * Allow only a limited subset of the single character options to avoid * collisions between interpretation of options in different * tar implementations. The old syntax has a risk to damage files * which is avoided with the 'fcompat' flag (see opentape()). * * The UNIX-98 documentation lists the following tar options: * Function Key: crtux * c Create * r Append * t List * u Update * x Extract * Additional Key: vwfblmo * v Verbose * w Wait for confirmation * f Archive file * b Blocking factor * l Report missing links * m Do not restore mtime from archive * o Do not restore owner/group from archive * * Incompatibilities with UNIX-98 tar: * l works the oposite way round as with star, but * if TAR_COMPAT is defined, star will behaves * as documented in UNIX-98 if av[0] is either * "tar" or "ustar". * * Additional functions from historic UNIX tar vesions: * 0..7 magtape_0 .. magtape_7 * * Additional functions from historic BSD tar vesions: * p Extract dir permissions too * h Follow symbolic links * i ignore directory checksum errors * B do multiple reads to reblock pipes * F Ommit unwanted files (e.g. core) * * Additional functions from historic Star vesions: * T Use $TAPE environment as archive * L Follow symbolic links * d do not archive/extract directories * k keep old files * n do not extract but tell what to do * * Additional functions from historic SVr4 tar vesions: * e Exit on unexpected errors * X Arg is File with unwanted filenames * * Additional functions from historic GNU tar vesions: * z do inline compression * * Missing in star (from SVr4/Solaris tar): * E Extended headers * P Supress '/' at beginning of filenames * q quit after extracting first file * Incompatibilities with SVr4/Solaris tar: * I Arg is File with filenames to be included * P SVr4/solaris: Supress '/', star: last partial * k set tape size for multi volume archives * n non tape device (do seeks) * * Incompatibilities with GNU tar: * There are many. GNU programs in many cases make smooth * coexistence hard. * * Problems: * The 'e' and 'X' option are currently not implemented. * The 'h' option can only be implemented if the -help XXX DONE * shortcut in star is removed. * There is a collision between the BSD -I (include) and * star's -I (interactive) which may be solved by using -w instead. */ LOCAL void docompat(pac, pav) int *pac; char *const **pav; { int ac = *pac; char *const *av = *pav; int nac; char **nav; char nopt[3]; char *copt = "crtuxbfXBFTLdehiklmnopvwz01234567"; const char *p; char c; char *const *oa; char **na; p = filename(av[0]); if (streql(p, "tar") || streql(p, "ustar")) tcompat = TRUE; if (ac <= 1) return; /* * Only the first arg is converted from the old to the new syntay. */ if (av[1][0] == '-') /* Do not convert new syntax */ return; if (strchr(av[1], '=') != NULL) /* Do not try to convert bs= */ return; nac = ac + strlen(av[1]); nav = __malloc(nac-- * sizeof(char *), /* keep space for NULL ptr */ "compat argv"); oa = av; /* remember old arg pointer */ na = nav; /* set up new arg pointer */ *na++ = *oa++; /* copy over av[0] */ oa++; /* Skip over av[1] */ nopt[0] = '-'; nopt[2] = '\0'; for (p=av[1]; (c = *p) != '\0'; p++) { if (strchr(copt, c) == NULL) { errmsgno(EX_BAD, "Illegal option '%c' for compat mode.\n", c); susage(EX_BAD); } nopt[1] = c; *na++ = __savestr(nopt); if (c == 'f' || c == 'b' || c == 'X') { if ((av + ac) <= oa) { comerrno(EX_BAD, "Missing arg for '%s' option.\n", nopt); } *na++ = *oa++; /* * The old syntax has a high risk of corrupting * files if the user disorders the args. */ if (c == 'f') fcompat = TRUE; } } /* * Now copy over the rest... */ while ((av + ac) > oa) *na++ = *oa++; *na = NULL; *pac = nac; *pav = nav; }