aboutsummaryrefslogtreecommitdiffstats
path: root/test/hello.c
diff options
context:
space:
mode:
authorBernd Schubert <bschubert@ddn.com>2025-04-16 00:24:42 +0200
committerBernd Schubert <bernd@bsbernd.com>2025-04-16 13:48:42 +0200
commit3863da58b1f7904675ca050434d8219bc410f34a (patch)
treea10b16ccda64e72fd4eeff0a69066c8436a9374f /test/hello.c
parentd66ca89e86a72fa5ad48d88ef5570062a79397be (diff)
downloadlibfuse-3863da58b1f7904675ca050434d8219bc410f34a.tar.gz
conn: prevent duplicate flag conversion in high-level interface
The high-level interface triggers flag conversion twice: once in the high-level init and once in the low-level init. This caused false "both 'want' and 'want_ext' are set" errors when using fuse_set_feature_flag() or fuse_unset_feature_flag(). The existing check for duplicate conversion only worked when 32-bit flags were set directly. When using the preferred flag manipulation functions, conn->want and the lower 32 bits of conn->want_ext would differ, triggering the error. Fix this by synchronizing conn->want with the lower 32 bits of conn->want_ext after conversion, ensuring consistent state for subsequent calls. Closes: https://github.com/libfuse/libfuse/issues/1171 Signed-off-by: Bernd Schubert <bschubert@ddn.com>
Diffstat (limited to 'test/hello.c')
-rw-r--r--test/hello.c180
1 files changed, 180 insertions, 0 deletions
diff --git a/test/hello.c b/test/hello.c
new file mode 100644
index 0000000..a07df0e
--- /dev/null
+++ b/test/hello.c
@@ -0,0 +1,180 @@
+/*
+ * FUSE: Filesystem in Userspace
+ * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+ *
+ * This program can be distributed under the terms of the GNU GPLv2.
+ * See the file COPYING.
+ */
+
+/** @file
+ *
+ * minimal example filesystem using high-level API
+ *
+ * Compile with:
+ *
+ * gcc -Wall hello.c `pkg-config fuse3 --cflags --libs` -o hello
+ *
+ * ## Source code ##
+ * \include hello.c
+ */
+
+#define FUSE_USE_VERSION 31
+
+#include <fuse.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <assert.h>
+
+/*
+ * Command line options
+ *
+ * We can't set default values for the char* fields here because
+ * fuse_opt_parse would attempt to free() them when the user specifies
+ * different values on the command line.
+ */
+static struct options {
+ const char *filename;
+ const char *contents;
+ int show_help;
+} options;
+
+#define OPTION(t, p) { t, offsetof(struct options, p), 1 }
+static const struct fuse_opt option_spec[] = {
+ OPTION("--name=%s", filename), OPTION("--contents=%s", contents),
+ OPTION("-h", show_help), OPTION("--help", show_help), FUSE_OPT_END
+};
+
+static void *hello_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
+{
+ (void)conn;
+ cfg->kernel_cache = 1;
+
+ /* Test setting flags the old way */
+ conn->want = FUSE_CAP_ASYNC_READ;
+ conn->want &= ~FUSE_CAP_ASYNC_READ;
+
+ return NULL;
+}
+
+static int hello_getattr(const char *path, struct stat *stbuf,
+ struct fuse_file_info *fi)
+{
+ (void)fi;
+ int res = 0;
+
+ memset(stbuf, 0, sizeof(struct stat));
+ if (strcmp(path, "/") == 0) {
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ } else if (strcmp(path + 1, options.filename) == 0) {
+ stbuf->st_mode = S_IFREG | 0444;
+ stbuf->st_nlink = 1;
+ stbuf->st_size = strlen(options.contents);
+ } else
+ res = -ENOENT;
+
+ return res;
+}
+
+static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi,
+ enum fuse_readdir_flags flags)
+{
+ (void)offset;
+ (void)fi;
+ (void)flags;
+
+ if (strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
+ filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
+ filler(buf, options.filename, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
+
+ return 0;
+}
+
+static int hello_open(const char *path, struct fuse_file_info *fi)
+{
+ if (strcmp(path + 1, options.filename) != 0)
+ return -ENOENT;
+
+ if ((fi->flags & O_ACCMODE) != O_RDONLY)
+ return -EACCES;
+
+ return 0;
+}
+
+static int hello_read(const char *path, char *buf, size_t size, off_t offset,
+ struct fuse_file_info *fi)
+{
+ size_t len;
+ (void)fi;
+ if (strcmp(path + 1, options.filename) != 0)
+ return -ENOENT;
+
+ len = strlen(options.contents);
+ if (offset < len) {
+ if (offset + size > len)
+ size = len - offset;
+ memcpy(buf, options.contents + offset, size);
+ } else
+ size = 0;
+
+ return size;
+}
+
+static const struct fuse_operations hello_oper = {
+ .init = hello_init,
+ .getattr = hello_getattr,
+ .readdir = hello_readdir,
+ .open = hello_open,
+ .read = hello_read,
+};
+
+static void show_help(const char *progname)
+{
+ printf("usage: %s [options] <mountpoint>\n\n", progname);
+ printf("File-system specific options:\n"
+ " --name=<s> Name of the \"hello\" file\n"
+ " (default: \"hello\")\n"
+ " --contents=<s> Contents \"hello\" file\n"
+ " (default \"Hello, World!\\n\")\n"
+ "\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+
+ /* Set defaults -- we have to use strdup so that
+ * fuse_opt_parse can free the defaults if other
+ * values are specified
+ */
+ options.filename = strdup("hello");
+ options.contents = strdup("Hello World!\n");
+
+ /* Parse options */
+ if (fuse_opt_parse(&args, &options, option_spec, NULL) == -1)
+ return 1;
+
+ /* When --help is specified, first print our own file-system
+ * specific help text, then signal fuse_main to show
+ * additional help (by adding `--help` to the options again)
+ * without usage: line (by setting argv[0] to the empty
+ * string)
+ */
+ if (options.show_help) {
+ show_help(argv[0]);
+ assert(fuse_opt_add_arg(&args, "--help") == 0);
+ args.argv[0][0] = '\0';
+ }
+
+ ret = fuse_main(args.argc, args.argv, &hello_oper, NULL);
+ fuse_opt_free_args(&args);
+ return ret;
+}