2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-05-29 00:10:54 +00:00

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> 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, * When passing multiple inspection arguments, run --check first,
and defer exit until after all the checks have been run. This and defer exit until after all the checks have been run. This
makes it possible to force operations such as --show-xref to be makes it possible to force operations such as --show-xref to be

View File

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

View File

@ -24,6 +24,7 @@
#include <io.h> #include <io.h>
#else #else
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h>
#endif #endif
std::string std::string
@ -188,6 +189,55 @@ QUtil::tell(FILE* stream)
#endif #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* char*
QUtil::copy_string(std::string const& str) QUtil::copy_string(std::string const& str)
{ {

View File

@ -0,0 +1 @@
test

View File

@ -35,3 +35,10 @@ quack1
quack2 quack2
quack3 quack3
quack4 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"); 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[]) int main(int argc, char* argv[])
{ {
try try
@ -155,6 +185,8 @@ int main(int argc, char* argv[])
to_utf8_test(); to_utf8_test();
std::cout << "----" << std::endl; std::cout << "----" << std::endl;
get_whoami_test(); get_whoami_test();
std::cout << "----" << std::endl;
same_file_test();
} }
catch (std::exception& e) 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"); 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 try
{ {
QPDF pdf; QPDF pdf;

View File

@ -284,3 +284,4 @@ QPDFWriter preserve unreferenced standard 0
QPDFObjectHandle non-stream in parsecontent 0 QPDFObjectHandle non-stream in parsecontent 0
QPDFObjectHandle errors in parsecontent 0 QPDFObjectHandle errors in parsecontent 0
QPDF stream with non-space 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(); show_ntests();
# ---------- # ----------
$td->notify("--- Miscellaneous Tests ---"); $td->notify("--- Miscellaneous Tests ---");
$n_tests += 93; $n_tests += 94;
$td->runtest("qpdf version", $td->runtest("qpdf version",
{$td->COMMAND => "qpdf --version"}, {$td->COMMAND => "qpdf --version"},
@ -641,6 +641,10 @@ $td->runtest("dump corrected bad xref",
$td->EXIT_STATUS => 3}, $td->EXIT_STATUS => 3},
$td->NORMALIZE_NEWLINES); $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(); show_ntests();
# ---------- # ----------