Linuxのfuse(Filesytem in Userspace)を使ってメールアドレスでしかファイルを作れないファイルシステムを作ろうとしています。
ls .
で作ったファイルの一覧を表示したいのですが、これを行うとInput/Output Errorが発生してしまいます。
なぜでしょうか?
下記コード内において、readdir system callに対するコールバックはll_readir
でこのあたりの実装に問題があるかもしれないなと思っています。
FUSE内でstrace ls .
をしてみたところ
getdents64(3, 0x5611ed000540, 32768) = -1 EIO (Input/output error)
となっていてこのあたりが原因ではないかなと考えています
FUSEのコード:
#define FUSE_USE_VERSION 310 #include <dirent.h> #include <fuse3/fuse_lowlevel.h> #include <time.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(); gid_t gid = getgid(); stbuf->st_ino = ino; if (ino == 1) { stbuf->st_mode = S_IFDIR | 0755; stbuf->st_nlink = 2; stbuf->st_uid = uid; stbuf->st_mode = S_IFDIR; } else { stbuf->st_mode = S_IFCHR | 0666; stbuf->st_nlink = 1; stbuf->st_size = name_length; stbuf->st_uid = uid; stbuf->st_gid = gid; } return 0; } static void list_dir() {} 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"); const char* s = mm.get_value(ino); size_t len = strlen(s); struct stat local_st; // do not use outsize of this function. memcpy(&local_st, attr, sizeof(struct stat)); sendmailfs_stat(ino, attr, len); struct timespec t; if (to_set & FUSE_SET_ATTR_MODE) { attr->st_mode = local_st.st_mode; } if (to_set & FUSE_SET_ATTR_UID) { attr->st_uid = local_st.st_uid; } if (to_set & FUSE_SET_ATTR_GID) { attr->st_gid = local_st.st_gid; } if (to_set & FUSE_SET_ATTR_SIZE) { attr->st_size = local_st.st_size; } if (to_set & FUSE_SET_ATTR_ATIME) { attr->st_atim = local_st.st_atim; } if (to_set & FUSE_SET_ATTR_MTIME) { attr->st_atim = local_st.st_mtim; } if (to_set & FUSE_SET_ATTR_ATIME_NOW) { clock_gettime(CLOCK_REALTIME, &attr->st_atim); } if (to_set & FUSE_SET_ATTR_MTIME_NOW) { clock_gettime(CLOCK_REALTIME, &attr->st_mtim); } if (to_set & FUSE_SET_ATTR_CTIME) { attr->st_ctim = local_st.st_ctim; } 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) { struct stat s; sendmailfs_stat(ino, &s, strlen(mm.get_value(ino))); puts("getattr"); fuse_reply_attr(req, &s, 2.0); } 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 void ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi) { // printf("size_t=%ld, fh=%ld, ino=%ld\n", size, fi->fh, ino); if (ino == 1) { off_t o = 0; size_t rest = size; size_t res; char* buf = (char*)calloc(1, size); struct stat dotst; sendmailfs_stat(ino, &dotst, strlen(".")); res = fuse_add_direntry(req, buf, rest, ".", &dotst, o); rest -= res; o++; printf("%s\n", "start of loop"); uint64_t num_contain = 0; for (auto& c : mm._data) { const char* t = c.second.c_str(); int ino2 = mm.get_ino(t); printf("ino2=%d\n", ino2); struct stat st; sendmailfs_stat(ino2, &st, strlen(t)); fuse_entry_param e; e.ino = ino2; e.attr_timeout = 0; sendmailfs_stat(ino2, &e.attr, strlen(t)); res = fuse_add_direntry_plus(req, buf, rest, t, &e, o); o += 1; rest -= res; } fuse_reply_buf(req, buf, size); } } static const struct fuse_lowlevel_ops opener = { .lookup = ll_lookup, .getattr = ll_getattr, .setattr = ll_setattr, .mknod = ll_mknod, .open = ll_open, .write = ll_write, .readdir = ll_readdir, }; 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; }
あなたの回答
tips
プレビュー