From 4b8df05859ea951e7c85947d8bf35c6be60e40e2 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sun, 29 Apr 2012 14:29:51 +1000 Subject: [PATCH] fs,fuse: new 'welcome' root node, where you can navigate/search anywhere. Change-Id: I66f076a85d8e474bb5d93ad8743fc8181de7502f --- cmd/cammount/cammount.go | 46 ++++++++++++++--------------- dev-cammount | 14 ++++++--- pkg/fs/fs.go | 50 ++++++++++++++++--------------- pkg/fs/root.go | 63 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 53 deletions(-) create mode 100644 pkg/fs/root.go diff --git a/cmd/cammount/cammount.go b/cmd/cammount/cammount.go index a9959f53e..ac880e48f 100644 --- a/cmd/cammount/cammount.go +++ b/cmd/cammount/cammount.go @@ -22,7 +22,6 @@ import ( "io/ioutil" "log" "os" - "sort" "camlistore.org/pkg/blobref" "camlistore.org/pkg/blobserver/localdisk" // used for the blob cache @@ -33,20 +32,6 @@ import ( "camlistore.org/third_party/code.google.com/p/rsc/fuse" ) -func PrintMap(m map[string]float64) { - keys := make([]string, len(m)) - for k, _ := range m { - keys = append(keys, k) - } - - sort.Strings(keys) - for _, k := range keys { - if m[k] > 0 { - fmt.Println(k, m[k]) - } - } -} - func main() { // Scans the arg list and sets up flags debug := flag.Bool("debug", false, "print debugging messages.") @@ -58,14 +43,12 @@ func main() { os.Exit(2) } - if flag.NArg() < 2 { - errorf("usage: cammount \n") + if n := flag.NArg(); n < 1 || n > 2 { + errorf("usage: cammount []\n") } - root := blobref.Parse(flag.Arg(0)) - if root == nil { - errorf("Error parsing root blobref: %q\n", root) - } + mountPoint := flag.Arg(0) + client := client.NewOrFail() // automatic from flags cacheDir, err := ioutil.TempDir("", "camlicache") @@ -79,18 +62,31 @@ func main() { } fetcher := cacher.NewCachingFetcher(diskcache, client) - fs := fs.NewCamliFileSystem(fetcher, root) + var camfs *fs.CamliFileSystem + if flag.NArg() == 2 { + root := blobref.Parse(flag.Arg(1)) + if root == nil { + errorf("Error parsing root blobref: %q\n", root) + } + var err error + camfs, err = fs.NewRootedCamliFileSystem(fetcher, root) + if err != nil { + errorf("Error creating root with %v: %v", root, err) + } + } else { + camfs = fs.NewCamliFileSystem(fetcher) + log.Printf("starting with fs %#v", camfs) + } + if *debug { // TODO: set fs's logger } - mountPoint := flag.Arg(1) - conn, err := fuse.Mount(mountPoint) if err != nil { log.Fatalf("Mount: %v", err) } - err = conn.Serve(fs) + err = conn.Serve(camfs) if err != nil { log.Fatalf("Serve: %v", err) } diff --git a/dev-cammount b/dev-cammount index 82ff95dae..dde16c160 100755 --- a/dev-cammount +++ b/dev-cammount @@ -5,10 +5,16 @@ use FindBin qw($Bin); use Getopt::Long; require "$Bin/misc/devlib.pl"; -my $blobref = shift; -unless ($blobref && $blobref =~ /^\w+-[0-9a-f]{10,}$/) { - die "Usage: dev-cammount [blobref]\n"; +my @blobref_arg; + +if (@ARGV) { + my $blobref = shift; + unless ($blobref && $blobref =~ /^\w+-[0-9a-f]{10,}$/) { + die "Usage: dev-cammount []\n"; + } + push @blobref_arg, $blobref; } + my $cammount = build_bin("./cmd/cammount"); my $dir = "/tmp/cammount-dir"; @@ -16,7 +22,7 @@ mkdir $dir, 0700 unless -d $dir; try_unmount(); print "Mounting on $dir ...\n"; -system("$cammount", "--blobserver=http://localhost:3179/bs", $blobref, $dir) +system("$cammount", "--blobserver=http://localhost:3179/bs", $dir, @blobref_arg) and warn "cammount failure: $!\n"; warn "Failed to unmount\n" unless try_unmount(); diff --git a/pkg/fs/fs.go b/pkg/fs/fs.go index 7ca437b23..08ad4377b 100644 --- a/pkg/fs/fs.go +++ b/pkg/fs/fs.go @@ -41,7 +41,7 @@ var errNotDir = fuse.Errno(syscall.ENOTDIR) type CamliFileSystem struct { fetcher blobref.SeekFetcher - root *blobref.BlobRef + root fuse.Node // IgnoreOwners, if true, collapses all file ownership to the // uid/gid running the fuse filesystem, and sets all the @@ -53,26 +53,40 @@ type CamliFileSystem struct { nameToAttr *lru.Cache // ~map[string]*fuse.Attr } -type CamliFile struct { - fs *CamliFileSystem - blob *blobref.BlobRef - ss *schema.Superset - - size uint64 // memoized -} - var _ fuse.FS = (*CamliFileSystem)(nil) -func NewCamliFileSystem(fetcher blobref.SeekFetcher, root *blobref.BlobRef) *CamliFileSystem { +func newCamliFileSystem(fetcher blobref.SeekFetcher) *CamliFileSystem { return &CamliFileSystem{ fetcher: fetcher, blobToSchema: lru.New(1024), // arbitrary; TODO: tunable/smarter? - root: root, nameToBlob: lru.New(1024), // arbitrary: TODO: tunable/smarter? nameToAttr: lru.New(1024), // arbitrary: TODO: tunable/smarter? } } +func NewCamliFileSystem(fetcher blobref.SeekFetcher) *CamliFileSystem { + fs := newCamliFileSystem(fetcher) + fs.root = &root{fs: fs} // root.go + return fs +} + +// NewRootedCamliFileSystem returns a CamliFileSystem with root as its +// base. +func NewRootedCamliFileSystem(fetcher blobref.SeekFetcher, root *blobref.BlobRef) (*CamliFileSystem, error) { + fs := newCamliFileSystem(fetcher) + + ss, err := fs.fetchSchemaSuperset(root) + if err != nil { + return nil, err + } + n := &node{fs: fs, blobref: root, ss: ss} + n.populateAttr() + fs.root = n + return fs, nil +} + +// node implements fuse.Node with a read-only Camli "file" or +// "directory" blob. type node struct { fs *CamliFileSystem blobref *blobref.BlobRef @@ -290,15 +304,7 @@ func (n *node) populateAttr() error { } func (fs *CamliFileSystem) Root() (fuse.Node, fuse.Error) { - ss, err := fs.fetchSchemaSuperset(fs.root) - if err != nil { - // TODO: map these to fuse.Error better - log.Printf("Error fetching root: %v", err) - return nil, fuse.EIO - } - n := &node{fs: fs, blobref: fs.root, ss: ss} - n.populateAttr() - return n, nil + return fs.root, nil } func (fs *CamliFileSystem) Statfs(req *fuse.StatfsRequest, res *fuse.StatfsResponse, intr fuse.Intr) fuse.Error { @@ -342,7 +348,3 @@ func (fs *CamliFileSystem) fetchSchemaSuperset(br *blobref.BlobRef) (*schema.Sup fs.blobToSchema.Add(blobStr, ss) return ss, nil } - -func (file *CamliFile) GetReader() (io.ReadCloser, error) { - return file.ss.NewFileReader(file.fs.fetcher) -} diff --git a/pkg/fs/root.go b/pkg/fs/root.go new file mode 100644 index 000000000..18348e140 --- /dev/null +++ b/pkg/fs/root.go @@ -0,0 +1,63 @@ +/* +Copyright 2012 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fs + +import ( + "log" + "os" + + "camlistore.org/pkg/blobref" + + "camlistore.org/third_party/code.google.com/p/rsc/fuse" +) + +// root implements fuse.Node and is the typical root of a +// CamliFilesystem with a little hello message and the ability to +// search and browse static snapshots, etc. +type root struct { + fs *CamliFileSystem +} + +func (n *root) Attr() fuse.Attr { + return fuse.Attr{ + Mode: os.ModeDir | 0755, + Uid: uint32(os.Getuid()), + Gid: uint32(os.Getgid()), + } +} + +func (n *root) ReadDir(intr fuse.Intr) ([]fuse.Dirent, fuse.Error) { + return []fuse.Dirent{ + {Name: "WELCOME.txt"}, + {Name: "tag"}, + {Name: "date"}, + {Name: "sha1-xxx...."}, + }, nil +} + +func (n *root) Lookup(name string, intr fuse.Intr) (fuse.Node, fuse.Error) { + if name == ".quitquitquit" { + log.Fatalf("Shutting down due to root .quitquitquit lookup.") + } + + br := blobref.Parse(name) + log.Printf("Root lookup of %q = %v", name, br) + if br != nil { + return &node{fs: n.fs, blobref: br}, nil + } + return nil, fuse.ENOENT +}