diff options
| author | Florian Weimer <fweimer@redhat.com> | 2024-08-30 21:52:53 +0200 |
|---|---|---|
| committer | Florian Weimer <fweimer@redhat.com> | 2024-09-05 12:05:32 +0200 |
| commit | f169509ded534537eec9df00cfada6dbca908352 (patch) | |
| tree | 2f30454c9478fdc77f6e3b46fe16ab0b70a21701 /support/fuse.h | |
| parent | 61f2c2e1d1287a791c22d86c943b44bcf66bb8ad (diff) | |
| download | glibc-f169509ded534537eec9df00cfada6dbca908352.tar.xz glibc-f169509ded534537eec9df00cfada6dbca908352.zip | |
support: Add FUSE-based file system test framework to support/
This allows to monitor the exact file system operations
performed by glibc and inject errors.
Hurd does not have <sys/mount.h>. To get the sources to compile
at least, the same approach as in support/test-container.c is used.
Reviewed-by: DJ Delorie <dj@redhat.com>
Diffstat (limited to 'support/fuse.h')
| -rw-r--r-- | support/fuse.h | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/support/fuse.h b/support/fuse.h new file mode 100644 index 0000000000..4c365fbc0c --- /dev/null +++ b/support/fuse.h @@ -0,0 +1,215 @@ +/* Facilities for FUSE-backed file system tests. + Copyright (C) 2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +/* Before using this functionality, use support_enter_mount_namespace + to ensure that mounts do not impact the overall system. */ + +#ifndef SUPPORT_FUSE_H +#define SUPPORT_FUSE_H + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> + +#include <support/bundled/linux/include/uapi/linux/fuse.h> + +/* This function must be called furst, before support_fuse_mount, to + prepare unprivileged mounting. */ +void support_fuse_init (void); + +/* This function can be called instead of support_fuse_init. It does + not use mount and user namespaces, so it requires root privileges, + and cleanup after testing may be incomplete. This is intended only + for test development. */ +void support_fuse_init_no_namespace (void); + +/* Opaque type for tracking FUSE mount state. */ +struct support_fuse; + +/* This function disables a mount point created using + support_fuse_mount. */ +void support_fuse_unmount (struct support_fuse *) __nonnull ((1)); + +/* This function is called on a separate thread after calling + support_fuse_mount. F is the mount state, and CLOSURE the argument + that was passed to support_fuse_mount. The callback function is + expected to call support_fuse_next to read packets from the kernel + and handle them according to the test's need. */ +typedef void (*support_fuse_callback) (struct support_fuse *f, void *closure); + +/* This function creates a new mount point, implemented by CALLBACK. + CLOSURE is passed to CALLBACK as the second argument. */ +struct support_fuse *support_fuse_mount (support_fuse_callback callback, + void *closure) + __nonnull ((1)) __attr_dealloc (support_fuse_unmount, 1); + +/* This function returns the path to the mount point for F. The + returned string is valid until support_fuse_unmount (F) is called. */ +const char * support_fuse_mountpoint (struct support_fuse *f) __nonnull ((1)); + + +/* Renders the OPCODE as a string (FUSE_* constant. The caller must + free the returned string. */ +char * support_fuse_opcode (uint32_t opcode) __attr_dealloc_free; + +/* Use to provide a checked cast facility. Use the + support_fuse_in_cast macro below. */ +void *support_fuse_cast_internal (struct fuse_in_header *, uint32_t) + __nonnull ((1)); +void *support_fuse_cast_name_internal (struct fuse_in_header *, uint32_t, + size_t skip, char **name) + __nonnull ((1)); + +/* The macro expansion support_fuse_in_cast (P, TYPE) casts the + pointer INH to the appropriate type corresponding to the FUSE_TYPE + opcode. It fails (terminates the process) if INH->opcode does not + match FUSE_TYPE. The type of the returned pointer matches that of + the FUSE_* constant. + + Maintenance note: Adding support for additional struct fuse_*_in + types is generally easy, except when there is trailing data after + the struct (see below for support_fuse_cast_name, for example), and + the kernel has changed struct sizes over time. This has happened + recently with struct fuse_setxattr_in, and would require special + handling if implemented. */ +#define support_fuse_payload_type_INIT struct fuse_init_in +#define support_fuse_payload_type_LOOKUP char +#define support_fuse_payload_type_OPEN struct fuse_open_in +#define support_fuse_payload_type_READ struct fuse_read_in +#define support_fuse_payload_type_SETATTR struct fuse_setattr_in +#define support_fuse_payload_type_WRITE struct fuse_write_in +#define support_fuse_cast(typ, inh) \ + ((support_fuse_payload_type_##typ *) \ + support_fuse_cast_internal ((inh), FUSE_##typ)) + +/* Same as support_fuse_cast, but also writes the passed name to *NAMEP. */ +#define support_fuse_payload_name_type_CREATE struct fuse_create_in +#define support_fuse_payload_name_type_MKDIR struct fuse_mkdir_in +#define support_fuse_cast_name(typ, inh, namep) \ + ((support_fuse_payload_name_type_##typ *) \ + support_fuse_cast_name_internal \ + ((inh), FUSE_##typ, sizeof (support_fuse_payload_name_type_##typ), \ + (namep))) + +/* This function should be called from the callback function. It + returns NULL if the mount point has been unmounted. The result can + be cast using support_fuse_in_cast. The pointer is invalidated + with the next call to support_fuse_next. + + Typical use involves handling some basics using the + support_fuse_handle_* building blocks, following by a switch + statement on the result member of the returned struct, to implement + what a particular test needs. Casts to payload data should be made + using support_fuse_in_cast. + + By default, FUSE_FORGET responses are filtered. See + support_fuse_filter_forget for turning that off. */ +struct fuse_in_header *support_fuse_next (struct support_fuse *f) + __nonnull ((1)); + +/* This function can be called from a callback function to handle + basic aspects of directories (OPENDIR, GETATTR, RELEASEDIR). + inh->nodeid is used as the inode number for the directory. This + function must be called after support_fuse_next. */ +bool support_fuse_handle_directory (struct support_fuse *f) __nonnull ((1)); + +/* This function can be called from a callback function to handle + access to the mount point itself, after call support_fuse_next. */ +bool support_fuse_handle_mountpoint (struct support_fuse *f) __nonnull ((1)); + +/* If FILTER_ENABLED, future support_fuse_next calls will not return + FUSE_FORGET events (and simply discared them, as they require no + reply). If !FILTER_ENABLED, the callback needs to handle + FUSE_FORGET events and call support_fuse_no_reply. */ +void support_fuse_filter_forget (struct support_fuse *f, bool filter_enabled) + __nonnull ((1)); + +/* This function should be called from the callback function after + support_fuse_next returned a non-null pointer. It sends out a + response packet on the FUSE device with the supplied payload data. */ +void support_fuse_reply (struct support_fuse *f, + const void *payload, size_t payload_size) + __nonnull ((1)) __attr_access ((__read_only__, 2, 3)); + +/* This function should be called from the callback function. It + replies to a request with an error indicator. ERROR must be positive. */ +void support_fuse_reply_error (struct support_fuse *f, uint32_t error) + __nonnull ((1)); + +/* This function should be called from the callback function. It + sends out an empty (but success-indicating) reply packet. */ +void support_fuse_reply_empty (struct support_fuse *f) __nonnull ((1)); + +/* Do not send a reply. Only to be used after a support_fuse_next + call that returned a FUSE_FORGET event. */ +void support_fuse_no_reply (struct support_fuse *f) __nonnull ((1)); + +/* Specific reponse preparation functions. The returned object can be + updated as needed. If a NODEID argument is present, it will be + used to set the inode and FUSE nodeid fields. Without such an + argument, it is initialized from the current request (if the reply + requires this field). This function must be called after + support_fuse_next. The actual response must be sent using + support_fuse_reply_prepared (or a support_fuse_reply_error call can + be used to cancel the response). */ +struct fuse_entry_out *support_fuse_prepare_entry (struct support_fuse *f, + uint64_t nodeid) + __nonnull ((1)); +struct fuse_attr_out *support_fuse_prepare_attr (struct support_fuse *f) + __nonnull ((1)); + +/* Similar to the other support_fuse_prepare_* functions, but it + prepares for two response packets. They can be updated through the + pointers written to *OUT_ENTRY and *OUT_OPEN prior to calling + support_fuse_reply_prepared. */ +void support_fuse_prepare_create (struct support_fuse *f, + uint64_t nodeid, + struct fuse_entry_out **out_entry, + struct fuse_open_out **out_open) + __nonnull ((1, 3, 4)); + + +/* Prepare sending a directory stream. Must be called after + support_fuse_next and before support_fuse_dirstream_add. */ +struct support_fuse_dirstream; +struct support_fuse_dirstream *support_fuse_prepare_readdir (struct + support_fuse *f); + +/* Adds directory using D_INO, D_OFF, D_TYPE, D_NAME to the directory + stream D. Must be called after support_fuse_prepare_readdir. + + D_OFF is the offset of the next directory entry, not the current + one. The first entry has offset zero. The first requested offset + can be obtained from the READ payload (struct fuse_read_in) prior + to calling this function. + + Returns true if the entry could be added to the buffer, or false if + there was insufficient room. Sending the buffer is delayed until + support_fuse_reply_prepared is called. */ +bool support_fuse_dirstream_add (struct support_fuse_dirstream *d, + uint64_t d_ino, uint64_t d_off, + uint32_t d_type, + const char *d_name); + +/* Send a prepared response. Must be called after one of the + support_fuse_prepare_* functions and before the next + support_fuse_next call. */ +void support_fuse_reply_prepared (struct support_fuse *f) __nonnull ((1)); + +#endif /* SUPPORT_FUSE_H */ |
