dedup/src/hash.cpp

131 lines
2.3 KiB
C++

#include "hash.h"
#include <cstring>
#include <fcntl.h>
#include <iomanip>
#include <sstream>
#include <sys/mman.h>
#include <unistd.h>
#include <openssl/sha.h>
#include <Log.h>
const static size_t HASHSIZE = 32; // the size of the SHA256 Hashsum in bytes
Hash::Hash() {}
Hash::Hash(const Hash& h) : data(new unsigned char[HASHSIZE]) {
std::memcpy(data, h.data, HASHSIZE);
}
Hash::Hash(Hash&& h) : data(h.data) {
h.data = nullptr;
}
Hash::~Hash() {
if(data) {
delete[] data;
data = nullptr;
}
}
bool Hash::create(Hash& h, const std::string& file) {
int fd = ::open(file.c_str(), O_RDONLY);
bool result = Hash::create(h, fd);
::close(fd);
return result;
}
bool Hash::create(Hash& h, int fd) {
if(fd < 0 || h.data) {
return false;
}
SHA256_CTX hashctx;
if(SHA256_Init(&hashctx) != 1) {
Log::error << "Can not create SHA256 CTX";
return false;
}
//determine filesize
loff_t fs = lseek64(fd, 0, SEEK_END);
lseek64(fd, 0, SEEK_SET);
void* mapping = ::mmap(NULL, fs, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0);
if(mapping == MAP_FAILED) {
return false;
}
::madvise(mapping, fs, MADV_DONTDUMP);
::madvise(mapping, fs, MADV_DONTFORK);
::madvise(mapping, fs, MADV_SEQUENTIAL);
::madvise(mapping, fs, MADV_WILLNEED);
if(SHA256_Update(&hashctx, mapping, fs) != 1) {
Log::error << "Can not update hash";
return false;
}
::munmap(mapping, fs);
unsigned char* md = new unsigned char[HASHSIZE];
if(SHA256_Final(md, &hashctx) != 1) {
Log::error << "Can not finalize hash";
return false;
}
h.data = md;
return true;
}
Hash::operator bool() const {
return data;
}
Hash::operator std::string() const {
std::stringstream str;
str << *this;
return str.str();
}
bool Hash::operator==(const Hash& rhs) const {
// same data ptr
if(data == rhs.data) return true;
// both no data
if(static_cast<bool>(data) == static_cast<bool>(rhs) && data == nullptr) return true;
return std::memcmp(data, rhs.data, HASHSIZE) == 0;
}
bool Hash::operator!=(const Hash& rhs) const {
return !(*this == rhs);
}
std::ostream& operator<<(std::ostream& str, const Hash& rhs) {
if(!rhs.data) {
return str << "[empty hash]";
}
std::ios_base::fmtflags flags = str.flags();
str << std::hex << std::setfill('0');
for(size_t i = 0; i < HASHSIZE; ++i) {
str << std::setw(2) << (int) rhs.data[i];
}
str.setf(flags);
return str;
}