github.com/hanwen/go-fuse@v1.0.0/fuse/pathfs/loopback.go (about) 1 // Copyright 2016 the Go-FUSE Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package pathfs 6 7 import ( 8 "io" 9 "log" 10 "os" 11 "path/filepath" 12 "syscall" 13 14 "github.com/hanwen/go-fuse/fuse" 15 "github.com/hanwen/go-fuse/fuse/nodefs" 16 ) 17 18 type loopbackFileSystem struct { 19 // TODO - this should need default fill in. 20 FileSystem 21 Root string 22 } 23 24 // A FUSE filesystem that shunts all request to an underlying file 25 // system. Its main purpose is to provide test coverage without 26 // having to build a synthetic filesystem. 27 func NewLoopbackFileSystem(root string) FileSystem { 28 // Make sure the Root path is absolute to avoid problems when the 29 // application changes working directory. 30 root, err := filepath.Abs(root) 31 if err != nil { 32 panic(err) 33 } 34 return &loopbackFileSystem{ 35 FileSystem: NewDefaultFileSystem(), 36 Root: root, 37 } 38 } 39 40 func (fs *loopbackFileSystem) StatFs(name string) *fuse.StatfsOut { 41 s := syscall.Statfs_t{} 42 err := syscall.Statfs(fs.GetPath(name), &s) 43 if err == nil { 44 out := &fuse.StatfsOut{} 45 out.FromStatfsT(&s) 46 return out 47 } 48 return nil 49 } 50 51 func (fs *loopbackFileSystem) OnMount(nodeFs *PathNodeFs) { 52 } 53 54 func (fs *loopbackFileSystem) OnUnmount() {} 55 56 func (fs *loopbackFileSystem) GetPath(relPath string) string { 57 return filepath.Join(fs.Root, relPath) 58 } 59 60 func (fs *loopbackFileSystem) GetAttr(name string, context *fuse.Context) (a *fuse.Attr, code fuse.Status) { 61 fullPath := fs.GetPath(name) 62 var err error = nil 63 st := syscall.Stat_t{} 64 if name == "" { 65 // When GetAttr is called for the toplevel directory, we always want 66 // to look through symlinks. 67 err = syscall.Stat(fullPath, &st) 68 } else { 69 err = syscall.Lstat(fullPath, &st) 70 } 71 if err != nil { 72 return nil, fuse.ToStatus(err) 73 } 74 a = &fuse.Attr{} 75 a.FromStat(&st) 76 return a, fuse.OK 77 } 78 79 func (fs *loopbackFileSystem) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) { 80 // What other ways beyond O_RDONLY are there to open 81 // directories? 82 f, err := os.Open(fs.GetPath(name)) 83 if err != nil { 84 return nil, fuse.ToStatus(err) 85 } 86 want := 500 87 output := make([]fuse.DirEntry, 0, want) 88 for { 89 infos, err := f.Readdir(want) 90 for i := range infos { 91 // workaround for https://code.google.com/p/go/issues/detail?id=5960 92 if infos[i] == nil { 93 continue 94 } 95 n := infos[i].Name() 96 d := fuse.DirEntry{ 97 Name: n, 98 } 99 if s := fuse.ToStatT(infos[i]); s != nil { 100 d.Mode = uint32(s.Mode) 101 d.Ino = s.Ino 102 } else { 103 log.Printf("ReadDir entry %q for %q has no stat info", n, name) 104 } 105 output = append(output, d) 106 } 107 if len(infos) < want || err == io.EOF { 108 break 109 } 110 if err != nil { 111 log.Println("Readdir() returned err:", err) 112 break 113 } 114 } 115 f.Close() 116 117 return output, fuse.OK 118 } 119 120 func (fs *loopbackFileSystem) Open(name string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) { 121 f, err := os.OpenFile(fs.GetPath(name), int(flags), 0) 122 if err != nil { 123 return nil, fuse.ToStatus(err) 124 } 125 return nodefs.NewLoopbackFile(f), fuse.OK 126 } 127 128 func (fs *loopbackFileSystem) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) { 129 err := os.Chmod(fs.GetPath(path), os.FileMode(mode)) 130 return fuse.ToStatus(err) 131 } 132 133 func (fs *loopbackFileSystem) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) { 134 return fuse.ToStatus(os.Chown(fs.GetPath(path), int(uid), int(gid))) 135 } 136 137 func (fs *loopbackFileSystem) Truncate(path string, offset uint64, context *fuse.Context) (code fuse.Status) { 138 return fuse.ToStatus(os.Truncate(fs.GetPath(path), int64(offset))) 139 } 140 141 func (fs *loopbackFileSystem) Readlink(name string, context *fuse.Context) (out string, code fuse.Status) { 142 f, err := os.Readlink(fs.GetPath(name)) 143 return f, fuse.ToStatus(err) 144 } 145 146 func (fs *loopbackFileSystem) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) { 147 return fuse.ToStatus(syscall.Mknod(fs.GetPath(name), mode, int(dev))) 148 } 149 150 func (fs *loopbackFileSystem) Mkdir(path string, mode uint32, context *fuse.Context) (code fuse.Status) { 151 return fuse.ToStatus(os.Mkdir(fs.GetPath(path), os.FileMode(mode))) 152 } 153 154 // Don't use os.Remove, it removes twice (unlink followed by rmdir). 155 func (fs *loopbackFileSystem) Unlink(name string, context *fuse.Context) (code fuse.Status) { 156 return fuse.ToStatus(syscall.Unlink(fs.GetPath(name))) 157 } 158 159 func (fs *loopbackFileSystem) Rmdir(name string, context *fuse.Context) (code fuse.Status) { 160 return fuse.ToStatus(syscall.Rmdir(fs.GetPath(name))) 161 } 162 163 func (fs *loopbackFileSystem) Symlink(pointedTo string, linkName string, context *fuse.Context) (code fuse.Status) { 164 return fuse.ToStatus(os.Symlink(pointedTo, fs.GetPath(linkName))) 165 } 166 167 func (fs *loopbackFileSystem) Rename(oldPath string, newPath string, context *fuse.Context) (codee fuse.Status) { 168 oldPath = fs.GetPath(oldPath) 169 newPath = fs.GetPath(newPath) 170 err := os.Rename(oldPath, newPath) 171 return fuse.ToStatus(err) 172 } 173 174 func (fs *loopbackFileSystem) Link(orig string, newName string, context *fuse.Context) (code fuse.Status) { 175 return fuse.ToStatus(os.Link(fs.GetPath(orig), fs.GetPath(newName))) 176 } 177 178 func (fs *loopbackFileSystem) Access(name string, mode uint32, context *fuse.Context) (code fuse.Status) { 179 return fuse.ToStatus(syscall.Access(fs.GetPath(name), mode)) 180 } 181 182 func (fs *loopbackFileSystem) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) { 183 f, err := os.OpenFile(fs.GetPath(path), int(flags)|os.O_CREATE, os.FileMode(mode)) 184 return nodefs.NewLoopbackFile(f), fuse.ToStatus(err) 185 }