Skip to content

Commit 2d305ce

Browse files
authored
fix: error out when the source and destination might be subdirectories of each other (#5173)
1 parent 3296636 commit 2d305ce

File tree

3 files changed

+72
-3
lines changed

3 files changed

+72
-3
lines changed

cmd/client-url.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,37 @@ func guessURLContentType(urlStr string) string {
267267
contentType := mimedb.TypeByExtension(filepath.Ext(url.Path))
268268
return contentType
269269
}
270+
271+
// urlParts - split URL into parts.
272+
func urlParts(urlStr string) []string {
273+
// Convert '/' on windows to filepath.Separator.
274+
urlStr = filepath.FromSlash(urlStr)
275+
276+
if runtime.GOOS == "windows" {
277+
// Remove '/' prefix before alias if any to support '\\home' alias
278+
// style under Windows
279+
urlStr = strings.TrimPrefix(urlStr, string(filepath.Separator))
280+
}
281+
282+
// Remove everything after alias (i.e. after '/').
283+
return strings.Split(urlStr, string(filepath.Separator))
284+
}
285+
286+
// isURLPrefix - check if source and destination be subdirectories of each other
287+
func isURLPrefix(src string, dest string) bool {
288+
srcURLParts := urlParts(src)
289+
dstURLParts := urlParts(dest)
290+
minIndex := min(len(srcURLParts), len(dstURLParts))
291+
isPrefix := true
292+
for i := 0; i < minIndex; i++ {
293+
// if one of the URLs ends with '/' and other does not
294+
if (i == minIndex-1) && (dstURLParts[i] == "" || srcURLParts[i] == "" || dstURLParts[i] == "*" || srcURLParts[i] == "*") {
295+
continue
296+
}
297+
if srcURLParts[i] != dstURLParts[i] {
298+
isPrefix = false
299+
break
300+
}
301+
}
302+
return isPrefix
303+
}

cmd/client-url_test.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717

1818
package cmd
1919

20-
import checkv1 "gopkg.in/check.v1"
20+
import (
21+
"testing"
22+
23+
checkv1 "gopkg.in/check.v1"
24+
)
2125

2226
// TestURL - tests url parsing and fields.
2327
func (s *TestSuite) TestURL(c *checkv1.C) {
@@ -52,3 +56,33 @@ func (s *TestSuite) TestURLJoinPath(c *checkv1.C) {
5256
url = urlJoinPath(url1, url2)
5357
c.Assert(url, checkv1.Equals, "http://s3.mycompany.io/dev/mybucket/bin/")
5458
}
59+
60+
func Test_isURLPrefix(t *testing.T) {
61+
type args struct {
62+
src string
63+
dest string
64+
}
65+
tests := []struct {
66+
name string
67+
args args
68+
want bool
69+
}{
70+
{"test1", args{"s3/test", "s3/test/test"}, true},
71+
{"test2", args{"s3/test/", "s3/test/test"}, true},
72+
{"test3", args{"s3/test/test", "s3/test/"}, true},
73+
{"test4", args{"s3/test/test", "s3/test/test.123"}, false},
74+
{"test5", args{"s3/test/", "s3/test/test/test/test"}, true},
75+
{"test6", args{"s3/test/*", "s3/test/test/"}, true},
76+
{"test7", args{"s3/test/*", "s3/test1/test/"}, false},
77+
}
78+
for _, tt := range tests {
79+
t.Run(tt.name, func(t *testing.T) {
80+
if got := isURLPrefix(tt.args.src, tt.args.dest); got != tt.want {
81+
t.Errorf("isURLPrefix() = %v, want %v", got, tt.want)
82+
}
83+
if got := isURLPrefix(tt.args.dest, tt.args.src); got != tt.want {
84+
t.Errorf("isURLPrefix() = %v, want %v", got, tt.want)
85+
}
86+
})
87+
}
88+
}

cmd/mv-main.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,9 @@ func mainMove(cliCtx *cli.Context) error {
210210
args := cliCtx.Args()
211211
srcURL := args.Get(0)
212212
dstURL := args.Get(1)
213-
if srcURL == dstURL {
214-
fatalIf(errDummy().Trace(), fmt.Sprintf("Source and destination urls cannot be the same: %v.", srcURL))
213+
if isURLPrefix(srcURL, dstURL) {
214+
fatalIf(errDummy().Trace(), fmt.Sprintf("The source %v and destination %v cannot be subdirectories of each other", srcURL, dstURL))
215+
return nil
215216
}
216217
}
217218

0 commit comments

Comments
 (0)