← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 3158: avoid re-hashing when upgrading the hash registry to v3

 

------------------------------------------------------------
revno: 3158
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Mon 2012-12-17 19:49:40 +0100
message:
  avoid re-hashing when upgrading the hash registry to v3
modified:
  changelog.txt
  dcpp/HashManager.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-14 17:07:25 +0000
+++ changelog.txt	2012-12-17 18:49:40 +0000
@@ -1,7 +1,3 @@
-*** 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)
@@ -16,6 +12,8 @@
 * [L#311818] Share file name duplicates due to directory merges (poy)
 * [L#311818] Share file name duplicates due to case differences (poy)
 
+Note for XP users: shared files will have to be re-hashed.
+
 -- 0.802 2012-10-20 --
 * Perf improvements using lock-free queues, requires P6 CPUs (poy)
 * Reduce freezes when displaying file list dirs that contain lots of files (poy)

=== modified file 'dcpp/HashManager.cpp'
--- dcpp/HashManager.cpp	2012-12-14 17:07:25 +0000
+++ dcpp/HashManager.cpp	2012-12-17 18:49:40 +0000
@@ -331,6 +331,7 @@
 	HashManager::HashStore& store;
 
 	int version;
+	string file;
 
 	bool inTrees;
 	bool inFiles;
@@ -349,6 +350,93 @@
 	}
 }
 
+namespace {
+/* version 2 files were stored in lower-case; carry the file registration over only if the file can
+be found, and if it has no case-insensitive duplicate. */
+
+#ifdef _WIN32
+
+/* we are going to use GetFinalPathNameByHandle to retrieve a properly cased path out of the
+lower-case one that the version 2 file registry has provided us with. that API is only available
+on Windows >= Vista. */
+typedef DWORD (WINAPI *t_GetFinalPathNameByHandle)(HANDLE, LPTSTR, DWORD, DWORD);
+t_GetFinalPathNameByHandle initGFPNBH() {
+	static bool init = false;
+	static t_GetFinalPathNameByHandle GetFinalPathNameByHandle = nullptr;
+
+	if(!init) {
+		init = true;
+
+		auto lib = ::LoadLibrary(_T("kernel32.dll"));
+		if(lib) {
+			GetFinalPathNameByHandle = reinterpret_cast<t_GetFinalPathNameByHandle>(
+				::GetProcAddress(lib, "GetFinalPathNameByHandleW"));
+		}
+	}
+
+	return GetFinalPathNameByHandle;
+}
+
+bool upgradeFromV2(string& file) {
+	auto GetFinalPathNameByHandle = initGFPNBH();
+	if(!GetFinalPathNameByHandle) {
+		return false;
+	}
+
+	WIN32_FIND_DATA data;
+	// FindFirstFile does a case-insensitive search by default
+	auto handle = ::FindFirstFile(Text::toT(file).c_str(), &data);
+	if(handle == INVALID_HANDLE_VALUE) {
+		// file not found
+		return false;
+	}
+	if(::FindNextFile(handle, &data)) {
+		// found a dupe
+		::FindClose(handle);
+		return false;
+	}
+	::FindClose(handle);
+
+	// don't use dcpp::File as that would be case-sensitive
+	handle = ::CreateFile((Text::toT(Util::getFilePath(file)) + data.cFileName).c_str(),
+		GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
+	if(handle == INVALID_HANDLE_VALUE) {
+		return false;
+	}
+
+	wstring buf(file.size() * 2, 0);
+	buf.resize(GetFinalPathNameByHandle(handle, &buf[0], buf.size(), 0));
+
+	::CloseHandle(handle);
+
+	if(buf.empty()) {
+		return false;
+	}
+	// GetFinalPathNameByHandle prepends "\\?\"; remove it.
+	if(buf.size() >= 4 && buf.substr(0, 4) == L"\\\\?\\") {
+		buf.erase(0, 4);
+	}
+
+	auto buf8 = Text::fromT(buf);
+	if(Text::toLower(buf8) == file) {
+		file = move(buf8);
+		return true;
+	}
+
+	return false;
+}
+
+#else
+
+bool upgradeFromV2(string& file) {
+	/// @todo implement this on Linux; by default, force re-hashing.
+	return false;
+}
+
+#endif
+
+}
+
 static const string sHashStore = "HashStore";
 static const string sversion = "version"; // Oops, v1 was like this
 static const string sVersion = "Version";
@@ -373,7 +461,6 @@
 		}
 		inHashStore = !simple;
 	} 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));
@@ -383,12 +470,11 @@
 			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 && version != 2 && name == sFile) {
-			const auto& file = getAttrib(attribs, sName, 0);
+		} else if (inFiles && name == sFile) {
+			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()) {
+			if(!file.empty() && timeStamp > 0 && !root.empty() && (version != 2 || upgradeFromV2(file))) {
 				auto fname = Util::getFileName(file), fpath = Util::getFilePath(file);
 				store.fileIndex[fpath].emplace_back(fname, TTHValue(root), timeStamp, false);
 			}