Skip to content

Commit

Permalink
fix: improve key sanitzation
Browse files Browse the repository at this point in the history
s3 keys do not start with a `/`, so we trim it if found.
Additionally, steamline the Stat operation to make the errors
clearer.

Signed-off-by: Lucas Roesler <roesler.lucas@gmail.com>
  • Loading branch information
LucasRoesler committed Apr 13, 2022
1 parent dd3e2f8 commit 92e6c9d
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 21 deletions.
39 changes: 22 additions & 17 deletions s3_fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ func (fs *Fs) OpenFile(name string, flag int, _ os.FileMode) (afero.File, error)
}

info, err := file.Stat()

if err != nil {
return nil, err
}
Expand Down Expand Up @@ -247,41 +246,39 @@ func (fs *Fs) Rename(oldname, newname string) error {
func (fs *Fs) Stat(name string) (os.FileInfo, error) {
name = sanitize(name)

// Special case because you can not HeadObject on the bucket itself.
if name == "/" {
return NewFileInfo(name, true, 0, time.Unix(0, 0)), nil
if strings.HasSuffix(name, "/") {
return fs.statDirectory(name)
}

out, err := fs.client.HeadObject(context.Background(), &s3.HeadObjectInput{
Bucket: aws.String(fs.bucket),
Key: aws.String(name),
})
if err != nil {
errOp := &smithy.OperationError{}
if errors.As(err, &errOp) {
if errOp.OperationName == "HeadObject" {
statDir, errStat := fs.statDirectory(name)
return statDir, errStat
}
// if it is a not found error, then we try to treat it as a directory
// before we give up.
var ae smithy.APIError
if errors.As(err, &ae) && ae.ErrorCode() == "NotFound" {
return fs.statDirectory(name)
}

return FileInfo{}, &os.PathError{
Op: "stat",
Path: name,
Err: err,
}
} else if strings.HasSuffix(name, "/") {
// user asked for a directory, but this is a file
return FileInfo{name: name}, nil
}
return NewFileInfo(path.Base(name), false, out.ContentLength, *out.LastModified), nil
}

func (fs *Fs) statDirectory(name string) (os.FileInfo, error) {
nameClean := path.Clean(name)
// Special case because you can not HeadObject on the bucket itself.
if name == "/" {
return NewFileInfo(name, true, 0, time.Unix(0, 0)), nil
}

out, err := fs.client.ListObjectsV2(context.Background(), &s3.ListObjectsV2Input{
Bucket: aws.String(fs.bucket),
Prefix: aws.String(strings.TrimPrefix(nameClean, "/")),
Prefix: aws.String(name),
MaxKeys: 1,
})
if err != nil {
Expand Down Expand Up @@ -396,5 +393,13 @@ func sanitize(name string) string {
}

// s3 requires forward slashes
return filepath.ToSlash(out)
out = filepath.ToSlash(out)

// a special case for the root path
if out == "/" {
return out
}

// s3 keys should not start with a slash
return strings.TrimPrefix(out, "/")
}
8 changes: 4 additions & 4 deletions s3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -914,14 +914,14 @@ func TestSanitize(t *testing.T) {
expected: "",
},
{
name: "linux path is unchanged",
name: "linux path to file trims leading /",
value: "/path/to/file",
expected: "/path/to/file",
expected: "path/to/file",
},
{
name: "linux path to directory is unchanged",
name: "linux path to directory trims leading /",
value: "/path/to/dir/",
expected: "/path/to/dir/",
expected: "path/to/dir/",
},
{
name: "handles linux root path",
Expand Down

0 comments on commit 92e6c9d

Please sign in to comment.