diff --git a/model/vfs/couchdb_indexer.go b/model/vfs/couchdb_indexer.go index ef60c48417f..f0d3bc6e40d 100644 --- a/model/vfs/couchdb_indexer.go +++ b/model/vfs/couchdb_indexer.go @@ -149,10 +149,10 @@ func (c *couchdbIndexer) prepareFileDoc(doc *FileDoc) error { func (c *couchdbIndexer) DirSize(doc *DirDoc) (int64, error) { start := doc.Fullpath + "/" - stop := doc.Fullpath + "0" // 0 is the next ascii character after / + stop := doc.Fullpath + "/\ufff0" if doc.DocID == consts.RootDirID { start = "/" - stop = "0" + stop = "/\ufff0" } // Find the subdirectories @@ -362,7 +362,7 @@ func (c *couchdbIndexer) MoveDir(oldpath, newpath string) error { // We limit the stack to 128 bulk updates to avoid infinite loops, as we // had a case in the past. start := oldpath + "/" - stop := oldpath + "0" // 0 is the next ascii character after / + stop := oldpath + "/\ufff0" for i := 0; i < 128; i++ { // The simple selector mango.StartWith can have some issues when // renaming a folder to the same name, but with a different unicode diff --git a/tests/system/lib/folder.rb b/tests/system/lib/folder.rb index 6ce23fc245a..265111bfab1 100644 --- a/tests/system/lib/folder.rb +++ b/tests/system/lib/folder.rb @@ -111,7 +111,8 @@ def save(inst) accept: "application/vnd.api+json", authorization: "Bearer #{inst.token_for doctype}" } - res = inst.client["/files/#{dir_id}?Type=directory&Name=#{name}"].post nil, opts + encoded_name = URI.encode_www_form_component(name) + res = inst.client["/files/#{dir_id}?Type=directory&Name=#{encoded_name}"].post nil, opts j = JSON.parse(res.body)["data"] @couch_id = j["id"] @couch_rev = j["rev"] diff --git a/tests/system/tests/filepath_operations.rb b/tests/system/tests/filepath_operations.rb new file mode 100644 index 00000000000..5947986dea1 --- /dev/null +++ b/tests/system/tests/filepath_operations.rb @@ -0,0 +1,68 @@ +require_relative '../boot' +require 'minitest/autorun' +require 'pry-rescue/minitest' unless ENV['CI'] + +describe "The VFS" do + it "is able to deal with special cases on filepaths (encoding, case sensitivity)" do + Helpers.scenario "filepath_operations" + Helpers.start_mailhog + + # Create the instance + inst = Instance.create name: "Alice" + + # Create a folder + dirname = "this" + folder = Folder.create inst, name: dirname + folder.couch_id.wont_be_empty + sub = Folder.create inst, dir_id: folder.couch_id + 3.times do + CozyFile.create inst, dir_id: sub.couch_id + CozyFile.create inst, dir_id: folder.couch_id + end + + # Create a folder with the same name, but not the same case + other = Folder.create inst, name: dirname.upcase + other.couch_id.wont_be_empty + 4.times do + CozyFile.create inst, dir_id: other.couch_id + end + other.remove inst + assert_equal inst.fsck, [] + + # Trying stupids tricks + before = File.join Helpers.current_dir, inst.domain + # Renaming to the same name + folder.rename inst, "that" rescue nil + # Moving inside its-self + folder.move_to inst, folder.couch_id rescue nil + # Moving inside a sub-directory + folder.move_to inst, sub.couch_id rescue nil + after = File.join Helpers.current_dir, inst.domain + diff = Helpers.fsdiff before, after + diff.must_be_empty + assert_equal inst.fsck, [] + + # Play with NFC and NFD unicode normalization + ["Pièces pour ampèremètre", "Diplômes", "Reçus"].each do |name| + nfc = name.unicode_normalize(:nfc) + nfd = name.unicode_normalize(:nfd) + folder.rename inst, nfc + assert_equal inst.fsck, [] + folder.rename inst, nfd + assert_equal inst.fsck, [] + folder.rename inst, nfc + assert_equal inst.fsck, [] + end + + folder.remove inst + Folder.clear_trash inst + + # Emojis can be tricky too + folder = Folder.create inst, name: "EDF" + Folder.create inst, name: "EDF⚡️" + folder.remove inst + assert_equal inst.fsck, [] + + inst.remove + end +end