github.com/rohankumardubey/proxyfs@v0.0.0-20210108201508-653efa9ab00e/inode/file.go (about) 1 package inode 2 3 import ( 4 "fmt" 5 "strconv" 6 "time" 7 8 "github.com/swiftstack/sortedmap" 9 10 "github.com/swiftstack/ProxyFS/blunder" 11 "github.com/swiftstack/ProxyFS/headhunter" 12 "github.com/swiftstack/ProxyFS/logger" 13 "github.com/swiftstack/ProxyFS/stats" 14 "github.com/swiftstack/ProxyFS/utils" 15 ) 16 17 type fileExtentStruct struct { 18 FileOffset uint64 19 Length uint64 20 LogSegmentNumber uint64 21 LogSegmentOffset uint64 22 } 23 24 func (vS *volumeStruct) CreateFile(filePerm InodeMode, userID InodeUserID, groupID InodeGroupID) (fileInodeNumber InodeNumber, err error) { 25 err = enforceRWMode(false) 26 if nil != err { 27 return 28 } 29 30 fileInode, err := vS.createFileInode(filePerm, userID, groupID) 31 if err != nil { 32 return 0, err 33 } 34 return fileInode.InodeNumber, nil 35 } 36 37 // REVIEW TODO: Should one of these (and its siblings, CreateDir & CreateSymlink) be doing flush here? 38 39 func (vS *volumeStruct) createFileInode(filePerm InodeMode, userID InodeUserID, groupID InodeGroupID) (fileInode *inMemoryInodeStruct, err error) { 40 stats.IncrementOperations(&stats.FileCreateOps) 41 42 // Create file mode out of file permissions plus inode type 43 fileMode, err := determineMode(filePerm, FileType) 44 if err != nil { 45 return nil, err 46 } 47 48 fileInode, err = vS.makeInMemoryInode(FileType, fileMode, userID, groupID) 49 if err != nil { 50 return nil, err 51 } 52 53 fileInode.dirty = true 54 55 // The payload of a file inode is a B+-tree map whose keys are uint64 file 56 // offsets and whose values are `fileExtent`s. 57 58 extents := 59 sortedmap.NewBPlusTree( 60 vS.maxExtentsPerFileNode, 61 sortedmap.CompareUint64, 62 &fileInodeCallbacks{treeNodeLoadable{inode: fileInode}}, 63 globals.fileExtentMapCache) 64 65 fileInode.payload = extents 66 67 ok, err := vS.inodeCacheInsert(fileInode) 68 if nil != err { 69 return 70 } 71 if !ok { 72 err = fmt.Errorf("inodeCacheInsert(fileInode) failed") 73 return 74 } 75 76 stats.IncrementOperations(&stats.FileCreateSuccessOps) 77 return 78 } 79 80 func fileLen(extents sortedmap.BPlusTree) uint64 { 81 numExtents, err := extents.Len() 82 if nil != err { 83 panic(err) 84 } 85 if numExtents == 0 { 86 return 0 87 } 88 89 _, value, ok, err := extents.GetByIndex(numExtents - 1) 90 if nil != err { 91 panic(err) 92 } 93 if !ok { 94 panic("couldn't find last extent of nonempty file") 95 } 96 97 lastFileExtent := value.(*fileExtentStruct) 98 99 return lastFileExtent.FileOffset + lastFileExtent.Length 100 } 101 102 // Update a file inode to have a new size, zero-padding as necessary. 103 // 104 // Doesn't flush anything. 105 func setSizeInMemory(fileInode *inMemoryInodeStruct, size uint64) (err error) { 106 extents := fileInode.payload.(sortedmap.BPlusTree) 107 extentIndex, found, err := extents.BisectLeft(size) 108 if nil != err { 109 panic(err) 110 } 111 112 if !found { 113 if 0 <= extentIndex { 114 // Potentially trim preceeding extent 115 _, extentValue, ok, getByIndexErr := extents.GetByIndex(extentIndex) 116 if nil != getByIndexErr { 117 panic(getByIndexErr) 118 } 119 if !ok { 120 unexpectedErr := fmt.Errorf("unexpected extents indexing problem") 121 panic(unexpectedErr) 122 } 123 extent := extentValue.(*fileExtentStruct) 124 if (extent.FileOffset + extent.Length) > size { 125 // Yes, we need to trim the preceeding extent 126 trimSize := extent.Length - (size - extent.FileOffset) 127 extent.Length -= trimSize 128 ok, patchByIndexErr := extents.PatchByIndex(extentIndex, extent) 129 if nil != patchByIndexErr { 130 panic(patchByIndexErr) 131 } 132 if !ok { 133 unexpectedErr := fmt.Errorf("unexpected extents indexing problem") 134 panic(unexpectedErr) 135 } 136 decrementLogSegmentMapFileData(fileInode, extent.LogSegmentNumber, trimSize) 137 } 138 } 139 140 extentIndex++ // Step to next extent entry (beyond potentially truncated preceeding extent) 141 } 142 143 for { 144 _, extentValue, ok, getByIndexErr := extents.GetByIndex(extentIndex) 145 if nil != getByIndexErr { 146 panic(getByIndexErr) 147 } 148 if !ok { 149 break 150 } 151 extent := extentValue.(*fileExtentStruct) 152 decrementLogSegmentMapFileData(fileInode, extent.LogSegmentNumber, extent.Length) 153 ok, deleteByIndexErr := extents.DeleteByIndex(extentIndex) 154 if nil != deleteByIndexErr { 155 panic(deleteByIndexErr) 156 } 157 if !ok { 158 deleteByIndexErr = fmt.Errorf("extent just indexed should have been delete-able") 159 panic(deleteByIndexErr) 160 } 161 } 162 163 fileInode.dirty = true 164 fileInode.Size = size 165 166 updateTime := time.Now() 167 fileInode.ModificationTime = updateTime 168 fileInode.AttrChangeTime = updateTime 169 return 170 } 171 172 func (vS *volumeStruct) Read(fileInodeNumber InodeNumber, offset uint64, length uint64, profiler *utils.Profiler) (buf []byte, err error) { 173 var ( 174 fileInode *inMemoryInodeStruct 175 readPlan []ReadPlanStep 176 readPlanBytes uint64 177 snapShotID uint64 178 ) 179 180 fileInode, err = vS.fetchInodeType(fileInodeNumber, FileType) 181 if nil != err { 182 logger.ErrorWithError(err) 183 return 184 } 185 186 _, snapShotID, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(fileInodeNumber)) 187 188 readPlan, readPlanBytes, err = vS.getReadPlanHelper(snapShotID, fileInode, &offset, &length) 189 if nil != err { 190 logger.WarnWithError(err) 191 return 192 } 193 194 buf, err = vS.doReadPlan(fileInode, readPlan, readPlanBytes) 195 if nil != err { 196 logger.WarnWithError(err) 197 return 198 } 199 200 stats.IncrementOperationsAndBucketedBytes(stats.FileRead, uint64(len(buf))) 201 202 err = nil 203 return 204 } 205 206 func (vS *volumeStruct) GetReadPlan(fileInodeNumber InodeNumber, offset *uint64, length *uint64) (readPlan []ReadPlanStep, err error) { 207 var ( 208 readPlanBytes uint64 209 snapShotID uint64 210 ) 211 212 fileInode, err := vS.fetchInodeType(fileInodeNumber, FileType) 213 if nil != err { 214 logger.ErrorWithError(err) 215 return 216 } 217 218 if fileInode.dirty { 219 err = flush(fileInode, false) 220 if nil != err { 221 logger.ErrorWithError(err) 222 return 223 } 224 } 225 226 _, snapShotID, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(fileInodeNumber)) 227 228 readPlan, readPlanBytes, err = vS.getReadPlanHelper(snapShotID, fileInode, offset, length) 229 if nil != err { 230 logger.ErrorWithError(err) 231 return 232 } 233 234 stats.IncrementOperationsBucketedEntriesAndBucketedBytes(stats.FileReadplan, uint64(len(readPlan)), readPlanBytes) 235 return 236 } 237 238 func (vS *volumeStruct) getReadPlanHelper(snapShotID uint64, fileInode *inMemoryInodeStruct, requestedOffset *uint64, requestedLength *uint64) (readPlan []ReadPlanStep, readPlanBytes uint64, err error) { 239 var ( 240 offset uint64 241 ) 242 243 readPlan = make([]ReadPlanStep, 0) 244 245 if requestedOffset == nil && requestedLength == nil { 246 err = fmt.Errorf("requestedOffset and requestedLength cannot both be nil") 247 return 248 } else if requestedOffset == nil { 249 // Suffix request, e.g. "bytes=-10", the last 10 bytes of the file 250 if fileInode.Size > *requestedLength { 251 offset = fileInode.Size - *requestedLength 252 readPlanBytes = *requestedLength 253 } else { 254 // A suffix request for more bytes than the file has must get the whole file. 255 offset = 0 256 readPlanBytes = fileInode.Size 257 } 258 } else if requestedLength == nil { 259 // Prefix request, e.g. "bytes=25-", from byte 25 to the end 260 offset = *requestedOffset 261 readPlanBytes = fileInode.Size - *requestedOffset 262 } else { 263 offset = *requestedOffset 264 readPlanBytes = *requestedLength 265 } 266 267 if (offset + readPlanBytes) > fileInode.Size { 268 if fileInode.Size > offset { 269 readPlanBytes = fileInode.Size - offset 270 } else { 271 readPlanBytes = 0 272 } 273 } 274 275 if 0 == readPlanBytes { 276 return 277 } 278 279 extents := fileInode.payload.(sortedmap.BPlusTree) 280 281 curOffset := offset 282 terminalOffset := offset + readPlanBytes 283 284 curExtentIndex, _, err := extents.BisectLeft(offset) 285 if nil != err { 286 panic(err) 287 } 288 289 for { 290 if 0 > curExtentIndex { 291 // Skip to next (0th) extent 292 curExtentIndex++ 293 } 294 295 // Fetch curExtent 296 _, curExtentValue, ok, getByIndexErr := extents.GetByIndex(curExtentIndex) 297 if nil != getByIndexErr { 298 panic(getByIndexErr) 299 } 300 if !ok { 301 // We have reached the end of extents 302 break 303 } 304 curExtent := curExtentValue.(*fileExtentStruct) 305 306 if curExtent.FileOffset >= terminalOffset { 307 // [curOffset:terminalOffset) ends before curExtent... so we need look no further 308 break 309 } 310 311 if (curExtent.FileOffset + curExtent.Length) <= curOffset { 312 // [curOffset:terminalOffset) starts beyond curExtent... so move to the next extent 313 curExtentIndex++ 314 continue 315 } 316 317 // At this point, we know [curOffset:terminalOffset) intersects curExtent 318 319 if curOffset < curExtent.FileOffset { 320 // [curOffset:terminalOffset) starts before curExtent... so insert a zero-fill ReadPlanStep to get to curExtent 321 step := ReadPlanStep{ 322 LogSegmentNumber: 0, 323 Offset: 0, 324 Length: curExtent.FileOffset - curOffset, 325 AccountName: "", 326 ContainerName: "", 327 ObjectName: "", 328 ObjectPath: "", 329 } 330 readPlan = append(readPlan, step) 331 curOffset = curExtent.FileOffset 332 } 333 334 skipSize := uint64(0) 335 336 if curOffset > curExtent.FileOffset { 337 // [curOffset:terminalOffset) starts after curExtent... so update skipSize appropriately 338 skipSize = curOffset - curExtent.FileOffset 339 } 340 341 if terminalOffset <= (curExtent.FileOffset + curExtent.Length) { 342 // [curOffset:terminalOffset) is completed by some or all of curExtent 343 step := ReadPlanStep{ 344 LogSegmentNumber: vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(snapShotID, curExtent.LogSegmentNumber), 345 Offset: curExtent.LogSegmentOffset + skipSize, 346 Length: terminalOffset - curOffset, 347 AccountName: vS.accountName, 348 } 349 step.ContainerName, step.ObjectName, step.ObjectPath, err = vS.getObjectLocationFromLogSegmentNumber(step.LogSegmentNumber) 350 if nil != err { 351 return 352 } 353 readPlan = append(readPlan, step) 354 curOffset = terminalOffset 355 break 356 } else { 357 // [curOffset:terminalOffset) extends beyond curExtent 358 step := ReadPlanStep{ 359 LogSegmentNumber: vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(snapShotID, curExtent.LogSegmentNumber), 360 Offset: curExtent.LogSegmentOffset + skipSize, 361 Length: curExtent.Length - skipSize, 362 AccountName: vS.accountName, 363 } 364 step.ContainerName, step.ObjectName, step.ObjectPath, err = vS.getObjectLocationFromLogSegmentNumber(step.LogSegmentNumber) 365 if nil != err { 366 return 367 } 368 readPlan = append(readPlan, step) 369 curOffset += step.Length 370 curExtentIndex++ 371 } 372 } 373 374 // Append trailing zero-fill ReadPlanStep if necessary 375 376 if curOffset < terminalOffset { 377 // We need a trailing zero-fill ReadPlanStep 378 step := ReadPlanStep{ 379 LogSegmentNumber: 0, 380 Offset: 0, 381 Length: terminalOffset - curOffset, 382 AccountName: "", 383 ContainerName: "", 384 ObjectName: "", 385 ObjectPath: "", 386 } 387 388 readPlan = append(readPlan, step) 389 } 390 391 err = nil 392 return 393 } 394 395 func (vS *volumeStruct) FetchExtentMapChunk(fileInodeNumber InodeNumber, fileOffset uint64, maxEntriesFromFileOffset int64, maxEntriesBeforeFileOffset int64) (extentMapChunk *ExtentMapChunkStruct, err error) { 396 var ( 397 containerName string 398 encodedLogSegmentNumber uint64 399 extentMap sortedmap.BPlusTree 400 extentMapLen int 401 extentMapIndex int 402 extentMapIndexAtOffset int 403 extentMapIndexAtOffsetFound bool 404 extentMapIndexEnd int 405 extentMapIndexStart int 406 fileExtent *fileExtentStruct 407 fileExtentAsValue sortedmap.Value 408 fileInode *inMemoryInodeStruct 409 objectName string 410 snapShotID uint64 411 ) 412 413 // Validate args 414 415 if maxEntriesFromFileOffset < 1 { 416 err = fmt.Errorf("inode.FetchExtentMap() requires maxEntriesFromOffset (%d) >= 1", maxEntriesFromFileOffset) 417 return 418 } 419 if maxEntriesBeforeFileOffset < 0 { 420 err = fmt.Errorf("inode.FetchExtentMap() requires maxEntriesBeforeOffset (%d) >= 0", maxEntriesBeforeFileOffset) 421 return 422 } 423 424 fileInode, err = vS.fetchInodeType(fileInodeNumber, FileType) 425 if nil != err { 426 return 427 } 428 429 // Ensure in-flight LogSegments are flushed 430 431 if fileInode.dirty { 432 err = flush(fileInode, false) 433 if nil != err { 434 logger.ErrorWithError(err) 435 return 436 } 437 } 438 439 // Locate extent that either contains fileOffset, 440 // or if no extent does, that we select the one just after fileOffset 441 442 extentMap = fileInode.payload.(sortedmap.BPlusTree) 443 444 extentMapLen, err = extentMap.Len() 445 if nil != err { 446 panic(err) 447 } 448 449 if 0 == extentMapLen { 450 // In the absence of any extents, just describe entire fileInode as zero-filled 451 452 extentMapChunk = &ExtentMapChunkStruct{ 453 FileOffsetRangeStart: 0, 454 FileOffsetRangeEnd: fileInode.Size, 455 FileSize: fileInode.Size, 456 ExtentMapEntry: make([]ExtentMapEntryStruct, 0), 457 } 458 return 459 } 460 461 extentMapIndexAtOffset, extentMapIndexAtOffsetFound, err = extentMap.BisectLeft(fileOffset) 462 if nil != err { 463 panic(err) 464 } 465 466 if !extentMapIndexAtOffsetFound { 467 if extentMapIndexAtOffset >= 0 { 468 _, fileExtentAsValue, _, err = extentMap.GetByIndex(extentMapIndexAtOffset) 469 if nil != err { 470 panic(err) 471 } 472 473 fileExtent = fileExtentAsValue.(*fileExtentStruct) 474 475 if (fileExtent.FileOffset + fileExtent.Length) <= fileOffset { 476 extentMapIndexAtOffset++ 477 } 478 } else { 479 extentMapIndexAtOffset = 0 480 } 481 } 482 483 // Compute extent indices surrounding fileOffset that are also requested 484 485 if int64(extentMapIndexAtOffset) > maxEntriesBeforeFileOffset { 486 extentMapIndexStart = extentMapIndexAtOffset - int(maxEntriesBeforeFileOffset) 487 } else { 488 extentMapIndexStart = 0 489 } 490 491 if int64(extentMapLen-extentMapIndexAtOffset) <= maxEntriesFromFileOffset { 492 extentMapIndexEnd = extentMapLen - 1 493 } else { 494 extentMapIndexEnd = extentMapIndexAtOffset + int(maxEntriesFromFileOffset) - 1 495 } 496 497 // Populate extentMapChunk with selected extents 498 499 _, snapShotID, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(fileInodeNumber)) 500 501 extentMapChunk = &ExtentMapChunkStruct{ 502 FileSize: fileInode.Size, 503 ExtentMapEntry: make([]ExtentMapEntryStruct, 0, extentMapIndexEnd-extentMapIndexStart+1), 504 } 505 506 // Fill in FileOffsetRangeStart to include zero-filled (non-)extent just before first returned extent 507 508 if extentMapIndexStart > 0 { 509 _, fileExtentAsValue, _, err = extentMap.GetByIndex(extentMapIndexStart - 1) 510 if nil != err { 511 panic(err) 512 } 513 514 fileExtent = fileExtentAsValue.(*fileExtentStruct) 515 516 extentMapChunk.FileOffsetRangeStart = fileExtent.FileOffset + fileExtent.Length 517 } else { 518 extentMapChunk.FileOffsetRangeStart = 0 519 } 520 521 for extentMapIndex = extentMapIndexStart; extentMapIndex <= extentMapIndexEnd; extentMapIndex++ { 522 _, fileExtentAsValue, _, err = extentMap.GetByIndex(extentMapIndex) 523 if nil != err { 524 panic(err) 525 } 526 527 fileExtent = fileExtentAsValue.(*fileExtentStruct) 528 529 encodedLogSegmentNumber = vS.headhunterVolumeHandle.SnapShotIDAndNonceEncode(snapShotID, fileExtent.LogSegmentNumber) 530 531 containerName, objectName, _, err = vS.getObjectLocationFromLogSegmentNumber(encodedLogSegmentNumber) 532 if nil != err { 533 panic(err) 534 } 535 536 extentMapChunk.ExtentMapEntry = append(extentMapChunk.ExtentMapEntry, ExtentMapEntryStruct{ 537 FileOffset: fileExtent.FileOffset, 538 LogSegmentOffset: fileExtent.LogSegmentOffset, 539 Length: fileExtent.Length, 540 ContainerName: containerName, 541 ObjectName: objectName, 542 }) 543 } 544 545 // Fill in FileOffsetRangeEnd to included zero-filled (non-)extent just after last returned extent 546 547 if (extentMapIndexEnd + 1) == extentMapLen { 548 extentMapChunk.FileOffsetRangeEnd = fileInode.Size 549 } else { 550 _, fileExtentAsValue, _, err = extentMap.GetByIndex(extentMapIndexEnd + 1) 551 if nil != err { 552 panic(err) 553 } 554 555 fileExtent = fileExtentAsValue.(*fileExtentStruct) 556 557 extentMapChunk.FileOffsetRangeEnd = fileExtent.FileOffset 558 } 559 560 return 561 } 562 563 func incrementLogSegmentMapFileData(fileInode *inMemoryInodeStruct, logSegmentNumber uint64, incrementAmount uint64) { 564 logSegmentRecord, ok := fileInode.LogSegmentMap[logSegmentNumber] 565 if ok { 566 logSegmentRecord += incrementAmount 567 fileInode.LogSegmentMap[logSegmentNumber] = logSegmentRecord 568 } else { 569 fileInode.LogSegmentMap[logSegmentNumber] = incrementAmount 570 } 571 } 572 573 func decrementLogSegmentMapFileData(fileInode *inMemoryInodeStruct, logSegmentNumber uint64, decrementAmount uint64) { 574 logSegmentRecord, ok := fileInode.LogSegmentMap[logSegmentNumber] 575 if ok { 576 if decrementAmount > logSegmentRecord { 577 err := fmt.Errorf("Unexpected decrementLogSegmentMapFileData() call would make FileData \"go negative\"") 578 panic(err) 579 } 580 logSegmentRecord -= decrementAmount 581 fileInode.LogSegmentMap[logSegmentNumber] = logSegmentRecord 582 } else { 583 err := fmt.Errorf("Unexpected decrementLogSegmentMapFileData() call referenced non-existent logSegmentNumber") 584 panic(err) 585 } 586 } 587 588 // `recordWrite` is called by `Write` and `Wrote` to update the file inode 589 // payload's record of the extents that compose the file. 590 func recordWrite(fileInode *inMemoryInodeStruct, fileOffset uint64, length uint64, logSegmentNumber uint64, logSegmentOffset uint64) (err error) { 591 extents := fileInode.payload.(sortedmap.BPlusTree) 592 593 // First we need to eliminate extents or portions thereof that overlap the specified write 594 595 extentIndex, found, err := extents.BisectLeft(fileOffset) 596 if nil != err { 597 panic(err) 598 } 599 600 if !found { 601 if 0 <= extentIndex { 602 // Potentially split preceeding extent 603 _, extentValue, ok, getByIndexErr := extents.GetByIndex(extentIndex) 604 if nil != getByIndexErr { 605 panic(getByIndexErr) 606 } 607 if !ok { 608 unexpectedErr := fmt.Errorf("unexpected extents indexing problem") 609 panic(unexpectedErr) 610 } 611 leftExtent := extentValue.(*fileExtentStruct) 612 if (leftExtent.FileOffset + leftExtent.Length) > fileOffset { 613 // Yes, we need to split the preceeding extent 614 splitOutSize := (leftExtent.FileOffset + leftExtent.Length) - fileOffset 615 leftExtent.Length -= splitOutSize 616 ok, patchByIndexErr := extents.PatchByIndex(extentIndex, leftExtent) 617 if nil != patchByIndexErr { 618 panic(patchByIndexErr) 619 } 620 if !ok { 621 unexpectedErr := fmt.Errorf("unexpected extents indexing problem") 622 panic(unexpectedErr) 623 } 624 rightExtent := &fileExtentStruct{ 625 FileOffset: fileOffset, 626 Length: splitOutSize, 627 LogSegmentNumber: leftExtent.LogSegmentNumber, 628 LogSegmentOffset: leftExtent.LogSegmentOffset + leftExtent.Length, 629 } 630 ok, putErr := extents.Put(rightExtent.FileOffset, rightExtent) 631 if nil != putErr { 632 panic(putErr) 633 } 634 if !ok { 635 unexpectedErr := fmt.Errorf("unexpected extents key problem") 636 panic(unexpectedErr) 637 } 638 } 639 } 640 641 extentIndex++ // Step to next extent entry (beyond potentially truncated preceeding extent) 642 } 643 644 for { 645 _, extentValue, ok, getByIndexErr := extents.GetByIndex(extentIndex) 646 if nil != getByIndexErr { 647 panic(getByIndexErr) 648 } 649 if !ok { 650 // We have reached the end of extents 651 break 652 } 653 leftExtent := extentValue.(*fileExtentStruct) 654 if leftExtent.FileOffset >= (fileOffset + length) { 655 // We are done pruning extents 656 break 657 } 658 if (fileOffset + length) >= (leftExtent.FileOffset + leftExtent.Length) { 659 // This extent entirely overwritten... just delete it 660 decrementLogSegmentMapFileData(fileInode, leftExtent.LogSegmentNumber, leftExtent.Length) 661 ok, deleteByIndexErr := extents.DeleteByIndex(extentIndex) 662 if nil != deleteByIndexErr { 663 panic(deleteByIndexErr) 664 } 665 if !ok { 666 unexpectedErr := fmt.Errorf("unexpected extents indexing problem") 667 panic(unexpectedErr) 668 } 669 } else { 670 // This extent partially overwritten... trim it from the front and we will be done trimming 671 ok, deleteByIndexErr := extents.DeleteByIndex(extentIndex) 672 if nil != deleteByIndexErr { 673 panic(deleteByIndexErr) 674 } 675 if !ok { 676 unexpectedErr := fmt.Errorf("unexpected extents indexing problem") 677 panic(unexpectedErr) 678 } 679 overlapSize := (fileOffset + length) - leftExtent.FileOffset 680 rightExtent := &fileExtentStruct{ 681 FileOffset: leftExtent.FileOffset + overlapSize, 682 Length: leftExtent.Length - overlapSize, 683 LogSegmentNumber: leftExtent.LogSegmentNumber, 684 LogSegmentOffset: leftExtent.LogSegmentOffset + overlapSize, 685 } 686 ok, putErr := extents.Put(rightExtent.FileOffset, rightExtent) 687 if nil != putErr { 688 panic(putErr) 689 } 690 if !ok { 691 unexpectedErr := fmt.Errorf("unexpected extents key problem") 692 panic(unexpectedErr) 693 } 694 decrementLogSegmentMapFileData(fileInode, leftExtent.LogSegmentNumber, overlapSize) 695 break 696 } 697 } 698 699 // Now that there will be no overlap, see if we can append to the preceding fileExtent 700 701 prevIndex, found, err := extents.BisectLeft(fileOffset) 702 if nil != err { 703 panic(err) 704 } 705 if found { 706 unexpectedErr := fmt.Errorf("unexpected to find fileOffset in extents at this point)") 707 panic(unexpectedErr) 708 } 709 710 var prevExtent *fileExtentStruct 711 712 prevExtent = nil 713 714 if 0 <= prevIndex { 715 _, prevExtentValue, ok, getByIndexErr := extents.GetByIndex(prevIndex) 716 if nil != getByIndexErr { 717 panic(getByIndexErr) 718 } 719 if !ok { 720 unexpectedErr := fmt.Errorf("unexpected to not find predecessor in extents at this point") 721 panic(unexpectedErr) 722 } 723 724 prevExtent = prevExtentValue.(*fileExtentStruct) 725 } 726 727 if (nil != prevExtent) && (prevExtent.LogSegmentNumber == logSegmentNumber) && ((prevExtent.FileOffset + prevExtent.Length) == fileOffset) && ((prevExtent.LogSegmentOffset + prevExtent.Length) == logSegmentOffset) { 728 // APPEND Case: We are able to simply lengthen prevExtent 729 730 prevExtent.Length += length 731 ok, patchByIndexErr := extents.PatchByIndex(prevIndex, prevExtent) 732 if nil != patchByIndexErr { 733 panic(patchByIndexErr) 734 } 735 if !ok { 736 unexpectedErr := fmt.Errorf("unexpected to not be able to PATCH at this point") 737 panic(unexpectedErr) 738 } 739 } else { 740 // Non-APPEND Case: We need to insert a new extent 741 742 newExtent := &fileExtentStruct{ 743 FileOffset: fileOffset, 744 Length: length, 745 LogSegmentNumber: logSegmentNumber, 746 LogSegmentOffset: logSegmentOffset, 747 } 748 749 ok, putErr := extents.Put(newExtent.FileOffset, newExtent) 750 if nil != putErr { 751 panic(putErr) 752 } 753 if !ok { 754 unexpectedErr := fmt.Errorf("unexpected to not be able to PUT at this point") 755 panic(unexpectedErr) 756 } 757 } 758 759 if (fileOffset + length) > fileInode.Size { 760 fileInode.Size = fileOffset + length 761 } 762 763 incrementLogSegmentMapFileData(fileInode, logSegmentNumber, length) 764 765 return nil 766 } 767 768 func (vS *volumeStruct) Write(fileInodeNumber InodeNumber, offset uint64, buf []byte, profiler *utils.Profiler) (err error) { 769 err = enforceRWMode(true) 770 if nil != err { 771 return 772 } 773 774 snapShotIDType, _, _ := vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(fileInodeNumber)) 775 if headhunter.SnapShotIDTypeLive != snapShotIDType { 776 err = fmt.Errorf("Write() on non-LiveView fileInodeNumber not allowed") 777 return 778 } 779 780 fileInode, err := vS.fetchInodeType(fileInodeNumber, FileType) 781 if nil != err { 782 logger.ErrorWithError(err) 783 return 784 } 785 786 // writes of length 0 succeed but do not change mtime 787 if len(buf) == 0 { 788 return 789 } 790 791 fileInode.dirty = true 792 793 logSegmentNumber, logSegmentOffset, err := vS.doSendChunk(fileInode, buf) 794 if nil != err { 795 logger.ErrorWithError(err) 796 return 797 } 798 799 length := uint64(len(buf)) 800 startingSize := fileInode.Size 801 802 err = recordWrite(fileInode, offset, length, logSegmentNumber, logSegmentOffset) 803 if nil != err { 804 logger.ErrorWithError(err) 805 return 806 } 807 808 appendedBytes := fileInode.Size - startingSize 809 overwrittenBytes := length - appendedBytes 810 811 stats.IncrementOperationsBucketedBytesAndAppendedOverwritten(stats.FileWrite, length, appendedBytes, overwrittenBytes) 812 813 updateTime := time.Now() 814 fileInode.AttrChangeTime = updateTime 815 fileInode.ModificationTime = updateTime 816 fileInode.NumWrites++ 817 818 return 819 } 820 821 func (vS *volumeStruct) Wrote(fileInodeNumber InodeNumber, containerName string, objectName string, fileOffset []uint64, objectOffset []uint64, length []uint64, patchOnly bool) (err error) { 822 err = enforceRWMode(false) 823 if nil != err { 824 return 825 } 826 827 if (len(fileOffset) != len(objectOffset)) || (len(objectOffset) != len(length)) { 828 err = fmt.Errorf("Wrote() called with unequal # of fileOffset's (%d), objectOffset's (%d), and length's (%d)", len(fileOffset), len(objectOffset), len(length)) 829 return 830 } 831 832 snapShotIDType, _, _ := vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(fileInodeNumber)) 833 if headhunter.SnapShotIDTypeLive != snapShotIDType { 834 err = fmt.Errorf("Wrote() on non-LiveView fileInodeNumber not allowed") 835 return 836 } 837 838 fileInode, err := vS.fetchInodeType(fileInodeNumber, FileType) 839 if err != nil { 840 logger.ErrorWithError(err) 841 return err 842 } 843 844 if fileInode.dirty { 845 err = flush(fileInode, false) 846 if nil != err { 847 logger.ErrorWithError(err) 848 return 849 } 850 } 851 852 logSegmentNumber, err := strconv.ParseUint(objectName, 16, 64) 853 if err != nil { 854 return 855 } 856 857 err = fileInode.volume.setLogSegmentContainer(logSegmentNumber, containerName) 858 if nil != err { 859 return 860 } 861 862 fileInode.dirty = true 863 864 if !patchOnly { 865 err = setSizeInMemory(fileInode, 0) 866 if err != nil { 867 logger.ErrorWithError(err) 868 return 869 } 870 } 871 872 bytesWritten := uint64(0) 873 874 for i, thisLength := range length { 875 if 0 < thisLength { 876 err = recordWrite(fileInode, fileOffset[i], thisLength, logSegmentNumber, objectOffset[i]) 877 if err != nil { 878 logger.ErrorWithError(err) 879 return 880 } 881 882 fileInode.NumWrites++ 883 bytesWritten += thisLength 884 } 885 } 886 887 if !patchOnly { 888 // For this case only, make it appear we did precisely one write 889 890 fileInode.NumWrites = 1 891 } 892 893 err = fileInode.volume.flushInode(fileInode) 894 if err != nil { 895 logger.ErrorWithError(err) 896 return 897 } 898 899 stats.IncrementOperationsAndBucketedBytes(stats.FileWrote, bytesWritten) 900 901 return 902 } 903 904 func (vS *volumeStruct) SetSize(fileInodeNumber InodeNumber, size uint64) (err error) { 905 err = enforceRWMode(false) 906 if nil != err { 907 return 908 } 909 910 snapShotIDType, _, _ := vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(fileInodeNumber)) 911 if headhunter.SnapShotIDTypeLive != snapShotIDType { 912 err = fmt.Errorf("SetSize() on non-LiveView fileInodeNumber not allowed") 913 return 914 } 915 916 fileInode, err := vS.fetchInodeType(fileInodeNumber, FileType) 917 if nil != err { 918 return err 919 } 920 921 fileInode.dirty = true 922 923 err = setSizeInMemory(fileInode, size) 924 if nil != err { 925 logger.ErrorWithError(err) 926 return 927 } 928 929 // changing the file's size is just like a write 930 fileInode.NumWrites++ 931 932 err = fileInode.volume.flushInode(fileInode) 933 if nil != err { 934 logger.ErrorWithError(err) 935 return err 936 } 937 938 stats.IncrementOperations(&stats.DirSetsizeOps) 939 940 return 941 } 942 943 func (vS *volumeStruct) Flush(fileInodeNumber InodeNumber, andPurge bool) (err error) { 944 err = enforceRWMode(false) 945 if nil != err { 946 return 947 } 948 949 fileInode, ok, err := vS.fetchInode(fileInodeNumber) 950 if nil != err { 951 // this indicates disk corruption or software bug 952 // (err includes volume name and inode number) 953 logger.ErrorfWithError(err, "%s: request to flush inode %d volume '%s' failed", 954 utils.GetFnName(), fileInodeNumber, vS.volumeName) 955 return 956 } 957 if !ok { 958 // this can happen if background flush loses a race with unlink() 959 logger.Infof("%s: request to flush free inode %d volume '%s' ignored", 960 utils.GetFnName(), fileInodeNumber, vS.volumeName) 961 return 962 } 963 if fileInode.InodeType != FileType { 964 // this should never happen unless there's disk corruption 965 logger.Errorf("%s: request to flush inode %d volume '%s' type '%v' ignored", 966 utils.GetFnName(), fileInodeNumber, vS.volumeName, fileInode.InodeType) 967 return 968 } 969 970 if fileInode.dirty { 971 err = flush(fileInode, andPurge) 972 if nil != err { 973 logger.ErrorWithError(err) 974 return 975 } 976 } 977 978 stats.IncrementOperations(&stats.FileFlushOps) 979 980 return 981 } 982 983 func flush(fileInode *inMemoryInodeStruct, andPurge bool) (err error) { 984 vS := fileInode.volume 985 err = vS.flushInode(fileInode) 986 if nil != err { 987 logger.ErrorfWithError(err, "flushInode(fileInode) failed") 988 } 989 990 if andPurge { 991 var ok bool 992 ok, err = vS.inodeCacheDrop(fileInode) 993 if nil != err { 994 return 995 } 996 if !ok { 997 err = fmt.Errorf("inodeCacheDrop(fileInode) failed") 998 return 999 } 1000 } 1001 1002 return 1003 } 1004 1005 func (vS *volumeStruct) resetFileInodeInMemory(fileInode *inMemoryInodeStruct) (err error) { 1006 var ( 1007 fileInodeExtentMap sortedmap.BPlusTree 1008 ok bool 1009 ) 1010 1011 fileInode.dirty = true 1012 fileInodeExtentMap = fileInode.payload.(sortedmap.BPlusTree) 1013 1014 ok = true 1015 for ok { 1016 ok, err = fileInodeExtentMap.DeleteByIndex(0) 1017 if nil != err { 1018 err = fmt.Errorf("resetFileInodeInMemory() on Inode# 0x%016X failed: %v", fileInode.InodeNumber, err) 1019 return 1020 } 1021 } 1022 1023 fileInode.LogSegmentMap = make(map[uint64]uint64) 1024 fileInode.Size = 0 1025 fileInode.NumWrites = 0 1026 1027 err = nil 1028 return 1029 } 1030 1031 func (vS *volumeStruct) Coalesce(destInodeNumber InodeNumber, metaDataName string, metaData []byte, elements []*CoalesceElement) (attrChangeTime time.Time, modificationTime time.Time, numWrites uint64, fileSize uint64, err error) { 1032 var ( 1033 alreadyInInodeMap bool 1034 coalesceTime time.Time 1035 destInode *inMemoryInodeStruct 1036 destInodeExtentMap sortedmap.BPlusTree 1037 destInodeOffsetBeforeElementAppend uint64 1038 dirEntryInodeNumber InodeNumber 1039 element *CoalesceElement 1040 elementInode *inMemoryInodeStruct 1041 elementInodeExtent *fileExtentStruct 1042 elementInodeExtentAsValue sortedmap.Value 1043 elementInodeExtentMap sortedmap.BPlusTree 1044 elementInodeExtentMapIndex int 1045 elementInodeExtentMapLen int 1046 inodeList []*inMemoryInodeStruct 1047 inodeMap map[InodeNumber]*inMemoryInodeStruct 1048 localErr error 1049 logSegmentReferencedBytes uint64 1050 ok bool 1051 snapShotIDType headhunter.SnapShotIDType 1052 ) 1053 1054 err = enforceRWMode(false) 1055 if nil != err { 1056 return 1057 } 1058 1059 // Validate all referenced {Dir|File}Inodes 1060 1061 inodeMap = make(map[InodeNumber]*inMemoryInodeStruct) 1062 inodeList = make([]*inMemoryInodeStruct, 0, 1+len(elements)) 1063 1064 snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(destInodeNumber)) 1065 if headhunter.SnapShotIDTypeLive != snapShotIDType { 1066 err = blunder.NewError(blunder.PermDeniedError, "Coalesce into non-LiveView destInodeNumber 0x%016X not allowed", destInodeNumber) 1067 return 1068 } 1069 1070 destInode, ok, err = vS.fetchInode(destInodeNumber) 1071 if nil != err { 1072 err = blunder.NewError(blunder.BadFileError, "Coalesce() couldn't fetch destInodeNumber 0x%016X: %v", destInodeNumber, err) 1073 return 1074 } 1075 if !ok { 1076 err = blunder.NewError(blunder.NotFoundError, "Coalesce() couldn't find destInodeNumber 0x%16X", destInodeNumber) 1077 return 1078 } 1079 if destInode.InodeType != FileType { 1080 err = blunder.NewError(blunder.PermDeniedError, "Coalesce() called for destInodeNumber 0x%016X that is not a FileInode", destInodeNumber) 1081 return 1082 } 1083 1084 inodeMap[destInodeNumber] = destInode 1085 inodeList = append(inodeList, destInode) 1086 1087 for _, element = range elements { 1088 snapShotIDType, _, _ = vS.headhunterVolumeHandle.SnapShotU64Decode(uint64(element.ElementInodeNumber)) 1089 if headhunter.SnapShotIDTypeLive != snapShotIDType { 1090 err = blunder.NewError(blunder.PermDeniedError, "Coalesce() from non-LiveView element.ElementInodeNumber (0x%016X) not allowed", element.ElementInodeNumber) 1091 return 1092 } 1093 1094 _, alreadyInInodeMap = inodeMap[element.ElementInodeNumber] 1095 if alreadyInInodeMap { 1096 err = blunder.NewError(blunder.InvalidArgError, "Coalesce() called with duplicate Element Inode 0x%016X", element.ElementInodeNumber) 1097 return 1098 } 1099 1100 elementInode, ok, err = vS.fetchInode(element.ElementInodeNumber) 1101 if nil != err { 1102 err = blunder.NewError(blunder.BadFileError, "Coalesce() couldn't fetch ElementInodeNumber 0x%016X: %v", element.ElementInodeNumber, err) 1103 return 1104 } 1105 1106 inodeMap[element.ElementInodeNumber] = elementInode 1107 inodeList = append(inodeList, elementInode) 1108 1109 if !ok { 1110 err = blunder.NewError(blunder.NotFoundError, "Coalesce() couldn't find ElementInodeNumber 0x%16X", element.ElementInodeNumber) 1111 return 1112 } 1113 if elementInode.InodeType != FileType { 1114 err = blunder.NewError(blunder.PermDeniedError, "Coalesce() called for ElementInodeNumber 0x%016X that is not a FileInode", element.ElementInodeNumber) 1115 return 1116 } 1117 if elementInode.LinkCount != 1 { 1118 err = blunder.NewError(blunder.TooManyLinksError, "Coalesce() called for ElementInodeNumber 0x%016X with LinkCount not == 1 (%v)", element.ElementInodeNumber, elementInode.LinkCount) 1119 return 1120 } 1121 1122 dirEntryInodeNumber, err = vS.lookupByDirInodeNumber(element.ContainingDirectoryInodeNumber, element.ElementName) 1123 if nil != err { 1124 err = blunder.NewError(blunder.InvalidArgError, "Coalesce() called for ElementName %s not found in ContainingDir 0x%016X: %v", element.ElementName, element.ContainingDirectoryInodeNumber, err) 1125 return 1126 } 1127 if dirEntryInodeNumber != element.ElementInodeNumber { 1128 err = blunder.NewError(blunder.InvalidArgError, "Coalesce() called for ElementName %s in ContainingDir 0x%016X had mismatched InodeNumber", element.ElementName, element.ContainingDirectoryInodeNumber) 1129 return 1130 } 1131 } 1132 1133 // Ensure all referenced FileInodes are pre-flushed 1134 1135 err = vS.flushInodes(inodeList) 1136 if nil != err { 1137 err = blunder.NewError(blunder.InvalidArgError, "Coalesce() unable to flush inodeList: %v", err) 1138 return 1139 } 1140 1141 // Now "append" each Element's extents to destInode (creating duplicate references to LogSegments for now) 1142 1143 destInodeExtentMap = destInode.payload.(sortedmap.BPlusTree) 1144 1145 destInode.dirty = true 1146 1147 destInodeOffsetBeforeElementAppend = fileLen(destInodeExtentMap) 1148 1149 for _, element = range elements { 1150 elementInode = inodeMap[element.ElementInodeNumber] 1151 destInode.NumWrites++ 1152 elementInodeExtentMap = elementInode.payload.(sortedmap.BPlusTree) 1153 elementInodeExtentMapLen, err = elementInodeExtentMap.Len() 1154 for elementInodeExtentMapIndex = 0; elementInodeExtentMapIndex < elementInodeExtentMapLen; elementInodeExtentMapIndex++ { 1155 _, elementInodeExtentAsValue, ok, err = elementInodeExtentMap.GetByIndex(elementInodeExtentMapIndex) 1156 if nil != err { 1157 localErr = vS.resetFileInodeInMemory(destInode) 1158 if nil != localErr { 1159 logger.Fatalf("Coalesce() doing resetFileInodeInMemory(destInode) failed: %v", localErr) 1160 } 1161 localErr = vS.flushInode(destInode) 1162 if nil != localErr { 1163 logger.Errorf("Coalesce() doing flushInode(destInode) failed: %v", localErr) 1164 } 1165 err = blunder.NewError(blunder.InvalidArgError, "Coalesce() unable to fetch fileExtentStruct from ExtentMap: %v", err) 1166 return 1167 } 1168 elementInodeExtent = elementInodeExtentAsValue.(*fileExtentStruct) 1169 elementInodeExtent.FileOffset += destInodeOffsetBeforeElementAppend 1170 _, err = destInodeExtentMap.Put(elementInodeExtent.FileOffset, elementInodeExtent) 1171 if nil != err { 1172 localErr = vS.resetFileInodeInMemory(destInode) 1173 if nil != localErr { 1174 logger.Fatalf("Coalesce() doing resetFileInodeInMemory(destInode) failed: %v", localErr) 1175 } 1176 localErr = vS.flushInode(destInode) 1177 if nil != localErr { 1178 logger.Errorf("Coalesce() doing flushInode(destInode) failed: %v", localErr) 1179 } 1180 err = blunder.NewError(blunder.InvalidArgError, "Coalesce() unable to append elementInodeExtent to destInodeExtentMap: %v", err) 1181 return 1182 } 1183 logSegmentReferencedBytes, ok = destInode.LogSegmentMap[elementInodeExtent.LogSegmentNumber] 1184 if ok { 1185 destInode.LogSegmentMap[elementInodeExtent.LogSegmentNumber] = elementInodeExtent.Length + logSegmentReferencedBytes 1186 } else { 1187 destInode.LogSegmentMap[elementInodeExtent.LogSegmentNumber] = elementInodeExtent.Length 1188 } 1189 } 1190 destInodeOffsetBeforeElementAppend += elementInode.Size 1191 err = setSizeInMemory(destInode, destInodeOffsetBeforeElementAppend) 1192 if nil != err { 1193 localErr = vS.resetFileInodeInMemory(destInode) 1194 if nil != localErr { 1195 logger.Fatalf("Coalesce() doing resetFileInodeInMemory(destInode) failed: %v", localErr) 1196 } 1197 localErr = vS.flushInode(destInode) 1198 if nil != localErr { 1199 logger.Errorf("Coalesce() doing flushInode(destInode) failed: %v", localErr) 1200 } 1201 err = blunder.NewError(blunder.InvalidArgError, "Coalesce() unable to setSize() destInodeNumber 0x%016X: %v", destInodeNumber, err) 1202 return 1203 } 1204 } 1205 1206 // Now, destInode is fully assembled... update its metadata & assemble remaining results 1207 coalesceTime = time.Now() 1208 destInode.CreationTime = coalesceTime 1209 destInode.AttrChangeTime = coalesceTime 1210 destInode.ModificationTime = coalesceTime 1211 1212 // attach new middleware headers (why are we making a copy?) 1213 inodeStreamBuf := make([]byte, len(metaData)) 1214 copy(inodeStreamBuf, metaData) 1215 destInode.StreamMap[metaDataName] = inodeStreamBuf 1216 1217 // collect the NumberOfWrites value while locked (important for Etag) 1218 numWrites = destInode.NumWrites 1219 fileSize = destInode.Size 1220 attrChangeTime = destInode.AttrChangeTime 1221 modificationTime = destInode.ModificationTime 1222 1223 // Now, destInode is fully assembled... need to remove all elements references to currently shared LogSegments 1224 1225 for _, element = range elements { 1226 localErr = vS.resetFileInodeInMemory(inodeMap[element.ElementInodeNumber]) 1227 if nil != err { 1228 logger.Fatalf("Coalesce() doing resetFileInodeInMemory(inodeMap[element.ElementInodeNumber]) failed: %v", localErr) 1229 } 1230 } 1231 1232 // Time to flush all affected FileInodes 1233 1234 err = vS.flushInodes(inodeList) 1235 if nil != err { 1236 err = fmt.Errorf("Coalesce() doing flushInodes(inodeList) failed: %v", err) 1237 return 1238 } 1239 1240 // Now we can Unlink and Destroy each element 1241 1242 for _, element = range elements { 1243 err = vS.Unlink(element.ContainingDirectoryInodeNumber, element.ElementName, false) 1244 if nil != err { 1245 err = fmt.Errorf("Coalesce() doing Unlink(element.ContainingDirectoryInodeNumber, element.ElementName, false) failed: %v", err) 1246 return 1247 } 1248 err = vS.Destroy(element.ElementInodeNumber) 1249 if nil != err { 1250 err = fmt.Errorf("Coalesce() doing Destroy(element.ElementInodeNumber) failed: %v", err) 1251 return 1252 } 1253 } 1254 1255 // All done 1256 1257 err = nil 1258 return 1259 } 1260 1261 func (vS *volumeStruct) DefragmentFile(fileInodeNumber InodeNumber, startingFileOffset uint64, chunkSize uint64) (nextFileOffset uint64, eofReached bool, err error) { 1262 var ( 1263 chunk []byte 1264 chunkSizeCapped uint64 1265 fileInode *inMemoryInodeStruct 1266 ) 1267 1268 err = enforceRWMode(false) 1269 if nil != err { 1270 return 1271 } 1272 1273 fileInode, err = vS.fetchInodeType(fileInodeNumber, FileType) 1274 if nil != err { 1275 return 1276 } 1277 1278 if startingFileOffset >= fileInode.Size { 1279 nextFileOffset = fileInode.Size 1280 eofReached = true 1281 err = nil 1282 return 1283 } 1284 1285 if (startingFileOffset + chunkSize) >= fileInode.Size { 1286 chunkSizeCapped = fileInode.Size - startingFileOffset 1287 eofReached = true 1288 } else { 1289 chunkSizeCapped = chunkSize 1290 eofReached = false 1291 } 1292 1293 nextFileOffset = startingFileOffset + chunkSizeCapped 1294 1295 chunk, err = vS.Read(fileInodeNumber, startingFileOffset, chunkSizeCapped, nil) 1296 if nil != err { 1297 return 1298 } 1299 1300 err = vS.Write(fileInodeNumber, startingFileOffset, chunk, nil) 1301 1302 return // err as returned by Write() is sufficient 1303 } 1304 1305 func (vS *volumeStruct) setLogSegmentContainer(logSegmentNumber uint64, containerName string) (err error) { 1306 containerNameAsByteSlice := utils.StringToByteSlice(containerName) 1307 err = vS.headhunterVolumeHandle.PutLogSegmentRec(logSegmentNumber, containerNameAsByteSlice) 1308 return 1309 } 1310 1311 func (vS *volumeStruct) getLogSegmentContainer(logSegmentNumber uint64) (containerName string, err error) { 1312 containerNameAsByteSlice, err := vS.headhunterVolumeHandle.GetLogSegmentRec(logSegmentNumber) 1313 if nil != err { 1314 return 1315 } 1316 containerName = utils.ByteSliceToString(containerNameAsByteSlice) 1317 return 1318 } 1319 1320 func (vS *volumeStruct) getObjectLocationFromLogSegmentNumber(logSegmentNumber uint64) (containerName string, objectName string, objectPath string, err error) { 1321 var ( 1322 nonce uint64 1323 ) 1324 1325 containerName, err = vS.getLogSegmentContainer(logSegmentNumber) 1326 if nil != err { 1327 return 1328 } 1329 1330 _, _, nonce = vS.headhunterVolumeHandle.SnapShotU64Decode(logSegmentNumber) 1331 1332 objectName = fmt.Sprintf("%016X", nonce) 1333 objectPath = fmt.Sprintf("/v1/%s/%s/%016X", vS.accountName, containerName, nonce) 1334 return 1335 }