linuxdcpp-team team mailing list archive
-
linuxdcpp-team team
-
Mailing list archive
-
Message #06353
[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 3156: Share file name duplicates due to case differences
------------------------------------------------------------
revno: 3156
committer: poy <poy@xxxxxxxxxx>
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));
}
}
}