从0到1写一个自己的文件系统-写一个简单的文件系统(2)

1.前言

首先,我们需要了解fuse的启动。
这里用官方源码/example/hello做示例。
在编译完成之后我们会得到hello.o的可执行文件,
使用./hello -h可以看到一大堆选项

usage: ./hello[options] <mountpoint>

FUSE options:
    -h   --help            print help
    -V   --version         print version
    -d   -o debug          enable debug output (implies -f)
    -f                     foreground operation
    -s                     disable multi-threaded operation
    -o clone_fd            use separate fuse device fd for 
.....

其中需要注意的是:

  • option是可选选项,较为常用的有:
    -d debug 模式,输出debug信息用于调试
    -f 前台运行,-f -d一起用的话就可以随时看见debug信息输出
  • mountpoint 挂载点,将你的文件系统挂载到哪一个目录下面。

再来看看上一节写的 fuse_main 函数

fuse_main(argc, argv, Operations(), nullptr);

其中argc, argv就对应传入的option 和 mountpoint。

需要理解的另一个点,假设我们的文件系统挂载点是在/tmp/user,那么我们实际操作的地方是在哪里呢?这个地方千万不能再是挂载点。因为我们在挂载点进行各种操作的时候,fuse会将各种操作映射到我们的处理函数,如果我们的处理函数中的操作位置还是在挂载点,那么又会继续调用我们的处理函数,从而变成死循环了。

image.png

2.编写函数

所以,我们实际操作的位置不能是挂载点。
我们先定义一个映射的位置static const string root_path = "/home/ubuntu/hello";那么挂载点中所有的操作都会映射到这个目录。
还学要解决一个问题就是我们的函数中传入的目录实际上都是相对于挂载点的路径:
比如:/挂载点/b/a.txt
函数实际接收到的路径是/b/a.txt
但是我们实际的操作目录是在/home/ubuntu/hello,因此需要对目录做一个转化。将/挂载点/b/a.txt转换为/home/ubuntu/hello/b/a.txt,这一步很简单,定义一个fullpath函数如下:

static const string root_path = "/home/ubuntu/hello";
string fullpath(const char *path) {
    string fpath = root_path + path;
    return fpath;
}

1)编写bcsfs_getattr

这个函数主要是用来获取各种属性信息,简单记get attributes。

这一步就是要去调用实际的linux调用,将实际的信息传递给fuse文件系统。流程如下:

  • /挂载点执行 ls
  • fuse调起我们自定义的bcsfs_getattr函数,传入的path是相对于挂载点,也就是/
  • 调用lstat函数获取真正的目录(/home/ubuntu/hello)信息,返回给fuse,实际上也就想当于在/home/ubuntu/hello下执行了ls命令
int BCSFS::bcsfs_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) {
    int res = 0;
    memset(stbuf, 0, sizeof(struct stat));//初始化stbuf
    puts("#getattr call");
    puts(path);
    string fpath = fullpath(path); //获取真实路径
    res = lstat(fpath.c_str(), stbuf);//填充结构体
    if(res<0) res=-errno;
    return res;
}

参数path为相对路径,stbuf是stat结构体,fi一般是NULL(fuse.h有说明)

2)编写bcsfs_opendir

同理 opendir 就是要去对应的 dir 调用 opendir 返回信息。opendir函数返回的打开的文件夹句柄,需要填充到fi->fh字段。(fh可以去fuse_common.h查看,如下:

    /** File handle id.  May be filled in by filesystem in create,
     * open, and opendir().  Available in most other file operations on the
     * same file handle. */
    uint64_t fh;
int BCSFS::bcsfs_opendir(const char *path, struct fuse_file_info *fi) {
    puts("#opendir call");
    DIR *dir;
    string fpath = fullpath(path);
    dir = opendir(fpath.c_str());
    if (dir == nullptr) {
        return -errno;
    }
    fi->fh = reinterpret_cast<uint64_t>(dir);

    return 0;
}

3)编写bcsfs_readdir

readdir 的写法也是类似,不过需要注意的是,目录中可能含有多个条目,因此我们需要将所有的条目全部返回给fuse。
关于目录可能用到的结构体可以参考:https://www.cnblogs.com/jikexianfeng/p/7084911.html

int BCSFS::bcsfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags) {
    puts("#bcsfs_readdir call");
    DIR *dir;
    struct dirent *de;
    dir = reinterpret_cast<DIR *>(fi->fh);

    while ((de = readdir(dir))!= nullptr){
        int ret = filler(buf,de->d_name, nullptr,0,FUSE_FILL_DIR_PLUS);
        if(ret!=0){
            return -errno;
        }
    }

    return 0;
}

目录文件(directory file)的概念:这种文件包含了其他文件的名字以及指向与这些文件有关的信息的指针。
dirent 结构体:

struct dirent
{
  long d_ino; /* inode number 索引节点号 */
    off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
    unsigned short d_reclen; /* length of this d_name 文件名长 */
    unsigned char d_type; /* the type of d_name 文件类型 */
    char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}

filler函数的官方解释如下:

/** Function to add an entry in a readdir() operation
 *
 * The *off* parameter can be any non-zero value that enables the
 * filesystem to identify the current point in the directory
 * stream. It does not need to be the actual physical position. A
 * value of zero is reserved to indicate that seeking in directories
 * is not supported.
 * 
 * @param buf the buffer passed to the readdir() operation
 * @param name the file name of the directory entry
 * @param stat file attributes, can be NULL
 * @param off offset of the next entry or zero
 * @param flags fill flags
 * @return 1 if buffer is full, zero otherwise
 */

是用来在readdir操作中添加entry(条目)的。

4)编写bcsfs_releasedir

顾名思义:释放目录,也就是释放资源。只需要调用closedir关闭dir即可。

int BCSFS::bcsfs_releasedir(const char * path, struct fuse_file_info * fi) {
    puts("#bcsfs_releasedir call");
    string fpath = fullpath(path);
    int ret = closedir(reinterpret_cast<DIR *>(fi->fh));
    if(ret<0){
        return -errno;
    }
    return ret;
}

3.测试

我们添加一个main函数用于测试:

#include "BCSFS.h"

int main(int argc, char *argv[]) {
    BCSFS fs;
    fs.run(argc,argv);
    return 0;
}

添加makefile文件:

PROG=BCSFS
OBJDIR=.obj
CC=g++

CFLAGS = -Wall --std=c++14 `pkg-config fuse3 --cflags` -I..
LDFLAGS = `pkg-config fuse3 --libs`

$(shell mkdir -p $(OBJDIR))

OBJS = $(OBJDIR)/BCSFS.o $(OBJDIR)/main.o

$(PROG) : $(OBJS)
    $(CC) $(OBJS) $(LDFLAGS) -o $(PROG)

-include $(OBJS:.o=.d)

$(OBJDIR)/%.o: %.cpp
    $(CC) -c $(CFLAGS) $*.cpp -o $(OBJDIR)/$*.o
    $(CC) -MM $(CFLAGS) $*.cpp > $(OBJDIR)/$*.d
    @mv -f $(OBJDIR)/$*.d $(OBJDIR)/$*.d.tmp
    @sed -e 's|.*:|$(OBJDIR)/$*.o:|' < $(OBJDIR)/$*.d.tmp > $(OBJDIR)/$*.d
    @sed -e 's/.*://' -e 's/\\$$//' < $(OBJDIR)/$*.d.tmp | fmt -1 | \
      sed -e 's/^ *//' -e 's/$$/:/' >> $(OBJDIR)/$*.d
    @rm -f $(OBJDIR)/$*.d.tmp

clean:
    rm -rf $(PROG) $(OBJDIR)


执行编译:

make

运行:

./BCSFS -d -f /挂载点

为了测试我们先在/home/ubuntu/hello目录下创建一个文件touch /home/ubuntu/hello/file1
运行文件系统之后我们在挂载点执行ls

ls -l /挂载点

可以看到结果输出file1。

ubuntu@Javascript:~$ ls -l mountdir/
total 0
-rw-r--r-- 1 ubuntu ubuntu 0 Dec  7 15:51 file1
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容