vendor: add github.com/hanwen/go-fuse/v2@master for mount2
This commit is contained in:
+389
@@ -0,0 +1,389 @@
|
||||
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/hanwen/go-fuse/v2/fuse"
|
||||
)
|
||||
|
||||
type loopbackRoot struct {
|
||||
loopbackNode
|
||||
|
||||
rootPath string
|
||||
rootDev uint64
|
||||
}
|
||||
|
||||
type loopbackNode struct {
|
||||
Inode
|
||||
}
|
||||
|
||||
var _ = (NodeStatfser)((*loopbackNode)(nil))
|
||||
var _ = (NodeStatfser)((*loopbackNode)(nil))
|
||||
var _ = (NodeGetattrer)((*loopbackNode)(nil))
|
||||
var _ = (NodeGetxattrer)((*loopbackNode)(nil))
|
||||
var _ = (NodeSetxattrer)((*loopbackNode)(nil))
|
||||
var _ = (NodeRemovexattrer)((*loopbackNode)(nil))
|
||||
var _ = (NodeListxattrer)((*loopbackNode)(nil))
|
||||
var _ = (NodeReadlinker)((*loopbackNode)(nil))
|
||||
var _ = (NodeOpener)((*loopbackNode)(nil))
|
||||
var _ = (NodeCopyFileRanger)((*loopbackNode)(nil))
|
||||
var _ = (NodeLookuper)((*loopbackNode)(nil))
|
||||
var _ = (NodeOpendirer)((*loopbackNode)(nil))
|
||||
var _ = (NodeReaddirer)((*loopbackNode)(nil))
|
||||
var _ = (NodeMkdirer)((*loopbackNode)(nil))
|
||||
var _ = (NodeMknoder)((*loopbackNode)(nil))
|
||||
var _ = (NodeLinker)((*loopbackNode)(nil))
|
||||
var _ = (NodeSymlinker)((*loopbackNode)(nil))
|
||||
var _ = (NodeUnlinker)((*loopbackNode)(nil))
|
||||
var _ = (NodeRmdirer)((*loopbackNode)(nil))
|
||||
var _ = (NodeRenamer)((*loopbackNode)(nil))
|
||||
|
||||
func (n *loopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno {
|
||||
s := syscall.Statfs_t{}
|
||||
err := syscall.Statfs(n.path(), &s)
|
||||
if err != nil {
|
||||
return ToErrno(err)
|
||||
}
|
||||
out.FromStatfsT(&s)
|
||||
return OK
|
||||
}
|
||||
|
||||
func (n *loopbackRoot) Getattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno {
|
||||
st := syscall.Stat_t{}
|
||||
err := syscall.Stat(n.rootPath, &st)
|
||||
if err != nil {
|
||||
return ToErrno(err)
|
||||
}
|
||||
out.FromStat(&st)
|
||||
return OK
|
||||
}
|
||||
|
||||
func (n *loopbackNode) root() *loopbackRoot {
|
||||
return n.Root().Operations().(*loopbackRoot)
|
||||
}
|
||||
|
||||
func (n *loopbackNode) path() string {
|
||||
path := n.Path(nil)
|
||||
return filepath.Join(n.root().rootPath, path)
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
|
||||
p := filepath.Join(n.path(), name)
|
||||
|
||||
st := syscall.Stat_t{}
|
||||
err := syscall.Lstat(p, &st)
|
||||
if err != nil {
|
||||
return nil, ToErrno(err)
|
||||
}
|
||||
|
||||
out.Attr.FromStat(&st)
|
||||
node := &loopbackNode{}
|
||||
ch := n.NewInode(ctx, node, n.root().idFromStat(&st))
|
||||
return ch, 0
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) {
|
||||
p := filepath.Join(n.path(), name)
|
||||
err := syscall.Mknod(p, mode, int(rdev))
|
||||
if err != nil {
|
||||
return nil, ToErrno(err)
|
||||
}
|
||||
st := syscall.Stat_t{}
|
||||
if err := syscall.Lstat(p, &st); err != nil {
|
||||
syscall.Rmdir(p)
|
||||
return nil, ToErrno(err)
|
||||
}
|
||||
|
||||
out.Attr.FromStat(&st)
|
||||
|
||||
node := &loopbackNode{}
|
||||
ch := n.NewInode(ctx, node, n.root().idFromStat(&st))
|
||||
|
||||
return ch, 0
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) {
|
||||
p := filepath.Join(n.path(), name)
|
||||
err := os.Mkdir(p, os.FileMode(mode))
|
||||
if err != nil {
|
||||
return nil, ToErrno(err)
|
||||
}
|
||||
st := syscall.Stat_t{}
|
||||
if err := syscall.Lstat(p, &st); err != nil {
|
||||
syscall.Rmdir(p)
|
||||
return nil, ToErrno(err)
|
||||
}
|
||||
|
||||
out.Attr.FromStat(&st)
|
||||
|
||||
node := &loopbackNode{}
|
||||
ch := n.NewInode(ctx, node, n.root().idFromStat(&st))
|
||||
|
||||
return ch, 0
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Rmdir(ctx context.Context, name string) syscall.Errno {
|
||||
p := filepath.Join(n.path(), name)
|
||||
err := syscall.Rmdir(p)
|
||||
return ToErrno(err)
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Unlink(ctx context.Context, name string) syscall.Errno {
|
||||
p := filepath.Join(n.path(), name)
|
||||
err := syscall.Unlink(p)
|
||||
return ToErrno(err)
|
||||
}
|
||||
|
||||
func toLoopbackNode(op InodeEmbedder) *loopbackNode {
|
||||
if r, ok := op.(*loopbackRoot); ok {
|
||||
return &r.loopbackNode
|
||||
}
|
||||
return op.(*loopbackNode)
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Rename(ctx context.Context, name string, newParent InodeEmbedder, newName string, flags uint32) syscall.Errno {
|
||||
newParentLoopback := toLoopbackNode(newParent)
|
||||
if flags&RENAME_EXCHANGE != 0 {
|
||||
return n.renameExchange(name, newParentLoopback, newName)
|
||||
}
|
||||
|
||||
p1 := filepath.Join(n.path(), name)
|
||||
|
||||
p2 := filepath.Join(newParentLoopback.path(), newName)
|
||||
err := os.Rename(p1, p2)
|
||||
return ToErrno(err)
|
||||
}
|
||||
|
||||
func (r *loopbackRoot) idFromStat(st *syscall.Stat_t) StableAttr {
|
||||
// We compose an inode number by the underlying inode, and
|
||||
// mixing in the device number. In traditional filesystems,
|
||||
// the inode numbers are small. The device numbers are also
|
||||
// small (typically 16 bit). Finally, we mask out the root
|
||||
// device number of the root, so a loopback FS that does not
|
||||
// encompass multiple mounts will reflect the inode numbers of
|
||||
// the underlying filesystem
|
||||
swapped := (uint64(st.Dev) << 32) | (uint64(st.Dev) >> 32)
|
||||
swappedRootDev := (r.rootDev << 32) | (r.rootDev >> 32)
|
||||
return StableAttr{
|
||||
Mode: uint32(st.Mode),
|
||||
Gen: 1,
|
||||
// This should work well for traditional backing FSes,
|
||||
// not so much for other go-fuse FS-es
|
||||
Ino: (swapped ^ swappedRootDev) ^ st.Ino,
|
||||
}
|
||||
}
|
||||
|
||||
var _ = (NodeCreater)((*loopbackNode)(nil))
|
||||
|
||||
func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (inode *Inode, fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
||||
p := filepath.Join(n.path(), name)
|
||||
flags = flags &^ syscall.O_APPEND
|
||||
fd, err := syscall.Open(p, int(flags)|os.O_CREATE, mode)
|
||||
if err != nil {
|
||||
return nil, nil, 0, ToErrno(err)
|
||||
}
|
||||
|
||||
st := syscall.Stat_t{}
|
||||
if err := syscall.Fstat(fd, &st); err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, nil, 0, ToErrno(err)
|
||||
}
|
||||
|
||||
node := &loopbackNode{}
|
||||
ch := n.NewInode(ctx, node, n.root().idFromStat(&st))
|
||||
lf := NewLoopbackFile(fd)
|
||||
|
||||
out.FromStat(&st)
|
||||
return ch, lf, 0, 0
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
|
||||
p := filepath.Join(n.path(), name)
|
||||
err := syscall.Symlink(target, p)
|
||||
if err != nil {
|
||||
return nil, ToErrno(err)
|
||||
}
|
||||
st := syscall.Stat_t{}
|
||||
if syscall.Lstat(p, &st); err != nil {
|
||||
syscall.Unlink(p)
|
||||
return nil, ToErrno(err)
|
||||
}
|
||||
node := &loopbackNode{}
|
||||
ch := n.NewInode(ctx, node, n.root().idFromStat(&st))
|
||||
|
||||
out.Attr.FromStat(&st)
|
||||
return ch, 0
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Link(ctx context.Context, target InodeEmbedder, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
|
||||
|
||||
p := filepath.Join(n.path(), name)
|
||||
targetNode := toLoopbackNode(target)
|
||||
err := syscall.Link(targetNode.path(), p)
|
||||
if err != nil {
|
||||
return nil, ToErrno(err)
|
||||
}
|
||||
st := syscall.Stat_t{}
|
||||
if syscall.Lstat(p, &st); err != nil {
|
||||
syscall.Unlink(p)
|
||||
return nil, ToErrno(err)
|
||||
}
|
||||
node := &loopbackNode{}
|
||||
ch := n.NewInode(ctx, node, n.root().idFromStat(&st))
|
||||
|
||||
out.Attr.FromStat(&st)
|
||||
return ch, 0
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
|
||||
p := n.path()
|
||||
|
||||
for l := 256; ; l *= 2 {
|
||||
buf := make([]byte, l)
|
||||
sz, err := syscall.Readlink(p, buf)
|
||||
if err != nil {
|
||||
return nil, ToErrno(err)
|
||||
}
|
||||
|
||||
if sz < len(buf) {
|
||||
return buf[:sz], 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
||||
flags = flags &^ syscall.O_APPEND
|
||||
p := n.path()
|
||||
f, err := syscall.Open(p, int(flags), 0)
|
||||
if err != nil {
|
||||
return nil, 0, ToErrno(err)
|
||||
}
|
||||
lf := NewLoopbackFile(f)
|
||||
return lf, 0, 0
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Opendir(ctx context.Context) syscall.Errno {
|
||||
fd, err := syscall.Open(n.path(), syscall.O_DIRECTORY, 0755)
|
||||
if err != nil {
|
||||
return ToErrno(err)
|
||||
}
|
||||
syscall.Close(fd)
|
||||
return OK
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Readdir(ctx context.Context) (DirStream, syscall.Errno) {
|
||||
return NewLoopbackDirStream(n.path())
|
||||
}
|
||||
|
||||
func (n *loopbackNode) Getattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno {
|
||||
if f != nil {
|
||||
return f.(FileGetattrer).Getattr(ctx, out)
|
||||
}
|
||||
p := n.path()
|
||||
|
||||
var err error = nil
|
||||
st := syscall.Stat_t{}
|
||||
err = syscall.Lstat(p, &st)
|
||||
if err != nil {
|
||||
return ToErrno(err)
|
||||
}
|
||||
out.FromStat(&st)
|
||||
return OK
|
||||
}
|
||||
|
||||
var _ = (NodeSetattrer)((*loopbackNode)(nil))
|
||||
|
||||
func (n *loopbackNode) Setattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
|
||||
p := n.path()
|
||||
fsa, ok := f.(FileSetattrer)
|
||||
if ok && fsa != nil {
|
||||
fsa.Setattr(ctx, in, out)
|
||||
} else {
|
||||
if m, ok := in.GetMode(); ok {
|
||||
if err := syscall.Chmod(p, m); err != nil {
|
||||
return ToErrno(err)
|
||||
}
|
||||
}
|
||||
|
||||
uid, uok := in.GetUID()
|
||||
gid, gok := in.GetGID()
|
||||
if uok || gok {
|
||||
suid := -1
|
||||
sgid := -1
|
||||
if uok {
|
||||
suid = int(uid)
|
||||
}
|
||||
if gok {
|
||||
sgid = int(gid)
|
||||
}
|
||||
if err := syscall.Chown(p, suid, sgid); err != nil {
|
||||
return ToErrno(err)
|
||||
}
|
||||
}
|
||||
|
||||
mtime, mok := in.GetMTime()
|
||||
atime, aok := in.GetATime()
|
||||
|
||||
if mok || aok {
|
||||
|
||||
ap := &atime
|
||||
mp := &mtime
|
||||
if !aok {
|
||||
ap = nil
|
||||
}
|
||||
if !mok {
|
||||
mp = nil
|
||||
}
|
||||
var ts [2]syscall.Timespec
|
||||
ts[0] = fuse.UtimeToTimespec(ap)
|
||||
ts[1] = fuse.UtimeToTimespec(mp)
|
||||
|
||||
if err := syscall.UtimesNano(p, ts[:]); err != nil {
|
||||
return ToErrno(err)
|
||||
}
|
||||
}
|
||||
|
||||
if sz, ok := in.GetSize(); ok {
|
||||
if err := syscall.Truncate(p, int64(sz)); err != nil {
|
||||
return ToErrno(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fga, ok := f.(FileGetattrer)
|
||||
if ok && fga != nil {
|
||||
fga.Getattr(ctx, out)
|
||||
} else {
|
||||
st := syscall.Stat_t{}
|
||||
err := syscall.Lstat(p, &st)
|
||||
if err != nil {
|
||||
return ToErrno(err)
|
||||
}
|
||||
out.FromStat(&st)
|
||||
}
|
||||
return OK
|
||||
}
|
||||
|
||||
// NewLoopback returns a root node for a loopback file system whose
|
||||
// root is at the given root. This node implements all NodeXxxxer
|
||||
// operations available.
|
||||
func NewLoopbackRoot(root string) (InodeEmbedder, error) {
|
||||
var st syscall.Stat_t
|
||||
err := syscall.Stat(root, &st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n := &loopbackRoot{
|
||||
rootPath: root,
|
||||
rootDev: uint64(st.Dev),
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
Reference in New Issue
Block a user