mirror of
https://github.com/versity/versitygw.git
synced 2026-09-26 18:04:36 +00:00
`CopyObject` treated a destination path equal to the source path as an in-place metadata rewrite, regardless of bucket versioning. On a versioned bucket a self copy therefore edited the current version and returned its existing version id instead of creating a new one. This also defeated object lock: `CheckObjectAccess` skips the retention and legal hold checks for overwrites on version-enabled buckets because an overwrite is expected to create a new version, so a `COMPLIANCE` retained or legal held version had its metadata replaced underneath it. A self copy is now rewritten in place only when the bucket is unversioned, and otherwise goes through the regular copy path, which creates a new version and leaves the one it replaces untouched. The rejection of a self copy that replaces nothing moved out of the in-place branch and is now applied only when the copy source carries no version id. Naming a version explicitly makes the request a regular copy, which AWS accepts even with the `COPY` metadata directive, while versitygw answered `InvalidRequest`. Source tagging for a `COPY` tagging directive is now read before `PutObject` writes the destination rather than after. A self copy replaces the source object's attributes, so the later read returned nothing and the tags were dropped from the new version.
75 lines
1.8 KiB
Go
75 lines
1.8 KiB
Go
// Copyright 2026 Versity Software
|
|
// This file is licensed under the Apache License, Version 2.0
|
|
// (the "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package posix
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
"sync"
|
|
)
|
|
|
|
func withReadBufferSize(r io.ReadCloser, size int) io.ReadCloser {
|
|
if size <= 0 {
|
|
return r
|
|
}
|
|
|
|
return &bufferedReadCloser{
|
|
r: bufio.NewReaderSize(r, size),
|
|
c: r,
|
|
}
|
|
}
|
|
|
|
type bufferedReadCloser struct {
|
|
r *bufio.Reader
|
|
c io.Closer
|
|
}
|
|
|
|
func (b *bufferedReadCloser) Read(p []byte) (int, error) {
|
|
return b.r.Read(p)
|
|
}
|
|
|
|
func (b *bufferedReadCloser) Close() error {
|
|
return b.c.Close()
|
|
}
|
|
|
|
// closeOnEOFReader closes c once r is drained, for readers whose source has
|
|
// to be released before the caller is done with the reader.
|
|
type closeOnEOFReader struct {
|
|
r io.Reader
|
|
c io.Closer
|
|
closed bool
|
|
}
|
|
|
|
func (e *closeOnEOFReader) Read(p []byte) (int, error) {
|
|
n, err := e.r.Read(p)
|
|
if errors.Is(err, io.EOF) && !e.closed {
|
|
e.closed = true
|
|
e.c.Close()
|
|
}
|
|
|
|
return n, err
|
|
}
|
|
|
|
var odirectUnsupportedWarnByOp sync.Map
|
|
|
|
func warnODirectUnsupportedOnce(op string, err error) {
|
|
v, _ := odirectUnsupportedWarnByOp.LoadOrStore(op, &sync.Once{})
|
|
v.(*sync.Once).Do(func() {
|
|
log.Printf("WARNING: O_DIRECT is enabled but unsupported (%s: %v); falling back to buffered I/O. This warning is shown once per operation.", op, err)
|
|
})
|
|
}
|