【linux内核】 fuse go 源码分析

    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 再传递给用户程序的接口进行操作

【linux内核】 fuse go 源码分析_第1张图片

FUSE 特点

  • 用户空间文件系统
  • 允许非超户在用户空间开发自己的文件系统
  • 内核的API接口,使用fs-type操作
  • 支持多种编程语言( c、c++、java 、golang)
  • 普通用户也可以挂载 FUSE 文件系统
  • 无需重新编译内核

 

结构体 Inode 

    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

 

结构体 InHeader 请求头

type inHeader struct {
	Len    uint32
	Opcode uint32
	Unique uint64
	Nodeid uint64
	Uid    uint32
	Gid    uint32
	Pid    uint32
	_      uint32
}
  • Len: 请求的字节数长度,包括请求头后的具体内容
  • Opcode: 请求的类型
  • Unique: 请求唯一标识,响应中要对应着该 Unique
  • Nodeid: 该请求针对的文件nodeid,目标文件或者文件夹的nodeid
  • *id: 包括对文件/文件夹操作进程的用户ID / 用户组ID,以及进程ID

 

1. Mount 函数

    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
		}
	}

    1.1 mount 函数

     可以看到创建了 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()

    1.2 initMount 函数

      调用 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,只有初始化后,才会接收到其他请求

 

2. 实例化 Server 服务

    路径 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
}

 

2. Server 的 Serve 方法

    需要实现 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)
	}

    2.1 根节点设置 inode 编号为 1

// 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)

    2.2 循环读取请求,然后调用 serve 异步处理

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 章节处理)处理 

   2.3 handleRequest 函数

    主要根据不同的请求类型,进行处理

// 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

 

怎么使用 fuse

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)
}

 

 

 

你可能感兴趣的:(linux)