github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/fs/ro.go (about)

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