90 lines
2.3 KiB
Go
90 lines
2.3 KiB
Go
package scoutfs
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"unsafe"
|
|
)
|
|
|
|
const allocInosBatchSize = 8192
|
|
|
|
// FetchAllocatedInos retrieves allocated inode numbers starting from startIno.
|
|
// It returns the inode numbers found in the allocation group. An empty slice
|
|
// signals that no more allocated inodes exist beyond startIno.
|
|
func (c *Client) FetchAllocatedInos(startIno uint64) ([]uint64, error) {
|
|
buf := make([]uint64, allocInosBatchSize)
|
|
|
|
req := GetAllocatedInosRequest{
|
|
StartIno: startIno,
|
|
InosPtr: uint64(uintptr(unsafe.Pointer(&buf[0]))),
|
|
InosBytes: uint64(len(buf) * 8),
|
|
}
|
|
|
|
if err := ioctlRaw(c.fd(), iocGetAllocatedInos, unsafe.Pointer(&req)); err != nil {
|
|
// Kernel signals end-of-iteration via ENOENT.
|
|
if errors.Is(err, ErrInodeNotFound) {
|
|
return nil, nil
|
|
}
|
|
return nil, fmt.Errorf("get allocated inos ioctl from ino %d: %w", startIno, err)
|
|
}
|
|
|
|
// The kernel writes the count of returned inodes into InosBytes.
|
|
count := req.InosBytes / 8
|
|
if count > uint64(len(buf)) {
|
|
count = uint64(len(buf))
|
|
}
|
|
|
|
result := make([]uint64, count)
|
|
copy(result, buf[:count])
|
|
return result, nil
|
|
}
|
|
|
|
// inoGroupSize is the ScoutFS inode allocation group size (SCOUTFS_LOCK_INODE_GROUP_NR).
|
|
const inoGroupSize = 1024
|
|
|
|
// maxEmptyGroups is the number of consecutive empty inode groups before we
|
|
// assume there are no more allocated inodes.
|
|
const maxEmptyGroups = 256
|
|
|
|
// AllAllocatedInos iterates through all allocated inodes in the filesystem,
|
|
// calling fn for each batch. Iteration stops when fn returns false or all
|
|
// inodes have been enumerated. Begins at FirstUserIno since inodes below that
|
|
// are reserved by ScoutFS.
|
|
func (c *Client) AllAllocatedInos(fn func(inos []uint64) bool) error {
|
|
startIno := uint64(FirstUserIno)
|
|
emptyRuns := 0
|
|
|
|
for {
|
|
inos, err := c.FetchAllocatedInos(startIno)
|
|
if err != nil {
|
|
return fmt.Errorf("iterating allocated inos from %d: %w", startIno, err)
|
|
}
|
|
|
|
if len(inos) == 0 {
|
|
emptyRuns++
|
|
if emptyRuns >= maxEmptyGroups {
|
|
return nil
|
|
}
|
|
// Advance to the next inode allocation group.
|
|
nextGroup := (startIno/inoGroupSize + 1) * inoGroupSize
|
|
if nextGroup <= startIno {
|
|
return nil
|
|
}
|
|
startIno = nextGroup
|
|
continue
|
|
}
|
|
|
|
emptyRuns = 0
|
|
|
|
if !fn(inos) {
|
|
return nil
|
|
}
|
|
|
|
nextStart := inos[len(inos)-1] + 1
|
|
if nextStart <= startIno {
|
|
return nil
|
|
}
|
|
startIno = nextStart
|
|
}
|
|
}
|