github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/inode/payload.go (about)

     1  // Copyright (c) 2015-2021, NVIDIA CORPORATION.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package inode
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  
    10  	"github.com/swiftstack/ProxyFS/evtlog"
    11  	"github.com/swiftstack/ProxyFS/logger"
    12  	"github.com/swiftstack/ProxyFS/stats"
    13  
    14  	"github.com/swiftstack/cstruct"
    15  	"github.com/swiftstack/sortedmap"
    16  )
    17  
    18  // For DirType inodes, our payload tree-map is from basenames to inode numbers.
    19  // For FileType inodes, our payload tree-map is from file offsets to file extents.
    20  
    21  // The signature of the pack/unpack methods says that failure is not an option
    22  // (which is OK because it's reasonable to expect a well-defined data structure to
    23  // have a well-defined serialization/deserialization), which is why our unpack
    24  // methods have so many panic codepaths (that we expect to never run).
    25  
    26  type treeNodeLoadable struct {
    27  	inode *inMemoryInodeStruct
    28  }
    29  
    30  func (tnl *treeNodeLoadable) GetNode(objectNumber uint64, objectOffset uint64, objectLength uint64) (nodeByteSlice []byte, err error) {
    31  	var (
    32  		inodeNumberAdordnedWithSnapShotID uint64
    33  		objectNumberAdordedWithSnapShotID uint64
    34  	)
    35  
    36  	inodeNumberAdordnedWithSnapShotID = tnl.inode.volume.headhunterVolumeHandle.SnapShotIDAndNonceEncode(tnl.inode.snapShotID, uint64(tnl.inode.InodeNumber))
    37  	objectNumberAdordedWithSnapShotID = tnl.inode.volume.headhunterVolumeHandle.SnapShotIDAndNonceEncode(tnl.inode.snapShotID, objectNumber)
    38  
    39  	if 0 != objectOffset {
    40  		err = fmt.Errorf("*treeNodeLoadable.GetNode(): Unexpected (non-zero) objectOffset (%v)", objectOffset)
    41  		return
    42  	}
    43  
    44  	stats.IncrementOperations(&stats.DirFileBPlusTreeNodeFaults)
    45  	evtlog.Record(evtlog.FormatDirFileBPlusTreeNodeFault, tnl.inode.volume.volumeName, inodeNumberAdordnedWithSnapShotID, objectNumberAdordedWithSnapShotID)
    46  
    47  	nodeByteSlice, err = tnl.inode.volume.headhunterVolumeHandle.GetBPlusTreeObject(objectNumberAdordedWithSnapShotID)
    48  
    49  	if (nil == err) && (uint64(len(nodeByteSlice)) != objectLength) {
    50  		err = fmt.Errorf("*treeNodeLoadable.GetNode(): Requested objectLength (%v) != Actual objectLength (%v)", objectLength, len(nodeByteSlice))
    51  		return
    52  	}
    53  
    54  	return
    55  }
    56  
    57  func (tnl *treeNodeLoadable) PutNode(nodeByteSlice []byte) (objectNumber uint64, objectOffset uint64, err error) {
    58  	objectNumber = tnl.inode.volume.headhunterVolumeHandle.FetchNonce()
    59  
    60  	objectOffset = 0
    61  
    62  	err = tnl.inode.volume.headhunterVolumeHandle.PutBPlusTreeObject(objectNumber, nodeByteSlice)
    63  
    64  	return
    65  }
    66  
    67  func (tnl *treeNodeLoadable) DiscardNode(objectNumber uint64, objectOffset uint64, objectLength uint64) (err error) {
    68  	logger.Tracef("inode.Discardnode(): volume '%s' inode %d: "+
    69  		"root Object %016X  discarding Object %016X  len %d",
    70  		tnl.inode.volume.volumeName, tnl.inode.onDiskInodeV1Struct.InodeNumber,
    71  		tnl.inode.onDiskInodeV1Struct.PayloadObjectNumber,
    72  		objectNumber, objectLength)
    73  
    74  	if 0 != objectOffset {
    75  		err = fmt.Errorf("*treeNodeLoadable.DiscardNode(): Unexpected (non-zero) objectOffset (%v)", objectOffset)
    76  		return
    77  	}
    78  
    79  	// Tell Headhunter we are done with this persisted node
    80  
    81  	err = tnl.inode.volume.headhunterVolumeHandle.DeleteBPlusTreeObject(objectNumber)
    82  
    83  	return
    84  }
    85  
    86  type fileInodeCallbacks struct {
    87  	treeNodeLoadable
    88  }
    89  
    90  func (c *fileInodeCallbacks) DumpKey(key sortedmap.Key) (keyAsString string, err error) {
    91  	keyAsUint64, ok := key.(uint64)
    92  	if !ok {
    93  		err = fmt.Errorf("fileInodeCallbacks.DumpKey() could not parse key as a uint64")
    94  		return
    95  	}
    96  
    97  	keyAsString = fmt.Sprintf("0x%016X", keyAsUint64)
    98  
    99  	err = nil
   100  	return
   101  }
   102  
   103  func (c *fileInodeCallbacks) DumpValue(value sortedmap.Value) (valueAsString string, err error) {
   104  	valueAsFileExtentPtr, ok := value.(*fileExtentStruct)
   105  	if !ok {
   106  		err = fmt.Errorf("fileInodeCallbacks.DumpValue() could not parse key as a *fileExtent")
   107  		return
   108  	}
   109  
   110  	valueAsString = fmt.Sprintf("@%p: %#v", valueAsFileExtentPtr, valueAsFileExtentPtr)
   111  
   112  	err = nil
   113  	return
   114  }
   115  
   116  func (c *fileInodeCallbacks) PackKey(key sortedmap.Key) (packedKey []byte, err error) {
   117  	packedKey, err = cstruct.Pack(key, sortedmap.OnDiskByteOrder)
   118  	return
   119  }
   120  
   121  func (c *fileInodeCallbacks) PackValue(value sortedmap.Value) (packedValue []byte, err error) {
   122  	fileExtent, ok := value.(*fileExtentStruct)
   123  	if !ok {
   124  		err = fmt.Errorf("PackValue() arg is not a *fileExtentStruct")
   125  		return
   126  	}
   127  	packedValue, err = cstruct.Pack(fileExtent, sortedmap.OnDiskByteOrder)
   128  	if nil != err {
   129  		return
   130  	}
   131  	if uint64(len(packedValue)) != globals.fileExtentStructSize {
   132  		err = fmt.Errorf("PackValue() should have produced len(packedValue) == %v", globals.fileExtentStructSize)
   133  	}
   134  	return
   135  }
   136  
   137  func (c *fileInodeCallbacks) UnpackKey(payloadData []byte) (key sortedmap.Key, bytesConsumed uint64, err error) {
   138  	var fileOffset uint64
   139  	bytesConsumed, err = cstruct.Unpack(payloadData, &fileOffset, sortedmap.OnDiskByteOrder)
   140  	if nil != err {
   141  		return
   142  	}
   143  	key = fileOffset
   144  	return
   145  }
   146  
   147  func (c *fileInodeCallbacks) UnpackValue(payloadData []byte) (value sortedmap.Value, bytesConsumed uint64, err error) {
   148  	if uint64(len(payloadData)) < globals.fileExtentStructSize {
   149  		err = fmt.Errorf("UnpackValue() arg not big enough to encode fileExtentStruct")
   150  		return
   151  	}
   152  	valueAsFileExtentPtr := &fileExtentStruct{}
   153  	_, err = cstruct.Unpack(payloadData, valueAsFileExtentPtr, sortedmap.OnDiskByteOrder)
   154  	if nil != err {
   155  		return
   156  	}
   157  	value = valueAsFileExtentPtr
   158  	bytesConsumed = globals.fileExtentStructSize
   159  	err = nil
   160  	return
   161  }
   162  
   163  type dirInodeCallbacks struct {
   164  	treeNodeLoadable
   165  }
   166  
   167  func (c *dirInodeCallbacks) DumpKey(key sortedmap.Key) (keyAsString string, err error) {
   168  	keyAsString, ok := key.(string)
   169  
   170  	if ok {
   171  		err = nil
   172  	} else {
   173  		err = fmt.Errorf("dirInodeCallbacks.DumpKey() could not parse key as a string")
   174  	}
   175  
   176  	return
   177  }
   178  
   179  func (c *dirInodeCallbacks) DumpValue(value sortedmap.Value) (valueAsString string, err error) {
   180  	valueAsUint64, ok := value.(InodeNumber)
   181  	if !ok {
   182  		err = fmt.Errorf("dirInodeCallbacks.DumpValue() could not parse value as a uint64")
   183  		return
   184  	}
   185  
   186  	valueAsString = fmt.Sprintf("0x%016X", valueAsUint64)
   187  
   188  	err = nil
   189  	return
   190  }
   191  
   192  func (c *dirInodeCallbacks) PackKey(key sortedmap.Key) (packedKey []byte, err error) {
   193  	basename, ok := key.(string)
   194  	if !ok {
   195  		err = fmt.Errorf("PackKey() arg not a string")
   196  		return
   197  	}
   198  	packedKey = []byte(basename)
   199  	packedKey = append(packedKey, 0) // null terminator
   200  	err = nil
   201  	return
   202  }
   203  
   204  func (c *dirInodeCallbacks) PackValue(value sortedmap.Value) (packedValue []byte, err error) {
   205  	valueAsInodeNumber, ok := value.(InodeNumber)
   206  	if !ok {
   207  		err = fmt.Errorf("PackValue() arg is not an InodeNumber")
   208  		return
   209  	}
   210  	valueAsUint64 := uint64(valueAsInodeNumber)
   211  	packedValue, err = cstruct.Pack(valueAsUint64, sortedmap.OnDiskByteOrder)
   212  	return
   213  }
   214  
   215  func (c *dirInodeCallbacks) UnpackKey(payloadData []byte) (key sortedmap.Key, bytesConsumed uint64, err error) {
   216  	basenameAndRemainderBytes := bytes.SplitN(payloadData, []byte{0}, 2)
   217  	basenameBytes := basenameAndRemainderBytes[0]
   218  	basename := string(basenameBytes)
   219  	key = basename
   220  	bytesConsumed = uint64(len(basenameBytes) + 1)
   221  	err = nil
   222  	return
   223  }
   224  
   225  func (c *dirInodeCallbacks) UnpackValue(payloadData []byte) (value sortedmap.Value, bytesConsumed uint64, err error) {
   226  	var valueAsUint64 uint64
   227  	bytesConsumed, err = cstruct.Unpack(payloadData, &valueAsUint64, sortedmap.OnDiskByteOrder)
   228  	if nil != err {
   229  		return
   230  	}
   231  	value = InodeNumber(valueAsUint64)
   232  	bytesConsumed = 8
   233  	return
   234  }