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