shithub: mntgen

ref: 40ad4e69f1c70b8152c095fee65d37d03e248e24
dir: /mntgen.go/

View raw version
package main

import (
	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"context"
	"fmt"
	"os"
	"os/signal"
	"sync/atomic"
	"syscall"
	"time"
)

var inodeCount uint64

type Dir struct {
	Fs         *FS
	Name       string
	Attributes fuse.Attr
	Entries    map[string]*Dir
}

func NewDir(fs *FS, name string) *Dir {
	atomic.AddUint64(&inodeCount, 1)
	now := time.Now()
	return &Dir{
		Fs:   fs,
		Name: name,
		Attributes: fuse.Attr{
			Inode: inodeCount,
			Atime: now,
			Mtime: now,
			Ctime: now,
			Mode:  os.ModeDir | 0o755,
			Nlink: 2,
			Uid:   uint32(os.Getuid()),
			Gid:   uint32(os.Getgid()),
		},
		Entries: make(map[string]*Dir),
	}
}

func (d *Dir) Attr(ctx context.Context, a *fuse.Attr) error {
	*a = d.Attributes
	return nil
}

func (d *Dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
	if d == d.Fs.RootDir {
		if node, ok := d.Entries[name]; ok {
			return node, nil
		} else {
			nd := NewDir(d.Fs, name)
			d.Entries[name] = nd
			return nd, nil
		}
	}
	return nil, syscall.ENOENT
}

func (d *Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	entries := make([]fuse.Dirent, 0, len(d.Entries))
	for name, entry := range d.Entries {
		entries = append(entries, fuse.Dirent{
			Inode: entry.Attributes.Inode,
			Type:  fuse.DT_Dir,
			Name:  name,
		})
	}
	return entries, nil
}

type FS struct {
	RootDir *Dir
}

func NewFS() *FS {
	fs := &FS{}
	fs.RootDir = NewDir(fs, "")
	return fs
}

func (fs FS) Root() (fs.Node, error) {
	return fs.RootDir, nil
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: %s [ mnt ]\n", os.Args[0])
	os.Exit(1)
}

func main() {
	mtpt := "/n"
	switch len(os.Args) {
	case 1:
	case 2:
		mtpt = os.Args[1]
	default:
		usage()
	}
	c, err := fuse.Mount(mtpt, fuse.FSName("mntgen"), fuse.Subtype("mntgen"), fuse.AllowOther())
	if err != nil {
		fmt.Fprintf(os.Stderr, "mount: %v\n", err)
		os.Exit(1)
	}
	defer c.Close()
	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		for {
			<-sigc
			if err := fuse.Unmount(mtpt); err != nil {
				fmt.Fprintf(os.Stderr, "unmount failed: %v\n", err)
			}
		}
	}()
	err = fs.Serve(c, NewFS())
	if err != nil {
		fmt.Fprintf(os.Stderr, "serve: %v\n", err)
		os.Exit(1)
	}
}