| From 8272f98ee26144fc8108ca35bc49b7e25868535f Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?Fran=C3=A7ois=20Degros?= <fdegros@chromium.org> |
| Date: Thu, 17 Sep 2020 18:06:27 +1000 |
| Subject: [PATCH] Deduplicate file names |
| |
| --- |
| lib/fileNode.cpp | 27 ++++++-------------------- |
| lib/fileNode.h | 9 ++++----- |
| lib/fuse-zip.cpp | 4 ++-- |
| lib/fuseZipData.cpp | 47 ++++++++++++++++++++++++++++----------------- |
| lib/fuseZipData.h | 6 +++++- |
| 5 files changed, 46 insertions(+), 47 deletions(-) |
| |
| diff --git a/lib/fileNode.cpp b/lib/fileNode.cpp |
| index 62723d6..8a5b16a 100644 |
| --- a/lib/fileNode.cpp |
| +++ b/lib/fileNode.cpp |
| @@ -89,7 +89,6 @@ FileNode *FileNode::createDir(struct zip *zip, const char *fname, |
| FileNode *FileNode::createRootNode(struct zip *zip) { |
| auto data = DataNode::createNew(S_IFDIR | 0755, 0, 0, 0); |
| FileNode *n = new FileNode(zip, "", ROOT_NODE_INDEX, std::move(data)); |
| - n->name = n->full_name.c_str(); |
| |
| int len = 0; |
| n->m_comment = zip_get_archive_comment(zip, &len, ZIP_FL_ENC_RAW); |
| @@ -137,26 +136,12 @@ FileNode::~FileNode() { |
| void FileNode::parse_name() { |
| assert(!full_name.empty()); |
| |
| - const char *lsl = full_name.c_str(); |
| - while (*lsl++) {} |
| - lsl--; |
| - while (lsl > full_name.c_str() && *lsl != '/') { |
| - lsl--; |
| - } |
| // If the last symbol in file name is '/' then it is a directory |
| - if (*lsl == '/' && *(lsl+1) == '\0') { |
| - // It will produce two \0s at the end of file name. I think that |
| - // it is not a problem |
| - full_name[full_name.size() - 1] = 0; |
| - while (lsl > full_name.c_str() && *lsl != '/') { |
| - lsl--; |
| - } |
| - } |
| - // Setting short name of node |
| - if (*lsl == '/') { |
| - lsl++; |
| - } |
| - this->name = lsl; |
| + if (full_name.back() == '/') |
| + full_name.pop_back(); |
| + |
| + name_start = full_name.find_last_of('/') + 1; |
| + assert(name_start <= full_name.size()); |
| } |
| |
| void FileNode::readComment() { |
| @@ -372,7 +357,7 @@ int FileNode::updateExternalAttributes() const { |
| // FILE_ATTRIBUTE_DIRECTORY |
| mode |= 0x10; |
| } |
| - if (name[0] == '.') { |
| + if (full_name[name_start] == '.') { |
| // FILE_ATTRIBUTE_HIDDEN |
| mode |= 2; |
| } |
| diff --git a/lib/fileNode.h b/lib/fileNode.h |
| index ab07cd9..e20cd0d 100644 |
| --- a/lib/fileNode.h |
| +++ b/lib/fileNode.h |
| @@ -191,10 +191,9 @@ public: |
| /** |
| * Get parent name |
| */ |
| - //TODO: rewrite without memory allocation |
| - inline std::string getParentName () const { |
| - if (name > full_name.c_str()) { |
| - return std::string (full_name, 0, static_cast<size_t>(name - full_name.c_str() - 1)); |
| + std::string getParentName() const { |
| + if (name_start > 0) { |
| + return std::string(full_name, 0, name_start - 1); |
| } else { |
| return ""; |
| } |
| @@ -230,7 +229,7 @@ public: |
| const char *getComment() const { return m_comment; } |
| uint16_t getCommentLength() const { return m_commentLen; } |
| |
| - const char *name; |
| + std::string::size_type name_start = 0; |
| std::string full_name; |
| nodelist_t childs; |
| FileNode *parent; |
| diff --git a/lib/fuse-zip.cpp b/lib/fuse-zip.cpp |
| index 7c33999..5310925 100644 |
| --- a/lib/fuse-zip.cpp |
| +++ b/lib/fuse-zip.cpp |
| @@ -167,8 +167,8 @@ int fusezip_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t o |
| } |
| filler(buf, ".", NULL, 0); |
| filler(buf, "..", NULL, 0); |
| - for (nodelist_t::const_iterator i = node->childs.begin(); i != node->childs.end(); ++i) { |
| - filler(buf, (*i)->name, NULL, 0); |
| + for (const FileNode *p : node->childs) { |
| + filler(buf, p->full_name.c_str() + p->name_start, NULL, 0); |
| } |
| |
| return 0; |
| diff --git a/lib/fuseZipData.cpp b/lib/fuseZipData.cpp |
| index 6d5d498..c511e1d 100644 |
| --- a/lib/fuseZipData.cpp |
| +++ b/lib/fuseZipData.cpp |
| @@ -298,22 +298,40 @@ mode_t FuseZipData::getEntryAttributes(zip_uint64_t id, const char *name, bool & |
| return mode; |
| } |
| |
| +void FuseZipData::renameAndAttachNode(FileNode *node) { |
| + std::string &f = node->full_name; |
| + if (files.insert({f.c_str(), node}).second) |
| + return; |
| + |
| + // Duplicate file name |
| + const std::string s = std::move(f); |
| + |
| + // Find extension start |
| + std::string::size_type e = s.find_last_of('.'); |
| + if (e <= node->name_start || e >= s.size() - 1) |
| + e = s.size(); |
| + |
| + // Add a number before the extension |
| + int i = 0; |
| + do { |
| + f.clear(); |
| + f.append(s, 0, e); |
| + f.append(" ("); |
| + f.append(std::to_string(++i)); |
| + f.append(")"); |
| + f.append(s, e); |
| + } while (!files.insert({f.c_str(), node}).second); |
| +} |
| + |
| void FuseZipData::attachNode(zip_int64_t id, const char *name, mode_t mode, bool readonly, |
| bool needPrefix, filemap_t &origNames) |
| { |
| std::string converted; |
| convertFileName(name, readonly, needPrefix, converted); |
| const char *cname = converted.c_str(); |
| - if (files.find(cname) != files.end()) { |
| - throw ZipError(std::string("Duplicated file name: ") + cname, |
| - ZIP_ER_EXISTS); |
| - } |
| FileNode *node = FileNode::createNodeForZipEntry(m_zip, cname, id, mode); |
| - if (node == NULL) { |
| - throw std::bad_alloc(); |
| - } |
| - files[node->full_name.c_str()] = node; |
| - origNames[name] = node; |
| + renameAndAttachNode(node); |
| + origNames.insert({name, node}); |
| } |
| |
| bool FuseZipData::attachHardlink(zip_int64_t sid, const char *name, mode_t mode, bool readonly, |
| @@ -378,16 +396,9 @@ bool FuseZipData::attachHardlink(zip_int64_t sid, const char *name, mode_t mode, |
| std::string converted; |
| convertFileName(name, readonly, needPrefix, converted); |
| const char *cname = converted.c_str(); |
| - if (files.find(cname) != files.end()) { |
| - throw ZipError(std::string("Duplicated file name: ") + cname, |
| - ZIP_ER_EXISTS); |
| - } |
| FileNode *node = FileNode::createHardlink(m_zip, cname, sid, it->second); |
| - if (node == NULL) { |
| - throw std::bad_alloc(); |
| - } |
| - files[node->full_name.c_str()] = node; |
| - origNames[name] = node; |
| + renameAndAttachNode(node); |
| + origNames.insert({name, node}); |
| |
| return true; |
| } |
| diff --git a/lib/fuseZipData.h b/lib/fuseZipData.h |
| index e25c060..92d4191 100644 |
| --- a/lib/fuseZipData.h |
| +++ b/lib/fuseZipData.h |
| @@ -66,6 +66,11 @@ private: |
| */ |
| mode_t getEntryAttributes(zip_uint64_t id, const char *name, bool &isHardlink); |
| |
| + /** |
| + * Attach a node, renaming it if necessary to prevent name collisions. |
| + */ |
| + void renameAndAttachNode(FileNode *node); |
| + |
| /** |
| * create and attach file node |
| */ |
| @@ -143,4 +148,3 @@ public: |
| }; |
| |
| #endif |
| - |
| -- |
| 2.29.1.341.ge80a0c044ae-goog |
| |