diff --git a/c_defs_linux.go b/c_defs_linux.go index 5a0dd89..6e48537 100644 --- a/c_defs_linux.go +++ b/c_defs_linux.go @@ -93,6 +93,7 @@ const IOCDELQUOTARULE = C.SCOUTFS_IOC_DEL_QUOTA_RULE const IOCADDQUOTARULE = C.SCOUTFS_IOC_ADD_QUOTA_RULE const IOCGETPROJECTID = C.SCOUTFS_IOC_GET_PROJECT_ID const IOCSETPROJECTID = C.SCOUTFS_IOC_SET_PROJECT_ID +const IOCREADXATTRINDEX = C.SCOUTFS_IOC_READ_XATTR_INDEX const QUERYINODESMETASEQ = C.SCOUTFS_IOC_WALK_INODES_META_SEQ const QUERYINODESDATASEQ = C.SCOUTFS_IOC_WALK_INODES_DATA_SEQ diff --git a/examples/xattridx/main.go b/examples/xattridx/main.go new file mode 100644 index 0000000..b40d92e --- /dev/null +++ b/examples/xattridx/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + + "github.com/versity/scoutfs-go" +) + +func main() { + // flags mount path name + mountPath := flag.String("mount", "", "mount path name") + // flags index type uint64 + indexType := flag.Uint64("type", 0, "index type") + // flags index start uint64 + indexStart := flag.Uint64("start", 0, "index start") + // flags index end uint64 + indexEnd := flag.Uint64("end", 0, "index end") + // flags show filenames + resolveNames := flag.Bool("resolve", false, "show filenames") + + flag.Parse() + + f, err := os.Open(*mountPath) + if err != nil { + log.Fatalf("Error opening mount: %s", err) + } + defer f.Close() + + idx := scoutfs.NewIndexSearch(f, *indexType, *indexStart, *indexEnd) + for { + ents, err := idx.Next() + if err != nil { + log.Fatalf("Error reading index: %v", err) + } + if ents == nil { + break + } + + for _, e := range ents { + if *resolveNames { + name, err := scoutfs.InoToPath(f, e.Inode) + if err != nil { + log.Fatalf("Error resolving name for %v: %V", e.Inode, err) + continue + } + fmt.Println(name) + continue + } + fmt.Println(e.Inode, "=", e.Value) + } + } +} diff --git a/scoutfs.go b/scoutfs.go index d34adef..0a547b7 100644 --- a/scoutfs.go +++ b/scoutfs.go @@ -495,7 +495,7 @@ type XattrQuery struct { // NewXattrQuery creates a new scoutfs Xattr Query // Specify query xattr key -// and specify optinally batching with WithXBatchSize() +// and specify optionally batching with WithXBatchSize() // An open file within scoutfs is supplied for ioctls // (usually just the base mount point directory) func NewXattrQuery(f *os.File, key string, opts ...XOption) *XattrQuery { @@ -577,7 +577,9 @@ func (q *XattrQuery) Next() ([]uint64, error) { } q.next = e - q.next++ + if q.next < math.MaxUint64 { + q.next++ + } return inodes, nil } @@ -1660,3 +1662,121 @@ func SetProjectID(f *os.File, projectid uint64) error { _, err := scoutfsctl(f, IOCSETPROJECTID, unsafe.Pointer(&projectid)) return err } + +type IndexSearch struct { + // file handle for ioctls + f *os.File + // The index search end + end indexEntry + // last returned position + pos indexEntry + // max number of inodes to return per Next() call + batch uint64 + // buffer for return results + buf []byte +} + +const ( + indexXattrBatch = 1024 +) + +func NewIndexSearch(f *os.File, key, start, end uint64, opts ...IOption) *IndexSearch { + i := &IndexSearch{ + f: f, + pos: indexEntry{ + A: key, + B: start, + }, + end: indexEntry{ + A: key, + B: end, + Ino: math.MaxUint64, + }, + batch: indexXattrBatch, + } + + for _, opt := range opts { + opt(i) + } + + i.buf = make([]byte, int(unsafe.Sizeof(indexEntry{}))*indexXattrBatch) + + return i +} + +// IOption sets various options for NewIndexSearch +type IOption func(*IndexSearch) + +// WithIBatchSize sets the max number of inodes to be returned at a time +func WithIBatchSize(size uint64) IOption { + return func(q *IndexSearch) { + q.batch = size + } +} + +// WithIStartIno starts query at speficied inode +func WithIStartIno(ino uint64) IOption { + return func(q *IndexSearch) { + q.pos.Ino = ino + } +} + +// WithIEndIno stop query at speficied inode +func WithIEndIno(ino uint64) IOption { + return func(q *IndexSearch) { + q.end.Ino = ino + } +} + +// IndexEnt is type returned by index query Next() call +type IndexEnt struct { + Inode uint64 + Value uint64 +} + +func (i *IndexSearch) Next() ([]IndexEnt, error) { + query := readXattrIndex{ + First: i.pos, + Last: i.end, + Ptr: uint64(uintptr(unsafe.Pointer(&i.buf[0]))), + Nr: indexXattrBatch, + } + + n, err := scoutfsctl(i.f, IOCREADXATTRINDEX, unsafe.Pointer(&query)) + if err != nil { + return nil, err + } + + if n == 0 { + return nil, nil + } + + rbuf := bytes.NewReader(i.buf) + inodes := make([]IndexEnt, n) + + var e indexEntry + for i := 0; i < n; i++ { + err := binary.Read(rbuf, binary.LittleEndian, &e) + if err != nil { + return nil, err + } + + inodes[i].Inode = e.Ino + inodes[i].Value = e.B + } + i.pos = e.increment() + + return inodes, nil +} + +// increment returns the next seq entry position +func (i indexEntry) increment() indexEntry { + i.Ino++ + if i.Ino == 0 { + i.B++ + if i.B == 0 { + i.A++ + } + } + return i +} diff --git a/scoutfsdefs.go b/scoutfsdefs.go index 28c7e37..8dafd20 100644 --- a/scoutfsdefs.go +++ b/scoutfsdefs.go @@ -23,6 +23,7 @@ const IOCDELQUOTARULE = 0x4030e816 const IOCADDQUOTARULE = 0x4030e815 const IOCGETPROJECTID = 0x8008e812 const IOCSETPROJECTID = 0x4008e813 +const IOCREADXATTRINDEX = 0x8048e817 const QUERYINODESMETASEQ = 0x0 const QUERYINODESDATASEQ = 0x1