fusego/samples/hellofs/hello_fs.go

262 lines
4.9 KiB
Go

// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 hellofs
import (
"context"
"io"
"os"
"strings"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil"
"github.com/jacobsa/timeutil"
)
// Create a file system with a fixed structure that looks like this:
//
// hello
// dir/
// world
//
// Each file contains the string "Hello, world!".
func NewHelloFS(clock timeutil.Clock) (fuse.Server, error) {
fs := &helloFS{
Clock: clock,
}
return fuseutil.NewFileSystemServer(fs), nil
}
type helloFS struct {
fuseutil.NotImplementedFileSystem
Clock timeutil.Clock
}
const (
rootInode fuseops.InodeID = fuseops.RootInodeID + iota
helloInode
dirInode
worldInode
)
type inodeInfo struct {
attributes fuseops.InodeAttributes
// File or directory?
dir bool
// For directories, children.
children []fuseutil.Dirent
}
// We have a fixed directory structure.
var gInodeInfo = map[fuseops.InodeID]inodeInfo{
// root
rootInode: inodeInfo{
attributes: fuseops.InodeAttributes{
Nlink: 1,
Mode: 0555 | os.ModeDir,
},
dir: true,
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,
},
},
},
// hello
helloInode: inodeInfo{
attributes: fuseops.InodeAttributes{
Nlink: 1,
Mode: 0444,
Size: uint64(len("Hello, world!")),
},
},
// dir
dirInode: inodeInfo{
attributes: fuseops.InodeAttributes{
Nlink: 1,
Mode: 0555 | os.ModeDir,
},
dir: true,
children: []fuseutil.Dirent{
fuseutil.Dirent{
Offset: 1,
Inode: worldInode,
Name: "world",
Type: fuseutil.DT_File,
},
},
},
// world
worldInode: inodeInfo{
attributes: fuseops.InodeAttributes{
Nlink: 1,
Mode: 0444,
Size: uint64(len("Hello, world!")),
},
},
}
func findChildInode(
name string,
children []fuseutil.Dirent) (fuseops.InodeID, error) {
for _, e := range children {
if e.Name == name {
return e.Inode, nil
}
}
return 0, fuse.ENOENT
}
func (fs *helloFS) patchAttributes(
attr *fuseops.InodeAttributes) {
now := fs.Clock.Now()
attr.Atime = now
attr.Mtime = now
attr.Crtime = now
}
func (fs *helloFS) StatFS(
ctx context.Context,
op *fuseops.StatFSOp) error {
return nil
}
func (fs *helloFS) LookUpInode(
ctx context.Context,
op *fuseops.LookUpInodeOp) error {
// Find the info for the parent.
parentInfo, ok := gInodeInfo[op.Parent]
if !ok {
return fuse.ENOENT
}
// Find the child within the parent.
childInode, err := findChildInode(op.Name, parentInfo.children)
if err != nil {
return err
}
// Copy over information.
op.Entry.Child = childInode
op.Entry.Attributes = gInodeInfo[childInode].attributes
// Patch attributes.
fs.patchAttributes(&op.Entry.Attributes)
return nil
}
func (fs *helloFS) GetInodeAttributes(
ctx context.Context,
op *fuseops.GetInodeAttributesOp) error {
// Find the info for this inode.
info, ok := gInodeInfo[op.Inode]
if !ok {
return fuse.ENOENT
}
// Copy over its attributes.
op.Attributes = info.attributes
// Patch attributes.
fs.patchAttributes(&op.Attributes)
return nil
}
func (fs *helloFS) OpenDir(
ctx context.Context,
op *fuseops.OpenDirOp) error {
// Allow opening any directory.
return nil
}
func (fs *helloFS) ReadDir(
ctx context.Context,
op *fuseops.ReadDirOp) error {
// Find the info for this inode.
info, ok := gInodeInfo[op.Inode]
if !ok {
return fuse.ENOENT
}
if !info.dir {
return fuse.EIO
}
entries := info.children
// Grab the range of interest.
if op.Offset > fuseops.DirOffset(len(entries)) {
return fuse.EIO
}
entries = entries[op.Offset:]
// Resume at the specified offset into the array.
for _, e := range entries {
n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], e)
if n == 0 {
break
}
op.BytesRead += n
}
return nil
}
func (fs *helloFS) OpenFile(
ctx context.Context,
op *fuseops.OpenFileOp) error {
// Allow opening any file.
return nil
}
func (fs *helloFS) ReadFile(
ctx context.Context,
op *fuseops.ReadFileOp) error {
// Let io.ReaderAt deal with the semantics.
reader := strings.NewReader("Hello, world!")
var err error
op.BytesRead, err = reader.ReadAt(op.Dst, op.Offset)
// Special case: FUSE doesn't expect us to return io.EOF.
if err == io.EOF {
return nil
}
return err
}