From 45a4880b9cbb47ddc6c1758744629b5835d5dd19 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 26 Feb 2023 10:23:05 -0800 Subject: refuse to compress directories fix #1211, reported by @imba-tjd --- programs/lz4io.c | 26 ++++++++++++++++++-------- programs/util.h | 20 ++++++++++++++++---- tests/test-lz4-basic.sh | 35 +++++++++++++++++++---------------- 3 files changed, 53 insertions(+), 28 deletions(-) diff --git a/programs/lz4io.c b/programs/lz4io.c index 8b70b91..09d906e 100644 --- a/programs/lz4io.c +++ b/programs/lz4io.c @@ -325,11 +325,16 @@ static FILE* LZ4IO_openSrcFile(const char* srcFileName) DISPLAYLEVEL(4,"Using stdin for input \n"); f = stdin; SET_BINARY_MODE(stdin); - } else { - f = fopen(srcFileName, "rb"); - if (f==NULL) DISPLAYLEVEL(1, "%s: %s \n", srcFileName, strerror(errno)); + return f; + } + + if (UTIL_isDir(srcFileName)) { + DISPLAYLEVEL(1, "lz4: %s is a directory -- ignored \n", srcFileName); + return NULL; } + f = fopen(srcFileName, "rb"); + if (f==NULL) DISPLAYLEVEL(1, "%s: %s \n", srcFileName, strerror(errno)); return f; } @@ -337,7 +342,8 @@ static FILE* LZ4IO_openSrcFile(const char* srcFileName) * prefs is writable, because sparseFileSupport might be updated. * condition : `dstFileName` must be non-NULL. * @result : FILE* to `dstFileName`, or NULL if it fails */ -static FILE* LZ4IO_openDstFile(const char* dstFileName, const LZ4IO_prefs_t* const prefs) +static FILE* +LZ4IO_openDstFile(const char* dstFileName, const LZ4IO_prefs_t* const prefs) { FILE* f; assert(dstFileName != NULL); @@ -563,11 +569,14 @@ static void* LZ4IO_createDict(size_t* dictSize, const char* const dictFilename) char* dictBuf; FILE* dictFile; - if (!circularBuf) END_PROCESS(25, "Allocation error : not enough memory for circular buffer"); - if (!dictFilename) END_PROCESS(26, "Dictionary error : no filename provided"); + if (!dictFilename) + END_PROCESS(26, "Dictionary error : no filename provided"); + if (!circularBuf) + END_PROCESS(25, "Allocation error : not enough memory for circular buffer"); dictFile = LZ4IO_openSrcFile(dictFilename); - if (!dictFile) END_PROCESS(27, "Dictionary error : could not open dictionary file"); + if (!dictFile) + END_PROCESS(27, "Dictionary error : could not open dictionary file"); /* opportunistically seek to the part of the file we care about. * If this fails it's not a problem since we'll just read everything anyways. */ @@ -1311,7 +1320,8 @@ LZ4IO_decompressSrcFile(dRess_t ress, static int LZ4IO_decompressDstFile(dRess_t ress, - const char* input_filename, const char* output_filename, + const char* input_filename, + const char* output_filename, const LZ4IO_prefs_t* const prefs) { int result; diff --git a/programs/util.h b/programs/util.h index 3192ddc..f1d8a92 100644 --- a/programs/util.h +++ b/programs/util.h @@ -195,10 +195,7 @@ UTIL_STATIC void* UTIL_realloc(void* ptr, size_t size) /*-**************************************** * String functions ******************************************/ -/* - * A modified version of realloc(). - * If UTIL_realloc() fails the original block is freed. -*/ +/* supports a==NULL or b==NULL */ UTIL_STATIC int UTIL_sameString(const char* a, const char* b) { assert(a!=NULL && b!=NULL); /* unsupported scenario */ @@ -430,6 +427,21 @@ UTIL_STATIC int UTIL_isRegFile(const char* infilename) return UTIL_getFileStat(infilename, &statbuf); /* Only need to know whether it is a regular file */ } +UTIL_STATIC int UTIL_isDir(const char* infilename) +{ + stat_t statbuf; + int r; +#if defined(_MSC_VER) + r = _stat64(infilename, &statbuf); + if (r) return 0; + return (statbuf.st_mode & S_IFDIR); +#else + r = stat(infilename, &statbuf); + if (r) return 0; + return (S_ISDIR(statbuf.st_mode)); +#endif +} + UTIL_STATIC U32 UTIL_isDirectory(const char* infilename) { diff --git a/tests/test-lz4-basic.sh b/tests/test-lz4-basic.sh index 1767827..260d751 100755 --- a/tests/test-lz4-basic.sh +++ b/tests/test-lz4-basic.sh @@ -5,7 +5,7 @@ FPREFIX="tmp-tlb" set -e remove () { - rm $FPREFIX* + rm -rf $FPREFIX* } trap remove EXIT @@ -27,42 +27,45 @@ datagen -g33M | lz4 --no-frame-crc | lz4 -t datagen -g256MB | lz4 -vqB4D | lz4 -t --no-crc echo "hello world" > $FPREFIX-hw lz4 --rm -f $FPREFIX-hw $FPREFIX-hw.lz4 -test ! -f $FPREFIX-hw # must fail (--rm) +test ! -f $FPREFIX-hw # must fail (--rm) test -f $FPREFIX-hw.lz4 lz4cat $FPREFIX-hw.lz4 | grep "hello world" unlz4 --rm $FPREFIX-hw.lz4 $FPREFIX-hw test -f $FPREFIX-hw -test ! -f $FPREFIX-hw.lz4 # must fail (--rm) -test ! -f $FPREFIX-hw.lz4.lz4 # must fail (unlz4) -lz4cat $FPREFIX-hw # pass-through mode +test ! -f $FPREFIX-hw.lz4 # must fail (--rm) +test ! -f $FPREFIX-hw.lz4.lz4 # must fail (unlz4) +lz4cat $FPREFIX-hw # pass-through mode test -f $FPREFIX-hw -test ! -f $FPREFIX-hw.lz4 # must fail (lz4cat) -lz4 $FPREFIX-hw $FPREFIX-hw.lz4 # creates $FPREFIX-hw.lz4 +test ! -f $FPREFIX-hw.lz4 # must fail (lz4cat) +lz4 $FPREFIX-hw $FPREFIX-hw.lz4 # creates $FPREFIX-hw.lz4 lz4cat < $FPREFIX-hw.lz4 > ${FPREFIX}3 # checks lz4cat works with stdin (#285) diff -q $FPREFIX-hw ${FPREFIX}3 lz4cat < $FPREFIX-hw > ${FPREFIX}2 # checks lz4cat works in pass-through mode diff -q $FPREFIX-hw ${FPREFIX}2 cp $FPREFIX-hw ./-d -lz4 --rm -- -d -d.lz4 # compresses ./d into ./-d.lz4 +lz4 --rm -- -d -d.lz4 # compresses ./d into ./-d.lz4 test -f ./-d.lz4 test ! -f ./-d mv ./-d.lz4 ./-z -lz4 -d --rm -- -z ${FPREFIX}4 # uncompresses ./-z into $FPREFIX4 +lz4 -d --rm -- -z ${FPREFIX}4 # uncompresses ./-z into $FPREFIX4 test ! -f ./-z diff -q $FPREFIX-hw ${FPREFIX}4 -lz4 ${FPREFIX}2 ${FPREFIX}3 ${FPREFIX}4 && exit 1 # must fail: refuse to handle 3+ file names -lz4 -f $FPREFIX-hw # create $FPREFIX-hw.lz4, for next tests -lz4 --list $FPREFIX-hw.lz4 # test --list on valid single-frame file -lz4 --list < $FPREFIX-hw.lz4 # test --list from stdin (file only) +lz4 ${FPREFIX}2 ${FPREFIX}3 ${FPREFIX}4 && exit 1 # must fail: refuse to handle 3+ file names +mkdir -p ${FPREFIX}-dir +lz4 ${FPREFIX}-dir && exit 1 # must fail: refuse to compress directory +test ! -f ${FPREFIX}-dir.lz4 # must not create artifact (#1211) +lz4 -f $FPREFIX-hw # create $FPREFIX-hw.lz4, for next tests +lz4 --list $FPREFIX-hw.lz4 # test --list on valid single-frame file +lz4 --list < $FPREFIX-hw.lz4 # test --list from stdin (file only) cat $FPREFIX-hw >> $FPREFIX-hw.lz4 -lz4 -f $FPREFIX-hw.lz4 && exit 1 # uncompress valid frame followed by invalid data (must fail now) -lz4 -BX $FPREFIX-hw -c -q | lz4 -tv # test block checksum +lz4 -f $FPREFIX-hw.lz4 && exit 1 # uncompress valid frame followed by invalid data (must fail now) +lz4 -BX $FPREFIX-hw -c -q | lz4 -tv # test block checksum # datagen -g20KB generates the same file every single time # cannot save output of datagen -g20KB as input file to lz4 because the following shell commands are run before datagen -g20KB test "$(datagen -g20KB | lz4 -c --fast | wc -c)" -lt "$(datagen -g20KB | lz4 -c --fast=9 | wc -c)" # -1 vs -9 test "$(datagen -g20KB | lz4 -c -1 | wc -c)" -lt "$(datagen -g20KB| lz4 -c --fast=1 | wc -c)" # 1 vs -1 test "$(datagen -g20KB | lz4 -c --fast=1 | wc -c)" -eq "$(datagen -g20KB| lz4 -c --fast| wc -c)" # checks default fast compression is -1 -lz4 -c --fast=0 $FPREFIX-dg20K && exit 1 # lz4 should fail when fast=0 +lz4 -c --fast=0 $FPREFIX-dg20K && exit 1 # lz4 should fail when fast=0 lz4 -c --fast=-1 $FPREFIX-dg20K && exit 1 # lz4 should fail when fast=-1 # High --fast values can result in out-of-bound dereferences #876 datagen -g1M | lz4 -c --fast=999999999 > $FPREFIX-trash -- cgit v0.12