------------------------------------------------------------ revno: 3156 committer: poy <p...@123gen.com> branch nick: trunk timestamp: Fri 2012-12-14 18:07:25 +0100 message: Share file name duplicates due to case differences modified: changelog.txt dcpp/File.cpp dcpp/FileReader.cpp dcpp/HashManager.cpp dcpp/HashManager.h dcpp/QueueManager.cpp dcpp/ShareManager.cpp
-- lp:dcplusplus https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk Your team Dcplusplus-team is subscribed to branch lp:dcplusplus. To unsubscribe from this branch go to https://code.launchpad.net/~dcplusplus-team/dcplusplus/trunk/+edit-subscription
=== modified file 'changelog.txt' --- changelog.txt 2012-12-12 22:44:47 +0000 +++ changelog.txt 2012-12-14 17:07:25 +0000 @@ -1,3 +1,7 @@ +*** WARNING *** + In order to allow case-sensitive sharing, your files will have to be + re-hashed. Hashes stored by queued downloads won't be lost. +*** WARNING *** * Fix a race condition on file list download (thanks bigmuscle) * [L#668548] Fix a potential infinite loop in BufferedSocket->setDataMode (crise) * Add "chunked" transfer encoding as per the HTTP/1.1 spec (crise) @@ -10,6 +14,7 @@ * Restore "Requesting" messages in the transfer list * [L#1071363] Apply link & plugin formatting to status messages (crise, poy) * [L#311818] Share file name duplicates due to directory merges (poy) +* [L#311818] Share file name duplicates due to case differences (poy) -- 0.802 2012-10-20 -- * Perf improvements using lock-free queues, requires P6 CPUs (poy) === modified file 'dcpp/File.cpp' --- dcpp/File.cpp 2012-09-13 21:15:43 +0000 +++ dcpp/File.cpp 2012-12-14 17:07:25 +0000 @@ -41,7 +41,8 @@ } DWORD shared = FILE_SHARE_READ | (mode & SHARED ? FILE_SHARE_WRITE : 0); - h = ::CreateFile(Text::toT(aFileName).c_str(), access, shared, NULL, m, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + h = ::CreateFile(Text::toT(aFileName).c_str(), access, shared, nullptr, m, + FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN, nullptr); if(h == INVALID_HANDLE_VALUE) { throw FileException(Util::translateError(GetLastError())); @@ -164,17 +165,8 @@ } int64_t File::getSize(const string& aFileName) noexcept { - WIN32_FIND_DATA fd; - HANDLE hFind; - - hFind = FindFirstFile(Text::toT(aFileName).c_str(), &fd); - - if (hFind == INVALID_HANDLE_VALUE) { - return -1; - } else { - FindClose(hFind); - return ((int64_t)fd.nFileSizeHigh << 32 | (int64_t)fd.nFileSizeLow); - } + auto i = FileFindIter(aFileName); + return i != FileFindIter() ? i->getSize() : -1; } void File::ensureDirectory(const string& aFile) noexcept { @@ -422,19 +414,14 @@ StringList ret; #ifdef _WIN32 - WIN32_FIND_DATA data; - HANDLE hFind; - - hFind = ::FindFirstFile(Text::toT(path + pattern).c_str(), &data); - if(hFind != INVALID_HANDLE_VALUE) { - do { - const char* extra = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? "\\" : ""; - ret.push_back(path + Text::fromT(data.cFileName) + extra); - } while(::FindNextFile(hFind, &data)); - - ::FindClose(hFind); + for(FileFindIter i(path + pattern), end; i != end; ++i) { + ret.push_back(path + i->getFileName()); + if(i->isDirectory()) { + ret.back() += PATH_SEPARATOR; + } } #else + /// @todo can FileFindIter be used on linux? DIR* dir = opendir(Text::fromUtf8(path).c_str()); if (dir) { while (struct dirent* ent = readdir(dir)) { @@ -457,7 +444,8 @@ FileFindIter::FileFindIter() : handle(INVALID_HANDLE_VALUE) { } FileFindIter::FileFindIter(const string& path) : handle(INVALID_HANDLE_VALUE) { - handle = ::FindFirstFile(Text::toT(path).c_str(), &data); + handle = ::FindFirstFileEx(Text::toT(path).c_str(), FindExInfoStandard, &data, + FindExSearchNameMatch, nullptr, FIND_FIRST_EX_CASE_SENSITIVE); } FileFindIter::~FileFindIter() { === modified file 'dcpp/FileReader.cpp' --- dcpp/FileReader.cpp 2012-03-21 17:39:02 +0000 +++ dcpp/FileReader.cpp 2012-12-14 17:07:25 +0000 @@ -110,8 +110,8 @@ return READ_FAILED; } - auto tmp = ::CreateFile(tfile.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); + auto tmp = ::CreateFile(tfile.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED | FILE_FLAG_POSIX_SEMANTICS, nullptr); if (tmp == INVALID_HANDLE_VALUE) { dcdebug("Failed to open unbuffered file: %s\n", Util::translateError(::GetLastError()).c_str()); @@ -210,8 +210,8 @@ auto tfile = Text::toT(file); - auto tmp = ::CreateFile(tfile.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_FLAG_SEQUENTIAL_SCAN, NULL); + auto tmp = ::CreateFile(tfile.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, + FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN, nullptr); if (tmp == INVALID_HANDLE_VALUE) { dcdebug("Failed to open unbuffered file: %s\n", Util::translateError(::GetLastError()).c_str()); === modified file 'dcpp/HashManager.cpp' --- dcpp/HashManager.cpp 2012-12-13 17:04:31 +0000 +++ dcpp/HashManager.cpp 2012-12-14 17:07:25 +0000 @@ -32,8 +32,12 @@ using std::swap; -#define HASH_FILE_VERSION_STRING "2" -static const uint32_t HASH_FILE_VERSION = 2; +/* Version history: +- Version 1: DC++ 0.307 to 0.68. +- Version 2: DC++ 0.670 to DC++ 0.802. Improved efficiency. +- Version 3: from DC++ 0.810 on. Changed the file registry to be case-sensitive. */ +#define HASH_FILE_VERSION_STRING "3" +static const uint32_t HASH_FILE_VERSION = 3; const int64_t HashManager::MIN_BLOCK_SIZE = 64 * 1024; optional<TTHValue> HashManager::getTTH(const string& aFileName, int64_t aSize, uint32_t aTimeStamp) noexcept { @@ -50,7 +54,7 @@ return store.getTree(root, tt); } -size_t HashManager::getBlockSize(const TTHValue& root) { +int64_t HashManager::getBlockSize(const TTHValue& root) { Lock l(cs); return store.getBlockSize(root); } @@ -80,10 +84,9 @@ void HashManager::HashStore::addFile(const string& aFileName, uint32_t aTimeStamp, const TigerTree& tth, bool aUsed) { addTree(tth); - string fname = Text::toLower(Util::getFileName(aFileName)); - string fpath = Text::toLower(Util::getFilePath(aFileName)); + auto fname = Util::getFileName(aFileName), fpath = Util::getFilePath(aFileName); - FileInfoList& fileList = fileIndex[fpath]; + auto& fileList = fileIndex[fpath]; auto j = find(fileList.begin(), fileList.end(), fname); if (j != fileList.end()) { @@ -163,14 +166,13 @@ } } -size_t HashManager::HashStore::getBlockSize(const TTHValue& root) const { +int64_t HashManager::HashStore::getBlockSize(const TTHValue& root) const { auto i = treeIndex.find(root); return i == treeIndex.end() ? 0 : i->second.getBlockSize(); } optional<TTHValue> HashManager::HashStore::getTTH(const string& aFileName, int64_t aSize, uint32_t aTimeStamp) noexcept { - auto fname = Text::toLower(Util::getFileName(aFileName)); - auto fpath = Text::toLower(Util::getFilePath(aFileName)); + auto fname = Util::getFileName(aFileName), fpath = Util::getFilePath(aFileName); auto i = fileIndex.find(fpath); if (i != fileIndex.end()) { @@ -194,8 +196,8 @@ void HashManager::HashStore::rebuild() { try { - DirMap newFileIndex; - TreeMap newTreeIndex; + decltype(fileIndex) newFileIndex; + decltype(treeIndex) newTreeIndex; for (auto& i: fileIndex) { for (auto& j: i.second) { @@ -230,16 +232,22 @@ } for (auto& i: fileIndex) { - auto fi = newFileIndex.emplace(i.first, FileInfoList()).first; +#ifndef _MSC_VER + decltype(fileIndex)::mapped_type newFileList; +#else + /// @todo remove this workaround when VS has a proper decltype... + decltype(fileIndex.begin()->second) newFileList; +#endif for (auto& j: i.second) { if (newTreeIndex.find(j.getRoot()) != newTreeIndex.end()) { - fi->second.push_back(j); + newFileList.push_back(j); } } - if (fi->second.empty()) - newFileIndex.erase(fi); + if(!newFileList.empty()) { + newFileIndex[i.first] = move(newFileList); + } } File::deleteFile(origName); @@ -315,17 +323,13 @@ class HashLoader: public SimpleXMLReader::CallBack { public: HashLoader(HashManager::HashStore& s) : - store(s), size(0), timeStamp(0), version(HASH_FILE_VERSION), inTrees(false), inFiles(false), inHashStore(false) { + store(s), version(HASH_FILE_VERSION), inTrees(false), inFiles(false), inHashStore(false) { } void startTag(const string& name, StringPairList& attribs, bool simple); - void endTag(const string& name); private: HashManager::HashStore& store; - string file; - int64_t size; - uint32_t timeStamp; int version; bool inTrees; @@ -368,7 +372,8 @@ version = Util::toInt(getAttrib(attribs, sversion, 0)); } inHashStore = !simple; - } else if (inHashStore && version == 2) { + } else if (inHashStore && (version == 2 || version == 3)) { + // when upgrading from version 2 to 3, import trees but not the file registry. if (inTrees && name == sHash) { const string& type = getAttrib(attribs, sType, 0); int64_t index = Util::toInt64(getAttrib(attribs, sIndex, 1)); @@ -378,16 +383,14 @@ if (!root.empty() && type == sTTH && (index >= 8 || index == HashManager::SMALL_TREE) && blockSize >= 1024) { store.treeIndex[TTHValue(root)] = HashManager::HashStore::TreeInfo(size, index, blockSize); } - } else if (inFiles && name == sFile) { - file = getAttrib(attribs, sName, 0); - timeStamp = Util::toUInt32(getAttrib(attribs, sTimeStamp, 1)); - const string& root = getAttrib(attribs, sRoot, 2); - - if (!file.empty() && size >= 0 && timeStamp > 0 && !root.empty()) { - string fname = Text::toLower(Util::getFileName(file)); - string fpath = Text::toLower(Util::getFilePath(file)); - - store.fileIndex[fpath].push_back(HashManager::HashStore::FileInfo(fname, TTHValue(root), timeStamp, false)); + } else if (inFiles && version != 2 && name == sFile) { + const auto& file = getAttrib(attribs, sName, 0); + auto timeStamp = Util::toUInt32(getAttrib(attribs, sTimeStamp, 1)); + const auto& root = getAttrib(attribs, sRoot, 2); + + if (!file.empty() && timeStamp > 0 && !root.empty()) { + auto fname = Util::getFileName(file), fpath = Util::getFilePath(file); + store.fileIndex[fpath].emplace_back(fname, TTHValue(root), timeStamp, false); } } else if (name == sTrees) { inTrees = !simple; @@ -397,12 +400,6 @@ } } -void HashLoader::endTag(const string& name) { - if (name == sFile) { - file.clear(); - } -} - HashManager::HashStore::HashStore() : dirty(false) { @@ -469,8 +466,8 @@ void HashManager::Hasher::stopHashing(const string& baseDir) { Lock l(cs); - for (auto i = w.begin(); i != w.end();) { - if (Util::strnicmp(baseDir, i->first, baseDir.length()) == 0) { + for(auto i = w.begin(); i != w.end();) { + if(strncmp(baseDir.c_str(), i->first.c_str(), baseDir.size()) == 0) { w.erase(i++); } else { ++i; === modified file 'dcpp/HashManager.h' --- dcpp/HashManager.h 2012-12-13 17:50:01 +0000 +++ dcpp/HashManager.h 2012-12-14 17:07:25 +0000 @@ -67,7 +67,7 @@ bool getTree(const TTHValue& root, TigerTree& tt); /** Return block size of the tree associated with root, or 0 if no such tree is in the store */ - size_t getBlockSize(const TTHValue& root); + int64_t getBlockSize(const TTHValue& root); void addTree(const string& aFileName, uint32_t aTimeStamp, const TigerTree& tt) { hashDone(aFileName, aTimeStamp, tt, -1, -1); @@ -127,10 +127,7 @@ private: // Case-sensitive (faster), it is rather unlikely that case changes, and if it does it's harmless. // map because it's sorted (to avoid random hash order that would create quite strange shares while hashing) - typedef map<string, int64_t> WorkMap; - typedef WorkMap::iterator WorkIter; - - WorkMap w; + map<string, int64_t> w; mutable CriticalSection cs; Semaphore s; @@ -160,7 +157,7 @@ void addTree(const TigerTree& tt) noexcept; bool getTree(const TTHValue& root, TigerTree& tth); - size_t getBlockSize(const TTHValue& root) const; + int64_t getBlockSize(const TTHValue& root) const; bool isDirty() { return dirty; } private: /** Root -> tree mapping info, we assume there's only one tree for each root (a collision would mean we've broken tiger...) */ @@ -187,19 +184,10 @@ GETSET(bool, used, Used); }; - typedef vector<FileInfo> FileInfoList; - typedef FileInfoList::iterator FileInfoIter; - - typedef unordered_map<string, FileInfoList> DirMap; - typedef DirMap::iterator DirIter; - - typedef unordered_map<TTHValue, TreeInfo> TreeMap; - typedef TreeMap::iterator TreeIter; - friend class HashLoader; - DirMap fileIndex; - TreeMap treeIndex; + unordered_map<string, vector<FileInfo>> fileIndex; + unordered_map<TTHValue, TreeInfo> treeIndex; bool dirty; === modified file 'dcpp/QueueManager.cpp' --- dcpp/QueueManager.cpp 2012-11-06 18:52:30 +0000 +++ dcpp/QueueManager.cpp 2012-12-14 17:07:25 +0000 @@ -208,7 +208,7 @@ continue; } if(!qi->isSet(QueueItem::FLAG_USER_LIST)) { - int64_t blockSize = HashManager::getInstance()->getBlockSize(qi->getTTH()); + auto blockSize = HashManager::getInstance()->getBlockSize(qi->getTTH()); if(blockSize == 0) blockSize = qi->getSize(); if(qi->getNextSegment(blockSize, wantedSize).getSize() == 0) { === modified file 'dcpp/ShareManager.cpp' --- dcpp/ShareManager.cpp 2012-12-13 18:05:50 +0000 +++ dcpp/ShareManager.cpp 2012-12-14 17:07:25 +0000 @@ -702,8 +702,10 @@ // don't share the private key file if(Util::stricmp(fileName, SETTING(TLS_PRIVATE_KEY_FILE)) == 0) { continue; } - lastFileIter = dir->files.insert(lastFileIter, Directory::File(name, size, dir, - HashManager::getInstance()->getTTH(fileName, size, i->getLastWriteTime()))); + Directory::File f(name, size, dir, + HashManager::getInstance()->getTTH(fileName, size, i->getLastWriteTime())); + f.validateName(aName); + lastFileIter = dir->files.insert(lastFileIter, move(f)); } } @@ -1502,8 +1504,10 @@ // Check if the finished download dir is supposed to be shared auto dir = getDirectory(realPath); if(dir) { - dir->files.insert(Directory::File(Util::getFileName(realPath), size, dir, - HashManager::getInstance()->getTTH(realPath, size, 0))); + Directory::File f(Util::getFileName(realPath), size, dir, + HashManager::getInstance()->getTTH(realPath, size, 0)); + f.validateName(Util::getFilePath(realPath)); + dir->files.insert(move(f)); } } }
_______________________________________________ Mailing list: https://launchpad.net/~linuxdcpp-team Post to : linuxdcpp-team@lists.launchpad.net Unsubscribe : https://launchpad.net/~linuxdcpp-team More help : https://help.launchpad.net/ListHelp