github: https://github.com/bazil/fuse
Fuse是filesystem in user space,一个用户空间的文件系统框架,fuse包含包含一个内核模块和一个用户空间守护进程
FUSE分为三大模块:
- FUSE内核模块(内核态)
- LibFUSE模块(用户态)
- 用户程序模块(用户态)
LibFUSE 实现文件系统主要框架、对实现的文件系统操作进行封装、mount管理、通过设备/dev/fuse与内核模块通信
FUSE 内核模块实现 VFS 接口(fuse文件驱动注册、supper block、dentry、inode的维护),接收请求传递给LibFUSE,LibFUSE 再传递给用户程序的接口进行操作
- 用户空间文件系统
- 允许非超户在用户空间开发自己的文件系统
- 内核的API接口,使用fs-type操作
- 支持多种编程语言( c、c++、java 、golang)
- 普通用户也可以挂载 FUSE 文件系统
- 无需重新编译内核
StaleAttr 包括权限,编号,主要结构在 rawBridge
注意:生成 Inode 使用 NewInode 或者 NewPersistentInode
// Inode is a node in VFS tree. Inodes are one-to-one mapped to // Operations instances, which is the extension interface for file // systems. One can create fully-formed trees of Inodes ahead of time // by creating "persistent" Inodes. // // The Inode struct contains a lock, so it should not be // copied. Inodes should be obtained by calling Inode.NewInode() or // Inode.NewPersistentInode(). type Inode struct { stableAttr StableAttr ops InodeEmbedder bridge *rawBridge // Following data is mutable. // file handles. // protected by bridge.mu openFiles []uint32
type inHeader struct {
Len uint32
Opcode uint32
Unique uint64
Nodeid uint64
Uid uint32
Gid uint32
Pid uint32
_ uint32
}
Mount 函数在目录 dir 下,开启服务请求,调用也非常简单,路径 bazil.org/fuse/fuse.go 文件
conn, err := fuse.Mount(f.WorkSpace)
if err != nil {
logrus.Errorf("fuse mount err: %v", err)
return
}
defer conn.Close()
Mount 函数创建 socket pair,调用 fusermount 命令进行挂载,打开设备 /dev/fuse
// Mount mounts a new FUSE connection on the named directory
// and returns a connection for reading and writing FUSE messages.
//
// After a successful return, caller must call Close to free
// resources.
//
// Even on successful return, the new mount is not guaranteed to be
// visible until after Conn.Ready is closed. See Conn.MountError for
// possible errors. Incoming requests on Conn must be served to make
// progress.
func Mount(dir string, options ...MountOption) (*Conn, error) {
conf := mountConfig{
options: make(map[string]string),
}
for _, option := range options {
if err := option(&conf); err != nil {
return nil, err
}
}
可以看到创建了 socket pair,调用 fusemount 命令执行挂载,打开 /dev/fuse 设备
func mount(dir string, conf *mountConfig, ready chan<- struct{}, errp *error) (fusefd *os.File, err error) {
// linux mount is never delayed
close(ready)
fds, err := syscall.Socketpair(syscall.AF_FILE, syscall.SOCK_STREAM, 0)
if err != nil {
return nil, fmt.Errorf("socketpair error: %v", err)
}
writeFile := os.NewFile(uintptr(fds[0]), "fusermount-child-writes")
defer writeFile.Close()
readFile := os.NewFile(uintptr(fds[1]), "fusermount-parent-reads")
defer readFile.Close()
调用 ReadRequest 函数读取消息,比较协议的版本不能低于多少,然后发送 response
func initMount(c *Conn, conf *mountConfig) error {
req, err := c.ReadRequest()
if err != nil {
if err == io.EOF {
return ErrClosedWithoutInit
}
return err
}
r, ok := req.(*InitRequest)
if !ok {
return fmt.Errorf("missing init, got: %T", req)
}
Fuse文件系统的初始化函数,当Mount以后,从/dev/fuse
读到的第一个请求就是init
,只有初始化后,才会接收到其他请求
路径 bazil.org/fuse/fs/serve.go,主要是通过 /dev/fuse 与内核模块通信
// New returns a new FUSE server ready to serve this kernel FUSE
// connection.
//
// Config may be nil.
func New(conn *fuse.Conn, config *Config) *Server {
s := &Server{
conn: conn,
req: map[fuse.RequestID]*serveRequest{},
nodeRef: map[Node]fuse.NodeID{},
dynamicInode: GenerateDynamicInode,
}
if config != nil {
s.debug = config.Debug
s.context = config.WithContext
}
if s.debug == nil {
s.debug = fuse.Debug
}
return s
}
需要实现 FS 接口定义文件系统,方法 Root 返回的作为文件系统的根
// Serve serves the FUSE connection by making calls to the methods
// of fs and the Nodes and Handles it makes available. It returns only
// when the connection has been closed or an unexpected error occurs.
func (s *Server) Serve(fs FS) error {
defer s.wg.Wait() // Wait for worker goroutines to complete before return
s.fs = fs
if dyn, ok := fs.(FSInodeGenerator); ok {
s.dynamicInode = dyn.GenerateInode
}
root, err := fs.Root()
if err != nil {
return fmt.Errorf("cannot obtain root node: %v", err)
}
// Recognize the root node if it's ever returned from Lookup,
// passed to Invalidate, etc.
s.nodeRef[root] = 1
s.node = append(s.node, nil, &serveNode{
inode: 1,
generation: s.nodeGen,
node: root,
refs: 1,
})
s.handle = append(s.handle, nil)
for {
req, err := s.conn.ReadRequest()
if err != nil {
if err == io.EOF {
break
}
return err
}
s.wg.Add(1)
go func() {
defer s.wg.Done()
s.serve(req)
}()
}
return nil
2.2.1 serve 函数主要调用 handleRequest 函数(2.3 章节处理)处理
主要根据不同的请求类型,进行处理
// handleRequest will either a) call done(s) and r.Respond(s) OR b) return an error.
func (c *Server) handleRequest(ctx context.Context, node Node, snode *serveNode, r fuse.Request, done func(resp interface{})) error {
switch r := r.(type) {
default:
// Note: To FUSE, ENOSYS means "this server never implements this request."
// It would be inappropriate to return ENOSYS for other operations in this
// switch that might only be unavailable in some contexts, not all.
return syscall.ENOSYS
2.3.1 请求为 GetattrRequest 类型
如果节点实现了 NodeGetattrer 方法,则调用 Getattr 方法,未实现好像调用 Attr 方法
// Node operations.
case *fuse.GetattrRequest:
s := &fuse.GetattrResponse{}
if n, ok := node.(NodeGetattrer); ok {
if err := n.Getattr(ctx, r, s); err != nil {
return err
}
} else {
if err := snode.attr(ctx, &s.Attr); err != nil {
return err
}
}
done(s)
r.Respond(s)
return nil
2.3.2 请求类型为 MkdirRequest 类型
调用 Mkdir 这个用户自己实现,这也缓存 saveLoopup
case *fuse.MkdirRequest:
s := &fuse.MkdirResponse{}
initLookupResponse(&s.LookupResponse)
n, ok := node.(NodeMkdirer)
if !ok {
return syscall.EPERM
}
n2, err := n.Mkdir(ctx, r)
if err != nil {
return err
}
if err := c.saveLookup(ctx, &s.LookupResponse, snode, r.Name, n2); err != nil {
return err
}
done(s)
r.Respond(s)
return nil
conn, err := fuse.Mount(f.WorkSpace)
if err != nil {
logrus.Errorf("fuse mount err: %v", err)
return
}
defer conn.Close()
server := fs.New(conn, nil)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go waitForUpdateMetada(f, ctx)
if err := server.Serve(f); err != nil {
logrus.Errorf("server serve err: %v", err)
return
}
<-conn.Ready
if err := conn.MountError; err != nil {
logrus.Errorf("conn mount error: %v", err)
}