From c5ac901e8dac48d45079095a6bab04674872b28b Mon Sep 17 00:00:00 2001 From: Anis Elleuch Date: Thu, 1 Aug 2019 22:13:06 +0100 Subject: [PATCH] xl: Fix healing empty directories (#8013) After some extensive refactors, it turned out empty directories are not healed and heal status is also not reported correctly. This commit fixes it and adds the appropriate unit tests --- cmd/xl-v1-healing.go | 9 +++-- cmd/xl-v1-healing_test.go | 76 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/cmd/xl-v1-healing.go b/cmd/xl-v1-healing.go index a8df3f94d..29abbb376 100644 --- a/cmd/xl-v1-healing.go +++ b/cmd/xl-v1-healing.go @@ -501,10 +501,14 @@ func (xl xlObjects) healObjectDir(ctx context.Context, bucket, object string, dr drive = storageDisks[i].String() } switch err { + case nil: + hr.Before.Drives[i] = madmin.HealDriveInfo{State: madmin.DriveStateOk} + hr.After.Drives[i] = madmin.HealDriveInfo{State: madmin.DriveStateOk} case errDiskNotFound: hr.Before.Drives[i] = madmin.HealDriveInfo{State: madmin.DriveStateOffline} hr.After.Drives[i] = madmin.HealDriveInfo{State: madmin.DriveStateOffline} - case errVolumeNotFound: + case errVolumeNotFound, errFileNotFound: + // Bucket or prefix/directory not found hr.Before.Drives[i] = madmin.HealDriveInfo{Endpoint: drive, State: madmin.DriveStateMissing} hr.After.Drives[i] = madmin.HealDriveInfo{Endpoint: drive, State: madmin.DriveStateMissing} default: @@ -517,7 +521,8 @@ func (xl xlObjects) healObjectDir(ctx context.Context, bucket, object string, dr } for i, err := range errs { switch err { - case errVolumeNotFound: + case errVolumeNotFound, errFileNotFound: + // Bucket or prefix/directory not found merr := storageDisks[i].MakeVol(pathJoin(bucket, object)) switch merr { case nil, errVolumeExists: diff --git a/cmd/xl-v1-healing_test.go b/cmd/xl-v1-healing_test.go index 70a968f15..893932670 100644 --- a/cmd/xl-v1-healing_test.go +++ b/cmd/xl-v1-healing_test.go @@ -250,3 +250,79 @@ func TestHealObjectXL(t *testing.T) { t.Errorf("Expected %v but received %v", InsufficientReadQuorum{}, err) } } + +// Tests healing of empty directories +func TestHealEmptyDirectoryXL(t *testing.T) { + nDisks := 16 + fsDirs, err := getRandomDisks(nDisks) + if err != nil { + t.Fatal(err) + } + defer removeRoots(fsDirs) + + // Everything is fine, should return nil + obj, _, err := initObjectLayer(mustGetNewEndpointList(fsDirs...)) + if err != nil { + t.Fatal(err) + } + + bucket := "bucket" + object := "empty-dir/" + var opts ObjectOptions + + err = obj.MakeBucketWithLocation(context.Background(), bucket, "") + if err != nil { + t.Fatalf("Failed to make a bucket - %v", err) + } + + // Upload an empty directory + _, err = obj.PutObject(context.Background(), bucket, object, mustGetPutObjReader(t, bytes.NewReader([]byte{}), 0, "", ""), opts) + if err != nil { + t.Fatal(err) + } + + // Remove the object backend files from the first disk. + xl := obj.(*xlObjects) + firstDisk := xl.storageDisks[0] + err = firstDisk.DeleteFile(bucket, object) + if err != nil { + t.Fatalf("Failed to delete a file - %v", err) + } + + // Heal the object + hr, err := obj.HealObject(context.Background(), bucket, object, false, false, madmin.HealNormalScan) + if err != nil { + t.Fatalf("Failed to heal object - %v", err) + } + + // Check if the empty directory is restored in the first disk + _, err = firstDisk.StatVol(pathJoin(bucket, object)) + if err != nil { + t.Fatalf("Expected object to be present but stat failed - %v", err) + } + + // Check the state of the object in the first disk (should be missing) + if hr.Before.Drives[0].State != madmin.DriveStateMissing { + t.Fatalf("Unexpected drive state: %v", hr.Before.Drives[0].State) + } + + // Check the state of all other disks (should be ok) + for i, h := range append(hr.Before.Drives[1:], hr.After.Drives...) { + if h.State != madmin.DriveStateOk { + t.Fatalf("Unexpected drive state (%d): %v", i+1, h.State) + } + } + + // Heal the same object again + hr, err = obj.HealObject(context.Background(), bucket, object, false, false, madmin.HealNormalScan) + if err != nil { + t.Fatalf("Failed to heal object - %v", err) + } + + // Check that Before & After states are all okay + for i, h := range append(hr.Before.Drives, hr.After.Drives...) { + if h.State != madmin.DriveStateOk { + t.Fatalf("Unexpected drive state (%d): %v", i+1, h.State) + } + } +}