From f4aec22da0648663c51f3f9d88f4b769bebea951 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Thu, 12 Jun 2025 10:04:28 +0200 Subject: [PATCH] Bump reva to 85468735db To get new GetUserNoGroups() helper --- go.mod | 8 +- go.sum | 16 +- .../cephfs/admin/subvolume_snapshot_path.go | 22 ++ vendor/github.com/ceph/go-ceph/cephfs/file.go | 8 +- .../ceph/go-ceph/cephfs/permissions.go | 12 +- .../go-ceph/common/commands/interfaces.go | 20 ++ .../go-ceph/internal/commands/commands.go | 38 +++ .../ceph/go-ceph/internal/commands/trace.go | 40 ++- .../cloudflare/circl/dh/x25519/curve_amd64.s | 3 +- .../cloudflare/circl/dh/x448/curve_amd64.s | 3 +- .../cloudflare/circl/ecc/goldilocks/curve.go | 10 +- .../cloudflare/circl/internal/conv/conv.go | 33 ++ .../cloudflare/circl/math/fp25519/fp_amd64.s | 3 +- .../cloudflare/circl/math/fp448/fp_amd64.s | 3 +- .../cloudflare/circl/math/integer.go | 16 + .../cloudflare/circl/sign/ed25519/point.go | 2 +- .../cloudflare/circl/sign/ed448/ed448.go | 2 +- .../github.com/cloudflare/circl/sign/sign.go | 3 + .../minio/minio-go/v7/api-bucket-cors.go | 2 +- .../minio/minio-go/v7/api-bucket-policy.go | 2 +- .../minio/minio-go/v7/api-error-response.go | 37 ++- .../minio/minio-go/v7/api-get-object.go | 8 +- .../github.com/minio/minio-go/v7/api-list.go | 34 +- .../minio/minio-go/v7/api-prompt-object.go | 4 +- .../minio/minio-go/v7/api-put-bucket.go | 2 +- .../minio-go/v7/api-put-object-multipart.go | 17 +- .../minio-go/v7/api-put-object-streaming.go | 2 +- .../minio/minio-go/v7/api-remove.go | 187 ++++++++++- .../minio/minio-go/v7/api-s3-datatypes.go | 8 + .../github.com/minio/minio-go/v7/api-stat.go | 12 +- vendor/github.com/minio/minio-go/v7/api.go | 65 +++- .../minio/minio-go/v7/bucket-cache.go | 8 +- .../minio/minio-go/v7/functional_tests.go | 308 ++++++++++++++++-- .../v7/pkg/replication/replication.go | 45 ++- .../v7/pkg/utils/peek-reader-closer.go | 73 +++++ .../minio/minio-go/v7/post-policy.go | 2 +- vendor/github.com/minio/minio-go/v7/retry.go | 2 + .../github.com/minio/minio-go/v7/s3-error.go | 130 +++++--- vendor/github.com/minio/minio-go/v7/utils.go | 22 +- .../reva/v2/pkg/share/manager/json/json.go | 2 +- .../v2/pkg/share/manager/jsoncs3/jsoncs3.go | 2 +- .../share/manager/owncloudsql/conversions.go | 6 +- .../share/manager/owncloudsql/owncloudsql.go | 2 +- .../opencloud-eu/reva/v2/pkg/utils/grpc.go | 19 +- vendor/modules.txt | 11 +- 45 files changed, 1057 insertions(+), 197 deletions(-) create mode 100644 vendor/github.com/ceph/go-ceph/cephfs/admin/subvolume_snapshot_path.go create mode 100644 vendor/github.com/cloudflare/circl/math/integer.go create mode 100644 vendor/github.com/minio/minio-go/v7/pkg/utils/peek-reader-closer.go diff --git a/go.mod b/go.mod index c00179cf7d..6c462bfe61 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/onsi/gomega v1.37.0 github.com/open-policy-agent/opa v1.5.1 github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250603072916-fa601fb14450 - github.com/opencloud-eu/reva/v2 v2.33.1 + github.com/opencloud-eu/reva/v2 v2.33.2-0.20250612080213-85468735dbd6 github.com/orcaman/concurrent-map v1.0.0 github.com/pkg/errors v0.9.1 github.com/pkg/xattr v0.4.11 @@ -152,10 +152,10 @@ require ( github.com/bluele/gcache v0.0.2 // indirect github.com/bombsimon/logrusr/v3 v3.1.0 // indirect github.com/cenkalti/backoff/v5 v5.0.2 // indirect - github.com/ceph/go-ceph v0.33.0 // indirect + github.com/ceph/go-ceph v0.34.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 // indirect - github.com/cloudflare/circl v1.3.7 // indirect + github.com/cloudflare/circl v1.6.1 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cornelk/hashmap v1.0.8 // indirect @@ -254,7 +254,7 @@ require ( github.com/minio/crc64nvme v1.0.1 // indirect github.com/minio/highwayhash v1.0.3 // indirect github.com/minio/md5-simd v1.1.2 // indirect - github.com/minio/minio-go/v7 v7.0.92 // indirect + github.com/minio/minio-go/v7 v7.0.93 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect diff --git a/go.sum b/go.sum index bdbadbe2fc..874e882470 100644 --- a/go.sum +++ b/go.sum @@ -206,8 +206,8 @@ github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7 github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/ceph/go-ceph v0.33.0 h1:xT9v/MAa+DIBmflyITyFkGRgWngATghGegKJguEOInQ= -github.com/ceph/go-ceph v0.33.0/go.mod h1:6ef0lIyDHnwArykqfWZDWCfbbJAVTXL1tOYrM1M4bAE= +github.com/ceph/go-ceph v0.34.0 h1:C45yU8VRl0Rg+/I0qw5bzT337HG6DL0yBQ0VR6QHv4o= +github.com/ceph/go-ceph v0.34.0/go.mod h1:otRLwpVgM81lK5zdGYOfr4OELdeS97luDBE/PjXAB5o= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -219,8 +219,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -784,8 +784,8 @@ github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.92 h1:jpBFWyRS3p8P/9tsRc+NuvqoFi7qAmTCFPoRFmobbVw= -github.com/minio/minio-go/v7 v7.0.92/go.mod h1:vTIc8DNcnAZIhyFsk8EB90AbPjj3j68aWIEQCiPj7d0= +github.com/minio/minio-go/v7 v7.0.93 h1:lAB4QJp8Nq3vDMOU0eKgMuyBiEGMNlXQ5Glc8qAxqSU= +github.com/minio/minio-go/v7 v7.0.93/go.mod h1:71t2CqDt3ThzESgZUlU1rBN54mksGGlkLcFgguDnnAc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= @@ -869,8 +869,8 @@ github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-202505121527 github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a/go.mod h1:pjcozWijkNPbEtX5SIQaxEW/h8VAVZYTLx+70bmB3LY= github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250603072916-fa601fb14450 h1:QWn9G2f1R/EbyZSbkjtd9jqNq9X0NIphmmD4KYLNZtA= github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250603072916-fa601fb14450/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q= -github.com/opencloud-eu/reva/v2 v2.33.1 h1:dm3AxLx7MCHPI9h23iYr/Uw/AQHmQN+G4XlbZQnm35Y= -github.com/opencloud-eu/reva/v2 v2.33.1/go.mod h1:0Eu27TSdmj1+0PGn8MjFNh84nW16kGzt2Cxdz9oUncM= +github.com/opencloud-eu/reva/v2 v2.33.2-0.20250612080213-85468735dbd6 h1:9NUMVWfrRIJtm41kPNc4bXjCsaIyerumuMx3rnFYNro= +github.com/opencloud-eu/reva/v2 v2.33.2-0.20250612080213-85468735dbd6/go.mod h1:o3ilVD4jpf3v6nk2oG9D7rCh/TRVEBAUOzH9z83d+Yc= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= diff --git a/vendor/github.com/ceph/go-ceph/cephfs/admin/subvolume_snapshot_path.go b/vendor/github.com/ceph/go-ceph/cephfs/admin/subvolume_snapshot_path.go new file mode 100644 index 0000000000..7ed8108fb0 --- /dev/null +++ b/vendor/github.com/ceph/go-ceph/cephfs/admin/subvolume_snapshot_path.go @@ -0,0 +1,22 @@ +//go:build !(octopus || pacific || quincy || reef || squid) && ceph_preview + +package admin + +// SubVolumeSnapshotPath returns the path for a snapshot from the source subvolume. +// +// Similar To: +// +// ceph fs subvolume snapshot getpath --group-name= +func (fsa *FSAdmin) SubVolumeSnapshotPath(volume, group, source, name string) (string, error) { + m := map[string]string{ + "prefix": "fs subvolume snapshot getpath", + "vol_name": volume, + "sub_name": source, + "snap_name": name, + "format": "json", + } + if group != NoGroup { + m["group_name"] = group + } + return parsePathResponse(fsa.marshalMgrCommand(m)) +} diff --git a/vendor/github.com/ceph/go-ceph/cephfs/file.go b/vendor/github.com/ceph/go-ceph/cephfs/file.go index f7a8b3264f..50677b1f9e 100644 --- a/vendor/github.com/ceph/go-ceph/cephfs/file.go +++ b/vendor/github.com/ceph/go-ceph/cephfs/file.go @@ -7,6 +7,10 @@ package cephfs #include #include #include + +int _go_ceph_fchown(struct ceph_mount_info *cmount, int fd, uid_t uid, gid_t gid) { + return ceph_fchown(cmount, fd, uid, gid); +} */ import "C" @@ -278,13 +282,13 @@ func (f *File) Fchmod(mode uint32) error { // // Implements: // -// int ceph_fchown(struct ceph_mount_info *cmount, int fd, int uid, int gid); +// int ceph_fchown(struct ceph_mount_info *cmount, int fd, uid_t uid, gid_t gid); func (f *File) Fchown(user uint32, group uint32) error { if err := f.validate(); err != nil { return err } - ret := C.ceph_fchown(f.mount.mount, f.fd, C.int(user), C.int(group)) + ret := C._go_ceph_fchown(f.mount.mount, f.fd, C.uid_t(user), C.gid_t(group)) return getError(ret) } diff --git a/vendor/github.com/ceph/go-ceph/cephfs/permissions.go b/vendor/github.com/ceph/go-ceph/cephfs/permissions.go index d55470befa..707d431c25 100644 --- a/vendor/github.com/ceph/go-ceph/cephfs/permissions.go +++ b/vendor/github.com/ceph/go-ceph/cephfs/permissions.go @@ -5,6 +5,14 @@ package cephfs #cgo CPPFLAGS: -D_FILE_OFFSET_BITS=64 #include #include + +int _go_ceph_chown(struct ceph_mount_info *cmount, const char *path, uid_t uid, gid_t gid) { + return ceph_chown(cmount, path, uid, gid); +} + +int _go_ceph_lchown(struct ceph_mount_info *cmount, const char *path, uid_t uid, gid_t gid) { + return ceph_lchown(cmount, path, uid, gid); +} */ import "C" @@ -26,7 +34,7 @@ func (mount *MountInfo) Chown(path string, user uint32, group uint32) error { cPath := C.CString(path) defer C.free(unsafe.Pointer(cPath)) - ret := C.ceph_chown(mount.mount, cPath, C.int(user), C.int(group)) + ret := C._go_ceph_chown(mount.mount, cPath, C.uid_t(user), C.gid_t(group)) return getError(ret) } @@ -35,6 +43,6 @@ func (mount *MountInfo) Lchown(path string, user uint32, group uint32) error { cPath := C.CString(path) defer C.free(unsafe.Pointer(cPath)) - ret := C.ceph_lchown(mount.mount, cPath, C.int(user), C.int(group)) + ret := C._go_ceph_lchown(mount.mount, cPath, C.uid_t(user), C.gid_t(group)) return getError(ret) } diff --git a/vendor/github.com/ceph/go-ceph/common/commands/interfaces.go b/vendor/github.com/ceph/go-ceph/common/commands/interfaces.go index c6350b7539..4b3543d99d 100644 --- a/vendor/github.com/ceph/go-ceph/common/commands/interfaces.go +++ b/vendor/github.com/ceph/go-ceph/common/commands/interfaces.go @@ -6,15 +6,35 @@ type MgrCommander interface { MgrCommand(buf [][]byte) ([]byte, string, error) } +// MgrBufferCommander is an interface for the API needed to execute JSON +// formatted commands with an input buffer on the Ceph mgr. +type MgrBufferCommander interface { + MgrCommandWithInputBuffer([][]byte, []byte) ([]byte, string, error) +} + // MonCommander is an interface for the API needed to execute JSON formatted // commands on the ceph mon(s). type MonCommander interface { MonCommand(buf []byte) ([]byte, string, error) } +// MonBufferCommander is an interface for the API needed to execute JSON +// formatted commands with an input buffer on the Ceph mon(s). +type MonBufferCommander interface { + MonCommandWithInputBuffer([]byte, []byte) ([]byte, string, error) +} + // RadosCommander provides an interface for APIs needed to execute JSON // formatted commands on the Ceph cluster. type RadosCommander interface { MgrCommander MonCommander } + +// RadosBufferCommander provides an interface for APIs that need to execute +// JSON formatted commands with an input buffer on the Ceph cluster. +type RadosBufferCommander interface { + RadosCommander + MgrBufferCommander + MonBufferCommander +} diff --git a/vendor/github.com/ceph/go-ceph/internal/commands/commands.go b/vendor/github.com/ceph/go-ceph/internal/commands/commands.go index d0318c6597..113ac016a6 100644 --- a/vendor/github.com/ceph/go-ceph/internal/commands/commands.go +++ b/vendor/github.com/ceph/go-ceph/internal/commands/commands.go @@ -51,3 +51,41 @@ func MarshalMonCommand(m ccom.MonCommander, v interface{}) Response { } return RawMonCommand(m, b) } + +// MarshalMgrCommandWithBuffer takes a generic interface{} value, converts +// it to JSON and sends the JSON, along with the buffer data, to the MGR as +// a command. +func MarshalMgrCommandWithBuffer( + m ccom.MgrBufferCommander, v interface{}, buf []byte) Response { + + b, err := json.Marshal(v) + if err != nil { + return Response{err: err} + } + if err := validate(m); err != nil { + return Response{err: err} + } + return NewResponse(m.MgrCommandWithInputBuffer( + [][]byte{b}, + buf, + )) +} + +// MarshalMonCommandWithBuffer takes a generic interface{} value, converts +// it to JSON and sends the JSON, along with the buffer data, to the MON(s) +// as a command. +func MarshalMonCommandWithBuffer( + m ccom.MonBufferCommander, v interface{}, buf []byte) Response { + + b, err := json.Marshal(v) + if err != nil { + return Response{err: err} + } + if err := validate(m); err != nil { + return Response{err: err} + } + return NewResponse(m.MonCommandWithInputBuffer( + b, + buf, + )) +} diff --git a/vendor/github.com/ceph/go-ceph/internal/commands/trace.go b/vendor/github.com/ceph/go-ceph/internal/commands/trace.go index 8d575fd909..468cf025c8 100644 --- a/vendor/github.com/ceph/go-ceph/internal/commands/trace.go +++ b/vendor/github.com/ceph/go-ceph/internal/commands/trace.go @@ -9,7 +9,7 @@ import ( // NewTraceCommander is a RadosCommander that wraps a given RadosCommander // and when commands are executes prints debug level "traces" to the // standard output. -func NewTraceCommander(c ccom.RadosCommander) ccom.RadosCommander { +func NewTraceCommander(c ccom.RadosBufferCommander) ccom.RadosBufferCommander { return &tracingCommander{c} } @@ -19,7 +19,7 @@ func NewTraceCommander(c ccom.RadosCommander) ccom.RadosCommander { // interface in FSAdmin. You can layer any sort of debugging, error injection, // or whatnot between the FSAdmin layer and the RADOS layer. type tracingCommander struct { - conn ccom.RadosCommander + conn ccom.RadosBufferCommander } func (t *tracingCommander) MgrCommand(buf [][]byte) ([]byte, string, error) { @@ -51,3 +51,39 @@ func (t *tracingCommander) MonCommand(buf []byte) ([]byte, string, error) { } return r, s, err } + +func (t *tracingCommander) MgrCommandWithInputBuffer( + cbuf [][]byte, dbuf []byte) ([]byte, string, error) { + + fmt.Println("(MGR Command w/buffer)") + for i := range cbuf { + fmt.Println("IN:", string(cbuf[i])) + } + fmt.Println("IN DATA:", string(dbuf)) + r, s, err := t.conn.MgrCommandWithInputBuffer(cbuf, dbuf) + fmt.Println("OUT(result):", string(r)) + if s != "" { + fmt.Println("OUT(status):", s) + } + if err != nil { + fmt.Println("OUT(error):", err.Error()) + } + return r, s, err +} + +func (t *tracingCommander) MonCommandWithInputBuffer( + cbuf []byte, dbuf []byte) ([]byte, string, error) { + + fmt.Println("(MON Command w/buffer)") + fmt.Println("IN:", string(cbuf)) + fmt.Println("IN DATA:", string(dbuf)) + r, s, err := t.conn.MonCommandWithInputBuffer(cbuf, dbuf) + fmt.Println("OUT(result):", string(r)) + if s != "" { + fmt.Println("OUT(status):", s) + } + if err != nil { + fmt.Println("OUT(error):", err.Error()) + } + return r, s, err +} diff --git a/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.s b/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.s index b7723185b6..ce9f062894 100644 --- a/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.s +++ b/vendor/github.com/cloudflare/circl/dh/x25519/curve_amd64.s @@ -1,4 +1,5 @@ -// +build amd64 +//go:build amd64 && !purego +// +build amd64,!purego #include "textflag.h" diff --git a/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.s b/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.s index 810aa9e648..ed33ba3d03 100644 --- a/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.s +++ b/vendor/github.com/cloudflare/circl/dh/x448/curve_amd64.s @@ -1,4 +1,5 @@ -// +build amd64 +//go:build amd64 && !purego +// +build amd64,!purego #include "textflag.h" diff --git a/vendor/github.com/cloudflare/circl/ecc/goldilocks/curve.go b/vendor/github.com/cloudflare/circl/ecc/goldilocks/curve.go index 5a939100d2..1f165141a9 100644 --- a/vendor/github.com/cloudflare/circl/ecc/goldilocks/curve.go +++ b/vendor/github.com/cloudflare/circl/ecc/goldilocks/curve.go @@ -18,6 +18,9 @@ func (Curve) Identity() *Point { func (Curve) IsOnCurve(P *Point) bool { x2, y2, t, t2, z2 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} rhs, lhs := &fp.Elt{}, &fp.Elt{} + // Check z != 0 + eq0 := !fp.IsZero(&P.z) + fp.Mul(t, &P.ta, &P.tb) // t = ta*tb fp.Sqr(x2, &P.x) // x^2 fp.Sqr(y2, &P.y) // y^2 @@ -27,13 +30,14 @@ func (Curve) IsOnCurve(P *Point) bool { fp.Mul(rhs, t2, ¶mD) // dt^2 fp.Add(rhs, rhs, z2) // z^2 + dt^2 fp.Sub(lhs, lhs, rhs) // x^2 + y^2 - (z^2 + dt^2) - eq0 := fp.IsZero(lhs) + eq1 := fp.IsZero(lhs) fp.Mul(lhs, &P.x, &P.y) // xy fp.Mul(rhs, t, &P.z) // tz fp.Sub(lhs, lhs, rhs) // xy - tz - eq1 := fp.IsZero(lhs) - return eq0 && eq1 + eq2 := fp.IsZero(lhs) + + return eq0 && eq1 && eq2 } // Generator returns the generator point. diff --git a/vendor/github.com/cloudflare/circl/internal/conv/conv.go b/vendor/github.com/cloudflare/circl/internal/conv/conv.go index 649a8e931d..3fd0df496f 100644 --- a/vendor/github.com/cloudflare/circl/internal/conv/conv.go +++ b/vendor/github.com/cloudflare/circl/internal/conv/conv.go @@ -5,6 +5,8 @@ import ( "fmt" "math/big" "strings" + + "golang.org/x/crypto/cryptobyte" ) // BytesLe2Hex returns an hexadecimal string of a number stored in a @@ -138,3 +140,34 @@ func BigInt2Uint64Le(z []uint64, x *big.Int) { z[i] = 0 } } + +// MarshalBinary encodes a value into a byte array in a format readable by UnmarshalBinary. +func MarshalBinary(v cryptobyte.MarshalingValue) ([]byte, error) { + const DefaultSize = 32 + b := cryptobyte.NewBuilder(make([]byte, 0, DefaultSize)) + b.AddValue(v) + return b.Bytes() +} + +// MarshalBinaryLen encodes a value into an array of n bytes in a format readable by UnmarshalBinary. +func MarshalBinaryLen(v cryptobyte.MarshalingValue, length uint) ([]byte, error) { + b := cryptobyte.NewFixedBuilder(make([]byte, 0, length)) + b.AddValue(v) + return b.Bytes() +} + +// A UnmarshalingValue decodes itself from a cryptobyte.String and advances the pointer. +// It reports whether the read was successful. +type UnmarshalingValue interface { + Unmarshal(*cryptobyte.String) bool +} + +// UnmarshalBinary recovers a value from a byte array. +// It returns an error if the read was unsuccessful. +func UnmarshalBinary(v UnmarshalingValue, data []byte) (err error) { + s := cryptobyte.String(data) + if data == nil || !v.Unmarshal(&s) || !s.Empty() { + err = fmt.Errorf("cannot read %T from input string", v) + } + return +} diff --git a/vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.s b/vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.s index 5c4aeddecb..1fcc2dee17 100644 --- a/vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.s +++ b/vendor/github.com/cloudflare/circl/math/fp25519/fp_amd64.s @@ -1,4 +1,5 @@ -// +build amd64 +//go:build amd64 && !purego +// +build amd64,!purego #include "textflag.h" #include "fp_amd64.h" diff --git a/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.s b/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.s index 435addf5e6..3f1f07c986 100644 --- a/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.s +++ b/vendor/github.com/cloudflare/circl/math/fp448/fp_amd64.s @@ -1,4 +1,5 @@ -// +build amd64 +//go:build amd64 && !purego +// +build amd64,!purego #include "textflag.h" #include "fp_amd64.h" diff --git a/vendor/github.com/cloudflare/circl/math/integer.go b/vendor/github.com/cloudflare/circl/math/integer.go new file mode 100644 index 0000000000..9c80c23b59 --- /dev/null +++ b/vendor/github.com/cloudflare/circl/math/integer.go @@ -0,0 +1,16 @@ +package math + +import "math/bits" + +// NextPow2 finds the next power of two (N=2^k, k>=0) greater than n. +// If n is already a power of two, then this function returns n, and log2(n). +func NextPow2(n uint) (N uint, k uint) { + if bits.OnesCount(n) == 1 { + k = uint(bits.TrailingZeros(n)) + N = n + } else { + k = uint(bits.Len(n)) + N = uint(1) << k + } + return +} diff --git a/vendor/github.com/cloudflare/circl/sign/ed25519/point.go b/vendor/github.com/cloudflare/circl/sign/ed25519/point.go index 374a69503c..d1c3b146b7 100644 --- a/vendor/github.com/cloudflare/circl/sign/ed25519/point.go +++ b/vendor/github.com/cloudflare/circl/sign/ed25519/point.go @@ -164,7 +164,7 @@ func (P *pointR1) isEqual(Q *pointR1) bool { fp.Mul(r, r, &P.z) fp.Sub(l, l, r) b = b && fp.IsZero(l) - return b + return b && !fp.IsZero(&P.z) && !fp.IsZero(&Q.z) } func (P *pointR3) neg() { diff --git a/vendor/github.com/cloudflare/circl/sign/ed448/ed448.go b/vendor/github.com/cloudflare/circl/sign/ed448/ed448.go index 324bd8f334..c368b181b4 100644 --- a/vendor/github.com/cloudflare/circl/sign/ed448/ed448.go +++ b/vendor/github.com/cloudflare/circl/sign/ed448/ed448.go @@ -206,7 +206,7 @@ func newKeyFromSeed(privateKey, seed []byte) { func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) { if len(ctx) > ContextMaxSize { - panic(fmt.Errorf("ed448: bad context length: " + strconv.Itoa(len(ctx)))) + panic(fmt.Errorf("ed448: bad context length: %v", len(ctx))) } H := sha3.NewShake256() diff --git a/vendor/github.com/cloudflare/circl/sign/sign.go b/vendor/github.com/cloudflare/circl/sign/sign.go index 13b20fa4b0..557d6f0960 100644 --- a/vendor/github.com/cloudflare/circl/sign/sign.go +++ b/vendor/github.com/cloudflare/circl/sign/sign.go @@ -107,4 +107,7 @@ var ( // ErrContextNotSupported is the error used if a context is not // supported. ErrContextNotSupported = errors.New("context not supported") + + // ErrContextTooLong is the error used if the context string is too long. + ErrContextTooLong = errors.New("context string too long") ) diff --git a/vendor/github.com/minio/minio-go/v7/api-bucket-cors.go b/vendor/github.com/minio/minio-go/v7/api-bucket-cors.go index 8bf537f73b..9d514947dd 100644 --- a/vendor/github.com/minio/minio-go/v7/api-bucket-cors.go +++ b/vendor/github.com/minio/minio-go/v7/api-bucket-cors.go @@ -98,7 +98,7 @@ func (c *Client) GetBucketCors(ctx context.Context, bucketName string) (*cors.Co bucketCors, err := c.getBucketCors(ctx, bucketName) if err != nil { errResponse := ToErrorResponse(err) - if errResponse.Code == "NoSuchCORSConfiguration" { + if errResponse.Code == NoSuchCORSConfiguration { return nil, nil } return nil, err diff --git a/vendor/github.com/minio/minio-go/v7/api-bucket-policy.go b/vendor/github.com/minio/minio-go/v7/api-bucket-policy.go index dbb5259a81..3a168c13ee 100644 --- a/vendor/github.com/minio/minio-go/v7/api-bucket-policy.go +++ b/vendor/github.com/minio/minio-go/v7/api-bucket-policy.go @@ -104,7 +104,7 @@ func (c *Client) GetBucketPolicy(ctx context.Context, bucketName string) (string bucketPolicy, err := c.getBucketPolicy(ctx, bucketName) if err != nil { errResponse := ToErrorResponse(err) - if errResponse.Code == "NoSuchBucketPolicy" { + if errResponse.Code == NoSuchBucketPolicy { return "", nil } return "", err diff --git a/vendor/github.com/minio/minio-go/v7/api-error-response.go b/vendor/github.com/minio/minio-go/v7/api-error-response.go index 7df211fdaa..e85aa322ca 100644 --- a/vendor/github.com/minio/minio-go/v7/api-error-response.go +++ b/vendor/github.com/minio/minio-go/v7/api-error-response.go @@ -136,15 +136,15 @@ func httpRespToErrorResponse(resp *http.Response, bucketName, objectName string) if objectName == "" { errResp = ErrorResponse{ StatusCode: resp.StatusCode, - Code: "NoSuchBucket", - Message: "The specified bucket does not exist.", + Code: NoSuchBucket, + Message: s3ErrorResponseMap[NoSuchBucket], BucketName: bucketName, } } else { errResp = ErrorResponse{ StatusCode: resp.StatusCode, - Code: "NoSuchKey", - Message: "The specified key does not exist.", + Code: NoSuchKey, + Message: s3ErrorResponseMap[NoSuchKey], BucketName: bucketName, Key: objectName, } @@ -152,23 +152,23 @@ func httpRespToErrorResponse(resp *http.Response, bucketName, objectName string) case http.StatusForbidden: errResp = ErrorResponse{ StatusCode: resp.StatusCode, - Code: "AccessDenied", - Message: "Access Denied.", + Code: AccessDenied, + Message: s3ErrorResponseMap[AccessDenied], BucketName: bucketName, Key: objectName, } case http.StatusConflict: errResp = ErrorResponse{ StatusCode: resp.StatusCode, - Code: "Conflict", - Message: "Bucket not empty.", + Code: Conflict, + Message: s3ErrorResponseMap[Conflict], BucketName: bucketName, } case http.StatusPreconditionFailed: errResp = ErrorResponse{ StatusCode: resp.StatusCode, - Code: "PreconditionFailed", - Message: s3ErrorResponseMap["PreconditionFailed"], + Code: PreconditionFailed, + Message: s3ErrorResponseMap[PreconditionFailed], BucketName: bucketName, Key: objectName, } @@ -209,7 +209,7 @@ func httpRespToErrorResponse(resp *http.Response, bucketName, objectName string) if errResp.Region == "" { errResp.Region = resp.Header.Get("x-amz-bucket-region") } - if errResp.Code == "InvalidRegion" && errResp.Region != "" { + if errResp.Code == InvalidRegion && errResp.Region != "" { errResp.Message = fmt.Sprintf("Region does not match, expecting region ‘%s’.", errResp.Region) } @@ -218,10 +218,11 @@ func httpRespToErrorResponse(resp *http.Response, bucketName, objectName string) // errTransferAccelerationBucket - bucket name is invalid to be used with transfer acceleration. func errTransferAccelerationBucket(bucketName string) error { + msg := "The name of the bucket used for Transfer Acceleration must be DNS-compliant and must not contain periods ‘.’." return ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "InvalidArgument", - Message: "The name of the bucket used for Transfer Acceleration must be DNS-compliant and must not contain periods ‘.’.", + Code: InvalidArgument, + Message: msg, BucketName: bucketName, } } @@ -231,7 +232,7 @@ func errEntityTooLarge(totalSize, maxObjectSize int64, bucketName, objectName st msg := fmt.Sprintf("Your proposed upload size ‘%d’ exceeds the maximum allowed object size ‘%d’ for single PUT operation.", totalSize, maxObjectSize) return ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "EntityTooLarge", + Code: EntityTooLarge, Message: msg, BucketName: bucketName, Key: objectName, @@ -243,7 +244,7 @@ func errEntityTooSmall(totalSize int64, bucketName, objectName string) error { msg := fmt.Sprintf("Your proposed upload size ‘%d’ is below the minimum allowed object size ‘0B’ for single PUT operation.", totalSize) return ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "EntityTooSmall", + Code: EntityTooSmall, Message: msg, BucketName: bucketName, Key: objectName, @@ -255,7 +256,7 @@ func errUnexpectedEOF(totalRead, totalSize int64, bucketName, objectName string) msg := fmt.Sprintf("Data read ‘%d’ is not equal to the size ‘%d’ of the input Reader.", totalRead, totalSize) return ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "UnexpectedEOF", + Code: UnexpectedEOF, Message: msg, BucketName: bucketName, Key: objectName, @@ -266,7 +267,7 @@ func errUnexpectedEOF(totalRead, totalSize int64, bucketName, objectName string) func errInvalidArgument(message string) error { return ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "InvalidArgument", + Code: InvalidArgument, Message: message, RequestID: "minio", } @@ -277,7 +278,7 @@ func errInvalidArgument(message string) error { func errAPINotSupported(message string) error { return ErrorResponse{ StatusCode: http.StatusNotImplemented, - Code: "APINotSupported", + Code: APINotSupported, Message: message, RequestID: "minio", } diff --git a/vendor/github.com/minio/minio-go/v7/api-get-object.go b/vendor/github.com/minio/minio-go/v7/api-get-object.go index 5cc85f61c2..d3cb6c22a0 100644 --- a/vendor/github.com/minio/minio-go/v7/api-get-object.go +++ b/vendor/github.com/minio/minio-go/v7/api-get-object.go @@ -34,14 +34,14 @@ func (c *Client) GetObject(ctx context.Context, bucketName, objectName string, o if err := s3utils.CheckValidBucketName(bucketName); err != nil { return nil, ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "InvalidBucketName", + Code: InvalidBucketName, Message: err.Error(), } } if err := s3utils.CheckValidObjectName(objectName); err != nil { return nil, ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "XMinioInvalidObjectName", + Code: XMinioInvalidObjectName, Message: err.Error(), } } @@ -659,14 +659,14 @@ func (c *Client) getObject(ctx context.Context, bucketName, objectName string, o if err := s3utils.CheckValidBucketName(bucketName); err != nil { return nil, ObjectInfo{}, nil, ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "InvalidBucketName", + Code: InvalidBucketName, Message: err.Error(), } } if err := s3utils.CheckValidObjectName(objectName); err != nil { return nil, ObjectInfo{}, nil, ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "XMinioInvalidObjectName", + Code: XMinioInvalidObjectName, Message: err.Error(), } } diff --git a/vendor/github.com/minio/minio-go/v7/api-list.go b/vendor/github.com/minio/minio-go/v7/api-list.go index 1af0fadbfa..634b8e304d 100644 --- a/vendor/github.com/minio/minio-go/v7/api-list.go +++ b/vendor/github.com/minio/minio-go/v7/api-list.go @@ -285,7 +285,7 @@ func (c *Client) listObjectsV2Query(ctx context.Context, bucketName, objectPrefi // sure proper responses are received. if listBucketResult.IsTruncated && listBucketResult.NextContinuationToken == "" { return listBucketResult, ErrorResponse{ - Code: "NotImplemented", + Code: NotImplemented, Message: "Truncated response should have continuation token set", } } @@ -419,19 +419,25 @@ func (c *Client) listObjectVersions(ctx context.Context, bucketName string, opts } for _, version := range vers { info := ObjectInfo{ - ETag: trimEtag(version.ETag), - Key: version.Key, - LastModified: version.LastModified.Truncate(time.Millisecond), - Size: version.Size, - Owner: version.Owner, - StorageClass: version.StorageClass, - IsLatest: version.IsLatest, - VersionID: version.VersionID, - IsDeleteMarker: version.isDeleteMarker, - UserTags: version.UserTags, - UserMetadata: version.UserMetadata, - Internal: version.Internal, - NumVersions: numVersions, + ETag: trimEtag(version.ETag), + Key: version.Key, + LastModified: version.LastModified.Truncate(time.Millisecond), + Size: version.Size, + Owner: version.Owner, + StorageClass: version.StorageClass, + IsLatest: version.IsLatest, + VersionID: version.VersionID, + IsDeleteMarker: version.isDeleteMarker, + UserTags: version.UserTags, + UserMetadata: version.UserMetadata, + Internal: version.Internal, + NumVersions: numVersions, + ChecksumMode: version.ChecksumType, + ChecksumCRC32: version.ChecksumCRC32, + ChecksumCRC32C: version.ChecksumCRC32C, + ChecksumSHA1: version.ChecksumSHA1, + ChecksumSHA256: version.ChecksumSHA256, + ChecksumCRC64NVME: version.ChecksumCRC64NVME, } if !yield(info) { return false diff --git a/vendor/github.com/minio/minio-go/v7/api-prompt-object.go b/vendor/github.com/minio/minio-go/v7/api-prompt-object.go index bf6239d2dd..26c41d34aa 100644 --- a/vendor/github.com/minio/minio-go/v7/api-prompt-object.go +++ b/vendor/github.com/minio/minio-go/v7/api-prompt-object.go @@ -35,14 +35,14 @@ func (c *Client) PromptObject(ctx context.Context, bucketName, objectName, promp if err := s3utils.CheckValidBucketName(bucketName); err != nil { return nil, ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "InvalidBucketName", + Code: InvalidBucketName, Message: err.Error(), } } if err := s3utils.CheckValidObjectName(objectName); err != nil { return nil, ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "XMinioInvalidObjectName", + Code: XMinioInvalidObjectName, Message: err.Error(), } } diff --git a/vendor/github.com/minio/minio-go/v7/api-put-bucket.go b/vendor/github.com/minio/minio-go/v7/api-put-bucket.go index 447d0c7961..47d8419e6f 100644 --- a/vendor/github.com/minio/minio-go/v7/api-put-bucket.go +++ b/vendor/github.com/minio/minio-go/v7/api-put-bucket.go @@ -35,7 +35,7 @@ func (c *Client) makeBucket(ctx context.Context, bucketName string, opts MakeBuc err = c.doMakeBucket(ctx, bucketName, opts) if err != nil && (opts.Region == "" || opts.Region == "us-east-1") { - if resp, ok := err.(ErrorResponse); ok && resp.Code == "AuthorizationHeaderMalformed" && resp.Region != "" { + if resp, ok := err.(ErrorResponse); ok && resp.Code == AuthorizationHeaderMalformed && resp.Region != "" { opts.Region = resp.Region err = c.doMakeBucket(ctx, bucketName, opts) } diff --git a/vendor/github.com/minio/minio-go/v7/api-put-object-multipart.go b/vendor/github.com/minio/minio-go/v7/api-put-object-multipart.go index 84bc19b28f..844172324f 100644 --- a/vendor/github.com/minio/minio-go/v7/api-put-object-multipart.go +++ b/vendor/github.com/minio/minio-go/v7/api-put-object-multipart.go @@ -44,7 +44,7 @@ func (c *Client) putObjectMultipart(ctx context.Context, bucketName, objectName errResp := ToErrorResponse(err) // Verify if multipart functionality is not available, if not // fall back to single PutObject operation. - if errResp.Code == "AccessDenied" && strings.Contains(errResp.Message, "Access Denied") { + if errResp.Code == AccessDenied && strings.Contains(errResp.Message, "Access Denied") { // Verify if size of reader is greater than '5GiB'. if size > maxSinglePutObjectSize { return UploadInfo{}, errEntityTooLarge(size, maxSinglePutObjectSize, bucketName, objectName) @@ -392,13 +392,14 @@ func (c *Client) completeMultipartUpload(ctx context.Context, bucketName, object // Instantiate all the complete multipart buffer. completeMultipartUploadBuffer := bytes.NewReader(completeMultipartUploadBytes) reqMetadata := requestMetadata{ - bucketName: bucketName, - objectName: objectName, - queryValues: urlValues, - contentBody: completeMultipartUploadBuffer, - contentLength: int64(len(completeMultipartUploadBytes)), - contentSHA256Hex: sum256Hex(completeMultipartUploadBytes), - customHeader: headers, + bucketName: bucketName, + objectName: objectName, + queryValues: urlValues, + contentBody: completeMultipartUploadBuffer, + contentLength: int64(len(completeMultipartUploadBytes)), + contentSHA256Hex: sum256Hex(completeMultipartUploadBytes), + customHeader: headers, + expect200OKWithError: true, } // Execute POST to complete multipart upload for an objectName. diff --git a/vendor/github.com/minio/minio-go/v7/api-put-object-streaming.go b/vendor/github.com/minio/minio-go/v7/api-put-object-streaming.go index 987a3c6928..4a7243edc8 100644 --- a/vendor/github.com/minio/minio-go/v7/api-put-object-streaming.go +++ b/vendor/github.com/minio/minio-go/v7/api-put-object-streaming.go @@ -56,7 +56,7 @@ func (c *Client) putObjectMultipartStream(ctx context.Context, bucketName, objec errResp := ToErrorResponse(err) // Verify if multipart functionality is not available, if not // fall back to single PutObject operation. - if errResp.Code == "AccessDenied" && strings.Contains(errResp.Message, "Access Denied") { + if errResp.Code == AccessDenied && strings.Contains(errResp.Message, "Access Denied") { // Verify if size of reader is greater than '5GiB'. if size > maxSinglePutObjectSize { return UploadInfo{}, errEntityTooLarge(size, maxSinglePutObjectSize, bucketName, objectName) diff --git a/vendor/github.com/minio/minio-go/v7/api-remove.go b/vendor/github.com/minio/minio-go/v7/api-remove.go index 5b4443ec57..2a38e014a2 100644 --- a/vendor/github.com/minio/minio-go/v7/api-remove.go +++ b/vendor/github.com/minio/minio-go/v7/api-remove.go @@ -22,6 +22,7 @@ import ( "context" "encoding/xml" "io" + "iter" "net/http" "net/url" "time" @@ -271,7 +272,7 @@ func processRemoveMultiObjectsResponse(body io.Reader, resultCh chan<- RemoveObj for _, obj := range rmResult.UnDeletedObjects { // Version does not exist is not an error ignore and continue. switch obj.Code { - case "InvalidArgument", "NoSuchVersion": + case InvalidArgument, NoSuchVersion: continue } resultCh <- RemoveObjectResult{ @@ -333,6 +334,33 @@ func (c *Client) RemoveObjects(ctx context.Context, bucketName string, objectsCh return errorCh } +// RemoveObjectsWithIter bulk deletes multiple objects from a bucket. +// Objects (with optional versions) to be removed must be provided with +// an iterator. Objects are removed asynchronously and results must be +// consumed. If the returned result iterator is stopped, the context is +// canceled, or a remote call failed, the provided iterator will no +// longer accept more objects. +func (c *Client) RemoveObjectsWithIter(ctx context.Context, bucketName string, objectsIter iter.Seq[ObjectInfo], opts RemoveObjectsOptions) (iter.Seq[RemoveObjectResult], error) { + // Validate if bucket name is valid. + if err := s3utils.CheckValidBucketName(bucketName); err != nil { + return nil, err + } + // Validate objects channel to be properly allocated. + if objectsIter == nil { + return nil, errInvalidArgument("Objects iter can never by nil") + } + + return func(yield func(RemoveObjectResult) bool) { + select { + case <-ctx.Done(): + return + default: + } + + c.removeObjectsIter(ctx, bucketName, objectsIter, yield, opts) + }, nil +} + // RemoveObjectsWithResult removes multiple objects from a bucket while // it is possible to specify objects versions which are received from // objectsCh. Remove results, successes and failures are sent back via @@ -381,6 +409,144 @@ func hasInvalidXMLChar(str string) bool { return false } +// Generate and call MultiDelete S3 requests based on entries received from the iterator. +func (c *Client) removeObjectsIter(ctx context.Context, bucketName string, objectsIter iter.Seq[ObjectInfo], yield func(RemoveObjectResult) bool, opts RemoveObjectsOptions) { + maxEntries := 1000 + urlValues := make(url.Values) + urlValues.Set("delete", "") + + // Build headers. + headers := make(http.Header) + if opts.GovernanceBypass { + // Set the bypass goverenance retention header + headers.Set(amzBypassGovernance, "true") + } + + processRemoveMultiObjectsResponseIter := func(batch []ObjectInfo, yield func(RemoveObjectResult) bool) bool { + if len(batch) == 0 { + return false + } + + // Generate remove multi objects XML request + removeBytes := generateRemoveMultiObjectsRequest(batch) + // Execute POST on bucket to remove objects. + resp, err := c.executeMethod(ctx, http.MethodPost, requestMetadata{ + bucketName: bucketName, + queryValues: urlValues, + contentBody: bytes.NewReader(removeBytes), + contentLength: int64(len(removeBytes)), + contentMD5Base64: sumMD5Base64(removeBytes), + contentSHA256Hex: sum256Hex(removeBytes), + customHeader: headers, + }) + if resp != nil { + defer closeResponse(resp) + if resp.StatusCode != http.StatusOK { + err = httpRespToErrorResponse(resp, bucketName, "") + } + } + if err != nil { + for _, b := range batch { + if !yield(RemoveObjectResult{ + ObjectName: b.Key, + ObjectVersionID: b.VersionID, + Err: err, + }) { + return false + } + } + return false + } + + // Parse multi delete XML response + rmResult := &deleteMultiObjectsResult{} + if err := xmlDecoder(resp.Body, rmResult); err != nil { + yield(RemoveObjectResult{ObjectName: "", Err: err}) + return false + } + + // Fill deletion that returned an error. + for _, obj := range rmResult.UnDeletedObjects { + // Version does not exist is not an error ignore and continue. + switch obj.Code { + case "InvalidArgument", "NoSuchVersion": + continue + } + if !yield(RemoveObjectResult{ + ObjectName: obj.Key, + ObjectVersionID: obj.VersionID, + Err: ErrorResponse{ + Code: obj.Code, + Message: obj.Message, + }, + }) { + return false + } + } + + // Fill deletion that returned success + for _, obj := range rmResult.DeletedObjects { + if !yield(RemoveObjectResult{ + ObjectName: obj.Key, + // Only filled with versioned buckets + ObjectVersionID: obj.VersionID, + DeleteMarker: obj.DeleteMarker, + DeleteMarkerVersionID: obj.DeleteMarkerVersionID, + }) { + return false + } + } + + return true + } + + var batch []ObjectInfo + + next, stop := iter.Pull(objectsIter) + defer stop() + + for { + // Loop over entries by 1000 and call MultiDelete requests + object, ok := next() + if !ok { + // delete the remaining batch. + processRemoveMultiObjectsResponseIter(batch, yield) + return + } + + if hasInvalidXMLChar(object.Key) { + // Use single DELETE so the object name will be in the request URL instead of the multi-delete XML document. + removeResult := c.removeObject(ctx, bucketName, object.Key, RemoveObjectOptions{ + VersionID: object.VersionID, + GovernanceBypass: opts.GovernanceBypass, + }) + if err := removeResult.Err; err != nil { + // Version does not exist is not an error ignore and continue. + switch ToErrorResponse(err).Code { + case "InvalidArgument", "NoSuchVersion": + continue + } + } + if !yield(removeResult) { + return + } + + continue + } + + batch = append(batch, object) + if len(batch) < maxEntries { + continue + } + + if !processRemoveMultiObjectsResponseIter(batch, yield) { + return + } + + batch = batch[:0] + } +} + // Generate and call MultiDelete S3 requests based on entries received from objectsCh func (c *Client) removeObjects(ctx context.Context, bucketName string, objectsCh <-chan ObjectInfo, resultCh chan<- RemoveObjectResult, opts RemoveObjectsOptions) { maxEntries := 1000 @@ -407,7 +573,7 @@ func (c *Client) removeObjects(ctx context.Context, bucketName string, objectsCh if err := removeResult.Err; err != nil { // Version does not exist is not an error ignore and continue. switch ToErrorResponse(err).Code { - case "InvalidArgument", "NoSuchVersion": + case InvalidArgument, NoSuchVersion: continue } resultCh <- removeResult @@ -442,13 +608,14 @@ func (c *Client) removeObjects(ctx context.Context, bucketName string, objectsCh removeBytes := generateRemoveMultiObjectsRequest(batch) // Execute POST on bucket to remove objects. resp, err := c.executeMethod(ctx, http.MethodPost, requestMetadata{ - bucketName: bucketName, - queryValues: urlValues, - contentBody: bytes.NewReader(removeBytes), - contentLength: int64(len(removeBytes)), - contentMD5Base64: sumMD5Base64(removeBytes), - contentSHA256Hex: sum256Hex(removeBytes), - customHeader: headers, + bucketName: bucketName, + queryValues: urlValues, + contentBody: bytes.NewReader(removeBytes), + contentLength: int64(len(removeBytes)), + contentMD5Base64: sumMD5Base64(removeBytes), + contentSHA256Hex: sum256Hex(removeBytes), + customHeader: headers, + expect200OKWithError: true, }) if resp != nil { if resp.StatusCode != http.StatusOK { @@ -535,7 +702,7 @@ func (c *Client) abortMultipartUpload(ctx context.Context, bucketName, objectNam // This is needed specifically for abort and it cannot // be converged into default case. errorResponse = ErrorResponse{ - Code: "NoSuchUpload", + Code: NoSuchUpload, Message: "The specified multipart upload does not exist.", BucketName: bucketName, Key: objectName, diff --git a/vendor/github.com/minio/minio-go/v7/api-s3-datatypes.go b/vendor/github.com/minio/minio-go/v7/api-s3-datatypes.go index aaa3158b97..32d5897169 100644 --- a/vendor/github.com/minio/minio-go/v7/api-s3-datatypes.go +++ b/vendor/github.com/minio/minio-go/v7/api-s3-datatypes.go @@ -107,6 +107,14 @@ type Version struct { M int // Parity blocks } `xml:"Internal"` + // Checksum values. Only returned by AiStor servers. + ChecksumCRC32 string `xml:",omitempty"` + ChecksumCRC32C string `xml:",omitempty"` + ChecksumSHA1 string `xml:",omitempty"` + ChecksumSHA256 string `xml:",omitempty"` + ChecksumCRC64NVME string `xml:",omitempty"` + ChecksumType string `xml:",omitempty"` + isDeleteMarker bool } diff --git a/vendor/github.com/minio/minio-go/v7/api-stat.go b/vendor/github.com/minio/minio-go/v7/api-stat.go index 11455beb3f..a4b2af7aef 100644 --- a/vendor/github.com/minio/minio-go/v7/api-stat.go +++ b/vendor/github.com/minio/minio-go/v7/api-stat.go @@ -39,14 +39,14 @@ func (c *Client) BucketExists(ctx context.Context, bucketName string) (bool, err }) defer closeResponse(resp) if err != nil { - if ToErrorResponse(err).Code == "NoSuchBucket" { + if ToErrorResponse(err).Code == NoSuchBucket { return false, nil } return false, err } if resp != nil { resperr := httpRespToErrorResponse(resp, bucketName, "") - if ToErrorResponse(resperr).Code == "NoSuchBucket" { + if ToErrorResponse(resperr).Code == NoSuchBucket { return false, nil } if resp.StatusCode != http.StatusOK { @@ -63,14 +63,14 @@ func (c *Client) StatObject(ctx context.Context, bucketName, objectName string, if err := s3utils.CheckValidBucketName(bucketName); err != nil { return ObjectInfo{}, ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "InvalidBucketName", + Code: InvalidBucketName, Message: err.Error(), } } if err := s3utils.CheckValidObjectName(objectName); err != nil { return ObjectInfo{}, ErrorResponse{ StatusCode: http.StatusBadRequest, - Code: "XMinioInvalidObjectName", + Code: XMinioInvalidObjectName, Message: err.Error(), } } @@ -102,8 +102,8 @@ func (c *Client) StatObject(ctx context.Context, bucketName, objectName string, if resp.StatusCode == http.StatusMethodNotAllowed && opts.VersionID != "" && deleteMarker { errResp := ErrorResponse{ StatusCode: resp.StatusCode, - Code: "MethodNotAllowed", - Message: "The specified method is not allowed against this resource.", + Code: MethodNotAllowed, + Message: s3ErrorResponseMap[MethodNotAllowed], BucketName: bucketName, Key: objectName, } diff --git a/vendor/github.com/minio/minio-go/v7/api.go b/vendor/github.com/minio/minio-go/v7/api.go index cc00f92a31..27f19ca278 100644 --- a/vendor/github.com/minio/minio-go/v7/api.go +++ b/vendor/github.com/minio/minio-go/v7/api.go @@ -21,6 +21,7 @@ import ( "bytes" "context" "encoding/base64" + "encoding/xml" "errors" "fmt" "io" @@ -38,6 +39,7 @@ import ( "sync/atomic" "time" + "github.com/dustin/go-humanize" md5simd "github.com/minio/md5-simd" "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/kvcache" @@ -45,6 +47,8 @@ import ( "github.com/minio/minio-go/v7/pkg/signer" "github.com/minio/minio-go/v7/pkg/singleflight" "golang.org/x/net/publicsuffix" + + internalutils "github.com/minio/minio-go/v7/pkg/utils" ) // Client implements Amazon S3 compatible methods. @@ -159,7 +163,7 @@ type Options struct { // Global constants. const ( libraryName = "minio-go" - libraryVersion = "v7.0.92" + libraryVersion = "v7.0.93" ) // User Agent should always following the below style. @@ -455,7 +459,7 @@ func (c *Client) HealthCheck(hcDuration time.Duration) (context.CancelFunc, erro gcancel() if !IsNetworkOrHostDown(err, false) { switch ToErrorResponse(err).Code { - case "NoSuchBucket", "AccessDenied", "": + case NoSuchBucket, AccessDenied, "": atomic.CompareAndSwapInt32(&c.healthStatus, offline, online) } } @@ -477,7 +481,7 @@ func (c *Client) HealthCheck(hcDuration time.Duration) (context.CancelFunc, erro gcancel() if !IsNetworkOrHostDown(err, false) { switch ToErrorResponse(err).Code { - case "NoSuchBucket", "AccessDenied", "": + case NoSuchBucket, AccessDenied, "": atomic.CompareAndSwapInt32(&c.healthStatus, offline, online) } } @@ -512,6 +516,8 @@ type requestMetadata struct { streamSha256 bool addCrc *ChecksumType trailer http.Header // (http.Request).Trailer. Requires v4 signature. + + expect200OKWithError bool } // dumpHTTP - dump HTTP request and response. @@ -615,6 +621,28 @@ func (c *Client) do(req *http.Request) (resp *http.Response, err error) { return resp, nil } +// Peek resp.Body looking for S3 XMl error response: +// - Return the error XML bytes if an error is found +// - Make sure to always restablish the whole http response stream before returning +func tryParseErrRespFromBody(resp *http.Response) ([]byte, error) { + peeker := internalutils.NewPeekReadCloser(resp.Body, 5*humanize.MiByte) + defer func() { + peeker.ReplayFromStart() + resp.Body = peeker + }() + + errResp := ErrorResponse{} + errBytes, err := xmlDecodeAndBody(peeker, &errResp) + if err != nil { + var unmarshalErr xml.UnmarshalError + if errors.As(err, &unmarshalErr) { + return nil, nil + } + return nil, err + } + return errBytes, nil +} + // List of success status. var successStatus = []int{ http.StatusOK, @@ -702,16 +730,30 @@ func (c *Client) executeMethod(ctx context.Context, method string, metadata requ return nil, err } - // For any known successful http status, return quickly. + var success bool + var errBodyBytes []byte + for _, httpStatus := range successStatus { if httpStatus == res.StatusCode { - return res, nil + success = true + break } } - // Read the body to be saved later. - errBodyBytes, err := io.ReadAll(res.Body) - // res.Body should be closed + if success { + if !metadata.expect200OKWithError { + return res, nil + } + errBodyBytes, err = tryParseErrRespFromBody(res) + if err == nil && len(errBodyBytes) == 0 { + // No S3 XML error is found + return res, nil + } + } else { + errBodyBytes, err = io.ReadAll(res.Body) + } + + // By now, res.Body should be closed closeResponse(res) if err != nil { return nil, err @@ -723,6 +765,7 @@ func (c *Client) executeMethod(ctx context.Context, method string, metadata requ // For errors verify if its retryable otherwise fail quickly. errResponse := ToErrorResponse(httpRespToErrorResponse(res, metadata.bucketName, metadata.objectName)) + err = errResponse // Save the body back again. errBodySeeker.Seek(0, 0) // Seek back to starting point. @@ -736,11 +779,11 @@ func (c *Client) executeMethod(ctx context.Context, method string, metadata requ // region is empty. if c.region == "" { switch errResponse.Code { - case "AuthorizationHeaderMalformed": + case AuthorizationHeaderMalformed: fallthrough - case "InvalidRegion": + case InvalidRegion: fallthrough - case "AccessDenied": + case AccessDenied: if errResponse.Region == "" { // Region is empty we simply return the error. return res, err diff --git a/vendor/github.com/minio/minio-go/v7/bucket-cache.go b/vendor/github.com/minio/minio-go/v7/bucket-cache.go index e43b889b98..b41902f652 100644 --- a/vendor/github.com/minio/minio-go/v7/bucket-cache.go +++ b/vendor/github.com/minio/minio-go/v7/bucket-cache.go @@ -84,18 +84,18 @@ func processBucketLocationResponse(resp *http.Response, bucketName string) (buck // request. Move forward and let the top level callers // succeed if possible based on their policy. switch errResp.Code { - case "NotImplemented": + case NotImplemented: switch errResp.Server { case "AmazonSnowball": return "snowball", nil case "cloudflare": return "us-east-1", nil } - case "AuthorizationHeaderMalformed": + case AuthorizationHeaderMalformed: fallthrough - case "InvalidRegion": + case InvalidRegion: fallthrough - case "AccessDenied": + case AccessDenied: if errResp.Region == "" { return "us-east-1", nil } diff --git a/vendor/github.com/minio/minio-go/v7/functional_tests.go b/vendor/github.com/minio/minio-go/v7/functional_tests.go index 33e87e6e16..97c6930fb9 100644 --- a/vendor/github.com/minio/minio-go/v7/functional_tests.go +++ b/vendor/github.com/minio/minio-go/v7/functional_tests.go @@ -31,6 +31,7 @@ import ( "hash" "hash/crc32" "io" + "iter" "log/slog" "math/rand" "mime/multipart" @@ -259,7 +260,7 @@ func cleanupVersionedBucket(bucketName string, c *minio.Client) error { } func isErrNotImplemented(err error) bool { - return minio.ToErrorResponse(err).Code == "NotImplemented" + return minio.ToErrorResponse(err).Code == minio.NotImplemented } func isRunOnFail() bool { @@ -465,8 +466,8 @@ func testMakeBucketError() { return } // Verify valid error response from server. - if minio.ToErrorResponse(err).Code != "BucketAlreadyExists" && - minio.ToErrorResponse(err).Code != "BucketAlreadyOwnedByYou" { + if minio.ToErrorResponse(err).Code != minio.BucketAlreadyExists && + minio.ToErrorResponse(err).Code != minio.BucketAlreadyOwnedByYou { logError(testName, function, args, startTime, "", "Invalid error returned by server", err) return } @@ -1073,7 +1074,7 @@ func testPutObjectWithVersioning() { var results []minio.ObjectInfo for info := range objectsInfo { if info.Err != nil { - logError(testName, function, args, startTime, "", "Unexpected error during listing objects", err) + logError(testName, function, args, startTime, "", "Unexpected error during listing objects", info.Err) return } results = append(results, info) @@ -3204,7 +3205,7 @@ func testGetObjectAttributesErrorCases() { } errorResponse := err.(minio.ErrorResponse) - if errorResponse.Code != "NoSuchBucket" { + if errorResponse.Code != minio.NoSuchBucket { logError(testName, function, args, startTime, "", "Invalid error code, expected NoSuchBucket but got "+errorResponse.Code, nil) return } @@ -3247,8 +3248,8 @@ func testGetObjectAttributesErrorCases() { } errorResponse = err.(minio.ErrorResponse) - if errorResponse.Code != "NoSuchKey" { - logError(testName, function, args, startTime, "", "Invalid error code, expected NoSuchKey but got "+errorResponse.Code, nil) + if errorResponse.Code != minio.NoSuchKey { + logError(testName, function, args, startTime, "", "Invalid error code, expected "+minio.NoSuchKey+" but got "+errorResponse.Code, nil) return } @@ -3272,8 +3273,8 @@ func testGetObjectAttributesErrorCases() { return } errorResponse = err.(minio.ErrorResponse) - if errorResponse.Code != "NoSuchVersion" { - logError(testName, function, args, startTime, "", "Invalid error code, expected NoSuchVersion but got "+errorResponse.Code, nil) + if errorResponse.Code != minio.NoSuchVersion { + logError(testName, function, args, startTime, "", "Invalid error code, expected "+minio.NoSuchVersion+" but got "+errorResponse.Code, nil) return } @@ -3928,10 +3929,10 @@ func testRemoveMultipleObjects() { defer cleanupBucket(bucketName, c) - r := bytes.NewReader(bytes.Repeat([]byte("a"), 8)) + r := bytes.NewReader(bytes.Repeat([]byte("a"), 1)) // Multi remove of 1100 objects - nrObjects := 200 + nrObjects := 1100 objectsCh := make(chan minio.ObjectInfo) @@ -3940,7 +3941,7 @@ func testRemoveMultipleObjects() { // Upload objects and send them to objectsCh for i := 0; i < nrObjects; i++ { objectName := "sample" + strconv.Itoa(i) + ".txt" - info, err := c.PutObject(context.Background(), bucketName, objectName, r, 8, + info, err := c.PutObject(context.Background(), bucketName, objectName, r, 1, minio.PutObjectOptions{ContentType: "application/octet-stream"}) if err != nil { logError(testName, function, args, startTime, "", "PutObject failed", err) @@ -3968,6 +3969,78 @@ func testRemoveMultipleObjects() { logSuccess(testName, function, args, startTime) } +// Test removing multiple objects with Remove API as iterator +func testRemoveMultipleObjectsIter() { + // initialize logging params + startTime := time.Now() + testName := getFuncName() + function := "RemoveObjects(bucketName, objectsCh)" + args := map[string]interface{}{ + "bucketName": "", + } + + c, err := NewClient(ClientConfig{}) + if err != nil { + logError(testName, function, args, startTime, "", "MinIO client object creation failed", err) + return + } + + // Generate a new random bucket name. + bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-") + args["bucketName"] = bucketName + + // Make a new bucket. + err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"}) + if err != nil { + logError(testName, function, args, startTime, "", "MakeBucket failed", err) + return + } + + defer cleanupBucket(bucketName, c) + + buf := []byte("a") + + // Multi remove of 1100 objects + nrObjects := 1100 + + objectsIter := func() iter.Seq[minio.ObjectInfo] { + return func(yield func(minio.ObjectInfo) bool) { + // Upload objects and send them to objectsCh + for i := 0; i < nrObjects; i++ { + objectName := "sample" + strconv.Itoa(i) + ".txt" + info, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), 1, + minio.PutObjectOptions{ContentType: "application/octet-stream"}) + if err != nil { + logError(testName, function, args, startTime, "", "PutObject failed", err) + continue + } + if !yield(minio.ObjectInfo{ + Key: info.Key, + VersionID: info.VersionID, + }) { + return + } + } + } + } + + // Call RemoveObjects API + results, err := c.RemoveObjectsWithIter(context.Background(), bucketName, objectsIter(), minio.RemoveObjectsOptions{}) + if err != nil { + logError(testName, function, args, startTime, "", "Unexpected error", err) + return + } + + for result := range results { + if result.Err != nil { + logError(testName, function, args, startTime, "", "Unexpected error", result.Err) + return + } + } + + logSuccess(testName, function, args, startTime) +} + // Test removing multiple objects and check for results func testRemoveMultipleObjectsWithResult() { // initialize logging params @@ -3997,7 +4070,7 @@ func testRemoveMultipleObjectsWithResult() { defer cleanupVersionedBucket(bucketName, c) - r := bytes.NewReader(bytes.Repeat([]byte("a"), 8)) + buf := []byte("a") nrObjects := 10 nrLockedObjects := 5 @@ -4009,7 +4082,7 @@ func testRemoveMultipleObjectsWithResult() { // Upload objects and send them to objectsCh for i := 0; i < nrObjects; i++ { objectName := "sample" + strconv.Itoa(i) + ".txt" - info, err := c.PutObject(context.Background(), bucketName, objectName, r, 8, + info, err := c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader(buf), 1, minio.PutObjectOptions{ContentType: "application/octet-stream"}) if err != nil { logError(testName, function, args, startTime, "", "PutObject failed", err) @@ -7589,7 +7662,7 @@ func testGetObjectModified() { // Confirm that a Stat() call in between doesn't change the Object's cached etag. _, err = reader.Stat() - expectedError := "At least one of the pre-conditions you specified did not hold" + expectedError := "At least one of the pre-conditions you specified did not hold." if err.Error() != expectedError { logError(testName, function, args, startTime, "", "Expected Stat to fail with error "+expectedError+", but received "+err.Error(), err) return @@ -7751,8 +7824,8 @@ func testMakeBucketErrorV2() { return } // Verify valid error response from server. - if minio.ToErrorResponse(err).Code != "BucketAlreadyExists" && - minio.ToErrorResponse(err).Code != "BucketAlreadyOwnedByYou" { + if minio.ToErrorResponse(err).Code != minio.BucketAlreadyExists && + minio.ToErrorResponse(err).Code != minio.BucketAlreadyOwnedByYou { logError(testName, function, args, startTime, "", "Invalid error returned by server", err) return } @@ -11415,6 +11488,87 @@ func testPutObject0ByteV2() { logSuccess(testName, function, args, startTime) } +// Test put object with 0 byte object with non-US-ASCII characters. +func testPutObjectMetadataNonUSASCIIV2() { + // initialize logging params + startTime := time.Now() + testName := getFuncName() + function := "PutObject(bucketName, objectName, reader, size, opts)" + args := map[string]interface{}{ + "bucketName": "", + "objectName": "", + "size": 0, + "opts": "", + } + metadata := map[string]string{ + "test-zh": "你好", + "test-ja": "こんにちは", + "test-ko": "안녕하세요", + "test-ru": "Здравствуй", + "test-de": "Hallo", + "test-it": "Ciao", + "test-pt": "Olá", + "test-ar": "مرحبا", + "test-hi": "नमस्ते", + "test-hu": "Helló", + "test-ro": "Bună", + "test-be": "Прывiтанне", + "test-sl": "Pozdravljen", + "test-sr": "Здраво", + "test-bg": "Здравейте", + "test-uk": "Привіт", + } + c, err := NewClient(ClientConfig{CredsV2: true}) + if err != nil { + logError(testName, function, args, startTime, "", "MinIO v2 client object creation failed", err) + return + } + + // Generate a new random bucket name. + bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-") + args["bucketName"] = bucketName + + // Make a new bucket. + err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1"}) + if err != nil { + logError(testName, function, args, startTime, "", "MakeBucket failed", err) + return + } + + defer cleanupBucket(bucketName, c) + + objectName := bucketName + "unique" + args["objectName"] = objectName + args["opts"] = minio.PutObjectOptions{} + + // Upload an object. + _, err = c.PutObject(context.Background(), bucketName, objectName, bytes.NewReader([]byte("")), 0, minio.PutObjectOptions{ + UserMetadata: metadata, + }) + if err != nil { + logError(testName, function, args, startTime, "", "PutObjectWithSize failed", err) + return + } + st, err := c.StatObject(context.Background(), bucketName, objectName, minio.StatObjectOptions{}) + if err != nil { + logError(testName, function, args, startTime, "", "StatObjectWithSize failed", err) + return + } + if st.Size != 0 { + logError(testName, function, args, startTime, "", "Expected upload object size 0 but got "+string(st.Size), err) + return + } + + for k, v := range metadata { + if st.Metadata.Get(http.CanonicalHeaderKey("X-Amz-Meta-"+k)) != v { + logError(testName, function, args, startTime, "", "Expected upload object metadata "+k+": "+v+" but got "+st.Metadata.Get("X-Amz-Meta-"+k), err) + return + } + } + + logSuccess(testName, function, args, startTime) +} + // Test expected error cases func testComposeObjectErrorCases() { // initialize logging params @@ -13557,6 +13711,115 @@ func testRemoveObjects() { logSuccess(testName, function, args, startTime) } +// Test deleting multiple objects with object retention set in Governance mode, via iterators +func testRemoveObjectsIter() { + // initialize logging params + startTime := time.Now() + testName := getFuncName() + function := "RemoveObjects(bucketName, objectsCh, opts)" + args := map[string]interface{}{ + "bucketName": "", + "objectPrefix": "", + "recursive": "true", + } + + c, err := NewClient(ClientConfig{}) + if err != nil { + logError(testName, function, args, startTime, "", "MinIO client v4 object creation failed", err) + return + } + + // Generate a new random bucket name. + bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test-") + args["bucketName"] = bucketName + objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "") + args["objectName"] = objectName + + // Make a new bucket. + err = c.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true}) + if err != nil { + logError(testName, function, args, startTime, "", "MakeBucket failed", err) + return + } + + bufSize := dataFileMap["datafile-129-MB"] + reader := getDataReader("datafile-129-MB") + defer reader.Close() + + _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{}) + if err != nil { + logError(testName, function, args, startTime, "", "Error uploading object", err) + return + } + + // Replace with smaller... + bufSize = dataFileMap["datafile-10-kB"] + reader = getDataReader("datafile-10-kB") + defer reader.Close() + + _, err = c.PutObject(context.Background(), bucketName, objectName, reader, int64(bufSize), minio.PutObjectOptions{}) + if err != nil { + logError(testName, function, args, startTime, "", "Error uploading object", err) + } + + t := time.Date(2030, time.April, 25, 14, 0, 0, 0, time.UTC) + m := minio.RetentionMode(minio.Governance) + opts := minio.PutObjectRetentionOptions{ + GovernanceBypass: false, + RetainUntilDate: &t, + Mode: &m, + } + err = c.PutObjectRetention(context.Background(), bucketName, objectName, opts) + if err != nil { + logError(testName, function, args, startTime, "", "Error setting retention", err) + return + } + + objectsIter := c.ListObjectsIter(context.Background(), bucketName, minio.ListObjectsOptions{ + WithVersions: true, + Recursive: true, + }) + results, err := c.RemoveObjectsWithIter(context.Background(), bucketName, objectsIter, minio.RemoveObjectsOptions{}) + if err != nil { + logError(testName, function, args, startTime, "", "Error sending delete request", err) + return + } + for result := range results { + if result.Err != nil { + // Error is expected here because Retention is set on the object + // and RemoveObjects is called without Bypass Governance + break + } + logError(testName, function, args, startTime, "", "Expected error during deletion", nil) + return + } + + objectsIter = c.ListObjectsIter(context.Background(), bucketName, minio.ListObjectsOptions{UseV1: true, Recursive: true}) + results, err = c.RemoveObjectsWithIter(context.Background(), bucketName, objectsIter, minio.RemoveObjectsOptions{ + GovernanceBypass: true, + }) + if err != nil { + logError(testName, function, args, startTime, "", "Error sending delete request", err) + return + } + for result := range results { + if result.Err != nil { + // Error is not expected here because Retention is set on the object + // and RemoveObjects is called with Bypass Governance + logError(testName, function, args, startTime, "", "Error detected during deletion", result.Err) + return + } + } + + // Delete all objects and buckets + if err = cleanupVersionedBucket(bucketName, c); err != nil { + logError(testName, function, args, startTime, "", "CleanupBucket failed", err) + return + } + + logSuccess(testName, function, args, startTime) +} + // Test get bucket tags func testGetBucketTagging() { // initialize logging params @@ -13585,7 +13848,7 @@ func testGetBucketTagging() { } _, err = c.GetBucketTagging(context.Background(), bucketName) - if minio.ToErrorResponse(err).Code != "NoSuchTagSet" { + if minio.ToErrorResponse(err).Code != minio.NoSuchTagSet { logError(testName, function, args, startTime, "", "Invalid error from server failed", err) return } @@ -13627,7 +13890,7 @@ func testSetBucketTagging() { } _, err = c.GetBucketTagging(context.Background(), bucketName) - if minio.ToErrorResponse(err).Code != "NoSuchTagSet" { + if minio.ToErrorResponse(err).Code != minio.NoSuchTagSet { logError(testName, function, args, startTime, "", "Invalid error from server", err) return } @@ -13699,7 +13962,7 @@ func testRemoveBucketTagging() { } _, err = c.GetBucketTagging(context.Background(), bucketName) - if minio.ToErrorResponse(err).Code != "NoSuchTagSet" { + if minio.ToErrorResponse(err).Code != minio.NoSuchTagSet { logError(testName, function, args, startTime, "", "Invalid error from server", err) return } @@ -13740,7 +14003,7 @@ func testRemoveBucketTagging() { } _, err = c.GetBucketTagging(context.Background(), bucketName) - if minio.ToErrorResponse(err).Code != "NoSuchTagSet" { + if minio.ToErrorResponse(err).Code != minio.NoSuchTagSet { logError(testName, function, args, startTime, "", "Invalid error from server", err) return } @@ -13809,6 +14072,7 @@ func main() { testPutMultipartObjectWithChecksums(false) testPutMultipartObjectWithChecksums(true) testPutObject0ByteV2() + testPutObjectMetadataNonUSASCIIV2() testPutObjectNoLengthV2() testPutObjectsUnknownV2() testGetObjectContextV2() @@ -13826,6 +14090,7 @@ func main() { testGetObjectS3Zip() testRemoveMultipleObjects() testRemoveMultipleObjectsWithResult() + testRemoveMultipleObjectsIter() testFPutObjectMultipart() testFPutObject() testGetObjectReadSeekFunctional() @@ -13852,6 +14117,7 @@ func main() { testPutObjectWithContentLanguage() testListObjects() testRemoveObjects() + testRemoveObjectsIter() testListObjectVersions() testStatObjectWithVersioning() testGetObjectWithVersioning() diff --git a/vendor/github.com/minio/minio-go/v7/pkg/replication/replication.go b/vendor/github.com/minio/minio-go/v7/pkg/replication/replication.go index 55636ad481..2f7993f4b4 100644 --- a/vendor/github.com/minio/minio-go/v7/pkg/replication/replication.go +++ b/vendor/github.com/minio/minio-go/v7/pkg/replication/replication.go @@ -730,6 +730,8 @@ type Metrics struct { Errors TimedErrStats `json:"failed,omitempty"` // Total number of entries that are queued for replication QStats InQueueMetric `json:"queued"` + // Total number of entries that have replication in progress + InProgress InProgressMetric `json:"inProgress"` // Deprecated fields // Total Pending size in bytes across targets PendingSize uint64 `json:"pendingReplicationSize,omitempty"` @@ -830,6 +832,9 @@ type InQueueMetric struct { Max QStat `json:"peak" msg:"pq"` } +// InProgressMetric holds stats for objects with replication in progress +type InProgressMetric InQueueMetric + // MetricName name of replication metric type MetricName string @@ -849,6 +854,14 @@ type WorkerStat struct { Max int32 `json:"max"` } +// TgtHealth holds health status of a target +type TgtHealth struct { + Online bool `json:"online"` + LastOnline time.Time `json:"lastOnline"` + TotalDowntime time.Duration `json:"totalDowntime"` + OfflineCount int64 `json:"offlineCount"` +} + // ReplMRFStats holds stats of MRF backlog saved to disk in the last 5 minutes // and number of entries that failed replication after 3 retries type ReplMRFStats struct { @@ -863,15 +876,18 @@ type ReplMRFStats struct { type ReplQNodeStats struct { NodeName string `json:"nodeName"` Uptime int64 `json:"uptime"` - Workers WorkerStat `json:"activeWorkers"` + Workers WorkerStat `json:"workers"` XferStats map[MetricName]XferStats `json:"transferSummary"` TgtXferStats map[string]map[MetricName]XferStats `json:"tgtTransferStats"` - QStats InQueueMetric `json:"queueStats"` - MRFStats ReplMRFStats `json:"mrfStats"` - Retries CounterSummary `json:"retries"` - Errors CounterSummary `json:"errors"` + QStats InQueueMetric `json:"queueStats"` + InProgressStats InProgressMetric `json:"progressStats"` + + MRFStats ReplMRFStats `json:"mrfStats"` + Retries CounterSummary `json:"retries"` + Errors CounterSummary `json:"errors"` + TgtHealth map[string]TgtHealth `json:"tgtHealth,omitempty"` } // CounterSummary denotes the stats counter summary @@ -918,6 +934,19 @@ func (q ReplQueueStats) qStatSummary() InQueueMetric { return m } +// inProgressSummary returns cluster level stats for objects with replication in progress +func (q ReplQueueStats) inProgressSummary() InProgressMetric { + m := InProgressMetric{} + for _, v := range q.Nodes { + m.Avg.Add(v.InProgressStats.Avg) + m.Curr.Add(v.InProgressStats.Curr) + if m.Max.Count < v.InProgressStats.Max.Count { + m.Max.Add(v.InProgressStats.Max) + } + } + return m +} + // ReplQStats holds stats for objects in replication queue type ReplQStats struct { Uptime int64 `json:"uptime"` @@ -926,7 +955,9 @@ type ReplQStats struct { XferStats map[MetricName]XferStats `json:"xferStats"` TgtXferStats map[string]map[MetricName]XferStats `json:"tgtXferStats"` - QStats InQueueMetric `json:"qStats"` + QStats InQueueMetric `json:"qStats"` + InProgressStats InProgressMetric `json:"progressStats"` + MRFStats ReplMRFStats `json:"mrfStats"` Retries CounterSummary `json:"retries"` Errors CounterSummary `json:"errors"` @@ -935,10 +966,10 @@ type ReplQStats struct { // QStats returns cluster level stats for objects in replication queue func (q ReplQueueStats) QStats() (r ReplQStats) { r.QStats = q.qStatSummary() + r.InProgressStats = q.inProgressSummary() r.XferStats = make(map[MetricName]XferStats) r.TgtXferStats = make(map[string]map[MetricName]XferStats) r.Workers = q.Workers() - for _, node := range q.Nodes { for arn := range node.TgtXferStats { xmap, ok := node.TgtXferStats[arn] diff --git a/vendor/github.com/minio/minio-go/v7/pkg/utils/peek-reader-closer.go b/vendor/github.com/minio/minio-go/v7/pkg/utils/peek-reader-closer.go new file mode 100644 index 0000000000..d6f674facc --- /dev/null +++ b/vendor/github.com/minio/minio-go/v7/pkg/utils/peek-reader-closer.go @@ -0,0 +1,73 @@ +/* + * MinIO Go Library for Amazon S3 Compatible Cloud Storage + * Copyright 2015-2025 MinIO, Inc. + * + * 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 utils + +import ( + "bytes" + "errors" + "io" +) + +// PeekReadCloser offers a way to peek a ReadCloser stream and then +// return the exact stream of the underlying ReadCloser +type PeekReadCloser struct { + io.ReadCloser + + recordMode bool + recordMaxBuf int + recordBuf *bytes.Buffer +} + +// ReplayFromStart ensures next Read() will restart to stream the +// underlying ReadCloser stream from the beginning +func (prc *PeekReadCloser) ReplayFromStart() { + prc.recordMode = false +} + +func (prc *PeekReadCloser) Read(p []byte) (int, error) { + if prc.recordMode { + if prc.recordBuf.Len() > prc.recordMaxBuf { + return 0, errors.New("maximum peek buffer exceeded") + } + n, err := prc.ReadCloser.Read(p) + prc.recordBuf.Write(p[:n]) + return n, err + } + // Replay mode + if prc.recordBuf.Len() > 0 { + pn, _ := prc.recordBuf.Read(p) + return pn, nil + } + return prc.ReadCloser.Read(p) +} + +// Close releases the record buffer memory and close the underlying ReadCloser +func (prc *PeekReadCloser) Close() error { + prc.recordBuf.Reset() + return prc.ReadCloser.Close() +} + +// NewPeekReadCloser returns a new peek reader +func NewPeekReadCloser(rc io.ReadCloser, maxBufSize int) *PeekReadCloser { + return &PeekReadCloser{ + ReadCloser: rc, + recordMode: true, // recording mode by default + recordBuf: bytes.NewBuffer(make([]byte, 0, 1024)), + recordMaxBuf: maxBufSize, + } +} diff --git a/vendor/github.com/minio/minio-go/v7/post-policy.go b/vendor/github.com/minio/minio-go/v7/post-policy.go index 26bf441b56..e2c24b60ae 100644 --- a/vendor/github.com/minio/minio-go/v7/post-policy.go +++ b/vendor/github.com/minio/minio-go/v7/post-policy.go @@ -161,7 +161,7 @@ func (p *PostPolicy) SetTagging(tagging string) error { } _, err := tags.ParseObjectXML(strings.NewReader(tagging)) if err != nil { - return errors.New("The XML you provided was not well-formed or did not validate against our published schema.") //nolint + return errors.New(s3ErrorResponseMap[MalformedXML]) //nolint } policyCond := policyCondition{ matchType: "eq", diff --git a/vendor/github.com/minio/minio-go/v7/retry.go b/vendor/github.com/minio/minio-go/v7/retry.go index b83d1b2e5d..59c7a163d4 100644 --- a/vendor/github.com/minio/minio-go/v7/retry.go +++ b/vendor/github.com/minio/minio-go/v7/retry.go @@ -104,6 +104,8 @@ var retryableS3Codes = map[string]struct{}{ "ExpiredToken": {}, "ExpiredTokenException": {}, "SlowDown": {}, + "SlowDownWrite": {}, + "SlowDownRead": {}, // Add more AWS S3 codes here. } diff --git a/vendor/github.com/minio/minio-go/v7/s3-error.go b/vendor/github.com/minio/minio-go/v7/s3-error.go index f7fad19f6a..4bcc47d80a 100644 --- a/vendor/github.com/minio/minio-go/v7/s3-error.go +++ b/vendor/github.com/minio/minio-go/v7/s3-error.go @@ -17,46 +17,100 @@ package minio +// Constants for error keys +const ( + NoSuchBucket = "NoSuchBucket" + NoSuchKey = "NoSuchKey" + NoSuchUpload = "NoSuchUpload" + AccessDenied = "AccessDenied" + Conflict = "Conflict" + PreconditionFailed = "PreconditionFailed" + InvalidArgument = "InvalidArgument" + EntityTooLarge = "EntityTooLarge" + EntityTooSmall = "EntityTooSmall" + UnexpectedEOF = "UnexpectedEOF" + APINotSupported = "APINotSupported" + InvalidRegion = "InvalidRegion" + NoSuchBucketPolicy = "NoSuchBucketPolicy" + BadDigest = "BadDigest" + IncompleteBody = "IncompleteBody" + InternalError = "InternalError" + InvalidAccessKeyID = "InvalidAccessKeyId" + InvalidBucketName = "InvalidBucketName" + InvalidDigest = "InvalidDigest" + InvalidRange = "InvalidRange" + MalformedXML = "MalformedXML" + MissingContentLength = "MissingContentLength" + MissingContentMD5 = "MissingContentMD5" + MissingRequestBodyError = "MissingRequestBodyError" + NotImplemented = "NotImplemented" + RequestTimeTooSkewed = "RequestTimeTooSkewed" + SignatureDoesNotMatch = "SignatureDoesNotMatch" + MethodNotAllowed = "MethodNotAllowed" + InvalidPart = "InvalidPart" + InvalidPartOrder = "InvalidPartOrder" + InvalidObjectState = "InvalidObjectState" + AuthorizationHeaderMalformed = "AuthorizationHeaderMalformed" + MalformedPOSTRequest = "MalformedPOSTRequest" + BucketNotEmpty = "BucketNotEmpty" + AllAccessDisabled = "AllAccessDisabled" + MalformedPolicy = "MalformedPolicy" + MissingFields = "MissingFields" + AuthorizationQueryParametersError = "AuthorizationQueryParametersError" + MalformedDate = "MalformedDate" + BucketAlreadyOwnedByYou = "BucketAlreadyOwnedByYou" + InvalidDuration = "InvalidDuration" + XAmzContentSHA256Mismatch = "XAmzContentSHA256Mismatch" + XMinioInvalidObjectName = "XMinioInvalidObjectName" + NoSuchCORSConfiguration = "NoSuchCORSConfiguration" + BucketAlreadyExists = "BucketAlreadyExists" + NoSuchVersion = "NoSuchVersion" + NoSuchTagSet = "NoSuchTagSet" + Testing = "Testing" + Success = "Success" +) + // Non exhaustive list of AWS S3 standard error responses - // http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html var s3ErrorResponseMap = map[string]string{ - "AccessDenied": "Access Denied.", - "BadDigest": "The Content-Md5 you specified did not match what we received.", - "EntityTooSmall": "Your proposed upload is smaller than the minimum allowed object size.", - "EntityTooLarge": "Your proposed upload exceeds the maximum allowed object size.", - "IncompleteBody": "You did not provide the number of bytes specified by the Content-Length HTTP header.", - "InternalError": "We encountered an internal error, please try again.", - "InvalidAccessKeyId": "The access key ID you provided does not exist in our records.", - "InvalidBucketName": "The specified bucket is not valid.", - "InvalidDigest": "The Content-Md5 you specified is not valid.", - "InvalidRange": "The requested range is not satisfiable", - "MalformedXML": "The XML you provided was not well-formed or did not validate against our published schema.", - "MissingContentLength": "You must provide the Content-Length HTTP header.", - "MissingContentMD5": "Missing required header for this request: Content-Md5.", - "MissingRequestBodyError": "Request body is empty.", - "NoSuchBucket": "The specified bucket does not exist.", - "NoSuchBucketPolicy": "The bucket policy does not exist", - "NoSuchKey": "The specified key does not exist.", - "NoSuchUpload": "The specified multipart upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.", - "NotImplemented": "A header you provided implies functionality that is not implemented", - "PreconditionFailed": "At least one of the pre-conditions you specified did not hold", - "RequestTimeTooSkewed": "The difference between the request time and the server's time is too large.", - "SignatureDoesNotMatch": "The request signature we calculated does not match the signature you provided. Check your key and signing method.", - "MethodNotAllowed": "The specified method is not allowed against this resource.", - "InvalidPart": "One or more of the specified parts could not be found.", - "InvalidPartOrder": "The list of parts was not in ascending order. The parts list must be specified in order by part number.", - "InvalidObjectState": "The operation is not valid for the current state of the object.", - "AuthorizationHeaderMalformed": "The authorization header is malformed; the region is wrong.", - "MalformedPOSTRequest": "The body of your POST request is not well-formed multipart/form-data.", - "BucketNotEmpty": "The bucket you tried to delete is not empty", - "AllAccessDisabled": "All access to this bucket has been disabled.", - "MalformedPolicy": "Policy has invalid resource.", - "MissingFields": "Missing fields in request.", - "AuthorizationQueryParametersError": "Error parsing the X-Amz-Credential parameter; the Credential is mal-formed; expecting \"/YYYYMMDD/REGION/SERVICE/aws4_request\".", - "MalformedDate": "Invalid date format header, expected to be in ISO8601, RFC1123 or RFC1123Z time format.", - "BucketAlreadyOwnedByYou": "Your previous request to create the named bucket succeeded and you already own it.", - "InvalidDuration": "Duration provided in the request is invalid.", - "XAmzContentSHA256Mismatch": "The provided 'x-amz-content-sha256' header does not match what was computed.", - "NoSuchCORSConfiguration": "The specified bucket does not have a CORS configuration.", + AccessDenied: "Access Denied.", + BadDigest: "The Content-Md5 you specified did not match what we received.", + EntityTooSmall: "Your proposed upload is smaller than the minimum allowed object size.", + EntityTooLarge: "Your proposed upload exceeds the maximum allowed object size.", + IncompleteBody: "You did not provide the number of bytes specified by the Content-Length HTTP header.", + InternalError: "We encountered an internal error, please try again.", + InvalidAccessKeyID: "The access key ID you provided does not exist in our records.", + InvalidBucketName: "The specified bucket is not valid.", + InvalidDigest: "The Content-Md5 you specified is not valid.", + InvalidRange: "The requested range is not satisfiable.", + MalformedXML: "The XML you provided was not well-formed or did not validate against our published schema.", + MissingContentLength: "You must provide the Content-Length HTTP header.", + MissingContentMD5: "Missing required header for this request: Content-Md5.", + MissingRequestBodyError: "Request body is empty.", + NoSuchBucket: "The specified bucket does not exist.", + NoSuchBucketPolicy: "The bucket policy does not exist.", + NoSuchKey: "The specified key does not exist.", + NoSuchUpload: "The specified multipart upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.", + NotImplemented: "A header you provided implies functionality that is not implemented.", + PreconditionFailed: "At least one of the pre-conditions you specified did not hold.", + RequestTimeTooSkewed: "The difference between the request time and the server's time is too large.", + SignatureDoesNotMatch: "The request signature we calculated does not match the signature you provided. Check your key and signing method.", + MethodNotAllowed: "The specified method is not allowed against this resource.", + InvalidPart: "One or more of the specified parts could not be found.", + InvalidPartOrder: "The list of parts was not in ascending order. The parts list must be specified in order by part number.", + InvalidObjectState: "The operation is not valid for the current state of the object.", + AuthorizationHeaderMalformed: "The authorization header is malformed; the region is wrong.", + MalformedPOSTRequest: "The body of your POST request is not well-formed multipart/form-data.", + BucketNotEmpty: "The bucket you tried to delete is not empty.", + AllAccessDisabled: "All access to this bucket has been disabled.", + MalformedPolicy: "Policy has invalid resource.", + MissingFields: "Missing fields in request.", + AuthorizationQueryParametersError: "Error parsing the X-Amz-Credential parameter; the Credential is mal-formed; expecting \"/YYYYMMDD/REGION/SERVICE/aws4_request\".", + MalformedDate: "Invalid date format header, expected to be in ISO8601, RFC1123 or RFC1123Z time format.", + BucketAlreadyOwnedByYou: "Your previous request to create the named bucket succeeded and you already own it.", + InvalidDuration: "Duration provided in the request is invalid.", + XAmzContentSHA256Mismatch: "The provided 'x-amz-content-sha256' header does not match what was computed.", + NoSuchCORSConfiguration: "The specified bucket does not have a CORS configuration.", + Conflict: "Bucket not empty.", // Add new API errors here. } diff --git a/vendor/github.com/minio/minio-go/v7/utils.go b/vendor/github.com/minio/minio-go/v7/utils.go index 6024bfa5b2..cc96005b9b 100644 --- a/vendor/github.com/minio/minio-go/v7/utils.go +++ b/vendor/github.com/minio/minio-go/v7/utils.go @@ -30,6 +30,7 @@ import ( "hash" "io" "math/rand" + "mime" "net" "net/http" "net/url" @@ -210,6 +211,7 @@ func extractObjMetadata(header http.Header) http.Header { "X-Amz-Server-Side-Encryption", "X-Amz-Tagging-Count", "X-Amz-Meta-", + "X-Minio-Meta-", // Add new headers to be preserved. // if you add new headers here, please extend // PutObjectOptions{} to preserve them @@ -223,6 +225,16 @@ func extractObjMetadata(header http.Header) http.Header { continue } found = true + if prefix == "X-Amz-Meta-" || prefix == "X-Minio-Meta-" { + for index, val := range v { + if strings.HasPrefix(val, "=?") { + decoder := mime.WordDecoder{} + if decoded, err := decoder.DecodeHeader(val); err == nil { + v[index] = decoded + } + } + } + } break } if found { @@ -268,7 +280,7 @@ func ToObjectInfo(bucketName, objectName string, h http.Header) (ObjectInfo, err if err != nil { // Content-Length is not valid return ObjectInfo{}, ErrorResponse{ - Code: "InternalError", + Code: InternalError, Message: fmt.Sprintf("Content-Length is not an integer, failed with %v", err), BucketName: bucketName, Key: objectName, @@ -283,7 +295,7 @@ func ToObjectInfo(bucketName, objectName string, h http.Header) (ObjectInfo, err mtime, err := parseRFC7231Time(h.Get("Last-Modified")) if err != nil { return ObjectInfo{}, ErrorResponse{ - Code: "InternalError", + Code: InternalError, Message: fmt.Sprintf("Last-Modified time format is invalid, failed with %v", err), BucketName: bucketName, Key: objectName, @@ -305,7 +317,7 @@ func ToObjectInfo(bucketName, objectName string, h http.Header) (ObjectInfo, err expiry, err = parseRFC7231Time(expiryStr) if err != nil { return ObjectInfo{}, ErrorResponse{ - Code: "InternalError", + Code: InternalError, Message: fmt.Sprintf("'Expiry' is not in supported format: %v", err), BucketName: bucketName, Key: objectName, @@ -327,7 +339,7 @@ func ToObjectInfo(bucketName, objectName string, h http.Header) (ObjectInfo, err userTags, err := tags.ParseObjectTags(h.Get(amzTaggingHeader)) if err != nil { return ObjectInfo{}, ErrorResponse{ - Code: "InternalError", + Code: InternalError, } } @@ -336,7 +348,7 @@ func ToObjectInfo(bucketName, objectName string, h http.Header) (ObjectInfo, err tagCount, err = strconv.Atoi(count) if err != nil { return ObjectInfo{}, ErrorResponse{ - Code: "InternalError", + Code: InternalError, Message: fmt.Sprintf("x-amz-tagging-count is not an integer, failed with %v", err), BucketName: bucketName, Key: objectName, diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/json/json.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/json/json.go index e5f24dcf10..f17a34a02b 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/json/json.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/json/json.go @@ -485,7 +485,7 @@ func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.F if err != nil { return nil, errors.Wrap(err, "failed to list shares") } - u, err := utils.GetUser(forUser, gwc) + u, err := utils.GetUser(ctx, forUser, gwc) if err != nil { return nil, errtypes.BadRequest("user not found") } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go index ea4b346e93..12edc2fc5c 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go @@ -843,7 +843,7 @@ func (m *Manager) ListReceivedShares(ctx context.Context, filters []*collaborati if err != nil { return nil, err } - u, err := utils.GetUser(forUser, client) + u, err := utils.GetUser(ctx, forUser, client) if err != nil { return nil, errtypes.BadRequest("user not found") } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/owncloudsql/conversions.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/owncloudsql/conversions.go index 09c67036df..e2eb871163 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/owncloudsql/conversions.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/owncloudsql/conversions.go @@ -61,7 +61,7 @@ type DBShare struct { type UserConverter interface { UserNameToUserID(ctx context.Context, username string) (*userpb.UserId, error) UserIDToUserName(ctx context.Context, userid *userpb.UserId) (string, error) - GetUser(userid *userpb.UserId) (*userpb.User, error) + GetUser(ctx context.Context, userid *userpb.UserId) (*userpb.User, error) } // GatewayUserConverter converts usernames and ids using the gateway @@ -140,12 +140,12 @@ func (c *GatewayUserConverter) UserNameToUserID(ctx context.Context, username st } // GetUser gets the user -func (c *GatewayUserConverter) GetUser(userid *userpb.UserId) (*userpb.User, error) { +func (c *GatewayUserConverter) GetUser(ctx context.Context, userid *userpb.UserId) (*userpb.User, error) { gwc, err := pool.GetGatewayServiceClient(c.gwAddr) if err != nil { return nil, err } - return utils.GetUser(userid, gwc) + return utils.GetUser(ctx, userid, gwc) } func (m *mgr) formatGrantee(ctx context.Context, g *provider.Grantee) (int, string, error) { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/owncloudsql/owncloudsql.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/owncloudsql/owncloudsql.go index 8957688d8d..4f0c859acd 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/owncloudsql/owncloudsql.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/owncloudsql/owncloudsql.go @@ -339,7 +339,7 @@ func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.Filter) ( func (m *mgr) ListReceivedShares(ctx context.Context, filters []*collaboration.Filter, forUser *userpb.UserId) ([]*collaboration.ReceivedShare, error) { user := ctxpkg.ContextMustGetUser(ctx) if user.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE { - u, err := m.userConverter.GetUser(forUser) + u, err := m.userConverter.GetUser(ctx, forUser) if err != nil { return nil, errtypes.BadRequest("user not found") } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/grpc.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/grpc.go index e4ec16ab78..cc90801600 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/grpc.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/grpc.go @@ -71,14 +71,21 @@ func GetServiceUserToken(ctx context.Context, gwc gateway.GatewayAPIClient, serv } // GetUser gets the specified user -// Deprecated: Use GetUserWithContext() -func GetUser(userID *user.UserId, gwc gateway.GatewayAPIClient) (*user.User, error) { - return GetUserWithContext(context.Background(), userID, gwc) +func GetUser(ctx context.Context, userID *user.UserId, gwc gateway.GatewayAPIClient) (*user.User, error) { + return getUser(ctx, userID, false, gwc) } -// GetUserWithContext gets the specified user -func GetUserWithContext(ctx context.Context, userID *user.UserId, gwc gateway.GatewayAPIClient) (*user.User, error) { - getUserResponse, err := gwc.GetUser(ctx, &user.GetUserRequest{UserId: userID}) +// GetUserNoGroups gets the specified user without expanding groupmemberships +func GetUserNoGroups(ctx context.Context, userID *user.UserId, gwc gateway.GatewayAPIClient) (*user.User, error) { + return getUser(ctx, userID, true, gwc) +} + +// getUser gets the specified user +func getUser(ctx context.Context, userID *user.UserId, skipGroups bool, gwc gateway.GatewayAPIClient) (*user.User, error) { + getUserResponse, err := gwc.GetUser(ctx, &user.GetUserRequest{ + UserId: userID, + SkipFetchingUserGroups: skipGroups, + }) if err != nil { return nil, err } diff --git a/vendor/modules.txt b/vendor/modules.txt index 467b889abb..654f2c55d9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -275,7 +275,7 @@ github.com/cenkalti/backoff # github.com/cenkalti/backoff/v5 v5.0.2 ## explicit; go 1.23 github.com/cenkalti/backoff/v5 -# github.com/ceph/go-ceph v0.33.0 +# github.com/ceph/go-ceph v0.34.0 ## explicit; go 1.23.0 github.com/ceph/go-ceph/cephfs github.com/ceph/go-ceph/cephfs/admin @@ -294,8 +294,8 @@ github.com/cespare/xxhash/v2 # github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 ## explicit github.com/cevaris/ordered_map -# github.com/cloudflare/circl v1.3.7 -## explicit; go 1.19 +# github.com/cloudflare/circl v1.6.1 +## explicit; go 1.22.0 github.com/cloudflare/circl/dh/x25519 github.com/cloudflare/circl/dh/x448 github.com/cloudflare/circl/ecc/goldilocks @@ -946,7 +946,7 @@ github.com/minio/highwayhash # github.com/minio/md5-simd v1.1.2 ## explicit; go 1.14 github.com/minio/md5-simd -# github.com/minio/minio-go/v7 v7.0.92 +# github.com/minio/minio-go/v7 v7.0.93 ## explicit; go 1.23.0 github.com/minio/minio-go/v7 github.com/minio/minio-go/v7/internal/json @@ -963,6 +963,7 @@ github.com/minio/minio-go/v7/pkg/signer github.com/minio/minio-go/v7/pkg/singleflight github.com/minio/minio-go/v7/pkg/sse github.com/minio/minio-go/v7/pkg/tags +github.com/minio/minio-go/v7/pkg/utils # github.com/mitchellh/copystructure v1.2.0 ## explicit; go 1.15 github.com/mitchellh/copystructure @@ -1209,7 +1210,7 @@ github.com/open-policy-agent/opa/v1/version # github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250603072916-fa601fb14450 ## explicit; go 1.18 github.com/opencloud-eu/libre-graph-api-go -# github.com/opencloud-eu/reva/v2 v2.33.1 +# github.com/opencloud-eu/reva/v2 v2.33.2-0.20250612080213-85468735dbd6 ## explicit; go 1.24.1 github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace github.com/opencloud-eu/reva/v2/cmd/revad/runtime