[zstdcli] Refuse to overwrite input file

Compare the input and output files by their inode number and
refuse to open the output file if the input file is the same.

This doesn't work when (de)compressing multiple files to a single
file, but that is a very uncommon use case, mostly used for
benchmarking by me.

Fixes #1422.
This commit is contained in:
Nick Terrell 2018-12-18 15:29:54 -08:00
parent 517d8c984c
commit cd2c8defad
2 changed files with 18 additions and 5 deletions

View File

@ -404,7 +404,7 @@ static FILE* FIO_openSrcFile(const char* srcFileName)
/** FIO_openDstFile() : /** FIO_openDstFile() :
* condition : `dstFileName` must be non-NULL. * condition : `dstFileName` must be non-NULL.
* @result : FILE* to `dstFileName`, or NULL if it fails */ * @result : FILE* to `dstFileName`, or NULL if it fails */
static FILE* FIO_openDstFile(const char* dstFileName) static FILE* FIO_openDstFile(const char* srcFileName, const char* dstFileName)
{ {
assert(dstFileName != NULL); assert(dstFileName != NULL);
if (!strcmp (dstFileName, stdoutmark)) { if (!strcmp (dstFileName, stdoutmark)) {
@ -416,6 +416,16 @@ static FILE* FIO_openDstFile(const char* dstFileName)
} }
return stdout; return stdout;
} }
if (srcFileName != NULL) {
stat_t srcStat;
stat_t dstStat;
if (UTIL_getFileStat(srcFileName, &srcStat) && UTIL_getFileStat(dstFileName, &dstStat)) {
if (srcStat.st_ino == dstStat.st_ino) {
DISPLAYLEVEL(1, "zstd: Refusing to open a output file which will overwrite the input file \n");
return NULL;
}
}
}
if (g_sparseFileSupport == 1) { if (g_sparseFileSupport == 1) {
g_sparseFileSupport = ZSTD_SPARSE_DEFAULT; g_sparseFileSupport = ZSTD_SPARSE_DEFAULT;
@ -1114,7 +1124,7 @@ static int FIO_compressFilename_dstFile(cRess_t ress,
if (ress.dstFile == NULL) { if (ress.dstFile == NULL) {
closeDstFile = 1; closeDstFile = 1;
DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: opening dst: %s", dstFileName); DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: opening dst: %s", dstFileName);
ress.dstFile = FIO_openDstFile(dstFileName); ress.dstFile = FIO_openDstFile(srcFileName, dstFileName);
if (ress.dstFile==NULL) return 1; /* could not open dstFileName */ if (ress.dstFile==NULL) return 1; /* could not open dstFileName */
/* Must only be added after FIO_openDstFile() succeeds. /* Must only be added after FIO_openDstFile() succeeds.
* Otherwise we may delete the destination file if it already exists, * Otherwise we may delete the destination file if it already exists,
@ -1264,7 +1274,7 @@ int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFile
assert(outFileName != NULL || suffix != NULL); assert(outFileName != NULL || suffix != NULL);
if (outFileName != NULL) { /* output into a single destination (stdout typically) */ if (outFileName != NULL) { /* output into a single destination (stdout typically) */
ress.dstFile = FIO_openDstFile(outFileName); ress.dstFile = FIO_openDstFile(NULL, outFileName);
if (ress.dstFile == NULL) { /* could not open outFileName */ if (ress.dstFile == NULL) { /* could not open outFileName */
error = 1; error = 1;
} else { } else {
@ -1880,7 +1890,7 @@ static int FIO_decompressDstFile(dRess_t ress, FILE* srcFile,
if (ress.dstFile == NULL) { if (ress.dstFile == NULL) {
releaseDstFile = 1; releaseDstFile = 1;
ress.dstFile = FIO_openDstFile(dstFileName); ress.dstFile = FIO_openDstFile(srcFileName, dstFileName);
if (ress.dstFile==0) return 1; if (ress.dstFile==0) return 1;
/* Must only be added after FIO_openDstFile() succeeds. /* Must only be added after FIO_openDstFile() succeeds.
@ -2057,7 +2067,7 @@ FIO_decompressMultipleFilenames(const char* srcNamesTable[], unsigned nbFiles,
if (outFileName) { if (outFileName) {
unsigned u; unsigned u;
ress.dstFile = FIO_openDstFile(outFileName); ress.dstFile = FIO_openDstFile(NULL, outFileName);
if (ress.dstFile == 0) EXM_THROW(71, "cannot open %s", outFileName); if (ress.dstFile == 0) EXM_THROW(71, "cannot open %s", outFileName);
for (u=0; u<nbFiles; u++) for (u=0; u<nbFiles; u++)
error |= FIO_decompressSrcFile(ress, outFileName, srcNamesTable[u]); error |= FIO_decompressSrcFile(ress, outFileName, srcNamesTable[u]);

View File

@ -183,6 +183,9 @@ $ECHO "test: --no-progress flag"
$ZSTD tmpro -c --no-progress | $ZSTD -d -o "$INTOVOID" --no-progress $ZSTD tmpro -c --no-progress | $ZSTD -d -o "$INTOVOID" --no-progress
$ZSTD tmpro -cv --no-progress | $ZSTD -dv -o "$INTOVOID" --no-progress $ZSTD tmpro -cv --no-progress | $ZSTD -dv -o "$INTOVOID" --no-progress
rm -f tmpro tmpro.zst rm -f tmpro tmpro.zst
$ECHO "test: overwrite input file (must fail)"
$ZSTD tmp -fo tmp && die "zstd overwrote the input file"
$ZSTD tmp.zst -dfo tmp.zst && die "zstd overwrote the input file"
$ECHO "test : file removal" $ECHO "test : file removal"