github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/inode/validate_test.go (about) 1 // Copyright (c) 2015-2021, NVIDIA CORPORATION. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package inode 5 6 import ( 7 "strings" 8 "testing" 9 10 "github.com/swiftstack/ProxyFS/blunder" 11 "github.com/swiftstack/ProxyFS/swiftclient" 12 ) 13 14 // Helper to fetch a volume handle and create a file for a test. Return the 15 // volume handle and the file inode number. 16 func volumeAndFileInoForTest(t *testing.T) (VolumeHandle, InodeNumber) { 17 testVolumeHandle, err := FetchVolumeHandle("TestVolume") 18 if err != nil { 19 t.Fatalf("FetchVolumeHandle(\"TestVolume\") should have worked - got error: %v", err) 20 } 21 22 fileInodeNumber, err := testVolumeHandle.CreateFile(PosixModePerm, 0, 0) 23 if err != nil { 24 t.Fatalf("CreateFile() failed: %v", err) 25 } 26 27 err = testVolumeHandle.Write(fileInodeNumber, 0, []byte{0x00, 0x01, 0x02, 0x03, 0x04}, nil) 28 if err != nil { 29 t.Fatalf("Write(fileInodeNumber, 0, []byte{0x00, 0x01, 0x02, 0x03, 0x04}) failed: %v", err) 30 } 31 32 err = testVolumeHandle.Flush(fileInodeNumber, true) 33 if err != nil { 34 t.Fatalf("Flush of inode %v failed", fileInodeNumber) 35 } 36 37 return testVolumeHandle, fileInodeNumber 38 } 39 40 func TestValidate(t *testing.T) { 41 testSetup(t, false) 42 43 testVolumeHandle, fileInodeNumber := volumeAndFileInoForTest(t) 44 45 // Validation succeeds if nothing has gone wrong 46 err := testVolumeHandle.Validate(fileInodeNumber, false) 47 if err != nil { 48 t.Fatalf("Validate() failed on presumably-good inode") 49 } 50 51 // Now let's artificially make something go wrong and confirm that 52 // Validate() catches it! 53 54 testVolume := testVolumeHandle.(*volumeStruct) 55 56 // Grab the InodeRec 57 inodeRec, ok, err := testVolume.headhunterVolumeHandle.GetInodeRec(uint64(fileInodeNumber)) 58 if err != nil || !ok { 59 t.Fatalf("failed to fetch InodeRec for inode 0x%016X", fileInodeNumber) 60 } 61 62 // Corrupt the non-preamble part 63 for i := len(globals.inodeRecDefaultPreambleBuf); i < len(inodeRec); i++ { 64 inodeRec[i] = 0xff 65 } 66 67 // And put it back 68 err = testVolume.headhunterVolumeHandle.PutInodeRec(uint64(fileInodeNumber), inodeRec) 69 if err != nil { 70 t.Fatalf("failed to save corrupted InodeRec for inode 0x%016X", fileInodeNumber) 71 } 72 73 // Now remove fileInodeNumber from inodeCache 74 fileInode, ok, err := testVolume.inodeCacheFetch(fileInodeNumber) 75 if err != nil { 76 t.Fatalf("inodeCacheFetch(fileInodeNumber) failed: %v", err) 77 } 78 if ok { 79 ok, err = testVolume.inodeCacheDrop(fileInode) 80 if err != nil { 81 t.Fatalf("inodeCacheDrop(fileInode) failed: %v", err) 82 } 83 if !ok { 84 t.Fatalf("inodeCacheDrop(fileInode) returned !ok") 85 } 86 } 87 88 // Try to Validate, observe that it fails 89 validationErr := testVolumeHandle.Validate(fileInodeNumber, false) 90 if validationErr == nil { 91 t.Fatalf("expected validation to fail") 92 } 93 if blunder.IsNot(validationErr, blunder.CorruptInodeError) { 94 t.Fatalf("expected validation error %q to have error value %v, actual value was %v", validationErr, blunder.CorruptInodeError, blunder.FsError(blunder.Errno(validationErr)).String()) 95 } 96 97 // Try to fetch from disk, observe that corruption was marked in the headhunter database 98 _, ok, corruptionErr := testVolume.fetchOnDiskInode(fileInodeNumber) 99 if corruptionErr == nil && ok { 100 t.Fatalf("expected not to get inode pointer when fetching presumptively corrupt inode %v", fileInodeNumber) 101 } 102 103 testTeardown(t) 104 } 105 106 func TestValidateFileExtents(t *testing.T) { 107 testSetup(t, false) 108 109 testVolumeHandle, fileInodeNumber := volumeAndFileInoForTest(t) 110 111 // then let's write some more data into the file so that there will be more segments to verify 112 testVolumeHandle.Write(fileInodeNumber, 2, []byte{0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06}, nil) 113 testVolumeHandle.Flush(fileInodeNumber, true) 114 testVolumeHandle.Write(fileInodeNumber, 12, []byte{0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07}, nil) 115 testVolumeHandle.Write(fileInodeNumber, 22, []byte{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, nil) 116 testVolumeHandle.Flush(fileInodeNumber, true) 117 testVolumeHandle.Write(fileInodeNumber, 32, []byte{0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09}, nil) 118 testVolumeHandle.Write(fileInodeNumber, 39, []byte{0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A}, nil) 119 testVolumeHandle.Write(fileInodeNumber, 48, []byte{0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B}, nil) 120 testVolumeHandle.Flush(fileInodeNumber, false) 121 122 err := testVolumeHandle.Validate(fileInodeNumber, true) 123 if err != nil { 124 t.Fatalf("Validate() failed on presumably-good inode") 125 } 126 127 // let's get a read plan and then sneakily sabotage the log segments 128 var zero uint64 129 readPlan, err := testVolumeHandle.GetReadPlan(fileInodeNumber, &zero, nil) 130 if err != nil { 131 t.Fatalf("failed to get read plan for inode %v", fileInodeNumber) 132 } 133 134 // go delete the LogSegment referenced in the 1st readPlan step (that must exist) 135 136 if 0 == len(readPlan) { 137 t.Fatalf("readPlan should have contained at least one entry") 138 } 139 140 readPlanStep := readPlan[0] 141 142 deleteErr := swiftclient.ObjectDelete(readPlanStep.AccountName, readPlanStep.ContainerName, readPlanStep.ObjectName, 0) 143 if nil != deleteErr { 144 t.Fatalf("HTTP DELETE %v should have worked... failed: %v", readPlanStep.ObjectPath, deleteErr) 145 } 146 147 err = testVolumeHandle.Validate(fileInodeNumber, true) 148 if err == nil { 149 t.Fatalf("expected validation to fail!") 150 } 151 // TODO: validate error type more rigorously with blunder &c. 152 if !strings.Contains(err.Error(), "returned HTTP StatusCode 404") { 153 t.Fatalf("expected error to contain 'returned HTTP StatusCode 404'") 154 } 155 156 testTeardown(t) 157 }