Commit e29d3106 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys Committed by Han-Wen Nienhuys

fs: allow extending LoopbackNode

Make LoopbackNode and LoopbackRoot public, and add an example of how
to extend them.

Fixes #364.

Change-Id: Idcce1bf3765e93ece3bcf9d14de8a957b8c6b63a
parent 09a3c381
...@@ -13,10 +13,10 @@ import ( ...@@ -13,10 +13,10 @@ import (
"github.com/hanwen/go-fuse/v2/fuse" "github.com/hanwen/go-fuse/v2/fuse"
) )
// loopbackRoot holds the parameters for creating a new loopback // LoopbackRoot holds the parameters for creating a new loopback
// filesystem. Loopback filesystem delegate their operations to an // filesystem. Loopback filesystem delegate their operations to an
// underlying POSIX file system. // underlying POSIX file system.
type loopbackRoot struct { type LoopbackRoot struct {
// The path to the root of the underlying file system. // The path to the root of the underlying file system.
Path string Path string
...@@ -27,19 +27,19 @@ type loopbackRoot struct { ...@@ -27,19 +27,19 @@ type loopbackRoot struct {
// NewNode returns a new InodeEmbedder to be used to respond // NewNode returns a new InodeEmbedder to be used to respond
// to a LOOKUP/CREATE/MKDIR/MKNOD opcode. If not set, use a // to a LOOKUP/CREATE/MKDIR/MKNOD opcode. If not set, use a
// LoopbackNode. // LoopbackNode.
NewNode func(rootData *loopbackRoot) InodeEmbedder NewNode func(rootData *LoopbackRoot, parent *Inode, name string, st *syscall.Stat_t) InodeEmbedder
} }
func (r *loopbackRoot) newNode() InodeEmbedder { func (r *LoopbackRoot) newNode(parent *Inode, name string, st *syscall.Stat_t) InodeEmbedder {
if r.NewNode != nil { if r.NewNode != nil {
return r.NewNode(r) return r.NewNode(r, parent, name, st)
} }
return &loopbackNode{ return &LoopbackNode{
RootData: r, RootData: r,
} }
} }
func (r *loopbackRoot) idFromStat(st *syscall.Stat_t) StableAttr { func (r *LoopbackRoot) idFromStat(st *syscall.Stat_t) StableAttr {
// We compose an inode number by the underlying inode, and // We compose an inode number by the underlying inode, and
// mixing in the device number. In traditional filesystems, // mixing in the device number. In traditional filesystems,
// the inode numbers are small. The device numbers are also // the inode numbers are small. The device numbers are also
...@@ -58,35 +58,39 @@ func (r *loopbackRoot) idFromStat(st *syscall.Stat_t) StableAttr { ...@@ -58,35 +58,39 @@ func (r *loopbackRoot) idFromStat(st *syscall.Stat_t) StableAttr {
} }
} }
// LoopbackNode is a filesystem node in a loopback file system. // LoopbackNode is a filesystem node in a loopback file system. It is
type loopbackNode struct { // public so it can be used as a basis for other loopback based
// filesystems. See NewLoopbackFile or LoopbackRoot for more
// information.
type LoopbackNode struct {
Inode Inode
RootData *loopbackRoot // RootData points back to the root of the loopback filesystem.
RootData *LoopbackRoot
} }
var _ = (NodeStatfser)((*loopbackNode)(nil)) var _ = (NodeStatfser)((*LoopbackNode)(nil))
var _ = (NodeStatfser)((*loopbackNode)(nil)) var _ = (NodeStatfser)((*LoopbackNode)(nil))
var _ = (NodeGetattrer)((*loopbackNode)(nil)) var _ = (NodeGetattrer)((*LoopbackNode)(nil))
var _ = (NodeGetxattrer)((*loopbackNode)(nil)) var _ = (NodeGetxattrer)((*LoopbackNode)(nil))
var _ = (NodeSetxattrer)((*loopbackNode)(nil)) var _ = (NodeSetxattrer)((*LoopbackNode)(nil))
var _ = (NodeRemovexattrer)((*loopbackNode)(nil)) var _ = (NodeRemovexattrer)((*LoopbackNode)(nil))
var _ = (NodeListxattrer)((*loopbackNode)(nil)) var _ = (NodeListxattrer)((*LoopbackNode)(nil))
var _ = (NodeReadlinker)((*loopbackNode)(nil)) var _ = (NodeReadlinker)((*LoopbackNode)(nil))
var _ = (NodeOpener)((*loopbackNode)(nil)) var _ = (NodeOpener)((*LoopbackNode)(nil))
var _ = (NodeCopyFileRanger)((*loopbackNode)(nil)) var _ = (NodeCopyFileRanger)((*LoopbackNode)(nil))
var _ = (NodeLookuper)((*loopbackNode)(nil)) var _ = (NodeLookuper)((*LoopbackNode)(nil))
var _ = (NodeOpendirer)((*loopbackNode)(nil)) var _ = (NodeOpendirer)((*LoopbackNode)(nil))
var _ = (NodeReaddirer)((*loopbackNode)(nil)) var _ = (NodeReaddirer)((*LoopbackNode)(nil))
var _ = (NodeMkdirer)((*loopbackNode)(nil)) var _ = (NodeMkdirer)((*LoopbackNode)(nil))
var _ = (NodeMknoder)((*loopbackNode)(nil)) var _ = (NodeMknoder)((*LoopbackNode)(nil))
var _ = (NodeLinker)((*loopbackNode)(nil)) var _ = (NodeLinker)((*LoopbackNode)(nil))
var _ = (NodeSymlinker)((*loopbackNode)(nil)) var _ = (NodeSymlinker)((*LoopbackNode)(nil))
var _ = (NodeUnlinker)((*loopbackNode)(nil)) var _ = (NodeUnlinker)((*LoopbackNode)(nil))
var _ = (NodeRmdirer)((*loopbackNode)(nil)) var _ = (NodeRmdirer)((*LoopbackNode)(nil))
var _ = (NodeRenamer)((*loopbackNode)(nil)) var _ = (NodeRenamer)((*LoopbackNode)(nil))
func (n *loopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno { func (n *LoopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno {
s := syscall.Statfs_t{} s := syscall.Statfs_t{}
err := syscall.Statfs(n.path(), &s) err := syscall.Statfs(n.path(), &s)
if err != nil { if err != nil {
...@@ -96,12 +100,14 @@ func (n *loopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall. ...@@ -96,12 +100,14 @@ func (n *loopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.
return OK return OK
} }
func (n *loopbackNode) path() string { // path returns the full path to the file in the underlying file
// system.
func (n *LoopbackNode) path() string {
path := n.Path(n.Root()) path := n.Path(n.Root())
return filepath.Join(n.RootData.Path, path) return filepath.Join(n.RootData.Path, path)
} }
func (n *loopbackNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) { func (n *LoopbackNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
p := filepath.Join(n.path(), name) p := filepath.Join(n.path(), name)
st := syscall.Stat_t{} st := syscall.Stat_t{}
...@@ -111,14 +117,14 @@ func (n *loopbackNode) Lookup(ctx context.Context, name string, out *fuse.EntryO ...@@ -111,14 +117,14 @@ func (n *loopbackNode) Lookup(ctx context.Context, name string, out *fuse.EntryO
} }
out.Attr.FromStat(&st) out.Attr.FromStat(&st)
node := n.RootData.newNode() node := n.RootData.newNode(n.EmbeddedInode(), name, &st)
ch := n.NewInode(ctx, node, n.RootData.idFromStat(&st)) ch := n.NewInode(ctx, node, n.RootData.idFromStat(&st))
return ch, 0 return ch, 0
} }
// preserveOwner sets uid and gid of `path` according to the caller information // preserveOwner sets uid and gid of `path` according to the caller information
// in `ctx`. // in `ctx`.
func (n *loopbackNode) preserveOwner(ctx context.Context, path string) error { func (n *LoopbackNode) preserveOwner(ctx context.Context, path string) error {
if os.Getuid() != 0 { if os.Getuid() != 0 {
return nil return nil
} }
...@@ -129,7 +135,7 @@ func (n *loopbackNode) preserveOwner(ctx context.Context, path string) error { ...@@ -129,7 +135,7 @@ func (n *loopbackNode) preserveOwner(ctx context.Context, path string) error {
return syscall.Lchown(path, int(caller.Uid), int(caller.Gid)) return syscall.Lchown(path, int(caller.Uid), int(caller.Gid))
} }
func (n *loopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) { func (n *LoopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) {
p := filepath.Join(n.path(), name) p := filepath.Join(n.path(), name)
err := syscall.Mknod(p, mode, int(rdev)) err := syscall.Mknod(p, mode, int(rdev))
if err != nil { if err != nil {
...@@ -144,13 +150,13 @@ func (n *loopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32 ...@@ -144,13 +150,13 @@ func (n *loopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32
out.Attr.FromStat(&st) out.Attr.FromStat(&st)
node := n.RootData.newNode() node := n.RootData.newNode(n.EmbeddedInode(), name, &st)
ch := n.NewInode(ctx, node, n.RootData.idFromStat(&st)) ch := n.NewInode(ctx, node, n.RootData.idFromStat(&st))
return ch, 0 return ch, 0
} }
func (n *loopbackNode) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) { func (n *LoopbackNode) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) {
p := filepath.Join(n.path(), name) p := filepath.Join(n.path(), name)
err := os.Mkdir(p, os.FileMode(mode)) err := os.Mkdir(p, os.FileMode(mode))
if err != nil { if err != nil {
...@@ -165,25 +171,25 @@ func (n *loopbackNode) Mkdir(ctx context.Context, name string, mode uint32, out ...@@ -165,25 +171,25 @@ func (n *loopbackNode) Mkdir(ctx context.Context, name string, mode uint32, out
out.Attr.FromStat(&st) out.Attr.FromStat(&st)
node := n.RootData.newNode() node := n.RootData.newNode(n.EmbeddedInode(), name, &st)
ch := n.NewInode(ctx, node, n.RootData.idFromStat(&st)) ch := n.NewInode(ctx, node, n.RootData.idFromStat(&st))
return ch, 0 return ch, 0
} }
func (n *loopbackNode) Rmdir(ctx context.Context, name string) syscall.Errno { func (n *LoopbackNode) Rmdir(ctx context.Context, name string) syscall.Errno {
p := filepath.Join(n.path(), name) p := filepath.Join(n.path(), name)
err := syscall.Rmdir(p) err := syscall.Rmdir(p)
return ToErrno(err) return ToErrno(err)
} }
func (n *loopbackNode) Unlink(ctx context.Context, name string) syscall.Errno { func (n *LoopbackNode) Unlink(ctx context.Context, name string) syscall.Errno {
p := filepath.Join(n.path(), name) p := filepath.Join(n.path(), name)
err := syscall.Unlink(p) err := syscall.Unlink(p)
return ToErrno(err) return ToErrno(err)
} }
func (n *loopbackNode) Rename(ctx context.Context, name string, newParent InodeEmbedder, newName string, flags uint32) syscall.Errno { func (n *LoopbackNode) Rename(ctx context.Context, name string, newParent InodeEmbedder, newName string, flags uint32) syscall.Errno {
if flags&RENAME_EXCHANGE != 0 { if flags&RENAME_EXCHANGE != 0 {
return n.renameExchange(name, newParent, newName) return n.renameExchange(name, newParent, newName)
} }
...@@ -195,9 +201,9 @@ func (n *loopbackNode) Rename(ctx context.Context, name string, newParent InodeE ...@@ -195,9 +201,9 @@ func (n *loopbackNode) Rename(ctx context.Context, name string, newParent InodeE
return ToErrno(err) return ToErrno(err)
} }
var _ = (NodeCreater)((*loopbackNode)(nil)) 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) { 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) p := filepath.Join(n.path(), name)
flags = flags &^ syscall.O_APPEND flags = flags &^ syscall.O_APPEND
fd, err := syscall.Open(p, int(flags)|os.O_CREATE, mode) fd, err := syscall.Open(p, int(flags)|os.O_CREATE, mode)
...@@ -211,7 +217,7 @@ func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mo ...@@ -211,7 +217,7 @@ func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mo
return nil, nil, 0, ToErrno(err) return nil, nil, 0, ToErrno(err)
} }
node := n.RootData.newNode() node := n.RootData.newNode(n.EmbeddedInode(), name, &st)
ch := n.NewInode(ctx, node, n.RootData.idFromStat(&st)) ch := n.NewInode(ctx, node, n.RootData.idFromStat(&st))
lf := NewLoopbackFile(fd) lf := NewLoopbackFile(fd)
...@@ -219,7 +225,7 @@ func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mo ...@@ -219,7 +225,7 @@ func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mo
return ch, lf, 0, 0 return ch, lf, 0, 0
} }
func (n *loopbackNode) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) { func (n *LoopbackNode) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
p := filepath.Join(n.path(), name) p := filepath.Join(n.path(), name)
err := syscall.Symlink(target, p) err := syscall.Symlink(target, p)
if err != nil { if err != nil {
...@@ -231,14 +237,14 @@ func (n *loopbackNode) Symlink(ctx context.Context, target, name string, out *fu ...@@ -231,14 +237,14 @@ func (n *loopbackNode) Symlink(ctx context.Context, target, name string, out *fu
syscall.Unlink(p) syscall.Unlink(p)
return nil, ToErrno(err) return nil, ToErrno(err)
} }
node := n.RootData.newNode() node := n.RootData.newNode(n.EmbeddedInode(), name, &st)
ch := n.NewInode(ctx, node, n.RootData.idFromStat(&st)) ch := n.NewInode(ctx, node, n.RootData.idFromStat(&st))
out.Attr.FromStat(&st) out.Attr.FromStat(&st)
return ch, 0 return ch, 0
} }
func (n *loopbackNode) Link(ctx context.Context, target InodeEmbedder, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) { func (n *LoopbackNode) Link(ctx context.Context, target InodeEmbedder, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
p := filepath.Join(n.path(), name) p := filepath.Join(n.path(), name)
err := syscall.Link(filepath.Join(n.RootData.Path, target.EmbeddedInode().Path(nil)), p) err := syscall.Link(filepath.Join(n.RootData.Path, target.EmbeddedInode().Path(nil)), p)
...@@ -250,14 +256,14 @@ func (n *loopbackNode) Link(ctx context.Context, target InodeEmbedder, name stri ...@@ -250,14 +256,14 @@ func (n *loopbackNode) Link(ctx context.Context, target InodeEmbedder, name stri
syscall.Unlink(p) syscall.Unlink(p)
return nil, ToErrno(err) return nil, ToErrno(err)
} }
node := n.RootData.newNode() node := n.RootData.newNode(n.EmbeddedInode(), name, &st)
ch := n.NewInode(ctx, node, n.RootData.idFromStat(&st)) ch := n.NewInode(ctx, node, n.RootData.idFromStat(&st))
out.Attr.FromStat(&st) out.Attr.FromStat(&st)
return ch, 0 return ch, 0
} }
func (n *loopbackNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) { func (n *LoopbackNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
p := n.path() p := n.path()
for l := 256; ; l *= 2 { for l := 256; ; l *= 2 {
...@@ -273,7 +279,7 @@ func (n *loopbackNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) { ...@@ -273,7 +279,7 @@ func (n *loopbackNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
} }
} }
func (n *loopbackNode) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) { func (n *LoopbackNode) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
flags = flags &^ syscall.O_APPEND flags = flags &^ syscall.O_APPEND
p := n.path() p := n.path()
f, err := syscall.Open(p, int(flags), 0) f, err := syscall.Open(p, int(flags), 0)
...@@ -284,7 +290,7 @@ func (n *loopbackNode) Open(ctx context.Context, flags uint32) (fh FileHandle, f ...@@ -284,7 +290,7 @@ func (n *loopbackNode) Open(ctx context.Context, flags uint32) (fh FileHandle, f
return lf, 0, 0 return lf, 0, 0
} }
func (n *loopbackNode) Opendir(ctx context.Context) syscall.Errno { func (n *LoopbackNode) Opendir(ctx context.Context) syscall.Errno {
fd, err := syscall.Open(n.path(), syscall.O_DIRECTORY, 0755) fd, err := syscall.Open(n.path(), syscall.O_DIRECTORY, 0755)
if err != nil { if err != nil {
return ToErrno(err) return ToErrno(err)
...@@ -293,11 +299,11 @@ func (n *loopbackNode) Opendir(ctx context.Context) syscall.Errno { ...@@ -293,11 +299,11 @@ func (n *loopbackNode) Opendir(ctx context.Context) syscall.Errno {
return OK return OK
} }
func (n *loopbackNode) Readdir(ctx context.Context) (DirStream, syscall.Errno) { func (n *LoopbackNode) Readdir(ctx context.Context) (DirStream, syscall.Errno) {
return NewLoopbackDirStream(n.path()) return NewLoopbackDirStream(n.path())
} }
func (n *loopbackNode) Getattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno { func (n *LoopbackNode) Getattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno {
if f != nil { if f != nil {
return f.(FileGetattrer).Getattr(ctx, out) return f.(FileGetattrer).Getattr(ctx, out)
} }
...@@ -319,9 +325,9 @@ func (n *loopbackNode) Getattr(ctx context.Context, f FileHandle, out *fuse.Attr ...@@ -319,9 +325,9 @@ func (n *loopbackNode) Getattr(ctx context.Context, f FileHandle, out *fuse.Attr
return OK return OK
} }
var _ = (NodeSetattrer)((*loopbackNode)(nil)) var _ = (NodeSetattrer)((*LoopbackNode)(nil))
func (n *loopbackNode) Setattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno { func (n *LoopbackNode) Setattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
p := n.path() p := n.path()
fsa, ok := f.(FileSetattrer) fsa, ok := f.(FileSetattrer)
if ok && fsa != nil { if ok && fsa != nil {
...@@ -402,10 +408,10 @@ func NewLoopbackRoot(rootPath string) (InodeEmbedder, error) { ...@@ -402,10 +408,10 @@ func NewLoopbackRoot(rootPath string) (InodeEmbedder, error) {
return nil, err return nil, err
} }
root := &loopbackRoot{ root := &LoopbackRoot{
Path: rootPath, Path: rootPath,
Dev: uint64(st.Dev), Dev: uint64(st.Dev),
} }
return root.newNode(), nil return root.newNode(nil, "", &st), nil
} }
...@@ -16,23 +16,23 @@ import ( ...@@ -16,23 +16,23 @@ import (
"github.com/hanwen/go-fuse/v2/internal/utimens" "github.com/hanwen/go-fuse/v2/internal/utimens"
) )
func (n *loopbackNode) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) { func (n *LoopbackNode) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) {
return 0, syscall.ENOSYS return 0, syscall.ENOSYS
} }
func (n *loopbackNode) Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno { func (n *LoopbackNode) Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno {
return syscall.ENOSYS return syscall.ENOSYS
} }
func (n *loopbackNode) Removexattr(ctx context.Context, attr string) syscall.Errno { func (n *LoopbackNode) Removexattr(ctx context.Context, attr string) syscall.Errno {
return syscall.ENOSYS return syscall.ENOSYS
} }
func (n *loopbackNode) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) { func (n *LoopbackNode) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) {
return 0, syscall.ENOSYS return 0, syscall.ENOSYS
} }
func (n *loopbackNode) renameExchange(name string, newparent InodeEmbedder, newName string) syscall.Errno { func (n *LoopbackNode) renameExchange(name string, newparent InodeEmbedder, newName string) syscall.Errno {
return syscall.ENOSYS return syscall.ENOSYS
} }
...@@ -111,7 +111,7 @@ func (f *loopbackFile) utimens(a *time.Time, m *time.Time) syscall.Errno { ...@@ -111,7 +111,7 @@ func (f *loopbackFile) utimens(a *time.Time, m *time.Time) syscall.Errno {
return ToErrno(err) return ToErrno(err)
} }
func (n *loopbackNode) CopyFileRange(ctx context.Context, fhIn FileHandle, func (n *LoopbackNode) CopyFileRange(ctx context.Context, fhIn FileHandle,
offIn uint64, out *Inode, fhOut FileHandle, offOut uint64, offIn uint64, out *Inode, fhOut FileHandle, offOut uint64,
len uint64, flags uint64) (uint32, syscall.Errno) { len uint64, flags uint64) (uint32, syscall.Errno) {
return 0, syscall.ENOSYS return 0, syscall.ENOSYS
......
...@@ -14,27 +14,27 @@ import ( ...@@ -14,27 +14,27 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
func (n *loopbackNode) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) { func (n *LoopbackNode) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) {
sz, err := unix.Lgetxattr(n.path(), attr, dest) sz, err := unix.Lgetxattr(n.path(), attr, dest)
return uint32(sz), ToErrno(err) return uint32(sz), ToErrno(err)
} }
func (n *loopbackNode) Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno { func (n *LoopbackNode) Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno {
err := unix.Lsetxattr(n.path(), attr, data, int(flags)) err := unix.Lsetxattr(n.path(), attr, data, int(flags))
return ToErrno(err) return ToErrno(err)
} }
func (n *loopbackNode) Removexattr(ctx context.Context, attr string) syscall.Errno { func (n *LoopbackNode) Removexattr(ctx context.Context, attr string) syscall.Errno {
err := unix.Lremovexattr(n.path(), attr) err := unix.Lremovexattr(n.path(), attr)
return ToErrno(err) return ToErrno(err)
} }
func (n *loopbackNode) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) { func (n *LoopbackNode) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) {
sz, err := unix.Llistxattr(n.path(), dest) sz, err := unix.Llistxattr(n.path(), dest)
return uint32(sz), ToErrno(err) return uint32(sz), ToErrno(err)
} }
func (n *loopbackNode) renameExchange(name string, newparent InodeEmbedder, newName string) syscall.Errno { func (n *LoopbackNode) renameExchange(name string, newparent InodeEmbedder, newName string) syscall.Errno {
fd1, err := syscall.Open(n.path(), syscall.O_DIRECTORY, 0) fd1, err := syscall.Open(n.path(), syscall.O_DIRECTORY, 0)
if err != nil { if err != nil {
return ToErrno(err) return ToErrno(err)
...@@ -69,7 +69,7 @@ func (n *loopbackNode) renameExchange(name string, newparent InodeEmbedder, newN ...@@ -69,7 +69,7 @@ func (n *loopbackNode) renameExchange(name string, newparent InodeEmbedder, newN
return ToErrno(unix.Renameat2(fd1, name, fd2, newName, unix.RENAME_EXCHANGE)) return ToErrno(unix.Renameat2(fd1, name, fd2, newName, unix.RENAME_EXCHANGE))
} }
func (n *loopbackNode) CopyFileRange(ctx context.Context, fhIn FileHandle, func (n *LoopbackNode) CopyFileRange(ctx context.Context, fhIn FileHandle,
offIn uint64, out *Inode, fhOut FileHandle, offOut uint64, offIn uint64, out *Inode, fhOut FileHandle, offOut uint64,
len uint64, flags uint64) (uint32, syscall.Errno) { len uint64, flags uint64) (uint32, syscall.Errno) {
lfIn, ok := fhIn.(*loopbackFile) lfIn, ok := fhIn.(*loopbackFile)
......
// Copyright 2020 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_test
import (
"context"
"fmt"
"log"
"sync"
"syscall"
"time"
"github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse"
)
// WindowsNode emulates Windows FS semantics, which forbids deleting open files.
type WindowsNode struct {
// WindowsNode inherits most functionality from LoopbackNode.
fs.LoopbackNode
mu sync.Mutex
openCount int
}
var _ = (fs.NodeOpener)((*WindowsNode)(nil))
func (n *WindowsNode) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) {
fh, flags, errno := n.LoopbackNode.Open(ctx, flags)
if errno == 0 {
n.mu.Lock()
defer n.mu.Unlock()
n.openCount++
}
return fh, flags, errno
}
var _ = (fs.NodeCreater)((*WindowsNode)(nil))
func (n *WindowsNode) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (*fs.Inode, fs.FileHandle, uint32, syscall.Errno) {
inode, fh, flags, errno := n.LoopbackNode.Create(ctx, name, flags, mode, out)
if errno == 0 {
wn := inode.Operations().(*WindowsNode)
wn.openCount++
}
return inode, fh, flags, errno
}
var _ = (fs.NodeReleaser)((*WindowsNode)(nil))
// Release decreases the open count. The kernel doesn't wait with
// returning from close(), so if the caller is too quick to
// unlink/rename after calling close(), this may still trigger EBUSY.
func (n *WindowsNode) Release(ctx context.Context, f fs.FileHandle) syscall.Errno {
n.mu.Lock()
defer n.mu.Unlock()
n.openCount--
if fr, ok := f.(fs.FileReleaser); ok {
return fr.Release(ctx)
}
return 0
}
func isBusy(parent *fs.Inode, name string) bool {
if ch := parent.GetChild(name); ch != nil {
if wn, ok := ch.Operations().(*WindowsNode); ok {
wn.mu.Lock()
defer wn.mu.Unlock()
if wn.openCount > 0 {
return true
}
}
}
return false
}
var _ = (fs.NodeUnlinker)((*WindowsNode)(nil))
func (n *WindowsNode) Unlink(ctx context.Context, name string) syscall.Errno {
if isBusy(n.EmbeddedInode(), name) {
return syscall.EBUSY
}
return n.LoopbackNode.Unlink(ctx, name)
}
func newWindowsNode(rootData *fs.LoopbackRoot, parent *fs.Inode, name string, st *syscall.Stat_t) fs.InodeEmbedder {
n := &WindowsNode{
LoopbackNode: fs.LoopbackNode{
RootData: rootData,
},
}
return n
}
// ExampleLoopbackReuse shows how to build a file system on top of the
// loopback file system.
func Example_loopbackReuse() {
mntDir := "/tmp/mnt"
origDir := "/tmp/orig"
rootData := &fs.LoopbackRoot{
NewNode: newWindowsNode,
Path: origDir,
}
sec := time.Second
opts := &fs.Options{
AttrTimeout: &sec,
EntryTimeout: &sec,
}
server, err := fs.Mount(mntDir, newWindowsNode(rootData, nil, "", nil), opts)
if err != nil {
log.Fatalf("Mount fail: %v\n", err)
}
fmt.Printf("files under %s cannot be deleted if they are opened", mntDir)
server.Wait()
}
// Copyright 2020 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_test
import (
"bytes"
"io/ioutil"
"os"
"syscall"
"testing"
"time"
"github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/internal/testutil"
)
func TestWindowsEmulations(t *testing.T) {
mntDir, err := ioutil.TempDir("", "ZipFS")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(mntDir)
origDir, err := ioutil.TempDir("", "ZipFS")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(origDir)
rootData := &fs.LoopbackRoot{
NewNode: newWindowsNode,
Path: origDir,
}
opts := fs.Options{}
opts.Debug = testutil.VerboseTest()
server, err := fs.Mount(mntDir, newWindowsNode(rootData, nil, "", nil), &opts)
if err != nil {
t.Fatal(err)
}
defer server.Unmount()
data := []byte("hello")
nm := mntDir + "/file"
if err := ioutil.WriteFile(nm, data, 0644); err != nil {
t.Fatal(err)
}
if got, err := ioutil.ReadFile(nm); err != nil {
t.Fatal(err)
} else if bytes.Compare(got, data) != 0 {
t.Fatalf("got %q want %q", got, data)
}
f, err := os.Open(nm)
if err != nil {
t.Fatal(err)
}
if err := syscall.Unlink(nm); err == nil {
t.Fatal("Unlink should have failed")
}
f.Close()
// Ugh - it may take a while for the RELEASE to be processed.
time.Sleep(10 * time.Millisecond)
if err := syscall.Unlink(nm); err != nil {
t.Fatalf("Unlink: %v", err)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment