1// +build linux darwin
2
3/*
4Copyright 2012 The Perkeep Authors
5
6Licensed under the Apache License, Version 2.0 (the "License");
7you may not use this file except in compliance with the License.
8You may obtain a copy of the License at
9
10     http://www.apache.org/licenses/LICENSE-2.0
11
12Unless required by applicable law or agreed to in writing, software
13distributed under the License is distributed on an "AS IS" BASIS,
14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15See the License for the specific language governing permissions and
16limitations under the License.
17*/
18
19package fs
20
21import (
22	"context"
23	"log"
24	"os"
25	"sync"
26
27	"bazil.org/fuse"
28	"bazil.org/fuse/fs"
29	"perkeep.org/pkg/blob"
30)
31
32// root implements fuse.Node and is the typical root of a
33// CamliFilesystem with a little hello message and the ability to
34// search and browse static snapshots, etc.
35type root struct {
36	fs *CamliFileSystem
37
38	mu          sync.Mutex
39	recent      *recentDir
40	roots       *rootsDir
41	atDir       *atDir
42	versionsDir *versionsDir
43}
44
45var (
46	_ fs.Node               = (*root)(nil)
47	_ fs.HandleReadDirAller = (*root)(nil)
48	_ fs.NodeStringLookuper = (*root)(nil)
49)
50
51func (n *root) Attr(ctx context.Context, a *fuse.Attr) error {
52	a.Mode = os.ModeDir | 0700
53	a.Uid = uint32(os.Getuid())
54	a.Gid = uint32(os.Getgid())
55	return nil
56}
57
58func (n *root) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
59	return []fuse.Dirent{
60		{Name: "WELCOME.txt"},
61		{Name: "tag"},
62		{Name: "date"},
63		{Name: "recent"},
64		{Name: "roots"},
65		{Name: "at"},
66		{Name: "sha1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
67		{Name: "sha224-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
68		{Name: "versions"},
69	}, nil
70}
71
72func (n *root) getRecentDir() *recentDir {
73	n.mu.Lock()
74	defer n.mu.Unlock()
75	if n.recent == nil {
76		n.recent = &recentDir{fs: n.fs}
77	}
78	return n.recent
79}
80
81func (n *root) getRootsDir() *rootsDir {
82	n.mu.Lock()
83	defer n.mu.Unlock()
84	if n.roots == nil {
85		n.roots = &rootsDir{fs: n.fs}
86	}
87	return n.roots
88}
89
90func (n *root) getAtDir() *atDir {
91	n.mu.Lock()
92	defer n.mu.Unlock()
93	if n.atDir == nil {
94		n.atDir = &atDir{fs: n.fs}
95	}
96	return n.atDir
97}
98
99func (n *root) getVersionsDir() *versionsDir {
100	n.mu.Lock()
101	defer n.mu.Unlock()
102	if n.versionsDir == nil {
103		n.versionsDir = &versionsDir{fs: n.fs}
104	}
105	return n.versionsDir
106}
107
108func (n *root) Lookup(ctx context.Context, name string) (fs.Node, error) {
109	Logger.Printf("root.Lookup(%s)", name)
110	switch name {
111	case ".quitquitquit":
112		log.Fatalf("Shutting down due to root .quitquitquit lookup.")
113	case "WELCOME.txt":
114		return staticFileNode("Welcome to PerkeepFS.\n\nMore information is available in the pk-mount documentation.\n\nSee https://perkeep.org/cmd/pk-mount/ , or run 'go doc perkeep.org/cmd/pk-mount'.\n"), nil
115	case "recent":
116		return n.getRecentDir(), nil
117	case "tag", "date":
118		return notImplementDirNode{}, nil
119	case "at":
120		return n.getAtDir(), nil
121	case "roots":
122		return n.getRootsDir(), nil
123	case "versions":
124		return n.getVersionsDir(), nil
125	case "sha1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx":
126		return notImplementDirNode{}, nil
127	case "sha224-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx":
128		return notImplementDirNode{}, nil
129	case ".camli_fs_stats":
130		return statsDir{}, nil
131	case "mach_kernel", ".hidden", "._.":
132		// Just quiet some log noise on OS X.
133		return nil, fuse.ENOENT
134	}
135
136	if br, ok := blob.Parse(name); ok {
137		Logger.Printf("Root lookup of blobref. %q => %v", name, br)
138		return &node{fs: n.fs, blobref: br}, nil
139	}
140	Logger.Printf("Bogus root lookup of %q", name)
141	return nil, fuse.ENOENT
142}
143