2015-02-27 00:54:16 +03:00
|
|
|
// 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 00:54:16 +03:00
|
|
|
|
2015-02-27 08:31:04 +03:00
|
|
|
package hellofs
|
2015-02-27 00:54:16 +03:00
|
|
|
|
|
|
|
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"
|
2015-02-27 01:59:51 +03:00
|
|
|
"github.com/jacobsa/fuse/fuseutil"
|
2015-02-27 00:54:16 +03:00
|
|
|
)
|
|
|
|
|
2015-03-24 07:46:49 +03:00
|
|
|
// Create a file system with a fixed structure that looks like this:
|
2015-02-27 00:54:16 +03:00
|
|
|
//
|
|
|
|
// 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) {
|
2015-03-25 01:29:24 +03:00
|
|
|
fs := &helloFS{
|
2015-03-24 07:46:49 +03:00
|
|
|
Clock: clock,
|
|
|
|
}
|
|
|
|
|
2015-03-25 01:29:24 +03:00
|
|
|
server = fuseutil.NewFileSystemServer(fs)
|
2015-03-24 07:46:49 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type helloFS struct {
|
2015-03-25 01:29:24 +03:00
|
|
|
fuseutil.NotImplementedFileSystem
|
2015-03-24 08:00:46 +03:00
|
|
|
|
2015-03-25 01:29:24 +03:00
|
|
|
Clock timeutil.Clock
|
2015-03-24 07:46:49 +03:00
|
|
|
}
|
|
|
|
|
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-06-05 07:23:05 +03:00
|
|
|
func (fs *helloFS) Init(op *fuseops.InitOp) (err error) {
|
2015-03-25 01:29:24 +03:00
|
|
|
return
|
2015-02-27 06:40:32 +03:00
|
|
|
}
|
|
|
|
|
2015-06-05 07:23:05 +03:00
|
|
|
func (fs *helloFS) LookUpInode(op *fuseops.LookUpInodeOp) (err error) {
|
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-25 01:29:24 +03:00
|
|
|
func (fs *helloFS) GetInodeAttributes(
|
2015-06-05 07:23:05 +03:00
|
|
|
op *fuseops.GetInodeAttributesOp) (err error) {
|
2015-02-27 05:37:21 +03:00
|
|
|
// Find the info for this inode.
|
2015-03-24 07:43:19 +03:00
|
|
|
info, ok := gInodeInfo[op.Inode]
|
2015-02-27 05:37:21 +03:00
|
|
|
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 05:37:21 +03:00
|
|
|
|
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
|
|
|
|
2015-02-27 05:37:21 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-03-25 01:29:24 +03:00
|
|
|
func (fs *helloFS) OpenDir(
|
2015-06-05 07:23:05 +03:00
|
|
|
op *fuseops.OpenDirOp) (err error) {
|
2015-02-27 06:43:05 +03:00
|
|
|
// Allow opening any directory.
|
2015-03-25 01:29:24 +03:00
|
|
|
return
|
2015-02-27 00:54:16 +03:00
|
|
|
}
|
2015-02-27 04:21:12 +03:00
|
|
|
|
2015-03-25 01:29:24 +03:00
|
|
|
func (fs *helloFS) ReadDir(
|
2015-06-05 07:23:05 +03:00
|
|
|
op *fuseops.ReadDirOp) (err error) {
|
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-25 01:29:24 +03:00
|
|
|
func (fs *helloFS) OpenFile(
|
2015-06-05 07:23:05 +03:00
|
|
|
op *fuseops.OpenFileOp) (err error) {
|
2015-02-27 07:50:01 +03:00
|
|
|
// Allow opening any file.
|
2015-03-25 01:29:24 +03:00
|
|
|
return
|
2015-02-27 07:50:01 +03:00
|
|
|
}
|
2015-02-27 07:57:06 +03:00
|
|
|
|
2015-03-25 01:29:24 +03:00
|
|
|
func (fs *helloFS) ReadFile(
|
2015-06-05 07:23:05 +03:00
|
|
|
op *fuseops.ReadFileOp) (err error) {
|
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
|
|
|
|
}
|