Files
William Gill e96c71cf16
Some checks failed
Release / Build & Vet (push) Successful in 1m29s
Release / Test (push) Successful in 1m35s
Release / Lint (push) Successful in 2m7s
Release / GoReleaser Check (push) Successful in 1m25s
Release / Build & Release (push) Failing after 2m27s
style: Run gofmt on all Go source files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:12:41 -05:00

103 lines
2.5 KiB
Go

package scoutfs
import (
"fmt"
"math"
"unsafe"
)
const walkBatchSize = 128
// InodeIterator yields (seq, ino) pairs from a WALK_INODES ioctl in batched
// calls. It is not safe for concurrent use.
type InodeIterator struct {
fd uintptr
index WalkIndex
first WalkInodesEntry
last WalkInodesEntry
buf [walkBatchSize]WalkInodesEntry
pos int
count int
done bool
err error
}
// NewInodeIterator creates an iterator that walks inodes in the given index
// between startSeq and endSeq (inclusive). Use WalkMetaSeq or WalkDataSeq.
func NewInodeIterator(c *Client, index WalkIndex, startSeq, endSeq uint64) *InodeIterator {
if c == nil {
panic("NewInodeIterator: client must not be nil")
}
return &InodeIterator{
fd: c.fd(),
index: index,
first: WalkInodesEntry{Major: startSeq, Ino: 0, Minor: 0},
last: WalkInodesEntry{Major: endSeq, Ino: math.MaxUint64, Minor: math.MaxUint32},
}
}
// Next returns the next (seq, ino) pair. When the iteration is exhausted or
// an error occurs, ok is false. Check Err() after the loop.
func (it *InodeIterator) Next() (seq uint64, ino uint64, ok bool) {
if it.done {
return 0, 0, false
}
if it.pos >= it.count {
if err := it.fetch(); err != nil {
it.err = err
it.done = true
return 0, 0, false
}
if it.count == 0 {
it.done = true
return 0, 0, false
}
}
entry := it.buf[it.pos]
it.pos++
return entry.Major, entry.Ino, true
}
// Err returns the first error encountered during iteration, if any.
func (it *InodeIterator) Err() error {
return it.err
}
func (it *InodeIterator) fetch() error {
req := WalkInodesRequest{
First: it.first,
Last: it.last,
EntriesPtr: uint64(uintptr(unsafe.Pointer(&it.buf[0]))),
NrEntries: walkBatchSize,
Index: uint8(it.index),
}
if err := ioctlRaw(it.fd, iocWalkInodes, unsafe.Pointer(&req)); err != nil {
return fmt.Errorf("walk inodes ioctl: %w", err)
}
it.count = int(req.NrEntries)
it.pos = 0
if it.count > 0 {
last := it.buf[it.count-1]
it.first = advanceWalkEntry(last)
}
return nil
}
// advanceWalkEntry computes the next starting entry after the given one,
// handling minor/ino/major wraparound.
func advanceWalkEntry(e WalkInodesEntry) WalkInodesEntry {
if e.Minor < math.MaxUint32 {
return WalkInodesEntry{Major: e.Major, Ino: e.Ino, Minor: e.Minor + 1}
}
if e.Ino < math.MaxUint64 {
return WalkInodesEntry{Major: e.Major, Ino: e.Ino + 1, Minor: 0}
}
return WalkInodesEntry{Major: e.Major + 1, Ino: 0, Minor: 0}
}