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

     1  // +build linux darwin
     2  
     3  /*
     4  Copyright 2012 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  	"log"
    23  	"os"
    24  	"strings"
    25  	"sync"
    26  	"time"
    27  
    28  	"camlistore.org/pkg/blob"
    29  	"camlistore.org/pkg/schema"
    30  	"camlistore.org/pkg/search"
    31  	"camlistore.org/pkg/syncutil"
    32  	"camlistore.org/third_party/bazil.org/fuse"
    33  	"camlistore.org/third_party/bazil.org/fuse/fs"
    34  )
    35  
    36  const refreshTime = 1 * time.Minute
    37  
    38  type rootsDir struct {
    39  	noXattr
    40  	fs *CamliFileSystem
    41  	at time.Time
    42  
    43  	mu        sync.Mutex // guards following
    44  	lastQuery time.Time
    45  	m         map[string]blob.Ref // ent name => permanode
    46  	children  map[string]fs.Node  // ent name => child node
    47  }
    48  
    49  func (n *rootsDir) isRO() bool {
    50  	return !n.at.IsZero()
    51  }
    52  
    53  func (n *rootsDir) dirMode() os.FileMode {
    54  	if n.isRO() {
    55  		return 0500
    56  	}
    57  	return 0700
    58  }
    59  
    60  func (n *rootsDir) Attr() fuse.Attr {
    61  	return fuse.Attr{
    62  		Mode: os.ModeDir | n.dirMode(),
    63  		Uid:  uint32(os.Getuid()),
    64  		Gid:  uint32(os.Getgid()),
    65  	}
    66  }
    67  
    68  func (n *rootsDir) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) {
    69  	n.mu.Lock()
    70  	defer n.mu.Unlock()
    71  	if err := n.condRefresh(); err != nil {
    72  		return nil, fuse.EIO
    73  	}
    74  	var ents []fuse.Dirent
    75  	for name := range n.m {
    76  		ents = append(ents, fuse.Dirent{Name: name})
    77  	}
    78  	log.Printf("rootsDir.ReadDir() -> %v", ents)
    79  	return ents, nil
    80  }
    81  
    82  func (n *rootsDir) Remove(req *fuse.RemoveRequest, intr fs.Intr) fuse.Error {
    83  	if n.isRO() {
    84  		return fuse.EPERM
    85  	}
    86  	n.mu.Lock()
    87  	defer n.mu.Unlock()
    88  
    89  	if err := n.condRefresh(); err != nil {
    90  		return err
    91  	}
    92  	br := n.m[req.Name]
    93  	if !br.Valid() {
    94  		return fuse.ENOENT
    95  	}
    96  
    97  	claim := schema.NewDelAttributeClaim(br, "camliRoot", "")
    98  	_, err := n.fs.client.UploadAndSignBlob(claim)
    99  	if err != nil {
   100  		log.Println("rootsDir.Remove:", err)
   101  		return fuse.EIO
   102  	}
   103  
   104  	delete(n.m, req.Name)
   105  	delete(n.children, req.Name)
   106  
   107  	return nil
   108  }
   109  
   110  func (n *rootsDir) Rename(req *fuse.RenameRequest, newDir fs.Node, intr fs.Intr) fuse.Error {
   111  	log.Printf("rootsDir.Rename %q -> %q", req.OldName, req.NewName)
   112  	if n.isRO() {
   113  		return fuse.EPERM
   114  	}
   115  
   116  	n.mu.Lock()
   117  	target, exists := n.m[req.OldName]
   118  	_, collision := n.m[req.NewName]
   119  	n.mu.Unlock()
   120  	if !exists {
   121  		log.Printf("*rootsDir.Rename src name %q isn't known", req.OldName)
   122  		return fuse.ENOENT
   123  	}
   124  	if collision {
   125  		log.Printf("*rootsDir.Rename dest %q already exists", req.NewName)
   126  		return fuse.EIO
   127  	}
   128  
   129  	// Don't allow renames if the root contains content.  Rename
   130  	// is mostly implemented to make GUIs that create directories
   131  	// before asking for the directory name.
   132  	res, err := n.fs.client.Describe(&search.DescribeRequest{BlobRef: target})
   133  	if err != nil {
   134  		log.Println("rootsDir.Rename:", err)
   135  		return fuse.EIO
   136  	}
   137  	db := res.Meta[target.String()]
   138  	if db == nil {
   139  		log.Printf("Failed to pull meta for target: %v", target)
   140  		return fuse.EIO
   141  	}
   142  
   143  	for k := range db.Permanode.Attr {
   144  		const p = "camliPath:"
   145  		if strings.HasPrefix(k, p) {
   146  			log.Printf("Found file in %q: %q, disallowing rename", req.OldName, k[len(p):])
   147  			return fuse.EIO
   148  		}
   149  	}
   150  
   151  	claim := schema.NewSetAttributeClaim(target, "camliRoot", req.NewName)
   152  	_, err = n.fs.client.UploadAndSignBlob(claim)
   153  	if err != nil {
   154  		log.Printf("Upload rename link error: %v", err)
   155  		return fuse.EIO
   156  	}
   157  
   158  	// Comment transplanted from mutDir.Rename
   159  	// TODO(bradfitz): this locking would be racy, if the kernel
   160  	// doesn't do it properly. (It should) Let's just trust the
   161  	// kernel for now. Later we can verify and remove this
   162  	// comment.
   163  	n.mu.Lock()
   164  	if n.m[req.OldName] != target {
   165  		panic("Race.")
   166  	}
   167  	delete(n.m, req.OldName)
   168  	delete(n.children, req.OldName)
   169  	delete(n.children, req.NewName)
   170  	n.m[req.NewName] = target
   171  	n.mu.Unlock()
   172  
   173  	return nil
   174  }
   175  
   176  func (n *rootsDir) Lookup(name string, intr fs.Intr) (fs.Node, fuse.Error) {
   177  	log.Printf("fs.roots: Lookup(%q)", name)
   178  	n.mu.Lock()
   179  	defer n.mu.Unlock()
   180  	if err := n.condRefresh(); err != nil {
   181  		return nil, err
   182  	}
   183  	br := n.m[name]
   184  	if !br.Valid() {
   185  		return nil, fuse.ENOENT
   186  	}
   187  
   188  	nod, ok := n.children[name]
   189  	if ok {
   190  		return nod, nil
   191  	}
   192  
   193  	if n.isRO() {
   194  		nod = newRODir(n.fs, br, name, n.at)
   195  	} else {
   196  		nod = &mutDir{
   197  			fs:        n.fs,
   198  			permanode: br,
   199  			name:      name,
   200  			xattrs:    map[string][]byte{},
   201  		}
   202  	}
   203  	n.children[name] = nod
   204  
   205  	return nod, nil
   206  }
   207  
   208  // requires n.mu is held
   209  func (n *rootsDir) condRefresh() fuse.Error {
   210  	if n.lastQuery.After(time.Now().Add(-refreshTime)) {
   211  		return nil
   212  	}
   213  	log.Printf("fs.roots: querying")
   214  
   215  	var rootRes, impRes *search.WithAttrResponse
   216  	var grp syncutil.Group
   217  	grp.Go(func() (err error) {
   218  		rootRes, err = n.fs.client.GetPermanodesWithAttr(&search.WithAttrRequest{N: 100, Attr: "camliRoot"})
   219  		return
   220  	})
   221  	grp.Go(func() (err error) {
   222  		impRes, err = n.fs.client.GetPermanodesWithAttr(&search.WithAttrRequest{N: 100, Attr: "camliImportRoot"})
   223  		return
   224  	})
   225  	if err := grp.Err(); err != nil {
   226  		log.Printf("fs.recent: GetRecentPermanodes error in ReadDir: %v", err)
   227  		return fuse.EIO
   228  	}
   229  
   230  	n.m = make(map[string]blob.Ref)
   231  	if n.children == nil {
   232  		n.children = make(map[string]fs.Node)
   233  	}
   234  
   235  	dr := &search.DescribeRequest{
   236  		Depth: 1,
   237  	}
   238  	for _, wi := range rootRes.WithAttr {
   239  		dr.BlobRefs = append(dr.BlobRefs, wi.Permanode)
   240  	}
   241  	for _, wi := range impRes.WithAttr {
   242  		dr.BlobRefs = append(dr.BlobRefs, wi.Permanode)
   243  	}
   244  	if len(dr.BlobRefs) == 0 {
   245  		return nil
   246  	}
   247  
   248  	dres, err := n.fs.client.Describe(dr)
   249  	if err != nil {
   250  		log.Printf("Describe failure: %v", err)
   251  		return fuse.EIO
   252  	}
   253  
   254  	// Roots
   255  	currentRoots := map[string]bool{}
   256  	for _, wi := range rootRes.WithAttr {
   257  		pn := wi.Permanode
   258  		db := dres.Meta[pn.String()]
   259  		if db != nil && db.Permanode != nil {
   260  			name := db.Permanode.Attr.Get("camliRoot")
   261  			if name != "" {
   262  				currentRoots[name] = true
   263  				n.m[name] = pn
   264  			}
   265  		}
   266  	}
   267  
   268  	// Remove any children objects we have mapped that are no
   269  	// longer relevant.
   270  	for name := range n.children {
   271  		if !currentRoots[name] {
   272  			delete(n.children, name)
   273  		}
   274  	}
   275  
   276  	// Importers (mapped as roots for now)
   277  	for _, wi := range impRes.WithAttr {
   278  		pn := wi.Permanode
   279  		db := dres.Meta[pn.String()]
   280  		if db != nil && db.Permanode != nil {
   281  			name := db.Permanode.Attr.Get("camliImportRoot")
   282  			if name != "" {
   283  				name = strings.Replace(name, ":", "-", -1)
   284  				name = strings.Replace(name, "/", "-", -1)
   285  				n.m["importer-"+name] = pn
   286  			}
   287  		}
   288  	}
   289  
   290  	n.lastQuery = time.Now()
   291  	return nil
   292  }
   293  
   294  func (n *rootsDir) Mkdir(req *fuse.MkdirRequest, intr fs.Intr) (fs.Node, fuse.Error) {
   295  	if n.isRO() {
   296  		return nil, fuse.EPERM
   297  	}
   298  
   299  	name := req.Name
   300  
   301  	// Create a Permanode for the root.
   302  	pr, err := n.fs.client.UploadNewPermanode()
   303  	if err != nil {
   304  		log.Printf("rootsDir.Create(%q): %v", name, err)
   305  		return nil, fuse.EIO
   306  	}
   307  
   308  	// Add a camliRoot attribute to the root permanode.
   309  	claim := schema.NewSetAttributeClaim(pr.BlobRef, "camliRoot", name)
   310  	_, err = n.fs.client.UploadAndSignBlob(claim)
   311  	if err != nil {
   312  		log.Printf("rootsDir.Create(%q): %v", name, err)
   313  		return nil, fuse.EIO
   314  	}
   315  
   316  	nod := &mutDir{
   317  		fs:        n.fs,
   318  		permanode: pr.BlobRef,
   319  		name:      name,
   320  		xattrs:    map[string][]byte{},
   321  	}
   322  	n.mu.Lock()
   323  	n.m[name] = pr.BlobRef
   324  	n.mu.Unlock()
   325  
   326  	return nod, nil
   327  }