From f2ad5ea802a39d761446b48c9e520602937c6cba Mon Sep 17 00:00:00 2001 From: Brett Gordon Date: Sat, 19 Dec 2015 11:48:17 -0500 Subject: [PATCH] utils: add tar --- Applications/util/Makefile.6809 | 1 + Applications/util/tar.c | 645 +++++++++++++++++++++++ Standalone/filesystem-src/ucp-script.txt | 2 + 3 files changed, 648 insertions(+) create mode 100644 Applications/util/tar.c diff --git a/Applications/util/Makefile.6809 b/Applications/util/Makefile.6809 index ec9d2806..8cf8643f 100644 --- a/Applications/util/Makefile.6809 +++ b/Applications/util/Makefile.6809 @@ -79,6 +79,7 @@ SRCS = banner.c \ sum.c \ su.c \ sync.c \ + tar.c \ tee.c \ tail.c \ telinit.c \ diff --git a/Applications/util/tar.c b/Applications/util/tar.c new file mode 100644 index 00000000..c20a6c80 --- /dev/null +++ b/Applications/util/tar.c @@ -0,0 +1,645 @@ +/* A very simple, lite, version of tar for the Fuzix project + + Copyright(c) 2015 Brett M Gordon + + 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 3 of the License, 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. If not, see . + + + NOTES: + * no record blocking + * cannot extract symbolic links + * hardlinks are added as normal files + * no built-in tar file, if option "f" is not set then tar will use + stdin/stdout. + + TODO: + * don't archive the archive file +*/ + +#include +#include +#include +#include +#include +#include +/* #include */ +#include + +/* tar header format, with ustar extension */ +struct header{ + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char cksum[8]; + char type; + char lname[100]; + char ustar[6]; + char version[2]; + char uname[32]; + char gname[32]; + char major[8]; + char minor[8]; + char prefix[155]; + char pad[12]; +}h; + + +uint8_t buffer[512]; /* buffer for data blocks */ +int infile; /* input file: a tar file */ +int outfile; /* output file: a tar file */ +int verbose; /* verbose flag */ +int uflag; /* ustar flag */ +char *ofile=NULL; /* pointer to archive name from cmd line */ +char key=0; /* key mode of command (t,x,c) */ +int noreplace=0; /* replace old files? */ +int cksum=1; /* don't worry about cksums */ + + +/* Return a number from an octal string */ +static uint32_t b8get ( char *ptr, unsigned int n ){ + uint32_t ret=0; + while( *ptr && n ){ + ret *= 8; + ret += (*ptr++)-'0'; + n--; + } + return ret; +} + +/* Put a number in octal to string */ +static void b8put( uint32_t x, char *ptr ){ + int digit; + + *--ptr=0; + while(x){ + digit = x & 7; + x /= 8; + *--ptr ='0'+digit; + } +} + +/* Print filename for error reporting */ +static void pname(){ + fprintf(stderr,"%s ", h.name ); + perror( "" ); +} + + +/* Change owner/group of file to match tar file */ +static void my_chown(){ + int x=chown( h.name, b8get( h.uid, 8 ), + b8get( h.gid, 8 ) + ); + if( x < 0 ) pname(); +} + + +/* Change mod of file to match tar file */ +static void my_chmod(){ + int x=chmod( h.name, b8get( h.mode, 8 ) ); + if( x < 0 ) pname(); +} + +static void printusage(){ + fprintf(stderr, "usage: tar [-ckxntv] [-f archive] [file]...\n" ); + exit(1); +} + +/* skip data blocks of regular type file */ +static void skip(){ + uint32_t size=b8get( h.size, 12 ) ; + uint32_t count= size / 512 ; + uint16_t rem = size % 512 ; + + if( h.type == 0 || h.type == '0' ){ + if( rem ) count++; + off_t x=lseek( infile, count*512, SEEK_CUR ); + if( x < 0 ){ + pname(); + exit(1); + } + + } +} + +/* Calculate sum of header */ +static uint32_t cksum_calc(){ + uint32_t acc=0; + unsigned char *ptr=(unsigned char *)h.name; + while( ptr != (unsigned char *)h.cksum ) + acc += *ptr++; + acc += 32 * 8; + ptr = &(h.type); + while( ptr != (unsigned char *)h.pad ) + acc += *ptr++; + return acc; +} + + +/* Prints information in header to stdout (if applicable) */ +static void printheader(){ + char pad[10]; + int pindex=10; + unsigned int m; + int x; + + pad[10]=0; + + /* don't print if creating to stdout */ + if( !ofile && key=='c' ) return; + + /* print verbosely */ + if( verbose && key=='t' ){ + /* print type */ + switch( h.type ){ + case '0': + case 0: + printf("-"); + break; + case '1': + printf("h"); + break; + case '2': + printf("s"); + break; + case '3': + printf("c"); + break; + case '4': + printf("b"); + break; + case '5': + printf("d"); + break; + case '6': + printf("f"); + break; + default: + printf("?"); + break; + } + /* print mode */ + m=b8get( h.mode, 8 ); + for( x=0; x<3; x++ ){ + if( m & 1 ) + pad[--pindex]='x'; + else + pad[--pindex]='-'; + m = m / 2; + if( m & 1 ) + pad[--pindex]='w'; + else + pad[--pindex]='-'; + m = m / 2; + + if( m & 1 ) + pad[--pindex]='r'; + else + pad[--pindex]='-'; + m = m / 2; + } + printf( &(pad[pindex] ) ); + /* print uid gid */ + printf( " %4ld %4ld", b8get( h.uid, 8 ), + b8get( h.gid, 8 ) ); + /* print file size */ + printf( " %8ld ", b8get( h.size, 12 ) ); + /* print maj/min */ + + /* This make binary big... disabling */ + /* + printf( " %2ld,%2ld", b8get( h.major, 8 ), + b8get( h.minor, 8 ) ); + { + char *s; + time_t time=b8get( h.mtime, 12 ); + s=&( ctime( &time )[4] ); + s[16]=0; + printf( " %-12s", s ); + } + */ + + } + if( key == 't' || verbose ) + printf("%s\n", h.name ); +} + + +/* process a file name + This is called recursively as new subdirs are found */ +static void storedir( char *name){ + struct stat s; + int fd; + int ret; + DIR *dirstream; + struct dirent *dir; + char cname[100]; + uint32_t count; + uint16_t rem; + + /* stat file */ + ret=stat( name, &s ); + if( ret<0 ){ + fprintf(stderr,"%s ", name ); + perror(""); + return; + } + + if( S_ISREG (s.st_mode ) ){ + count = s.st_size / 512 ; + rem = s.st_size % 512 ; + if( rem ) count++; + } + + /* build a new header in mem */ + bzero( h.name, sizeof( h ) ); + memset( h.mode,'0', 57 ); + memset( h.major,'0',16 ); + strcpy( h.name, name ); + b8put( s.st_mode, h.mode + 8 ); + b8put( s.st_uid, h.uid + 8 ); + b8put( s.st_gid, h.gid + 8 ); + b8put( s.st_size, h.size + 12 ); + b8put( s.st_mtime, h.mtime + 12 ); + switch( s.st_mode & S_IFMT ){ + case S_IFREG: + h.type='0'; + break; + case S_IFDIR: + h.type='5'; + if( S_ISDIR( s.st_mode ) ) h.name[strlen(name)]='/'; + break; + case S_IFCHR: + h.type='3'; + b8put( s.st_rdev >> 8, h.major + 8 ); + b8put( s.st_rdev & 255, h.minor + 8 ); + break; + case S_IFBLK: + h.type='4'; + b8put( s.st_rdev >> 8, h.major + 8 ); + b8put( s.st_rdev & 255, h.minor + 8 ); + break; + case S_IFIFO: + h.type='6'; + break; + defailt: + fprintf(stderr,"unhandled file type error\n"); + exit(1); + } + + strcpy( h.ustar, "ustar" ); + b8put( 0, h.version + 2 ); + + /* calculate checksum */ + h.cksum[7]=32; + b8put( cksum_calc(), h.cksum + 7 ); + + /* write header to file */ + ret = write( outfile, h.name, 512 ); + if( ret < 512 ){ + pname(); + exit(1); + } + printheader(); + + switch( s.st_mode & S_IFMT ){ + case S_IFREG: + /* open subject file */ + fd=open( name, O_RDONLY ); + if( fd<0 ){ + fprintf(stderr, "%s ", name ); + perror(""); + return ; + } + while( count-- ){ + ret = read( fd, buffer, 512 ); + if( ret < 0 ){ + fprintf(stderr,"cannot read source file\n"); + exit(1); + } + /* !!! should zero rest of buffer here */ + ret = write( outfile, buffer, 512 ); + if( ret < 512 ){ + fprintf(stderr,"cannot write out file\n"); + exit(1); + } + } + close(fd); + break; + case S_IFDIR: + /* open the directory */ + dirstream = opendir( name ); + if( ret < 0 ){ + pname(); + break; + } + while( dir = readdir( dirstream ) ){ + /* dont arch .. or . */ + if( !strcmp( dir->d_name, "." ) || + !strcmp( dir->d_name, ".." ) + ) continue; + strncpy( cname, name, 100 ); + strncat( cname, "/", 100 ); + strncat( cname, dir->d_name, 100 ); + { + /* save state of files */ + uint32_t tell=telldir( dirstream ); + closedir( dirstream ); + /* recursive call to this dir */ + storedir( cname ); + /* restore state of files */ + dirstream=opendir( name ); + seekdir( dirstream, tell ); + } + } + closedir( dirstream ); + break; + } +} + + +/* list all the files in an archive */ +static void list( ){ + int x; + int zcount=2; + + + if( ofile ) infile=open( ofile, O_RDONLY ); + else infile = 0; + if( infile < 0 ){ + perror( "Cannot open archive"); + exit(1); + } + + while(1){ + x=read( infile, &h, 512 ); + if( x < 512 ){ + fprintf(stderr, "Bad filesize\n" ); + exit(1); + } + + + /* check for zero block */ + if ( h.name[0]==0 ){ + if( ! --zcount ) + exit(0); + continue; + } + + uflag = ! strncmp( h.ustar, "ustar", 5 ); + + printheader(); + + skip(); + } /* block while */ +} + + +/* Extract all file in archive */ +static void extract( char *argv[] ){ + int x; + char *ptr; + int zcount=2; + + if( ofile ) infile=open( ofile, O_RDONLY ); + else infile= 0; + + if( infile < 0 ){ + fprintf(stderr, "cannot open infile\n" ); + exit(1); + } + + while(1){ + x=read( infile, &h, 512 ); + if( x < 512 ){ + fprintf(stderr, "bad filesize\n" ); + exit(1); + } + + /* check for zero block */ + if ( h.name[0]==0 ){ + if( ! --zcount ) + exit(0); + continue; + } + + /* check cksum */ + if( cksum && (cksum_calc() != b8get( h.cksum, 8 )) ){ + fprintf(stderr,"%s: bad chksum\n", h.name ); + exit(1); + } + + /* remove trailing '/' */ + while( h.name[strlen(h.name)-1] == '/' ) + h.name[strlen(h.name)-1] = 0; + + /* does entry match any cmd line args? */ + if( argv[optind] ){ + for( x=optind; argv[x]; x++ ){ + if( ! strcmp( h.name, argv[x] ) ) + goto cont; + } + skip(); + continue; + } + cont: + printheader(); + + uflag = ! strncmp( h.ustar, "ustar", 5 ); + + switch( h.type ){ + case '1': /* a hard link */ + x=link( h.lname, h.name ); + if( x < 0 ){ + pname(); + break; + } + my_chmod(); + my_chown(); + break; + case '2': /* a soft link */ + fprintf(stderr, "soft links not supported\n" ); + break; + case '3': /* a charactor device */ + x=mknod( h.name, b8get( h.mode, 8 )|S_IFCHR, + (b8get(h.major,8)<<8)+b8get(h.minor,8) ); + if( x > 0 ){ + pname(); + break; + } + my_chown(); + break; + case '4': /* a block device */ + x=mknod( h.name, b8get( h.mode, 8)|S_IFBLK, + (b8get(h.major,8)<<8)+b8get(h.minor,8)); + if( x < 0 ){ + pname(); + break; + } + my_chown(); + break; + case '5': /* a directory */ + x=mkdir( h.name, b8get( h.mode, 8 ) ); + if( x ){ + pname(); + break; + } + my_chown(); + break; + case '6': /* a FIFO */ + x=mkfifo( h.name, b8get( h.mode, 8) ); + if( x < 0 ){ + pname(); + break; + } + my_chown(); + break; + case '0': /* regular file */ + case 0: + { + int i; + /* get a printable size */ + uint32_t size=b8get( h.size, 12 ) ; + uint32_t count; + /* count how many blocks we need */ + count = size / 512 ; + uint16_t rem = size % 512 ; + /* check for existance of file */ + if( noreplace && !access( h.name, F_OK ) ){ + errno=EEXIST; + pname(); + continue; + } + /* open output file */ + outfile=open(h.name,O_CREAT|O_WRONLY); + if( outfile < 0 ){ + pname(); + break; + } + /* send buffers to file */ + for( x=0; x0 ){ + switch( o ){ + case 'x': + case 't': + case 'c': + key=o; + break; + case 'v': + verbose=1; + break; + case 'f': + ofile=optarg; + break; + case 'k': + noreplace=1; + break; + case 'n': + cksum=0; + break; + default: + printusage(); + } + } + + switch( key ){ + case 'x': + extract( argv ); + case 't': + list( ); + case 'c': + create( argv ); + default: + fprintf(stderr, "tar: option x,c, or t must be used\n" ); + } + +} + + + + diff --git a/Standalone/filesystem-src/ucp-script.txt b/Standalone/filesystem-src/ucp-script.txt index 952cb67e..457a8ae4 100644 --- a/Standalone/filesystem-src/ucp-script.txt +++ b/Standalone/filesystem-src/ucp-script.txt @@ -199,6 +199,7 @@ bget ../../Applications/util/stty bget ../../Applications/util/sum bget ../../Applications/util/su bget ../../Applications/util/sync +bget ../../Applications/util/tar bget ../../Applications/util/tee bget ../../Applications/util/tail bget ../../Applications/util/touch @@ -279,6 +280,7 @@ chmod 0755 stty chmod 0755 sum chmod 4755 su chmod 0755 sync +chmod 0755 tar chmod 0755 tee chmod 0755 tail chmod 0755 touch -- 2.34.1