Detect input file = output file (fixes #29)

This commit is contained in:
Jay Berkenbilt 2017-07-29 20:19:54 -04:00
parent 885b8781cc
commit 5993c3e83c
9 changed files with 108 additions and 1 deletions

View File

@ -1,5 +1,8 @@
2017-07-29 Jay Berkenbilt <ejb@ql.org>
* Detect when input file and output file are the same and exit to
avoid overwriting and losing input file. Fixes #29.
* When passing multiple inspection arguments, run --check first,
and defer exit until after all the checks have been run. This
makes it possible to force operations such as --show-xref to be

View File

@ -74,6 +74,9 @@ namespace QUtil
QPDF_DLL
qpdf_offset_t tell(FILE* stream);
QPDF_DLL
bool same_file(char const* name1, char const* name2);
QPDF_DLL
char* copy_string(std::string const&);

View File

@ -24,6 +24,7 @@
#include <io.h>
#else
#include <unistd.h>
#include <sys/stat.h>
#endif
std::string
@ -188,6 +189,55 @@ QUtil::tell(FILE* stream)
#endif
}
bool
QUtil::same_file(char const* name1, char const* name2)
{
if ((name1 == 0) || (strlen(name1) == 0) ||
(name2 == 0) || (strlen(name2) == 0))
{
return false;
}
#ifdef _WIN32
HANDLE fh1 = CreateFile(name1, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE fh2 = CreateFile(name2, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
BY_HANDLE_FILE_INFORMATION fi1;
BY_HANDLE_FILE_INFORMATION fi2;
bool same = false;
if ((fh1 != INVALID_HANDLE_VALUE) &&
(fh2 != INVALID_HANDLE_VALUE) &&
GetFileInformationByHandle(fh1, &fi1) &&
GetFileInformationByHandle(fh2, &fi2) &&
(fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber) &&
(fi1.nFileIndexLow == fi2.nFileIndexLow) &&
(fi1.nFileIndexHigh == fi2.nFileIndexHigh))
{
same = true;
}
if (fh1 != INVALID_HANDLE_VALUE)
{
CloseHandle(fh1);
}
if (fh2 != INVALID_HANDLE_VALUE)
{
CloseHandle(fh2);
}
return same;
#else
struct stat st1;
struct stat st2;
if ((stat(name1, &st1) == 0) &&
(stat(name2, &st2) == 0) &&
(st1.st_ino == st2.st_ino) &&
(st1.st_dev == st2.st_dev))
{
return true;
}
#endif
return false;
}
char*
QUtil::copy_string(std::string const& str)
{

View File

@ -0,0 +1 @@
test

View File

@ -35,3 +35,10 @@ quack1
quack2
quack3
quack4
----
file1: -qutil.out-, file2: -./qutil.out-; same: 1: PASS
file1: -qutil.out-, file2: -qutil.out-; same: 1: PASS
file1: -qutil.out-, file2: -other-file-; same: 0: PASS
file1: -qutil.out-, file2: --; same: 0: PASS
file1: -qutil.out-, file2: -(null)-; same: 0: PASS
file1: --, file2: -qutil.out-; same: 0: PASS

View File

@ -140,6 +140,36 @@ void get_whoami_test()
print_whoami("a\\b\\c\\quack4.exe");
}
void assert_same_file(char const* file1, char const* file2, bool expected)
{
bool actual = QUtil::same_file(file1, file2);
std::cout << "file1: -" << (file1 ? file1 : "(null)") << "-, file2: -"
<< (file2 ? file2 : "(null)") << "-; same: "
<< actual << ": " << ((actual == expected) ? "PASS" : "FAIL")
<< std::endl;
}
void same_file_test()
{
try
{
fclose(QUtil::safe_fopen("qutil.out", "r"));
fclose(QUtil::safe_fopen("other-file", "r"));
}
catch (std::exception)
{
std::cout << "same_file_test expects to have qutil.out and other-file"
" exist in the current directory\n";
return;
}
assert_same_file("qutil.out", "./qutil.out", true);
assert_same_file("qutil.out", "qutil.out", true);
assert_same_file("qutil.out", "other-file", false);
assert_same_file("qutil.out", "", false);
assert_same_file("qutil.out", 0, false);
assert_same_file("", "qutil.out", false);
}
int main(int argc, char* argv[])
{
try
@ -155,6 +185,8 @@ int main(int argc, char* argv[])
to_utf8_test();
std::cout << "----" << std::endl;
get_whoami_test();
std::cout << "----" << std::endl;
same_file_test();
}
catch (std::exception& e)
{

View File

@ -1361,6 +1361,12 @@ int main(int argc, char* argv[])
usage("no output file may be given for this option");
}
if (QUtil::same_file(infilename, outfilename))
{
QTC::TC("qpdf", "qpdf same file error");
usage("input file and output file are the same; this would cause input file to be lost");
}
try
{
QPDF pdf;

View File

@ -284,3 +284,4 @@ QPDFWriter preserve unreferenced standard 0
QPDFObjectHandle non-stream in parsecontent 0
QPDFObjectHandle errors in parsecontent 0
QPDF stream with non-space 0
qpdf same file error 0

View File

@ -206,7 +206,7 @@ $td->runtest("remove page we don't have",
show_ntests();
# ----------
$td->notify("--- Miscellaneous Tests ---");
$n_tests += 93;
$n_tests += 94;
$td->runtest("qpdf version",
{$td->COMMAND => "qpdf --version"},
@ -641,6 +641,10 @@ $td->runtest("dump corrected bad xref",
$td->EXIT_STATUS => 3},
$td->NORMALIZE_NEWLINES);
$td->runtest("don't overwrite self",
{$td->COMMAND => "qpdf a.pdf a.pdf"},
{$td->REGEXP => "input file and output file are the same.*",
$td->EXIT_STATUS => 2});
show_ntests();
# ----------