package fs import ( "context" "fmt" "syscall" "github.com/badjware/gitlabfs/gitlab" "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" ) 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) { // Fetch the current logged user currentUser, err := n.param.Gitlab.FetchCurrentUser() // Skip if we are anonymous (or the call fails for some reason...) if err != nil { fmt.Println(err) } else { currentUserNode, _ := newUserNode(currentUser, n.param) inode := n.NewPersistentInode( ctx, currentUserNode, fs.StableAttr{ Ino: <-n.param.staticInoChan, Mode: fuse.S_IFDIR, }, ) n.AddChild(currentUserNode.user.Name, inode, false) } for _, userID := range n.userIds { if currentUser != nil && currentUser.ID == userID { // We already added the current user, we can skip it continue } userNode, err := newUserNodeByID(userID, n.param) if err != nil { fmt.Printf("user fetch fail: %v\n", err) fmt.Printf("Please verify the user exists and token with sufficient permissions is set in the config files.\n") fmt.Printf("Skipping user %v\n", userID) return } inode := n.NewPersistentInode( ctx, userNode, fs.StableAttr{ Ino: <-n.param.staticInoChan, Mode: fuse.S_IFDIR, }, ) n.AddChild(userNode.user.Name, inode, false) } } type userNode struct { fs.Inode param *FSParam user *gitlab.User staticNodes map[string]staticNode } // Ensure we are implementing the NodeReaddirer interface var _ = (fs.NodeReaddirer)((*userNode)(nil)) // Ensure we are implementing the NodeLookuper interface var _ = (fs.NodeLookuper)((*userNode)(nil)) func newUserNodeByID(uid int, param *FSParam) (*userNode, error) { user, err := param.Gitlab.FetchUser(uid) if err != nil { return nil, err } node := &userNode{ param: param, user: user, staticNodes: map[string]staticNode{ ".refresh": newRefreshNode(user, param), }, } return node, nil } func newUserNode(user *gitlab.User, param *FSParam) (*userNode, error) { node := &userNode{ param: param, user: user, staticNodes: map[string]staticNode{ ".refresh": newRefreshNode(user, param), }, } return node, nil } func (n *userNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) { userContent, _ := n.param.Gitlab.FetchUserContent(n.user) entries := make([]fuse.DirEntry, 0, len(userContent.Projects)+len(n.staticNodes)) for _, project := range userContent.Projects { entries = append(entries, fuse.DirEntry{ Name: project.Name, Ino: uint64(project.ID), Mode: fuse.S_IFLNK, }) } for name, staticNode := range n.staticNodes { entries = append(entries, fuse.DirEntry{ Name: name, Ino: staticNode.Ino(), Mode: staticNode.Mode(), }) } return fs.NewListDirStream(entries), 0 } func (n *userNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) { userContent, _ := n.param.Gitlab.FetchUserContent(n.user) // Check if the map of projects contains it project, ok := userContent.Projects[name] if ok { attrs := fs.StableAttr{ Ino: uint64(project.ID), Mode: fuse.S_IFLNK, } repositoryNode, _ := newRepositoryNode(project, n.param) return n.NewInode(ctx, repositoryNode, attrs), 0 } // Check if the map of static nodes contains it staticNode, ok := n.staticNodes[name] if ok { attrs := fs.StableAttr{ Ino: staticNode.Ino(), Mode: staticNode.Mode(), } return n.NewInode(ctx, staticNode, attrs), 0 } return nil, syscall.ENOENT }