mirror of
https://github.com/versity/scoutfs-go.git
synced 2026-09-18 22:14:26 +00:00
Support for bulk change sequence interface
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
FROM golang:1.24-bookworm
|
||||
|
||||
ARG SCOUTFS_BRANCH=main
|
||||
|
||||
# Clone scoutfs and checkout the specified branch
|
||||
RUN git clone https://github.com/versity/scoutfs.git /tmp/scoutfs && \
|
||||
cd /tmp/scoutfs && \
|
||||
git checkout ${SCOUTFS_BRANCH}
|
||||
|
||||
# Install the scoutfs headers where c_defs_linux.go expects them
|
||||
RUN mkdir -p /usr/include/scoutfs && \
|
||||
cp /tmp/scoutfs/kmod/src/ioctl.h /usr/include/scoutfs/ioctl.h && \
|
||||
cp /tmp/scoutfs/kmod/src/format.h /usr/include/scoutfs/format.h
|
||||
|
||||
WORKDIR /work
|
||||
COPY go.mod ./
|
||||
COPY c_defs_linux.go ./
|
||||
|
||||
# Generate scoutfsdefs.go
|
||||
CMD ["sh", "-c", "go tool cgo -godefs c_defs_linux.go > /output/scoutfsdefs.go"]
|
||||
@@ -34,7 +34,14 @@ package scoutfs
|
||||
// typedef int32_t __s32;
|
||||
// typedef int64_t __s64;
|
||||
// #define __packed
|
||||
// #ifndef DIV_ROUND_UP
|
||||
// #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
|
||||
// #endif
|
||||
// #ifndef BITS_PER_LONG
|
||||
// #define BITS_PER_LONG (sizeof(long) * 8)
|
||||
// #endif
|
||||
// #include "/usr/include/scoutfs/ioctl.h"
|
||||
// #include "/usr/include/scoutfs/format.h"
|
||||
// typedef struct scoutfs_ioctl_walk_inodes_entry scoutfs_ioctl_walk_inodes_entry_t;
|
||||
// typedef struct scoutfs_ioctl_walk_inodes scoutfs_ioctl_walk_inodes_t;
|
||||
// typedef struct scoutfs_ioctl_ino_path scoutfs_ioctl_ino_path_t;
|
||||
@@ -73,6 +80,12 @@ package scoutfs
|
||||
// typedef struct scoutfs_ioctl_read_xattr_index scoutfs_ioctl_read_xattr_index_t;
|
||||
// typedef struct scoutfs_ioctl_inode_attr_x scoutfs_ioctl_inode_attr_x_t;
|
||||
// typedef struct scoutfs_ioctl_punch_offline scoutfs_ioctl_punch_offline_t;
|
||||
// typedef struct scoutfs_ioctl_meta_seq scoutfs_ioctl_meta_seq_t;
|
||||
// typedef struct scoutfs_ioctl_raw_read_meta_seq scoutfs_ioctl_raw_read_meta_seq_t;
|
||||
// typedef struct scoutfs_ioctl_raw_read_inode_info scoutfs_ioctl_raw_read_inode_info_t;
|
||||
// typedef struct scoutfs_ioctl_raw_read_result scoutfs_ioctl_raw_read_result_t;
|
||||
// typedef struct scoutfs_inode scoutfs_inode_t;
|
||||
// typedef struct scoutfs_timespec scoutfs_timespec_t;
|
||||
import "C"
|
||||
|
||||
const IOCQUERYINODES = C.SCOUTFS_IOC_WALK_INODES
|
||||
@@ -124,6 +137,11 @@ const IOCIAXBITS = C.SCOUTFS_IOC_IAX__BITS
|
||||
const IOCGETATTRX = C.SCOUTFS_IOC_GET_ATTR_X
|
||||
const IOCSETATTRX = C.SCOUTFS_IOC_SET_ATTR_X
|
||||
const IOCPUNCHOFFLINE = C.SCOUTFS_IOC_PUNCH_OFFLINE
|
||||
const IOCRAWREADMETASEQ = C.SCOUTFS_IOC_RAW_READ_META_SEQ
|
||||
const IOCRAWREADINODEINFO = C.SCOUTFS_IOC_RAW_READ_INODE_INFO
|
||||
|
||||
const RAWREADRESULTINODE = C.SCOUTFS_IOC_RAW_READ_RESULT_INODE
|
||||
const RAWREADRESULTXATTR = C.SCOUTFS_IOC_RAW_READ_RESULT_XATTR
|
||||
|
||||
type InodesEntry C.scoutfs_ioctl_walk_inodes_entry_t
|
||||
type queryInodes C.scoutfs_ioctl_walk_inodes_t
|
||||
@@ -151,6 +169,12 @@ type indexEntry C.scoutfs_ioctl_xattr_index_entry_t
|
||||
type readXattrIndex C.scoutfs_ioctl_read_xattr_index_t
|
||||
type inodeAttrX C.scoutfs_ioctl_inode_attr_x_t
|
||||
type punchOffline C.scoutfs_ioctl_punch_offline_t
|
||||
type MetaSeqEntry C.scoutfs_ioctl_meta_seq_t
|
||||
type rawReadMetaSeq C.scoutfs_ioctl_raw_read_meta_seq_t
|
||||
type rawReadInodeInfo C.scoutfs_ioctl_raw_read_inode_info_t
|
||||
type RawReadResult C.scoutfs_ioctl_raw_read_result_t
|
||||
type ScoutfsInode C.scoutfs_inode_t
|
||||
type ScoutfsTimespec C.scoutfs_timespec_t
|
||||
|
||||
const sizeofstatfsMore = C.sizeof_scoutfs_ioctl_statfs_more_t
|
||||
const sizeofxattrTotal = C.sizeof_scoutfs_ioctl_xattr_total_t
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
scoutfs "github.com/versity/scoutfs-go"
|
||||
)
|
||||
|
||||
type inoState struct {
|
||||
metaSeq uint64
|
||||
}
|
||||
|
||||
func main() {
|
||||
path := flag.String("path", "", "path to scoutfs mount point")
|
||||
debug := flag.Bool("debug", false, "enable verbose debugging output")
|
||||
|
||||
var xattrNames stringSlice
|
||||
flag.Var(&xattrNames, "xattr", "xattr name to read with inodes (can be repeated)")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *path == "" {
|
||||
fmt.Fprintf(os.Stderr, "error: -path is required\n")
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
f, err := os.Open(*path)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening %s: %v\n", *path, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Track known inodes and their meta_seq
|
||||
known := make(map[uint64]*inoState)
|
||||
lines := 0
|
||||
|
||||
// Allocate a reusable results buffer for RawReadInodeInfo
|
||||
resultsBuf := make([]byte, 1024*1024)
|
||||
|
||||
for {
|
||||
reader := scoutfs.NewRawMetaSeqReader(f)
|
||||
|
||||
var allMS []scoutfs.MetaSeqEntry
|
||||
for {
|
||||
items, err := reader.Next()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error reading meta_seq: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if items == nil {
|
||||
break
|
||||
}
|
||||
allMS = append(allMS, items...)
|
||||
}
|
||||
|
||||
// Find inodes that differ from our known state
|
||||
var diffInos []uint64
|
||||
seen := make(map[uint64]uint64) // ino -> meta_seq from current scan
|
||||
|
||||
for _, ms := range allMS {
|
||||
seen[ms.Ino] = ms.Seq
|
||||
}
|
||||
|
||||
// Check for new or changed inodes
|
||||
for ino, metaSeq := range seen {
|
||||
st, ok := known[ino]
|
||||
if !ok || st.metaSeq != metaSeq {
|
||||
diffInos = append(diffInos, ino)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for removed inodes
|
||||
for ino := range known {
|
||||
if _, ok := seen[ino]; !ok {
|
||||
diffInos = append(diffInos, ino)
|
||||
}
|
||||
}
|
||||
|
||||
if len(diffInos) > 0 {
|
||||
// Sort and deduplicate (inos must be sorted for the ioctl)
|
||||
sort.Slice(diffInos, func(i, j int) bool {
|
||||
return diffInos[i] < diffInos[j]
|
||||
})
|
||||
|
||||
results, err := scoutfs.RawReadInodeInfo(f, diffInos, xattrNames, resultsBuf)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error reading inode info: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Build set of returned inos
|
||||
returnedInos := make(map[uint64]bool)
|
||||
for _, r := range results {
|
||||
returnedInos[r.Ino] = true
|
||||
}
|
||||
|
||||
// Remove inodes that weren't returned (nlink==0 or deleted)
|
||||
for _, ino := range diffInos {
|
||||
if !returnedInos[ino] {
|
||||
if *debug {
|
||||
fmt.Printf(" remove ino %d\n", ino)
|
||||
}
|
||||
delete(known, ino)
|
||||
}
|
||||
}
|
||||
|
||||
// Print header periodically
|
||||
if lines%16 == 0 {
|
||||
fmt.Printf("%8s %12s %12s %12s %6s %6s %6s %6s",
|
||||
"ino", "meta_seq", "data_seq", "data_ver",
|
||||
"nlink", "uid", "gid", "mode")
|
||||
if len(xattrNames) > 0 {
|
||||
fmt.Printf(" xattrs")
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
for _, r := range results {
|
||||
action := "update"
|
||||
if _, ok := known[r.Ino]; !ok {
|
||||
action = "add"
|
||||
}
|
||||
|
||||
known[r.Ino] = &inoState{metaSeq: r.Inode.Meta_seq}
|
||||
|
||||
fmt.Printf("%8d %12d %12d %12d %6d %6d %6d %6o",
|
||||
r.Ino,
|
||||
r.Inode.Meta_seq,
|
||||
r.Inode.Data_seq,
|
||||
r.Inode.Data_version,
|
||||
r.Inode.Nlink,
|
||||
r.Inode.Uid,
|
||||
r.Inode.Gid,
|
||||
r.Inode.Mode)
|
||||
|
||||
if len(r.Xattrs) > 0 {
|
||||
for _, x := range r.Xattrs {
|
||||
fmt.Printf(" %s=%q", x.Name, x.Value)
|
||||
}
|
||||
}
|
||||
|
||||
if *debug {
|
||||
fmt.Printf(" [%s]", action)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
lines++
|
||||
}
|
||||
}
|
||||
|
||||
// Update known state from current scan
|
||||
for ino, metaSeq := range seen {
|
||||
if st, ok := known[ino]; ok {
|
||||
st.metaSeq = metaSeq
|
||||
}
|
||||
}
|
||||
|
||||
if *debug {
|
||||
fmt.Printf("tracking %d inodes, inode result size %d\n",
|
||||
len(known), unsafe.Sizeof(scoutfs.ScoutfsInode{}))
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// stringSlice implements flag.Value for repeated string flags
|
||||
type stringSlice []string
|
||||
|
||||
func (s *stringSlice) String() string { return fmt.Sprintf("%v", *s) }
|
||||
func (s *stringSlice) Set(val string) error {
|
||||
*s = append(*s, val)
|
||||
return nil
|
||||
}
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# generate-defs.sh - Regenerate scoutfsdefs.go from scoutfs kernel headers
|
||||
#
|
||||
# This script uses Docker to build a Linux container with the scoutfs
|
||||
# kernel headers installed, then runs "go tool cgo -godefs" to generate
|
||||
# the Go type definitions from c_defs_linux.go.
|
||||
#
|
||||
# Usage:
|
||||
# ./generate-defs.sh [BRANCH]
|
||||
#
|
||||
# Arguments:
|
||||
# BRANCH The scoutfs git branch to use for headers (default: main)
|
||||
#
|
||||
# Examples:
|
||||
# ./generate-defs.sh # use main branch
|
||||
# ./generate-defs.sh zab/get_changed_inos # use a feature branch
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
BRANCH="${1:-main}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
IMAGE_NAME="scoutfs-godefs"
|
||||
|
||||
echo "==> Building Docker image with scoutfs branch: ${BRANCH}"
|
||||
docker build \
|
||||
--build-arg "SCOUTFS_BRANCH=${BRANCH}" \
|
||||
--no-cache \
|
||||
-f "${SCRIPT_DIR}/Dockerfile.godefs" \
|
||||
-t "${IMAGE_NAME}" \
|
||||
"${SCRIPT_DIR}"
|
||||
|
||||
echo "==> Generating scoutfsdefs.go"
|
||||
docker run --rm \
|
||||
-v "${SCRIPT_DIR}:/output" \
|
||||
"${IMAGE_NAME}"
|
||||
|
||||
echo "==> Verifying build"
|
||||
cd "${SCRIPT_DIR}"
|
||||
GOOS=linux go vet ./...
|
||||
GOOS=linux go build ./...
|
||||
|
||||
echo "==> Done. scoutfsdefs.go has been regenerated from branch: ${BRANCH}"
|
||||
+257
@@ -1830,3 +1830,260 @@ func PunchHole(f *os.File, length, offset, version uint64) error {
|
||||
_, err := scoutfsctl(f, IOCPUNCHOFFLINE, unsafe.Pointer(&po))
|
||||
return err
|
||||
}
|
||||
|
||||
// RawMetaSeqReader iterates over meta_seq items without cluster locking.
|
||||
// Use NewRawMetaSeqReader to create a new reader.
|
||||
type RawMetaSeqReader struct {
|
||||
fsfd *os.File
|
||||
start MetaSeqEntry
|
||||
end MetaSeqEntry
|
||||
last MetaSeqEntry
|
||||
batch uint32
|
||||
buf []byte
|
||||
done bool
|
||||
}
|
||||
|
||||
// RMSOption sets various options for NewRawMetaSeqReader
|
||||
type RMSOption func(*RawMetaSeqReader)
|
||||
|
||||
// WithRMSBatchSize sets the max number of meta_seq items to be returned at a time
|
||||
func WithRMSBatchSize(size uint32) RMSOption {
|
||||
return func(r *RawMetaSeqReader) {
|
||||
r.batch = size
|
||||
}
|
||||
}
|
||||
|
||||
// WithRMSRange sets the start and end range for the meta_seq reader
|
||||
func WithRMSRange(start, end MetaSeqEntry) RMSOption {
|
||||
return func(r *RawMetaSeqReader) {
|
||||
r.start = start
|
||||
r.end = end
|
||||
}
|
||||
}
|
||||
|
||||
// NewRawMetaSeqReader creates a new reader for raw meta_seq items.
|
||||
// An open file within scoutfs is supplied for ioctls
|
||||
// (usually just the base mount point directory).
|
||||
func NewRawMetaSeqReader(f *os.File, opts ...RMSOption) *RawMetaSeqReader {
|
||||
r := &RawMetaSeqReader{
|
||||
fsfd: f,
|
||||
batch: 1024,
|
||||
end: MetaSeqEntry{Seq: max64, Ino: max64},
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(r)
|
||||
}
|
||||
|
||||
r.buf = make([]byte, int(unsafe.Sizeof(MetaSeqEntry{}))*int(r.batch))
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// Next gets the next batch of meta_seq items.
|
||||
// Returns nil, nil when iteration is complete.
|
||||
func (r *RawMetaSeqReader) Next() ([]MetaSeqEntry, error) {
|
||||
if r.done {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
rms := rawReadMetaSeq{
|
||||
Start: r.start,
|
||||
End: r.end,
|
||||
Ptr: uint64(uintptr(unsafe.Pointer(&r.buf[0]))),
|
||||
Size: r.batch,
|
||||
}
|
||||
|
||||
n, err := scoutfsctl(r.fsfd, IOCRAWREADMETASEQ, unsafe.Pointer(&rms))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r.last = rms.Last
|
||||
|
||||
if n == 0 {
|
||||
r.done = true
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
rbuf := bytes.NewReader(r.buf)
|
||||
items := make([]MetaSeqEntry, n)
|
||||
|
||||
var e MetaSeqEntry
|
||||
for i := 0; i < n; i++ {
|
||||
err := binary.Read(rbuf, binary.LittleEndian, &e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items[i] = e
|
||||
}
|
||||
|
||||
// Advance start past last for the next call
|
||||
r.start = r.last
|
||||
r.start.Ino++
|
||||
if r.start.Ino == 0 {
|
||||
r.start.Seq++
|
||||
}
|
||||
|
||||
// If last reached the end of the range, we're done
|
||||
if r.last.Seq == r.end.Seq && r.last.Ino == r.end.Ino {
|
||||
r.done = true
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// Last returns the last meta_seq position from the most recent Next() call.
|
||||
// This is the last item that could have been returned by the kernel.
|
||||
func (r *RawMetaSeqReader) Last() MetaSeqEntry {
|
||||
return r.last
|
||||
}
|
||||
|
||||
// Reset resets the reader to start from the beginning of the range
|
||||
func (r *RawMetaSeqReader) Reset() {
|
||||
r.start = MetaSeqEntry{}
|
||||
r.last = MetaSeqEntry{}
|
||||
r.done = false
|
||||
}
|
||||
|
||||
// RawInodeResult contains the inode metadata and optional xattr values
|
||||
// returned by RawReadInodeInfo for a single inode
|
||||
type RawInodeResult struct {
|
||||
Ino uint64
|
||||
Inode ScoutfsInode
|
||||
Xattrs []RawXattrResult
|
||||
}
|
||||
|
||||
// RawXattrResult contains a single xattr name and value
|
||||
type RawXattrResult struct {
|
||||
Name string
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// RawReadInodeInfo reads inode metadata (and optionally xattr values) for
|
||||
// the given inode numbers without cluster locking.
|
||||
//
|
||||
// inos must be a sorted slice of unique non-zero inode numbers.
|
||||
// xattrNames optionally specifies xattr names to return with each inode.
|
||||
// resultsBuf is a caller-provided buffer for the ioctl results, allowing
|
||||
// reuse across calls to avoid repeated allocations.
|
||||
//
|
||||
// An open file within scoutfs is supplied for ioctls
|
||||
// (usually just the base mount point directory).
|
||||
func RawReadInodeInfo(f *os.File, inos []uint64, xattrNames []string, resultsBuf []byte) ([]RawInodeResult, error) {
|
||||
if len(inos) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Build the null-terminated xattr names buffer
|
||||
var namesBuf []byte
|
||||
for _, name := range xattrNames {
|
||||
namesBuf = append(namesBuf, []byte(name)...)
|
||||
namesBuf = append(namesBuf, 0)
|
||||
}
|
||||
|
||||
rii := rawReadInodeInfo{
|
||||
Inos_ptr: uint64(uintptr(unsafe.Pointer(&inos[0]))),
|
||||
Inos_count: uint32(len(inos)),
|
||||
Results_ptr: uint64(uintptr(unsafe.Pointer(&resultsBuf[0]))),
|
||||
Results_size: uint32(len(resultsBuf)),
|
||||
}
|
||||
|
||||
if len(namesBuf) > 0 {
|
||||
rii.Names_ptr = uint64(uintptr(unsafe.Pointer(&namesBuf[0])))
|
||||
rii.Names_count = uint32(len(xattrNames))
|
||||
}
|
||||
|
||||
n, err := scoutfsctl(f, IOCRAWREADINODEINFO, unsafe.Pointer(&rii))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return parseRawReadResults(resultsBuf[:n])
|
||||
}
|
||||
|
||||
func parseRawReadResults(buf []byte) ([]RawInodeResult, error) {
|
||||
var results []RawInodeResult
|
||||
resultHdrSize := int(unsafe.Sizeof(RawReadResult{}))
|
||||
off := 0
|
||||
|
||||
for off < len(buf) {
|
||||
if off+resultHdrSize > len(buf) {
|
||||
break
|
||||
}
|
||||
|
||||
// Read result header - copy to aligned struct
|
||||
var hdr RawReadResult
|
||||
copy((*[12]byte)(unsafe.Pointer(&hdr))[:], buf[off:off+resultHdrSize])
|
||||
off += resultHdrSize
|
||||
|
||||
if int(hdr.Size) > len(buf)-off {
|
||||
return results, fmt.Errorf("result payload size %d exceeds remaining buffer %d", hdr.Size, len(buf)-off)
|
||||
}
|
||||
|
||||
payload := buf[off : off+int(hdr.Size)]
|
||||
off += int(hdr.Size)
|
||||
|
||||
switch hdr.Type {
|
||||
case RAWREADRESULTINODE:
|
||||
if len(payload) < 8 {
|
||||
return results, fmt.Errorf("inode result too small: %d bytes", len(payload))
|
||||
}
|
||||
|
||||
ino := binary.LittleEndian.Uint64(payload[:8])
|
||||
inodeBytes := payload[8:]
|
||||
|
||||
var inode ScoutfsInode
|
||||
inodeSize := int(unsafe.Sizeof(inode))
|
||||
|
||||
// Handle potentially smaller inode structs from older format versions
|
||||
readSize := len(inodeBytes)
|
||||
if readSize > inodeSize {
|
||||
readSize = inodeSize
|
||||
}
|
||||
err := binary.Read(
|
||||
bytes.NewReader(inodeBytes[:readSize]),
|
||||
binary.LittleEndian,
|
||||
(*[168]byte)(unsafe.Pointer(&inode))[:readSize],
|
||||
)
|
||||
if err != nil {
|
||||
return results, fmt.Errorf("parse inode %d: %v", ino, err)
|
||||
}
|
||||
|
||||
results = append(results, RawInodeResult{
|
||||
Ino: ino,
|
||||
Inode: inode,
|
||||
})
|
||||
|
||||
case RAWREADRESULTXATTR:
|
||||
if len(results) == 0 {
|
||||
return results, fmt.Errorf("xattr result without preceding inode result")
|
||||
}
|
||||
|
||||
// Find null terminator separating name from value
|
||||
nullIdx := bytes.IndexByte(payload, 0)
|
||||
if nullIdx < 0 {
|
||||
return results, fmt.Errorf("xattr result missing null terminator")
|
||||
}
|
||||
|
||||
name := string(payload[:nullIdx])
|
||||
value := make([]byte, len(payload)-nullIdx-1)
|
||||
copy(value, payload[nullIdx+1:])
|
||||
|
||||
last := &results[len(results)-1]
|
||||
last.Xattrs = append(last.Xattrs, RawXattrResult{
|
||||
Name: name,
|
||||
Value: value,
|
||||
})
|
||||
|
||||
default:
|
||||
// Skip unknown result types for forward compatibility
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
@@ -52,6 +52,11 @@ const IOCIAXBITS = 0x100
|
||||
const IOCGETATTRX = 0x4068e812
|
||||
const IOCSETATTRX = 0x4068e813
|
||||
const IOCPUNCHOFFLINE = 0x4020e818
|
||||
const IOCRAWREADMETASEQ = 0x8040e819
|
||||
const IOCRAWREADINODEINFO = 0x8028e819
|
||||
|
||||
const RAWREADRESULTINODE = 0x1
|
||||
const RAWREADRESULTXATTR = 0x2
|
||||
|
||||
type InodesEntry struct {
|
||||
Major uint64
|
||||
@@ -248,6 +253,59 @@ type punchOffline struct {
|
||||
Version uint64
|
||||
Flags uint64
|
||||
}
|
||||
type MetaSeqEntry struct {
|
||||
Seq uint64
|
||||
Ino uint64
|
||||
}
|
||||
type rawReadMetaSeq struct {
|
||||
Start MetaSeqEntry
|
||||
End MetaSeqEntry
|
||||
Last MetaSeqEntry
|
||||
Ptr uint64
|
||||
Size uint32
|
||||
X_pad uint32
|
||||
}
|
||||
type rawReadInodeInfo struct {
|
||||
Inos_ptr uint64
|
||||
Inos_count uint32
|
||||
Names_count uint32
|
||||
Names_ptr uint64
|
||||
Results_ptr uint64
|
||||
Results_size uint32
|
||||
X_pad [4]uint8
|
||||
}
|
||||
type RawReadResult struct {
|
||||
Size uint32
|
||||
X_pad [7]uint8
|
||||
Type uint8
|
||||
}
|
||||
type ScoutfsInode struct {
|
||||
Size uint64
|
||||
Meta_seq uint64
|
||||
Data_seq uint64
|
||||
Data_version uint64
|
||||
Online_blocks uint64
|
||||
Offline_blocks uint64
|
||||
Next_readdir_pos uint64
|
||||
Next_xattr_id uint64
|
||||
Version uint64
|
||||
Nlink uint32
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
Mode uint32
|
||||
Rdev uint32
|
||||
Flags uint32
|
||||
Atime ScoutfsTimespec
|
||||
Ctime ScoutfsTimespec
|
||||
Mtime ScoutfsTimespec
|
||||
Crtime ScoutfsTimespec
|
||||
Proj uint64
|
||||
}
|
||||
type ScoutfsTimespec struct {
|
||||
Sec uint64
|
||||
Nsec uint32
|
||||
X__pad [4]uint8
|
||||
}
|
||||
|
||||
const sizeofstatfsMore = 0x30
|
||||
const sizeofxattrTotal = 0x28
|
||||
|
||||
Reference in New Issue
Block a user