mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-11-09 05:18:23 +01:00
filer: allow renaming into a sibling that shares the name prefix (#12337)
CanRename rejected a move whenever the new parent directory's path started with the source path as a plain string, so moving /dir/a into /dir/a.d (or anything under /dir/ab) failed with "can not move directory to a subdirectory of itself". Check the path boundary instead: reject only when the new parent is the source itself or lies under it. Claude-Session: https://claude.ai/code/session_01VncHKTehVroSAzRMUHykEX Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7298b4ed37
commit
1637941368
2 files changed
+32
-1
No files matched your search
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
func (f *Filer) CanRename(ctx context.Context, source, target util.FullPath, oldName string) error {
|
||||
sourcePath := source.Child(oldName)
|
||||
if strings.HasPrefix(string(target), string(sourcePath)) {
|
||||
if movesIntoItself(sourcePath, target) {
|
||||
return fmt.Errorf("mv: can not move directory to a subdirectory of itself")
|
||||
}
|
||||
|
||||
@@ -33,6 +33,13 @@ func (f *Filer) CanRename(ctx context.Context, source, target util.FullPath, old
|
||||
return nil
|
||||
}
|
||||
|
||||
// movesIntoItself reports whether target, the new parent directory, is
|
||||
// sourcePath or lies under it. A sibling that only shares a name prefix,
|
||||
// such as /a.d for /a, does not.
|
||||
func movesIntoItself(sourcePath, target util.FullPath) bool {
|
||||
return target == sourcePath || target.IsUnder(sourcePath)
|
||||
}
|
||||
|
||||
func (f *Filer) DetectBucket(source util.FullPath) (bucket string) {
|
||||
if strings.HasPrefix(string(source), f.DirBucketsPath+"/") {
|
||||
bucketAndObjectKey := string(source)[len(f.DirBucketsPath)+1:]
|
||||
|
||||
@@ -29,3 +29,27 @@ func TestDetectBucket(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMovesIntoItself(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
sourcePath string
|
||||
target string
|
||||
want bool
|
||||
}{
|
||||
{"into itself", "/dir/a", "/dir/a", true},
|
||||
{"into a child", "/dir/a", "/dir/a/b", true},
|
||||
{"into a deeper descendant", "/dir/a", "/dir/a/b/c", true},
|
||||
{"into a sibling sharing the name prefix", "/dir/a", "/dir/a.d", false},
|
||||
{"into a descendant of that sibling", "/dir/a", "/dir/ab/c", false},
|
||||
{"into the parent", "/dir/a", "/dir", false},
|
||||
{"into an unrelated directory", "/dir/a", "/other", false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := movesIntoItself(util.FullPath(tt.sourcePath), util.FullPath(tt.target)); got != tt.want {
|
||||
t.Errorf("movesIntoItself(%q, %q) = %v, want %v", tt.sourcePath, tt.target, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user