1
0
mirror of https://github.com/rclone/rclone.git synced 2025-12-18 17:23:20 +00:00
Files
rclone/cmd/serve/s3/utils.go
Nick Craig-Wood 1d200580df serve s3: make errors in --s3-auth-key fatal - fixes #9044
Previously if auth keys were provided without a comma then rclone
would only log an INFO message which could mean it went on to serve
without any auth.

The parsing for environment variables was changed in v1.70.0 to make
them work properly with multiple inputs. This means the input is
treated like a mini CSV file which works well except in this case when
the input has commas. This meant `user,auth` without quotes is treated
as two key pairs `user` and `quote`. The correct syntax is
`"user,auth"`. This updates the documentation accordingly.
2025-12-17 17:06:44 +00:00

140 lines
2.6 KiB
Go

package s3
import (
"context"
"encoding/hex"
"errors"
"io"
"os"
"path"
"strings"
"github.com/rclone/gofakes3"
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/hash"
"github.com/rclone/rclone/vfs"
)
func getDirEntries(prefix string, VFS *vfs.VFS) (vfs.Nodes, error) {
node, err := VFS.Stat(prefix)
if err == vfs.ENOENT {
return nil, gofakes3.ErrNoSuchKey
} else if err != nil {
return nil, err
}
if !node.IsDir() {
return nil, gofakes3.ErrNoSuchKey
}
dir := node.(*vfs.Dir)
dirEntries, err := dir.ReadDirAll()
if err != nil {
return nil, err
}
return dirEntries, nil
}
func getFileHashByte(node any, hashType hash.Type) []byte {
b, err := hex.DecodeString(getFileHash(node, hashType))
if err != nil {
return nil
}
return b
}
func getFileHash(node any, hashType hash.Type) string {
if hashType == hash.None {
return ""
}
var o fs.Object
switch b := node.(type) {
case vfs.Node:
fsObj, ok := b.DirEntry().(fs.Object)
if !ok {
fs.Debugf("serve s3", "File uploading - reading hash from VFS cache")
in, err := b.Open(os.O_RDONLY)
if err != nil {
return ""
}
defer func() {
_ = in.Close()
}()
h, err := hash.NewMultiHasherTypes(hash.NewHashSet(hashType))
if err != nil {
return ""
}
_, err = io.Copy(h, in)
if err != nil {
return ""
}
return h.Sums()[hashType]
}
o = fsObj
case fs.Object:
o = b
}
hash, err := o.Hash(context.Background(), hashType)
if err != nil {
return ""
}
return hash
}
func prefixParser(p *gofakes3.Prefix) (path, remaining string) {
idx := strings.LastIndexByte(p.Prefix, '/')
if idx < 0 {
return "", p.Prefix
}
return p.Prefix[:idx], p.Prefix[idx+1:]
}
// FIXME this could be implemented by VFS.MkdirAll()
func mkdirRecursive(path string, VFS *vfs.VFS) error {
path = strings.Trim(path, "/")
dirs := strings.Split(path, "/")
dir := ""
for _, d := range dirs {
dir += "/" + d
if _, err := VFS.Stat(dir); err != nil {
err := VFS.Mkdir(dir, 0777)
if err != nil {
return err
}
}
}
return nil
}
func rmdirRecursive(p string, VFS *vfs.VFS) {
dir := path.Dir(p)
if !strings.ContainsAny(dir, "/\\") {
// might be bucket(root)
return
}
if _, err := VFS.Stat(dir); err == nil {
err := VFS.Remove(dir)
if err != nil {
return
}
rmdirRecursive(dir, VFS)
}
}
func authlistResolver(list []string) (map[string]string, error) {
authList := make(map[string]string)
for _, v := range list {
parts := strings.Split(v, ",")
if len(parts) != 2 {
return nil, errors.New("invalid auth pair: expecting a single comma")
}
authList[parts[0]] = parts[1]
}
return authList, nil
}