mirror of
https://github.com/rclone/rclone.git
synced 2026-05-12 10:03:35 -04:00
Implement multipart upload support with configurable chunk size and concurrency options Enable OpenChunkWriter with per-chunk encryption Enhance multipart upload handling with new upload cutoff and error management for small files
114 lines
4.6 KiB
Go
114 lines
4.6 KiB
Go
// Package fs is a generic file system interface for rclone object storage systems
|
|
package fs
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"time"
|
|
)
|
|
|
|
// Constants
|
|
const (
|
|
// ModTimeNotSupported is a very large precision value to show
|
|
// mod time isn't supported on this Fs
|
|
ModTimeNotSupported = 100 * 365 * 24 * time.Hour
|
|
// MaxLevel is a sentinel representing an infinite depth for listings
|
|
MaxLevel = math.MaxInt32
|
|
// The suffix added to a translated symbolic link
|
|
LinkSuffix = ".rclonelink"
|
|
)
|
|
|
|
// Globals
|
|
var (
|
|
// ErrorNotFoundInConfigFile is returned by NewFs if not found in config file
|
|
ErrorNotFoundInConfigFile = errors.New("didn't find section in config file")
|
|
ErrorCantPurge = errors.New("can't purge directory")
|
|
ErrorCantCopy = errors.New("can't copy object - incompatible remotes")
|
|
ErrorCantMove = errors.New("can't move object - incompatible remotes")
|
|
ErrorCantDirMove = errors.New("can't move directory - incompatible remotes")
|
|
ErrorCantUploadEmptyFiles = errors.New("can't upload empty files to this remote")
|
|
ErrorDirExists = errors.New("can't copy directory - destination already exists")
|
|
ErrorCantSetModTime = errors.New("can't set modified time")
|
|
ErrorCantSetModTimeWithoutDelete = errors.New("can't set modified time without deleting existing object")
|
|
ErrorDirNotFound = errors.New("directory not found")
|
|
ErrorObjectNotFound = errors.New("object not found")
|
|
ErrorLevelNotSupported = errors.New("level value not supported")
|
|
ErrorListAborted = errors.New("list aborted")
|
|
ErrorListBucketRequired = errors.New("bucket or container name is needed in remote")
|
|
ErrorIsFile = errors.New("is a file not a directory")
|
|
ErrorIsDir = errors.New("is a directory not a file")
|
|
ErrorNotAFile = errors.New("is not a regular file")
|
|
ErrorNotDeleting = errors.New("not deleting files as there were IO errors")
|
|
ErrorNotDeletingDirs = errors.New("not deleting directories as there were IO errors")
|
|
ErrorOverlapping = errors.New("can't sync or move files on overlapping remotes (try excluding the destination with a filter rule)")
|
|
ErrorDirectoryNotEmpty = errors.New("directory not empty")
|
|
ErrorImmutableModified = errors.New("immutable file modified")
|
|
ErrorPermissionDenied = errors.New("permission denied")
|
|
ErrorCantShareDirectories = errors.New("this backend can't share directories with link")
|
|
ErrorNotImplemented = errors.New("optional feature not implemented")
|
|
ErrorCommandNotFound = errors.New("command not found")
|
|
ErrorFileNameTooLong = errors.New("file name too long")
|
|
ErrorCantListRoot = errors.New("can't list root")
|
|
ErrorFileTooSmall = errors.New("file too small for multipart upload")
|
|
)
|
|
|
|
// FileTooSmallError is returned by OpenChunkWriter when a file is below the
|
|
// backend's minimum size for multipart uploads. It wraps ErrorFileTooSmall
|
|
// and carries the minimum size so callers can retry with a larger file.
|
|
type FileTooSmallError struct {
|
|
MinSize int64
|
|
}
|
|
|
|
// Error implements the error interface.
|
|
func (e *FileTooSmallError) Error() string {
|
|
return fmt.Sprintf("file too small for multipart upload (minimum %d bytes)", e.MinSize)
|
|
}
|
|
|
|
// Unwrap returns ErrorFileTooSmall so errors.Is works.
|
|
func (e *FileTooSmallError) Unwrap() error {
|
|
return ErrorFileTooSmall
|
|
}
|
|
|
|
// CheckClose is a utility function used to check the return from
|
|
// Close in a defer statement.
|
|
func CheckClose(c io.Closer, err *error) {
|
|
cerr := c.Close()
|
|
if *err == nil {
|
|
*err = cerr
|
|
}
|
|
}
|
|
|
|
// FileExists returns true if a file remote exists.
|
|
// If remote is a directory, FileExists returns false.
|
|
func FileExists(ctx context.Context, fs Fs, remote string) (bool, error) {
|
|
_, err := fs.NewObject(ctx, remote)
|
|
if err != nil {
|
|
if err == ErrorObjectNotFound || err == ErrorNotAFile || err == ErrorPermissionDenied {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// GetModifyWindow calculates the maximum modify window between the given Fses
|
|
// and the Config.ModifyWindow parameter.
|
|
func GetModifyWindow(ctx context.Context, fss ...Info) time.Duration {
|
|
window := time.Duration(GetConfig(ctx).ModifyWindow)
|
|
for _, f := range fss {
|
|
if f != nil {
|
|
precision := f.Precision()
|
|
if precision == ModTimeNotSupported {
|
|
return ModTimeNotSupported
|
|
}
|
|
if precision > window {
|
|
window = precision
|
|
}
|
|
}
|
|
}
|
|
return window
|
|
}
|