From: Ingo Schwarze Subject: Re: libfuse: new man pages for low-level API To: Helg Cc: tech@openbsd.org Date: Wed, 28 Jan 2026 21:06:14 +0100 Hello Helg, you can go ahead based on the OK from claudio@, manual pages can easily be polished in the tree. However, if you want to avoid later churn, consider addressing the following issues before commit: 1. The .Dt name of each manual page should be the name of a real function, not a made-up name like fuse_session(3) that doesn't actually exist in any namespace. The name of the file (both in the source tree and, even more importantly, after installation of the manual page) should usually match the name in .Dt. Ideally pick the name of the function that is typically called first. IIUC, that is fuse_lowlevel_new(3), so it's not an option for the other two pages. Alternatively, use the name of the most important function or of the function most commonly or frequently called as the .Dt and file name, or of the function documented first in the manual page. 2. It is almost always desirable to document a destructor in the same manual page as the corresponding constructor. That implies fuse_session_destroy(3) should probably be documented in fuse_lowlevel_new(3). 3. If a manual page documents more than one function, choose a logically sound order of the functions and use that order consistently in all sections, in particular in NAME, SYNOPSIS, DESCRIPTION. In RETURN VALUES and HISTORY, group the functions according to the return value (or appearance), then sort the groups according to the function in each group that appears first in NAME, and in the normal order within each group. There are no hard and fast rules what is "logically sound"; the ultimate goal is to make serial reading as helpful as possible. A few ideas are: * Choose the order such that refering from functions to functions documented later is needed as rarely as possible. * Sort the functions in the order they are typically called. * Put the getter for a property right after the associated setter for the same property. * Put deprecated functions at the very end. Put the most important functions near the top. Put higher level before lower level functions. Some of the suggestions may occasionally contradict each other, requiring human judgement. Some additional comments in-line. Ingo Helg wrote on Wed, Jan 28, 2026 at 07:46:51AM +0100: > Index: fuse_session.3 Maybe call this fuse_session_loop.3 ? That looks like the most important and most high-level function in this page (IIUC). > =================================================================== > RCS file: fuse_session.3 > diff -N fuse_session.3 > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ fuse_session.3 27 Jan 2026 08:44:02 -0000 > @@ -0,0 +1,121 @@ > +.\" $OpenBSD$ > +.\" > +.\" Copyright (c) 2025 Helg Bredow > +.\" > +.\" Permission to use, copy, modify, and distribute this software for any > +.\" purpose with or without fee is hereby granted, provided that the above > +.\" copyright notice and this permission notice appear in all copies. > +.\" > +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > +.\" > +.Dd $Mdocdate: September 23 2025 $ > +.Dt FUSE_SESSION 3 Maybe .Dt FUSE_SESSION_LOOP 3 ? > +.Os > +.Sh NAME > +.Nm fuse_session_destroy , > +.Nm fuse_session_add_chan , > +.Nm fuse_session_remove_chan , > +.Nm fuse_session_loop , > +.Nm fuse_session_exit , > +.Nm fuse_session_exited , > +.Nm fuse_session_reset , > +.Nm fuse_session_process Sort in the same order as SYNOPSIS. > +.Nd manage lifecycle and event handling for a FUSE session > +.Sh SYNOPSIS > +.Lb fuse > +.In fuse_lowlevel.h > +.Ft int > +.Fn fuse_session_loop "struct fuse_session *se" > +.Ft void > +.Fn fuse_session_exit "struct fuse_session *se" > +.Ft int > +.Fn fuse_session_exited "struct fuse_session *se" > +.Ft void > +.Fn fuse_session_reset "struct fuse_session *se" > +.Ft void > +.Fn fuse_session_process "struct fuse_session *se" "struct fuse_chan *ch" \ > + "const struct fuse_buf *buf" "struct fuse_buf *outbuf" For functions having long arguments or more than one argument, prefer .Fo/.Fa/.Fc over .Fn, in particular when .Fn requires line continuation. > +.Ft void > +.Fn fuse_session_destroy "struct fuse_session *se" > +.Ft void > +.Fn fuse_session_add_chan "struct fuse_session *se" "struct fuse_chan *ch" > +.Ft void > +.Fn fuse_session_remove_chan "struct fuse_chan *ch" > +.Sh DESCRIPTION > +These functions are part of the FUSE low-level API and are used to manage the > +lifecycle, communication channel, and event processing of a FUSE session. > +.Pp > +A fuse_session is created with > +.Xr fuse_lowlevel_new 3 . > +.Bl -tag -width Ds Using a .Bl -tag list for listing functions is optional. If you choose to use a list, you did it exactly right. > +.It Fn fuse_session_loop "se" > +Runs the main event loop for the session If you use a list, the list tag is not part of the following sentence. Either way, each sentence should be complete and grammatically correct. So either of these is fine: Use a list and use the imperative: .Bl -tag -width Ds .It Fn fuse_session_loop "se" Run the ... Or do not use a list and use the indicative (slightly more common): .Fn fuse_session_loop runs the ... > +.Fa se . > +This function blocks and processes incoming requests until > +.Fn fuse_session_exit > +is called. > +.It Fn fuse_session_exit "se" > +Signals the session Signal the ... > +.Fa se > +to exit its event loop. > +This does not destroy the session or unmount the filesystem. > +.It Fn fuse_session_exited > +Returns non-zero if > +.Fn fuse_session_exit "se" > +has been called and the session Assuming this function has no side effects, this item belongs in the RETURN VALUES section, not in the DESCRIPTION. > +.Fa se > +is marked for termination. > +.It Fn fuse_session_reset "se" > +Resets the session Reset the ... > +.Fa se > +to a non-exited state, allowing the loop to be restarted. > +.It Fn fuse_session_process "se" "buf" "bufsize" "ch" > +Processes a single buffer Process a ... > +.Fa buf > +received from channel > +.Fa ch > +in the session > +.Fa se . > +If ch is > +.Dv NULL > +then the channel added to the session is used instead. > +Used for manual request handling outside of the main loop. > +.It Fn fuse_session_destroy "se" > +Destroys the FUSE session > +.Fa se , > +freeing all associated resources. > +This should be called after the session > +loop completes and the file system has been unmounted. I think this function belongs in fuse_lowlevel_new(3). > +.It Fn fuse_session_add_chan "se" "ch" > +Adds the communication channel Add the ... > +.Fa ch > +to the session > +.Fa se . > +Channels are used to receive requests from the kernel. > +Only one channel can be associated with a channel at a time. Is this a typo? Should this read: Only one channel can be associated with a session at a time. or Only one session can be associated with a channel at a time. or both? > +.It Fn fuse_session_remove_chan "ch" > +Removes the channel Remove the ... > +.Fa ch > +from its associated session. > +.El > +.Sh RETURN VALUES > +.Fn fuse_session_loop > +returns 0 on success or a negative error code on failure. If different negative return values have differentr meanings, all possible return values should be listed and explained. If the specific value does not matter, it should only say: returns 0 on success or a negative value on failure. > +.Fn fuse_session_exited > +returns 1 if the session has been exited, 0 otherwise. The text misplaced into the DESCRIPTION should probably be merged here. > +.Sh SEE ALSO > +.Xr fuse_chan_fd 3 , > +.Xr fuse_lowlevel_new 3 I suspect that .Xr fuse_mount 3 might be missing here because otherwise, readers aren't told where to get the "ch" argument from. > +.Sh STANDARDS > +These library functions conform to FUSE 2.6. > +.Sh HISTORY > +These library functions first appeared in > +.Ox 7.9 . That seems misleading, you did not design this API. I suggest the wording: These functions have been available since .Ox 7.9 . > +.Sh AUTHORS > +.An Helg Bredow Aq Mt helg@openbsd.org > Index: fuse_lowlevel_new.3 > =================================================================== > RCS file: fuse_lowlevel_new.3 > diff -N fuse_lowlevel_new.3 > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ fuse_lowlevel_new.3 27 Jan 2026 08:44:02 -0000 > @@ -0,0 +1,466 @@ > +.\" $OpenBSD: fuse_new.3,v 1.11 2025/09/23 09:28:28 schwarze Exp $ > +.\" > +.\" Copyright (c) 2025 Helg Bredow > +.\" > +.\" Permission to use, copy, modify, and distribute this software for any > +.\" purpose with or without fee is hereby granted, provided that the above > +.\" copyright notice and this permission notice appear in all copies. > +.\" > +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > +.\" > +.Dd $Mdocdate: September 23 2025 $ > +.Dt FUSE_LOWLEVEL_NEW 3 > +.Os > +.Sh NAME > +.Nm fuse_lowlevel_new > +.Nd FUSE implementation routine to initialise a FUSE session That sounds rather wordy to me. If you add fuse_session_destroy(3) here, i'd prefer something like .Nd initialise and destroy a FUSE session > +.Sh SYNOPSIS > +.Lb libfuse > +.In fuse_lowlevel.h > +.Ft struct fuse_session * > +.Fn fuse_lowlevel_new \ > + "struct fuse_args *fargs" \ > + "const struct fuse_lowlevel_ops *ops" \ > + "size_t ops_size" \ > + "void *userdata" Please use .Fo/.Fa/.Fc. > +.Sh DESCRIPTION > +The > +.Fn fuse_lowlevel_new > +function creates a new FUSE session using the low-level API. > +This session handles communication between the kernel and the user-space > +filesystem implementation. > +.Pp > +The > +.Fa args > +parameter is a pointer to a > +.Va struct fuse_args > +containing command-line arguments for the session. No need to verbosely restate what is already clear from the SYNOPSIS. Maybe say something like: The .Fa args parameter points to command-line arguments for the session > +The following arguments are supported: > +.Bl -tag -width indent "-width indent" feels excessive to me; i suggest using the conventional "-width Ds" instead. > +.It debug, -d I suspect what you mean might be: .It Fl d , \-debug For command line arguments, use the .Fl macro. We prefer POSIX-style short options over GNU-style long options, so where both exists, list short options before the corresponding long aliases. I *guess* your word "debug" is intended to refer to a long option --debug. For long options, the syntax .Fl \-debug is now required because newer groff will misformat .Fl -debug. > +Enable debug output that prints details of each request received to stderr. ... prints ... to standard error output. In manual pages, avoid the jargon "stderr" unless you are specifically referring to the macro defined in (as opposed to the general concept of standard error output), and in that case, you would have to use the .Dv macro, like in this example: Do not call .Xr fclose 3 on the .Vt FILE * object .Dv stderr . > +.It --help, -h .It Fl h , \-help > +Causes a short usage help to be printed to stderr. The word "cause" does not seem to say anything, and "usage" and "short help" seem synonyms of each other, so maybe something like: Print a short help message to standard error output. Print a usage message to standard error output. > +.It --version, -V .It Fl V , \-version > +Causes version information to be printed to stderr. Print version information to standard error output. Maybe also clarify what "version" refers to? API version? Filesystem version? Version of the FUSE implementation? Version of the user-space file system daemon? So many possibilities... > +.It max_write Is this also a command line options, like: .It Fl \-max_write > +The maximum number bytes that the filesystem can handle in one write request. number of bytes > +.El > +.Pp > +The > +.Fa ops > +parameter is a pointer to a > +.Va struct fuse_lowlevel_ops > +structure that defines the file system operations supported by the user-space > +file system daemon. ... points to the file system operations ... > +See below. > +.Pp > +The > +.Fa op_size > +parameter specifies the size of the > +.Va struct fuse_lowlevel_ops .Vt struct fuse_lowlevel_ops > +structure. Maybe state the unit, for example: ... the size of the ... structure in bytes. > +.Pp > +The > +.Fa userdata > +parameter is a pointer to user-defined data. > +This is passed back to the init and destroy handlers. I recommend s/back// because "userdata" appears to come from the caller, not from either of these two handlers, so it isn't passed "back", right? I also recommend to consistently mark up the names of the handler functions with .Fn, for example .Fn init > +.Pp > +This function does not mount the filesystem or start the event loop. > +After creating the session, > +.Xr fuse_session_loop 3 > +can be called to begin handling requests. > +.Pp > +FUSE operations work in the same way as their UNIX file system > +counterparts. > +A functional file system must implement at least lookup, getattr, readdir, > +open, read and statfs. Maybe use .Fn here, too? Do you want to add a sentence like The other functions are optional, and the struct members pointing to them can be set to .Dv NULL . Or is that not the intended meaning? > +.Pp > +The > +.Va fuse_lowlevel_ops .Ft fuse_lowlevel_ops > +structure is defined as: > +.Bd -literal OK, i think here, using a literal display is acceptable. It's not very nice though because it provides no markup like .Fo and .Fa and because the whole, long display repeats information that follows below. Maybe just say something like The .Vt fuse_lowlevel_ops structure contains one function pointer to each of the functions listed below. The return type of each function pointer is .Ft void . instead of listing all the functions twice? > +struct fuse_lowlevel_ops { > + void (*init)(void *, struct fuse_conn_info *); > + void (*destroy)(void *); > + void (*lookup)(fuse_req_t, fuse_ino_t, const char *); > + void (*getattr)(fuse_req_t, fuse_ino_t, struct fuse_file_info *); > + void (*setattr)(fuse_req_t, fuse_ino_t, struct stat *, int, > + struct fuse_file_info *); > + void (*readlink)(fuse_req_t, fuse_ino_t); > + void (*mknod)(fuse_req_t, fuse_ino_t, const char *, mode_t, dev_t); > + void (*mkdir)(fuse_req_t, fuse_ino_t, const char *, mode_t); > + void (*unlink)(fuse_req_t, fuse_ino_t, const char *); > + void (*rmdir)(fuse_req_t, fuse_ino_t, const char *); > + void (*symlink)(fuse_req_t, const char *, fuse_ino_t, > + const char *); > + void (*rename)(fuse_req_t, fuse_ino_t, const char *, fuse_ino_t, > + const char *); > + void (*link)(fuse_req_t, fuse_ino_t, fuse_ino_t, const char *); > + void (*open)(fuse_req_t, fuse_ino_t, struct fuse_file_info *); > + void (*read)(fuse_req_t, fuse_ino_t, size_t, off_t, > + struct fuse_file_info *); > + void (*write)(fuse_req_t, fuse_ino_t, const char *, size_t, off_t, > + struct fuse_file_info *); > + void (*flush)(fuse_req_t, fuse_ino_t, struct fuse_file_info *); > + void (*release)(fuse_req_t, fuse_ino_t, struct fuse_file_info *); > + void (*fsync)(fuse_req_t, fuse_ino_t, int, > + struct fuse_file_info *); > + void (*opendir)(fuse_req_t, fuse_ino_t, struct fuse_file_info *); > + void (*readdir)(fuse_req_t, fuse_ino_t, size_t, off_t, > + struct fuse_file_info *); > + void (*releasedir)(fuse_req_t, fuse_ino_t, > + struct fuse_file_info *); > + void (*statfs)(fuse_req_t, fuse_ino_t); > + > + /* Unsupported */ > + void (*access)(fuse_req_t, fuse_ino_t, int); > + void (*create)(fuse_req_t, fuse_ino_t, const char *, mode_t, > + struct fuse_file_info *); > + void (*bmap)(fuse_req_t, fuse_ino_t, size_t, uint64_t); > + void (*fsyncdir)(fuse_req_t, fuse_ino_t, int, > + struct fuse_file_info *); > +} > +.Ed > +.Pp > +The first parameter to each of these operations (except for init and > +destroy) is a reference to the kernel request. > +The relevant parameters from this request are passed as arguments to each > +operation. > +.Pp > +All operations can respond failure with The wording "respond failure" seems grammatically dubious. Do you mean "report failure" or "respond to failure"? > +.Xr fuse_reply_err 3 . > +If no other response is indicated below, then operations must respond > +success by passing 0 to the "report success" or "respond to success"? > +.Fa errno Your fuse_reply_err(3) manual calls this argument ".Fa err" instead. Please make this consistent. > +argument > +of > +.Xr fuse_reply_err 3 . > +.Pp > +The operations defined in the > +.Va struct fuse_lowlevel_ops .Vt struct fuse_lowlevel_ops > +structure are: > +.Bl -tag -width releasedir Since all the functions have arguments anyway, i don't think such a broad width is called for, in particular since some of the item bodies contain substantial amounts of text that would benefit from being less far indented. I suggest using the conventional -width Ds. > +.It Fn access "req" "ino" "mask" If you decide to get rid of the unwieldy struct display above, i recommend making this .It Xo Fo access .Fa "fuse_req_t req" .Fa "fuse_ino_t ino" .Fa "int mask" .Fc Xc to put all the information in one place, and similarly for the other handler functions. While mandoc(1) does not need the .Xo/.Xc pair, it seems to be needed with newer groff versions - not sure whether that is bug in newer groff or was needed with older groff, too. > +Not implemented. > +.Ox > +always behaves as if the default_permissions mount option was specified. > +See > +.Xr fuse_mount 3 . > +.It Fn create "req" "parent" "name" "mode" "ffi" > +Not implemented. > +File systems must implement mknod instead. > +.It Fn destroy "userdata" > +This is the last handler called when the file system is unmounted. > +.It Fn flush "req" "ino" "ffi" > +Called when a regular file is closed by the > +.Xr close 2 > +system call. > +This is the only way for a file system to return an error on close. > +.Pp > +The > +.Fa ino > +argument identifies the file that was closed. > +.Pp > +The > +.Fa ffi > +argument is a > +.Va struct fuse_file_info > +that contains the following fields: > +.Pp > +.Bl -tag -compact -width indent > +.It Va fh > +Contains the file handle returned by the file system when the file was opened. > +.It Va flush > +1 (true) if the file should be flushed. > +0 (false) otherwise. > +.El > +.It Fn fsync "req" "ino" "datasync" "ffi" > +Optional function that implements > +.Xr fsync 2 > +and > +.Xr fdatasync 2 . > +The > +.Fa datasync > +parameter specifies whether the operation is as a result > +of a call to > +.Xr fdatasync 2 > +and is currently always 0 (false). > +.Fa ffi.fh > +will contain the file handle returned by the file system when > +the file was opened. > +.It Fn fsyncdir "req" "ino" "datasync" "ffi" > +Not implemented. > +.It Fn getattr "req" "ino" "ffi" > +Corresponds to the > +.Xr stat 2 > +system call. > +.Pp > +On success, responds with > +.Xr fuse_reply_attr 3 . > +.It Fn getxattr "req" "ino" "name" "size" > +Not implemented. > +.It Fn init "userdata" "fci" > +The is the first handler called by the kernel when the file system is This is ... ? > +mounted. > +.Fa userdata > +is a pointer to the data passed to > +.Xr fuse_mount 3 . > +.Fa fci > +contains connection information in a structure as follows: > +.Bd -literal > +struct fuse_conn_info { > + uint32_t proto_major; > + uint32_t proto_minor; > + uint32_t async_read; > + uint32_t max_write; > + uint32_t max_readahead; > + uint32_t capable; > + uint32_t want; > + uint32_t max_background; > + uint32_t congestion_threshold; > + uint32_t reserved[23]; > +}; > +.Ed > +.It Fn link "req" "ino" "newparent" "name" > +Create a hard link to > +.Fa ino > +in directory > +.Fa parent > +with > +.Fa name > +.Pp > +On success, responds with > +.Xr fuse_reply_entry 3 . > +.It Fn lookup "req" "parent" "name" > +Looks up an entry > +.Fa name > +in the directory specified by > +.Fa parent . > +.Pp > +If the entry cannot be found then this operation should reply with > +.Er ENOENT . > +Alternatively, an entry with ino = 0 can be returned. > +This is intended to indicate that the negative result can be cached for > +.Fa entry_timeout > +seconds. > +However, > +.Ox > +does not cache lookups so this is equivalent to returning > +.Er ENOENT . > +.Pp > +Valid responses are > +.Xr fuse_reply_err 3 > +and > +.Xr fuse_reply_entry 3 . > +.It Fn mkdir "req" "parent" "name" "mode" > +Called on > +.Xr mkdir 2 > +to create a new directory > +.Fa name > +in directory > +.Fa parent > +with mode > +.Fa mode . > +On success, responds with > +.Xr fuse_reply_entry 3 . > +.It Fn mknod "req" "parent" "name" "mode" "rdev" > +Called on > +.Xr open 2 > +and > +.Xr mknod 2 > +to create regular files, pipes and device special files with > +.Fa name > +in the directory > +specified by > +.Fa parent . > +.Fa mode > +specifies the file type and mode with which to create the file. > +.Fa rdev > +specifies the device number if creating a device. > +.Pp > +On success, responds with > +.Xr fuse_reply_entry 3 . > +.It Fn open "req" "ino" "ffi" > +Called on > +.Xr open 2 > +to open the file specified by > +.Fa ino . > +Due to the difference between FUSE and the > +.Ox > +VFS, > +open will only be called once for each file > +for every different combination of flags provided to > +.Xr open 2 . > +The O_CREAT and O_TRUNC flags are never passed from the kernel to open, Use .Dv for O_CREAT and for O_TRUNC. > +the mknod and truncate operations are invoked before open instead. > +These flags are available in the > +.Fa flags > +member of > +.Fa ffi . > +.Pp > +On success, responds with > +.Xr fuse_reply_open 3 . > +.It Fn opendir "req" "ino" "ffi" > +Called when a directory is opened for reading. > +.Pp > +On success, responds with > +.Xr fuse_reply_open 3 . > +.It Fn read "req" "ino" "size" "off" "ffi" > +Reads the contents of file > +.Fa ino > +at offset > +.Fa off . > +.Pp > +On success, responds with > +.Xr fuse_reply_buf 3 . > +.It Fn readdir "req" "ino" "size" "off" "ffi" > +Called to read the entries in a directory specified by > +.Fa ino . > +The file system implementation must use the > +.Xr fuse_add_direntry 3 In which manual page is this function documented? $ man fuse_add_direntry man: No entry for fuse_add_direntry in the manual. If the intention is to document it in fuse_lowlevel_new(3), This function has to be added to NAME, SYNOPSIS, and RETURN VALUES. Documenting it in the middle of the documentation of a struct, which is itself part of the documentation of another function, is a strange choice, if that is the intention. Does this need some disentangling, for example moving the description of fuse_add_direntry(3) to the top level of the DESCRIPTION? > +function to add each entry to the buffer. > +The buffer must be large enough to hold the entry. > +If it's too small, the entry won't be written, but the required size > +will still be returned. > +To check for success, compare the buffer size (bufsize) with the returned > +entry size. > +If the entry size is greater than the buffer size, the operation failed. > +The size required can be determined by calling > +.Fn fuse_add_direntry > +with a > +NULL buf. Use .Dv for NULL, for example: ... by calling .Fn fuse_add_direntry with .Fa buf set to .Dv NULL . > +.Pp > +.Ft size_t > +.Fn fuse_add_direntry \ > + "fuse_req_t req" \ > + "char *buf" \ > + "const size_t bufsize" \ > + "const char *name" \ > + "struct stat *stbuf" \ > + "off_t off" > +.Pp > +.Fa buf > +points to the location in the buffer where the new dirent should be added and > +.Fa bufsize > +specifies the remaining size of the buffer. > +.Fa name > +is a NUL-terminated string for the name of the new entry. > +Only the st_ino field and bits 12-15 of the st_mode field from the > +.Fa stbuf > +argument are used. > +All other fields are ignored. > +.Fa off > +is a file system specific offset of the next entry. > +.Pp > +On success, responds with > +.Xr fuse_reply_buf 3 . > +.It Fn readlink "req" "ino" > +Called to read the target of a symbolic link. > +The target must be NUL-terminated. > +.Pp > +On success, responds with > +.Fa fuse_reply_readlink 3 . > +.It Fn release "req" "ino" "ffi" > +Called when there are no more references to the file specified by > +.Fa ino . > +.It Fn releasedir "req" "ino" "ffi" > +Called when there are no more references to the directory specified by > +.Fa ino . > +.It Fn rename "req" "parent" "name" "newparent" "newname" > +Renames the directory entry > +.Fa name > +to > +.Fa newname . > +.Fa newparent > +specifies the inode of the new parent directory. > +.It Fn rmdir "req" "parent" "name" > +Deletes directory > +.Fa name > +from directory > +.Fa parent . > +.It Fn symlink "req" "link" "parent" "name" > +Create a symbolic link with contents > +.Fa link > +in directory > +.Fa parent > +with > +.Fa name > +.Pp > +On success, responds with > +.Xr fuse_reply_entry 3 . > +.It Fn setattr "req" "ino" "attr" "flags" "ffi" > +Called when file attributes are changed such as access permissions or > +the file owner or group via > +.Xr chown 2 > +and > +.Xr chmod 2 > +system calls. > +.Pp > +The flags argument is the bitwise OR of one or more of the bitmasks from the .Fa flags > +following list: Should a phrase like the following be added: following list, requesting that the corresponding fields in .Fa attr be set on .Fa ino : That would explain why the flags are only listed, but not explained individually. > +.Bd -literal > +FUSE_SET_ATTR_MODE > +FUSE_SET_ATTR_UID > +FUSE_SET_ATTR_GID > +FUSE_SET_ATTR_SIZE > +FUSE_SET_ATTR_ATIME > +FUSE_SET_ATTR_MTIME > +FUSE_SET_ATTR_ATIME_NOW > +FUSE_SET_ATTR_MTIME_NOW Start each of these lines with ".Dv ". Yes, that is permissible even inside .Bd -literal. Alternatively, switch to .Bl -tag -width Ds and explain for each individual flag what it does. > +.Ed > +.Pp > +On success, returns the updated attributes with > +.Xr fuse_reply_attr 3 . > +.It Fn setxattr "req" "ino" "name" "value" "size" "flags" > +Not implemented. > +.It Fn statfs "req" "ino" > +Provides file system information. > +.Pp > +On success, responds with > +.Xr fuse_reply_statfs 3 . > +.It Fn unlink "req" "parent" "name" > +Deletes file > +.Fa name > +from directory > +.Fa parent . > +.It Fn write "req" "ino" "buf" "size" "off" "ffi" > +Writes the contents of > +.Fa buf > +to file > +.Fa ino > +at offset > +.Fa off . > +.Pp > +On success, responds with > +.Xr fuse_reply_write 3 . > +.El > +.Sh RETURN VALUES > +Returns .Fn fuse_lowlevel_new returns or this will likely become confusing as soon as a second function is added to this manual page. > a pointer to a newly created > +.Vt struct fuse_session > +on success. > +Returns > +.Dv NULL > +on failure. > +.Sh SEE ALSO > +.Xr fuse_get_context 3 , > +.Xr fuse_mount 3 , Is fuse_opt(3) missing here, or where should users get "args" from? > +.Xr fuse_reply 3 , > +.Xr fuse_session_loop 3 > +.Sh STANDARDS > +The > +.Fn fuse_lowlevel_new > +function conforms to FUSE 2.6. > +.Sh HISTORY > +The > +.Fn fuse_lowlevel_new > +function first appeared in has been available since > +.Ox 7.9 . > +.Sh AUTHORS > +.An Helg Bredow Aq Mt helg@openbsd.org > Index: fuse_reply.3 Maybe fuse_reply_err.3 ? > =================================================================== > RCS file: fuse_reply.3 > diff -N fuse_reply.3 > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ fuse_reply.3 27 Jan 2026 08:44:02 -0000 > @@ -0,0 +1,177 @@ > +.\" $OpenBSD$ > +.\" > +.\" Copyright (c) 2025 Helg Bredow > +.\" > +.\" Permission to use, copy, modify, and distribute this software for any > +.\" purpose with or without fee is hereby granted, provided that the above > +.\" copyright notice and this permission notice appear in all copies. > +.\" > +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > +.\" > +.Dd $Mdocdate: September 23 2025 $ > +.Dt FUSE_REPLY 3 Maybe FUSE_REPLY_ERR 3 ? > +.Os > +.Sh NAME > +.Nm fuse_reply_err , > +.Nm fuse_reply_buf , > +.Nm fuse_reply_attr , > +.Nm fuse_reply_open , > +.Nm fuse_reply_write , > +.Nm fuse_reply_entry , > +.Nm fuse_reply_statfs , > +.Nm fuse_reply_readlink > +.Nd send replies to FUSE requests > +.Sh SYNOPSIS > +.Lb libfuse > +.In fuse_lowlevel.h > +.Ft int > +.Fn fuse_reply_err "fuse_req_t req" "int err" > +.Ft int > +.Fn fuse_reply_buf "fuse_req_t req" "const char *buf" "off_t size" > +.Ft int > +.Fn fuse_reply_attr "fuse_req_t req" "const struct stat *attr" "double attr_timeout" .Fo/.Fa/.Fc really makes such stuff easier on the eye. > +.Ft int > +.Fn fuse_reply_open "fuse_req_t req" "const struct fuse_file_info *fi" > +.Ft int > +.Fn fuse_reply_write "fuse_req_t req" "size_t count" > +.Ft int > +.Fn fuse_reply_entry "fuse_req_t req" "const struct fuse_entry_param *e" > +.Ft int > +.Fn fuse_reply_statfs "fuse_req_t req" "const struct statvfs *stbuf" > +.Ft int > +.Fn fuse_reply_readlink "fuse_req_t req" "char *linkname" > +.Sh DESCRIPTION > +These functions are used in the FUSE low-level API to send responses to > +requests received from the kernel. > +.Pp > +Once called, the request is considered handled and must not be replied to > +again. > +.Bl -tag -width Ds > +.It Fn fuse_reply_err "req" "err" > +Sends an error response to the request Again, if using a list, use the imperative ("Send an error"). > +.Fa req . > +The > +.Fa err > +parameter must be a valid (non-negated) errno. I'd prefer "a valid (non-negated) error number." If you insist of the jargon "errno", it would have to be a valid (non-negated) .Va errno . but that might seem slightly confusing to some because .Va errno usually documents what users can find in that global variable, whereas this sentence is about something the programmer has to put into another place (and not directly into .Va errno), even though the kernel will later move that value there IIUC. > +e.g > +.Er ENOENT , > +.Er EACCES > +. An input line with nothing but a lone dot is an empty roff(7) request and does nothing whatsoever. My suggestion: a valid (non-negated) error number, for example .Er ENOENT or .Er EACCES . If you insist on "e.g.", don't forget the final dot, and don't forget to escape it if it is at the end of an input line: a valid (non-negated) error number, e.g.\& .Er ENOENT > +.Pp > +Valid response for all file system operations. Make this a complete sentence, for example something like: Calling this function is valid for all file system operations. This is a valid response for all file system operations. There are some more cases below. > +.It Fn fuse_reply_buf "req" "buf" "size" > +Sends a buffer > +.Fa buf > +of data of size > +.Fa size Send a buffer .Fa buf of .Fa size bytes of data would be more precise. > +to the kernel in response to > +.Fa req . > +.Pp > +Valid response for > +.Fn read > +and > +.Fn readdir > +operations. > +.It Fn fuse_reply_attr "req" "attr" "attr_timeout" > +Replies with file attributes specified in > +.Fa attr , > +valid for > +.Fa attr_timeout > +seconds. > +The > +.Ox > +kernel does not currently support attribute caching and > +.Fa attr_timeout > +will be ignored. > +.Pp > +Valid response for > +.Fn getattr > +and > +.Fn setattr > +operations. > +.It Fn fuse_reply_open "req" "ffi" > +Sends a response to an open request with the file info structure > +.Fa ffi . > +Only the > +.Fa fh > +member of > +.Fa ffi > +is used. > +.Pp > +Valid response for > +.Fn open > +and > +.Fn opendir > +operations. > +.It Fn fuse_reply_write "req" "count" > +Replies to a write request with the > +.Fa count > +of bytes written. > +.Pp > +Valid response for the > +.Fn write > +operation. > +.It Fn fuse_reply_entry "req" "e" > +Replies to a lookup or other request that resulst in new file creation with s/resulst/results/ > +the details of the new directory entry > +.Fa e , > +which is defined as follows: > +.Bd -literal > +struct fuse_entry_param { > + ino_t ino; /* inode number of the entry */ > + unsigned long generation; /* must be non-zero */ > + struct stat attr; /* attributes of the entry */ > + double attr_timeout; /* ignored */ > + double entry_timeout; /* ignored */ > +}; > +.Ed > +.Pp > +The > +.Ox > +kernel does not currently cache FUSE lookups or attributes and the > +.Fa attr_timeout > +and > +.Fa entry_timeout > +members are ignored. > +.Pp > +Valid response for the > +.Fn lookup , > +.Fn mknod , > +.Fn mkdir , > +.Fn symlink , and > +.Fn link > +operations. > +.It Fn fuse_reply_statfs "req" "stbuf" > +Replies with file system statistics provided in > +.Fa stbuf . > +.Pp > +Valid response for the > +.Fn statfs > +operation. > +.It Fn fuse_reply_readlink "req" "link" > +Replies to a readlink request with the symbolic link target string > +.Fa link . > +The target string cannot contain the NUL character. > +.Pp > +Valid response for the > +.Fn readlink > +operation. > +.El > +.Sh RETURN VALUES > +All functions return 0 on success. > +On failure, they return -errno to indicate the error. ... they retun .Pf \- Va errno > +.Sh SEE ALSO > +.Xr fuse_lowlevel_new 3 > +.Sh STANDARDS > +These library functions conform to FUSE 2.6. > +.Sh HISTORY > +These library functions first appeared in have been available since > +.Ox 7.9 . > +.Sh AUTHORS > +.An Helg Bredow Aq Mt helg@openbsd.org