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

Commit 9d9c978

Browse files
committed
add-go-fuse-to-inject-filesystem-error
1 parent 4b3a5ac commit 9d9c978

File tree

3 files changed

+278
-0
lines changed

3 files changed

+278
-0
lines changed

testutil/fs_hook.go

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
package testutil
2+
3+
import (
4+
"fmt"
5+
"path/filepath"
6+
"time"
7+
8+
"github.com/hanwen/go-fuse/fuse"
9+
"github.com/hanwen/go-fuse/fuse/nodefs"
10+
"github.com/hanwen/go-fuse/fuse/pathfs"
11+
log "github.com/sirupsen/logrus"
12+
)
13+
14+
type HookFs struct {
15+
Original string
16+
Mountpoint string
17+
FsName string
18+
fs pathfs.FileSystem
19+
hook Hook
20+
}
21+
22+
func NewHookFs(original string, mountpoint string, hook Hook) (*HookFs, error) {
23+
log.WithFields(log.Fields{
24+
"original": original,
25+
"mountpoint": mountpoint,
26+
}).Debug("Hooking a fs")
27+
28+
loopbackfs := pathfs.NewLoopbackFileSystem(original)
29+
hookfs := &HookFs{
30+
Original: original,
31+
Mountpoint: mountpoint,
32+
FsName: "hookfs",
33+
fs: loopbackfs,
34+
hook: hook,
35+
}
36+
return hookfs, nil
37+
}
38+
39+
func (h *HookFs) String() string {
40+
return fmt.Sprintf("HookFs{Original=%s, Mountpoint=%s, FsName=%s, Underlying fs=%s, hook=%s}",
41+
h.Original, h.Mountpoint, h.FsName, h.fs.String(), h.hook)
42+
}
43+
44+
func (h *HookFs) SetDebug(debug bool) {
45+
h.fs.SetDebug(debug)
46+
}
47+
48+
func (h *HookFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
49+
return h.fs.GetAttr(name, context)
50+
}
51+
52+
func (h *HookFs) Chmod(name string, mode uint32, context *fuse.Context) fuse.Status {
53+
return h.fs.Chmod(name, mode, context)
54+
}
55+
56+
func (h *HookFs) Chown(name string, uid uint32, gid uint32, context *fuse.Context) fuse.Status {
57+
return h.fs.Chown(name, uid, gid, context)
58+
}
59+
60+
func (h *HookFs) Utimens(name string, Atime *time.Time, Mtime *time.Time, context *fuse.Context) fuse.Status {
61+
return h.fs.Utimens(name, Atime, Mtime, context)
62+
}
63+
64+
func (h *HookFs) Truncate(name string, size uint64, context *fuse.Context) fuse.Status {
65+
return h.fs.Truncate(name, size, context)
66+
}
67+
68+
func (h *HookFs) Access(name string, mode uint32, context *fuse.Context) fuse.Status {
69+
return h.fs.Access(name, mode, context)
70+
}
71+
72+
func (h *HookFs) Link(oldName string, newName string, context *fuse.Context) fuse.Status {
73+
return h.fs.Link(oldName, newName, context)
74+
}
75+
76+
func (h *HookFs) Mkdir(name string, mode uint32, context *fuse.Context) fuse.Status {
77+
return h.fs.Mkdir(name, mode, context)
78+
}
79+
80+
func (h *HookFs) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) fuse.Status {
81+
return h.fs.Mknod(name, mode, dev, context)
82+
}
83+
84+
func (h *HookFs) Rename(oldName string, newName string, context *fuse.Context) fuse.Status {
85+
hook, hookEnabled := h.hook.(HookOnRename)
86+
if hookEnabled {
87+
preHooked, err := hook.PreRename(oldName, newName)
88+
if preHooked {
89+
if err != nil {
90+
return fuse.ToStatus(err)
91+
}
92+
}
93+
}
94+
95+
status := h.fs.Rename(oldName, newName, context)
96+
97+
if hookEnabled {
98+
postHooked, err := hook.PostRename(oldName, newName)
99+
if postHooked {
100+
if err != nil {
101+
return fuse.ToStatus(err)
102+
}
103+
}
104+
}
105+
106+
return status
107+
}
108+
109+
func (h *HookFs) Rmdir(name string, context *fuse.Context) fuse.Status {
110+
return h.fs.Rmdir(name, context)
111+
}
112+
113+
func (h *HookFs) Unlink(name string, context *fuse.Context) fuse.Status {
114+
return h.fs.Unlink(name, context)
115+
}
116+
117+
func (h *HookFs) GetXAttr(name string, attribute string, context *fuse.Context) ([]byte, fuse.Status) {
118+
return h.fs.GetXAttr(name, attribute, context)
119+
}
120+
121+
func (h *HookFs) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) {
122+
return h.fs.ListXAttr(name, context)
123+
}
124+
125+
func (h *HookFs) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status {
126+
return h.fs.RemoveXAttr(name, attr, context)
127+
}
128+
129+
func (h *HookFs) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
130+
return h.fs.SetXAttr(name, attr, data, flags, context)
131+
}
132+
133+
func (h *HookFs) OnMount(nodeFs *pathfs.PathNodeFs) {
134+
h.fs.OnMount(nodeFs)
135+
}
136+
137+
func (h *HookFs) OnUnmount() {
138+
h.fs.OnUnmount()
139+
}
140+
141+
func (h *HookFs) Open(name string, flags uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
142+
return h.fs.Open(name, flags, context)
143+
}
144+
145+
func (h *HookFs) Create(name string, flags uint32, mode uint32, context *fuse.Context) (nodefs.File, fuse.Status) {
146+
return h.fs.Create(name, flags, mode, context)
147+
}
148+
149+
func (h *HookFs) OpenDir(name string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
150+
return h.fs.OpenDir(name, context)
151+
}
152+
153+
func (h *HookFs) Symlink(value string, linkName string, context *fuse.Context) fuse.Status {
154+
return h.fs.Symlink(value, linkName, context)
155+
}
156+
157+
func (h *HookFs) Readlink(name string, context *fuse.Context) (string, fuse.Status) {
158+
return h.fs.Readlink(name, context)
159+
}
160+
161+
func (h *HookFs) StatFs(name string) *fuse.StatfsOut {
162+
return h.fs.StatFs(name)
163+
}
164+
165+
func (h *HookFs) NewServe() (*fuse.Server, error) {
166+
server, err := newHookServer(h)
167+
if err != nil {
168+
return nil, err
169+
}
170+
171+
return server, nil
172+
}
173+
174+
//tests will want to run this in a goroutine.
175+
func (h *HookFs) Start(server *fuse.Server) {
176+
server.Serve()
177+
}
178+
179+
func newHookServer(hookfs *HookFs) (*fuse.Server, error) {
180+
opts := &nodefs.Options{
181+
NegativeTimeout: time.Second,
182+
AttrTimeout: time.Second,
183+
EntryTimeout: time.Second,
184+
}
185+
pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true}
186+
pathFs := pathfs.NewPathNodeFs(hookfs, pathFsOpts)
187+
conn := nodefs.NewFileSystemConnector(pathFs.Root(), opts)
188+
originalAbs, _ := filepath.Abs(hookfs.Original)
189+
mOpts := &fuse.MountOptions{
190+
AllowOther: true,
191+
Name: hookfs.FsName,
192+
FsName: originalAbs,
193+
}
194+
server, err := fuse.NewServer(conn.RawFS(), hookfs.Mountpoint, mOpts)
195+
if err != nil {
196+
return nil, err
197+
}
198+
199+
return server, nil
200+
}

testutil/hook.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package testutil
2+
3+
type Hook interface{}
4+
5+
type HookContext interface{}
6+
7+
type HookOnRename interface {
8+
PreRename(oldPatgh string, newPath string) (hooked bool, err error)
9+
PostRename(oldPatgh string, newPath string) (hooked bool, err error)
10+
}

testutil/hook_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package testutil
2+
3+
import (
4+
"fmt"
5+
"github.com/hanwen/go-fuse/fuse"
6+
"log"
7+
"os"
8+
"syscall"
9+
"testing"
10+
)
11+
12+
func TestRenameHook_PreRenameHookRenameTest(t *testing.T) {
13+
//init action
14+
original := "/tmp/o"
15+
mountpoint := "/tmp/m"
16+
server := newFuseServer(t, original, mountpoint)
17+
//remember to call unmount after you do not use it
18+
defer cleanUp(server)
19+
20+
//normal logic
21+
os.Create("/tmp/m/tsdb.txt")
22+
//rename should be failed
23+
err := os.Rename("/tmp/m/tsdb.txt", "/tmp/m/tsdbNew.txt")
24+
NotOk(t, err)
25+
fmt.Println(err)
26+
}
27+
28+
func newFuseServer(t *testing.T, original,mountpoint string)(*fuse.Server){
29+
createDirIfAbsent(original)
30+
createDirIfAbsent(mountpoint)
31+
fs, err := NewHookFs("/tmp/o", "/tmp/m", &TestRenameHook{})
32+
Ok(t, err)
33+
server, err := fs.NewServe()
34+
if err != nil {
35+
log.Fatal("please execute `fusermount -u $mountpoint`")
36+
}
37+
Ok(t, err)
38+
go func(){
39+
fs.Start(server)
40+
}()
41+
42+
return server
43+
}
44+
45+
func cleanUp(server *fuse.Server) {
46+
err := server.Unmount()
47+
if err != nil {
48+
log.Fatal("umount failed, please umount the mountpoint by manual operation", err)
49+
}
50+
}
51+
52+
func createDirIfAbsent(name string) {
53+
_, err := os.Stat(name)
54+
if err != nil {
55+
os.Mkdir(name, os.ModePerm)
56+
}
57+
}
58+
59+
type TestRenameHook struct {}
60+
61+
func (h *TestRenameHook) PreRename(oldPatgh string, newPath string) (hooked bool, err error) {
62+
log.Printf("renamed file from %s to %s", oldPatgh, newPath)
63+
return true, syscall.EIO
64+
}
65+
func (h *TestRenameHook) PostRename(oldPatgh string, newPath string) (hooked bool, err error) {
66+
return false, nil
67+
}
68+

0 commit comments

Comments
 (0)