Файлы
gltth.zip Класс для работы с хешами в ntfs-потоках. Просьба не менять формат данных и название используемого потока.
File.cpp
// [...]
// greylink dc++: work with long timestamps
int64_t File::getTimeStamp(const string& aFileName) throw(FileException) {
WIN32_FIND_DATA fd;
HANDLE hFind = FindFirstFile(Text::toT(aFileName).c_str(), &fd);
if (hFind == INVALID_HANDLE_VALUE)
throw FileException(Util::translateError(GetLastError()) + ": " + aFileName);
FindClose(hFind);
return *(int64_t*)&fd.ftLastWriteTime;
}
void File::setTimeStamp(const string& aFileName, const int64_t stamp) throw(FileException) {
HANDLE h = CreateFile(Text::toT(aFileName).c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(h == INVALID_HANDLE_VALUE)
throw FileException(Util::translateError(GetLastError()) + ": " + aFileName);
if (!SetFileTime(h, NULL, NULL, (FILETIME*)&stamp))
throw FileException(Util::translateError(GetLastError()) + ": " + aFileName);
CloseHandle(h);
}
// [...]
// greylink dc++: work with long timestamps
int64_t File::getTimeStamp(const string& aFileName) throw(FileException) {
WIN32_FIND_DATA fd;
HANDLE hFind = FindFirstFile(Text::toT(aFileName).c_str(), &fd);
if (hFind == INVALID_HANDLE_VALUE)
throw FileException(Util::translateError(GetLastError()) + ": " + aFileName);
FindClose(hFind);
return *(int64_t*)&fd.ftLastWriteTime;
}
void File::setTimeStamp(const string& aFileName, const int64_t stamp) throw(FileException) {
HANDLE h = CreateFile(Text::toT(aFileName).c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(h == INVALID_HANDLE_VALUE)
throw FileException(Util::translateError(GetLastError()) + ": " + aFileName);
if (!SetFileTime(h, NULL, NULL, (FILETIME*)&stamp))
throw FileException(Util::translateError(GetLastError()) + ": " + aFileName);
CloseHandle(h);
}
// [...]
HashManager.h
#pragma once
#include "Singleton.h"
#include "MultiThreadHasher.h"
#include "TimerManager.h"
#include "DatabaseManager.h"
#include "HashManagerListener.h"
STANDARD_EXCEPTION(HashException);
class HashManager : public Singleton<HashManager>, public Speaker<HashManagerListener>, private TimerManagerListener, public MultiThreadHasher {
public:
/** We don't keep leaves for blocks smaller than this... */
static const int64_t MIN_BLOCK_SIZE = 64*1024;
// Check if the TTH tree associated with the filename is current.
tthroot_t checkTTH(const string& aFileName, int64_t aSize, uint32_t aTimeStamp);
// start background hashing or read TTH from NTFS stream
void hashFile(const string& aFileName, int64_t aSize, Flags flags = 0, DbFileObjPtr file = NULL);
// start hashing from GUI, etc
void hashFile(const string& aFileName, Flags flags = 0);
// fire TTHDone event for all queued done hashes
void flushHasher();
bool getTTH(const string& aFileName, TTHValue& dst) throw(); // [gl] nothrow version
bool getTree(const TTHValue& root, TigerTree& tt);
void addTree(const TigerTree& tree);
string getFilePath(const TTHValue& tth); // [gl]-s
size_t getBlockSize(const TTHValue& root, int64_t* fileSize = NULL);
void startup();
void shutdown();
class StreamStore { // greylink dc++: work with ntfs stream
public:
StreamStore(const string& aFileName) : fileName(aFileName) { }
bool loadTree(TigerTree& tt, int64_t aFileSize = -1);
bool saveTree(const TigerTree& tt);
private:
struct TTHStreamHeader {
uint32_t magic;
uint32_t checksum; // xor of other TTHStreamHeader DWORDs
uint64_t fileSize;
uint64_t timeStamp;
uint64_t blockSize;
TTHValue root;
};
static const uint64_t minStreamedFileSize = 16*1048576;
static const uint32_t MAGIC = '++lg';
static const string streamName;
string fileName;
void setCheckSum(TTHStreamHeader& h);
bool validateCheckSum(const TTHStreamHeader& h);
};
private:
HasherFileTask::List finished;
CriticalSection cs;
void hashDone(HasherFileTask::Ptr& fileTask, bool fromStream);
// `MultiThreadHasher` override
virtual void hashDone(HasherFileTask::Ptr& fileTask) { hashDone(fileTask, false); }
// TimerManagerListener
void on(Second, tick_t) throw() {}
void on(Minute, tick_t) throw();
};
#include "Singleton.h"
#include "MultiThreadHasher.h"
#include "TimerManager.h"
#include "DatabaseManager.h"
#include "HashManagerListener.h"
STANDARD_EXCEPTION(HashException);
class HashManager : public Singleton<HashManager>, public Speaker<HashManagerListener>, private TimerManagerListener, public MultiThreadHasher {
public:
/** We don't keep leaves for blocks smaller than this... */
static const int64_t MIN_BLOCK_SIZE = 64*1024;
// Check if the TTH tree associated with the filename is current.
tthroot_t checkTTH(const string& aFileName, int64_t aSize, uint32_t aTimeStamp);
// start background hashing or read TTH from NTFS stream
void hashFile(const string& aFileName, int64_t aSize, Flags flags = 0, DbFileObjPtr file = NULL);
// start hashing from GUI, etc
void hashFile(const string& aFileName, Flags flags = 0);
// fire TTHDone event for all queued done hashes
void flushHasher();
bool getTTH(const string& aFileName, TTHValue& dst) throw(); // [gl] nothrow version
bool getTree(const TTHValue& root, TigerTree& tt);
void addTree(const TigerTree& tree);
string getFilePath(const TTHValue& tth); // [gl]-s
size_t getBlockSize(const TTHValue& root, int64_t* fileSize = NULL);
void startup();
void shutdown();
class StreamStore { // greylink dc++: work with ntfs stream
public:
StreamStore(const string& aFileName) : fileName(aFileName) { }
bool loadTree(TigerTree& tt, int64_t aFileSize = -1);
bool saveTree(const TigerTree& tt);
private:
struct TTHStreamHeader {
uint32_t magic;
uint32_t checksum; // xor of other TTHStreamHeader DWORDs
uint64_t fileSize;
uint64_t timeStamp;
uint64_t blockSize;
TTHValue root;
};
static const uint64_t minStreamedFileSize = 16*1048576;
static const uint32_t MAGIC = '++lg';
static const string streamName;
string fileName;
void setCheckSum(TTHStreamHeader& h);
bool validateCheckSum(const TTHStreamHeader& h);
};
private:
HasherFileTask::List finished;
CriticalSection cs;
void hashDone(HasherFileTask::Ptr& fileTask, bool fromStream);
// `MultiThreadHasher` override
virtual void hashDone(HasherFileTask::Ptr& fileTask) { hashDone(fileTask, false); }
// TimerManagerListener
void on(Second, tick_t) throw() {}
void on(Minute, tick_t) throw();
};
HashManager.cpp
// [...]
// greylink dc++
const string HashManager::StreamStore::streamName(".gltth");
void HashManager::StreamStore::setCheckSum(TTHStreamHeader& h) {
h.magic = MAGIC;
uint32_t sum = 0;
for (size_t i = 0; i < sizeof(TTHStreamHeader) / sizeof(uint32_t); i++)
sum ^= ((uint32_t*)&h)[i];
h.checksum ^= sum;
}
bool HashManager::StreamStore::validateCheckSum(const TTHStreamHeader& h) {
if (h.magic != MAGIC)
return false;
uint32_t sum = 0;
for (size_t i = 0; i < sizeof(TTHStreamHeader) / sizeof(uint32_t); i++)
sum ^= ((uint32_t*)&h)[i];
return (sum == 0);
}
bool HashManager::StreamStore::loadTree(TigerTree& tt, int64_t aFileSize /* = -1 */ ) {
try {
int64_t fileSize = (aFileSize == -1)? File::getSize(fileName) : aFileSize;
if (fileSize < minStreamedFileSize) // that's why minStreamedFileSize never be changed!
return false;
uint64_t timeStamp = File::getTimeStamp(fileName);
{
File stream(fileName + ":" + streamName, File::READ, File::OPEN);
size_t sz = sizeof(TTHStreamHeader);
TTHStreamHeader h;
if (stream.read(&h, sz) != sizeof(TTHStreamHeader))
return false;
if (!validateCheckSum(h) || (uint64_t)fileSize != h.fileSize || timeStamp != h.timeStamp)
return false;
size_t datalen = TigerTree::calcBlocks(fileSize, h.blockSize) * TTHValue::BYTES;
sz = datalen;
AutoArray<uint8_t> buf(datalen);
if (stream.read((uint8_t*)buf, sz) != datalen)
return false;
tt = TigerTree(fileSize, h.blockSize, buf);
if(!(tt.getRoot() == h.root))
return false;
}
} catch (const Exception&) {
return false;
}
return true;
}
bool HashManager::StreamStore::saveTree(const TigerTree& tt) {
if (!gSETTING(TTH_IN_NTFS_STREAM))
return false;
try {
TTHStreamHeader h;
h.fileSize = File::getSize(fileName);
if (h.fileSize < minStreamedFileSize || h.fileSize != (uint64_t)tt.getFileSize())
return false; // that's why minStreamedFileSize never be changed!
h.timeStamp = File::getTimeStamp(fileName);
h.root = tt.getRoot();
h.blockSize = tt.getBlockSize();
setCheckSum(h);
{
File stream(fileName + ":" + streamName, File::WRITE, File::CREATE | File::TRUNCATE);
stream.write(&h, sizeof(TTHStreamHeader));
stream.write(tt.getLeaves()[0].data, tt.getLeaves().size() * TTHValue::BYTES);
}
File::setTimeStamp(fileName, h.timeStamp);
} catch (const Exception&) {
return false;
}
return true;
}
// [...]
// greylink dc++
const string HashManager::StreamStore::streamName(".gltth");
void HashManager::StreamStore::setCheckSum(TTHStreamHeader& h) {
h.magic = MAGIC;
uint32_t sum = 0;
for (size_t i = 0; i < sizeof(TTHStreamHeader) / sizeof(uint32_t); i++)
sum ^= ((uint32_t*)&h)[i];
h.checksum ^= sum;
}
bool HashManager::StreamStore::validateCheckSum(const TTHStreamHeader& h) {
if (h.magic != MAGIC)
return false;
uint32_t sum = 0;
for (size_t i = 0; i < sizeof(TTHStreamHeader) / sizeof(uint32_t); i++)
sum ^= ((uint32_t*)&h)[i];
return (sum == 0);
}
bool HashManager::StreamStore::loadTree(TigerTree& tt, int64_t aFileSize /* = -1 */ ) {
try {
int64_t fileSize = (aFileSize == -1)? File::getSize(fileName) : aFileSize;
if (fileSize < minStreamedFileSize) // that's why minStreamedFileSize never be changed!
return false;
uint64_t timeStamp = File::getTimeStamp(fileName);
{
File stream(fileName + ":" + streamName, File::READ, File::OPEN);
size_t sz = sizeof(TTHStreamHeader);
TTHStreamHeader h;
if (stream.read(&h, sz) != sizeof(TTHStreamHeader))
return false;
if (!validateCheckSum(h) || (uint64_t)fileSize != h.fileSize || timeStamp != h.timeStamp)
return false;
size_t datalen = TigerTree::calcBlocks(fileSize, h.blockSize) * TTHValue::BYTES;
sz = datalen;
AutoArray<uint8_t> buf(datalen);
if (stream.read((uint8_t*)buf, sz) != datalen)
return false;
tt = TigerTree(fileSize, h.blockSize, buf);
if(!(tt.getRoot() == h.root))
return false;
}
} catch (const Exception&) {
return false;
}
return true;
}
bool HashManager::StreamStore::saveTree(const TigerTree& tt) {
if (!gSETTING(TTH_IN_NTFS_STREAM))
return false;
try {
TTHStreamHeader h;
h.fileSize = File::getSize(fileName);
if (h.fileSize < minStreamedFileSize || h.fileSize != (uint64_t)tt.getFileSize())
return false; // that's why minStreamedFileSize never be changed!
h.timeStamp = File::getTimeStamp(fileName);
h.root = tt.getRoot();
h.blockSize = tt.getBlockSize();
setCheckSum(h);
{
File stream(fileName + ":" + streamName, File::WRITE, File::CREATE | File::TRUNCATE);
stream.write(&h, sizeof(TTHStreamHeader));
stream.write(tt.getLeaves()[0].data, tt.getLeaves().size() * TTHValue::BYTES);
}
File::setTimeStamp(fileName, h.timeStamp);
} catch (const Exception&) {
return false;
}
return true;
}
// [...]