diff --git a/src/fs.c b/src/fs.c index 58ae4c9..12b7692 100644 --- a/src/fs.c +++ b/src/fs.c @@ -1147,6 +1147,83 @@ crypt4gh_sqlite_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) return crypt4gh_sqlite_xattr_exec(req, ino, name, NULL, 0, 0, removexattr_query); } +/* ===================================== + StatFS + ===================================== */ + +static char *statfs_query = "SELECT count(inode) AS f_files, " + " CAST(sum(size) / 4096 AS int64) as f_blocks, " + " max(length(name)) as f_namemax " + "FROM entries e "; + +static void +crypt4gh_sqlite_statfs(fuse_req_t req, fuse_ino_t ino) +{ + D1("STATFS %lu", ino); + + sqlite3_stmt *stmt = NULL; + if(sqlite3_prepare_v2(config.db, statfs_query, -1, &stmt, NULL) /* != SQLITE_OK */ || + !stmt){ + E("Preparing statement: %s | %s", statfs_query, sqlite3_errmsg(config.db)); + return (void) fuse_reply_err(req, EIO); + } + + /* Bind arguments */ + // sqlite3_bind_int64(stmt, 1, ino); + + print_expand_statement(stmt); + + int rc = 0; + int found = 0; + while(1){ /* Execute the query. */ + + rc = sqlite3_step(stmt); + if(rc == SQLITE_DONE || rc == SQLITE_ERROR) + break; + + if(!found && rc == SQLITE_ROW){ + + struct statvfs s; + memset(&s, 0, sizeof(struct statvfs)); + + /* Filesystem block size */ + s.f_bsize = 4096; + /* Fragment size */ + s.f_frsize = 4096; + /* Size of fs in f_frsize units */ + s.f_blocks = (fsblkcnt_t)sqlite3_column_int64(stmt, 1); + /* Number of free blocks */ + s.f_bfree = 0; + /* Number of free blocks for unprivileged users */ + s.f_bavail = 0; + /* Number of inodes */ + s.f_files = (fsfilcnt_t)sqlite3_column_int64(stmt, 0); + /* Number of free inodes */ + s.f_ffree = 0; + /* Number of free inodes for unprivileged users */ + s.f_favail = 0; + /* Filesystem ID */ + s.f_fsid = ino; + // See: https://man7.org/linux/man-pages/man2/statfs.2.html#VERSIONS + /* Mount flags */ + s.f_flag = ST_NOATIME | ST_NODEV | ST_NODIRATIME | ST_NOEXEC | ST_NOSUID; + if(!config.is_readwrite) + s.f_flag |= ST_RDONLY; + /* Maximum filename length */ + s.f_namemax = (unsigned long)sqlite3_column_int64(stmt, 2); + + + fuse_reply_statfs(req, &s); + found = 1; + } + } + + sqlite3_finalize(stmt); + + if(found) return; + fuse_reply_err(req, ENOSYS); // and stop asking +} + /* ===================================== Operations ===================================== */ @@ -1175,6 +1252,6 @@ fs_operations(void) fs_oper.setxattr = crypt4gh_sqlite_setxattr; fs_oper.removexattr = crypt4gh_sqlite_removexattr; - //fs_oper.statfs = crypt4gh_sqlite_statfs; + fs_oper.statfs = crypt4gh_sqlite_statfs; return &fs_oper; } diff --git a/src/includes.h b/src/includes.h index aeccb5f..e4f6cfd 100644 --- a/src/includes.h +++ b/src/includes.h @@ -21,19 +21,11 @@ #endif #ifndef FUSE_USE_VERSION -#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 12) +#define FUSE_USE_VERSION FUSE_MAKE_VERSION(3, 14) #endif #include -#ifndef FUSE_MAKE_VERSION -#define FUSE_MAKE_VERSION(maj, min) ((maj) * 100 + (min)) -#endif - -#ifndef FUSE_VERSION -#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) -#endif - #include #include #include @@ -53,6 +45,7 @@ #include #include #include +#include #include #define OFF_FMT "%lu" @@ -116,6 +109,7 @@ struct fs_config { time_t mounted_at; int direct_io; + int is_readwrite; unsigned int dperm; unsigned int fperm; @@ -151,6 +145,7 @@ struct fs_config { int singlethread; int clone_fd; int max_idle_threads; + int max_threads; }; diff --git a/src/main.c b/src/main.c index 49cf5a4..1198ee5 100644 --- a/src/main.c +++ b/src/main.c @@ -84,6 +84,7 @@ static struct fuse_opt fs_opts[] = { CRYPT4GH_SQLITE_OPT("-s" , singlethread , 1), CRYPT4GH_SQLITE_OPT("clone_fd" , clone_fd , 1), CRYPT4GH_SQLITE_OPT("max_idle_threads=%u", max_idle_threads, 0), + CRYPT4GH_SQLITE_OPT("max_threads=%u", max_threads, 0), CRYPT4GH_SQLITE_OPT("entry_timeout=%lf", entry_timeout, 0), CRYPT4GH_SQLITE_OPT("attr_timeout=%lf", attr_timeout, 0), @@ -286,10 +287,13 @@ int main(int argc, char *argv[]) config.singlethread = 0; config.foreground = 0; config.mounted_at = time(NULL); - config.max_idle_threads = DEFAULT_MAX_THREADS; + config.entry_timeout = DEFAULT_ENTRY_TIMEOUT; config.attr_timeout = DEFAULT_ATTR_TIMEOUT; + config.max_threads = DEFAULT_MAX_THREADS; + config.max_idle_threads = UINT_MAX; + config.uid = getuid(); /* current user */ config.gid = getgid(); /* current group */ @@ -353,10 +357,10 @@ int main(int argc, char *argv[]) sqlite3_config(SQLITE_CONFIG_MULTITHREAD); /* checking if the DB is writable */ - int is_readwrite = !access(config.db_path, R_OK | W_OK); - D1("Opening SQLite path: %s (%s)", config.db_path, (is_readwrite) ? "read-write" : "read-only"); + config.is_readwrite = !access(config.db_path, R_OK | W_OK); + D1("Opening SQLite path: %s (%s)", config.db_path, (config.is_readwrite) ? "read-write" : "read-only"); sqlite3_open_v2(config.db_path, &config.db, - ((is_readwrite) ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY) + (config.is_readwrite ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY) | SQLITE_OPEN_FULLMUTEX, NULL); if (config.db == NULL){ @@ -379,7 +383,7 @@ int main(int argc, char *argv[]) operations = fs_operations(); /* disable if you can't write in the DB file */ - if(!is_readwrite){ + if(!config.is_readwrite){ operations->setxattr = NULL; operations->removexattr = NULL; } @@ -422,12 +426,13 @@ int main(int argc, char *argv[]) D2("Mode: single-threaded"); res = fuse_session_loop(se); } else { - struct fuse_loop_config cf = { - .clone_fd = config.clone_fd, - .max_idle_threads = config.max_idle_threads, - }; - D2("Mode: multi-threaded (max idle threads: %d)", cf.max_idle_threads); - res = fuse_session_loop_mt(se, &cf); + struct fuse_loop_config *cf = fuse_loop_cfg_create(); + fuse_loop_cfg_set_idle_threads(cf, config.max_idle_threads); + fuse_loop_cfg_set_max_threads(cf, config.max_threads); + fuse_loop_cfg_set_clone_fd(cf, config.clone_fd); + D2("Mode: multi-threaded (max idle threads: %d)", config.max_threads); + res = fuse_session_loop_mt(se, cf); + fuse_loop_cfg_destroy(cf); } bailout_unmount: