mirror of
https://github.com/pgsty/minio.git
synced 2026-07-20 12:40:24 +03:00
fix: CVE-2026-42600 remove ReadMultiple storage-REST API
The internode storage-REST ReadMultiple endpoint (/rmpl) joined attacker-controlled Bucket/Prefix/Files into a filesystem path with no validation, letting a peer with internode credentials read files outside the drive root (GHSA-xh8f-g2qw-gcm7). ReadMultiple has had no production caller since upstream #20390 removed the last one (listParts) in Sep 2024; multipart now uses ReadParts (/rps). Following the upstream fix, remove the whole API instead of validating paths: route constant and registration, server handler, REST client wrapper, the StorageAPI/xlStorage/xlStorageDiskIDCheck methods, the storageMetricReadMultiple metric, and the ReadMultipleReq/Resp datatypes. Regenerated the msgp and stringer outputs; storageRESTVersion stays at v63. Co-authored-by: Codex <codex@openai.com> Co-authored-by: Claude Code <claude-code@anthropic.com>
This commit is contained in:
@@ -324,14 +324,6 @@ func (d *naughtyDisk) StatInfoFile(ctx context.Context, volume, path string, glo
|
||||
return d.disk.StatInfoFile(ctx, volume, path, glob)
|
||||
}
|
||||
|
||||
func (d *naughtyDisk) ReadMultiple(ctx context.Context, req ReadMultipleReq, resp chan<- ReadMultipleResp) error {
|
||||
if err := d.calcError(); err != nil {
|
||||
close(resp)
|
||||
return err
|
||||
}
|
||||
return d.disk.ReadMultiple(ctx, req, resp)
|
||||
}
|
||||
|
||||
func (d *naughtyDisk) CleanAbandonedData(ctx context.Context, volume string, path string) error {
|
||||
if err := d.calcError(); err != nil {
|
||||
return err
|
||||
|
||||
@@ -396,28 +396,6 @@ func newFileInfo(object string, dataBlocks, parityBlocks int) (fi FileInfo) {
|
||||
return fi
|
||||
}
|
||||
|
||||
// ReadMultipleReq contains information of multiple files to read from disk.
|
||||
type ReadMultipleReq struct {
|
||||
Bucket string `msg:"bk"` // Bucket. Can be empty if multiple buckets.
|
||||
Prefix string `msg:"pr,omitempty"` // Shared prefix of all files. Can be empty. Will be joined to filename without modification.
|
||||
Files []string `msg:"fl"` // Individual files to read.
|
||||
MaxSize int64 `msg:"ms"` // Return error if size is exceed.
|
||||
MetadataOnly bool `msg:"mo"` // Read as XL meta and truncate data.
|
||||
AbortOn404 bool `msg:"ab"` // Stop reading after first file not found.
|
||||
MaxResults int `msg:"mr"` // Stop after this many successful results. <= 0 means all.
|
||||
}
|
||||
|
||||
// ReadMultipleResp contains a single response from a ReadMultipleReq.
|
||||
type ReadMultipleResp struct {
|
||||
Bucket string `msg:"bk"` // Bucket as given by request.
|
||||
Prefix string `msg:"pr,omitempty"` // Prefix as given by request.
|
||||
File string `msg:"fl"` // File name as given in request.
|
||||
Exists bool `msg:"ex"` // Returns whether the file existed on disk.
|
||||
Error string `msg:"er,omitempty"` // Returns any error when reading.
|
||||
Data []byte `msg:"d"` // Contains all data of file.
|
||||
Modtime time.Time `msg:"m"` // Modtime of file on disk.
|
||||
}
|
||||
|
||||
// DeleteVersionHandlerParams are parameters for DeleteVersionHandler
|
||||
type DeleteVersionHandlerParams struct {
|
||||
DiskID string `msg:"id"`
|
||||
|
||||
@@ -4234,672 +4234,6 @@ func (z ReadAllHandlerParams) Msgsize() (s int) {
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *ReadMultipleReq) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
var zb0001Mask uint8 /* 1 bits */
|
||||
_ = zb0001Mask
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "bk":
|
||||
z.Bucket, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Bucket")
|
||||
return
|
||||
}
|
||||
case "pr":
|
||||
z.Prefix, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Prefix")
|
||||
return
|
||||
}
|
||||
zb0001Mask |= 0x1
|
||||
case "fl":
|
||||
var zb0002 uint32
|
||||
zb0002, err = dc.ReadArrayHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Files")
|
||||
return
|
||||
}
|
||||
if cap(z.Files) >= int(zb0002) {
|
||||
z.Files = (z.Files)[:zb0002]
|
||||
} else {
|
||||
z.Files = make([]string, zb0002)
|
||||
}
|
||||
for za0001 := range z.Files {
|
||||
z.Files[za0001], err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Files", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
case "ms":
|
||||
z.MaxSize, err = dc.ReadInt64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MaxSize")
|
||||
return
|
||||
}
|
||||
case "mo":
|
||||
z.MetadataOnly, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MetadataOnly")
|
||||
return
|
||||
}
|
||||
case "ab":
|
||||
z.AbortOn404, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "AbortOn404")
|
||||
return
|
||||
}
|
||||
case "mr":
|
||||
z.MaxResults, err = dc.ReadInt()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MaxResults")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clear omitted fields.
|
||||
if (zb0001Mask & 0x1) == 0 {
|
||||
z.Prefix = ""
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *ReadMultipleReq) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// check for omitted fields
|
||||
zb0001Len := uint32(7)
|
||||
var zb0001Mask uint8 /* 7 bits */
|
||||
_ = zb0001Mask
|
||||
if z.Prefix == "" {
|
||||
zb0001Len--
|
||||
zb0001Mask |= 0x2
|
||||
}
|
||||
// variable map header, size zb0001Len
|
||||
err = en.Append(0x80 | uint8(zb0001Len))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// skip if no fields are to be emitted
|
||||
if zb0001Len != 0 {
|
||||
// write "bk"
|
||||
err = en.Append(0xa2, 0x62, 0x6b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Bucket)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Bucket")
|
||||
return
|
||||
}
|
||||
if (zb0001Mask & 0x2) == 0 { // if not omitted
|
||||
// write "pr"
|
||||
err = en.Append(0xa2, 0x70, 0x72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Prefix)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Prefix")
|
||||
return
|
||||
}
|
||||
}
|
||||
// write "fl"
|
||||
err = en.Append(0xa2, 0x66, 0x6c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteArrayHeader(uint32(len(z.Files)))
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Files")
|
||||
return
|
||||
}
|
||||
for za0001 := range z.Files {
|
||||
err = en.WriteString(z.Files[za0001])
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Files", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
// write "ms"
|
||||
err = en.Append(0xa2, 0x6d, 0x73)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt64(z.MaxSize)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MaxSize")
|
||||
return
|
||||
}
|
||||
// write "mo"
|
||||
err = en.Append(0xa2, 0x6d, 0x6f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.MetadataOnly)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MetadataOnly")
|
||||
return
|
||||
}
|
||||
// write "ab"
|
||||
err = en.Append(0xa2, 0x61, 0x62)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.AbortOn404)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "AbortOn404")
|
||||
return
|
||||
}
|
||||
// write "mr"
|
||||
err = en.Append(0xa2, 0x6d, 0x72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteInt(z.MaxResults)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MaxResults")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *ReadMultipleReq) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// check for omitted fields
|
||||
zb0001Len := uint32(7)
|
||||
var zb0001Mask uint8 /* 7 bits */
|
||||
_ = zb0001Mask
|
||||
if z.Prefix == "" {
|
||||
zb0001Len--
|
||||
zb0001Mask |= 0x2
|
||||
}
|
||||
// variable map header, size zb0001Len
|
||||
o = append(o, 0x80|uint8(zb0001Len))
|
||||
|
||||
// skip if no fields are to be emitted
|
||||
if zb0001Len != 0 {
|
||||
// string "bk"
|
||||
o = append(o, 0xa2, 0x62, 0x6b)
|
||||
o = msgp.AppendString(o, z.Bucket)
|
||||
if (zb0001Mask & 0x2) == 0 { // if not omitted
|
||||
// string "pr"
|
||||
o = append(o, 0xa2, 0x70, 0x72)
|
||||
o = msgp.AppendString(o, z.Prefix)
|
||||
}
|
||||
// string "fl"
|
||||
o = append(o, 0xa2, 0x66, 0x6c)
|
||||
o = msgp.AppendArrayHeader(o, uint32(len(z.Files)))
|
||||
for za0001 := range z.Files {
|
||||
o = msgp.AppendString(o, z.Files[za0001])
|
||||
}
|
||||
// string "ms"
|
||||
o = append(o, 0xa2, 0x6d, 0x73)
|
||||
o = msgp.AppendInt64(o, z.MaxSize)
|
||||
// string "mo"
|
||||
o = append(o, 0xa2, 0x6d, 0x6f)
|
||||
o = msgp.AppendBool(o, z.MetadataOnly)
|
||||
// string "ab"
|
||||
o = append(o, 0xa2, 0x61, 0x62)
|
||||
o = msgp.AppendBool(o, z.AbortOn404)
|
||||
// string "mr"
|
||||
o = append(o, 0xa2, 0x6d, 0x72)
|
||||
o = msgp.AppendInt(o, z.MaxResults)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *ReadMultipleReq) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
var zb0001Mask uint8 /* 1 bits */
|
||||
_ = zb0001Mask
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "bk":
|
||||
z.Bucket, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Bucket")
|
||||
return
|
||||
}
|
||||
case "pr":
|
||||
z.Prefix, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Prefix")
|
||||
return
|
||||
}
|
||||
zb0001Mask |= 0x1
|
||||
case "fl":
|
||||
var zb0002 uint32
|
||||
zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Files")
|
||||
return
|
||||
}
|
||||
if cap(z.Files) >= int(zb0002) {
|
||||
z.Files = (z.Files)[:zb0002]
|
||||
} else {
|
||||
z.Files = make([]string, zb0002)
|
||||
}
|
||||
for za0001 := range z.Files {
|
||||
z.Files[za0001], bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Files", za0001)
|
||||
return
|
||||
}
|
||||
}
|
||||
case "ms":
|
||||
z.MaxSize, bts, err = msgp.ReadInt64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MaxSize")
|
||||
return
|
||||
}
|
||||
case "mo":
|
||||
z.MetadataOnly, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MetadataOnly")
|
||||
return
|
||||
}
|
||||
case "ab":
|
||||
z.AbortOn404, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "AbortOn404")
|
||||
return
|
||||
}
|
||||
case "mr":
|
||||
z.MaxResults, bts, err = msgp.ReadIntBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "MaxResults")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clear omitted fields.
|
||||
if (zb0001Mask & 0x1) == 0 {
|
||||
z.Prefix = ""
|
||||
}
|
||||
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *ReadMultipleReq) Msgsize() (s int) {
|
||||
s = 1 + 3 + msgp.StringPrefixSize + len(z.Bucket) + 3 + msgp.StringPrefixSize + len(z.Prefix) + 3 + msgp.ArrayHeaderSize
|
||||
for za0001 := range z.Files {
|
||||
s += msgp.StringPrefixSize + len(z.Files[za0001])
|
||||
}
|
||||
s += 3 + msgp.Int64Size + 3 + msgp.BoolSize + 3 + msgp.BoolSize + 3 + msgp.IntSize
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *ReadMultipleResp) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, err = dc.ReadMapHeader()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
var zb0001Mask uint8 /* 2 bits */
|
||||
_ = zb0001Mask
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, err = dc.ReadMapKeyPtr()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "bk":
|
||||
z.Bucket, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Bucket")
|
||||
return
|
||||
}
|
||||
case "pr":
|
||||
z.Prefix, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Prefix")
|
||||
return
|
||||
}
|
||||
zb0001Mask |= 0x1
|
||||
case "fl":
|
||||
z.File, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "File")
|
||||
return
|
||||
}
|
||||
case "ex":
|
||||
z.Exists, err = dc.ReadBool()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Exists")
|
||||
return
|
||||
}
|
||||
case "er":
|
||||
z.Error, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Error")
|
||||
return
|
||||
}
|
||||
zb0001Mask |= 0x2
|
||||
case "d":
|
||||
z.Data, err = dc.ReadBytes(z.Data)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Data")
|
||||
return
|
||||
}
|
||||
case "m":
|
||||
z.Modtime, err = dc.ReadTime()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Modtime")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clear omitted fields.
|
||||
if zb0001Mask != 0x3 {
|
||||
if (zb0001Mask & 0x1) == 0 {
|
||||
z.Prefix = ""
|
||||
}
|
||||
if (zb0001Mask & 0x2) == 0 {
|
||||
z.Error = ""
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *ReadMultipleResp) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// check for omitted fields
|
||||
zb0001Len := uint32(7)
|
||||
var zb0001Mask uint8 /* 7 bits */
|
||||
_ = zb0001Mask
|
||||
if z.Prefix == "" {
|
||||
zb0001Len--
|
||||
zb0001Mask |= 0x2
|
||||
}
|
||||
if z.Error == "" {
|
||||
zb0001Len--
|
||||
zb0001Mask |= 0x10
|
||||
}
|
||||
// variable map header, size zb0001Len
|
||||
err = en.Append(0x80 | uint8(zb0001Len))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// skip if no fields are to be emitted
|
||||
if zb0001Len != 0 {
|
||||
// write "bk"
|
||||
err = en.Append(0xa2, 0x62, 0x6b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Bucket)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Bucket")
|
||||
return
|
||||
}
|
||||
if (zb0001Mask & 0x2) == 0 { // if not omitted
|
||||
// write "pr"
|
||||
err = en.Append(0xa2, 0x70, 0x72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Prefix)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Prefix")
|
||||
return
|
||||
}
|
||||
}
|
||||
// write "fl"
|
||||
err = en.Append(0xa2, 0x66, 0x6c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.File)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "File")
|
||||
return
|
||||
}
|
||||
// write "ex"
|
||||
err = en.Append(0xa2, 0x65, 0x78)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBool(z.Exists)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Exists")
|
||||
return
|
||||
}
|
||||
if (zb0001Mask & 0x10) == 0 { // if not omitted
|
||||
// write "er"
|
||||
err = en.Append(0xa2, 0x65, 0x72)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteString(z.Error)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Error")
|
||||
return
|
||||
}
|
||||
}
|
||||
// write "d"
|
||||
err = en.Append(0xa1, 0x64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteBytes(z.Data)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Data")
|
||||
return
|
||||
}
|
||||
// write "m"
|
||||
err = en.Append(0xa1, 0x6d)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteTime(z.Modtime)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Modtime")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *ReadMultipleResp) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// check for omitted fields
|
||||
zb0001Len := uint32(7)
|
||||
var zb0001Mask uint8 /* 7 bits */
|
||||
_ = zb0001Mask
|
||||
if z.Prefix == "" {
|
||||
zb0001Len--
|
||||
zb0001Mask |= 0x2
|
||||
}
|
||||
if z.Error == "" {
|
||||
zb0001Len--
|
||||
zb0001Mask |= 0x10
|
||||
}
|
||||
// variable map header, size zb0001Len
|
||||
o = append(o, 0x80|uint8(zb0001Len))
|
||||
|
||||
// skip if no fields are to be emitted
|
||||
if zb0001Len != 0 {
|
||||
// string "bk"
|
||||
o = append(o, 0xa2, 0x62, 0x6b)
|
||||
o = msgp.AppendString(o, z.Bucket)
|
||||
if (zb0001Mask & 0x2) == 0 { // if not omitted
|
||||
// string "pr"
|
||||
o = append(o, 0xa2, 0x70, 0x72)
|
||||
o = msgp.AppendString(o, z.Prefix)
|
||||
}
|
||||
// string "fl"
|
||||
o = append(o, 0xa2, 0x66, 0x6c)
|
||||
o = msgp.AppendString(o, z.File)
|
||||
// string "ex"
|
||||
o = append(o, 0xa2, 0x65, 0x78)
|
||||
o = msgp.AppendBool(o, z.Exists)
|
||||
if (zb0001Mask & 0x10) == 0 { // if not omitted
|
||||
// string "er"
|
||||
o = append(o, 0xa2, 0x65, 0x72)
|
||||
o = msgp.AppendString(o, z.Error)
|
||||
}
|
||||
// string "d"
|
||||
o = append(o, 0xa1, 0x64)
|
||||
o = msgp.AppendBytes(o, z.Data)
|
||||
// string "m"
|
||||
o = append(o, 0xa1, 0x6d)
|
||||
o = msgp.AppendTime(o, z.Modtime)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *ReadMultipleResp) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
var field []byte
|
||||
_ = field
|
||||
var zb0001 uint32
|
||||
zb0001, bts, err = msgp.ReadMapHeaderBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
var zb0001Mask uint8 /* 2 bits */
|
||||
_ = zb0001Mask
|
||||
for zb0001 > 0 {
|
||||
zb0001--
|
||||
field, bts, err = msgp.ReadMapKeyZC(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
switch msgp.UnsafeString(field) {
|
||||
case "bk":
|
||||
z.Bucket, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Bucket")
|
||||
return
|
||||
}
|
||||
case "pr":
|
||||
z.Prefix, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Prefix")
|
||||
return
|
||||
}
|
||||
zb0001Mask |= 0x1
|
||||
case "fl":
|
||||
z.File, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "File")
|
||||
return
|
||||
}
|
||||
case "ex":
|
||||
z.Exists, bts, err = msgp.ReadBoolBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Exists")
|
||||
return
|
||||
}
|
||||
case "er":
|
||||
z.Error, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Error")
|
||||
return
|
||||
}
|
||||
zb0001Mask |= 0x2
|
||||
case "d":
|
||||
z.Data, bts, err = msgp.ReadBytesBytes(bts, z.Data)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Data")
|
||||
return
|
||||
}
|
||||
case "m":
|
||||
z.Modtime, bts, err = msgp.ReadTimeBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "Modtime")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clear omitted fields.
|
||||
if zb0001Mask != 0x3 {
|
||||
if (zb0001Mask & 0x1) == 0 {
|
||||
z.Prefix = ""
|
||||
}
|
||||
if (zb0001Mask & 0x2) == 0 {
|
||||
z.Error = ""
|
||||
}
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z *ReadMultipleResp) Msgsize() (s int) {
|
||||
s = 1 + 3 + msgp.StringPrefixSize + len(z.Bucket) + 3 + msgp.StringPrefixSize + len(z.Prefix) + 3 + msgp.StringPrefixSize + len(z.File) + 3 + msgp.BoolSize + 3 + msgp.StringPrefixSize + len(z.Error) + 2 + msgp.BytesPrefixSize + len(z.Data) + 2 + msgp.TimeSize
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *ReadPartsReq) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
var field []byte
|
||||
|
||||
@@ -2156,232 +2156,6 @@ func BenchmarkDecodeReadAllHandlerParams(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalReadMultipleReq(t *testing.T) {
|
||||
v := ReadMultipleReq{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsgReadMultipleReq(b *testing.B) {
|
||||
v := ReadMultipleReq{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgReadMultipleReq(b *testing.B) {
|
||||
v := ReadMultipleReq{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalReadMultipleReq(b *testing.B) {
|
||||
v := ReadMultipleReq{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodeReadMultipleReq(t *testing.T) {
|
||||
v := ReadMultipleReq{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodeReadMultipleReq Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := ReadMultipleReq{}
|
||||
err := msgp.Decode(&buf, &vn)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
msgp.Encode(&buf, &v)
|
||||
err = msgp.NewReader(&buf).Skip()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeReadMultipleReq(b *testing.B) {
|
||||
v := ReadMultipleReq{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
en := msgp.NewWriter(msgp.Nowhere)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.EncodeMsg(en)
|
||||
}
|
||||
en.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkDecodeReadMultipleReq(b *testing.B) {
|
||||
v := ReadMultipleReq{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
rd := msgp.NewEndlessReader(buf.Bytes(), b)
|
||||
dc := msgp.NewReader(rd)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := v.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalReadMultipleResp(t *testing.T) {
|
||||
v := ReadMultipleResp{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsgReadMultipleResp(b *testing.B) {
|
||||
v := ReadMultipleResp{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsgReadMultipleResp(b *testing.B) {
|
||||
v := ReadMultipleResp{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalReadMultipleResp(b *testing.B) {
|
||||
v := ReadMultipleResp{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeDecodeReadMultipleResp(t *testing.T) {
|
||||
v := ReadMultipleResp{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecodeReadMultipleResp Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := ReadMultipleResp{}
|
||||
err := msgp.Decode(&buf, &vn)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
msgp.Encode(&buf, &v)
|
||||
err = msgp.NewReader(&buf).Skip()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeReadMultipleResp(b *testing.B) {
|
||||
v := ReadMultipleResp{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
en := msgp.NewWriter(msgp.Nowhere)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
v.EncodeMsg(en)
|
||||
}
|
||||
en.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkDecodeReadMultipleResp(b *testing.B) {
|
||||
v := ReadMultipleResp{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
rd := msgp.NewEndlessReader(buf.Bytes(), b)
|
||||
dc := msgp.NewReader(rd)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := v.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalReadPartsReq(t *testing.T) {
|
||||
v := ReadPartsReq{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
|
||||
@@ -101,7 +101,6 @@ type StorageAPI interface {
|
||||
VerifyFile(ctx context.Context, volume, path string, fi FileInfo) (*CheckPartsResp, error)
|
||||
StatInfoFile(ctx context.Context, volume, path string, glob bool) (stat []StatInfo, err error)
|
||||
ReadParts(ctx context.Context, bucket string, partMetaPaths ...string) ([]*ObjectPartInfo, error)
|
||||
ReadMultiple(ctx context.Context, req ReadMultipleReq, resp chan<- ReadMultipleResp) error
|
||||
CleanAbandonedData(ctx context.Context, volume string, path string) error
|
||||
|
||||
// Write all data, syncs the data to disk.
|
||||
|
||||
@@ -912,45 +912,6 @@ func (client *storageRESTClient) StatInfoFile(ctx context.Context, volume, path
|
||||
return stat, toStorageErr(err)
|
||||
}
|
||||
|
||||
// ReadMultiple will read multiple files and send each back as response.
|
||||
// Files are read and returned in the given order.
|
||||
// The resp channel is closed before the call returns.
|
||||
// Only a canceled context or network errors returns an error.
|
||||
func (client *storageRESTClient) ReadMultiple(ctx context.Context, req ReadMultipleReq, resp chan<- ReadMultipleResp) error {
|
||||
defer xioutil.SafeClose(resp)
|
||||
body, err := req.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
respBody, err := client.call(ctx, storageRESTMethodReadMultiple, nil, bytes.NewReader(body), int64(len(body)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer xhttp.DrainBody(respBody)
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
pw.CloseWithError(waitForHTTPStream(respBody, xioutil.NewDeadlineWriter(pw, globalDriveConfig.GetMaxTimeout())))
|
||||
}()
|
||||
mr := msgp.NewReader(pr)
|
||||
defer readMsgpReaderPoolPut(mr)
|
||||
for {
|
||||
var file ReadMultipleResp
|
||||
if err := file.DecodeMsg(mr); err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
err = nil
|
||||
}
|
||||
pr.CloseWithError(err)
|
||||
return toStorageErr(err)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case resp <- file:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CleanAbandonedData will read metadata of the object on disk
|
||||
// and delete any data directories and inline data that isn't referenced in metadata.
|
||||
func (client *storageRESTClient) CleanAbandonedData(ctx context.Context, volume string, path string) error {
|
||||
|
||||
@@ -41,7 +41,6 @@ const (
|
||||
storageRESTMethodRenameFile = "/rfile"
|
||||
storageRESTMethodVerifyFile = "/vfile"
|
||||
storageRESTMethodStatInfoFile = "/sfile"
|
||||
storageRESTMethodReadMultiple = "/rmpl"
|
||||
storageRESTMethodCleanAbandoned = "/cln"
|
||||
storageRESTMethodDeleteBulk = "/dblk"
|
||||
storageRESTMethodReadParts = "/rps"
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"net/http"
|
||||
"os/user"
|
||||
"path"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -1283,48 +1282,6 @@ func (s *storageRESTServer) DeleteBulkHandler(w http.ResponseWriter, r *http.Req
|
||||
keepHTTPResponseAlive(w)(s.getStorage().DeleteBulk(r.Context(), volume, req.Paths...))
|
||||
}
|
||||
|
||||
// ReadMultiple returns multiple files
|
||||
func (s *storageRESTServer) ReadMultiple(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.IsValid(w, r) {
|
||||
return
|
||||
}
|
||||
rw := streamHTTPResponse(w)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
debug.PrintStack()
|
||||
rw.CloseWithError(fmt.Errorf("panic: %v", r))
|
||||
}
|
||||
}()
|
||||
|
||||
var req ReadMultipleReq
|
||||
mr := msgpNewReader(r.Body)
|
||||
defer readMsgpReaderPoolPut(mr)
|
||||
err := req.DecodeMsg(mr)
|
||||
if err != nil {
|
||||
rw.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
mw := msgp.NewWriter(rw)
|
||||
responses := make(chan ReadMultipleResp, len(req.Files))
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for resp := range responses {
|
||||
err := resp.EncodeMsg(mw)
|
||||
if err != nil {
|
||||
rw.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
mw.Flush()
|
||||
}
|
||||
}()
|
||||
err = s.getStorage().ReadMultiple(r.Context(), req, responses)
|
||||
wg.Wait()
|
||||
rw.CloseWithError(err)
|
||||
}
|
||||
|
||||
// globalLocalSetDrives is used for local drive as well as remote REST
|
||||
// API caller for other nodes to talk to this node.
|
||||
//
|
||||
@@ -1363,7 +1320,6 @@ func registerStorageRESTHandlers(router *mux.Router, endpointServerPools Endpoin
|
||||
subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVersions).HandlerFunc(h(server.DeleteVersionsHandler))
|
||||
subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodVerifyFile).HandlerFunc(h(server.VerifyFileHandler))
|
||||
subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodStatInfoFile).HandlerFunc(h(server.StatInfoFile))
|
||||
subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadMultiple).HandlerFunc(h(server.ReadMultiple))
|
||||
subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCleanAbandoned).HandlerFunc(h(server.CleanAbandonedDataHandler))
|
||||
subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteBulk).HandlerFunc(h(server.DeleteBulkHandler))
|
||||
subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadParts).HandlerFunc(h(server.ReadPartsHandler))
|
||||
|
||||
@@ -33,18 +33,17 @@ func _() {
|
||||
_ = x[storageMetricReadXL-22]
|
||||
_ = x[storageMetricReadAll-23]
|
||||
_ = x[storageMetricStatInfoFile-24]
|
||||
_ = x[storageMetricReadMultiple-25]
|
||||
_ = x[storageMetricDeleteAbandonedParts-26]
|
||||
_ = x[storageMetricDiskInfo-27]
|
||||
_ = x[storageMetricDeleteBulk-28]
|
||||
_ = x[storageMetricRenamePart-29]
|
||||
_ = x[storageMetricReadParts-30]
|
||||
_ = x[storageMetricLast-31]
|
||||
_ = x[storageMetricDeleteAbandonedParts-25]
|
||||
_ = x[storageMetricDiskInfo-26]
|
||||
_ = x[storageMetricDeleteBulk-27]
|
||||
_ = x[storageMetricRenamePart-28]
|
||||
_ = x[storageMetricReadParts-29]
|
||||
_ = x[storageMetricLast-30]
|
||||
}
|
||||
|
||||
const _storageMetric_name = "MakeVolBulkMakeVolListVolsStatVolDeleteVolWalkDirListDirReadFileAppendFileCreateFileReadFileStreamRenameFileRenameDataCheckPartsDeleteDeleteVersionsVerifyFileWriteAllDeleteVersionWriteMetadataUpdateMetadataReadVersionReadXLReadAllStatInfoFileReadMultipleDeleteAbandonedPartsDiskInfoDeleteBulkRenamePartReadPartsLast"
|
||||
const _storageMetric_name = "MakeVolBulkMakeVolListVolsStatVolDeleteVolWalkDirListDirReadFileAppendFileCreateFileReadFileStreamRenameFileRenameDataCheckPartsDeleteDeleteVersionsVerifyFileWriteAllDeleteVersionWriteMetadataUpdateMetadataReadVersionReadXLReadAllStatInfoFileDeleteAbandonedPartsDiskInfoDeleteBulkRenamePartReadPartsLast"
|
||||
|
||||
var _storageMetric_index = [...]uint16{0, 11, 18, 26, 33, 42, 49, 56, 64, 74, 84, 98, 108, 118, 128, 134, 148, 158, 166, 179, 192, 206, 217, 223, 230, 242, 254, 274, 282, 292, 302, 311, 315}
|
||||
var _storageMetric_index = [...]uint16{0, 11, 18, 26, 33, 42, 49, 56, 64, 74, 84, 98, 108, 118, 128, 134, 148, 158, 166, 179, 192, 206, 217, 223, 230, 242, 262, 270, 280, 290, 299, 303}
|
||||
|
||||
func (i storageMetric) String() string {
|
||||
if i >= storageMetric(len(_storageMetric_index)-1) {
|
||||
|
||||
@@ -68,7 +68,6 @@ const (
|
||||
storageMetricReadXL
|
||||
storageMetricReadAll
|
||||
storageMetricStatInfoFile
|
||||
storageMetricReadMultiple
|
||||
storageMetricDeleteAbandonedParts
|
||||
storageMetricDiskInfo
|
||||
storageMetricDeleteBulk
|
||||
@@ -723,21 +722,6 @@ func (p *xlStorageDiskIDCheck) ReadParts(ctx context.Context, volume string, par
|
||||
return p.storage.ReadParts(ctx, volume, partMetaPaths...)
|
||||
}
|
||||
|
||||
// ReadMultiple will read multiple files and send each files as response.
|
||||
// Files are read and returned in the given order.
|
||||
// The resp channel is closed before the call returns.
|
||||
// Only a canceled context will return an error.
|
||||
func (p *xlStorageDiskIDCheck) ReadMultiple(ctx context.Context, req ReadMultipleReq, resp chan<- ReadMultipleResp) (err error) {
|
||||
ctx, done, err := p.TrackDiskHealth(ctx, storageMetricReadMultiple, req.Bucket, req.Prefix)
|
||||
if err != nil {
|
||||
xioutil.SafeClose(resp)
|
||||
return err
|
||||
}
|
||||
defer done(0, &err)
|
||||
|
||||
return p.storage.ReadMultiple(ctx, req, resp)
|
||||
}
|
||||
|
||||
// CleanAbandonedData will read metadata of the object on disk
|
||||
// and delete any data directories and inline data that isn't referenced in metadata.
|
||||
func (p *xlStorageDiskIDCheck) CleanAbandonedData(ctx context.Context, volume string, path string) (err error) {
|
||||
|
||||
@@ -3187,81 +3187,6 @@ func (s *xlStorage) ReadParts(ctx context.Context, volume string, partMetaPaths
|
||||
return parts, nil
|
||||
}
|
||||
|
||||
// ReadMultiple will read multiple files and send each back as response.
|
||||
// Files are read and returned in the given order.
|
||||
// The resp channel is closed before the call returns.
|
||||
// Only a canceled context will return an error.
|
||||
func (s *xlStorage) ReadMultiple(ctx context.Context, req ReadMultipleReq, resp chan<- ReadMultipleResp) error {
|
||||
defer xioutil.SafeClose(resp)
|
||||
|
||||
volumeDir := pathJoin(s.drivePath, req.Bucket)
|
||||
found := 0
|
||||
for _, f := range req.Files {
|
||||
if contextCanceled(ctx) {
|
||||
return ctx.Err()
|
||||
}
|
||||
r := ReadMultipleResp{
|
||||
Bucket: req.Bucket,
|
||||
Prefix: req.Prefix,
|
||||
File: f,
|
||||
}
|
||||
|
||||
var data []byte
|
||||
var mt time.Time
|
||||
|
||||
fullPath := pathJoin(volumeDir, req.Prefix, f)
|
||||
w := xioutil.NewDeadlineWorker(globalDriveConfig.GetMaxTimeout())
|
||||
if err := w.Run(func() (err error) {
|
||||
if req.MetadataOnly {
|
||||
data, mt, err = s.readMetadataWithDMTime(ctx, fullPath)
|
||||
} else {
|
||||
data, mt, err = s.readAllDataWithDMTime(ctx, req.Bucket, volumeDir, fullPath)
|
||||
}
|
||||
return err
|
||||
}); err != nil {
|
||||
if !IsErr(err, errFileNotFound, errVolumeNotFound) {
|
||||
r.Exists = true
|
||||
r.Error = err.Error()
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case resp <- r:
|
||||
}
|
||||
if req.AbortOn404 && !r.Exists {
|
||||
// We stop at first file not found.
|
||||
// We have already reported the error, return nil.
|
||||
return nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
diskHealthCheckOK(ctx, nil)
|
||||
if req.MaxSize > 0 && int64(len(data)) > req.MaxSize {
|
||||
r.Exists = true
|
||||
r.Error = fmt.Sprintf("max size (%d) exceeded: %d", req.MaxSize, len(data))
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case resp <- r:
|
||||
continue
|
||||
}
|
||||
}
|
||||
found++
|
||||
r.Exists = true
|
||||
r.Data = data
|
||||
r.Modtime = mt
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case resp <- r:
|
||||
}
|
||||
if req.MaxResults > 0 && found >= req.MaxResults {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *xlStorage) StatInfoFile(ctx context.Context, volume, path string, glob bool) (stat []StatInfo, err error) {
|
||||
volumeDir, err := s.getVolDir(volume)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user