2024-05-05 20:23:07 +00:00
|
|
|
package fstree
|
2020-12-29 03:51:23 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2024-06-06 05:51:34 +00:00
|
|
|
"log/slog"
|
2021-03-22 01:45:59 +00:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
"syscall"
|
2020-12-29 03:51:23 +00:00
|
|
|
|
|
|
|
"github.com/hanwen/go-fuse/v2/fs"
|
|
|
|
"github.com/hanwen/go-fuse/v2/fuse"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
staticInodeStart = uint64(int(^(uint(0))>>1)) + 1
|
|
|
|
)
|
|
|
|
|
2020-12-31 20:00:10 +00:00
|
|
|
type staticNode interface {
|
|
|
|
fs.InodeEmbedder
|
|
|
|
Ino() uint64
|
|
|
|
Mode() uint32
|
|
|
|
}
|
|
|
|
|
2024-05-05 23:52:57 +00:00
|
|
|
type GitClient interface {
|
|
|
|
FetchLocalRepositoryPath(source RepositorySource) (string, error)
|
|
|
|
}
|
|
|
|
|
2024-05-05 20:09:03 +00:00
|
|
|
type GitPlatform interface {
|
|
|
|
FetchRootGroupContent() (map[string]GroupSource, error)
|
|
|
|
FetchGroupContent(gid uint64) (map[string]GroupSource, map[string]RepositorySource, error)
|
|
|
|
}
|
2020-12-29 03:51:23 +00:00
|
|
|
|
2024-05-05 20:09:03 +00:00
|
|
|
type FSParam struct {
|
2024-05-05 23:52:57 +00:00
|
|
|
GitClient GitClient
|
|
|
|
GitPlatform GitPlatform
|
2021-08-13 17:40:26 +00:00
|
|
|
|
2024-06-06 05:51:34 +00:00
|
|
|
logger *slog.Logger
|
2020-12-29 03:51:23 +00:00
|
|
|
staticInoChan chan uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
type rootNode struct {
|
|
|
|
fs.Inode
|
2024-05-05 20:09:03 +00:00
|
|
|
param *FSParam
|
2020-12-29 03:51:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var _ = (fs.NodeOnAdder)((*rootNode)(nil))
|
|
|
|
|
2024-06-06 05:51:34 +00:00
|
|
|
func Start(logger *slog.Logger, mountpoint string, mountoptions []string, param *FSParam, debug bool) error {
|
|
|
|
logger.Info("Mounting", "mountpoint", mountpoint)
|
2020-12-29 03:51:23 +00:00
|
|
|
|
|
|
|
opts := &fs.Options{}
|
2021-08-13 17:40:26 +00:00
|
|
|
opts.MountOptions.Options = mountoptions
|
2020-12-30 23:00:37 +00:00
|
|
|
opts.Debug = debug
|
2020-12-29 03:51:23 +00:00
|
|
|
|
2024-06-06 05:51:34 +00:00
|
|
|
param.logger = logger
|
2020-12-29 03:51:23 +00:00
|
|
|
param.staticInoChan = make(chan uint64)
|
|
|
|
root := &rootNode{
|
2024-05-05 20:09:03 +00:00
|
|
|
param: param,
|
2020-12-29 03:51:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
go staticInoGenerator(root.param.staticInoChan)
|
|
|
|
|
|
|
|
server, err := fs.Mount(mountpoint, root, opts)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("mount failed: %v", err)
|
|
|
|
}
|
2021-03-22 01:45:59 +00:00
|
|
|
|
|
|
|
signalChan := make(chan os.Signal)
|
2024-06-06 05:51:34 +00:00
|
|
|
go signalHandler(logger, signalChan, server)
|
2021-03-22 01:45:59 +00:00
|
|
|
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
|
2021-03-24 02:56:16 +00:00
|
|
|
// server.Serve() is already called in fs.Mount() so we shouldn't call it ourself. We wait for the server to terminate.
|
|
|
|
server.Wait()
|
2020-12-29 03:51:23 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-05-05 20:09:03 +00:00
|
|
|
func (n *rootNode) OnAdd(ctx context.Context) {
|
|
|
|
rootGroups, err := n.param.GitPlatform.FetchRootGroupContent()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for groupName, group := range rootGroups {
|
|
|
|
groupNode, _ := newGroupNodeFromSource(group, n.param)
|
|
|
|
persistentInode := n.NewPersistentInode(
|
|
|
|
ctx,
|
|
|
|
groupNode,
|
|
|
|
fs.StableAttr{
|
|
|
|
Ino: <-n.param.staticInoChan,
|
|
|
|
Mode: fuse.S_IFDIR,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
n.AddChild(groupName, persistentInode, false)
|
|
|
|
}
|
|
|
|
|
2024-06-06 05:51:34 +00:00
|
|
|
n.param.logger.Info("Mounted and ready to use")
|
2024-05-05 20:09:03 +00:00
|
|
|
}
|
|
|
|
|
2020-12-29 03:51:23 +00:00
|
|
|
func staticInoGenerator(staticInoChan chan<- uint64) {
|
|
|
|
i := staticInodeStart
|
|
|
|
for {
|
|
|
|
staticInoChan <- i
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
}
|
2021-03-22 01:45:59 +00:00
|
|
|
|
2024-06-06 05:51:34 +00:00
|
|
|
func signalHandler(logger *slog.Logger, signalChan <-chan os.Signal, server *fuse.Server) {
|
2021-03-24 02:56:16 +00:00
|
|
|
err := server.WaitMount()
|
|
|
|
if err != nil {
|
2024-06-06 05:51:34 +00:00
|
|
|
logger.Error("failed to start exit signal handler", "error", err)
|
2021-03-24 02:56:16 +00:00
|
|
|
return
|
|
|
|
}
|
2021-03-22 01:45:59 +00:00
|
|
|
for {
|
|
|
|
s := <-signalChan
|
2024-06-06 05:51:34 +00:00
|
|
|
logger.Info("Caught signal", "signal", s)
|
2021-03-22 01:45:59 +00:00
|
|
|
err := server.Unmount()
|
|
|
|
if err != nil {
|
2024-06-06 05:51:34 +00:00
|
|
|
logger.Error("Failed to unmount", "error", err)
|
2021-03-22 01:45:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|