練習のために、LinuxのFUSE (Filesytem in
userspace)でメールアドレスの形式にマッチしたファイル名しか作れないファイルシステムを作っています。

しかし、そのファイルシステムをマウントして、touchコマンドで`hello@example.com`のようなファイルを作るとInput/Outputエラーが発生します。

どうやら`mknod`システムコールのコールバックが呼ばれると発生しているように見えます。  
なぜこのエラーが発生するのでしょうか?

Linux 5.12.15  
libfuse 3.10.4  
を使用しています

* * *

## Update:

ll_setattrを実装していますがうまく行きません。Input/Output
Errorが表示されています。fuseのデバッグモードのログではsuccessとなっていますがtouchを読んだ側にはInput/Output
Errorが返っているようです

なぜでしょうか?  
Code2:

```

    #define FUSE_USE_VERSION 310
    #include <fuse3/fuse_lowlevel.h>
    #include <unistd.h>
    
    #include <cerrno>
    #include <cstdio>
    #include <cstring>
    #include <mutex>
    #include <regex>
    #include <unordered_map>
    
    struct mutex_map {
        int counter = 2;
        std::mutex _mtx;
        std::unordered_map<int, std::string> _data;
        std::unordered_map<std::string, int> _rev_data;
    
       public:
        int set_value(const char* value) {
            std::string s = std::string(value);
            std::lock_guard<std::mutex> lock(_mtx);
            counter++;
            _data[counter] = s;
            _rev_data[s] = counter;
            return counter;
        }
    
        const char* get_value(int key) { return _data[key].c_str(); }
        int get_ino(const char* name) { return _rev_data[std::string(name)]; }
    };
    
    static mutex_map mm;
    
    static int sendmailfs_stat(fuse_ino_t ino, struct stat* stbuf,
                               size_t name_length) {
        uid_t uid = getuid();
        stbuf->st_ino = ino;
        if (ino == 1) {
            stbuf->st_mode = S_IFDIR | 0755;
            stbuf->st_nlink = 2;
            stbuf->st_uid = uid;
        } else {
            stbuf->st_mode = S_IFCHR | 0666;
            stbuf->st_nlink = 1;
            stbuf->st_size = name_length;
            stbuf->st_uid = uid;
        }
        return 0;
    }
    
    static void ll_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) {
        struct fuse_entry_param e;
        if (parent != 1) {
            fuse_reply_err(req, ENOENT);
            return;
        }
        if (mm._rev_data.find(std::string(name)) == mm._rev_data.end()) {
            fuse_reply_err(req, ENOENT);
            return;
        }
        puts("Found!");
        memset(&e, 0, sizeof(e));
        int new_ino = mm.set_value(name);
        e.ino = (uint64_t)new_ino;
        e.attr_timeout = 1.0;
        e.entry_timeout = 1.0;
        sendmailfs_stat(e.ino, &e.attr, strlen(name));
        puts("Helloworld2");
        printf("strlen:%zu: %s\n", strlen(name), name);
    
        fuse_reply_entry(req, &e);
    }
    
    static void ll_mknod(fuse_req_t req, fuse_ino_t parent, const char* name,
                         mode_t mode, dev_t rdev) {
        if (parent != 1) {
            fuse_reply_err(req, ENOENT);
            return;
        }
        int ret;
        std::regex re("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+");
        if (std::regex_match(name, re)) {
            puts("Matched");
            int inode_num = mm.set_value(name);
            struct fuse_entry_param e;
            e.ino = inode_num;
            e.attr_timeout = 1.0;
            e.entry_timeout = 1.0;
            sendmailfs_stat(e.ino, &e.attr, strlen(name));
    
            ret = fuse_reply_entry(req, &e);
        } else {
            puts("Unmatched");
            ret = fuse_reply_err(req, EINVAL);
        }
        printf("%d\n", ret);
    }
    
    static void ll_write(fuse_req_t req, fuse_ino_t ino, const char* buf,
                         size_t size, off_t off, struct fuse_file_info* fi) {
        if (mm.get_value(ino) == NULL) {
            fuse_reply_err(req, EINVAL);
            return;
        }
        fuse_reply_write(req, size);
    }
    
    static void ll_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
        if (ino == 1 || !mm.get_value(ino)) {
            puts("invalid access due to hello");
            fuse_reply_err(req, ENOENT);
            return;
        } else if ((fi->flags && O_WRONLY)) {
            fuse_reply_err(req, EINVAL);
            return;
        }
        fuse_reply_open(req, fi);
    }
    
    static void ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr,
                           int to_set, struct fuse_file_info* fi) {
        printf("to_set flag: %d\n", to_set);
        puts("setattr");
        int err = fuse_reply_attr(req, attr, 10.0);
        // if (err < 0) {
        //    puts("Error");
        //    fuse_reply_err(req, EINVAL);
        //    return;
        //}
    }
    static void ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char* name,
                            const char* value, size_t size, int flags) {
        puts("setxattr");
        fuse_reply_err(req, EINVAL);
    }
    static void ll_getattr(fuse_req_t req, fuse_ino_t ino,
                           struct fuse_file_info* fi) {
        puts("getattr");
        fuse_reply_err(req, EINVAL);
    }
    static void ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char* name,
                            size_t size) {
        puts("getxattr");
        fuse_reply_err(req, EINVAL);
    }
    
    static const struct fuse_lowlevel_ops opener = {
        .lookup = ll_lookup,
        .setattr = ll_setattr,
        .mknod = ll_mknod,
        .open = ll_open,
        .write = ll_write,
    
    };
    
    int main(int argc, char* argv[]) {
        struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
        struct fuse_session* se;
        struct fuse_cmdline_opts opts;
        struct fuse_loop_config config;
    
        int ret = -1;
    
        if (fuse_parse_cmdline(&args, &opts) != 0) {
            return 1;
        }
        if (opts.show_help) {
            printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
            fuse_cmdline_help();
            fuse_lowlevel_help();
            ret = 0;
            goto err_out1;
        } else if (opts.show_version) {
            printf("FUSE library version %d\n", fuse_version());
            fuse_lowlevel_version();
            ret = 0;
            goto err_out1;
        }
        if (opts.mountpoint == NULL) {
            printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
            printf("        %s --help\n", argv[0]);
            ret = 1;
            goto err_out1;
        }
        se = fuse_session_new(&args, &opener, sizeof(opener), NULL);
        if (fuse_set_signal_handlers(se) != 0) {
            goto err_out2;
        }
        if (fuse_session_mount(se, opts.mountpoint) != 0) {
            goto err_out3;
        }
        fuse_daemonize(opts.foreground);
    
        if (opts.singlethread) {
            ret = fuse_session_loop(se);
        } else {
            config.clone_fd = opts.clone_fd;
            config.max_idle_threads = opts.max_idle_threads;
            ret = fuse_session_loop_mt(se, &config);
        }
        fuse_session_unmount(se);
    err_out3:
        fuse_remove_signal_handlers(se);
    err_out2:
        fuse_session_destroy(se);
    err_out1:
        free(opts.mountpoint);
        fuse_opt_free_args(&args);
        return ret ? 1 : 0;
    }
    
    
    
```

* * *
```

    #define FUSE_USE_VERSION 310
    #include <fuse3/fuse_lowlevel.h>
    #include <unistd.h>
    
    #include <cerrno>
    #include <cstdio>
    #include <cstring>
    #include <mutex>
    #include <regex>
    #include <unordered_map>
    
    struct mutex_map {
        int counter = 2;
        std::mutex _mtx;
        std::unordered_map<int, const char*> _data;
        std::unordered_map<const char*, int> _rev_data;
    
       public:
        int set_value(const char* value) {
            std::lock_guard<std::mutex> lock(_mtx);
            counter++;
            _data[counter] = value;
            return counter;
        }
    
        const char* get_value(int key) { return _data[key]; }
        int get_ino(const char* name) { return _rev_data[name]; }
    };
    
    static mutex_map mm;
    
    static int sendmailfs_stat(fuse_ino_t ino, struct stat* stbuf,
                               size_t name_length) {
        uid_t uid = getuid();
        stbuf->st_ino = ino;
        if (ino == 1) {
            stbuf->st_mode = S_IFDIR | 0755;
            stbuf->st_nlink = 2;
            stbuf->st_uid = uid;
        } else {
            stbuf->st_mode = S_IFCHR | 0666;
            stbuf->st_nlink = 1;
            stbuf->st_size = name_length;
            stbuf->st_uid = uid;
        }
        return 0;
    }
    
    static void ll_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) {
        struct fuse_entry_param e;
        if (parent != 1) {
            fuse_reply_err(req, ENOENT);
            return;
        }
        if (mm._rev_data.find(name) == mm._rev_data.end()) {
            fuse_reply_err(req, ENOENT);
            return;
        }
        memset(&e, 0, sizeof(e));
        int new_ino = mm.set_value(name);
        e.ino = (uint64_t)new_ino;
        e.attr_timeout = 1.0;
        e.entry_timeout = 1.0;
        sendmailfs_stat(e.ino, &e.attr, strlen(name));
        puts("Helloworld2");
        printf("strlen:%zu: %s\n", strlen(name), name);
    
        fuse_reply_entry(req, &e);
    }
    
    static void ll_mknod(fuse_req_t req, fuse_ino_t parent, const char* name,
                         mode_t mode, dev_t rdev) {
        if (parent != 1) {
            fuse_reply_err(req, ENOENT);
            return;
        }
        int ret;
        std::regex re("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+");
        if (std::regex_match(name, re)) {
            puts("Matched");
            int inode_num = mm.set_value(name);
            struct fuse_entry_param e;
            e.ino = inode_num;
            e.attr_timeout = 1.0;
            e.entry_timeout = 1.0;
            sendmailfs_stat(e.ino, &e.attr, strlen(name));
    
            ret = fuse_reply_entry(req, &e);
        } else {
            puts("Unmatched");
            ret = fuse_reply_err(req, EINVAL);
        }
        printf("%d\n", ret);
    }
    
    static void ll_write(fuse_req_t req, fuse_ino_t ino, const char* buf,
                         size_t size, off_t off, struct fuse_file_info* fi) {
        if (mm.get_value(ino) == NULL) {
            fuse_reply_err(req, EINVAL);
            return;
        }
        fuse_reply_write(req, size);
    }
    
    static void ll_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) {
        if (ino == 1 || !mm.get_value(ino)) {
            puts("invalid access due to hello");
            fuse_reply_err(req, ENOENT);
            return;
        } else if ((fi->flags && O_WRONLY)) {
            fuse_reply_err(req, EINVAL);
            return;
        }
        fuse_reply_open(req, fi);
    }
    
    static void ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr,
                           int to_set, struct fuse_file_info* fi) {
        puts("setattr");
        fuse_reply_err(req, EINVAL);
    }
    static void ll_setxattr(fuse_req_t req, fuse_ino_t ino, const char* name,
                            const char* value, size_t size, int flags) {
        puts("setxattr");
        fuse_reply_err(req, EINVAL);
    }
    static void ll_getattr(fuse_req_t req, fuse_ino_t ino,
                           struct fuse_file_info* fi) {
        puts("getattr");
        fuse_reply_err(req, EINVAL);
    }
    static void ll_getxattr(fuse_req_t req, fuse_ino_t ino, const char* name,
                            size_t size) {
        puts("getxattr");
        fuse_reply_err(req, EINVAL);
    }
    
    static const struct fuse_lowlevel_ops opener = {
        .lookup = ll_lookup,
        .mknod = ll_mknod,
        .open = ll_open,
        .write = ll_write,
    
    };
    
    int main(int argc, char* argv[]) {
        struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
        struct fuse_session* se;
        struct fuse_cmdline_opts opts;
        struct fuse_loop_config config;
    
        int ret = -1;
    
        if (fuse_parse_cmdline(&args, &opts) != 0) {
            return 1;
        }
        if (opts.show_help) {
            printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
            fuse_cmdline_help();
            fuse_lowlevel_help();
            ret = 0;
            goto err_out1;
        } else if (opts.show_version) {
            printf("FUSE library version %d\n", fuse_version());
            fuse_lowlevel_version();
            ret = 0;
            goto err_out1;
        }
        if (opts.mountpoint == NULL) {
            printf("usage: %s [options] <mountpoint>\n\n", argv[0]);
            printf("        %s --help\n", argv[0]);
            ret = 1;
            goto err_out1;
        }
        se = fuse_session_new(&args, &opener, sizeof(opener), NULL);
        if (fuse_set_signal_handlers(se) != 0) {
            goto err_out2;
        }
        if (fuse_session_mount(se, opts.mountpoint) != 0) {
            goto err_out3;
        }
        fuse_daemonize(opts.foreground);
    
        if (opts.singlethread) {
            ret = fuse_session_loop(se);
        } else {
            config.clone_fd = opts.clone_fd;
            config.max_idle_threads = opts.max_idle_threads;
            ret = fuse_session_loop_mt(se, &config);
        }
        fuse_session_unmount(se);
    err_out3:
        fuse_remove_signal_handlers(se);
    err_out2:
        fuse_session_destroy(se);
    err_out1:
        free(opts.mountpoint);
        fuse_opt_free_args(&args);
        return ret ? 1 : 0;
    }
    
```
CC BY-SA 4.0
2021-07-11T03:32:43.603
78106
2021-07-23T21:51:16.213
2021-07-23T19:08:09.110
5246
5246
question
c++ linux filesystems
FUSE上でmknodしようとするとInput/Output Errorが起こる