Home Download Docs Code Community
     1	//go:build linux
     2	
     3	/*
     4	Copyright 2013 The Perkeep Authors
     5	
     6	Licensed under the Apache License, Version 2.0 (the "License");
     7	you may not use this file except in compliance with the License.
     8	You may obtain a copy of the License at
     9	
    10	     http://www.apache.org/licenses/LICENSE-2.0
    11	
    12	Unless required by applicable law or agreed to in writing, software
    13	distributed under the License is distributed on an "AS IS" BASIS,
    14	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15	See the License for the specific language governing permissions and
    16	limitations under the License.
    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	// TrackStats controls whether statistics are kept on operations.
    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	// TODO: https://github.com/perkeep/perkeep/issues/679
    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	// A stat is a wrapper around an atomic int64, as is a fuse.Node
    79	// exporting that data as a decimal.
    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		// Set DirectIO to keep this file from being cached in OS X's kernel.
   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	// A statsDir FUSE directory node is returned by root.go, by opening
   136	// ".camli_fs_stats" in the root directory.
   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	}
Website layout inspired by memcached.
Content by the authors.