blob: 04d8864a0297c981aeeb60b47fe9b9e0088e7619 [file] [log] [blame]
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