Skip to content

Instantly share code, notes, and snippets.

@crgimenes
Created July 13, 2018 00:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save crgimenes/4740c19f00989823a30881f0916edf02 to your computer and use it in GitHub Desktop.
Save crgimenes/4740c19f00989823a30881f0916edf02 to your computer and use it in GitHub Desktop.
simple filesystem example
package main
import (
"context"
"flag"
"fmt"
"io"
"os"
"syscall"
"bazil.org/fuse"
"bazil.org/fuse/fs"
_ "bazil.org/fuse/fs/fstestutil"
"bazil.org/fuse/fuseutil"
"github.com/nuveo/log"
)
func usage() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, " %s MOUNTPOINT\n", os.Args[0])
flag.PrintDefaults()
}
func close(c io.Closer) {
err := c.Close()
if err != nil {
log.Errorln(err)
}
}
func run(mountpoint string) (err error) {
c, err := fuse.Mount(
mountpoint,
fuse.FSName("database"),
fuse.Subtype("pgfs"),
fuse.LocalVolume(),
fuse.VolumeName("Postgres filesystem"),
)
if err != nil {
return
}
defer close(c)
if p := c.Protocol(); !p.HasInvalidate() {
return fmt.Errorf("kernel FUSE support is too old to have invalidations: version %v", p)
}
srv := fs.New(c, nil)
filesys := &FS{
Nodes: map[string]*Node{
"test1.txt": &Node{
Name: "test1.txt",
fuse: srv,
Inode: 2,
Type: fuse.DT_File,
Content: []byte("test file 1\n"),
},
"test2.txt": &Node{
Name: "test2.txt",
fuse: srv,
Inode: 3,
Type: fuse.DT_File,
Content: []byte("test file 2\n"),
},
"test3.txt": &Node{
Name: "test3.txt",
fuse: srv,
Inode: 4,
Type: fuse.DT_File,
Content: []byte("test file 3\n"),
},
"anotherDir": &Node{
Name: "outroDir",
fuse: srv,
Inode: 5,
Type: fuse.DT_Dir,
fs: &FS{
Nodes: map[string]*Node{
"anotherDir/test4.txt": &Node{
Name: "test4.txt",
fuse: srv,
Inode: 6,
Type: fuse.DT_File,
Content: []byte("test file 4\n"),
},
"anotherDir/test5.txt": &Node{
Name: "test5.txt",
fuse: srv,
Inode: 7,
Type: fuse.DT_File,
Content: []byte("test file 5\n"),
},
"anotherDir/test6.txt": &Node{
Name: "test6.txt",
fuse: srv,
Inode: 8,
Type: fuse.DT_File,
Content: []byte("test file 6\n"),
},
},
},
},
},
}
err = srv.Serve(filesys)
if err != nil {
return
}
// Check if the mount process has an error to report.
<-c.Ready
err = c.MountError
return
}
func main() {
flag.Usage = usage
flag.Parse()
if flag.NArg() != 1 {
usage()
os.Exit(2)
}
mountpoint := flag.Arg(0)
err := run(mountpoint)
if err != nil {
log.Fatal(err)
}
}
type FS struct {
Nodes map[string]*Node
}
var _ fs.FS = (*FS)(nil)
func (f *FS) Root() (fs.Node, error) {
return &Node{fs: f}, nil
}
var _ fs.NodeStringLookuper = (*Node)(nil)
func (d *Node) Lookup(ctx context.Context, name string) (fs.Node, error) {
node, ok := d.fs.Nodes[name]
if ok {
return node, nil
}
return nil, fuse.ENOENT
}
var _ fs.HandleReadDirAller = (*Node)(nil)
func (d *Node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
var dirDirs []fuse.Dirent
for _, n := range d.fs.Nodes {
dirent := fuse.Dirent{
Inode: n.Inode,
Name: n.Name,
Type: n.Type,
}
dirDirs = append(dirDirs, dirent)
}
return dirDirs, nil
}
type Node struct {
fuse *fs.Server
fs *FS
Inode uint64
Name string
Type fuse.DirentType
Content []byte
}
var _ fs.Node = (*Node)(nil)
func (f *Node) Attr(ctx context.Context, a *fuse.Attr) error {
a.Inode = 1
a.Mode = os.ModeDir | 0555
if f.Type == fuse.DT_File {
a.Inode = f.Inode
a.Mode = 0444
a.Size = uint64(len(f.Content))
}
return nil
}
var _ fs.NodeOpener = (*Node)(nil)
func (f *Node) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) {
if !req.Flags.IsReadOnly() {
return nil, fuse.Errno(syscall.EACCES)
}
resp.Flags |= fuse.OpenKeepCache
return f, nil
}
var _ fs.Handle = (*Node)(nil)
var _ fs.HandleReader = (*Node)(nil)
func (f *Node) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
fmt.Printf("Reading file %q from %v to %v\n", f.Name, req.Offset, req.Size)
fuseutil.HandleRead(req, resp, f.Content)
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment