您没有指定编程语言,尽管 FUSE 是本机 C++,但有本机 Golang 绑定,在bazil.org/fuse实现。
我想说答案的主要部分需要包括以下内容:
- 处理内存中文件系统树的数据结构
- 节点描述及其与 iNode 的关系
- 捕获 FUSE 服务器请求以处理 cli 命令的挂钩
- 使用 FUSE 服务器挂载文件夹的描述。
我最近使用这个适配器编写了一个内存文件系统:github.com/bbengfort/memfs。我关于它的性能的文章在这里:In-Memory File System with FUSE。很快,我做出了一些选择:
内存中的数据结构包含 2 个主要结构,dir 和 file,它们都是节点:
type Node struct {
ID uint64
Name string
Attrs fuse.Attr
Parent *Dir
}
type Dir struct {
Node
Children map[string]Node
}
type File struct {
Node
Data []byte
}
Children
如您所见,这是一个简单的树,可以通过和Parent
链接上下遍历。File的Data
属性保存了文件的所有内容。因此,文件系统只需要创建一个"\"
在挂载点调用的“根”目录,然后将 on mkdir
aDir
添加到其子级,然后添加 on cp
a File
。在 Go 中,这很简单:
type FS struct {
root *Dir
}
func Mount(path string) error {
// Unmount the FS in case it was mounted with errors.
fuse.Unmount(path)
// Mount the FS with the specified options
conn, err := fuse.Mount(path)
if err != nil {
return err
}
// Ensure that the file system is shutdown
defer conn.Close()
// Create the root dir and file system
memfs := FS{
root: &Dir{
ID: 1,
Name: "\",
Parent: nil,
},
}
// Serve the file system
if err := fs.Serve(conn, memfs); err != nil {
return err
}
}
现在您需要挂钩来实现各种 FUSE 请求和调用。这是一个示例mkdir
:
func (d *Dir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
// Update the directory Atime
d.Attrs.Atime = time.Now()
// Create the child directory
c := new(Dir)
c.Init(req.Name, req.Mode, d)
// Set the directory's UID and GID to that of the caller
c.Attrs.Uid = req.Header.Uid
c.Attrs.Gid = req.Header.Gid
// Add the directory to the directory
d.Children[c.Name] = c
// Update the directory Mtime
d.Attrs.Mtime = time.Now()
return c, nil
}
最后,通过讨论如何编译和运行服务器、安装到路径以及 FUSE 如何拦截内核调用并将它们传递给用户空间中的进程来结束面试问题。