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		"context"
    23		"errors"
    24		"fmt"
    25		"os"
    26		"path/filepath"
    27		"strings"
    28		"sync"
    29		"time"
    30	
    31		"bazil.org/fuse"
    32		"bazil.org/fuse/fs"
    33		"go4.org/types"
    34		"perkeep.org/pkg/blob"
    35		"perkeep.org/pkg/schema"
    36		"perkeep.org/pkg/search"
    37	)
    38	
    39	// roDir is a read-only directory.
    40	// Its permanode is the permanode with camliPath:entname attributes.
    41	type roDir struct {
    42		fs        *CamliFileSystem
    43		permanode blob.Ref
    44		parent    *roDir // or nil, if the root within its roots.go root.
    45		name      string // ent name (base name within parent)
    46		at        time.Time
    47	
    48		mu       sync.Mutex
    49		children map[string]roFileOrDir
    50		xattrs   map[string][]byte
    51	}
    52	
    53	var _ fs.Node = (*roDir)(nil)
    54	var _ fs.HandleReadDirAller = (*roDir)(nil)
    55	var _ fs.NodeGetxattrer = (*roDir)(nil)
    56	var _ fs.NodeListxattrer = (*roDir)(nil)
    57	
    58	func newRODir(fs *CamliFileSystem, permanode blob.Ref, name string, at time.Time) *roDir {
    59		return &roDir{
    60			fs:        fs,
    61			permanode: permanode,
    62			name:      name,
    63			at:        at,
    64		}
    65	}
    66	
    67	// for debugging
    68	func (n *roDir) fullPath() string {
    69		if n == nil {
    70			return ""
    71		}
    72		return filepath.Join(n.parent.fullPath(), n.name)
    73	}
    74	
    75	func (n *roDir) Attr(ctx context.Context, a *fuse.Attr) error {
    76		*a = fuse.Attr{
    77			Inode: n.permanode.Sum64(),
    78			Mode:  os.ModeDir | 0500,
    79			Uid:   uint32(os.Getuid()),
    80			Gid:   uint32(os.Getgid()),
    81		}
    82		return nil
    83	}
    84	
    85	// populate hits the blobstore to populate map of child nodes.
    86	func (n *roDir) populate(ctx context.Context) error {
    87		n.mu.Lock()
    88		defer n.mu.Unlock()
    89		// Things never change here, so if we've ever populated, we're
    90		// populated.
    91		if n.children != nil {
    92			return nil
    93		}
    94	
    95		Logger.Printf("roDir.populate(%q) - Sending request At %v", n.fullPath(), n.at)
    96	
    97		res, err := n.fs.client.Describe(ctx, &search.DescribeRequest{
    98			BlobRef: n.permanode,
    99			Depth:   3,
   100			At:      types.Time3339(n.at),
   101		})
   102		if err != nil {
   103			Logger.Println("roDir.paths:", err)
   104			return fmt.Errorf("error while describing permanode: %w", err)
   105		}
   106		db := res.Meta[n.permanode.String()]
   107		if db == nil {
   108			return errors.New("dir blobref not described")
   109		}
   110	
   111		// Find all child permanodes and stick them in n.children
   112		n.children = make(map[string]roFileOrDir)
   113		for k, v := range db.Permanode.Attr {
   114			const p = "camliPath:"
   115			if !strings.HasPrefix(k, p) || len(v) < 1 {
   116				continue
   117			}
   118			name := k[len(p):]
   119			childRef := v[0]
   120			child := res.Meta[childRef]
   121			if child == nil {
   122				Logger.Printf("child not described: %v", childRef)
   123				continue
   124			}
   125			if target := child.Permanode.Attr.Get("camliSymlinkTarget"); target != "" {
   126				// This is a symlink.
   127				n.children[name] = &roFile{
   128					fs:        n.fs,
   129					permanode: blob.ParseOrZero(childRef),
   130					parent:    n,
   131					name:      name,
   132					symLink:   true,
   133					target:    target,
   134				}
   135			} else if isDir(child.Permanode) {
   136				// This is a directory.
   137				n.children[name] = &roDir{
   138					fs:        n.fs,
   139					permanode: blob.ParseOrZero(childRef),
   140					parent:    n,
   141					name:      name,
   142					at:        n.at,
   143				}
   144			} else if contentRef := child.Permanode.Attr.Get("camliContent"); contentRef != "" {
   145				// This is a file.
   146				content := res.Meta[contentRef]
   147				if content == nil {
   148					Logger.Printf("child content not described: %v", childRef)
   149					continue
   150				}
   151				if content.CamliType != "file" {
   152					Logger.Printf("child not a file: %v", childRef)
   153					continue
   154				}
   155				n.children[name] = &roFile{
   156					fs:        n.fs,
   157					permanode: blob.ParseOrZero(childRef),
   158					parent:    n,
   159					name:      name,
   160					content:   blob.ParseOrZero(contentRef),
   161					size:      content.File.Size,
   162				}
   163			} else {
   164				// unknown type
   165				continue
   166			}
   167			n.children[name].xattr().load(child.Permanode)
   168		}
   169		return nil
   170	}
   171	
   172	func (n *roDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
   173		if err := n.populate(ctx); err != nil {
   174			Logger.Println("populate:", err)
   175			return nil, handleEIOorEINTR(err)
   176		}
   177		n.mu.Lock()
   178		defer n.mu.Unlock()
   179		var ents []fuse.Dirent
   180		for name, childNode := range n.children {
   181			var ino uint64
   182			switch v := childNode.(type) {
   183			case *roDir:
   184				ino = v.permanode.Sum64()
   185			case *roFile:
   186				ino = v.permanode.Sum64()
   187			default:
   188				Logger.Printf("roDir.ReadDirAll: unknown child type %T", childNode)
   189			}
   190	
   191			// TODO: figure out what Dirent.Type means.
   192			// fuse.go says "Type uint32 // ?"
   193			dirent := fuse.Dirent{
   194				Name:  name,
   195				Inode: ino,
   196			}
   197			Logger.Printf("roDir(%q) appending inode %x, %+v", n.fullPath(), dirent.Inode, dirent)
   198			ents = append(ents, dirent)
   199		}
   200		return ents, nil
   201	}
   202	
   203	var _ fs.NodeStringLookuper = (*roDir)(nil)
   204	
   205	func (n *roDir) Lookup(ctx context.Context, name string) (ret fs.Node, err error) {
   206		defer func() {
   207			Logger.Printf("roDir(%q).Lookup(%q) = %#v, %v", n.fullPath(), name, ret, err)
   208		}()
   209		if err := n.populate(ctx); err != nil {
   210			Logger.Println("populate:", err)
   211			return nil, handleEIOorEINTR(err)
   212		}
   213		n.mu.Lock()
   214		defer n.mu.Unlock()
   215		if n2 := n.children[name]; n2 != nil {
   216			return n2, nil
   217		}
   218		return nil, fuse.ENOENT
   219	}
   220	
   221	// roFile is a read-only file, or symlink.
   222	type roFile struct {
   223		fs        *CamliFileSystem
   224		permanode blob.Ref
   225		parent    *roDir
   226		name      string // ent name (base name within parent)
   227	
   228		mu           sync.Mutex // protects all following fields
   229		symLink      bool       // if true, is a symlink
   230		target       string     // if a symlink
   231		content      blob.Ref   // if a regular file
   232		size         int64
   233		mtime, atime time.Time // if zero, use serverStart
   234		xattrs       map[string][]byte
   235	}
   236	
   237	var _ fs.Node = (*roFile)(nil)
   238	var _ fs.NodeGetxattrer = (*roFile)(nil)
   239	var _ fs.NodeListxattrer = (*roFile)(nil)
   240	var _ fs.NodeSetxattrer = (*roFile)(nil)
   241	var _ fs.NodeRemovexattrer = (*roFile)(nil)
   242	var _ fs.NodeOpener = (*roFile)(nil)
   243	var _ fs.NodeFsyncer = (*roFile)(nil)
   244	var _ fs.NodeReadlinker = (*roFile)(nil)
   245	
   246	func (n *roDir) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, res *fuse.GetxattrResponse) error {
   247		return n.xattr().get(req, res)
   248	}
   249	
   250	func (n *roDir) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, res *fuse.ListxattrResponse) error {
   251		return n.xattr().list(req, res)
   252	}
   253	
   254	func (n *roFile) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, res *fuse.GetxattrResponse) error {
   255		return n.xattr().get(req, res)
   256	}
   257	
   258	func (n *roFile) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, res *fuse.ListxattrResponse) error {
   259		return n.xattr().list(req, res)
   260	}
   261	
   262	func (n *roFile) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error {
   263		return fuse.EPERM
   264	}
   265	
   266	func (n *roFile) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error {
   267		return fuse.EPERM
   268	}
   269	
   270	// for debugging
   271	func (n *roFile) fullPath() string {
   272		if n == nil {
   273			return ""
   274		}
   275		return filepath.Join(n.parent.fullPath(), n.name)
   276	}
   277	
   278	func (n *roFile) Attr(ctx context.Context, a *fuse.Attr) error {
   279		// TODO: don't grab n.mu three+ times in here.
   280		var mode os.FileMode = 0400 // read-only
   281	
   282		n.mu.Lock()
   283		size := n.size
   284		var blocks uint64
   285		if size > 0 {
   286			blocks = uint64(size)/512 + 1
   287		}
   288		inode := n.permanode.Sum64()
   289		if n.symLink {
   290			mode |= os.ModeSymlink
   291		}
   292		n.mu.Unlock()
   293	
   294		*a = fuse.Attr{
   295			Inode:  inode,
   296			Mode:   mode,
   297			Uid:    uint32(os.Getuid()),
   298			Gid:    uint32(os.Getgid()),
   299			Size:   uint64(size),
   300			Blocks: blocks,
   301			Mtime:  n.modTime(),
   302			Atime:  n.accessTime(),
   303			Ctime:  serverStart,
   304		}
   305		return nil
   306	}
   307	
   308	func (n *roFile) accessTime() time.Time {
   309		n.mu.Lock()
   310		if !n.atime.IsZero() {
   311			defer n.mu.Unlock()
   312			return n.atime
   313		}
   314		n.mu.Unlock()
   315		return n.modTime()
   316	}
   317	
   318	func (n *roFile) modTime() time.Time {
   319		n.mu.Lock()
   320		defer n.mu.Unlock()
   321		if !n.mtime.IsZero() {
   322			return n.mtime
   323		}
   324		return serverStart
   325	}
   326	
   327	// Empirically:
   328	//
   329	//	open for read:   req.Flags == 0
   330	//	open for append: req.Flags == 1
   331	//	open for write:  req.Flags == 1
   332	//	open for read/write (+<)   == 2 (bitmask? of?)
   333	//
   334	// open flags are O_WRONLY (1), O_RDONLY (0), or O_RDWR (2). and also
   335	// bitmaks of O_SYMLINK (0x200000) maybe. (from
   336	// fuse_filehandle_xlate_to_oflags in macosx/kext/fuse_file.h)
   337	func (n *roFile) Open(ctx context.Context, req *fuse.OpenRequest, res *fuse.OpenResponse) (fs.Handle, error) {
   338		roFileOpen.Incr()
   339	
   340		if isWriteFlags(req.Flags) {
   341			return nil, fuse.EPERM
   342		}
   343	
   344		Logger.Printf("roFile.Open: %v: content: %v dir=%v flags=%v", n.permanode, n.content, req.Dir, req.Flags)
   345		r, err := schema.NewFileReader(ctx, n.fs.fetcher, n.content)
   346		if err != nil {
   347			roFileOpenError.Incr()
   348			Logger.Printf("roFile.Open: %v", err)
   349			return nil, handleEIOorEINTR(err)
   350		}
   351	
   352		// Turn off the OpenDirectIO bit (on by default in rsc fuse server.go),
   353		// else append operations don't work for some reason.
   354		res.Flags &= ^fuse.OpenDirectIO
   355	
   356		// Read-only.
   357		nod := &node{
   358			fs:      n.fs,
   359			blobref: n.content,
   360		}
   361		return &nodeReader{n: nod, fr: r}, nil
   362	}
   363	
   364	func (n *roFile) Fsync(ctx context.Context, r *fuse.FsyncRequest) error {
   365		// noop
   366		return nil
   367	}
   368	
   369	func (n *roFile) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) {
   370		Logger.Printf("roFile.Readlink(%q)", n.fullPath())
   371		n.mu.Lock()
   372		defer n.mu.Unlock()
   373		if !n.symLink {
   374			Logger.Printf("roFile.Readlink on node that's not a symlink?")
   375			return "", fuse.EIO
   376		}
   377		return n.target, nil
   378	}
   379	
   380	// roFileOrDir is a *roFile or *roDir
   381	type roFileOrDir interface {
   382		fs.Node
   383		permanodeString() string
   384		xattr() *xattr
   385	}
   386	
   387	func (n *roFile) permanodeString() string {
   388		return n.permanode.String()
   389	}
   390	
   391	func (n *roDir) permanodeString() string {
   392		return n.permanode.String()
   393	}
   394	
   395	func (n *roFile) xattr() *xattr {
   396		return &xattr{"roFile", n.fs, n.permanode, &n.mu, &n.xattrs}
   397	}
   398	
   399	func (n *roDir) xattr() *xattr {
   400		return &xattr{"roDir", n.fs, n.permanode, &n.mu, &n.xattrs}
   401	}
Website layout inspired by memcached.
Content by the authors.