blob: 19ae65ab0353d128a9d95c63ef9a901d2a7125d4 [file] [log] [blame]
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fs
import (
"encoding/hex"
"fmt"
"log"
"gopkg.in/src-d/go-git.v4/plumbing"
"github.com/google/slothfs/cache"
"github.com/google/slothfs/gitiles"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
)
type gitilesConfigFSRoot struct {
nodefs.Node
fsConn *nodefs.FileSystemConnector
cache *cache.Cache
service *gitiles.RepoService
options GitilesOptions
}
func (r *gitilesConfigFSRoot) OnMount(fsConn *nodefs.FileSystemConnector) {
r.fsConn = fsConn
}
func parseID(s string) (*plumbing.Hash, error) {
b, err := hex.DecodeString(s)
if err != nil || len(b) != 20 {
return nil, fmt.Errorf("NewOid(%q): %v", s, err)
}
var h plumbing.Hash
copy(h[:], b)
return &h, nil
}
func (r *gitilesConfigFSRoot) Lookup(out *fuse.Attr, name string, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
id, err := parseID(name)
if err != nil {
return nil, fuse.ENOENT
}
if ch := r.Inode().GetChild(name); ch != nil {
return ch, fuse.OK
}
tree, err := r.cache.Tree.Get(id)
if err != nil {
tree, err = r.service.GetTree(id.String(), "/", true)
if err != nil {
log.Printf("GetTree(%s): %v", id, err)
return nil, fuse.EIO
}
if err := r.cache.Tree.Add(id, tree); err != nil {
log.Printf("TreeCache.Add(%s): %v", id, err)
}
}
gro := GitilesRevisionOptions{
Revision: id.String(),
GitilesOptions: r.options,
}
newRoot := NewGitilesRoot(r.cache, tree, r.service, gro)
ch := r.Inode().NewChild(id.String(), true, newRoot)
out.Mode = fuse.S_IFDIR | 0755
newRoot.OnMount(r.fsConn)
return ch, fuse.OK
}
// NewGitilesConfigFSRoot returns a root node for a filesystem that lazily
// instantiates a repository if you access any subdirectory named by a
// 40-byte hex SHA1.
func NewGitilesConfigFSRoot(c *cache.Cache, service *gitiles.RepoService, options *GitilesOptions) nodefs.Node {
// TODO(hanwen): nodefs.Node has an OnForget(), but it will
// never trigger for directories that have children. That
// means that we effectively never drop old trees. We can fix
// this by either: 1) reconsidering OnForget in go-fuse 2) do
// a periodic removal of all subtrees trees. Since the FS is
// read-only that should cause no ill effects.
return &gitilesConfigFSRoot{
Node: nodefs.NewDefaultNode(),
cache: c,
service: service,
options: *options,
}
}