// SPDX-License-Identifier: MPL-2.0 OR LGPL-3.0-or-later
/*
 * libpathrs: safe path resolution on Linux
 * Copyright (C) 2019-2025 SUSE LLC
 * Copyright (C) 2026 Aleksa Sarai <cyphar@cyphar.com>
 *
 * == MPL-2.0 ==
 *
 *  This Source Code Form is subject to the terms of the Mozilla Public
 *  License, v. 2.0. If a copy of the MPL was not distributed with this
 *  file, You can obtain one at https://mozilla.org/MPL/2.0/.
 *
 * Alternatively, this Source Code Form may also (at your option) be used
 * under the terms of the GNU Lesser General Public License Version 3, as
 * described below:
 *
 * == LGPL-3.0-or-later ==
 *
 *  This program 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 3 of the License, or (at
 *  your option) any later version.
 *
 *  This program 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 General Public License
 *  for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program. If not, see <https://www.gnu.org/licenses/>.
 */


#ifdef __CBINDGEN_ALIGNED
#undef __CBINDGEN_ALIGNED
#endif
#define __CBINDGEN_ALIGNED(n) __attribute__((aligned(n)))


#ifndef LIBPATHRS_H
#define LIBPATHRS_H

/*
 * WARNING: This file was auto-generated by rust-cbindgen. Don't modify it.
 *          Instead, re-generate it with:
 *            % cbindgen -c cbindgen.toml -o include/pathrs.h
 */


#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>

/*
 * Returns whether the given numerical value is a libpathrs error (which can be
 * passed to pathrs_errorinfo()). Users are recommended to use this instead of
 * a bare `<0` comparison because some functions may return a negative number
 * even in a success condition.
 */
#define IS_PATHRS_ERR(ret) ((ret) < __PATHRS_MAX_ERR_VALUE)

/*
 * Used to construct pathrs_proc_base_t values for a PID (or TID). Passing
 * PATHRS_PROC_PID(pid) to pathrs_proc_*() as pathrs_proc_base_t will cause
 * libpathrs to use /proc/$pid as the base of the operation.
 *
 * This is essentially functionally equivalent to prefixing "$pid/" to the
 * subpath argument and using PATHRS_PROC_ROOT.
 *
 * Note that this operation is inherently racy -- the process referenced by this
 * PID may have died and the PID recycled with a different process. In
 * principle, this means that it is only really safe to use this with:
 *
 *  - PID 1 (the init process), as that PID cannot ever get recycled.
 *  - Your current PID (though you should just use PATHRS_PROC_SELF).
 *  - Your current TID (though you should just use PATHRS_PROC_THREAD_SELF),
 *    or _possibly_ other TIDs in your thread-group if you are absolutely sure
 *    they have not been reaped (typically with pthread_join(3), though there
 *    are other ways).
 *  - PIDs of child processes (as long as you are sure that no other part of
 *    your program incorrectly catches or ignores SIGCHLD, and that you do it
 *    *before* you call wait(2) or any equivalent method that could reap
 *    zombies).
 *
 * Outside of those specific uses, users should probably avoid using this.
 */
#define PATHRS_PROC_PID(n) (__PATHRS_PROC_TYPE_PID | (n))

/*
 * A sentinel value to tell `pathrs_proc_*` methods to use the default procfs
 * root handle (which may be globally cached).
 */
#define PATHRS_PROC_DEFAULT_ROOTFD -9 /* (-EBADF) */


/**
 * Bits in `pathrs_proc_base_t` that indicate the type of the base value.
 *
 * NOTE: This is used internally by libpathrs. You should avoid using this
 * macro if possible.
 */
#define __PATHRS_PROC_TYPE_MASK 18446744069414584320ull

/**
 * Bits in `pathrs_proc_base_t` that must be set for `/proc/$pid` values. Don't
 * use this directly, instead use `PATHRS_PROC_PID(n)` to convert a PID to an
 * appropriate `pathrs_proc_base_t` value.
 *
 * NOTE: This is used internally by libpathrs. You should avoid using this
 * macro if possible.
 */
#define __PATHRS_PROC_TYPE_PID 9223372036854775808ull

/**
 * Construct a completely unmasked procfs handle.
 *
 * This is equivalent to [`ProcfsHandleBuilder::unmasked`], and is meant as
 * a flag argument to [`ProcfsOpenFlags`] (the `flags` field in `struct
 * pathrs_procfs_open_how`) for use with pathrs_procfs_open().
 */
#define PATHRS_PROCFS_NEW_UNMASKED 1

/**
 * Indicate what base directory should be used when doing operations with
 * `pathrs_proc_*`. In addition to the values defined here, the following
 * macros can be used for other values:
 *
 *  * `PATHRS_PROC_PID(pid)` refers to the `/proc/<pid>` directory for the
 *    process with PID (or TID) `pid`.
 *
 *    Note that this operation is inherently racy and should probably avoided
 *    for most uses -- see the block comment above `PATHRS_PROC_PID(n)` for
 *    more details.
 *
 * Unknown values will result in an error being returned.
 */
enum pathrs_proc_base_t {
    /**
     * Use /proc. Note that this mode may be more expensive because we have
     * to take steps to try to avoid leaking unmasked procfs handles, so you
     * should use PATHRS_PROC_SELF if you can.
     */
    PATHRS_PROC_ROOT = 18446744067006164835ull,
    /**
     * Use /proc/self. For most programs, this is the standard choice.
     */
    PATHRS_PROC_SELF = 18446744065272536607ull,
    /**
     * Use /proc/thread-self. In multi-threaded programs where one thread has a
     * different CLONE_FS, it is possible for /proc/self to point the wrong
     * thread and so /proc/thread-self may be necessary.
     *
     * NOTE: Using /proc/thread-self may require care if used from languages
     * where your code can change threads without warning and old threads can
     * be killed (such as Go -- where you want to use runtime.LockOSThread).
     */
    PATHRS_PROC_THREAD_SELF = 18446744066171166239ull,
};
typedef uint64_t pathrs_proc_base_t;

typedef struct {
    uint64_t flags;
} pathrs_procfs_open_how;

/**
 * Attempts to represent a Rust Error type in C. This structure must be freed
 * using pathrs_errorinfo_free().
 */
typedef struct __CBINDGEN_ALIGNED(8) {
    /**
     * Raw errno(3) value of the underlying error (or 0 if the source of the
     * error was not due to a syscall error).
     */
    uint64_t saved_errno;
    /**
     * Textual description of the error.
     */
    const char *description;
} pathrs_error_t;

/**
 * Represents the version information of the runtime libpathrs library.
 */
typedef struct {
    /**
     * Pointer to static version string containing the crate version.
     */
    const char * version_string;
} pathrs_version_info_t;

/**
 * The smallest return value which cannot be a libpathrs error ID.
 *
 * While all libpathrs error IDs are negative numbers, some functions may
 * return a negative number in a success scenario. This macro defines the high
 * range end of the numbers that can be used as an error ID. Don't use this
 * value directly, instead use `IS_PATHRS_ERR(ret)` to check if a returned
 * value is an error or not.
 *
 * NOTE: This is used internally by libpathrs. You should avoid using this
 * macro if possible.
 */
#define __PATHRS_MAX_ERR_VALUE -4096

/**
 * Open a root handle.
 *
 * The provided path must be an existing directory.
 *
 * Note that root handles are not special -- this function is effectively
 * equivalent to
 *
 * ```c
 * fd = open(path, O_PATH|O_DIRECTORY);
 * ```
 *
 * # Return Value
 *
 * On success, this function returns a file descriptor that can be used as a
 * root handle in subsequent pathrs_inroot_* operations. The file descriptor
 * will have the `O_CLOEXEC` flag automatically applied.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_open_root(const char *path);

/**
 * "Upgrade" an O_PATH file descriptor to a usable fd, suitable for reading and
 * writing. This does not consume the original file descriptor. (This can be
 * used with non-O_PATH file descriptors as well.)
 *
 * It should be noted that the use of O_CREAT *is not* supported (and will
 * result in an error). Handles only refer to *existing* files. Instead you
 * need to use pathrs_inroot_creat().
 *
 * In addition, O_NOCTTY is automatically set when opening the path. If you
 * want to use the path as a controlling terminal, you will have to do
 * ioctl(fd, TIOCSCTTY, 0) yourself.
 *
 * # Return Value
 *
 * On success, this function returns a file descriptor. The file descriptor
 * will have the `O_CLOEXEC` flag automatically applied.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_reopen(int fd, uint64_t flags);

/**
 * Resolve the given path within the rootfs referenced by root_fd. The path
 * *must already exist*, otherwise an error will occur.
 *
 * All symlinks (including trailing symlinks) are followed, but they are
 * resolved within the rootfs. If you wish to open a handle to the symlink
 * itself, use pathrs_inroot_resolve_nofollow().
 *
 * # Return Value
 *
 * On success, this function returns an O_PATH file descriptor referencing the
 * resolved path.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_resolve(int root_fd, const char *path);

/**
 * pathrs_inroot_resolve_nofollow() is effectively an O_NOFOLLOW version of
 * pathrs_inroot_resolve(). Their behaviour is identical, except that
 * *trailing* symlinks will not be followed. If the final component is a
 * trailing symlink, an O_PATH|O_NOFOLLOW handle to the symlink itself is
 * returned.
 *
 * # Return Value
 *
 * On success, this function returns an O_PATH file descriptor referencing the
 * resolved path.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_resolve_nofollow(int root_fd, const char *path);

/**
 * pathrs_inroot_open() is effectively shorthand for pathrs_inroot_resolve()
 * followed by pathrs_reopen(). If you only need to open a path and don't care
 * about re-opening it later, this can be slightly more efficient than the
 * alternative for the openat2-based resolver as it doesn't require allocating
 * an extra file descriptor. For languages where C FFI is expensive (such as
 * Go), using this also saves a function call.
 *
 * If flags contains O_NOFOLLOW, the behaviour is like that of
 * pathrs_inroot_resolve_nofollow() followed by pathrs_reopen().
 *
 * In addition, O_NOCTTY is automatically set when opening the path. If you
 * want to use the path as a controlling terminal, you will have to do
 * ioctl(fd, TIOCSCTTY, 0) yourself.
 *
 * # Return Value
 *
 * On success, this function returns a file descriptor. The file descriptor
 * will have the `O_CLOEXEC` flag automatically applied.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_open(int root_fd, const char *path, uint64_t flags);

/**
 * Get the target of a symlink within the rootfs referenced by root_fd.
 *
 * NOTE: The returned path is not modified to be "safe" outside of the
 * root. You should not use this path for doing further path lookups -- use
 * pathrs_inroot_resolve() instead.
 *
 * This method is just shorthand for:
 *
 * ```c
 * int linkfd = pathrs_inroot_resolve_nofollow(rootfd, path);
 * if (IS_PATHRS_ERR(linkfd)) {
 *     liberr = fd; // for use with pathrs_errorinfo()
 *     goto err;
 * }
 * copied = readlinkat(linkfd, "", linkbuf, linkbuf_size);
 * close(linkfd);
 * ```
 *
 * # Return Value
 *
 * On success, this function copies the symlink contents to `linkbuf` (up to
 * `linkbuf_size` bytes) and returns the full size of the symlink path buffer.
 * This function will not copy the trailing NUL byte, and the return size does
 * not include the NUL byte. A `NULL` `linkbuf` or invalid `linkbuf_size` are
 * treated as zero-size buffers.
 *
 * NOTE: Unlike readlinkat(2), in the case where linkbuf is too small to
 * contain the symlink contents, pathrs_inroot_readlink() will return *the
 * number of bytes it would have copied if the buffer was large enough*. This
 * matches the behaviour of pathrs_proc_readlink().
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_readlink(int root_fd,
                           const char *path,
                           char *linkbuf,
                           size_t linkbuf_size);

/**
 * Rename a path within the rootfs referenced by root_fd. The flags argument is
 * identical to the renameat2(2) flags that are supported on the system.
 *
 * # Future-proofing
 *
 * This function takes two `root_fd` arguments in order for future extensions
 * to be able to use them, but callers must specify the same *value* for both
 * arguments. (NOTE: This is not referring to the same underlying file, the
 * actual file descriptor number must be identical.)
 *
 * # Return Value
 *
 * On success, this function returns 0.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_rename(int old_root_fd,
                         const char *old_path,
                         int new_root_fd,
                         const char *new_path,
                         uint64_t flags);

/**
 * Remove the empty directory at path within the rootfs referenced by root_fd.
 *
 * The semantics are effectively equivalent to unlinkat(..., AT_REMOVEDIR).
 * This function will return an error if the path doesn't exist, was not a
 * directory, or was a non-empty directory.
 *
 * # Return Value
 *
 * On success, this function returns 0.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_rmdir(int root_fd, const char *path);

/**
 * Remove the file (a non-directory inode) at path within the rootfs referenced
 * by root_fd.
 *
 * The semantics are effectively equivalent to unlinkat(..., 0). This function
 * will return an error if the path doesn't exist or was a directory.
 *
 * # Return Value
 *
 * On success, this function returns 0.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_unlink(int root_fd, const char *path);

/**
 * Recursively delete the path and any children it contains if it is a
 * directory. The semantics are equivalent to `rm -r`.
 *
 * # Return Value
 *
 * On success, this function returns 0.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_remove_all(int root_fd, const char *path);

/**
 * Create a new regular file within the rootfs referenced by root_fd. This is
 * effectively an O_CREAT operation, and so (unlike pathrs_inroot_resolve()),
 * this function can be used on non-existent paths.
 *
 * If you want to ensure the creation is a new file, use O_EXCL.
 *
 * If you want to create a file without opening a handle to it, you can do
 * pathrs_inroot_mknod(root_fd, path, S_IFREG|mode, 0) instead.
 *
 * As with pathrs_reopen(), O_NOCTTY is automatically set when opening the
 * path. If you want to use the path as a controlling terminal, you will have
 * to do ioctl(fd, TIOCSCTTY, 0) yourself.
 *
 * NOTE: Unlike O_CREAT, pathrs_inroot_creat() will return an error if the
 * final component is a dangling symlink. O_CREAT will create such files, and
 * while openat2 does support this it would be difficult to implement this in
 * the emulated resolver.
 *
 * # Return Value
 *
 * On success, this function returns a file descriptor to the requested file.
 * The open flags are based on the provided flags. The file descriptor will
 * have the `O_CLOEXEC` flag automatically applied.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_creat(int root_fd,
                        const char *path,
                        uint64_t flags,
                        unsigned int mode);

/**
 * Create a new directory within the rootfs referenced by root_fd.
 *
 * This is shorthand for pathrs_inroot_mknod(root_fd, path, S_IFDIR|mode, 0).
 *
 * # Return Value
 *
 * On success, this function returns 0.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_mkdir(int root_fd, const char *path, unsigned int mode);

/**
 * Create a new directory (and any of its path components if they don't exist)
 * within the rootfs referenced by root_fd.
 *
 * # Return Value
 *
 * On success, this function returns an O_DIRECTORY file descriptor to the
 * newly created directory.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_mkdir_all(int root_fd, const char *path, unsigned int mode);

/**
 * Create a inode within the rootfs referenced by root_fd. The type of inode to
 * be created is configured using the S_IFMT bits in mode (a-la mknod(2)).
 *
 * # Return Value
 *
 * On success, this function returns 0.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_mknod(int root_fd,
                        const char *path,
                        unsigned int mode,
                        dev_t dev);

/**
 * Create a symlink within the rootfs referenced by root_fd. Note that the
 * symlink target string is not modified when creating the symlink.
 *
 * # Return Value
 *
 * On success, this function returns 0.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_symlink(const char *target,
                          int root_fd,
                          const char *linkpath);

/**
 * Create a hardlink within the rootfs referenced by root_fd. Both the hardlink
 * path and target are resolved within the rootfs.
 *
 * # Future-proofing
 *
 * This function takes two `root_fd` arguments in order for future extensions
 * to be able to use them, but callers must specify the same *value* for both
 * arguments. (NOTE: This is not referring to the same underlying file, the
 * actual file descriptor number must be identical.)
 *
 * The `flags` argument is included for future extensions and must be 0.
 *
 * # Return Value
 *
 * On success, this function returns 0.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_inroot_hardlink(int old_root_fd,
                           const char *old_path,
                           int new_root_fd,
                           const char *new_path,
                           uint64_t flags);

/**
 * Create a new (custom) procfs root handle.
 *
 * This is effectively a C wrapper around [`ProcfsHandleBuilder`], allowing you
 * to create a custom procfs root handle that can be used with other
 * `pathrs_proc_*at` methods.
 *
 * While most users should just use `PATHRS_PROC_DEFAULT_ROOTFD` (or the
 * non-`at` variants of `pathrs_proc_*`), creating an unmasked procfs root
 * handle (using `PATHRS_PROCFS_NEW_UNMASKED`) can be useful for programs that
 * need to operate on a lot of global procfs files. (Note that accessing global
 * procfs files does not *require* creating a custom procfs handle --
 * `pathrs_proc_*` will automatically create a global-friendly handle
 * internally when necessary but will close it immediately after operating on
 * it.)
 *
 * # Extensible Structs
 *
 * The [`ProcfsOpenHow`] (`struct pathrs_procfs_open_how`) argument is
 * designed to be extensible, modelled after the extensible structs scheme used
 * by Linux (for syscalls such as [clone3(2)], [openat2(2)] and other such
 * syscalls). Normally one would use symbol versioning to achieve this, but
 * unfortunately Rust's symbol versioning support is incredibly primitive (one
 * might even say "non-existent") and so this system is more robust, even if
 * the calling convention is a little strange for userspace libraries.
 *
 * In addition to a pointer argument, the caller must also provide the size of
 * the structure it is passing. By providing this information, it is possible
 * for `pathrs_procfs_open()` to provide both forwards- and
 * backwards-compatibility, with size acting as an implicit version number.
 * (Because new extension fields will always be appended, the structure size
 * will always increase.)
 *
 * If we let `usize` be the structure specified by the caller, and `lsize` be
 * the size of the structure internal to libpathrs, then there are three cases
 * to consider:
 *
 * * If `usize == lsize`, then there is no version mismatch and the structure
 *   provided by the caller can be used verbatim.
 * * If `usize < lsize`, then there are some extension fields which libpathrs
 *   supports that the caller does not. Because a zero value in any added
 *   extension field signifies a no-op, libpathrs treats all of the extension
 *   fields not provided by the caller as having zero values. This provides
 *   backwards-compatibility.
 * * If `usize > lsize`, then there are some extension fields which the caller
 *   is aware of but this version of libpathrs does not support. Because any
 *   extension field must have its zero values signify a no-op, libpathrs can
 *   safely ignore the unsupported extension fields if they are all-zero. If
 *   any unsupported extension fields are nonzero, then an `E2BIG` error is
 *   returned. This provides forwards-compatibility.
 *
 * Because the definition of `struct pathrs_procfs_open_how` may open in the
 * future
 *
 * Because the definition of `struct pathrs_procfs_open_how` may change in the
 * future (with new fields being added when headers are updated), callers
 * should zero-fill the structure to ensure that recompiling the program with
 * new headers will not result in spurious errors at run time. The simplest
 * way is to use a designated initialiser:
 *
 * ```c
 *     struct pathrs_procfs_open_how how = {
 *         .flags = PATHRS_PROCFS_NEW_UNMASKED,
 *     };
 * ```
 *
 * or explicitly using `memset(3)` or similar:
 *
 * ```c
 * struct pathrs_procfs_open_how how;
 * memset(&how, 0, sizeof(how));
 * how.flags = PATHRS_PROCFS_NEW_UNMASKED;
 * ```
 *
 * # Return Value
 *
 * On success, this function returns *either* a file descriptor *or*
 * `PATHRS_PROC_DEFAULT_ROOTFD` (this is a negative number, equal to `-EBADF`).
 * The file descriptor will have the `O_CLOEXEC` flag automatically applied.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 *
 * [clone3(2)]: https://www.man7.org/linux/man-pages/man2/clone3.2.html
 * [openat2(2)]: https://www.man7.org/linux/man-pages/man2/openat2.2.html
 */
int pathrs_procfs_open(const pathrs_procfs_open_how *args, size_t size);

/**
 * `pathrs_proc_open` but with a caller-provided file descriptor for `/proc`.
 *
 * Internally, `pathrs_proc_open` will attempt to use a cached copy of a very
 * restricted `/proc` handle (a detached mount object with `subset=pid` and
 * `hidepid=4`). If a user requests a global `/proc` file, a temporary handle
 * capable of accessing global files is created and destroyed after the
 * operation completes.
 *
 * For most users, this is more than sufficient. However, if a user needs to
 * operate on many global `/proc` files, the cost of creating handles can get
 * quite expensive. `pathrs_proc_openat` allows a user to manually manage the
 * global-friendly `/proc` handle. Note that passing a `subset=pid` file
 * descriptor to `pathrs_proc_openat` will *not* stop the automatic creation of
 * a global-friendly handle internally if necessary.
 *
 * In order to get the behaviour of `pathrs_proc_open`, you can pass the
 * special value `PATHRS_PROC_DEFAULT_ROOTFD` (`-EBADF`) as the `proc_rootfd`
 * argument.
 *
 * # Return Value
 *
 * On success, this function returns a file descriptor. The file descriptor
 * will have the `O_CLOEXEC` flag automatically applied.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_proc_openat(int proc_rootfd,
                       pathrs_proc_base_t base,
                       const char *path,
                       uint64_t flags);

/**
 * Safely open a path inside a `/proc` handle.
 *
 * Any bind-mounts or other over-mounts will (depending on what kernel features
 * are available) be detected and an error will be returned. Non-trailing
 * symlinks are followed but care is taken to ensure the symlinks are
 * legitimate.
 *
 * Unless you intend to open a magic-link, `O_NOFOLLOW` should be set in flags.
 * Lookups with `O_NOFOLLOW` are guaranteed to never be tricked by bind-mounts
 * (on new enough Linux kernels).
 *
 * If you wish to resolve a magic-link, you need to unset `O_NOFOLLOW`.
 * Unfortunately (if libpathrs is using the regular host `/proc` mount), this
 * lookup mode cannot protect you against an attacker that can modify the mount
 * table during this operation.
 *
 * NOTE: Instead of using paths like `/proc/thread-self/fd`, `base` is used to
 * indicate what "base path" inside procfs is used. For example, to re-open a
 * file descriptor:
 *
 * ```c
 * fd = pathrs_proc_open(PATHRS_PROC_THREAD_SELF, "fd/101", O_RDWR);
 * if (IS_PATHRS_ERR(fd)) {
 *     liberr = fd; // for use with pathrs_errorinfo()
 *     goto err;
 * }
 * ```
 *
 * # Return Value
 *
 * On success, this function returns a file descriptor. The file descriptor
 * will have the `O_CLOEXEC` flag automatically applied.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_proc_open(pathrs_proc_base_t base, const char *path, uint64_t flags);

/**
 * `pathrs_proc_readlink` but with a caller-provided file descriptor for
 * `/proc`.
 *
 * See the documentation of pathrs_proc_openat() for when this API might be
 * useful.
 *
 * # Return Value
 *
 * On success, this function copies the symlink contents to `linkbuf` (up to
 * `linkbuf_size` bytes) and returns the full size of the symlink path buffer.
 * This function will not copy the trailing NUL byte, and the return size does
 * not include the NUL byte. A `NULL` `linkbuf` or invalid `linkbuf_size` are
 * treated as zero-size buffers.
 *
 * NOTE: Unlike readlinkat(2), in the case where linkbuf is too small to
 * contain the symlink contents, pathrs_proc_readlink() will return *the number
 * of bytes it would have copied if the buffer was large enough*. This matches
 * the behaviour of pathrs_inroot_readlink().
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_proc_readlinkat(int proc_rootfd,
                           pathrs_proc_base_t base,
                           const char *path,
                           char *linkbuf,
                           size_t linkbuf_size);

/**
 * Safely read the contents of a symlink inside `/proc`.
 *
 * As with `pathrs_proc_open`, any bind-mounts or other over-mounts will
 * (depending on what kernel features are available) be detected and an error
 * will be returned. Non-trailing symlinks are followed but care is taken to
 * ensure the symlinks are legitimate.
 *
 * This function is effectively shorthand for
 *
 * ```c
 * fd = pathrs_proc_open(base, path, O_PATH|O_NOFOLLOW);
 * if (IS_PATHRS_ERR(fd)) {
 *     liberr = fd; // for use with pathrs_errorinfo()
 *     goto err;
 * }
 * copied = readlinkat(fd, "", linkbuf, linkbuf_size);
 * close(fd);
 * ```
 *
 * # Return Value
 *
 * On success, this function copies the symlink contents to `linkbuf` (up to
 * `linkbuf_size` bytes) and returns the full size of the symlink path buffer.
 * This function will not copy the trailing NUL byte, and the return size does
 * not include the NUL byte. A `NULL` `linkbuf` or invalid `linkbuf_size` are
 * treated as zero-size buffers.
 *
 * NOTE: Unlike readlinkat(2), in the case where linkbuf is too small to
 * contain the symlink contents, pathrs_proc_readlink() will return *the number
 * of bytes it would have copied if the buffer was large enough*. This matches
 * the behaviour of pathrs_inroot_readlink().
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_proc_readlink(pathrs_proc_base_t base,
                         const char *path,
                         char *linkbuf,
                         size_t linkbuf_size);

/**
 * Retrieve error information about an error id returned by a pathrs operation.
 *
 * Whenever an error occurs with libpathrs, a negative number describing that
 * error (the error id) is returned. pathrs_errorinfo() is used to retrieve
 * that information:
 *
 * ```c
 * fd = pathrs_inroot_resolve(root, "/foo/bar");
 * if (IS_PATHRS_ERR(fd)) {
 *     // fd is an error id
 *     pathrs_error_t *error = pathrs_errorinfo(fd);
 *     // ... print the error information ...
 *     pathrs_errorinfo_free(error);
 * }
 * ```
 *
 * Once pathrs_errorinfo() is called for a particular error id, that error id
 * is no longer valid and should not be used for subsequent pathrs_errorinfo()
 * calls.
 *
 * Error ids are only unique from one another until pathrs_errorinfo() is
 * called, at which point the id can be reused for subsequent errors. The
 * precise format of error ids is completely opaque and they should never be
 * compared directly or used for anything other than with pathrs_errorinfo().
 *
 * Error ids are not thread-specific and thus pathrs_errorinfo() can be called
 * on a different thread to the thread where the operation failed (this is of
 * particular note to green-thread language bindings like Go, where this is
 * important).
 *
 * # Return Value
 *
 * If there was a saved error with the provided id, a pathrs_error_t is
 * returned describing the error. Use pathrs_errorinfo_free() to free the
 * associated memory once you are done with the error.
 */
pathrs_error_t *pathrs_errorinfo(int err_id);

/**
 * Free the pathrs_error_t object returned by pathrs_errorinfo().
 */
void pathrs_errorinfo_free(pathrs_error_t *ptr);

/**
 * Return the version information of the libpathrs library at runtime.
 *
 * This API is designed to be extensible. The caller must pass a pointer to a
 * contiguous buffer `dst` that is large enough to store `dst_size` bytes. This
 * buffer is then filled with version information. If the library has less data
 * than the given buffer size, the trailing bytes are zero-filled. If the
 * library has more data than the given buffer size the data is truncated and
 * (if the truncated data contained non-zero values), the return value
 * indicates what buffer size is needed to contain all version data.
 *
 * # Return Value
 *
 * On success, this function returns `0` or (if the version data was truncated)
 * a positive integer that is the size of the buffer needed to store all data.
 *
 * If an error occurs, this function will return a negative error code. To
 * retrieve information about the error (such as a string describing the error,
 * the system errno(7) value associated with the error, etc), use
 * pathrs_errorinfo().
 */
int pathrs_version(pathrs_version_info_t *dst, size_t dst_size);

#endif  /* LIBPATHRS_H */

#ifdef __CBINDGEN_ALIGNED
#undef __CBINDGEN_ALIGNED
#endif
