fusego/fuseutil/file_system.go

251 lines
7.1 KiB
Go
Raw Permalink Normal View History

// 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 fuseutil
import (
"context"
2015-03-25 01:24:44 +03:00
"io"
2015-06-09 03:47:35 +03:00
"sync"
2015-03-25 01:24:44 +03:00
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
)
// An interface with a method for each op type in the fuseops package. This can
// be used in conjunction with NewFileSystemServer to avoid writing a "dispatch
// loop" that switches on op types, instead receiving typed method calls
// directly.
//
2015-07-27 08:44:17 +03:00
// The FileSystem implementation should not call Connection.Reply, instead
// returning the error with which the caller should respond.
2015-03-25 01:17:58 +03:00
//
// See NotImplementedFileSystem for a convenient way to embed default
// implementations for methods you don't care about.
type FileSystem interface {
2015-09-09 02:47:48 +03:00
StatFS(context.Context, *fuseops.StatFSOp) error
2015-07-27 08:15:07 +03:00
LookUpInode(context.Context, *fuseops.LookUpInodeOp) error
GetInodeAttributes(context.Context, *fuseops.GetInodeAttributesOp) error
SetInodeAttributes(context.Context, *fuseops.SetInodeAttributesOp) error
ForgetInode(context.Context, *fuseops.ForgetInodeOp) error
BatchForget(context.Context, *fuseops.BatchForgetOp) error
2015-07-27 08:15:07 +03:00
MkDir(context.Context, *fuseops.MkDirOp) error
2015-12-15 02:35:40 +03:00
MkNode(context.Context, *fuseops.MkNodeOp) error
2015-07-27 08:15:07 +03:00
CreateFile(context.Context, *fuseops.CreateFileOp) error
2017-11-24 14:44:41 +03:00
CreateLink(context.Context, *fuseops.CreateLinkOp) error
2015-07-27 08:15:07 +03:00
CreateSymlink(context.Context, *fuseops.CreateSymlinkOp) error
Rename(context.Context, *fuseops.RenameOp) error
RmDir(context.Context, *fuseops.RmDirOp) error
Unlink(context.Context, *fuseops.UnlinkOp) error
OpenDir(context.Context, *fuseops.OpenDirOp) error
ReadDir(context.Context, *fuseops.ReadDirOp) error
ReleaseDirHandle(context.Context, *fuseops.ReleaseDirHandleOp) error
OpenFile(context.Context, *fuseops.OpenFileOp) error
ReadFile(context.Context, *fuseops.ReadFileOp) error
WriteFile(context.Context, *fuseops.WriteFileOp) error
SyncFile(context.Context, *fuseops.SyncFileOp) error
FlushFile(context.Context, *fuseops.FlushFileOp) error
ReleaseFileHandle(context.Context, *fuseops.ReleaseFileHandleOp) error
ReadSymlink(context.Context, *fuseops.ReadSymlinkOp) error
2015-12-04 23:26:49 +03:00
RemoveXattr(context.Context, *fuseops.RemoveXattrOp) error
2015-12-10 23:58:46 +03:00
GetXattr(context.Context, *fuseops.GetXattrOp) error
ListXattr(context.Context, *fuseops.ListXattrOp) error
2017-03-06 06:40:50 +03:00
SetXattr(context.Context, *fuseops.SetXattrOp) error
Fallocate(context.Context, *fuseops.FallocateOp) error
2023-03-22 18:46:22 +03:00
Poll(context.Context, *fuseops.PollOp) error
2015-06-05 06:51:27 +03:00
2023-03-23 00:30:11 +03:00
SetConnection(*fuse.Connection)
2015-06-05 06:51:27 +03:00
// Regard all inodes (including the root inode) as having their lookup counts
// decremented to zero, and clean up any resources associated with the file
// system. No further calls to the file system will be made.
Destroy()
}
// Create a fuse.Server that handles ops by calling the associated FileSystem
// method.Respond with the resulting error. Unsupported ops are responded to
// directly with ENOSYS.
//
// Each call to a FileSystem method (except ForgetInode) is made on
// its own goroutine, and is free to block. ForgetInode may be called
// synchronously, and should not depend on calls to other methods
// being received concurrently.
2015-04-02 02:50:44 +03:00
//
// (It is safe to naively process ops concurrently because the kernel
// guarantees to serialize operations that the user expects to happen in order,
// cf. http://goo.gl/jnkHPO, fuse-devel thread "Fuse guarantees on concurrent
// requests").
2015-03-25 01:24:44 +03:00
func NewFileSystemServer(fs FileSystem) fuse.Server {
return &fileSystemServer{
2015-06-09 03:47:35 +03:00
fs: fs,
}
2015-03-25 01:24:44 +03:00
}
type fileSystemServer struct {
2015-06-09 03:47:35 +03:00
fs FileSystem
opsInFlight sync.WaitGroup
2015-03-25 01:24:44 +03:00
}
func (s *fileSystemServer) ServeOps(c *fuse.Connection) {
2023-03-23 00:30:11 +03:00
s.fs.SetConnection(c)
2015-06-09 04:11:52 +03:00
// When we are done, we clean up by waiting for all in-flight ops then
// destroying the file system.
defer func() {
s.opsInFlight.Wait()
s.fs.Destroy()
}()
2015-03-25 01:24:44 +03:00
for {
2015-07-27 08:15:07 +03:00
ctx, op, err := c.ReadOp()
2015-03-25 01:24:44 +03:00
if err == io.EOF {
break
}
if err != nil {
panic(err)
}
2015-06-09 03:47:35 +03:00
s.opsInFlight.Add(1)
if _, ok := op.(*fuseops.ForgetInodeOp); ok {
// Special case: call in this goroutine for
// forget inode ops, which may come in a
// flurry from the kernel and are generally
// cheap for the file system to handle
s.handleOp(c, ctx, op)
} else {
go s.handleOp(c, ctx, op)
}
2015-04-02 02:52:58 +03:00
}
}
2015-03-25 01:24:44 +03:00
2015-07-27 08:15:07 +03:00
func (s *fileSystemServer) handleOp(
c *fuse.Connection,
ctx context.Context,
2015-07-27 08:44:17 +03:00
op interface{}) {
defer s.opsInFlight.Done()
2015-04-02 03:19:56 +03:00
// Dispatch to the appropriate method.
2015-06-05 07:20:39 +03:00
var err error
2015-04-02 02:52:58 +03:00
switch typed := op.(type) {
default:
2015-06-05 07:20:39 +03:00
err = fuse.ENOSYS
2015-03-25 01:24:44 +03:00
2015-09-09 02:53:11 +03:00
case *fuseops.StatFSOp:
err = s.fs.StatFS(ctx, typed)
2015-04-02 02:52:58 +03:00
case *fuseops.LookUpInodeOp:
2015-07-27 08:15:07 +03:00
err = s.fs.LookUpInode(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-04-02 02:52:58 +03:00
case *fuseops.GetInodeAttributesOp:
2015-07-27 08:15:07 +03:00
err = s.fs.GetInodeAttributes(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-04-02 02:52:58 +03:00
case *fuseops.SetInodeAttributesOp:
2015-07-27 08:15:07 +03:00
err = s.fs.SetInodeAttributes(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-04-02 02:52:58 +03:00
case *fuseops.ForgetInodeOp:
2015-07-27 08:15:07 +03:00
err = s.fs.ForgetInode(ctx, typed)
2015-03-25 01:24:44 +03:00
case *fuseops.BatchForgetOp:
err = s.fs.BatchForget(ctx, typed)
if err == fuse.ENOSYS {
// Handle as a series of single-inode forget operations
for _, entry := range typed.Entries {
err = s.fs.ForgetInode(ctx, &fuseops.ForgetInodeOp{
Inode: entry.Inode,
N: entry.N,
OpContext: typed.OpContext,
})
if err != nil {
break
}
}
}
2015-04-02 02:52:58 +03:00
case *fuseops.MkDirOp:
2015-07-27 08:15:07 +03:00
err = s.fs.MkDir(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-12-15 02:35:40 +03:00
case *fuseops.MkNodeOp:
err = s.fs.MkNode(ctx, typed)
2015-04-02 02:52:58 +03:00
case *fuseops.CreateFileOp:
2015-07-27 08:15:07 +03:00
err = s.fs.CreateFile(ctx, typed)
2015-03-25 01:24:44 +03:00
2017-11-24 14:44:41 +03:00
case *fuseops.CreateLinkOp:
err = s.fs.CreateLink(ctx, typed)
2015-05-19 08:33:19 +03:00
case *fuseops.CreateSymlinkOp:
2015-07-27 08:15:07 +03:00
err = s.fs.CreateSymlink(ctx, typed)
2015-05-19 08:33:19 +03:00
case *fuseops.RenameOp:
2015-07-27 08:15:07 +03:00
err = s.fs.Rename(ctx, typed)
2015-04-02 02:52:58 +03:00
case *fuseops.RmDirOp:
2015-07-27 08:15:07 +03:00
err = s.fs.RmDir(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-04-02 02:52:58 +03:00
case *fuseops.UnlinkOp:
2015-07-27 08:15:07 +03:00
err = s.fs.Unlink(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-04-02 02:52:58 +03:00
case *fuseops.OpenDirOp:
2015-07-27 08:15:07 +03:00
err = s.fs.OpenDir(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-04-02 02:52:58 +03:00
case *fuseops.ReadDirOp:
2015-07-27 08:15:07 +03:00
err = s.fs.ReadDir(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-04-02 02:52:58 +03:00
case *fuseops.ReleaseDirHandleOp:
2015-07-27 08:15:07 +03:00
err = s.fs.ReleaseDirHandle(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-04-02 02:52:58 +03:00
case *fuseops.OpenFileOp:
2015-07-27 08:15:07 +03:00
err = s.fs.OpenFile(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-04-02 02:52:58 +03:00
case *fuseops.ReadFileOp:
2015-07-27 08:15:07 +03:00
err = s.fs.ReadFile(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-04-02 02:52:58 +03:00
case *fuseops.WriteFileOp:
2015-07-27 08:15:07 +03:00
err = s.fs.WriteFile(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-04-02 02:52:58 +03:00
case *fuseops.SyncFileOp:
2015-07-27 08:15:07 +03:00
err = s.fs.SyncFile(ctx, typed)
2015-03-25 01:24:44 +03:00
2015-04-02 02:52:58 +03:00
case *fuseops.FlushFileOp:
2015-07-27 08:15:07 +03:00
err = s.fs.FlushFile(ctx, typed)
2015-04-02 02:52:58 +03:00
case *fuseops.ReleaseFileHandleOp:
2015-07-27 08:15:07 +03:00
err = s.fs.ReleaseFileHandle(ctx, typed)
2015-05-19 09:06:57 +03:00
case *fuseops.ReadSymlinkOp:
2015-07-27 08:15:07 +03:00
err = s.fs.ReadSymlink(ctx, typed)
2015-12-04 23:26:49 +03:00
case *fuseops.RemoveXattrOp:
err = s.fs.RemoveXattr(ctx, typed)
2015-12-10 23:58:46 +03:00
case *fuseops.GetXattrOp:
err = s.fs.GetXattr(ctx, typed)
case *fuseops.ListXattrOp:
err = s.fs.ListXattr(ctx, typed)
2017-03-06 06:40:50 +03:00
case *fuseops.SetXattrOp:
err = s.fs.SetXattr(ctx, typed)
case *fuseops.FallocateOp:
err = s.fs.Fallocate(ctx, typed)
2023-03-22 18:46:22 +03:00
case *fuseops.PollOp:
err = s.fs.Poll(ctx, typed)
2015-03-25 01:24:44 +03:00
}
2015-06-05 07:20:39 +03:00
2015-07-27 08:15:07 +03:00
c.Reply(ctx, err)
2015-03-25 01:24:44 +03:00
}