1
2
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
18
19 package fs
20
21 import (
22 "bytes"
23 "context"
24 "fmt"
25 "os"
26 "strconv"
27 "sync/atomic"
28
29 "bazil.org/fuse"
30 "bazil.org/fuse/fs"
31 )
32
33
34 var TrackStats bool
35
36 func init() {
37 TrackStats, _ = strconv.ParseBool(os.Getenv("CAMLI_TRACK_FS_STATS"))
38 }
39
40 var (
41 mutFileOpen = newStat("mutfile-open")
42 mutFileOpenError = newStat("mutfile-open-error")
43 mutFileOpenRO = newStat("mutfile-open-ro")
44 mutFileOpenRW = newStat("mutfile-open-rw")
45 roFileOpen = newStat("rofile-open")
46 roFileOpenError = newStat("rofile-open-error")
47 )
48
49 var statByName = map[string]*stat{}
50
51 func newStat(name string) *stat {
52 if statByName[name] != nil {
53 panic("duplicate registraton of " + name)
54 }
55 s := &stat{name: name}
56 statByName[name] = s
57 return s
58 }
59
60
61
62 type atomicInt64 struct {
63 v int64
64 }
65
66 func (a *atomicInt64) Get() int64 {
67 return atomic.LoadInt64(&a.v)
68 }
69
70 func (a *atomicInt64) Set(v int64) {
71 atomic.StoreInt64(&a.v, v)
72 }
73
74 func (a *atomicInt64) Add(delta int64) int64 {
75 return atomic.AddInt64(&a.v, delta)
76 }
77
78
79
80 type stat struct {
81 n atomicInt64
82 name string
83 }
84
85 var (
86 _ fs.Node = (*stat)(nil)
87 _ fs.NodeOpener = (*stat)(nil)
88 _ fs.HandleReader = (*stat)(nil)
89 )
90
91 func (s *stat) Incr() {
92 if TrackStats {
93 s.n.Add(1)
94 }
95 }
96
97 func (s *stat) content() []byte {
98 var buf bytes.Buffer
99 fmt.Fprintf(&buf, "%d", s.n.Get())
100 buf.WriteByte('\n')
101 return buf.Bytes()
102 }
103
104 func (s *stat) Attr(ctx context.Context, a *fuse.Attr) error {
105 a.Mode = 0400
106 a.Uid = uint32(os.Getuid())
107 a.Gid = uint32(os.Getgid())
108 a.Size = uint64(len(s.content()))
109 a.Mtime = serverStart
110 a.Ctime = serverStart
111 return nil
112 }
113
114 func (s *stat) Open(ctx context.Context, req *fuse.OpenRequest, res *fuse.OpenResponse) (fs.Handle, error) {
115
116 res.Flags |= fuse.OpenDirectIO
117 return s, nil
118 }
119
120 func (s *stat) Read(ctx context.Context, req *fuse.ReadRequest, res *fuse.ReadResponse) error {
121 c := s.content()
122 if req.Offset > int64(len(c)) {
123 return nil
124 }
125 c = c[req.Offset:]
126 size := req.Size
127 if size > len(c) {
128 size = len(c)
129 }
130 res.Data = make([]byte, size)
131 copy(res.Data, c)
132 return nil
133 }
134
135
136
137 type statsDir struct{}
138
139 var (
140 _ fs.Node = statsDir{}
141 _ fs.NodeRequestLookuper = statsDir{}
142 _ fs.HandleReadDirAller = statsDir{}
143 )
144
145 func (statsDir) Attr(ctx context.Context, a *fuse.Attr) error {
146 a.Mode = os.ModeDir | 0700
147 a.Uid = uint32(os.Getuid())
148 a.Gid = uint32(os.Getgid())
149 return nil
150 }
151
152 func (statsDir) ReadDirAll(ctx context.Context) (ents []fuse.Dirent, err error) {
153 for k := range statByName {
154 ents = append(ents, fuse.Dirent{Name: k})
155 }
156 return
157 }
158
159 func (statsDir) Lookup(ctx context.Context, req *fuse.LookupRequest, res *fuse.LookupResponse) (fs.Node, error) {
160 name := req.Name
161 s, ok := statByName[name]
162 if !ok {
163 return nil, fuse.ENOENT
164 }
165 return s, nil
166 }