diff --git a/Changelog b/Changelog index f506e095e..4a9fb6fc0 100644 --- a/Changelog +++ b/Changelog @@ -11,16 +11,15 @@ patch your kernel with the devps patch in the kernel-patches/ directory. * Wrote basename, killall, and uptime. + * tar has been completely rewritten by me. Both tar creation and + extraction are now well behaved. Costs 7.6k with all optional + tar features enabled, and 5k for just tar extraction support. * Added freeramdisk, which will free up all memory associated with a ram disk. Contributed by Emanuele Caratti and then adjusted a bit by me. * Added tr and dirname from John Lombardo * Added echo and test (from me). * Added usleep contributed by Nicolas Pitre - * tar wouldn't create directory entries that don't end in '/', - now it does (thanks to Avery Pennarun ) - * tar has been completely rewritten, and tar creation and extraction - is now much better behaved. * Several fixes from Pavel Roskin : - When `tail' fails to open a file it now exits. - When `syslogd' is given the `-n' option it should still use diff --git a/Makefile b/Makefile index d647a5139..38d518eed 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ BUILDTIME := $(shell TZ=UTC date --utc "+%Y.%m.%d-%H:%M%z") # Set the following to `true' to make a debuggable build. # Leave this set to `false' for production use. # eg: `make DODEBUG=true tests' -DODEBUG = true +DODEBUG = false # If you want a static binary, turn this on. I can't think # of many situations where anybody would ever want it static, @@ -72,7 +72,7 @@ endif # -D_GNU_SOURCE is needed because environ is used in init.c ifeq ($(DODEBUG),true) CFLAGS += -Wall -g -D_GNU_SOURCE - LDFLAGS = + LDFLAGS = STRIP = else CFLAGS += -Wall $(OPTIMIZATION) -fomit-frame-pointer -fno-builtin -D_GNU_SOURCE diff --git a/TODO b/TODO index 421c402e3..48c8fefea 100644 --- a/TODO +++ b/TODO @@ -22,7 +22,7 @@ around to it some time. If you have any good ideas, please let me know. * hwclock * stty * cut -* expr (maybe?) (ash builtin?) +* expr @@ -42,12 +42,6 @@ Perhaps I need to add a better build system (like the Linux kernel?) ----------------------- -There is no exclude file(s) option to tar. LRP's packaging system can not -function without this. Will you have the time to add this soon? - - ------------------------ - Feature request: /bin/busybox --install -s which makes all links to commands that it diff --git a/archival/tar.c b/archival/tar.c index 02fde0a64..732f26ddc 100644 --- a/archival/tar.c +++ b/archival/tar.c @@ -52,33 +52,33 @@ #include /* for PATH_MAX */ +static const char tar_usage[] = #ifdef BB_FEATURE_TAR_CREATE - -static const char tar_usage[] = - "tar -[cxtvOf] [tarFile] [-X excludeFile] [FILE] ...\n\n" - "Create, extract, or list files from a tar file. Note that\n" - "this version of tar packs hard links as separate files.\n\n" - "Options:\n" - - "\tc=create, x=extract, t=list contents, v=verbose,\n" - "\tO=extract to stdout, f=tarfile or \"-\" for stdin\n" - "\tX=exclude file\n"; - + "tar -[cxtvO] " #else - -static const char tar_usage[] = - "tar -[xtvO] [-f tarFile] [-X excludeFile] [FILE] ...\n\n" - "Extract, or list files stored in a tar file. This\n" - "version of tar does not support creation of tar files.\n\n" - "Options:\n" - - "\tx=extract, t=list contents, v=verbose,\n" - "\tO=extract to stdout, f=tarfile or \"-\" for stdin\n" - "\tX=exclude file\n"; - - + "tar -[xtvO] " #endif - +#if defined BB_FEATURE_TAR_EXCLUDE + "[--exclude File] " +#endif + "[-f tarFile] [FILE] ...\n\n" + "Create, extract, or list files from a tar file. Note that\n" + "this version of tar treats hard links as separate files.\n\n" + "Main operation mode:\n" +#ifdef BB_FEATURE_TAR_CREATE + "\tc\t\tcreate\n" +#endif + "\tx\t\textract\n" + "\tt\t\tlist\n" + "File selection:\n" + "\tf\t\tname of tarfile or \"-\" for stdin\n" + "\tO\t\textract to stdout\n" +#if defined BB_FEATURE_TAR_EXCLUDE + "\t--exclude\tfile to exclude\n" +#endif + "Informative output:\n" + "\tv\t\tverbosely list files processed\n" + ; /* Tar file constants */ #ifndef MAJOR @@ -91,31 +91,30 @@ static const char tar_usage[] = struct TarHeader { /* byte offset */ - char name[100]; /* 0 */ - char mode[8]; /* 100 */ - char uid[8]; /* 108 */ - char gid[8]; /* 116 */ - char size[12]; /* 124 */ - char mtime[12]; /* 136 */ - char chksum[8]; /* 148 */ - char typeflag; /* 156 */ - char linkname[100]; /* 157 */ - char magic[6]; /* 257 */ - char version[2]; /* 263 */ - char uname[32]; /* 265 */ - char gname[32]; /* 297 */ - char devmajor[8]; /* 329 */ - char devminor[8]; /* 337 */ - char prefix[155]; /* 345 */ - /* padding 500 */ + char name[100]; /* 0-99 */ + char mode[8]; /* 100-107 */ + char uid[8]; /* 108-115 */ + char gid[8]; /* 116-123 */ + char size[12]; /* 124-135 */ + char mtime[12]; /* 136-147 */ + char chksum[8]; /* 148-155 */ + char typeflag; /* 156-156 */ + char linkname[100]; /* 157-256 */ + char magic[6]; /* 257-262 */ + char version[2]; /* 263-264 */ + char uname[32]; /* 265-296 */ + char gname[32]; /* 297-328 */ + char devmajor[8]; /* 329-336 */ + char devminor[8]; /* 337-344 */ + char prefix[155]; /* 345-499 */ + char padding[12]; /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */ }; typedef struct TarHeader TarHeader; /* A few useful constants */ #define TAR_MAGIC "ustar" /* ustar and a null */ -//#define TAR_VERSION "00" /* 00 and no null */ -#define TAR_VERSION " " /* Be compatable with old GNU format */ +#define TAR_VERSION " " /* Be compatable with GNU tar format */ #define TAR_MAGIC_LEN 6 #define TAR_VERSION_LEN 2 #define TAR_BLOCK_SIZE 512 @@ -170,7 +169,9 @@ static int writeTarFile(const char* tarName, int tostdoutFlag, extern int tar_main(int argc, char **argv) { char** excludeList=NULL; +#if defined BB_FEATURE_TAR_EXCLUDE int excludeListSize=0; +#endif const char *tarName=NULL; int listFlag = FALSE; int extractFlag = FALSE; @@ -224,22 +225,26 @@ extern int tar_main(int argc, char **argv) tostdoutFlag = TRUE; tarName = "-"; break; - case 'X': - if (--argc == 0) { - fatalError( "Option requires an argument: No file specified\n"); - } - excludeList=realloc( excludeList, sizeof(char**) * (excludeListSize+1)); - excludeList[excludeListSize] = *(++argv); - /* Remove leading "/"s */ - if (*excludeList[excludeListSize] =='/') { - excludeList[excludeListSize] = (excludeList[excludeListSize])+1; - } - if (excludeList[excludeListSize++] == NULL) - fatalError( "Option requires an argument: No file specified\n"); - stopIt=TRUE; - break; - case '-': +#if defined BB_FEATURE_TAR_EXCLUDE + if (strcmp(*argv, "-exclude")==0) { + if (--argc == 0) { + fatalError( "Option requires an argument: No file specified\n"); + } + excludeList=realloc( excludeList, sizeof(char**) * (excludeListSize+2)); + excludeList[excludeListSize] = *(++argv); + /* Remove leading "/"s */ + if (*excludeList[excludeListSize] =='/') { + excludeList[excludeListSize] = (excludeList[excludeListSize])+1; + } + if (excludeList[excludeListSize++] == NULL) + fatalError( "Option requires an argument: No file specified\n"); + /* Tack a NULL onto the end of the list */ + excludeList[excludeListSize] = NULL; + stopIt=TRUE; + break; + } +#endif usage(tar_usage); break; @@ -249,13 +254,6 @@ extern int tar_main(int argc, char **argv) } } } -#if 0 - for (i=0; iname, strerror(errno)); return( FALSE); } + } else { + actualWriteSz=writeSize; } size -= actualWriteSz; @@ -532,11 +532,13 @@ readTarHeader(struct TarHeader *rawHeader, struct TarInfo *header) static int readTarFile(const char* tarName, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag, char** excludeList) { - int status, tarFd=0; + int status, tarFd=-1; int errorFlag=FALSE; TarHeader rawHeader; TarInfo header; - //int skipFileFlag=FALSE; +#if defined BB_FEATURE_TAR_EXCLUDE + char** tmpList; +#endif /* Open the tar file for reading. */ if (!strcmp(tarName, "-")) @@ -557,7 +559,6 @@ static int readTarFile(const char* tarName, int extractFlag, int listFlag, /* First, try to read the header */ if ( readTarHeader(&rawHeader, &header) == FALSE ) { - close( tarFd); if ( *(header.name) == '\0' ) { goto endgame; } else { @@ -568,7 +569,34 @@ static int readTarFile(const char* tarName, int extractFlag, int listFlag, } if ( *(header.name) == '\0' ) goto endgame; + header.tarFd = tarFd; +#if defined BB_FEATURE_TAR_EXCLUDE + { + int skipFlag=FALSE; + /* Check for excluded files.... */ + for (tmpList=excludeList; tmpList && *tmpList; tmpList++) { + /* Do some extra hoop jumping for when directory names + * end in '/' but the entry in tmpList doesn't */ + if (strncmp( *tmpList, header.name, strlen(*tmpList))==0 || ( + header.name[strlen(header.name)-1]=='/' + && strncmp( *tmpList, header.name, + MIN(strlen(header.name)-1, strlen(*tmpList)))==0)) { + /* If it is a regular file, pretend to extract it with + * the extractFlag set to FALSE, so the junk in the tarball + * is properly skipped over */ + if ( header.type==REGTYPE || header.type==REGTYPE0 ) { + tarExtractRegularFile(&header, FALSE, FALSE); + } + skipFlag=TRUE; + break; + } + } + /* There are not the droids you're looking for, move along */ + if (skipFlag==TRUE) + continue; + } +#endif /* Special treatment if the list (-t) flag is on */ if (verboseFlag == TRUE && extractFlag == FALSE) { int len, len1; @@ -623,13 +651,6 @@ static int readTarFile(const char* tarName, int extractFlag, int listFlag, printf("\n"); } -#if 0 - /* See if we want to restore this file or not */ - skipFileFlag=FALSE; - if (wantFileName(outName) == FALSE) { - skipFileFlag = TRUE; - } -#endif /* Remove any clutter lying in our way */ unlink( header.name); @@ -751,7 +772,9 @@ writeTarHeader(struct TarBallInfo *tbInfo, const char *fileName, struct stat *st { long chksum=0; struct TarHeader header; +#if defined BB_FEATURE_TAR_EXCLUDE char** tmpList; +#endif const unsigned char *cp = (const unsigned char *) &header; ssize_t size = sizeof(struct TarHeader); @@ -769,15 +792,23 @@ writeTarHeader(struct TarBallInfo *tbInfo, const char *fileName, struct stat *st strncpy(header.name, fileName, sizeof(header.name)); } - /* Now that leading '/''s have been removed, - * check for excluded files.... */ +#if defined BB_FEATURE_TAR_EXCLUDE + /* Check for excluded files.... */ for (tmpList=tbInfo->excludeList; tmpList && *tmpList; tmpList++) { - printf( "comparing '%s' and '%s'", *tmpList, header.name); - if (strcmp( *tmpList, header.name)==0) - printf( ": match\n"); - else - printf( "\n"); + /* Do some extra hoop jumping for when directory names + * end in '/' but the entry in tmpList doesn't */ + if (strncmp( *tmpList, header.name, strlen(*tmpList))==0 || ( + header.name[strlen(header.name)-1]=='/' + && strncmp( *tmpList, header.name, + MIN(strlen(header.name)-1, strlen(*tmpList)))==0)) { + /* Set the mode to something that is not a regular file, thereby + * faking out writeTarFile into thinking that nothing further need + * be done for this file. Yes, I know this is ugly, but it works. */ + statbuf->st_mode = 0; + return( TRUE); + } } +#endif putOctal(header.mode, sizeof(header.mode), statbuf->st_mode); putOctal(header.uid, sizeof(header.uid), statbuf->st_uid); @@ -965,6 +996,12 @@ static int writeTarFile(const char* tarName, int tostdoutFlag, for (size=0; size<(2*TAR_BLOCK_SIZE); size++) { write(tbInfo.tarFd, "\0", 1); } + + /* To be pedantically correct, we would check if the tarball + * is smaller then 20 tar blocks, and pad it if it was smaller, + * but that isn't necessary for GNU tar interoperability, and + * so is considered a waste of space */ + /* Hang up the tools, close up shop, head home */ close(tarFd); if (errorFlag == TRUE) { diff --git a/busybox.def.h b/busybox.def.h index dd59a284a..b05f17a68 100644 --- a/busybox.def.h +++ b/busybox.def.h @@ -180,6 +180,9 @@ // Enable support for creation of tar files. #define BB_FEATURE_TAR_CREATE // +// Enable support for "--exclude" for excluding files +//#define BB_FEATURE_TAR_EXCLUDE +// //// Enable reverse sort //#define BB_FEATURE_SORT_REVERSE // diff --git a/tar.c b/tar.c index 02fde0a64..732f26ddc 100644 --- a/tar.c +++ b/tar.c @@ -52,33 +52,33 @@ #include /* for PATH_MAX */ +static const char tar_usage[] = #ifdef BB_FEATURE_TAR_CREATE - -static const char tar_usage[] = - "tar -[cxtvOf] [tarFile] [-X excludeFile] [FILE] ...\n\n" - "Create, extract, or list files from a tar file. Note that\n" - "this version of tar packs hard links as separate files.\n\n" - "Options:\n" - - "\tc=create, x=extract, t=list contents, v=verbose,\n" - "\tO=extract to stdout, f=tarfile or \"-\" for stdin\n" - "\tX=exclude file\n"; - + "tar -[cxtvO] " #else - -static const char tar_usage[] = - "tar -[xtvO] [-f tarFile] [-X excludeFile] [FILE] ...\n\n" - "Extract, or list files stored in a tar file. This\n" - "version of tar does not support creation of tar files.\n\n" - "Options:\n" - - "\tx=extract, t=list contents, v=verbose,\n" - "\tO=extract to stdout, f=tarfile or \"-\" for stdin\n" - "\tX=exclude file\n"; - - + "tar -[xtvO] " #endif - +#if defined BB_FEATURE_TAR_EXCLUDE + "[--exclude File] " +#endif + "[-f tarFile] [FILE] ...\n\n" + "Create, extract, or list files from a tar file. Note that\n" + "this version of tar treats hard links as separate files.\n\n" + "Main operation mode:\n" +#ifdef BB_FEATURE_TAR_CREATE + "\tc\t\tcreate\n" +#endif + "\tx\t\textract\n" + "\tt\t\tlist\n" + "File selection:\n" + "\tf\t\tname of tarfile or \"-\" for stdin\n" + "\tO\t\textract to stdout\n" +#if defined BB_FEATURE_TAR_EXCLUDE + "\t--exclude\tfile to exclude\n" +#endif + "Informative output:\n" + "\tv\t\tverbosely list files processed\n" + ; /* Tar file constants */ #ifndef MAJOR @@ -91,31 +91,30 @@ static const char tar_usage[] = struct TarHeader { /* byte offset */ - char name[100]; /* 0 */ - char mode[8]; /* 100 */ - char uid[8]; /* 108 */ - char gid[8]; /* 116 */ - char size[12]; /* 124 */ - char mtime[12]; /* 136 */ - char chksum[8]; /* 148 */ - char typeflag; /* 156 */ - char linkname[100]; /* 157 */ - char magic[6]; /* 257 */ - char version[2]; /* 263 */ - char uname[32]; /* 265 */ - char gname[32]; /* 297 */ - char devmajor[8]; /* 329 */ - char devminor[8]; /* 337 */ - char prefix[155]; /* 345 */ - /* padding 500 */ + char name[100]; /* 0-99 */ + char mode[8]; /* 100-107 */ + char uid[8]; /* 108-115 */ + char gid[8]; /* 116-123 */ + char size[12]; /* 124-135 */ + char mtime[12]; /* 136-147 */ + char chksum[8]; /* 148-155 */ + char typeflag; /* 156-156 */ + char linkname[100]; /* 157-256 */ + char magic[6]; /* 257-262 */ + char version[2]; /* 263-264 */ + char uname[32]; /* 265-296 */ + char gname[32]; /* 297-328 */ + char devmajor[8]; /* 329-336 */ + char devminor[8]; /* 337-344 */ + char prefix[155]; /* 345-499 */ + char padding[12]; /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */ }; typedef struct TarHeader TarHeader; /* A few useful constants */ #define TAR_MAGIC "ustar" /* ustar and a null */ -//#define TAR_VERSION "00" /* 00 and no null */ -#define TAR_VERSION " " /* Be compatable with old GNU format */ +#define TAR_VERSION " " /* Be compatable with GNU tar format */ #define TAR_MAGIC_LEN 6 #define TAR_VERSION_LEN 2 #define TAR_BLOCK_SIZE 512 @@ -170,7 +169,9 @@ static int writeTarFile(const char* tarName, int tostdoutFlag, extern int tar_main(int argc, char **argv) { char** excludeList=NULL; +#if defined BB_FEATURE_TAR_EXCLUDE int excludeListSize=0; +#endif const char *tarName=NULL; int listFlag = FALSE; int extractFlag = FALSE; @@ -224,22 +225,26 @@ extern int tar_main(int argc, char **argv) tostdoutFlag = TRUE; tarName = "-"; break; - case 'X': - if (--argc == 0) { - fatalError( "Option requires an argument: No file specified\n"); - } - excludeList=realloc( excludeList, sizeof(char**) * (excludeListSize+1)); - excludeList[excludeListSize] = *(++argv); - /* Remove leading "/"s */ - if (*excludeList[excludeListSize] =='/') { - excludeList[excludeListSize] = (excludeList[excludeListSize])+1; - } - if (excludeList[excludeListSize++] == NULL) - fatalError( "Option requires an argument: No file specified\n"); - stopIt=TRUE; - break; - case '-': +#if defined BB_FEATURE_TAR_EXCLUDE + if (strcmp(*argv, "-exclude")==0) { + if (--argc == 0) { + fatalError( "Option requires an argument: No file specified\n"); + } + excludeList=realloc( excludeList, sizeof(char**) * (excludeListSize+2)); + excludeList[excludeListSize] = *(++argv); + /* Remove leading "/"s */ + if (*excludeList[excludeListSize] =='/') { + excludeList[excludeListSize] = (excludeList[excludeListSize])+1; + } + if (excludeList[excludeListSize++] == NULL) + fatalError( "Option requires an argument: No file specified\n"); + /* Tack a NULL onto the end of the list */ + excludeList[excludeListSize] = NULL; + stopIt=TRUE; + break; + } +#endif usage(tar_usage); break; @@ -249,13 +254,6 @@ extern int tar_main(int argc, char **argv) } } } -#if 0 - for (i=0; iname, strerror(errno)); return( FALSE); } + } else { + actualWriteSz=writeSize; } size -= actualWriteSz; @@ -532,11 +532,13 @@ readTarHeader(struct TarHeader *rawHeader, struct TarInfo *header) static int readTarFile(const char* tarName, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag, char** excludeList) { - int status, tarFd=0; + int status, tarFd=-1; int errorFlag=FALSE; TarHeader rawHeader; TarInfo header; - //int skipFileFlag=FALSE; +#if defined BB_FEATURE_TAR_EXCLUDE + char** tmpList; +#endif /* Open the tar file for reading. */ if (!strcmp(tarName, "-")) @@ -557,7 +559,6 @@ static int readTarFile(const char* tarName, int extractFlag, int listFlag, /* First, try to read the header */ if ( readTarHeader(&rawHeader, &header) == FALSE ) { - close( tarFd); if ( *(header.name) == '\0' ) { goto endgame; } else { @@ -568,7 +569,34 @@ static int readTarFile(const char* tarName, int extractFlag, int listFlag, } if ( *(header.name) == '\0' ) goto endgame; + header.tarFd = tarFd; +#if defined BB_FEATURE_TAR_EXCLUDE + { + int skipFlag=FALSE; + /* Check for excluded files.... */ + for (tmpList=excludeList; tmpList && *tmpList; tmpList++) { + /* Do some extra hoop jumping for when directory names + * end in '/' but the entry in tmpList doesn't */ + if (strncmp( *tmpList, header.name, strlen(*tmpList))==0 || ( + header.name[strlen(header.name)-1]=='/' + && strncmp( *tmpList, header.name, + MIN(strlen(header.name)-1, strlen(*tmpList)))==0)) { + /* If it is a regular file, pretend to extract it with + * the extractFlag set to FALSE, so the junk in the tarball + * is properly skipped over */ + if ( header.type==REGTYPE || header.type==REGTYPE0 ) { + tarExtractRegularFile(&header, FALSE, FALSE); + } + skipFlag=TRUE; + break; + } + } + /* There are not the droids you're looking for, move along */ + if (skipFlag==TRUE) + continue; + } +#endif /* Special treatment if the list (-t) flag is on */ if (verboseFlag == TRUE && extractFlag == FALSE) { int len, len1; @@ -623,13 +651,6 @@ static int readTarFile(const char* tarName, int extractFlag, int listFlag, printf("\n"); } -#if 0 - /* See if we want to restore this file or not */ - skipFileFlag=FALSE; - if (wantFileName(outName) == FALSE) { - skipFileFlag = TRUE; - } -#endif /* Remove any clutter lying in our way */ unlink( header.name); @@ -751,7 +772,9 @@ writeTarHeader(struct TarBallInfo *tbInfo, const char *fileName, struct stat *st { long chksum=0; struct TarHeader header; +#if defined BB_FEATURE_TAR_EXCLUDE char** tmpList; +#endif const unsigned char *cp = (const unsigned char *) &header; ssize_t size = sizeof(struct TarHeader); @@ -769,15 +792,23 @@ writeTarHeader(struct TarBallInfo *tbInfo, const char *fileName, struct stat *st strncpy(header.name, fileName, sizeof(header.name)); } - /* Now that leading '/''s have been removed, - * check for excluded files.... */ +#if defined BB_FEATURE_TAR_EXCLUDE + /* Check for excluded files.... */ for (tmpList=tbInfo->excludeList; tmpList && *tmpList; tmpList++) { - printf( "comparing '%s' and '%s'", *tmpList, header.name); - if (strcmp( *tmpList, header.name)==0) - printf( ": match\n"); - else - printf( "\n"); + /* Do some extra hoop jumping for when directory names + * end in '/' but the entry in tmpList doesn't */ + if (strncmp( *tmpList, header.name, strlen(*tmpList))==0 || ( + header.name[strlen(header.name)-1]=='/' + && strncmp( *tmpList, header.name, + MIN(strlen(header.name)-1, strlen(*tmpList)))==0)) { + /* Set the mode to something that is not a regular file, thereby + * faking out writeTarFile into thinking that nothing further need + * be done for this file. Yes, I know this is ugly, but it works. */ + statbuf->st_mode = 0; + return( TRUE); + } } +#endif putOctal(header.mode, sizeof(header.mode), statbuf->st_mode); putOctal(header.uid, sizeof(header.uid), statbuf->st_uid); @@ -965,6 +996,12 @@ static int writeTarFile(const char* tarName, int tostdoutFlag, for (size=0; size<(2*TAR_BLOCK_SIZE); size++) { write(tbInfo.tarFd, "\0", 1); } + + /* To be pedantically correct, we would check if the tarball + * is smaller then 20 tar blocks, and pad it if it was smaller, + * but that isn't necessary for GNU tar interoperability, and + * so is considered a waste of space */ + /* Hang up the tools, close up shop, head home */ close(tarFd); if (errorFlag == TRUE) {