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 }