diff --git a/fs/filesystem.go b/fs/filesystem.go deleted file mode 100644 index 81b16bb..0000000 --- a/fs/filesystem.go +++ /dev/null @@ -1,28 +0,0 @@ -package fs - -import ( - "fmt" - - "github.com/badjware/gitlabfs/git" - "github.com/badjware/gitlabfs/gitlab" - - "github.com/hanwen/go-fuse/v2/fs" -) - -func Start(gf gitlab.GroupFetcher, gp git.GitClonerPuller, mountpoint string, rootGrouptID int) error { - fmt.Printf("Mounting in %v\n", mountpoint) - - opts := &fs.Options{} - opts.Debug = true - root, err := newRootGroupNode(gf, gp, rootGrouptID) - if err != nil { - return fmt.Errorf("root group fetch fail: %v", err) - } - server, err := fs.Mount(mountpoint, root, opts) - if err != nil { - return fmt.Errorf("mount failed: %v", err) - } - server.Wait() - - return nil -} diff --git a/fs/group.go b/fs/group.go index 2d48a33..a903293 100644 --- a/fs/group.go +++ b/fs/group.go @@ -4,8 +4,6 @@ import ( "context" "syscall" - "github.com/badjware/gitlabfs/git" - "github.com/badjware/gitlabfs/gitlab" "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" @@ -13,9 +11,8 @@ import ( type groupNode struct { fs.Inode + param *FSParam group *gitlab.Group - gf gitlab.GroupFetcher - gcp git.GitClonerPuller } // Ensure we are implementing the NodeReaddirer interface @@ -24,30 +21,28 @@ var _ = (fs.NodeReaddirer)((*groupNode)(nil)) // Ensure we are implementing the NodeLookuper interface var _ = (fs.NodeLookuper)((*groupNode)(nil)) -func newRootGroupNode(gf gitlab.GroupFetcher, gcp git.GitClonerPuller, gid int) (*groupNode, error) { - group, err := gf.FetchGroup(gid) +func newRootGroupNode(gid int, param *FSParam) (*groupNode, error) { + group, err := param.Gf.FetchGroup(gid) if err != nil { return nil, err } node := &groupNode{ + param: param, group: group, - gf: gf, - gcp: gcp, } return node, nil } -func newGroupNode(gf gitlab.GroupFetcher, gcp git.GitClonerPuller, group *gitlab.Group) (*groupNode, error) { +func newGroupNode(group *gitlab.Group, param *FSParam) (*groupNode, error) { node := &groupNode{ + param: param, group: group, - gf: gf, - gcp: gcp, } return node, nil } func (n *groupNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) { - groupContent, _ := n.gf.FetchGroupContent(n.group) + groupContent, _ := n.param.Gf.FetchGroupContent(n.group) entries := make([]fuse.DirEntry, 0, len(groupContent.Groups)+len(groupContent.Repositories)) for _, group := range groupContent.Groups { entries = append(entries, fuse.DirEntry{ @@ -67,7 +62,7 @@ func (n *groupNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) { } func (n *groupNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) { - groupContent, _ := n.gf.FetchGroupContent(n.group) + groupContent, _ := n.param.Gf.FetchGroupContent(n.group) // Check if the map of groups contains it group, ok := groupContent.Groups[name] @@ -76,7 +71,7 @@ func (n *groupNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) Ino: uint64(group.ID), Mode: fuse.S_IFDIR, } - groupNode, _ := newGroupNode(n.gf, n.gcp, group) + groupNode, _ := newGroupNode(group, n.param) return n.NewInode(ctx, groupNode, attrs), 0 } @@ -87,7 +82,7 @@ func (n *groupNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) Ino: uint64(repository.ID), Mode: fuse.S_IFLNK, } - repositoryNode, _ := newRepositoryNode(n.gcp, repository) + repositoryNode, _ := newRepositoryNode(repository, n.param) return n.NewInode(ctx, repositoryNode, attrs), 0 } return nil, syscall.ENOENT diff --git a/fs/projects.go b/fs/projects.go new file mode 100644 index 0000000..05f1064 --- /dev/null +++ b/fs/projects.go @@ -0,0 +1,44 @@ +package fs + +import ( + "context" + "fmt" + + "github.com/hanwen/go-fuse/v2/fs" + "github.com/hanwen/go-fuse/v2/fuse" +) + +type projectsNode struct { + fs.Inode + param *FSParam + + rootGroupIds []int +} + +// Ensure we are implementing the NodeOnAdder interface +var _ = (fs.NodeOnAdder)((*projectsNode)(nil)) + +func NewProjectsNode(rootGroupIds []int, param *FSParam) *projectsNode { + return &projectsNode{ + param: param, + rootGroupIds: rootGroupIds, + } +} + +func (n *projectsNode) OnAdd(ctx context.Context) { + for _, groupID := range n.rootGroupIds { + groupNode, err := newRootGroupNode(groupID, n.param) + if err != nil { + fmt.Printf("root group fetch fail: %v\n", err) + } + inode := n.NewPersistentInode( + ctx, + groupNode, + fs.StableAttr{ + Ino: <-n.param.staticInoChan, + Mode: fuse.S_IFDIR, + }, + ) + n.AddChild(groupNode.group.Path, inode, false) + } +} diff --git a/fs/repository.go b/fs/repository.go index 04581dc..d9497e4 100644 --- a/fs/repository.go +++ b/fs/repository.go @@ -4,26 +4,24 @@ import ( "context" "syscall" - "github.com/badjware/gitlabfs/git" "github.com/badjware/gitlabfs/gitlab" "github.com/hanwen/go-fuse/v2/fs" ) type RepositoryNode struct { fs.Inode + param *FSParam repository *gitlab.Repository - - gcp git.GitClonerPuller } // Ensure we are implementing the NodeReaddirer interface var _ = (fs.NodeReadlinker)((*RepositoryNode)(nil)) -func newRepositoryNode(gcp git.GitClonerPuller, repository *gitlab.Repository) (*RepositoryNode, error) { +func newRepositoryNode(repository *gitlab.Repository, param *FSParam) (*RepositoryNode, error) { node := &RepositoryNode{ + param: param, repository: repository, - gcp: gcp, } // Passthrough the error if there is one, nothing to add here // Errors on clone/pull are non-fatal @@ -32,7 +30,7 @@ func newRepositoryNode(gcp git.GitClonerPuller, repository *gitlab.Repository) ( func (n *RepositoryNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) { // Create the local copy of the repo - localRepoLoc, _ := n.gcp.CloneOrPull(n.repository.CloneURL, n.repository.ID, "master") + localRepoLoc, _ := n.param.Gcp.CloneOrPull(n.repository.CloneURL, n.repository.ID, "master") return []byte(localRepoLoc), 0 } diff --git a/fs/root.go b/fs/root.go new file mode 100644 index 0000000..e11c9c3 --- /dev/null +++ b/fs/root.go @@ -0,0 +1,92 @@ +package fs + +import ( + "context" + "fmt" + + "github.com/badjware/gitlabfs/git" + "github.com/badjware/gitlabfs/gitlab" + + "github.com/hanwen/go-fuse/v2/fs" + "github.com/hanwen/go-fuse/v2/fuse" +) + +const ( + staticInodeStart = uint64(int(^(uint(0))>>1)) + 1 +) + +type FSParam struct { + Gf gitlab.GroupFetcher + Gcp git.GitClonerPuller + + staticInoChan chan uint64 +} + +type rootNode struct { + fs.Inode + param *FSParam + rootGroupIds []int + userIds []int +} + +var _ = (fs.NodeOnAdder)((*rootNode)(nil)) + +func (n *rootNode) OnAdd(ctx context.Context) { + projectsInode := n.NewPersistentInode( + ctx, + NewProjectsNode( + n.rootGroupIds, + n.param, + ), + fs.StableAttr{ + Ino: <-n.param.staticInoChan, + Mode: fuse.S_IFDIR, + }, + ) + n.AddChild("projects", projectsInode, false) + + usersInode := n.NewPersistentInode( + ctx, + NewUsersNode( + n.userIds, + n.param, + ), + fs.StableAttr{ + Ino: <-n.param.staticInoChan, + Mode: fuse.S_IFDIR, + }, + ) + n.AddChild("users", usersInode, false) +} + +func Start(mountpoint string, rootGroupIds []int, userIds []int, param *FSParam) error { + fmt.Printf("Mounting in %v\n", mountpoint) + + opts := &fs.Options{} + opts.Debug = true + + param.staticInoChan = make(chan uint64) + root := &rootNode{ + param: param, + rootGroupIds: rootGroupIds, + userIds: userIds, + } + + go staticInoGenerator(root.param.staticInoChan) + + server, err := fs.Mount(mountpoint, root, opts) + if err != nil { + return fmt.Errorf("mount failed: %v", err) + } + server.Wait() + + return nil +} + +func staticInoGenerator(staticInoChan chan<- uint64) { + i := staticInodeStart + for { + staticInoChan <- i + i++ + } +} diff --git a/fs/users.go b/fs/users.go new file mode 100644 index 0000000..f247be8 --- /dev/null +++ b/fs/users.go @@ -0,0 +1,42 @@ +package fs + +import ( + "context" + + "github.com/hanwen/go-fuse/v2/fs" +) + +type usersNode struct { + fs.Inode + param *FSParam + + userIds []int +} + +// Ensure we are implementing the NodeOnAdder interface +var _ = (fs.NodeOnAdder)((*usersNode)(nil)) + +func NewUsersNode(userIds []int, param *FSParam) *usersNode { + return &usersNode{ + param: param, + userIds: userIds, + } +} + +func (n *usersNode) OnAdd(ctx context.Context) { + // for _, userId := range n.userIds { + // userNode, err := newRootUserNode(userId, n.param) + // if err != nil { + // fmt.Printf("user fetch fail: %v\n", err) + // } + // inode := n.NewPersistentInode( + // ctx, + // userNode, + // fs.StableAttr{ + // Ino: <-n.param.staticInoChan, + // Mode: fuse.S_IFDIR, + // }, + // ) + // n.AddChild(userNode.user.Path, inode, false) + // } +} diff --git a/git/pullclone.go b/git/pullclone.go index c76883d..ae27f25 100644 --- a/git/pullclone.go +++ b/git/pullclone.go @@ -127,5 +127,6 @@ func (c *gitClient) pull(gpp *gitClonePullParam) error { // Check if the local repo is on default branch // Check if the local repo is dirty // Checkout the remote default branch + // TODO return nil } diff --git a/main.go b/main.go index 394df9f..c7145ae 100644 --- a/main.go +++ b/main.go @@ -45,5 +45,5 @@ func main() { gitClient, _ := git.NewClient(gitClientParam) // Start the filesystem - fs.Start(gitlabClient, gitClient, mountpoint, *gitlabRootGroupID) + fs.Start(mountpoint, []int{*gitlabRootGroupID}, []int{}, &fs.FSParam{Gf: gitlabClient, Gcp: gitClient}) }