Factor Compute(File|String)MD5 into cmCryptoHash helper

Define an abstract API around the backend hash algorithm.  Expose
ifstream errors to HashFile callers.  Always try opening the file.
Succeed only if the end of file is reached without error.
This commit is contained in:
Brad King 2011-11-15 19:37:38 -05:00
parent a9e686d68b
commit ed7cef5634
4 changed files with 139 additions and 50 deletions

View File

@ -129,6 +129,8 @@ SET(SRCS
cmComputeLinkInformation.h cmComputeLinkInformation.h
cmComputeTargetDepends.h cmComputeTargetDepends.h
cmComputeTargetDepends.cxx cmComputeTargetDepends.cxx
cmCryptoHash.cxx
cmCryptoHash.h
cmCustomCommand.cxx cmCustomCommand.cxx
cmCustomCommand.h cmCustomCommand.h
cmCustomCommandGenerator.cxx cmCustomCommandGenerator.cxx

90
Source/cmCryptoHash.cxx Normal file
View File

@ -0,0 +1,90 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmCryptoHash.h"
#include <cmsys/MD5.h>
//----------------------------------------------------------------------------
std::string cmCryptoHash::HashString(const char* input)
{
this->Initialize();
this->Append(reinterpret_cast<unsigned char const*>(input),
static_cast<int>(strlen(input)));
return this->Finalize();
}
//----------------------------------------------------------------------------
std::string cmCryptoHash::HashFile(const char* file)
{
std::ifstream fin(file, std::ios::in | cmsys_ios_binary);
if(!fin)
{
return "";
}
this->Initialize();
// Should be efficient enough on most system:
const int bufferSize = 4096;
char buffer[bufferSize];
unsigned char const* buffer_uc =
reinterpret_cast<unsigned char const*>(buffer);
// This copy loop is very sensitive on certain platforms with
// slightly broken stream libraries (like HPUX). Normally, it is
// incorrect to not check the error condition on the fin.read()
// before using the data, but the fin.gcount() will be zero if an
// error occurred. Therefore, the loop should be safe everywhere.
while(fin)
{
fin.read(buffer, bufferSize);
if(int gcount = static_cast<int>(fin.gcount()))
{
this->Append(buffer_uc, gcount);
}
}
if(fin.eof())
{
return this->Finalize();
}
return "";
}
//----------------------------------------------------------------------------
cmCryptoHashMD5::cmCryptoHashMD5(): MD5(cmsysMD5_New())
{
}
//----------------------------------------------------------------------------
cmCryptoHashMD5::~cmCryptoHashMD5()
{
cmsysMD5_Delete(this->MD5);
}
//----------------------------------------------------------------------------
void cmCryptoHashMD5::Initialize()
{
cmsysMD5_Initialize(this->MD5);
}
//----------------------------------------------------------------------------
void cmCryptoHashMD5::Append(unsigned char const* buf, int sz)
{
cmsysMD5_Append(this->MD5, buf, sz);
}
//----------------------------------------------------------------------------
std::string cmCryptoHashMD5::Finalize()
{
char md5out[32];
cmsysMD5_FinalizeHex(this->MD5, md5out);
return std::string(md5out, 32);
}

40
Source/cmCryptoHash.h Normal file
View File

@ -0,0 +1,40 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmCryptoHash_h
#define cmCryptoHash_h
#include "cmStandardIncludes.h"
class cmCryptoHash
{
public:
std::string HashString(const char* input);
std::string HashFile(const char* file);
protected:
virtual void Initialize()=0;
virtual void Append(unsigned char const*, int)=0;
virtual std::string Finalize()=0;
};
class cmCryptoHashMD5: public cmCryptoHash
{
struct cmsysMD5_s* MD5;
public:
cmCryptoHashMD5();
~cmCryptoHashMD5();
protected:
virtual void Initialize();
virtual void Append(unsigned char const* buf, int sz);
virtual std::string Finalize();
};
#endif

View File

@ -54,7 +54,7 @@
#if defined(CMAKE_BUILD_WITH_CMAKE) #if defined(CMAKE_BUILD_WITH_CMAKE)
# include <memory> // auto_ptr # include <memory> // auto_ptr
# include <fcntl.h> # include <fcntl.h>
# include <cmsys/MD5.h> # include "cmCryptoHash.h"
#endif #endif
#if defined(CMAKE_USE_ELF_PARSER) #if defined(CMAKE_USE_ELF_PARSER)
@ -1197,48 +1197,10 @@ bool cmSystemTools::RenameFile(const char* oldname, const char* newname)
bool cmSystemTools::ComputeFileMD5(const char* source, char* md5out) bool cmSystemTools::ComputeFileMD5(const char* source, char* md5out)
{ {
#if defined(CMAKE_BUILD_WITH_CMAKE) #if defined(CMAKE_BUILD_WITH_CMAKE)
if(!cmSystemTools::FileExists(source)) cmCryptoHashMD5 md5;
{ std::string str = md5.HashFile(source);
return false; strncpy(md5out, str.c_str(), 32);
} return !str.empty();
// Open files
#if defined(_WIN32) || defined(__CYGWIN__)
cmsys_ios::ifstream fin(source, cmsys_ios::ios::binary | cmsys_ios::ios::in);
#else
cmsys_ios::ifstream fin(source);
#endif
if(!fin)
{
return false;
}
cmsysMD5* md5 = cmsysMD5_New();
cmsysMD5_Initialize(md5);
// Should be efficient enough on most system:
const int bufferSize = 4096;
char buffer[bufferSize];
unsigned char const* buffer_uc =
reinterpret_cast<unsigned char const*>(buffer);
// This copy loop is very sensitive on certain platforms with
// slightly broken stream libraries (like HPUX). Normally, it is
// incorrect to not check the error condition on the fin.read()
// before using the data, but the fin.gcount() will be zero if an
// error occurred. Therefore, the loop should be safe everywhere.
while(fin)
{
fin.read(buffer, bufferSize);
if(int gcount = static_cast<int>(fin.gcount()))
{
cmsysMD5_Append(md5, buffer_uc, gcount);
}
}
cmsysMD5_FinalizeHex(md5, md5out);
cmsysMD5_Delete(md5);
fin.close();
return true;
#else #else
(void)source; (void)source;
(void)md5out; (void)md5out;
@ -1250,13 +1212,8 @@ bool cmSystemTools::ComputeFileMD5(const char* source, char* md5out)
std::string cmSystemTools::ComputeStringMD5(const char* input) std::string cmSystemTools::ComputeStringMD5(const char* input)
{ {
#if defined(CMAKE_BUILD_WITH_CMAKE) #if defined(CMAKE_BUILD_WITH_CMAKE)
char md5out[32]; cmCryptoHashMD5 md5;
cmsysMD5* md5 = cmsysMD5_New(); return md5.HashString(input);
cmsysMD5_Initialize(md5);
cmsysMD5_Append(md5, reinterpret_cast<unsigned char const*>(input), -1);
cmsysMD5_FinalizeHex(md5, md5out);
cmsysMD5_Delete(md5);
return std::string(md5out, 32);
#else #else
(void)input; (void)input;
cmSystemTools::Message("md5sum not supported in bootstrapping mode","Error"); cmSystemTools::Message("md5sum not supported in bootstrapping mode","Error");