103 lines
2.5 KiB
Go
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}
|
|
}
|