FUSE协议解析

FUSE(Filesystem in Userspace)允许在用户空间实现自定义文件系统,无需编写内核模块。本文详细介绍了FUSE的工作原理,包括交互方式、请求头和响应头的结构,并列举了多种FUSE指令,如LOOKUP、FORGET、GETATTR等,帮助读者理解FUSE如何在用户态和内核间通信。
摘要由CSDN通过智能技术生成

由于以前有项目是用到FUSE,将S3等对象存储映射为文件存储的,但我不是负责那一块,所以一直只是知道FUSE是个什么东西,而没有用过。刚好趁着没工作的这段时间,学习Golang,顺便把FUSE也了解下,实现了一个简易版的libfuse: https://github.com/mingforpc/fuse-go和一个可以将HDFS Mount到本地文件夹的程序: https://github.com/mingforpc/hadoop-fs

由于刚学并整理,如有遗漏或者错误!请拼命指出,谢谢!

什么是FUSE

FUSE的全称是Filesystem in Userspace,即用户空间文件系统,是系统内核提供的一个功能,使得可以在用户态下实现一个自定义的文件系统。比如CEPH和GlusterFS等都有使用到FUSE。

libfuse则是提供给用户态下的开发包。

FUSE是怎么交互的

FUSE是通过读写/dev/fuse让用户态的文件系统进程和内核通信的。

大概流程

程序需要先打开/dev/fuse,然后通过mount()/dev/fuse的fd,进程的用户id和组id传入,进行文件系统的挂载。

PS: libfuse通过自己写的fusermount程序(编译安装libfuse后会在/bin/下),可以让我们实现的文件系统程序在非root权限下挂载,这部分我还不是很了解,我自己实现的go版libfuse也只是依赖于这个fusermount。

FUSE的指令号、对应的函数、请求格式与响应

程序从/dev/fuse中读取请求,不需要担心像TCP有半包粘包等问题,一次读取一条请求。buffer不够大,会报异常。

请求和响应,都是以二进制字节表示的,下文中的结构体只是为了方便看。

请求头

每个命令的前 40 bytes为请求头,转为Golang的结构体如下:

// Each query starts with a FuseInHeader
type FuseInHeader struct {
	Len     uint32
	Opcode  uint32
	Unique  uint64
	Nodeid  uint64
	Uid     uint32
	Gid     uint32
	Pid     uint32
	Padding uint32
}
  • Len: 是整个请求的字节数长度,包括请求头后的具体内容
  • Opcode: 请求的类型
  • Unique: 该请求唯一标识,响应中要对应着该Unique
  • Nodeid: 该请求针对的文件nodeid,目标文件或者文件夹的nodeid
  • Uid: 对该文件/文件夹操作的进程的用户ID
  • Gid: 对该文件/文件夹操作的进程的用户组ID
  • Pid: 对该文件/文件夹操作的进程的进程ID

响应头

程序写入/dev/fuse的每个响应的前 16 bytes为响应头,转为Golang的结构体如下:

type FuseOutHeader struct {
	Len    uint32
	Error  int32
	Unique uint64
}
  • Len: 是整个响应的字节数长度,包括响应头后的具体内容
  • Error: 一个负数的错误码,成功返回0,其他对应着系统(error.h)的错误代码,但是为负数,每个操作的错误返回可以查看linux man中相应的函数
  • Unique: 对应者请求的唯一标识

请求类型和具体结构

数字对应着请求中的Opcode

FUSE_LOOKUP = 1

lookup()函数,Look up a directory entry by name and get its attributes.

如解析所说,获取代请求头Nodeid文件夹下该名字的文件的属性,包含着 inode id等。

请求头后的实体
type FuseLookupIn struct {
	Name string // 字符串结尾的`\0`会计算到长度中,解析时需注意
}
  • Name: 文件名
响应头的实体
type FuseEntryOut struct {
	NodeId         uint64 /* Inode ID */
	Generation     uint64 /* Inode generation: nodeid:gen must be unique for the fs's lifetime */
	EntryValid     uint64 /* Cache timeout for the name */
	AttrValid      uint64 /* Cache timeout for the attributes */
	EntryValidNsec uint32
	AttrValidNsec  uint32

	Attr FuseAttr
}

type FuseAttr struct {
	Ino       uint64
	Size      uint64
	Blocks    uint64
	Atime     uint64
	Mtime     uint64
	Ctime     uint64
	AtimeNsec uint32
	MtimeNsec uint32
	CtimeNsec uint32
	Mode      uint32
	Nlink     uint32
	Uid       uint32
	Gid       uint32
	Rdev      uint32
	Blksize   uint32
	Padding   uint32
}
  • NodeId: 文件的Inode ID
  • Generation: 同一个文件, nodeid和gen的组合,必须在整个文件系统的生命周期中唯一
  • EntryValid: 对于文件的Name的缓存时间,单位是秒
  • EntryValidNsec: 同上,但是该属性表示毫秒部分
  • AttrValid: 对于文件的属性的缓存时间,单位是秒
  • AttrValidNsec: 同上,但是该属性表示毫秒部分
  • Attr: 该文件的属性,可以对应属性的意义可以参考文件属性stat
FUSE_FORGET = 2

forget()函数,Forget about an inode

不需要返回任何响应的操作。

请求头后的实体
// forget (should not send any reply)
type FuseForgetIn struct {
	Nlookup uint64
}
FUSE_GETATTR = 3

getattr()函数,Get file attributes.

请求头后的实体
type FuseGetattrIn struct {
	GetattrFlags uint32
	Dummy        uint32
	Fh           uint64
}
响应头的实体
type FuseAttrOut struct {
	AttrValid     uint64 /* Cache timeout for the attributes */
	AttrValidNsec uint32
	Dummp         uint32

	Attr FuseAttr
}

type FuseAttr struct {
	Ino       uint64
	Size      uint64
	Blocks    uint64
	Atime     uint64
	Mtime     uint64
	Ctime     uint64
	AtimeNsec uint32
	MtimeNsec uint32
	CtimeNsec uint32
	Mode      uint32
	Nlink     uint32
	Uid       uint32
	Gid       uint32
	Rdev      uint32
	Blksize   uint32
	Paddi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值