From fa29bc331a57b01ef87df66bc05b5e0b1440ac82 Mon Sep 17 00:00:00 2001 From: Brett Jones Date: Sat, 20 Nov 2021 18:30:50 -0600 Subject: [PATCH] fixing #321 (#322) File.Readdir appends trailing slash even if there is already one present by @blockloop --- s3_file.go | 7 ++++--- s3_fs.go | 16 +++------------- s3_test.go | 29 +++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/s3_file.go b/s3_file.go index cbe222d8..21e5334a 100644 --- a/s3_file.go +++ b/s3_file.go @@ -9,6 +9,7 @@ import ( "os" "path" "path/filepath" + "strings" "time" "github.com/spf13/afero" @@ -68,10 +69,10 @@ func (f *File) Readdir(n int) ([]os.FileInfo, error) { } // ListObjects treats leading slashes as part of the directory name // It also needs a trailing slash to list contents of a directory. - name := trimLeadingSlash(f.Name()) // + "/" + name := strings.TrimPrefix(f.Name(), "/") // + "/" // For the root of the bucket, we need to remove any prefix - if name != "" { + if name != "" && !strings.HasSuffix(name, "/") { name += "/" } output, err := f.fs.s3API.ListObjectsV2(&s3.ListObjectsV2Input{ @@ -93,7 +94,7 @@ func (f *File) Readdir(n int) ([]os.FileInfo, error) { fis = append(fis, NewFileInfo(path.Base("/"+*subfolder.Prefix), true, 0, time.Unix(0, 0))) } for _, fileObject := range output.Contents { - if hasTrailingSlash(*fileObject.Key) { + if strings.HasSuffix(*fileObject.Key, "/") { // S3 includes / in the Contents listing for continue } diff --git a/s3_fs.go b/s3_fs.go index f44c4abe..a489a9b2 100644 --- a/s3_fs.go +++ b/s3_fs.go @@ -9,6 +9,7 @@ import ( "os" "path" "path/filepath" + "strings" "time" "github.com/aws/aws-sdk-go/aws" @@ -223,17 +224,6 @@ func (fs Fs) Rename(oldname, newname string) error { return err } -func hasTrailingSlash(s string) bool { - return len(s) > 0 && s[len(s)-1] == '/' -} - -func trimLeadingSlash(s string) string { - if len(s) > 0 && s[0] == '/' { - return s[1:] - } - return s -} - // Stat returns a FileInfo describing the named file. // If there is an error, it will be of type *os.PathError. func (fs Fs) Stat(name string) (os.FileInfo, error) { @@ -254,7 +244,7 @@ func (fs Fs) Stat(name string) (os.FileInfo, error) { Path: name, Err: err, } - } else if hasTrailingSlash(name) { + } else if strings.HasSuffix(name, "/") { // user asked for a directory, but this is a file return FileInfo{name: name}, nil /* @@ -272,7 +262,7 @@ func (fs Fs) statDirectory(name string) (os.FileInfo, error) { nameClean := path.Clean(name) out, err := fs.s3API.ListObjectsV2(&s3.ListObjectsV2Input{ Bucket: aws.String(fs.bucket), - Prefix: aws.String(trimLeadingSlash(nameClean)), + Prefix: aws.String(strings.TrimPrefix(nameClean, "/")), MaxKeys: aws.Int64(1), }) if err != nil { diff --git a/s3_test.go b/s3_test.go index 674c7f5b..aa833eaf 100644 --- a/s3_test.go +++ b/s3_test.go @@ -754,6 +754,35 @@ func TestFileProps(t *testing.T) { } +func TestFileReaddir(t *testing.T) { + fs := GetFs(t) + req := require.New(t) + + err := fs.Mkdir("/dir1", 0750) + req.NoError(err, "Could not create dir1") + + _, err = fs.Create("/dir1/readme.txt") + req.NoError(err, "could not create file") + + t.Run("WithNoTrailingSlash", func(t *testing.T) { + dir, err := fs.Open("/dir1") + req.NoError(err, "could not open /dir1") + + fis, err := dir.Readdir(1) + req.NoError(err, "could not readdir /dir1") + req.Len(fis,1) + }) + + t.Run("WithNoTrailingSlash", func(t *testing.T) { + dir, err := fs.Open("/dir1/") + req.NoError(err, "could not open /dir1/") + + fis, err := dir.Readdir(1) + req.NoError(err, "could not readdir /dir1/") + req.Len(fis,1) + }) +} + // Source: rog's code from https://groups.google.com/forum/#!topic/golang-nuts/keG78hYt1I0 func ReadersEqual(r1, r2 io.Reader) (bool, error) { const chunkSize = 8 * 1024 // 8 KB