linuxdcpp-team team mailing list archive
-
linuxdcpp-team team
-
Mailing list archive
-
Message #06348
[Branch ~dcplusplus-team/dcplusplus/trunk] Rev 3151: Share file name duplicates due to directory merges (not tested)
------------------------------------------------------------
revno: 3151
committer: poy <poy@xxxxxxxxxx>
branch nick: trunk
timestamp: Wed 2012-12-12 23:44:47 +0100
message:
Share file name duplicates due to directory merges (not tested)
modified:
changelog.txt
dcpp/ShareManager.cpp
dcpp/ShareManager.h
--
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-11-08 19:11:36 +0000
+++ changelog.txt 2012-12-12 22:44:47 +0000
@@ -9,6 +9,7 @@
* Update Boost to version 1.52
* 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)
-- 0.802 2012-10-20 --
* Perf improvements using lock-free queues, requires P6 CPUs (poy)
=== modified file 'dcpp/ShareManager.cpp'
--- dcpp/ShareManager.cpp 2012-06-21 18:52:47 +0000
+++ dcpp/ShareManager.cpp 2012-12-12 22:44:47 +0000
@@ -486,17 +486,17 @@
Lock l(cs);
shares.emplace(realPath, vName);
- updateIndices(*merge(dp));
+ updateIndices(*merge(dp, realPath));
setDirty();
}
}
-ShareManager::Directory::Ptr ShareManager::merge(const Directory::Ptr& directory) {
+ShareManager::Directory::Ptr ShareManager::merge(const Directory::Ptr& directory, const string& realPath) {
for(auto& i: directories) {
if(Util::stricmp(i->getName(), directory->getName()) == 0) {
- dcdebug("Merging directory %s\n", directory->getName().c_str());
- i->merge(directory);
+ dcdebug("Merging directory <%s> into %s\n", realPath.c_str(), directory->getName().c_str());
+ i->merge(directory, realPath);
return i;
}
}
@@ -507,38 +507,63 @@
return directory;
}
-void ShareManager::Directory::merge(const Directory::Ptr& source) {
+void ShareManager::Directory::merge(const Directory::Ptr& source, const string& realPath) {
+ // merge directories
for(auto& i: source->directories) {
auto subSource = i.second;
auto ti = directories.find(subSource->getName());
if(ti == directories.end()) {
- if(findFile(subSource->getName()) != files.end()) {
- dcdebug("File named the same as directory");
- } else {
- directories.emplace(subSource->getName(), subSource);
- subSource->parent = this;
+ // the directory doesn't exist; create it.
+ directories.emplace(subSource->getName(), subSource);
+ subSource->parent = this;
+
+ auto f = findFile(subSource->getName());
+ if(f != files.end()) {
+ // we have a file that has the same name as the dir being merged; rename it.
+ const_cast<File&>(*f).validateName(Util::getFilePath(f->getRealPath()));
}
+
} else {
+ // the directory was already existing; merge into it.
auto subTarget = ti->second;
- subTarget->merge(subSource);
+ subTarget->merge(subSource, realPath + subSource->getName() + PATH_SEPARATOR);
}
}
- // All subdirs either deleted or moved to target...
- source->directories.clear();
-
+ // merge files
for(auto& i: source->files) {
- if(findFile(i.getName()) == files.end()) {
- if(directories.find(i.getName()) != directories.end()) {
- dcdebug("Directory named the same as file");
- } else {
- auto added = files.insert(i);
- if(added.second) {
- const_cast<File&>(*added.first).setParent(this);
- }
- }
+ auto& file = const_cast<File&>(i);
+
+ file.setParent(this);
+ file.validateName(realPath);
+
+ files.insert(move(file));
+ }
+}
+
+bool ShareManager::Directory::nameInUse(const string& name) const {
+ return findFile(name) != files.end() || directories.find(name) != directories.end();
+}
+
+void ShareManager::Directory::File::validateName(const string& sourcePath) {
+ if(parent->nameInUse(name)) {
+ uint32_t num = 0;
+ string base = name, ext, vname;
+ auto dot = base.rfind('.');
+ if(dot != string::npos) {
+ ext = base.substr(dot);
+ base.erase(dot);
}
+ do {
+ ++num;
+ vname = base + " (" + Util::toString(num) + ")" + ext;
+ } while(parent->nameInUse(vname));
+ dcdebug("Renaming duplicate <%s> to <%s>\n", name.c_str(), vname.c_str());
+ realPath = sourcePath + name;
+ name = move(vname);
+ } else {
+ realPath.reset();
}
}
@@ -573,7 +598,7 @@
if(Util::stricmp(i->second, vName) == 0 && checkHidden(i->first)) {
Directory::Ptr dp = buildTree(i->first, 0);
dp->setName(i->second);
- merge(dp);
+ merge(dp, i->first);
}
}
@@ -721,7 +746,7 @@
if(!SETTING(LIST_DUPES)) {
try {
LogManager::getInstance()->message(str(F_("Duplicate file will not be shared: %1% (Size: %2% B) Dupe matched against: %3%")
- % Util::addBrackets(dir.getRealPath(f.getName())) % Util::toString(f.getSize()) % Util::addBrackets(j->second->getParent()->getRealPath(j->second->getName()))));
+ % Util::addBrackets(f.getRealPath()) % Util::toString(f.getSize()) % Util::addBrackets(j->second->getRealPath())));
dir.files.erase(i);
} catch (const ShareException&) {
}
@@ -784,12 +809,12 @@
lastFullUpdate = GET_TICK();
- DirList newDirs;
+ vector<pair<Directory::Ptr, string>> newDirs;
for(auto& i: dirs) {
if (checkHidden(i.second)) {
Directory::Ptr dp = buildTree(i.second, Directory::Ptr());
dp->setName(i.first);
- newDirs.push_back(dp);
+ newDirs.emplace_back(dp, i.second);
}
}
@@ -798,7 +823,7 @@
directories.clear();
for(auto& i: newDirs) {
- merge(i);
+ merge(i.first, i.second);
}
rebuildIndices();
=== modified file 'dcpp/ShareManager.h'
--- dcpp/ShareManager.h 2012-05-27 14:02:55 +0000
+++ dcpp/ShareManager.h 2012-12-12 22:44:47 +0000
@@ -24,6 +24,8 @@
#include <set>
#include <unordered_map>
+#include <boost/optional.hpp>
+
#include "TimerManager.h"
#include "SearchManager.h"
#include "SettingsManager.h"
@@ -143,28 +145,28 @@
File() : size(0), parent(0) { }
File(const string& aName, int64_t aSize, const Directory::Ptr& aParent, const TTHValue& aRoot) :
name(aName), tth(aRoot), size(aSize), parent(aParent.get()) { }
- File(const File& rhs) :
- name(rhs.getName()), tth(rhs.getTTH()), size(rhs.getSize()), parent(rhs.getParent()) { }
-
- ~File() { }
-
- File& operator=(const File& rhs) {
- name = rhs.name; size = rhs.size; parent = rhs.parent; tth = rhs.tth;
- return *this;
- }
bool operator==(const File& rhs) const {
return getParent() == rhs.getParent() && (Util::stricmp(getName(), rhs.getName()) == 0);
}
+ /** Ensure this file's name doesn't clash with the names of the parent directory's sub-
+ directories or files; rename to "file (N).ext" otherwise (and set realPath to the
+ actual path on disk).
+ @param sourcePath Real path (on the disk) of the directory this file came from. */
+ void validateName(const string& sourcePath);
+
string getADCPath() const { return parent->getADCPath() + name; }
string getFullName() const { return parent->getFullName() + name; }
- string getRealPath() const { return parent->getRealPath(name); }
+ string getRealPath() const { return realPath ? realPath.get() : parent->getRealPath(name); }
GETSET(string, name, Name);
GETSET(TTHValue, tth, TTH);
GETSET(int64_t, size, Size);
GETSET(Directory*, parent, Parent);
+
+ private:
+ boost::optional<string> realPath;
};
int64_t size;
@@ -182,6 +184,10 @@
string getFullName() const noexcept;
string getRealPath(const std::string& path) const;
+ /** Check whether the given name would clash with this directory's sub-directories or
+ files. */
+ bool nameInUse(const string& name) const;
+
int64_t getSize() const noexcept;
void search(SearchResultList& aResults, StringSearch::List& aStrings, int aSearchType, int64_t aSize, int aFileType, Client* aClient, StringList::size_type maxResults) const noexcept;
@@ -193,7 +199,7 @@
File::Set::const_iterator findFile(const string& aFile) const { return find_if(files.begin(), files.end(), Directory::File::StringComp(aFile)); }
- void merge(const Ptr& source);
+ void merge(const Ptr& source, const string& realPath);
GETSET(string, name, Name);
GETSET(Directory*, parent, Parent);
@@ -262,8 +268,9 @@
typedef std::list<Directory::Ptr> DirList;
DirList directories;
- /** Map real name to virtual name - multiple real names may be mapped to a single virtual one */
- StringMap shares;
+ /** Map real name to virtual name - multiple real names may be mapped to a single virtual one.
+ The map is sorted to make sure conflicts are always resolved in the same order when merging. */
+ map<string, string> shares;
typedef unordered_map<TTHValue, Directory::File::Set::const_iterator> HashFileMap;
typedef HashFileMap::iterator HashFileIter;
@@ -282,7 +289,7 @@
void updateIndices(Directory& aDirectory);
void updateIndices(Directory& dir, const Directory::File::Set::iterator& i);
- Directory::Ptr merge(const Directory::Ptr& directory);
+ Directory::Ptr merge(const Directory::Ptr& directory, const string& realPath);
void generateXmlList();
bool loadCache() noexcept;