yfs-源码剖析(2)--基本文件服务1

文章目录
  1. 1. Intro
  2. 2. extent_protocol
  3. 3. extent_client
  4. 4. extent_server
  5. 5. yfs_client
  6. 6. fuse
    1. 6.0.1. Attr
    2. 6.0.2. read
    3. 6.0.3. write
    4. 6.0.4. create file / dir
    5. 6.0.5. lookup
    6. 6.0.6. readdir
  • 7. 参考资料
  • Intro

    extent_server 用于存储文件系统所有的数据,有多个客户端与 extent_server 进行通信。

    为了实现 extent_server 就需要实现 extent_protocol, extent_client 和 extent_server。 通信流程和之前的锁服务类似。

    extent_protocol

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    // extent wire protocol

    #ifndef extent_protocol_h
    #define extent_protocol_h

    #include "rpc.h"

    class extent_protocol {
    public:
    typedef int status;
    typedef unsigned long long extentid_t;
    enum xxstatus { OK, RPCERR, NOENT, IOERR };
    enum rpc_numbers {
    put = 0x6001,
    get,
    getattr,
    remove
    };

    struct attr {
    unsigned int atime;
    unsigned int mtime;
    unsigned int ctime;
    unsigned int size;
    };
    };

    inline unmarshall &
    operator>>(unmarshall &u, extent_protocol::attr &a)
    {
    u >> a.atime;
    u >> a.mtime;
    u >> a.ctime;
    u >> a.size;
    return u;
    }

    inline marshall &
    operator<<(marshall &m, extent_protocol::attr a)
    {
    m << a.atime;
    m << a.mtime;
    m << a.ctime;
    m << a.size;
    return m;
    }

    #endif

    extent_client

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // extent client interface.

    #ifndef extent_client_h
    #define extent_client_h

    #include <string>
    #include "extent_protocol.h"
    #include "rpc.h"

    class extent_client {
    private:
    rpcc *cl;

    public:
    extent_client(std::string dst);

    extent_protocol::status get(extent_protocol::extentid_t eid,
    std::string &buf);
    extent_protocol::status getattr(extent_protocol::extentid_t eid,
    extent_protocol::attr &a);
    extent_protocol::status put(extent_protocol::extentid_t eid, std::string buf);
    extent_protocol::status remove(extent_protocol::extentid_t eid);
    };

    #endif
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    // RPC stubs for clients to talk to extent_server

    #include "extent_client.h"
    #include <sstream>
    #include <iostream>
    #include <stdio.h>
    #include <unistd.h>
    #include <time.h>

    // The calls assume that the caller holds a lock on the extent

    extent_client::extent_client(std::string dst)
    {
    sockaddr_in dstsock;
    make_sockaddr(dst.c_str(), &dstsock);
    cl = new rpcc(dstsock);
    if (cl->bind() != 0) {
    printf("extent_client: bind failed\n");
    }
    }

    extent_protocol::status
    extent_client::get(extent_protocol::extentid_t eid, std::string &buf)
    {
    extent_protocol::status ret = extent_protocol::OK;
    ret = cl->call(extent_protocol::get, eid, buf);
    return ret;
    }

    extent_protocol::status
    extent_client::getattr(extent_protocol::extentid_t eid,
    extent_protocol::attr &attr)
    {
    extent_protocol::status ret = extent_protocol::OK;
    ret = cl->call(extent_protocol::getattr, eid, attr);
    return ret;
    }

    extent_protocol::status
    extent_client::put(extent_protocol::extentid_t eid, std::string buf)
    {
    extent_protocol::status ret = extent_protocol::OK;
    int r;
    ret = cl->call(extent_protocol::put, eid, buf, r);
    return ret;
    }

    extent_protocol::status
    extent_client::remove(extent_protocol::extentid_t eid)
    {
    extent_protocol::status ret = extent_protocol::OK;
    int r;
    ret = cl->call(extent_protocol::remove, eid, r);
    return ret;
    }

    // 都是套路代码 为了 rpc。。。

    重点在于下面的 extent_server

    extent_server

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #ifndef extent_server_h
    #define extent_server_h

    #include <string>
    #include <map>
    #include <mutex>
    #include "extent_protocol.h"

    struct extent {
    // 数据
    std::string data;
    // 数据属性
    extent_protocol::attr attr;
    };

    class extent_server {

    public:
    extent_server();

    int put(extent_protocol::extentid_t id, std::string, int &);
    int get(extent_protocol::extentid_t id, std::string &);
    int getattr(extent_protocol::extentid_t id, extent_protocol::attr &);
    int remove(extent_protocol::extentid_t id, int &); // 对应 client 的 stub

    private:
    std::mutex m_mutex;
    std::map<extent_protocol::extentid_t, extent> m_dataMap; // 暂时类似 kv-store
    };

    #endif
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    // the extent server implementation

    #include "extent_server.h"
    #include <sstream>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>

    extent_server::extent_server()
    {
    int ret;
    put(1, "", ret);
    }


    int extent_server::put(extent_protocol::extentid_t id, std::string buf, int &)
    {
    // You fill this in for Lab 2.
    std::lock_guard<std::mutex> lg(m_mutex);

    extent_protocol::attr attr;
    attr.atime = attr.mtime = attr.ctime = time(NULL);

    if(m_dataMap.find(id) != m_dataMap.end())
    {
    attr.atime = m_dataMap[id].attr.atime;
    }
    attr.size = buf.size();
    m_dataMap[id].data = buf;
    m_dataMap[id].attr = attr;

    return extent_protocol::OK;
    }

    int extent_server::get(extent_protocol::extentid_t id, std::string &buf)
    {
    // You fill this in for Lab 2.
    std::lock_guard<std::mutex> lg(m_mutex);

    if(m_dataMap.find(id) != m_dataMap.end())
    {
    m_dataMap[id].attr.atime = time(NULL);
    buf = m_dataMap[id].data;
    return extent_protocol::OK;
    }

    return extent_protocol::NOENT;
    }

    int extent_server::getattr(extent_protocol::extentid_t id, extent_protocol::attr &a)
    {
    // You fill this in for Lab 2.
    // You replace this with a real implementation. We send a phony response
    // for now because it's difficult to get FUSE to do anything (including
    // unmount) if getattr fails.
    std::lock_guard<std::mutex> lg(m_mutex);

    if(m_dataMap.find(id) != m_dataMap.end())
    {
    a = m_dataMap[id].attr;
    return extent_protocol::OK;
    }

    return extent_protocol::NOENT;
    }

    int extent_server::remove(extent_protocol::extentid_t id, int &)
    {
    // You fill this in for Lab 2.
    std::lock_guard<std::mutex> lg(m_mutex);

    auto it = m_dataMap.find(id);
    if(it != m_dataMap.end())
    {
    m_dataMap.erase(it);
    return extent_protocol::OK;
    }

    return extent_protocol::NOENT;
    }

    // 一些需要线程安全的 map 操作, 用 lock_guard 锁住全局

    yfs_client

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    #ifndef yfs_client_h
    #define yfs_client_h

    #include <string>
    //#include "yfs_protocol.h"
    #include "extent_client.h"
    #include <vector>


    // 每个文件和目录都有一个唯一的 inum
    // FUSE 假定根目录的 inum 是 0x00000001。因此,需要确保在 yfs_client 启动时,它已准备好导出存储在该inum 下的空目录。
    // 在创建一个新的文件或者目录的时候,必须要给它配置一个唯一的 inum, 这个 inum 可以随机生成(但是如果文件量变大,会产生 collision
    // YFS needs to be able to tell whether a particular inum refers to a file or a directory. Do this by allocating IDs with the 31st bit of one for new files, and IDs with the 31st bit of zero for new directories. The method yfs_client::isfile assumes this property holds for inum.

    class yfs_client {
    extent_client *ec;
    public:

    typedef unsigned long long inum;
    enum xxstatus { OK, RPCERR, NOENT, IOERR, EXIST };
    typedef int status;

    struct fileinfo {
    unsigned long long size;
    unsigned long atime; // 访问时间
    unsigned long mtime; // 修改时间
    unsigned long ctime; // 变化时间
    };
    struct dirinfo {
    unsigned long atime;
    unsigned long mtime;
    unsigned long ctime;
    };
    struct dirent {
    std::string name;
    yfs_client::inum inum;
    };

    private:
    static std::string filename(inum);
    static inum n2i(std::string);
    public:

    yfs_client(std::string, std::string);

    bool isfile(inum);
    bool isdir(inum);

    int getfile(inum, fileinfo &);
    int getdir(inum, dirinfo &);

    yfs_client::inum random_inum(bool isfile);
    int create(inum, const char*, inum&);
    int lookup(inum, const char*, inum&, bool*);
    int readdir(inum, std::list<dirent>&);

    int setattr(inum, struct stat*);
    int read(inum, off_t, size_t, std::string&);
    int write(inum, off_t, size_t, const char*);

    };

    #endif
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    // 构造函数
    yfs_client::yfs_client(std::string extent_dst, std::string lock_dst)
    {
    ec = new extent_client(extent_dst);
    }

    // string 转成 64-bit 的 inum
    yfs_client::inum
    yfs_client::n2i(std::string n)
    {
    std::istringstream ist(n);
    unsigned long long finum;
    ist >> finum;
    return finum;
    }


    // 64-bit inum 转成 string 类型的文件名
    std::string
    yfs_client::filename(inum inum)
    {
    std::ostringstream ost;
    ost << inum;
    return ost.str();
    }


    // YFS needs to be able to tell whether a particular inum refers to a file or a directory. Do this by allocating IDs with the 31st bit of one for new files, and IDs with the 31st bit of zero for new directories.

    // 根据 31st bits 来判断是否为文件
    bool
    yfs_client::isfile(inum inum)
    {
    if(inum & 0x80000000)
    return true;
    return false;
    }

    bool
    yfs_client::isdir(inum inum)
    {
    return ! isfile(inum);
    }

    int
    yfs_client::getfile(inum inum, fileinfo &fin)
    {
    int r = OK;

    printf("getfile %016llx\n", inum);
    extent_protocol::attr a;
    if (ec->getattr(inum, a) != extent_protocol::OK) {
    r = IOERR;
    goto release;
    }

    fin.atime = a.atime;
    fin.mtime = a.mtime;
    fin.ctime = a.ctime;
    fin.size = a.size;
    printf("getfile %016llx -> sz %llu\n", inum, fin.size);

    release:

    return r;
    }

    int
    yfs_client::getdir(inum inum, dirinfo &din)
    {
    int r = OK;

    printf("getdir %016llx\n", inum);
    extent_protocol::attr a;
    if (ec->getattr(inum, a) != extent_protocol::OK) {
    r = IOERR;
    goto release;
    }
    din.atime = a.atime;
    din.mtime = a.mtime;
    din.ctime = a.ctime;

    release:
    return r;
    }


    // 随机生成 inum
    yfs_client::inum
    yfs_client::random_inum(bool isfile)
    {
    inum ret = (unsigned long long)((rand() & 0x7fffffff) | (isfile << 31));
    ret = 0xffffffff & ret;
    return ret;
    }


    int
    yfs_client::create(inum parent, const char* name, inum& inum)
    {
    std::string data;
    std::string file_name;
    if(ec->get(parent, data) != extent_protocol::OK)
    {
    return IOERR;
    }

    file_name = "/" + std::string(name) + "/";
    // 文件已经存在
    if (data.find(file_name) != std::string::npos)
    {
    return EXIST;
    }

    inum = random_inum(true);
    if(ec->put(inum, std::string()) != extent_protocol::OK)
    {
    return IOERR;
    }

    data.append(file_name + filename(inum) + "/");
    if(ec->put(parent, data) != extent_protocol::OK)
    {
    return IOERR;
    }

    return OK;
    }


    int
    yfs_client::lookup(inum parent, const char* name, inum& inum, bool* found)
    {
    size_t pos, end;
    std::string data, file_name, ino;

    if(ec->get(parent, data) != extent_protocol::OK)
    {
    return IOERR;
    }

    file_name = "/" + std::string(name) + "/";
    pos = data.find(file_name);
    if(pos != std::string::npos)
    {
    *found = true;
    pos += file_name.size();
    end = data.find_first_of("/", pos);
    if(end != std::string::npos)
    {
    ino = data.substr(pos, end - pos);
    inum = n2i(ino.c_str()); // 获得 inum
    }
    else
    {
    return IOERR;
    }
    }
    else
    {
    return IOERR;
    }
    return OK;
    }

    int
    yfs_client::readdir(inum inum, std::list<dirent> & dirents)
    {
    std::string data, inum_str;
    size_t pos, name_end, name_len, inum_end, inum_len;
    if(ec->get(inum, data) != extent_protocol::OK)
    {
    return IOERR;
    }

    pos = 0;
    while(pos != data.size())
    {
    dirent d;
    pos = data.find("/", pos);
    if(pos == std::string::npos)
    {
    break;
    }

    name_end = data.find_first_of("/", pos+1);
    name_len = name_end - pos - 1;
    d.name = data.substr(pos+1, name_len);


    inum_end = data.find_first_of("/", name_end + 1);
    inum_len = inum_end - inum_end - 1;
    inum_str = data.substr(name_end+1, inum_len);

    d.inum = n2i(inum_str.c_str());
    dirents.push_back(d);
    pos = inum_end + 1;
    }

    return OK;
    }

    int
    yfs_client::setattr(inum inum, struct stat* attr)
    {
    size_t size = attr->st_size;
    std::string buf;
    if(ec->get(inum, buf) != extent_protocol::OK)
    {
    return IOERR;
    }

    buf.resize(size, '\0');

    if(ec->put(inum, buf) != extent_protocol::OK)
    {
    return IOERR;
    }

    return OK;
    }

    int
    yfs_client::read(inum inum, off_t off, size_t size, std::string &buf)
    {
    std::string data;
    size_t read_size;
    if(ec->get(inum, data) != extent_protocol::OK)
    {
    return IOERR;
    }

    if(off >= data.size()) // 起始 offset 超过范围 直接返回空
    {
    buf = std::string();
    }

    read_size = size;
    if(off + size > data.size())
    {
    read_size = data.size() - off;
    } // 超过范围 截断
    buf = data.substr(off, read_size);

    return OK;
    }

    int
    yfs_client::write(inum inum, off_t off, size_t size, const char *buf)
    {
    std::string data;
    if(ec->get(inum, data) != extent_protocol::OK)
    {
    return IOERR;
    }

    if(size + off > data.size()) // 扩容
    {
    data.resize(size + off, '\0');
    }

    for(size_t i = 0; i < size; i++) {
    data[off+i] = buf[i];
    }

    if(ec->put(inum, data) != extent_protocol::OK) {
    return IOERR;
    }

    return OK;
    }

    fuse

    fuse.cc

    receive request from fuse and call methods of yfs_client。

    When a program (such as ls or a test script) manipulates a file or directory (such as yfs1) served by your yfs_client, the FUSE code in the kernel sends corresponding operations to yfs_client via FUSE. The code we provide you in fuse.ccresponds to each such operation by calling one of a number of procedures, for create, read, write, etc. operations. You should modify the relevant routines in fuse.cc to call methods in yfs_client.cc. fuse.cc should just contain glue code, and the core of your file system logic should be in yfs_client.cc. For example, to handle file creation, you should modify fuseserver_createhelper to call yfs->create(...), and you should add a new create(...) method to yfs_client.cc. Look at getattr() in fuse.cc for an example of how a fuse operation handler works, how it calls methods in yfs_client, and how it sends results and errors back to the kernel. YFS uses FUSE’s “lowlevel” API.

    我把 fuse.cc 当作 yfs_client 的 wrapper。

    Attr

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    yfs_client::status
    getattr(yfs_client::inum inum, struct stat &st)
    {
    yfs_client::status ret;

    bzero(&st, sizeof(st));

    st.st_ino = inum;
    printf("getattr %016llx %d\n", inum, yfs->isfile(inum));
    if(yfs->isfile(inum)){ // file
    yfs_client::fileinfo info;
    ret = yfs->getfile(inum, info);
    if(ret != yfs_client::OK)
    return ret;
    st.st_mode = S_IFREG | 0666; // regular 普通文件 可读可写不可执行
    st.st_nlink = 1; // 链接计数
    st.st_atime = info.atime;
    st.st_mtime = info.mtime;
    st.st_ctime = info.ctime;
    st.st_size = info.size;
    printf(" getattr -> %llu\n", info.size);
    } else { // dir
    yfs_client::dirinfo info;
    ret = yfs->getdir(inum, info);
    if(ret != yfs_client::OK)
    return ret;
    st.st_mode = S_IFDIR | 0777;
    st.st_nlink = 2;
    st.st_atime = info.atime;
    st.st_mtime = info.mtime;
    st.st_ctime = info.ctime;
    printf(" getattr -> %lu %lu %lu\n", info.atime, info.mtime, info.ctime);
    }
    return yfs_client::OK;
    }
    1. struct stat
    2. {
    3. mode_t st_mode;/* file type & mode (permissions) */
    4. ino_t st_ino;/* i-node number (serial number) */
    5. dev_t st_dev;/* device number (file system) */
    6. dev_t st_rdev;/* device number for special files */
    7. nlink_t st_nlink;/* number of links */
    8. uid_t st_uid;/* user ID of owner */
    9. gid_t st_gid;/* group ID of owner */
    10. off_t st_size;/* size in bytes, for regular files */
    11. struct timespec st_atim;/* time of last access */
    12. struct timespec st_mtim;/* time of last modification */
    13. struct timespec st_ctim;/* time of last file status change */
    14. blksize_t st_blksize;/* best I/O block size */
    15. blkcnt_t st_blocks;/* number of disk blocks allocated */
    16. };

    一个操作对应着一个 handler,下面是 getattr 的 handler。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // The @ino argument indicates the file or directory FUSE wants
    // you to operate on. It's a 32-bit FUSE identifier; just assign
    // it to a yfs_client::inum to get a 64-bit YFS inum.
    void
    fuseserver_getattr(fuse_req_t req, fuse_ino_t ino,
    struct fuse_file_info *fi)
    {
    struct stat st;
    yfs_client::inum inum = ino; // req->in.h.nodeid;
    yfs_client::status ret;

    ret = getattr(inum, st);
    if(ret != yfs_client::OK){
    fuse_reply_err(req, ENOENT);
    return;
    }
    fuse_reply_attr(req, &st, 0);
    }

    如果发生错误,需调用 fuse_reply_err(req, errno). 成功的话就调用 fuse_reply_xx(req, …)

    相应地,可以 setAttr,具体实现如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    void
    fuseserver_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi)
    {
    printf("fuseserver_setattr 0x%x\n", to_set);
    if (FUSE_SET_ATTR_SIZE & to_set) {
    printf(" fuseserver_setattr set size to %zu\n", attr->st_size);
    struct stat st;
    #if 1
    // fill st using getattr before fuse_reply_attr
    yfs_client::inum inum = ino;
    yfs_client::status ret;

    ret = yfs->setattr(inum, attr);

    if(ret != yfs_client::OK)
    {
    fuse_reply_err(req, ENOENT);
    return;
    }

    ret = getattr(inum, st);
    if(ret != yfs_client::OK){
    fuse_reply_err(req, ENOENT);
    return;
    }
    fuse_reply_attr(req, &st, 0);
    #else
    fuse_reply_err(req, ENOSYS);
    #endif
    } else {
    fuse_reply_err(req, ENOSYS);
    }
    }

    read

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    void
    fuseserver_read(fuse_req_t req, fuse_ino_t ino, size_t size,
    off_t off, struct fuse_file_info *fi)
    {
    #if 1
    std::string buf;
    yfs_client::inum inum = ino;
    yfs_client::status ret;
    ret = yfs->read(inum, off, size, buf);
    if(ret != yfs_client::OK)
    {
    fuse_reply_err(req, ENOENT);
    return;
    }

    fuse_reply_buf(req, buf.data(), buf.size());
    #else
    fuse_reply_err(req, ENOSYS);
    #endif
    }

    write

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    void
    fuseserver_write(fuse_req_t req, fuse_ino_t ino,
    const char *buf, size_t size, off_t off,
    struct fuse_file_info *fi)
    {
    #if 1
    yfs_client::inum inum = ino;
    yfs_client::status ret;
    ret = yfs->write(inum, off, size, buf);
    if(ret != yfs_client::OK)
    {
    fuse_reply_err(req, ENOENT);
    return;
    }

    fuse_reply_write(req, size);
    #else
    fuse_reply_err(req, ENOSYS);
    #endif
    }

    create file / dir

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    // 在 parent 目录下创建名为 name 的文件

    // - @mode specifies the create mode of the file. Ignore it - you do not
    // have to implement file mode.
    // - If a file named @name already exists in @parent, return EXIST.
    // - Pick an ino (with type of yfs_client::inum) for file @name.
    // Make sure ino indicates a file, not a directory!
    // - Create an empty extent for ino.
    // - Add a <name, ino> entry into @parent.
    // - On success, store the inum of newly created file into @e->ino,
    // and the new file's attribute into @e->attr. Get the file's
    // attributes with getattr().
    //
    // @return yfs_client::OK on success, and EXIST if @name already exists.
    //
    yfs_client::status
    fuseserver_createhelper(fuse_ino_t parent, const char *name,
    mode_t mode, struct fuse_entry_param *e)
    {
    // In yfs, timeouts are always set to 0.0, and generations are always set to 0
    e->attr_timeout = 0.0;
    e->entry_timeout = 0.0;
    e->generation = 0;

    yfs_client::inum inum = 0;
    yfs_client::status ret;
    ret = yfs->create(parent, name, inum);
    if(ret == yfs_client::OK)
    {
    e->ino = inum;
    getattr(inum, e->attr);
    }

    return ret;
    }


    // wrap 了一下上面的 helper
    void
    fuseserver_create(fuse_req_t req, fuse_ino_t parent, const char *name,
    mode_t mode, struct fuse_file_info *fi)
    {
    struct fuse_entry_param e;
    yfs_client::status ret;
    if( (ret = fuseserver_createhelper( parent, name, mode, &e )) == yfs_client::OK ) {
    fuse_reply_create(req, &e, fi);
    } else {
    if (ret == yfs_client::EXIST) {
    fuse_reply_err(req, EEXIST);
    }else{
    fuse_reply_err(req, ENOENT);
    }
    }
    }

    // mknod 创建块设备或者字符设备文件

    void fuseserver_mknod( fuse_req_t req, fuse_ino_t parent,
    const char *name, mode_t mode, dev_t rdev ) {
    struct fuse_entry_param e;
    yfs_client::status ret;
    if( (ret = fuseserver_createhelper( parent, name, mode, &e )) == yfs_client::OK ) {
    fuse_reply_entry(req, &e);
    } else {
    if (ret == yfs_client::EXIST) {
    fuse_reply_err(req, EEXIST);
    }else{
    fuse_reply_err(req, ENOENT);
    }
    }
    }

    lookup

    在 parent 文件夹下寻找文件或者文件夹,如果能够找到名为 name 的文件或文件夹,更改 e.attr 和 e.ino.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    void
    fuseserver_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
    {
    struct fuse_entry_param e;
    // In yfs, timeouts are always set to 0.0, and generations are always set to 0
    e.attr_timeout = 0.0;
    e.entry_timeout = 0.0;
    e.generation = 0;
    bool found = false;

    yfs_client::status ret;
    yfs_client::inum inum;
    ret = yfs->lookup(parent, name, inum, &found);
    if(ret == yfs_client::OK)
    {
    e.ino = inum;
    getattr(e.ino, e.attr);
    }

    if (found)
    fuse_reply_entry(req, &e);
    else
    fuse_reply_err(req, ENOENT);
    }

    readdir

    获取一个目录下所有的目录项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    // Send the reply using reply_buf_limited.
    //
    // Call dirbuf_add(&b, name, inum) for each entry in the directory.
    //
    void
    fuseserver_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
    off_t off, struct fuse_file_info *fi)
    {
    yfs_client::inum inum = ino; // req->in.h.nodeid;
    struct dirbuf b;

    printf("fuseserver_readdir\n");

    if(!yfs->isdir(inum)){
    fuse_reply_err(req, ENOTDIR);
    return;
    }

    memset(&b, 0, sizeof(b));


    // You fill this in for Lab 2
    yfs_client::status ret;
    std::list<yfs_client::dirent> dirents;
    ret = yfs->readdir(inum, dirents);
    if(ret != yfs_client::OK)
    {
    fuse_reply_err(req, ENOENT);
    return;
    }

    for(auto it = dirents.begin(); it != dirents.end(); ++it)
    {
    dirbuf_add(&b, it->name.c_str(), it->inum);
    }


    reply_buf_limited(req, b.p, b.size, off, size);
    free(b.p);
    }

    其中 dirbuf 的定义如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    struct dirbuf {
    char *p;
    size_t size;
    };

    void dirbuf_add(struct dirbuf *b, const char *name, fuse_ino_t ino)
    {
    struct stat stbuf;
    size_t oldsize = b->size;
    b->size += fuse_dirent_size(strlen(name));
    b->p = (char *) realloc(b->p, b->size);
    memset(&stbuf, 0, sizeof(stbuf));
    stbuf.st_ino = ino;
    fuse_add_dirent(b->p + oldsize, name, &stbuf, b->size);
    }

    其中 reply_buf_limited 定义如下:

    1
    2
    3
    4
    5
    6
    7
    8
    int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize,
    off_t off, size_t maxsize)
    {
    if ((size_t)off < bufsize)
    return fuse_reply_buf(req, buf + off, min(bufsize - off, maxsize));
    else
    return fuse_reply_buf(req, NULL, 0);
    }

    参考资料