diff --git a/c_defs_linux.go b/c_defs_linux.go index 911e4f8..32bad2f 100644 --- a/c_defs_linux.go +++ b/c_defs_linux.go @@ -53,6 +53,8 @@ package scoutfs // typedef struct scoutfs_ioctl_alloc_detail scoutfs_ioctl_alloc_detail_t; // typedef struct scoutfs_ioctl_read_xattr_totals scoutfs_ioctl_read_xattr_totals_t; // typedef struct scoutfs_ioctl_xattr_total scoutfs_ioctl_xattr_total_t; +// typedef struct scoutfs_ioctl_get_referring_entries scoutfs_ioctl_get_referring_entries_t; +// typedef struct scoutfs_ioctl_dirent scoutfs_ioctl_dirent_t; // // // Go doesnt handle bitfields in structs, so we need to override the scoutfs // // struct definition here @@ -81,6 +83,7 @@ const IOCDATAWAITERR = C.SCOUTFS_IOC_DATA_WAIT_ERR const IOCALLOCDETAIL = C.SCOUTFS_IOC_ALLOC_DETAIL const IOCMOVEBLOCKS = C.SCOUTFS_IOC_MOVE_BLOCKS const IOCREADXATTRTOTALS = C.SCOUTFS_IOC_READ_XATTR_TOTALS +const IOCGETREFERRINGENTRIES = C.SCOUTFS_IOC_GET_REFERRING_ENTRIES const QUERYINODESMETASEQ = C.SCOUTFS_IOC_WALK_INODES_META_SEQ const QUERYINODESDATASEQ = C.SCOUTFS_IOC_WALK_INODES_DATA_SEQ @@ -93,6 +96,8 @@ const SEARCHXATTRSOFLAGEND = C.SCOUTFS_SEARCH_XATTRS_OFLAG_END const MBSTAGEFLG = C.SCOUTFS_IOC_MB_STAGE +const DIRENTFLAGLAST = C.SCOUTFS_IOCTL_DIRENT_FLAG_LAST + type InodesEntry C.scoutfs_ioctl_walk_inodes_entry_t type queryInodes C.scoutfs_ioctl_walk_inodes_t type inoPath C.scoutfs_ioctl_ino_path_t @@ -111,6 +116,8 @@ type allocDetailEntry C.scoutfs_ioctl_alloc_detail_entry_t type moveBlocks C.scoutfs_ioctl_move_blocks_t type readXattrTotals C.scoutfs_ioctl_read_xattr_totals_t type xattrTotal C.scoutfs_ioctl_xattr_total_t +type getReferringEntries C.scoutfs_ioctl_get_referring_entries_t +type scoutfsDirent C.scoutfs_ioctl_dirent_t const sizeofstatfsMore = C.sizeof_scoutfs_ioctl_statfs_more_t const sizeofxattrTotal = C.sizeof_scoutfs_ioctl_xattr_total_t diff --git a/examples/getparents/main.go b/examples/getparents/main.go new file mode 100644 index 0000000..88f7798 --- /dev/null +++ b/examples/getparents/main.go @@ -0,0 +1,41 @@ +// Copyright (c) 2018 Versity Software, Inc. +// +// Use of this source code is governed by a BSD-3-Clause license +// that can be found in the LICENSE file in the root of the source +// tree. + +package main + +import ( + "fmt" + "log" + "os" + "strconv" + + scoutfs "github.com/versity/scoutfs-go" +) + +func main() { + if len(os.Args) != 3 || os.Args[1] == "-h" { + fmt.Fprintln(os.Stderr, "usage:", os.Args[0], " ") + os.Exit(1) + } + + f, err := os.Open(os.Args[1]) + if err != nil { + log.Fatalln("error open mount:", err) + } + defer f.Close() + + u, err := strconv.ParseUint(os.Args[2], 10, 64) + if err != nil { + log.Fatalln("error parsing inode:", err) + } + + s, err := scoutfs.GetParents(f, u, nil) + if err != nil { + log.Fatalln("error get parents:", err) + } + + fmt.Println(s) +} diff --git a/scoutfs.go b/scoutfs.go index 1c8a0d5..5d767ea 100644 --- a/scoutfs.go +++ b/scoutfs.go @@ -10,6 +10,7 @@ import ( "bufio" "bytes" "encoding/binary" + "errors" "fmt" "io" "math" @@ -23,13 +24,14 @@ import ( ) const ( - max64 = 0xffffffffffffffff - max32 = 0xffffffff - pathmax = 4096 - sysscoutfs = "/sys/fs/scoutfs/" - statusfile = "quorum/status" - listattrBufsize = 256 * 1024 - scoutfsBS = 4096 + max64 = 0xffffffffffffffff + max32 = 0xffffffff + pathmax = 4096 + sysscoutfs = "/sys/fs/scoutfs/" + statusfile = "quorum/status" + listattrBufsize = 256 * 1024 + getparentBufsize = 4096 * 1024 + scoutfsBS = 4096 //leaderfile = "quorum/is_leader" ) @@ -1019,3 +1021,116 @@ func (t *TotalsGroup) Reset() { t.pos[1] = t.id2 t.pos[2] = 0 } + +// Parent contains inode of parent and what the child inode is named within +// this parent +type Parent struct { + Ino uint64 // Parent inode + Pos uint64 // Entry directory position in parent + Type uint8 // Entry inode type matching DT_ enum values in readdir(3) + Ent string // Entry name as known by parent +} + +// GetParents returns all parents for the given inode +// An open file within scoutfs is supplied for ioctls +// (usually just the base mount point directory) +// If passed in buffer is nil, call will allocate its own buffer. +func GetParents(dirfd *os.File, ino uint64, b []byte) ([]Parent, error) { + if b == nil { + b = make([]byte, getparentBufsize) + } + + gre := getReferringEntries{} + + gre.Entries_bytes = uint64(len(b)) + gre.Entries_ptr = uint64(uintptr(unsafe.Pointer(&b[0]))) + gre.Ino = ino + + var parents []Parent + + for { + n, err := scoutfsctl(dirfd, IOCGETREFERRINGENTRIES, unsafe.Pointer(&gre)) + if err != nil { + return nil, err + } + if n == 0 { + break + } + + ents, isLast, err := parseDents(b) + if err != nil { + return nil, err + } + + parents = append(parents, ents...) + if isLast { + break + } + } + + return parents, nil +} + +func parseDents(b []byte) ([]Parent, bool, error) { + r := bytes.NewReader(b) + var parents []Parent + var isLast bool + for { + var err error + var parent Parent + parent, isLast, err = parseDent(r) + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return nil, false, err + } + parents = append(parents, parent) + if isLast { + break + } + if r.Len() == 0 { + break + } + } + return parents, isLast, nil +} + +type dirent struct { + Dir_ino uint64 + Dir_pos uint64 + Ino uint64 + Entry_bytes uint16 + Flags uint8 + D_type uint8 + Name_len uint8 +} + +func parseDent(r *bytes.Reader) (Parent, bool, error) { + var dent dirent + err := binary.Read(r, binary.LittleEndian, &dent) + if err != nil { + return Parent{}, false, err + } + + b := new(strings.Builder) + _, err = io.CopyN(b, r, int64(dent.Name_len)) + if err != nil { + return Parent{}, false, err + } + + pad := int(dent.Entry_bytes) - int(unsafe.Sizeof(dent)) + int(dent.Name_len) + for i := 0; i < pad; i++ { + _, err = r.ReadByte() + if err != nil { + return Parent{}, false, err + } + } + + return Parent{ + Ino: dent.Dir_ino, + Pos: dent.Dir_pos, + Type: dent.D_type, + Ent: b.String(), + }, dent.Flags&DIRENTFLAGLAST == DIRENTFLAGLAST, nil +} diff --git a/scoutfsdefs.go b/scoutfsdefs.go index 16565ce..618c191 100644 --- a/scoutfsdefs.go +++ b/scoutfsdefs.go @@ -1,5 +1,5 @@ // Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs /workspace/go/src/github.com/versity/scoutfs-go/c_defs_linux.go +// cgo -godefs c_defs_linux.go package scoutfs @@ -17,6 +17,7 @@ const IOCDATAWAITERR = 0x4030e80b const IOCALLOCDETAIL = 0x4010e80c const IOCMOVEBLOCKS = 0x4030e80d const IOCREADXATTRTOTALS = 0x4028e80f +const IOCGETREFERRINGENTRIES = 0x4028e811 const QUERYINODESMETASEQ = 0x0 const QUERYINODESDATASEQ = 0x1 @@ -29,6 +30,8 @@ const SEARCHXATTRSOFLAGEND = 0x1 const MBSTAGEFLG = 0x1 +const DIRENTFLAGLAST = 0x1 + type InodesEntry struct { Major uint64 Ino uint64 @@ -157,6 +160,23 @@ type xattrTotal struct { Total uint64 Count uint64 } +type getReferringEntries struct { + Ino uint64 + Dir_ino uint64 + Dir_pos uint64 + Entries_ptr uint64 + Entries_bytes uint64 +} +type scoutfsDirent struct { + Dir_ino uint64 + Dir_pos uint64 + Ino uint64 + Entry_bytes uint16 + Flags uint8 + D_type uint8 + Name_len uint8 + Name [3]uint8 +} const sizeofstatfsMore = 0x30 const sizeofxattrTotal = 0x28