diff --git a/vfs/dir.go b/vfs/dir.go index 243f0d321..e9cbbbc99 100644 --- a/vfs/dir.go +++ b/vfs/dir.go @@ -478,6 +478,9 @@ func (d *Dir) AddVirtual(leaf string, size int64, isDir bool) { // since the path came from the cache it may have fs.LinkSuffix, // so remove it and mark the *File accordingly leaf, isLink = strings.CutSuffix(leaf, fs.LinkSuffix) + if leaf == "" || leaf == "." || leaf == ".." { + return + } } f := newFile(d, dPath, nil, leaf) if isLink { @@ -724,12 +727,14 @@ func (d *Dir) _readDirFromEntries(entries fs.DirEntries, dirTree dirtree.DirTree mv := d._newManageVirtuals() for _, entry := range entries { name := path.Base(entry.Remote()) - if name == "." || name == ".." { - continue - } if d.vfs.Opt.Links { name, _ = strings.CutSuffix(name, fs.LinkSuffix) } + // Check after removing the suffix so "...rclonelink" can't + // become an entry called ".." + if name == "" || name == "." || name == ".." { + continue + } node := d.items[name] if mv.add(d, name) { continue diff --git a/vfs/dir_test.go b/vfs/dir_test.go index 264319d7f..0d5f2c8e2 100644 --- a/vfs/dir_test.go +++ b/vfs/dir_test.go @@ -16,6 +16,7 @@ import ( "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/operations" "github.com/rclone/rclone/fstest" + "github.com/rclone/rclone/vfs/vfscommon" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -33,6 +34,41 @@ func dirCreate(t *testing.T) (r *fstest.Run, vfs *VFS, dir *Dir, item fstest.Ite return r, vfs, node.(*Dir), file1 } +// TestDirLinksInvalidName checks that with --links an object called +// "...rclonelink" doesn't become a directory entry called "..", which +// would address the parent, whether it comes from a listing or from +// the cache. +func TestDirLinksInvalidName(t *testing.T) { + opt := vfscommon.Opt + opt.Links = true + r, vfs := newTestVFSOpt(t, &opt) + + file1 := r.WriteObject(context.Background(), "dir/.."+fs.LinkSuffix, "target", t1) + file2 := r.WriteObject(context.Background(), "dir/file1", "file1 contents", t1) + r.CheckRemoteItems(t, file1, file2) + + node, err := vfs.Stat("dir") + require.NoError(t, err) + dir := node.(*Dir) + + checkEntries := func() { + t.Helper() + entries, err := dir.ReadDirAll() + require.NoError(t, err) + var names []string + for _, entry := range entries { + names = append(names, entry.Name()) + } + assert.Equal(t, []string{"file1"}, names) + } + checkEntries() + + for _, leaf := range []string{".." + fs.LinkSuffix, "." + fs.LinkSuffix, fs.LinkSuffix} { + dir.AddVirtual(leaf, 6, false) + } + checkEntries() +} + func TestDirMethods(t *testing.T) { _, vfs, dir, _ := dirCreate(t)