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  }