From d693431183d2c6e85831eff6e77f45376cbe306c Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Wed, 13 Oct 2021 19:49:14 -0700 Subject: [PATCH] fix: ReadFileStream should return an error when size mismatches (#13435) offset+length should match the Size() of the individual parts return 'errFileCorrupt' otherwise, to trigger healing of the individual parts do not error out prematurely when healing such bitrot's upon successful parts being written to the client. another issue this PR fixes is to not return and error to the client if we have just triggered a heal on a specific part of the object, instead continue to read all the content and let the heal happen asynchronously later. --- cmd/erasure-object.go | 17 ++++++++++++----- cmd/xl-storage.go | 7 +++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/cmd/erasure-object.go b/cmd/erasure-object.go index 61abb6438..3b5405a2f 100644 --- a/cmd/erasure-object.go +++ b/cmd/erasure-object.go @@ -313,19 +313,26 @@ func (er erasureObjects) getObjectWithFileInfo(ctx context.Context, bucket, obje // - attempt a heal to successfully heal them for future calls. if written == partLength { var scan madmin.HealScanMode - if errors.Is(err, errFileNotFound) { + switch { + case errors.Is(err, errFileNotFound): scan = madmin.HealNormalScan - logger.Info("Healing required, attempting to heal missing shards for %s", pathJoin(bucket, object, fi.VersionID)) - } else if errors.Is(err, errFileCorrupt) { + logger.Info("Healing required, triggering async heal missing shards for %s", pathJoin(bucket, object, fi.VersionID)) + case errors.Is(err, errFileCorrupt): scan = madmin.HealDeepScan - logger.Info("Healing required, attempting to heal bitrot for %s", pathJoin(bucket, object, fi.VersionID)) + logger.Info("Healing required, triggering async heal bitrot for %s", pathJoin(bucket, object, fi.VersionID)) } - if scan == madmin.HealNormalScan || scan == madmin.HealDeepScan { + switch scan { + case madmin.HealNormalScan, madmin.HealDeepScan: healOnce.Do(func() { if _, healing := er.getOnlineDisksWithHealing(); !healing { go healObject(bucket, object, fi.VersionID, scan) } }) + // Healing is triggered and we have written + // successfully the content to client for + // the specific part, we should `nil` this error + // and proceed forward, instead of throwing errors. + err = nil } } if err != nil { diff --git a/cmd/xl-storage.go b/cmd/xl-storage.go index d486c0a74..ded5c9a77 100644 --- a/cmd/xl-storage.go +++ b/cmd/xl-storage.go @@ -1454,6 +1454,13 @@ func (s *xlStorage) ReadFileStream(ctx context.Context, volume, path string, off return nil, errIsNotRegular } + if st.Size() < offset+length { + // Expected size cannot be satisfied for + // requested offset and length + file.Close() + return nil, errFileCorrupt + } + alignment := offset%xioutil.DirectioAlignSize == 0 if !alignment { if err = disk.DisableDirectIO(file); err != nil {