184 lines
5.5 KiB
Go
184 lines
5.5 KiB
Go
// Copyright 2016 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 pathfs
|
|
|
|
import (
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
|
|
"github.com/hanwen/go-fuse/fuse"
|
|
"github.com/hanwen/go-fuse/fuse/nodefs"
|
|
)
|
|
|
|
type loopbackFileSystem struct {
|
|
// TODO - this should need default fill in.
|
|
FileSystem
|
|
Root string
|
|
}
|
|
|
|
// A FUSE filesystem that shunts all request to an underlying file
|
|
// system. Its main purpose is to provide test coverage without
|
|
// having to build a synthetic filesystem.
|
|
func NewLoopbackFileSystem(root string) FileSystem {
|
|
// Make sure the Root path is absolute to avoid problems when the
|
|
// application changes working directory.
|
|
root, err := filepath.Abs(root)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return &loopbackFileSystem{
|
|
FileSystem: NewDefaultFileSystem(),
|
|
Root: root,
|
|
}
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) StatFs(name string) *fuse.StatfsOut {
|
|
s := syscall.Statfs_t{}
|
|
err := syscall.Statfs(fs.GetPath(name), &s)
|
|
if err == nil {
|
|
out := &fuse.StatfsOut{}
|
|
out.FromStatfsT(&s)
|
|
return out
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) OnMount(nodeFs *PathNodeFs) {
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) OnUnmount() {}
|
|
|
|
func (fs *loopbackFileSystem) GetPath(relPath string) string {
|
|
return filepath.Join(fs.Root, relPath)
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) GetAttr(name string, context *fuse.Context) (a *fuse.Attr, code fuse.Status) {
|
|
fullPath := fs.GetPath(name)
|
|
var err error = nil
|
|
st := syscall.Stat_t{}
|
|
if name == "" {
|
|
// When GetAttr is called for the toplevel directory, we always want
|
|
// to look through symlinks.
|
|
err = syscall.Stat(fullPath, &st)
|
|
} else {
|
|
err = syscall.Lstat(fullPath, &st)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, fuse.ToStatus(err)
|
|
}
|
|
a = &fuse.Attr{}
|
|
a.FromStat(&st)
|
|
return a, fuse.OK
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) {
|
|
// What other ways beyond O_RDONLY are there to open
|
|
// directories?
|
|
f, err := os.Open(fs.GetPath(name))
|
|
if err != nil {
|
|
return nil, fuse.ToStatus(err)
|
|
}
|
|
want := 500
|
|
output := make([]fuse.DirEntry, 0, want)
|
|
for {
|
|
infos, err := f.Readdir(want)
|
|
for i := range infos {
|
|
// workaround for https://code.google.com/p/go/issues/detail?id=5960
|
|
if infos[i] == nil {
|
|
continue
|
|
}
|
|
n := infos[i].Name()
|
|
d := fuse.DirEntry{
|
|
Name: n,
|
|
}
|
|
if s := fuse.ToStatT(infos[i]); s != nil {
|
|
d.Mode = uint32(s.Mode)
|
|
d.Ino = s.Ino
|
|
} else {
|
|
log.Printf("ReadDir entry %q for %q has no stat info", n, name)
|
|
}
|
|
output = append(output, d)
|
|
}
|
|
if len(infos) < want || err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
log.Println("Readdir() returned err:", err)
|
|
break
|
|
}
|
|
}
|
|
f.Close()
|
|
|
|
return output, fuse.OK
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Open(name string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
|
|
f, err := os.OpenFile(fs.GetPath(name), int(flags), 0)
|
|
if err != nil {
|
|
return nil, fuse.ToStatus(err)
|
|
}
|
|
return nodefs.NewLoopbackFile(f), fuse.OK
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
|
err := os.Chmod(fs.GetPath(path), os.FileMode(mode))
|
|
return fuse.ToStatus(err)
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
|
|
return fuse.ToStatus(os.Chown(fs.GetPath(path), int(uid), int(gid)))
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Truncate(path string, offset uint64, context *fuse.Context) (code fuse.Status) {
|
|
return fuse.ToStatus(os.Truncate(fs.GetPath(path), int64(offset)))
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Readlink(name string, context *fuse.Context) (out string, code fuse.Status) {
|
|
f, err := os.Readlink(fs.GetPath(name))
|
|
return f, fuse.ToStatus(err)
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) {
|
|
return fuse.ToStatus(syscall.Mknod(fs.GetPath(name), mode, int(dev)))
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Mkdir(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
|
return fuse.ToStatus(os.Mkdir(fs.GetPath(path), os.FileMode(mode)))
|
|
}
|
|
|
|
// Don't use os.Remove, it removes twice (unlink followed by rmdir).
|
|
func (fs *loopbackFileSystem) Unlink(name string, context *fuse.Context) (code fuse.Status) {
|
|
return fuse.ToStatus(syscall.Unlink(fs.GetPath(name)))
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
|
|
return fuse.ToStatus(syscall.Rmdir(fs.GetPath(name)))
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Symlink(pointedTo string, linkName string, context *fuse.Context) (code fuse.Status) {
|
|
return fuse.ToStatus(os.Symlink(pointedTo, fs.GetPath(linkName)))
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Rename(oldPath string, newPath string, context *fuse.Context) (codee fuse.Status) {
|
|
err := os.Rename(fs.GetPath(oldPath), fs.GetPath(newPath))
|
|
return fuse.ToStatus(err)
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Link(orig string, newName string, context *fuse.Context) (code fuse.Status) {
|
|
return fuse.ToStatus(os.Link(fs.GetPath(orig), fs.GetPath(newName)))
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Access(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
|
|
return fuse.ToStatus(syscall.Access(fs.GetPath(name), mode))
|
|
}
|
|
|
|
func (fs *loopbackFileSystem) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) {
|
|
f, err := os.OpenFile(fs.GetPath(path), int(flags)|os.O_CREATE, os.FileMode(mode))
|
|
return nodefs.NewLoopbackFile(f), fuse.ToStatus(err)
|
|
}
|