fusego/samples/hellofs/hello_fs.go

304 lines
5.6 KiB
Go
Raw Normal View History

// Copyright 2015 Google Inc. All Rights Reserved.
2015-03-04 00:27:42 +03:00
//
// 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.
2015-02-27 08:31:04 +03:00
package hellofs
import (
2015-02-27 07:57:06 +03:00
"io"
2015-02-27 05:44:51 +03:00
"os"
2015-02-27 07:57:06 +03:00
"strings"
2015-02-27 05:44:51 +03:00
2015-03-24 07:39:45 +03:00
"github.com/googlecloudplatform/gcsfuse/timeutil"
2015-02-27 00:57:18 +03:00
"github.com/jacobsa/fuse"
2015-03-24 07:39:45 +03:00
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil"
)
2015-03-24 07:46:49 +03:00
// Create a file system with a fixed structure that looks like this:
//
// hello
// dir/
// world
//
// Each file contains the string "Hello, world!".
2015-03-24 07:46:49 +03:00
func NewHelloFS(clock timeutil.Clock) (server fuse.Server, err error) {
fs := &helloFS{
Clock: clock,
}
server = fuse.Server(fs.serve)
return
}
type helloFS struct {
Clock timeutil.Clock
}
2015-03-24 07:46:49 +03:00
func (fs *helloFS) serve(c *fuse.Connection) {
for {
op, err := c.ReadOp()
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
switch typed := op.(type) {
2015-03-24 07:49:26 +03:00
case *fuseops.InitOp:
fs.init(typed)
2015-03-24 08:00:46 +03:00
case *fuseops.LookUpInodeOp:
fs.lookUpInode(typed)
case *fuseops.GetInodeAttributesOp:
fs.getInodeAttributes(typed)
case *fuseops.OpenDirOp:
fs.openDir(typed)
case *fuseops.ReadDirOp:
fs.readDir(typed)
case *fuseops.OpenFileOp:
fs.openFile(typed)
case *fuseops.ReadFileOp:
fs.readFile(typed)
2015-03-24 07:46:49 +03:00
default:
typed.Respond(fuse.ENOSYS)
}
}
}
2015-02-27 03:55:00 +03:00
const (
2015-03-24 07:39:45 +03:00
rootInode fuseops.InodeID = fuseops.RootInodeID + iota
2015-02-27 03:55:00 +03:00
helloInode
dirInode
worldInode
)
2015-02-27 05:35:16 +03:00
type inodeInfo struct {
2015-03-24 07:39:45 +03:00
attributes fuseops.InodeAttributes
2015-02-27 05:36:20 +03:00
2015-02-27 05:35:16 +03:00
// File or directory?
dir bool
// For directories, children.
children []fuseutil.Dirent
}
// We have a fixed directory structure.
2015-03-24 07:39:45 +03:00
var gInodeInfo = map[fuseops.InodeID]inodeInfo{
2015-02-27 05:35:16 +03:00
// root
rootInode: inodeInfo{
2015-03-24 07:39:45 +03:00
attributes: fuseops.InodeAttributes{
2015-03-18 06:11:36 +03:00
Nlink: 1,
Mode: 0555 | os.ModeDir,
2015-02-27 05:44:51 +03:00
},
dir: true,
2015-02-27 05:35:16 +03:00
children: []fuseutil.Dirent{
fuseutil.Dirent{
Offset: 1,
Inode: helloInode,
Name: "hello",
Type: fuseutil.DT_File,
},
fuseutil.Dirent{
Offset: 2,
Inode: dirInode,
Name: "dir",
Type: fuseutil.DT_Directory,
},
},
},
2015-02-27 05:52:43 +03:00
// hello
helloInode: inodeInfo{
2015-03-24 07:39:45 +03:00
attributes: fuseops.InodeAttributes{
2015-03-18 06:11:36 +03:00
Nlink: 1,
Mode: 0444,
Size: uint64(len("Hello, world!")),
2015-02-27 05:52:43 +03:00
},
},
2015-02-27 06:00:18 +03:00
// dir
dirInode: inodeInfo{
2015-03-24 07:39:45 +03:00
attributes: fuseops.InodeAttributes{
2015-03-18 06:11:36 +03:00
Nlink: 1,
Mode: 0555 | os.ModeDir,
2015-02-27 06:00:18 +03:00
},
dir: true,
children: []fuseutil.Dirent{
fuseutil.Dirent{
Offset: 1,
Inode: worldInode,
Name: "world",
Type: fuseutil.DT_File,
},
},
},
2015-02-27 06:03:38 +03:00
// world
worldInode: inodeInfo{
2015-03-24 07:39:45 +03:00
attributes: fuseops.InodeAttributes{
2015-03-18 06:11:36 +03:00
Nlink: 1,
Mode: 0444,
Size: uint64(len("Hello, world!")),
2015-02-27 06:03:38 +03:00
},
},
2015-02-27 05:35:16 +03:00
}
2015-02-27 05:56:51 +03:00
func findChildInode(
name string,
2015-03-24 07:39:45 +03:00
children []fuseutil.Dirent) (inode fuseops.InodeID, err error) {
2015-02-27 05:56:51 +03:00
for _, e := range children {
if e.Name == name {
inode = e.Inode
return
}
}
err = fuse.ENOENT
return
}
2015-03-24 07:46:49 +03:00
func (fs *helloFS) patchAttributes(
2015-03-24 07:39:45 +03:00
attr *fuseops.InodeAttributes) {
2015-02-27 06:24:31 +03:00
now := fs.Clock.Now()
attr.Atime = now
attr.Mtime = now
attr.Crtime = now
}
2015-03-24 07:46:49 +03:00
func (fs *helloFS) init(op *fuseops.InitOp) {
2015-03-24 07:40:31 +03:00
op.Respond(nil)
2015-02-27 06:40:32 +03:00
}
2015-03-24 07:46:49 +03:00
func (fs *helloFS) lookUpInode(op *fuseops.LookUpInodeOp) {
2015-03-24 07:43:19 +03:00
var err error
defer func() { op.Respond(err) }()
2015-02-27 05:56:51 +03:00
// Find the info for the parent.
2015-03-24 07:43:19 +03:00
parentInfo, ok := gInodeInfo[op.Parent]
2015-02-27 05:56:51 +03:00
if !ok {
err = fuse.ENOENT
return
}
// Find the child within the parent.
2015-03-24 07:43:19 +03:00
childInode, err := findChildInode(op.Name, parentInfo.children)
2015-02-27 05:56:51 +03:00
if err != nil {
return
}
// Copy over information.
2015-03-24 07:43:19 +03:00
op.Entry.Child = childInode
op.Entry.Attributes = gInodeInfo[childInode].attributes
2015-02-27 05:56:51 +03:00
2015-02-27 06:05:52 +03:00
// Patch attributes.
2015-03-24 07:43:19 +03:00
fs.patchAttributes(&op.Entry.Attributes)
2015-02-27 06:05:52 +03:00
2015-02-27 05:56:51 +03:00
return
}
2015-03-24 07:46:49 +03:00
func (fs *helloFS) getInodeAttributes(op *fuseops.GetInodeAttributesOp) {
2015-03-24 07:43:19 +03:00
var err error
defer func() { op.Respond(err) }()
// Find the info for this inode.
2015-03-24 07:43:19 +03:00
info, ok := gInodeInfo[op.Inode]
if !ok {
err = fuse.ENOENT
return
}
// Copy over its attributes.
2015-03-24 07:43:19 +03:00
op.Attributes = info.attributes
2015-02-27 06:05:52 +03:00
// Patch attributes.
2015-03-24 07:43:19 +03:00
fs.patchAttributes(&op.Attributes)
2015-02-27 06:05:52 +03:00
return
}
2015-03-24 07:46:49 +03:00
func (fs *helloFS) openDir(op *fuseops.OpenDirOp) {
2015-02-27 06:43:05 +03:00
// Allow opening any directory.
2015-03-24 07:43:19 +03:00
op.Respond(nil)
}
2015-02-27 04:21:12 +03:00
2015-03-24 08:00:46 +03:00
func (fs *helloFS) readDir(op *fuseops.ReadDirOp) {
2015-03-24 07:43:19 +03:00
var err error
defer func() { op.Respond(err) }()
2015-02-27 04:21:12 +03:00
2015-02-27 05:35:16 +03:00
// Find the info for this inode.
2015-03-24 07:43:19 +03:00
info, ok := gInodeInfo[op.Inode]
2015-02-27 04:21:12 +03:00
if !ok {
err = fuse.ENOENT
return
}
2015-02-27 05:35:16 +03:00
if !info.dir {
err = fuse.EIO
return
}
entries := info.children
2015-02-27 04:38:27 +03:00
// Grab the range of interest.
2015-03-24 07:46:49 +03:00
if op.Offset > fuseops.DirOffset(len(entries)) {
2015-02-27 04:21:12 +03:00
err = fuse.EIO
return
}
2015-03-24 07:43:19 +03:00
entries = entries[op.Offset:]
2015-02-27 04:38:27 +03:00
2015-02-27 04:21:12 +03:00
// Resume at the specified offset into the array.
for _, e := range entries {
2015-03-24 07:43:19 +03:00
op.Data = fuseutil.AppendDirent(op.Data, e)
if len(op.Data) > op.Size {
op.Data = op.Data[:op.Size]
2015-02-27 04:21:12 +03:00
break
}
}
return
}
2015-02-27 07:50:01 +03:00
2015-03-24 07:46:49 +03:00
func (fs *helloFS) openFile(op *fuseops.OpenFileOp) {
2015-02-27 07:50:01 +03:00
// Allow opening any file.
2015-03-24 07:43:19 +03:00
op.Respond(nil)
2015-02-27 07:50:01 +03:00
}
2015-02-27 07:57:06 +03:00
2015-03-24 07:46:49 +03:00
func (fs *helloFS) readFile(op *fuseops.ReadFileOp) {
2015-03-24 07:43:19 +03:00
var err error
defer func() { op.Respond(err) }()
2015-02-27 07:57:06 +03:00
// Let io.ReaderAt deal with the semantics.
reader := strings.NewReader("Hello, world!")
2015-03-24 07:43:19 +03:00
op.Data = make([]byte, op.Size)
n, err := reader.ReadAt(op.Data, op.Offset)
op.Data = op.Data[:n]
2015-02-27 07:57:06 +03:00
// Special case: FUSE doesn't expect us to return io.EOF.
if err == io.EOF {
err = nil
}
return
}