mirror of
https://github.com/versity/versitygw.git
synced 2026-08-17 04:36:19 +00:00
feat: add option to disable copy-file-range for multipart uploads
The standard io.Copy() will attempt to use copy_file_range when available. However, this can cause problems with certain filesystems. Add an option that will prevent io.Copy() from being able to use copy_file_range to force a standard data copy between file descriptors when needed for completing multipart uploads.
This commit is contained in:
+36
-15
@@ -81,6 +81,12 @@ type Posix struct {
|
||||
// there are different filesystems mounted below the bucket level.
|
||||
forceNoTmpFile bool
|
||||
|
||||
// forceNoCopyFileRange is a flag to disable the use of io.Copy to
|
||||
// reassemble multipart upload parts, which uses copy_file_range on
|
||||
// linux. This is needed for cases where a filesystem that does not
|
||||
// support copy_file_range is mounted over NFSv4.2.
|
||||
forceNoCopyFileRange bool
|
||||
|
||||
// enable posix level bucket name validations, not needed if the
|
||||
// frontend handlers are already validating bucket names
|
||||
validateBucketName bool
|
||||
@@ -150,6 +156,8 @@ type PosixOpts struct {
|
||||
// ForceNoTmpFile disables the use of O_TMPFILE even if the filesystem
|
||||
// supports it
|
||||
ForceNoTmpFile bool
|
||||
// ForceNoCopyFileRange disables the use of io.Copy for multipart uploads parts
|
||||
ForceNoCopyFileRange bool
|
||||
// ValidateBucketNames enables minimal bucket name validation to prevent
|
||||
// incorrect access to the filesystem. This is only needed if the
|
||||
// frontend is not already validating bucket names.
|
||||
@@ -212,19 +220,20 @@ func New(rootdir string, meta meta.MetadataStorer, opts PosixOpts) (*Posix, erro
|
||||
}
|
||||
|
||||
return &Posix{
|
||||
meta: meta,
|
||||
rootfd: f,
|
||||
rootdir: rootdir,
|
||||
euid: os.Geteuid(),
|
||||
egid: os.Getegid(),
|
||||
chownuid: opts.ChownUID,
|
||||
chowngid: opts.ChownGID,
|
||||
bucketlinks: opts.BucketLinks,
|
||||
versioningDir: versioningdirAbs,
|
||||
newDirPerm: opts.NewDirPerm,
|
||||
forceNoTmpFile: opts.ForceNoTmpFile,
|
||||
validateBucketName: opts.ValidateBucketNames,
|
||||
actionLimiter: semaphore.NewWeighted(int64(concurrencyOrDefault(opts.Concurrency))),
|
||||
meta: meta,
|
||||
rootfd: f,
|
||||
rootdir: rootdir,
|
||||
euid: os.Geteuid(),
|
||||
egid: os.Getegid(),
|
||||
chownuid: opts.ChownUID,
|
||||
chowngid: opts.ChownGID,
|
||||
bucketlinks: opts.BucketLinks,
|
||||
versioningDir: versioningdirAbs,
|
||||
newDirPerm: opts.NewDirPerm,
|
||||
forceNoTmpFile: opts.ForceNoTmpFile,
|
||||
forceNoCopyFileRange: opts.ForceNoCopyFileRange,
|
||||
validateBucketName: opts.ValidateBucketNames,
|
||||
actionLimiter: semaphore.NewWeighted(int64(concurrencyOrDefault(opts.Concurrency))),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1610,6 +1619,10 @@ func (p *Posix) CompleteMultipartUpload(ctx context.Context, input *s3.CompleteM
|
||||
return p.CompleteMultipartUploadWithCopy(ctx, input, nil)
|
||||
}
|
||||
|
||||
type onlyRead struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (p *Posix) CompleteMultipartUploadWithCopy(ctx context.Context, input *s3.CompleteMultipartUploadInput, customMove func(from *os.File, to *os.File) error) (s3response.CompleteMultipartUploadResult, string, error) {
|
||||
acct, ok := ctx.Value("account").(auth.Account)
|
||||
if !ok {
|
||||
@@ -1819,10 +1832,18 @@ func (p *Posix) CompleteMultipartUploadWithCopy(ctx context.Context, input *s3.C
|
||||
bucket, object, err)
|
||||
fw := f.File()
|
||||
fw.Seek(0, io.SeekEnd)
|
||||
_, err = io.Copy(fw, pf)
|
||||
if p.forceNoCopyFileRange {
|
||||
_, err = io.Copy(fw, &onlyRead{pf})
|
||||
} else {
|
||||
_, err = io.Copy(fw, pf)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_, err = io.Copy(f.File(), pf)
|
||||
if p.forceNoCopyFileRange {
|
||||
_, err = io.Copy(f.File(), &onlyRead{pf})
|
||||
} else {
|
||||
_, err = io.Copy(f.File(), pf)
|
||||
}
|
||||
}
|
||||
pf.Close()
|
||||
if err != nil {
|
||||
|
||||
+24
-16
@@ -25,14 +25,15 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
chownuid, chowngid bool
|
||||
bucketlinks bool
|
||||
versioningDir string
|
||||
dirPerms uint
|
||||
sidecar string
|
||||
nometa bool
|
||||
forceNoTmpFile bool
|
||||
actionsConcurrency int
|
||||
chownuid, chowngid bool
|
||||
bucketlinks bool
|
||||
versioningDir string
|
||||
dirPerms uint
|
||||
sidecar string
|
||||
nometa bool
|
||||
forceNoTmpFile bool
|
||||
forceNoCopyFileRange bool
|
||||
actionsConcurrency int
|
||||
)
|
||||
|
||||
func posixCommand() *cli.Command {
|
||||
@@ -108,6 +109,12 @@ will be translated into the file /mnt/fs/gwroot/mybucket/a/b/c/myobject`,
|
||||
EnvVars: []string{"VGW_DISABLE_OTMP"},
|
||||
Destination: &forceNoTmpFile,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "disable-copy-file-range",
|
||||
Usage: "explicitly copy multipart upload parts instead of using copy_file_range (which may hang with some NFS servers)",
|
||||
EnvVars: []string{"VGW_DISABLE_COPY_FILE_RANGE"},
|
||||
Destination: &forceNoCopyFileRange,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -132,14 +139,15 @@ func runPosix(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
opts := posix.PosixOpts{
|
||||
ChownUID: chownuid,
|
||||
ChownGID: chowngid,
|
||||
BucketLinks: bucketlinks,
|
||||
VersioningDir: versioningDir,
|
||||
NewDirPerm: fs.FileMode(dirPerms),
|
||||
ForceNoTmpFile: forceNoTmpFile,
|
||||
ValidateBucketNames: disableStrictBucketNames,
|
||||
Concurrency: actionsConcurrency,
|
||||
ChownUID: chownuid,
|
||||
ChownGID: chowngid,
|
||||
BucketLinks: bucketlinks,
|
||||
VersioningDir: versioningDir,
|
||||
NewDirPerm: fs.FileMode(dirPerms),
|
||||
ForceNoTmpFile: forceNoTmpFile,
|
||||
ForceNoCopyFileRange: forceNoCopyFileRange,
|
||||
ValidateBucketNames: disableStrictBucketNames,
|
||||
Concurrency: actionsConcurrency,
|
||||
}
|
||||
|
||||
var ms meta.MetadataStorer
|
||||
|
||||
Reference in New Issue
Block a user