Skip to content
This repository has been archived by the owner on Aug 13, 2019. It is now read-only.

Commit

Permalink
add-go-fuse-to-inject-filesystem-error
Browse files Browse the repository at this point in the history
  • Loading branch information
qiffang committed Apr 16, 2019
1 parent 4b3a5ac commit 9d9c978
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 0 deletions.
200 changes: 200 additions & 0 deletions testutil/fs_hook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package testutil

import (
"fmt"
"path/filepath"
"time"

"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/fuse/pathfs"
log "github.com/sirupsen/logrus"
)

type HookFs struct {
Original string
Mountpoint string
FsName string
fs pathfs.FileSystem
hook Hook
}

func NewHookFs(original string, mountpoint string, hook Hook) (*HookFs, error) {
log.WithFields(log.Fields{
"original": original,
"mountpoint": mountpoint,
}).Debug("Hooking a fs")

loopbackfs := pathfs.NewLoopbackFileSystem(original)
hookfs := &HookFs{
Original: original,
Mountpoint: mountpoint,
FsName: "hookfs",
fs: loopbackfs,
hook: hook,
}
return hookfs, nil
}

func (h *HookFs) String() string {
return fmt.Sprintf("HookFs{Original=%s, Mountpoint=%s, FsName=%s, Underlying fs=%s, hook=%s}",
h.Original, h.Mountpoint, h.FsName, h.fs.String(), h.hook)
}

func (h *HookFs) SetDebug(debug bool) {
h.fs.SetDebug(debug)
}

func (h *HookFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
return h.fs.GetAttr(name, context)
}

func (h *HookFs) Chmod(name string, mode uint32, context *fuse.Context) fuse.Status {
return h.fs.Chmod(name, mode, context)
}

func (h *HookFs) Chown(name string, uid uint32, gid uint32, context *fuse.Context) fuse.Status {
return h.fs.Chown(name, uid, gid, context)
}

func (h *HookFs) Utimens(name string, Atime *time.Time, Mtime *time.Time, context *fuse.Context) fuse.Status {
return h.fs.Utimens(name, Atime, Mtime, context)
}

func (h *HookFs) Truncate(name string, size uint64, context *fuse.Context) fuse.Status {
return h.fs.Truncate(name, size, context)
}

func (h *HookFs) Access(name string, mode uint32, context *fuse.Context) fuse.Status {
return h.fs.Access(name, mode, context)
}

func (h *HookFs) Link(oldName string, newName string, context *fuse.Context) fuse.Status {
return h.fs.Link(oldName, newName, context)
}

func (h *HookFs) Mkdir(name string, mode uint32, context *fuse.Context) fuse.Status {
return h.fs.Mkdir(name, mode, context)
}

func (h *HookFs) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) fuse.Status {
return h.fs.Mknod(name, mode, dev, context)
}

func (h *HookFs) Rename(oldName string, newName string, context *fuse.Context) fuse.Status {
hook, hookEnabled := h.hook.(HookOnRename)
if hookEnabled {
preHooked, err := hook.PreRename(oldName, newName)
if preHooked {
if err != nil {
return fuse.ToStatus(err)
}
}
}

status := h.fs.Rename(oldName, newName, context)

if hookEnabled {
postHooked, err := hook.PostRename(oldName, newName)
if postHooked {
if err != nil {
return fuse.ToStatus(err)
}
}
}

return status
}

func (h *HookFs) Rmdir(name string, context *fuse.Context) fuse.Status {
return h.fs.Rmdir(name, context)
}

func (h *HookFs) Unlink(name string, context *fuse.Context) fuse.Status {
return h.fs.Unlink(name, context)
}

func (h *HookFs) GetXAttr(name string, attribute string, context *fuse.Context) ([]byte, fuse.Status) {
return h.fs.GetXAttr(name, attribute, context)
}

func (h *HookFs) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) {
return h.fs.ListXAttr(name, context)
}

func (h *HookFs) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status {
return h.fs.RemoveXAttr(name, attr, context)
}

func (h *HookFs) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
return h.fs.SetXAttr(name, attr, data, flags, context)
}

func (h *HookFs) OnMount(nodeFs *pathfs.PathNodeFs) {
h.fs.OnMount(nodeFs)
}

func (h *HookFs) OnUnmount() {
h.fs.OnUnmount()
}

func (h *HookFs) Open(name string, flags uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
return h.fs.Open(name, flags, context)
}

func (h *HookFs) Create(name string, flags uint32, mode uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
return h.fs.Create(name, flags, mode, context)
}

func (h *HookFs) OpenDir(name string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
return h.fs.OpenDir(name, context)
}

func (h *HookFs) Symlink(value string, linkName string, context *fuse.Context) fuse.Status {
return h.fs.Symlink(value, linkName, context)
}

func (h *HookFs) Readlink(name string, context *fuse.Context) (string, fuse.Status) {
return h.fs.Readlink(name, context)
}

func (h *HookFs) StatFs(name string) *fuse.StatfsOut {
return h.fs.StatFs(name)
}

func (h *HookFs) NewServe() (*fuse.Server, error) {
server, err := newHookServer(h)
if err != nil {
return nil, err
}

return server, nil
}

//tests will want to run this in a goroutine.
func (h *HookFs) Start(server *fuse.Server) {
server.Serve()
}

func newHookServer(hookfs *HookFs) (*fuse.Server, error) {
opts := &nodefs.Options{
NegativeTimeout: time.Second,
AttrTimeout: time.Second,
EntryTimeout: time.Second,
}
pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true}
pathFs := pathfs.NewPathNodeFs(hookfs, pathFsOpts)
conn := nodefs.NewFileSystemConnector(pathFs.Root(), opts)
originalAbs, _ := filepath.Abs(hookfs.Original)
mOpts := &fuse.MountOptions{
AllowOther: true,
Name: hookfs.FsName,
FsName: originalAbs,
}
server, err := fuse.NewServer(conn.RawFS(), hookfs.Mountpoint, mOpts)
if err != nil {
return nil, err
}

return server, nil
}
10 changes: 10 additions & 0 deletions testutil/hook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package testutil

type Hook interface{}

type HookContext interface{}

type HookOnRename interface {
PreRename(oldPatgh string, newPath string) (hooked bool, err error)
PostRename(oldPatgh string, newPath string) (hooked bool, err error)
}
68 changes: 68 additions & 0 deletions testutil/hook_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package testutil

import (
"fmt"
"github.com/hanwen/go-fuse/fuse"
"log"
"os"
"syscall"
"testing"
)

func TestRenameHook_PreRenameHookRenameTest(t *testing.T) {
//init action
original := "/tmp/o"
mountpoint := "/tmp/m"
server := newFuseServer(t, original, mountpoint)
//remember to call unmount after you do not use it
defer cleanUp(server)

//normal logic
os.Create("/tmp/m/tsdb.txt")
//rename should be failed
err := os.Rename("/tmp/m/tsdb.txt", "/tmp/m/tsdbNew.txt")
NotOk(t, err)
fmt.Println(err)
}

func newFuseServer(t *testing.T, original,mountpoint string)(*fuse.Server){
createDirIfAbsent(original)
createDirIfAbsent(mountpoint)
fs, err := NewHookFs("/tmp/o", "/tmp/m", &TestRenameHook{})
Ok(t, err)
server, err := fs.NewServe()
if err != nil {
log.Fatal("please execute `fusermount -u $mountpoint`")
}
Ok(t, err)
go func(){
fs.Start(server)
}()

return server
}

func cleanUp(server *fuse.Server) {
err := server.Unmount()
if err != nil {
log.Fatal("umount failed, please umount the mountpoint by manual operation", err)
}
}

func createDirIfAbsent(name string) {
_, err := os.Stat(name)
if err != nil {
os.Mkdir(name, os.ModePerm)
}
}

type TestRenameHook struct {}

func (h *TestRenameHook) PreRename(oldPatgh string, newPath string) (hooked bool, err error) {
log.Printf("renamed file from %s to %s", oldPatgh, newPath)
return true, syscall.EIO
}
func (h *TestRenameHook) PostRename(oldPatgh string, newPath string) (hooked bool, err error) {
return false, nil
}

0 comments on commit 9d9c978

Please sign in to comment.