func fetchSegment()

in Sources/TSFCASFileTree/Internal/FileSegmenter.swift [181:293]


    func fetchSegment(segmentNumber: Int) throws -> (LLBFastData, isEOF: Bool)? {

        let (fileOffset, overflow) = segmentSize.multipliedReportingOverflow(by: segmentNumber)
        guard !overflow else {
            // FIXME: Need to support ERANGE in FileSystemError.
            throw FileSystemError(.unknownOSError)
        }
        let fileSize = Int(statInfo.st_size)
        let currentSegmentSize = min(fileSize - fileOffset, segmentSize)
        if currentSegmentSize <= 0 {
            if fileOffset == 0, statInfo.st_size == 0 {
                // It is safe to not to check whether the file is still zero.
                // This way even if the file changes we're on the safe side
                // of the race, sending the idea that the file had changed
                // either completely before or completely after we've used it.
                return (LLBFastData([]), isEOF: true)
            } else {
                return nil
            }
        }
        let atEOF = fileOffset + currentSegmentSize >= fileSize

        if let basePointer = self.mappedAt {
            let ptr = UnsafeRawBufferPointer(start: basePointer.advanced(by: fileOffset), count: currentSegmentSize)
            return (LLBFastData(ptr, deallocator: { _ in
                    withExtendedLifetime(self) { }
                }), isEOF: atEOF)
        }

        let fd: CInt
        switch reuseFD.exchange(with: -1) {
        case let reused where reused >= 0:
            fd = reused
        default:
            let newFD: CInt
            do {
                newFD = try LLBFutureFileSystem.openImpl(path.pathString, flags: O_RDONLY | O_NOFOLLOW)
            } catch {
                guard allowInconsistency else {
                    // Ignore the details of the actual error: in most cases
                    // (file not found, or something else) it is as good as
                    // "something is different with this file".
                    // After all, we have succeeded opening it before.
                    throw Error.resourceChanged(reason: "Can't reopen: \(error)")
                }

                // If we can't open something that we used to be able to open,
                // just consider it deleted/truncated and upload empty
                // (if less than a segment size) or truncated.
                return (LLBFastData([]), isEOF: true)
            }

            // The main check to perform here is whether it is a regular file
            // (S_IFREG). The rest is just an early bail out: we do check the
            // stat information again after the file chunk is read.
            let inconsistency = self.checkConsistency(ofSameFileOpenedAs: newFD)
            switch inconsistency {
            case .Same,
                 .Modified where allowInconsistency == true:
                // Do not log here, avoid duplicate logging.
                break
            case .Deleted, .Replaced:
                close(newFD)
                throw FileSystemError(.noEntry, path)
            case .Modified:
                close(newFD)
                throw Error.resourceChanged(reason: String(describing: inconsistency))
            }

            fd = newFD
        }
        defer { close(fd) }

        let data = try LLBFutureFileSystem.syncReadComplete(fd: fd, readSize: currentSegmentSize, fileOffset: fileOffset)

        if atEOF, segmentNumber == 0 {
            // If we've just slurped the whole relatively short (1 segm) file,
            // we don't have to check whether the file has been modified or
            // not at the end of the read:
            //  - If it is a first read, we'll have a chance to check
            //    this on a subsequent read. And upload something else instead
            //    if it changed.
            //  - If it is the last read (where we use data for the actual
            //    upload), then this situation is just freezes the inconsistent
            //    state of the [relatively small] file.

            // Actually, ignore all that, let's get safety over speed.
            // Also, testing didn't find material changes.
        }

        // Even if we read the file correctly, make sure it didn't change
        // after we've read it. That would be a race.
        let inconsistency: ConsistencyStatus
        if data.count != currentSegmentSize {
            inconsistency = .Modified(reason: "File size shrunk")
        } else {
            inconsistency = self.checkConsistency(ofSameFileOpenedAs: fd)
        }
        switch inconsistency {
        case .Same:
            break
        case .Modified where allowInconsistency == true:
            // Note inconsistency
            break
        case .Deleted, .Replaced:
            // The file was deleted at _some_ point in the middle of processing.
            throw FileSystemError(.noEntry, path)
        case .Modified(let reason):
            throw Error.resourceChanged(reason: reason)
        }

        return (LLBFastData(data), isEOF: atEOF)
    }