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