当前位置:   article > 正文

FUSE协议解析

fuse读取异常

由于以前有项目是用到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的结构体如下:

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

响应头

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

  1. type FuseOutHeader struct {
  2. Len uint32
  3. Error int32
  4. Unique uint64
  5. }
  • 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等。

请求头后的实体
  1. type FuseLookupIn struct {
  2. Name string // 字符串结尾的`\0`会计算到长度中,解析时需注意
  3. }
  • Name: 文件名
响应头的实体
  1. type FuseEntryOut struct {
  2. NodeId uint64 /* Inode ID */
  3. Generation uint64 /* Inode generation: nodeid:gen must be unique for the fs's lifetime */
  4. EntryValid uint64 /* Cache timeout for the name */
  5. AttrValid uint64 /* Cache timeout for the attributes */
  6. EntryValidNsec uint32
  7. AttrValidNsec uint32
  8. Attr FuseAttr
  9. }
  10. type FuseAttr struct {
  11. Ino uint64
  12. Size uint64
  13. Blocks uint64
  14. Atime uint64
  15. Mtime uint64
  16. Ctime uint64
  17. AtimeNsec uint32
  18. MtimeNsec uint32
  19. CtimeNsec uint32
  20. Mode uint32
  21. Nlink uint32
  22. Uid uint32
  23. Gid uint32
  24. Rdev uint32
  25. Blksize uint32
  26. Padding uint32
  27. }
  • NodeId: 文件的Inode ID
  • Generation: 同一个文件, nodeid和gen的组合,必须在整个文件系统的生命周期中唯一
  • EntryValid: 对于文件的Name的缓存时间,单位是秒
  • EntryValidNsec: 同上,但是该属性表示毫秒部分
  • AttrValid: 对于文件的属性的缓存时间,单位是秒
  • AttrValidNsec: 同上,但是该属性表示毫秒部分
  • Attr: 该文件的属性,可以对应属性的意义可以参考文件属性stat
FUSE_FORGET = 2

forget()函数,Forget about an inode

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

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

getattr()函数,Get file attributes.

请求头后的实体
  1. type FuseGetattrIn struct {
  2. GetattrFlags uint32
  3. Dummy uint32
  4. Fh uint64
  5. }
响应头的实体
  1. type FuseAttrOut struct {
  2. AttrValid uint64 /* Cache timeout for the attributes */
  3. AttrValidNsec uint32
  4. Dummp uint32
  5. Attr FuseAttr
  6. }
  7. type FuseAttr struct {
  8. Ino uint64
  9. Size uint64
  10. Blocks uint64
  11. Atime uint64
  12. Mtime uint64
  13. Ctime uint64
  14. AtimeNsec uint32
  15. MtimeNsec uint32
  16. CtimeNsec uint32
  17. Mode uint32
  18. Nlink uint32
  19. Uid uint32
  20. Gid uint32
  21. Rdev uint32
  22. Blksize uint32
  23. Padding uint32
  24. }
    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/363383
    推荐阅读
    相关标签
      

    闽ICP备14008679号