     [file version.texi not included]

BookmarkFS User Manual
**********************

This manual is for BookmarkFS, version VERSION.

     Copyright © 2024  CismonX <admin@cismon.net>

     Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.  A copy of the license is included in the section entitled ``GNU Free Documentation License''.

Overview
********

BookmarkFS is a FUSE-based pseudo-filesystem which provides an interface to the bookmark data of web browsers.

Currently, the following browsers (and their derivatives) are supported:

  * Firefox

  * Chromium

To install BookmarkFS, refer to the INSTALL.md file under the root directory of the project codebase.

BookmarkFS is free software, distributed under the terms of the GNU General Public License, either version 3, or any later version of the license. You should have received a copy of the GNU General Public License along with BookmarkFS.  If not, see https://www.gnu.org/licenses/.

BookmarkFS is hosted on Savannah (https://savannah.nongnu.org/projects/bookmarkfs). Write to the mailing lists (https://savannah.nongnu.org/mail/?group=bookmarkfs) for bug reports, feature requests, and other discussions.

Porting BookmarkFS
==================

Currently, BookmarkFS only runs on GNU/Linux and FreeBSD.

Although BookmarkFS sticks hard to POSIX and avoids using platform-specific features, porting it to other operating systems is not trivial.

The major pitfall is the FUSE (https://docs.kernel.org/filesystems/fuse.html) dependency. Generally speaking, FUSE is Linux-only. FreeBSD partially implements the FUSE protocol in its kernel, to the extent that BookmarkFS is mostly usable. However, that's not the case for other operating systems.

For example, OpenBSD implements its own FUSE protocol, which is incompatible with the Linux one. While OpenBSD does provide a libfuse-compatible library, however, it only covers the high-level API, and BookmarkFS uses the low-level API (https://libfuse.github.io/doxygen/fuse__lowlevel_8h.html). For a similar reason, WinFsp (https://github.com/winfsp/winfsp) won't work if you're trying to port BookmarkFS to Microsoft Windows.

Other notable portability issues:

Sandboxing
     Not all operating system kernels provide sandboxing mechanisms similar to Linux and FreeBSD.

     If not supported, operations that require sandboxing should fail. Users should not be provided with a false sense of security. If they wish, they could pass a -o no_sandbox option to explicitly disable sandboxing.

     Also see Sandboxing.

Limitations on FreeBSD
======================

Currently, the FreeBSD fusefs(5) implementation does not support FUSE_IOCTL. All custom ioctl(2) calls on a FUSE filesystem fail with ENOTTY without the requests being sent to the FUSE server.

Thus, BookmarkFS features that depend on ioctl() do not work on FreeBSD, which includes:

  * Permute Directory Entries

  * Online Filesystem Check

Meanwhile, FreeBSD does not support FUSE_READDIRPLUS and directory entry caching, which makes listing directory entries less efficient.

Sandboxing
==========

A BookmarkFS backend can be instructed to enter a sandboxed state, where it irrevocably relinquishes most access to the system resources that it's not supposed to touch. For example:

  * access local files other than the bookmark storage

  * establish socket connections

  * execute other files

This mechanism reduces the attack surface for exploit, if a vulnerability is discovered in BookmarkFS and/or its dependencies. However, it only deals with untrusted input, and cannot help if the operating system has already been compromised.

Examples of what ``untrusted input'' may include:

  * Bookmark storage not created by the user with a trusted program (e.g., obtained from some random person on the internet).

  * Filesystem calls from untrusted programs. The program may be running in an isolated environment, but it has a chance to escape if BookmarkFS can be exploited.

On Linux, sandboxing is achieved using seccomp(2) and landlock(7). On FreeBSD, capsicum(4) is used.

The Utility Library
===================

The BookmarkFS Utility Library implements various common utility functions. It is used internally by most of BookmarkFS's components, including the backends (see Backends) and the mount.bookmarkfs program.

Typically, the library is built into a shared object, and installed as:

     ${LIBDIR}/bookmarkfs_util${SHLIB_SUFFIX}

${libdir}
     Presumably ${PREFIX}/lib. See The Uniform Naming Scheme (GNU Automake).

${shlib_suffix}
     The common filename extension for shared library files on the current platform (e.g., .so on GNU/Linux and FreeBSD).

Public headers are installed under ${PKGINCLUDEDIR} (presumably ${PREFIX}/include/bookmarkfs). To use the library functions, include the following headers as needed:

hash.h
     Non-cryptographic hash function.

hashmap.h
     A simple hashtable implementation.

prng.h
     Non-cryptographic pseudo-random number generator.

sandbox.h
     A simple sandbox implementation. See Sandboxing.

version.h
     Get version and feature information of the library.

watcher.h
     Single-file filesystem watcher.

Usage of the library functions is rather simple and straightforward, thus there's currently no dedicated manual sections. Refer to the comments in the corresponding header files for details.

Programs
********

BookmarkFS ships with programs that provide users with a command-line interface to create, mount, fix, and manage BookmarkFS filesystems.

Those programs do not work on their own, and usually require a ``backend'' for low-level functionalities. See Backends.

mount.bookmarkfs
================

The mount.bookmarkfs program mounts a BookmarkFS filesystem.

     mount.bookmarkfs [OPTIONS] SRC TARGET

src
     The bookmark storage, presumably the pathname of a regular file that contains bookmark data.

     The exact interpretation of this option is backend-defined.

target
     Pathname of the directory where the filesystem shall be mounted to (i.e., the ``mountpoint'').

Files under the filesystem are assigned ownership according to the effective user ID and group ID of the calling process. On FreeBSD, you may wish to set the vfs.usermount sysctl(8) to 1 for unprivileged mounts.

To unmount a BookmarkFS filesystem, run fusermount3(1) or umount(8) on TARGET. The daemon process will automatically dismount the filesystem upon SIGINT or SIGTERM receipt, however, it only works in non-sandbox mode.

Options:

-o backend=NAME
     The backend used by the filesystem (see Backends). This option is mandatory.

     Alternatively, NAME could be specified in LIB_PATH:SYM_NAME format, where:

     lib_path
          Presumably the pathname to the backend library. Its exact interpretation is equivalent to the first argument for dlopen(3p).

     sym_name
          See Backend Symbol Name Format.

-o @KEY[=VALUE]
     A backend-specific option. This option can be provided multiple times.

-o accmode=MODE
     File access mode. Defaults to 0700.

     This option applies to both directories and regular files. Execution bits on regular files are masked off.

     Should be used in combination with -o allow_other for other users to access the files.

-o ctime
     Maintain last status change time instead of last modification time.

     Usually, a bookmark's ``modification time'' attribute behaves differently from both mtime and ctime. In Chromium, for instance, when a bookmark is renamed, neither itself nor the parent directory changes timestamp accordingly.

     BookmarkFS do not follow browser behavior here, and instead try to stay compatible with POSIX. Since a bookmark has only one ``modification time'' attribute instead of two, the user has to choose which one to maintain:

     Last modification time
          ctime only updates when mtime does.

     Last status change time
          ctime updates normally; mtime always updates when ctime does, even if the file content is not modified.

          This behavior may be inefficient, but makes applications that depend on ctime less fragile.

     The kernel may automatically update and cache ctime, making it more ``correct'' than what we expect. However, this behavior should not be relied upon.

-o eol
     Add a newline (ASCII LF character) to the end of each file.

     Before writing the file content back to the backend, a trailing newline is automatically removed (if one exists).

-o file_max=BYTES
     Max file size limit. Defaults to 32768.

     This limit also applies to extended attribute values.

-o no_sandbox
     Do not enable sandboxing features (see Sandboxing).

-o no_landlock
     Do not use Landlock for sandboxing. This option is ignored on non-Linux platforms.

     Without Landlock, sandboxing offers less security. However, Landlock is a rather new feature (requires kernel version 5.13 or later), thus we provide an option to disable it separately.

-F
     Stay in the foreground, do not daemonize.

-h
     Print help text, and then exit.

-V
     Print version and feature information, and then exit.

Unrecognized options specified with -o are passed to libfuse (and subsequently to the kernel, if applicable) as-is. Notable options:

-o rw
     Mount the filesystem read/write.

          Always backup the bookmark storage before mounting it read/write, or risk losing your data!

     By default, the filesystem is mounted read-only. This behavior won't change in the future, due to the hackish nature of most BookmarkFS backends.

     When mounted read/write, other process must not write to the underlying bookmark storage, otherwise data corruption may occur.

-o debug
     Set libfuse log level to FUSE_LOG_DEBUG. Log messages related to each FUSE request will be printed to standard error.

-o fsname=NAME
     Name for the filesystem. Defaults to the backend name.

     This name is equivalent to the fs_spec field in fstab(5), and appears as the SOURCE column in findmnt(8) output.

-o atime,diratime,relatime
     These options (and other atime-related ones) are ignored.

     BookmarkFS only supports noatime mounts, since the ``last access time'' attribute of a bookmark necessarily means ``the last time it was accessed from the browser''. As a bookmark management tool independent from the browser, BookmarkFS should never update that time automatically.

     Nonetheless, the user is still allowed to explicitly update atime (e.g., with futimens(3p)).

-o auto_unmount
     Instruct libfuse to fork-exec a helper process, which automatically dismounts the filesystem when the filesystem daemon terminates without unmounting, so that the user don't have to manually dismount the inactive filesystem. See mount.fuse3(8) for details.

     This option is helpful when sandboxing is enabled, especially when the -F option is given, since a sandboxed process can neither umount(2) nor fork-exec.

     Currently, this option is not available on FreeBSD.

fsck.bookmarkfs
===============

The fsck.bookmarkfs program checks and optionally repairs a BookmarkFS filesystem.

     fsck.bookmarkfs [OPTIONS] PATHNAME

Filesystem check on BookmarkFS has a different purpose compared to on-disk filesystems. See Filesystem Check.

Depending on the options specified, filesystem check works either in online or offline mode:

Online Mode
     In online mode, fsck is performed on a mounted BookmarkFS filesystem using ioctl(). See Online Filesystem Check.

     The PATHNAME argument refers to the directory to operate on.

Offline Mode
     In offline mode, fsck is performed directly on the bookmark storage via the corresponding backend.

     The PATHNAME argument is the path to the bookmark storage, equivalent to the SRC argument given to mount.bookmarkfs.

     Alternatively, PATHNAME could be specified in SRC:DIR format, where DIR refers to the directory to operate on, relative to the root directory of the subsystem.

Options:

-o backend=NAME
     The backend used by the filesystem. See Backends.

     Value of NAME could be specified in an alternative format. See Alternative Backend Name.

     If this option is not provided, or NAME is empty, performs online fsck. Otherwise perform offline fsck.

-o @KEY[=VALUE]
     A backend-specific option. This option can be provided multiple times.

-o handler=NAME
     The handler for resolving errors found during fsck (see Filesystem-Check Handlers).

     Alternatively, NAME could be specified in LIB_PATH:SYM_NAME format, where:

     lib_path
          Presumably the pathname to the fsck handler library. Its exact interpretation is equivalent to the first argument for dlopen(3p).

     sym_name
          See Filesystem-Check Handler Symbol Name Format.

     If this option is not provided, or NAME is empty, a built-in handler will be used. See Built-in Handler.

-o %KEY[=VALUE]
     A handler-specific option. This option can be provided multiple times.

-o repair
     Attempt to repair errors found during fsck.

          Always backup the bookmark storage before repairing, or risk losing your data!

-o rl_app=NAME
     Readline application name in interactive mode. Defaults to fsck.bookmarkfs.

     See Conditional Init Constructs (GNU Readline Library).

-o type=bookmark|tag|keyword
     Subsystem type (see Filesystem Hierarchy). Defaults to bookmark.

     This option is ignored when performing online fsck.

-i
     Enable interactive mode.

-R
     Perform fsck on subdirectories recursively.

     This option is ignored when performing fsck on tags or keywords.

-o no_sandbox
     Do not enable sandboxing features. See Sandboxing.

-o no_landlock
     Do not use Landlock for sandboxing. This option is ignored on non-Linux platforms.

     Also see Disabling Landlock.

-h
     Print help text, and then exit.

-V
     Print version and feature information, and then exit.

mkfs.bookmarkfs
===============

The mkfs.bookmarkfs program creates a new BookmarkFS filesystem.

     mkfs.bookmarkfs [OPTIONS] PATHNAME

pathname
     The underlying bookmark storage for the new filesystem.

     This option is equivalent to the SRC argument for mount.bookmarkfs.

Options:

-o backend=NAME
     The backend used by the filesystem (see Backends). This option is mandatory.

     Value of NAME could be specified in an alternative format. See Alternative Backend Name.

-o @KEY[=VALUE]
     A backend-specific option. This option can be provided multiple times.

-o force
     If the file referred to by PATHNAME already exists, overwrite it.

-h
     Print help text, and then exit.

-V
     Print version and feature information, and then exit.

bookmarkctl
===========

The bookmarkctl program is a command-line wrapper for various I/O controls of a BookmarkFS filesystem.

     bookmarkctl SUBCMD [ARGS]

Sub-commands:

permd
     Rearranges the order of the directory entries obtained from readdir(). See Permute Directory Entries.

          bookmarkctl permd PATHNAME OP NAME1 NAME2

     pathname
          Path to the directory.

     name1

     name2
          Filename of entries under the directory.

     op
          Operation to perform on the directory:

          swap
               Exchange the positions of the directory entries represented by NAME1 and NAME2.

          move-before
               Move the directory entry represented by NAME1 to the position just before the one represented by NAME2.

          move-after
               Move the directory entry represented by NAME1 to the position just after the one represented by NAME2.

fsck
     Check for errors within a BookmarkFS filesystem. See Filesystem Check.

          bookmarkctl fsck PATHNAME OP

     pathname
          Path to the directory to perform checks on.

     op
          Operation to perform on the directory:

          list
               Display a list of errors found under the given directory. Will not recurse into subdirectories.

     For the full fsck functionalities, see fsck.bookmarkfs.

help
     Print help text, and then exit.

version
     Print version information, and then exit.

The Filesystem
**************

When a BookmarkFS filesystem is mounted using the mount.bookmarkfs program, a daemon process acts as a proxy between the kernel (which relays filesystem requests to FUSE requests) and the backend (which manipulates actual bookmark data, see Backends), thus providing POSIX (and platform-specific) filesystem API access to bookmarks.

BookmarkFS is designed in the hope that web browser bookmarks can be managed flexibly using a combination of existing software, without having to ``reinvent the wheel''. However, like most other pseudo-filesystems, it cannot be considered fully POSIX-compliant. Users should be aware of the limitations when using BookmarkFS.

Filesystem Hierarchy
====================

BookmarkFS has multiple subsystems. Each one appears as a directory under the mountpoint:

     ${MOUNTPOINT}/bookmarks
     ${MOUNTPOINT}/tags
     ${MOUNTPOINT}/keywords

If the backend does not support a subsystem, the corresponding directory does not exist.

Currently all subsystem definitions are hard-coded within the mount.bookmarkfs program, and cannot be extended by the backend.

Bookmarks
---------

The ``bookmarks'' subsystem maintains the hierarchical structure, names, URLs and other information of a bookmark storage.

     ${MOUNTPOINT}/bookmarks/${BOOKMARK_DIR...}/${BOOKMARK}

Each bookmark folder name appears as the filename for directory ${BOOKMARK_DIR}, and each ${BOOKMARK} is a regular file that refers to a bookmark.

The name of a bookmark file is usually the ``bookmark title'', which is the name that appears in the browser's bookmark manager. The content of a bookmark file is usually the URL associated with the bookmark.

Not all bookmark names can be represented as a filename. For a bookmark or bookmark folder with an invalid name, the corresponding file is not visible to lookups and readdir() calls. To deal with such bookmarks, see Filesystem Check; or you can instruct the backend to identify bookmarks using GUIDs instead of titles (and then access the titles via extended attributes).

Some file attributes are used to represent bookmark metadata:

st_ino
     ID of the bookmark (stored as lower bits).

st_size
     Length of the bookmark URL in bytes. Always 0 for directories.

st_atim
     Last access time of the bookmark.

st_mtim
     Last modification time of the bookmark. See Last Modification/Change Time.

Additional information of a bookmark or bookmark folder can be accessed via the extended attributes of the corresponding file, for backends that supports it. See Extended Attributes.

Tags
----

The ``tags'' subsystem maintains a many-to-many mapping between bookmarks and their alternative names.

     ${MOUNTPOINT}/tags/${TAG_DIR}/${BOOKMARK}

Each tag name appears as the filename for directory ${TAG_DIR}, and ${BOOKMARK} is a hard link to the bookmark file. A bookmark directory cannot be associated with a tag.

If multiple bookmark files with identical names are both associated with a tag, it is unspecified which one appears as an entry for the tag directory. However, consecutive lookups and readdir()s should produce consistent results for that file, provided that it is not renamed or deleted.

Tag files behave differently from traditional hard links. If the original bookmark file is renamed or deleted, it may also change accordingly. It may even link to another file that was previously shadowed. Applications should tread lightly if they wish to cache tag directory entries.

To associate a bookmark with a tag, use link(3p):

     fd = open("tags/gnu/readline", O_CREAT | O_WRONLY, 0600);
     // Oops, fd == -1, errno == EPERM

     fd = link("bookmarks/other/readline", "tags/gnu/readline", 0);
     // OK!

Make sure that the two files have identical names, otherwise link() fails with EPERM.

Keywords
--------

The ``keywords'' subsystem maintains a one-to-one mapping between bookmarks and their alternative names, independent from tag names.

     ${MOUNTPOINT}/keywords/${KEYWORD_NAME}

Each keyword name appears as the filename for regular file ${KEYWORD_NAME}, which is a hard link to the bookmark file. A bookmark directory cannot be associated with a keyword.

To associate a bookmark with a keyword, use link() like we do with tags. If the original file is already associated with another keyword, link() fails with EEXIST.

Error Codes
===========

When a filesystem operation fails, the kernel returns an error code for the system call. In addition to common error codes, there are a few more in BookmarkFS:

EPERM
     Attempting to perform an unsupported operation. For example:

       * chmod(), chown(), and other operations that make no sense for web browser bookmarks.

       * Moving a file across subsystems.

       * Creating a bookmark file with a name that is not valid UTF-8 (on Chromium backend).

EIO
     An unexpected internal error occurred, likely due to a bug in BookmarkFS, or a corruption in the bookmark storage.

     When this error occurs, a log message describing the situation may be printed to the standard error of the daemon process. Sometimes this error comes from the kernel-side FUSE implementation, and there's no error message.

     Once this error occurs, behavior of any further operations on the filesystem is undefined.

ESTALE
     The file associated with the file descriptor no longer exists.

     The error may occur when the underlying bookmark storage has been modified by another process (e.g., a web browser) after opening a file.

     If the filesystem is mounted in exclusive mode, this error should not occur.

Other BookmarkFS-specific errors may occur. See the corresponding manual section for details.

Extended Attributes
===================

BookmarkFS uses extended attributes to manage additional information associated with a bookmark.

Extended attributes is a platform-specific feature. On Linux, see xattr(7). On FreeBSD, see extattr(2).

All BookmarkFS extended attributes fall under the ``user'' namespace, which means they have a user. name prefix on Linux, and should be accessed with EXTATTR_NAMESPACE_USER on FreeBSD. All attributes have a bookmarkfs. name prefix.

For example, to get the GUID of a bookmark file (Firefox backend):

     // Linux
     len = fgetxattr(fd, "user.bookmarkfs.guid", buf, sizeof(buf));

     // FreeBSD
     len = extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, "bookmarkfs.guid",
             buf, sizeof(buf));

BookmarkFS does not define any common attributes, neither can users create arbitrary ones. The backend decides which attributes are available during initialization, and all bookmark files share the same set of attributes.

Directory Entries
=================

The . and .. entries
     POSIX consider . and .. as ``special'' filenames, which must refer to the current and parent directory, if they exist.

     These entries are optional, and BookmarkFS does not support them, for the sake of simplicity. Additionally, bookmarks with such names are hidden from the filesystem until fixed with fsck (see Filesystem Check).

Ordering of directory entries
     POSIX does not specify the ordering of the directory entries retrieved from the directory stream using readdir(3p). It only guarantees that if an entry is not added or removed from the directory after the most recent call to opendir(3p) or rewinddir(3p), that entry is returned once and only once.

     This allows filesystem implementations to organize directory entries in a more relaxed manner. There could be extra overhead to maintain a predictable ordering of directory entries, since they may not have a linear structure on modern on-disk filesystems (e.g., ext4 uses htree (https://docs.kernel.org/filesystems/ext4/dynamic.html#hash-tree-directories) for large directories).

     As for users of a filesystem, the order of directory entries generally does not matter. If they care, they can add a prefix to the filename, and let the application do the sorting.

     However, the order of which a bookmark entry appears in the web browser sometimes does matter. In BookmarkFS, it is guaranteed to be equivalent to the directory traversal order. New entries are appended to the end; removed entries do not affect the order of other entries.

     To change the ordering of directory entries, see Permute Directory Entries.

Permute Directory Entries
-------------------------

BookmarkFS provides an I/O control for rearranging directory entries:

     #include <bookmarkfs/ioctl.h>

     int ioctl (int dirfd, BOOKMARKFS_IOC_PERMD,
             struct bookmarkfs_permd_data const *argp);

The bookmarkfs_permd_data structure is defined as:

     struct bookmarkfs_permd_data {
         enum bookmarkfs_permd_op op;

         char name1[NAME_MAX + 1];
         char name2[NAME_MAX + 1];
     };

The op field denotes the operation to perform on the directory:

BOOKMARKFS_PERMD_OP_SWAP
     Exchange the positions of the directory entries represented by name1 and name2.

BOOKMARKFS_PERMD_OP_MOVE_BEFORE
     Move the directory entry represented by name1 to the position just before the one represented by name2.

BOOKMARKFS_PERMD_OP_MOVE_AFTER
     Move the directory entry represented by name1 to the position just after the one represented by name2.

On success, ioctl() returns 0. Otherwise, it returns -1 and sets errno:

EACCES
     Write or search permission is denied for the directory.

EINVAL
     op is not one of the values defined in enum bookmarkfs_permd_op.

EINVAL
     name1 or name2 is not a valid filename (e.g., empty string; contains / character).

ENOENT
     The directory does not contain entries named name1 or name2.

ENOTTY
     The kernel does not support FUSE_IOCTL. See Limitations on FreeBSD.

EPERM
     The backend does not support rearranging entries for this directory.

To ensure that the order change is visible to further readdir() calls, fsync() or close() the directory.

Filesystem Check
================

On-disk filesystems may suffer from data corruption due to power loss or hardware failures, thus they usually provide a ``filesystem check'' mechanism to detect and fix those problems.

As a pseudo-filesystem, BookmarkFS does not check for data integrity, and ``filesystem check'' is given a new purpose: To check if a bookmark name is valid as a filename, and ``repair'' (rename) it if it isn't.

A POSIX-compliant filesystem has various restrictions regarding filenames:

  * must not contain / characters

  * must not be empty or longer than NAME_MAX

  * must not duplicate with another file in the same directory

  * . and .. must refer to the current and parent directory

It is commonplace for bookmark names to not meet such criteria, thus a filesystem check is often necessary when switching to BookmarkFS from another bookmark management software.

Online Filesystem Check
-----------------------

To perform filesystem check on a mounted BookmarkFS filesystem, use the following I/O controls:

     #include <bookmarkfs/ioctl.h>

     int ioctl (int dirfd, BOOKMARKFS_IOC_FSCK_NEXT,
             struct bookmarkfs_fsck_data *argp);

     int ioctl (int dirfd, BOOKMARKFS_IOC_FSCK_APPLY,
             struct bookmarkfs_fsck_data *argp);

     int ioctl (int dirfd, BOOKMARKFS_IOC_FSCK_REWIND);

The bookmarkfs_fsck_data structure is defined as:

     struct bookmarkfs_fsck_data {
         uint64_t id;
         uint64_t extra;
         char     name[NAME_MAX + 1];
     };

Filesystem-check commands:

BOOKMARKFS_IOC_FSCK_NEXT

     Find the next bookmark with invalid name under the directory.

     On success, ioctl() updates ARGP, and returns one of the values defined in enum bookmarkfs_fsck_result:

     BOOKMARKFS_FSCK_RESULT_END
          There are no more bookmarks with invalid name under the directory.

          Fields in ARGP have unspecified values.

     BOOKMARKFS_FSCK_RESULT_NAME_DUPLICATE
          The bookmark name duplicates with another bookmark which appears earlier in the directory stream.

          Value of extra is the ID of the other bookmark.

     BOOKMARKFS_FSCK_RESULT_NAME_BADCHAR
          The bookmark contains a bad character (i.e., the ASCII / character).

          Value of extra is the byte offset where the bad character first appears in name.

     BOOKMARKFS_FSCK_RESULT_NAME_BADLEN
          The bookmark name is either longer than NAME_MAX, or an empty string.

          Value of extra is the length of the bookmark name, and name is truncated if longer than NAME_MAX.

     BOOKMARKFS_FSCK_RESULT_NAME_DOTDOT
          The bookmark name is either . or ...

          Value of extra is unspecified.

     On failure, ioctl() returns -1, and sets errno.

BOOKMARKFS_IOC_FSCK_APPLY
     ``Repair'' a bookmark by renaming it.

     The id field must be set to a value previously obtained from BOOKMARKFS_IOC_FSCK_NEXT with the same DIRFD, otherwise the behavior is undefined.

     The name field should be set to the new name for the bookmark. The extra field is unused.

     On success, ioctl() updates ARGP, and returns one of the values defined in enum bookmarkfs_fsck_result, like with BOOKMARKFS_IOC_FSCK_NEXT. Additionally, it may also return:

     BOOKMARKFS_FSCK_RESULT_NAME_INVALID
          The new name is not a valid bookmark name.

          Value of extra is a backend-specific reason code explaining why the bookmark name is invalid. It may equal to one of the following predefined values:

          BOOKMARKFS_NAME_INVALID_REASON_NOTUTF8
               The name is not valid UTF-8.

     On failure, ioctl() returns -1, and sets errno:

     EACCES
          Write or search permission is denied for the directory.

     To ensure that the rename is visible to further lookups and readdir() calls, fsync() or close() the directory.

BOOKMARKFS_IOC_FSCK_REWIND
     Reset the fsck state for the directory.

     Further BOOKMARKFS_IOC_FSCK_NEXT requests will start from the beginning of the directory stream.

     On success, ioctl() returns 0. Otherwise, it returns -1, and sets errno.

Common error codes for all filesystem-check ioctls:

ENOTTY
     The kernel does not support FUSE_IOCTL. See Limitations on FreeBSD.

EPERM
     The backend does not support fsck for this directory.

Backends
********

In BookmarkFS, each backend provides a way to manipulate a certain kind of application bookmarks.

Typically, backends are built into shared libraries, and are installed as:

     ${PKGLIBDIR}/backend_${NAME}${SHLIB_SUFFIX}

${pkglibdir}
     Presumably ${PREFIX}/lib/bookmarkfs. See The Uniform Naming Scheme (GNU Automake).

${name}
     The backend name, equivalent to the value given to the -o backend=NAME option of frontend programs.

${shlib_suffix}
     The common filename extension for shared library files on the current platform (e.g., .so on GNU/Linux and FreeBSD).

Firefox Backend
===============

The Firefox backend provides access to the bookmark data of the web browser Mozilla Firefox (https://www.mozilla.org/en-US/firefox/new/) and its derivatives, notably Tor Browser (https://www.torproject.org/) and Librewolf (https://librewolf.net/).

Firefox bookmarks are stored in a SQLite database under the profile directory:

     ~/.mozilla/firefox/${PROFILE_NAME}/places.sqlite

When mounting the filesystem, this pathname shall be passed as the SRC argument (see mount.bookmarkfs). Actual path for the profile directories may differ across distributions.

Backend name: firefox.

Backend-specific options (mount.bookmarkfs only):

filename=title|guid
     Whether to use the bookmark title or GUID as the bookmark file name. Defaults to title.

     A bookmark GUID is a base64url-encoded (https://datatracker.ietf.org/doc/html/rfc4648#section-5) 128-bit string uniquely associated with a bookmark or bookmark folder.

     When creating a new file:

     title
          The GUID is randomly generated by the backend.

     guid
          The filename must be a valid GUID, and must not duplicate with other files on the same filesystem, otherwise open() or mkdir() fails with EPERM.

          Also set the GUID string as the bookmark title.

lock=exclusive|normal
     The database connection locking mode for the bookmark storage. Defaults to normal when the filesystem is mounted read-only, exclusive otherwise.

     This option corresponds to the locking_mode (https://www.sqlite.org/pragma.html#pragma_locking_mode) pragma on SQLite. With lock=exclusive, other process cannot access the bookmark storage until the filesystem is dismounted.

     The Firefox browser holds an exclusive lock on the database by default. If you wish to mount the bookmarks while keeping the browser session open, set the storage.sqlite.exclusiveLock.enabled browser preference to false.

assume_title_distinct
     If this options is provided, the backend assumes that bookmark names are distinct under the same bookmark folder. This option is ignored with filename=guid.

     This option may improve readdir() performance, however, making a false assumption results in a directory entry with duplicate names. It is recommended to perform a full filesystem check (see Filesystem Check) on the bookmark storage before mounting with this option.

If launched from fsck.bookmarkfs, all backend-specific options are ignored, and always enforces the lock=exclusive option.

Backend-specific options (mkfs.bookmarkfs only):

date_added=TIMESTAMP
     File creation time for the predefined bookmark directories. Defaults to the current time.

     Format of TIMESTAMP is equivalent to the date_added extended attribute (see below).

Extended attributes:

title
     The bookmark title. Only available with filename=guid.

     This value is allowed be set to any string that does not contain a NUL character, and does not have to be valid as a filename.

guid
     The bookmark GUID. Only available with filename=title.

description
     An arbitrary text associated with the bookmark.

     Usually, when bookmarking a page in the browser, this value is set to the content attribute of the <meta> element whose name is description.

date_added
     The bookmark creation time.

     Value is a decimal integer representing number of microseconds since the Unix epoch.

Notable limitations:

  * Due to the way bookmarks are associated with the URLs, if multiple bookmarks share the same URL, changing the last access time of a bookmark may affect that of other bookmarks.

Chromium Backend
================

The Chromium backend provides access to the bookmark data of the web browser Chromium (https://www.chromium.org/Home/) and its derivatives, notably ungoogled-chromium (https://github.com/ungoogled-software/ungoogled-chromium).

Chromium bookmarks are stored in a text file (in JSON format) under the profile directory:

     ~/.config/chromium/${PROFILE_NAME}/Bookmarks

When mounting the filesystem, this pathname shall be passed as the SRC argument (see mount.bookmarkfs). Actual path for the profile directories may differ across distributions.

Backend name: chromium.

Backend-specific options (mount.bookmarkfs only):

filename=title|guid
     Whether to use the bookmark title or GUID as the bookmark file name. Defaults to title.

     A bookmark GUID is a hex-encoded 128-bit string uniquely associated with a bookmark or bookmark folder. The GUID string has a ``8-4-4-4-12'' format, while all alphabetic characters are in lowercase. For example:

          0bc5d13f-2cba-5d74-951f-3f233fe6c908

     When creating a new file:

     title
          The GUID is randomly generated by the backend. It is guaranteed to be a valid version 4 UUID as specified by RFC 4122 (https://datatracker.ietf.org/doc/html/rfc4122).

     guid
          The filename must be a valid GUID, and must not duplicate with other files on the same filesystem, otherwise open() or mkdir() fails with EPERM.

          Also set the GUID string as the bookmark title.

watcher=native|fallback|none
     The file watcher to use for the bookmark storage. Defaults to native when the filesystem is mounted read-only, none otherwise.

     native
          Watch for file changes using platform-specific features:

          Linux
               fanotify(7) is used. Requires kernel version 5.13 or later for unprivileged users.

               inotify(7) does not have this limitation, however, it is incompatible with our sandboxing design.

          FreeBSD
               kevent(2) with EVFILT_VNODE is used.

     fallback
          Watch for file changes by checking st_ino and st_mtim attributes with fstatat() periodically.

          Less efficient than ``native'' implementations, but should work on any POSIX-compatible system.

     none
          Do not watch for file changes.

          With watcher=none, changes on the bookmark storage are not visible to the filesystem.

If launched from fsck.bookmarkfs, all backend-specific options are ignored, and always enforces the watcher=none option.

Backend-specific options (mkfs.bookmarkfs only):

date_added=TIMESTAMP
     File creation time for the predefined bookmark directories. Defaults to the current time.

     Format of TIMESTAMP is equivalent to the date_added extended attribute (see below).

Extended attributes:

title
     The bookmark title. Only available with filename=guid.

     This value is allowed be set to any UTF-8 string that does not contain a NUL character, and does not have to be valid as a filename.

guid
     The bookmark GUID. Only available with filename=title.

date_added
     The bookmark creation time.

     Value is a decimal integer representing number of microseconds since the Windows FILETIME epoch (134774 days ahead of the Unix epoch).

Notable limitations:

  * Does not scale well with large bookmark storage, since everything is kept in memory, and the entire JSON file has to be parsed whenever a reload is required.

  * No support for tags (see Tags) and keywords (see Keywords), since Chromium does not have such concepts.

Backend API
===========

The Backend API specifies how a BookmarkFS backend communicates with a frontend program (e.g., mount.bookmarkfs).

     Currently BookmarkFS is experimental. The Backend API may change drastically without prior notice.

To implement the Backend API, a backend library should expose a symbol with the following name:

     bookmarkfs_backend_${NAME}

Where ${NAME} is equivalent to the value given to the -o backend=NAME option of frontend programs.

The symbol should name a data object identifier of the following type:

     #include <bookmarkfs/backend.h>

     struct bookmarkfs_backend {
         bookmarkfs_backend_create_func  *backend_create;
         bookmarkfs_backend_destroy_func *backend_destroy;
         bookmarkfs_backend_info_func    *backend_info;
         bookmarkfs_backend_init_func    *backend_init;
         bookmarkfs_backend_mkfs_func    *backend_mkfs;
         bookmarkfs_backend_sandbox_func *backend_sandbox;

         bookmarkfs_bookmark_check_func  *bookmark_check;
         bookmarkfs_bookmark_get_func    *bookmark_get;
         bookmarkfs_bookmark_list_func   *bookmark_list;
         bookmarkfs_bookmark_lookup_func *bookmark_lookup;

         bookmarkfs_bookmark_create_func  *bookmark_create;
         bookmarkfs_bookmark_delete_func  *bookmark_delete;
         bookmarkfs_bookmark_permute_func *bookmark_permute;
         bookmarkfs_bookmark_rename_func  *bookmark_rename;
         bookmarkfs_bookmark_set_func     *bookmark_set;
         bookmarkfs_bookmark_sync_func    *bookmark_sync;

         bookmarkfs_cookie_free_func *cookie_free;
     };

Each field is a function pointer provided by the backend. Some can be optional and set to NULL, if the backend does not support the corresponding features.

Initialize Backend
------------------

The backend_init function is called before any other functions (except for backend_info). If not NULL, it is guaranteed to be called exactly once throughout the lifetime of the process.

Type of the backend_init function is defined as:

     typedef int (bookmarkfs_backend_init_func) (
         uint32_t flags
     );

Function arguments:

flags
     A bit array of the following flags:

     BOOKMARKFS_BACKEND_LIB_READY
          Indicates that the utility library is already initialized. See The Utility Library.

          Some frontend programs use the utility library. If they do, they always initialize it before initializing the backend. The utility library must not be initialized multiple times, otherwise the behavior is undefined.

     BOOKMARKFS_FRONTEND_FSCK

     BOOKMARKFS_FRONTEND_MOUNT

     BOOKMARKFS_FRONTEND_MKFS
          Denotes the frontend program that launches the backend. These flags are mutually exclusive.

The function should return 0 on success, and -1 on error.

Get Backend Information
-----------------------

The backend_info function is called to print information about the backend. It can be NULL.

When this function is called, it should write a human-readable message of the corresponding information to standard output.

Type of the backend_info function is defined as:

     typedef void (bookmarkfs_backend_info_func) (
         uint32_t flags
     );

Function arguments:

flags
     A bit array of the following flags:

     BOOKMARKFS_BACKEND_INFO_HELP
          Indicates that the function should print a help message. Mutually exclusive with BOOKMARKFS_BACKEND_INFO_VERSION.

          The message should contain a brief description of this backend, as well as a list of backend-specific options.

     BOOKMARKFS_BACKEND_INFO_VERSION
          Indicates that the function should print a version message. Mutually exclusive with BOOKMARKFS_BACKEND_INFO_HELP.

          In addition to the version number, the version message may also contain dependency versions, compile-time options, and other information that can be used to determine a specific version of the backend.

     BOOKMARKFS_FRONTEND_FSCK

     BOOKMARKFS_FRONTEND_MOUNT

     BOOKMARKFS_FRONTEND_MKFS
          Denotes the frontend program that launches the backend. These flags are mutually exclusive.

Create Backend Context
----------------------

A backend context maintains internal states for manipulating a specific bookmark storage.

Each BookmarkFS filesystem mounted by mount.bookmarkfs is backed by a backend context. The context is created before mount, and destroyed after dismount. Operations on the filesystem, if applicable, are fulfilled by invoking the corresponding backend functions on the context.

To create a backend context, the backend_create function is called. It must not be NULL.

Type of the backend_create function is defined as:

     typedef int (bookmarkfs_backend_create_func) (
         struct bookmarkfs_backend_conf const  *conf,
         struct bookmarkfs_backend_create_resp *resp
     );

Function arguments:

conf
     Backend configuration items.

     The bookmarkfs_backend_conf structure is defined as:

          struct bookmarkfs_backend_conf {
              uint32_t  version;
              uint32_t  flags;
              char     *store_path;

              struct bookmarkfs_conf_opt *opts;
          };

     version
          Version number of the frontend program, equivalent to the BOOKMARKFS_VERNUM macro in ${PKGINCLUDEDIR}/version.h.

     flags
          A bit array of the following flags:

          BOOKMARKFS_BACKEND_READONLY
               Indicates that the filesystem is mounted read-only, and the functions that may write to the bookmark storage will not be called for this session.

          BOOKMARKFS_BACKEND_CTIME
               Indicates that the -o ctime option is given to mount.bookmarkfs.

          BOOKMARKFS_BACKEND_NO_SANDBOX
               Indicates that the -o no_sandbox option is given to mount.bookmarkfs.

               If the backend does not support sandboxing, backend_create should fail. Otherwise, function backend_sandbox should be implemented. See Enter Sandbox.

          BOOKMARKFS_BACKEND_NO_LANDLOCK
               Indicates that the -o no_landlock option is given to mount.bookmarkfs.

          BOOKMARKFS_BACKEND_FSCK_ONLY
               Indicates that the backend is launched from fsck.bookmarkfs.

               The backend must claim exclusive access to the bookmark storage, and only the bookmark_lookup, bookmark_list, bookmark_check and bookmark_sync functions will be called on the bookmarks.

     store_path
          Path to the bookmark storage, equivalent to the SRC argument passed to mount.bookmarkfs.

          The string is guaranteed to be NUL-terminated, and valid throughout the lifetime of the current backend. The backend is free to modify the string, however, it must not write past the string boundary.

     opts
          Linked list of backend-specific options, NULL if there are none.

          The bookmarkfs_conf_opt structure is defined as:

               struct bookmarkfs_conf_opt {
                   char *key;
                   char *val;

                   struct bookmarkfs_conf_opt *next;
               };

          key

          val
               Key and value of the current option. If the option does not have a value (no = character present), val is NULL.

               When not NULL, the strings are guaranteed to be NUL-terminated, and valid throughout the lifetime of the current backend context. The backend is free to modify the strings, however, it must not write past the string boundary.

          next
               Pointer to the next option, NULL if there are no more options.

          The backend must not re-assign the fields in opt.

resp
     Information of the new backend context.

     The initial content of this argument is unspecified. When the backend context is successfully created, the function should populate this argument with appropriate values.

     The bookmarkfs_backend_create_resp structure is defined as:

          struct bookmarkfs_backend_create_resp {
              char const *name;
              void       *backend_ctx;
              uint64_t    bookmarks_root_id;
              uint64_t    tags_root_id;
              char const *bookmark_attrs;
              uint32_t    flags;
          };

     name
          Name of the backend context, used as default value for the -o fsname=NAME option of mount.bookmarkfs.

          Must be a NUL-terminated string valid at least until the next function call on this backend context.

     backend_ctx
          An opaque pointer referring to the backend context.

          The pointer will be passed to further function calls on this context.

     bookmarks_root_id
          ID of the bookmarks root directory (i.e., ${MOUNTPOINT}/bookmarks). Must not be greater than BOOKMARKFS_MAX_ID.

          If sandboxing is requested, and the ID cannot be determined in a safe way before entering sandbox, the backend may leave this field as-is or set it to UINT64_MAX.

     tags_root_id
          ID of the tags root directory (i.e., ${MOUNTPOINT}/tags). Must not be greater than BOOKMARKFS_MAX_ID.

          This field should be left as-is or set to UINT64_MAX, if one of the following conditions is met:

            * Sandboxing is requested, and the ID cannot be determined in a safe way before entering sandbox.

            * The backend does not support tags for this context. See Tags.

     bookmark_attrs
          Names of extended attributes (see Extended Attributes) supported for this backend context.

          Must be a concatenation of NUL-terminated strings valid throughout the lifetime of this backend context. An empty string denotes the end of list.

     flags
          A bit array of the following flags:

          BOOKMARKFS_BACKEND_EXCLUSIVE
               Indicates that the backend claims exclusive access to the bookmark storage.

               In exclusive mode, modifications to the bookmark storage only come from the frontend, and the caller may perform optimizations based on this assumption.

          BOOKMARKFS_BACKEND_HAS_KEYWORD
               Indicates that the backend supports keywords for this context. See Keywords.

The function should return 0 on success, and -1 on error.

Destroy Backend Context
-----------------------

When a backend context is no longer used, the backend_destroy function is called. It must not be NULL.

Upon destruction, the backend should release all system resources associated with this context. Changes made to the bookmark storage should be persisted.

Type of the backend_destroy function is defined as:

     typedef void (bookmarkfs_backend_destroy_func) (
         void *backend_ctx
     );

Function arguments:

backend_ctx
     The pointer referring to the backend context.

Enter Sandbox
-------------

The backend_sandbox function is called to instruct the backend to enter a sandboxed state where it has limited access to system resources, thereby improving security. See Sandboxing.

This function is only called if the BOOKMARKFS_BACKEND_NO_SANDBOX flag is not given to backend_create when creating the current context. It can be NULL if the backend context does not support sandboxing.

Considering how sandboxing is usually implemented, it may be complicated or even impractical for multiple backend contexts to enter sandbox separately without affecting other ones. Thus it is guaranteed that, when launched from a frontend program like mount.bookmarkfs, only one backend context will be created throughout the lifetime of the process if sandboxing is ever needed.

Currently, the backends shipped with BookmarkFS use the sandbox implementation in the utility library (see The Utility Library). It may require some tweaking to be used for other backends.

Type of the backend_sandbox function is defined as:

     typedef int (bookmarkfs_backend_sandbox_func) (
         void                                  *backend_ctx,
         struct bookmarkfs_backend_create_resp *resp
     );

Function arguments:

backend_ctx
     The pointer referring to the backend context.

resp
     See Backend-Create Response for the definition of the bookmarkfs_backend_create_resp structure.

     Fields in this argument should be left as-is, except for:

     bookmarks_root_id
          This field must be set to the ID of the bookmarks root directory, if not already done so in backend_create.

     tags_root_id
          This field should be set to the ID of the tags root directory, if not already done so in backend_create.

          If the backend does not support tags for this context, this field should be left as-is or set to UINT64_MAX.

The function should return 0 on success, and -1 on error.

Once this function fails, no further function calls will be performed on the backend except for backend_destroy.

Lookup Bookmark
---------------

The bookmark_lookup function is called to obtain the attributes of a bookmark or a bookmark-related object. It must not be NULL.

Type of the bookmark_lookup function is defined as:

     typedef int (bookmarkfs_bookmark_lookup_func) (
         void                            *backend_ctx,
         uint64_t                         id,
         char const                      *name,
         uint32_t                         flags,
         struct bookmarkfs_bookmark_stat *stat_buf
     );

Function arguments:

backend_ctx
     The pointer referring to the backend context.

id
     ID of the object to be used for lookup. It could be a bookmark ID or tag ID, depending on the value of flags.

name
     Name of the object to lookup. It could be a bookmark name, tag name or keyword name, depending on the value of flags.

     When not NULL, name is guaranteed to be a valid filename, which should be used to lookup an object under the directory referred to by id. Otherwise, the function should operate on the object referred to by id itself.

flags
     A bit array of the following flags:

     BOOKMARKFS_BOOKMARK_TYPE_MASK
          The subsystem to operate on.

          Equals to one of the following values (after shifting):

          BOOKMARKFS_BOOKMARK_TYPE_BOOKMARK
               Lookup a bookmark or bookmark folder. See Bookmarks.

          BOOKMARKFS_BOOKMARK_TYPE_TAG
               Lookup a tag, or a bookmark under a tag. See Tags.

          BOOKMARKFS_BOOKMARK_TYPE_KEYWORD
               Lookup a keyword. See Keywords.

               The value of id is unspecified.

stat_buf
     Attributes of the object to lookup.

     The initial content of this argument is unspecified. When the object is successfully found, the function should populate this argument with appropriate values.

     The bookmarkfs_bookmark_stat structure is defined as:

          struct bookmarkfs_bookmark_stat {
              uint64_t id;
              ssize_t  value_len;

              struct timespec atime;
              struct timespec mtime;
          };

     id
          ID of the object.

     value_len
          Length of the object value in bytes. -1 if the object refers to a directory.

     atime

     mtime
          Last access and modification time of the object.

          Values should be the time elapsed since the Unix epoch.

On success, the function should return 0. Otherwise, it should return a negated errno indicating the error encountered.

Generally, the error code should follow filesystem conventions, but the backend may return other error codes which it sees fit. Also see Error Codes.

List Bookmarks
--------------

The bookmark_list function is called to list the bookmarks or bookmark-related objects under a directory. It must not be NULL.

Type of the bookmark_list function is defined as:

     typedef int (bookmarkfs_bookmark_list_func) (
         void                         *backend_ctx,
         uint64_t                      id,
         off_t                         off,
         uint32_t                      flags,
         bookmarkfs_bookmark_list_cb  *callback,
         void                         *user_data,
         void                        **cookie_ptr
     );

Function arguments:

backend_ctx
     The pointer referring to the backend context.

id
     Object ID of the directory. It could be a bookmark ID or tag ID, depending on the value of flags.

off
     Position of the current bookmark_list operation, equivalent to the second argument for seekdir(3p).

     Initially, the value is always 0. Subsequent calls (with the same cookie) use the values obtained from entry->off of callback function calls. If not, the position is unspecified.

     A subsequent call with a 0 value indicates rewinddir().

flags
     A bit array of the following flags:

     BOOKMARKFS_BOOKMARK_LIST_WITHSTAT
          Indicates that the caller requires the attributes of the objects in the list.

     BOOKMARKFS_BOOKMARK_TYPE_MASK
          The subsystem to operate on.

          Equals to one of the following values (after shifting):

          BOOKMARKFS_BOOKMARK_TYPE_BOOKMARK
               List bookmarks (and bookmark folders) under a bookmark folder. See Bookmarks.

          BOOKMARKFS_BOOKMARK_TYPE_TAG
               List tags, or bookmarks under a tag. See Tags.

          BOOKMARKFS_BOOKMARK_TYPE_KEYWORD
               List keywords. See Keywords.

               The value of id is unspecified.

callback
     Function to be called for each object found under the directory. If NULL, bookmark_list should return immediately without changing current position.

     Ordering of the objects should follow the ordering of which they appear in the browser. See Directory Entry Ordering.

     Type of the callback function is defined as:

          typedef int (bookmarkfs_bookmark_list_cb) (
              void                                   *user_data,
              struct bookmarkfs_bookmark_entry const *entry
          );

     Function arguments:

     user_data
          Must be equal to the user_data argument value for the current bookmark_list call.

     entry
          Information of this object.

          The bookmarkfs_bookmark_entry structure is defined as:

               struct bookmarkfs_bookmark_entry {
                   char const *name;
                   off_t       off;

                   struct bookmarkfs_bookmark_stat stat;
               };

          name
               Name of the object entry. Must be a valid filename.

               Objects with invalid name should be exempted from the list. See Filesystem Check.

          off
               Position of the current entry, to be used for subsequent calls to bookmark_list.

          stat
               Attributes of the object entry.

               See Bookmark Attributes for the definition of the bookmarkfs_bookmark_stat structure.

               If the BOOKMARKFS_BOOKMARK_LIST_WITHSTAT flag is not given, the caller ignores atime and mtime, and only use value_len to determine whether the entry refers to a directory.

          Values of entry and name only need to be valid until the callback function returns.

     Return value:

     0
          Continue to the next entry, if one exists.

     Non-zero value
          Stop listing entries, and use this value as the return value for the current bookmark_list call.

user_data
     An opaque pointer which should be passed as-is to the callback function.

cookie_ptr
     Pointer to the ``cookie'' for the current bookmark_list operation. NULL if not used by the caller.

     The ``cookie'' is an opaque pointer which stores internal states to be used for subsequent operations on the same directory.

     If the value pointed to by cookie_ptr is NULL, it indicates that this is an initial operation. The function should set the value to a pointer which is not NULL.

     The backend must not change the cookie value.

Return value:

0
     All entries in the directory have been listed.

Negated errno
     An error occurred.

Arbitrary non-zero value
     The function is terminated by a call to callback that returns a non-zero value.

If returning a negative value, the position of the current bookmark_list operation is unspecified.

Get Bookmark Content
--------------------

The bookmark_get function is called to get the content or extended attribute value of a bookmark.

The ``content'' of a bookmark is equivalent to the content of a regular file on a BookmarkFS filesystem that refers to a bookmark. See Bookmarks.

Type of the bookmark_get function is defined as:

     typedef int (bookmarkfs_bookmark_get_func) (
         void                        *backend_ctx,
         uint64_t                     id,
         char const                  *attr_key,
         bookmarkfs_bookmark_get_cb  *callback,
         void                        *user_data,
         void                       **cookie_ptr
     );

Function arguments:

backend_ctx
     The pointer referring to the backend context.

id
     ID of the bookmark (could be a bookmark folder if attr_key is not NULL).

attr_key
     Name of an extended attribute.

     If not NULL, attr_key is guaranteed to be a NUL-terminated string, and the function should get the corresponding extended attribute value instead of the bookmark content.

callback
     Function to be called for the required bookmark data. It is guaranteed not to be NULL.

     Type of the callback function is defined as:

          typedef int (bookmarkfs_bookmark_get_cb) (
              void       *user_data,
              void const *value,
              size_t      value_len
          );

     Function arguments:

     user_data
          Must be equal to the user_data argument value of the current bookmark_get call.

     value
          Bookmark content (or extended attribute) data. Must not be NULL.

          It may contain arbitrary bytes, and only need to be valid until the callback function returns.

     value_len
          Length of value in bytes.

     The function returns 0 on success. Otherwise, the function returns a negated errno which should be used as the return value for the current bookmark_get call.

user_data
     An opaque pointer which should be passed as-is to the callback function.

cookie_ptr
     Pointer to the ``cookie'' for the current bookmark_get operation. NULL if not used by the caller.

     The “cookie” is an opaque pointer which stores internal states for subsequent operations on the same bookmark (and attr_key) to determine whether the corresponding data has changed.

     If the value pointered to by cookie_ptr is NULL, it indicates that this is an initial operation. If the backend supports watching for changes of bookmark content, it may set the value to a pointer which is not NULL.

     The backend must not change the cookie value.

On success, the function should return 0. Otherwise, it should return a negated errno indicating the error encountered.

Notable error codes:

EAGAIN
     Bookmark data has not been changed externally since the last call to bookmark_get with the same cookie. If this error code is returned, callback must not be called.

     ``Externally'' means that the modification comes from another process which has access to the bookmark storage. In exclusive mode, this is not expected to happen, and bookmark_list is always called with a cookie_ptr of NULL value.

     If changes cannot be detected in an efficient way, false positives are acceptable. False negatives are also acceptable for a short time duration (preferably no more than a few seconds).

Free Cookie
-----------

When a cookie obtained from bookmark_list, bookmark_get or bookmark_check is no longer used, the cookie_free function is called. It must not be NULL.

The function should release all system resources associated with this cookie.

Type of the cookie_free function is defined as:

     typedef void (bookmarkfs_cookie_free_func) (
         void                        *backend_ctx,
         void                        *cookie,
         enum bookmarkfs_cookie_type  cookie_type
     );

Function arguments:

backend_ctx
     The pointer referring to the backend context.

cookie
     Cookie to be freed.

cookie_type
     Type of the cookie. It must be one of the values defined in enum bookmarkfs_cookie_type:

     BOOKMARKFS_COOKIE_TYPE_WATCH
          Indicates that the cookie is obtained from bookmark_get.

     BOOKMARKFS_COOKIE_TYPE_LIST
          Indicates that the cookie is obtained from bookmark_list or bookmark_check.

Set Bookmark Content
--------------------

The bookmark_set function is called to update the data associated with a bookmark or bookmark-related object. It is never called when the filesystem is mounted read-only.

Type of the bookmark_set function is defined as:

     typedef int (bookmarkfs_bookmark_set_func) (
         void       *backend_ctx,
         uint64_t    id,
         char const *attr_key,
         uint32_t    flags,
         void const *val,
         size_t      val_len
     );

Function arguments:

backend_ctx
     The pointer referring to the backend context.

id
     ID of the object to update. It could be a bookmark ID or tag ID, depending on the value of flags.

attr_key
     Name of an extended attribute.

     If not NULL, attr_key is guaranteed to be a NUL-terminated string, and the function should update the corresponding extended attribute value instead of the bookmark content.

flags
     A bit array of the following flags:

     BOOKMARKFS_BOOKMARK_SET_TIME
          Indicates that the function should update the timestamps associated with the object.

          The val argument points to an array of struct timespec, the first element refers to the last access time, and the second element refers to the last modification time of the object. Values of attr_key and val_len are unspecified.

          The tv_nsec field of each timestamp may be UTIME_OMIT, which indicates that the timestamp should be left as-is. It is guaranteed not to be UTIME_NOW.

          If this flag is not set, the function should update the bookmark content or extended attribute value instead.

     BOOKMARKFS_BOOKMARK_TYPE_MASK
          The subsystem to operate on.

          Equals to one of the following values (after shifting):

          BOOKMARKFS_BOOKMARK_TYPE_BOOKMARK
               Update the data associated with a bookmark or bookmark folder. See Bookmarks.

          BOOKMARKFS_BOOKMARK_TYPE_TAG
               Update the data associated with a tag. See Tags.

val
     Pointer to the new value of the object data.

val_len
     Length of the new value in bytes.

On success, the function should return 0. Otherwise, it should return a negated errno indicating the error encountered.

The backend does not need to update the last modification time of a bookmark upon bookmark content update, since the caller knows better when the bookmark content is actually modified, and always explicitly updates the mtime after a writeback.

However, if the BOOKMARKFS_BACKEND_CTIME flag is set for this context, the function should automatically update the bookmark mtime to the current time upon extended attribute update.

Create Bookmark
---------------

The bookmark_create function is called to create a new bookmark or bookmark-related object. It is never called when the filesystem is mounted read-only.

Type of the bookmark_create function is defined as:

     typedef int (bookmarkfs_bookmark_create_func) (
         void                            *backend_ctx,
         uint64_t                         parent_id,
         char const                      *name,
         uint32_t                         flags,
         struct bookmarkfs_bookmark_stat *stat_buf
     );

Function arguments:

backend_ctx
     The pointer referring to the backend context.

parent_id
     ID of the parent directory where the new object shall be created. It could be a bookmark ID or tag ID, depending on the value of flags.

name
     Name of the new object. It could be a bookmark name, tag name or keyword name, depending on the value of flags.

flags
     A bit array of the following flags:

     BOOKMARKFS_BOOKMARK_CREATE_DIR
          Indicates that the new object to be created should be a directory.

          If this flag is not set, the new object should be a bookmark.

     BOOKMARKFS_BOOKMARK_TYPE_MASK
          The subsystem to operate on.

          Equals to one of the following values (after shifting):

          BOOKMARKFS_BOOKMARK_TYPE_BOOKMARK
               Create a bookmark or bookmark folder. See Bookmarks.

          BOOKMARKFS_BOOKMARK_TYPE_TAG
               Create a tag, or associate an existing bookmark with a tag. See Tags.

          BOOKMARKFS_BOOKMARK_TYPE_KEYWORD
               Associate an existing bookmark with a new keyword. See Keywords.

               The value of parent_id is unspecified.

stat_buf
     Attributes of the new object.

     See Bookmark Attributes for the definition of the bookmarkfs_bookmark_stat structure.

     Except for the id field, the initial content of this argument is unspecified. When the object is successfully found, the function should populate this argument with appropriate values.

     When associating an existing bookmark with a tag or keyword, the initial value of the id field refers to the bookmark to be associated. Generally it should be left as-is, but the backend is allowed to re-assign it to the ID of another bookmark (see below for restrictions).

On success, the function should return 0. Otherwise, it should return a negated errno indicating the error encountered.

Restrictions for the new object:

New bookmark
     Bookmark content should be empty if possible.

     Other reasonable values (e.g., about:blank) are also acceptable if the bookmark storage does not allow empty bookmark content.

New directory
     Should not contain any entries.

New association
     Should appear to be the same object as the associated bookmark, or at least contain the same content.

When a new object is successfully created, the function should automatically update the last modification time of the parent directory to the current time.

Rename Bookmark
---------------

The bookmark_rename function is called to rename and/or reparent a bookmark or a bookmark-related object. It is never called when the filesystem is mounted read-only.

Type of the bookmark_rename function is defined as:

     typedef int (bookmarkfs_bookmark_rename_func) (
         void       *backend_ctx,
         uint64_t    old_parent_id,
         char const *old_name,
         uint64_t    new_parent_id,
         char const *new_name,
         uint32_t    flags
     );

Function arguments:

backend_ctx
     The pointer referring to the backend context.

old_parent_id
     ID of the parent directory containing the object to be renamed. It could be a bookmark ID or tag ID, depending on the value of flags.

old_name
     Name of the object to be renamed. It could be a bookmark name, tag name or keyword name, depending on the value of flags.

new_parent_id
     ID of the new parent directory for the object.

new_name
     New name of the object.

flags
     A bit array of the following flags:

     BOOKMARKFS_BOOKMARK_RENAME_NOREPLACE
          Indicates that the function should not replace an existing object.

          This flag is analogous to the RENAME_NOREPLACE flag for renameat2().

     BOOKMARKFS_BOOKMARK_TYPE_MASK
          The subsystem to operate on.

          Equals to one of the following values (after shifting):

          BOOKMARKFS_BOOKMARK_TYPE_BOOKMARK
               Rename a bookmark. See Bookmarks.

          BOOKMARKFS_BOOKMARK_TYPE_TAG
               Rename a tag. See Tags.

               The function should fail with EPERM if either old_parent_id or new_parent_id is not the ID of the tags root directory, since renaming a tag association makes no sense.

          BOOKMARKFS_BOOKMARK_TYPE_KEYWORD
               Rename a keyword. See Keywords.

               Values of old_parent_id and new_parent_id are unspecified.

On success, the function should return 0. Otherwise, it should return a negated errno indicating the error encountered.

When replacing an existing object:

  * The two objects should be of the same type (e.g., a bookmark cannot replace a bookmark folder).

  * When replacing a directory, the destination object must be empty.

  * If two objects are the same, the function should do nothing and return successfully.

Upon successful completion, the function should automatically update the last modification time of the parent directories.

Delete Bookmark
---------------

The bookmark_delete function is called to remove a bookmark or a bookmark-related object. It is never called when the filesystem is mounted read-only.

Type of the bookmark_delete function is defined as:

     typedef int (bookmarkfs_bookmark_delete_func) (
         void       *backend_ctx,
         uint64_t    parent_id,
         char const *name,
         uint32_t    flags
     );

Function arguments:

backend_ctx
     The pointer referring to the backend context.

parent_id
     ID of the parent directory containing the object to be removed. It could be a bookmark ID or tag ID, depending on the value of flags.

name
     Name of the object to be removed. It could be a bookmark name, tag name or keyword name, depending on the value of flags.

flags
     A bit array of the following flags:

     BOOKMARKFS_BOOKMARK_TYPE_MASK
          The subsystem to operate on.

          Equals to one of the following values (after shifting):

          BOOKMARKFS_BOOKMARK_TYPE_BOOKMARK
               Remove a bookmark or an empty bookmark folder. See Bookmarks.

          BOOKMARKFS_BOOKMARK_TYPE_TAG
               Remove a tag, or dissociate a bookmark from a tag. See Tags.

          BOOKMARKFS_BOOKMARK_TYPE_KEYWORD
               Remove a keyword. See Keywords.

               The value of parent_id is unspecified.

On success, the function should return 0. Otherwise, it should return a negated errno indicating the error encountered.

Upon successful completion, the function should automatically update the last modification time of the parent directory.

Permute Bookmarks
-----------------

The bookmark_permute function is called to rearrange bookmarks or bookmark-related objects under a directory. It is never called when the filesystem is mounted read-only.

This function should implement the BOOKMARKFS_IOC_PERMD I/O control. See Permute Directory Entries.

Type of the bookmark_permute function is defined as:

     typedef int (bookmarkfs_bookmark_permute_func) (
         void                     *backend_ctx,
         uint64_t                  parent_id,
         enum bookmarkfs_permd_op  op,
         char const               *name1,
         char const               *name2,
         uint32_t                  flags
     );

Function arguments:

backend_ctx
     The pointer referring to the backend context.

parent_id
     ID of the parent directory containing the objects to be rearranged. It could be a bookmark ID or tag ID, depending on the value of flags.

op
     Operation to perform on objects represented by name1 and name2.

     See Rearrange Operations for the meaning of each value.

name1

name2
     Names of the objects to be rearranged. They could be bookmark names, tag names or keyword names, depending on the value of flags.

flags
     A bit array of the following flags:

     BOOKMARKFS_BOOKMARK_TYPE_MASK
          The subsystem to operate on.

          Equals to one of the following values (after shifting):

          BOOKMARKFS_BOOKMARK_TYPE_BOOKMARK
               Rearrange entries under a bookmark folder. See Bookmarks.

          BOOKMARKFS_BOOKMARK_TYPE_TAG
               Rearrange tags or tag associations. See Tags.

          BOOKMARKFS_BOOKMARK_TYPE_KEYWORD
               Rearrange keywords. See Keywords.

               The value of parent_id is unspecified.

On success, the function should return 0. Otherwise, it should return a negated errno indicating the error encountered.

Sync Bookmarks
--------------

The bookmark_sync function is called to persist changes to the bookmark storage. It is never called when the filesystem is mounted read-only.

If the bookmark storage is also changed from another process, function behavior is unspecified. However, the backend should try not to corrupt the bookmark storage, and prefer changes from the caller, if possible.

Type of the bookmark_sync function is defined as:

     typedef int (bookmarkfs_bookmark_sync_func) (
         void *backend_ctx
     );

Function arguments:

backend_ctx
     The pointer referring to the backend context.

On success, the function should return 0. Otherwise, it should return a negated errno indicating the error encountered.

Create Bookmark Storage
-----------------------

The backend_mkfs function is called by the mkfs.bookmarkfs program to create a new bookmark storage. It can be NULL.

The new bookmark storage should be able to be opened directly, by both backend_create and the application that owns the bookmark storage (typically a web browser).

Type of the backend_mkfs function is defined as:

     typedef int (bookmarkfs_backend_mkfs_func) (
         struct bookmarkfs_backend_conf const *conf
     );

Function arguments:

conf
     See Backend Configuration for the definition of the bookmarkfs_backend_conf structure.

     Some fields should be handled differently from backend_create:

     flags
          A bit array of the following flags:

          BOOKMARKFS_BACKEND_MKFS_FORCE
               Indicates that the -o force option is given to mkfs.bookmarkfs.

     store_path
          Path to the bookmark storage to be created, equivalent to the store_path passed to backend_create.

          The function should not overwrite existing bookmark storage, unless the BOOKMARKFS_BACKEND_MKFS_FORCE flag is given.

The function should return 0 on success, and -1 on error.

Filesystem-Check Handlers
*************************

The filesystem-check handler is responsible for instructing the fsck.bookmarkfs program on what to do with each invalid bookmark name.

Like backends, filesystem-check handlers are typically built into shared libraries, and are installed as:

     ${PKGLIBDIR}/fsck_handler_${NAME}${SHLIB_SUFFIX}

${pkglibdir}
     Presumably ${PREFIX}/lib/bookmarkfs. See The Uniform Naming Scheme (GNU Automake).

${name}
     The handler name, equivalent to the value given to the -o handler=NAME option of fsck.bookmarkfs.

${shlib_suffix}
     The common filename extension for shared library files on the current platform (e.g., .so on GNU/Linux and FreeBSD).

Built-in Handler
================

The built-in filesystem-check handler is linked into the fsck.bookmarkfs program. It has minimal functionalities, but is capable enough to handle most common fsck scenarios.

Handler-specific options:

prompt=STR
     Readline prompt string. Defaults to % .

     This option is ignored in non-interactive mode.

translit=CHAR
     Transliterate bad (/) characters into CHAR (must be a single ASCII character). Defaults to _.

For each bookmark entry, the built-in handler prints a message to standard output explaining why the bookmark name is invalid.

When the -o repair option is given, the handler renames the bookmark according to the following rules:

BOOKMARKFS_FSCK_RESULT_NAME_DUPLICATE
     Append a _${COUNTER} suffix to the name, where ${COUNTER} is a self-incrementing 32-bit integer in decimal format.

     If appending the suffix would exceed max name length, truncate the name first.

BOOKMARKFS_FSCK_RESULT_NAME_BADCHAR
     Transliterate the bad character into another character.

BOOKMARKFS_FSCK_RESULT_NAME_BADLEN
     If the name is too long, truncate it to NAME_MAX bytes.

     If the name is empty, see below:

BOOKMARKFS_FSCK_RESULT_NAME_DOTDOT

BOOKMARKFS_FSCK_RESULT_NAME_INVALID
     Rename to fsck-${ID}, where ${ID} is the bookmark ID in decimal format.

In interactive mode, the handler prompts the user before applying the rename. The user may issue a command to indicate what to do with each entry:

p
     Print the ID and new name of current entry.

a[-]
     Apply the proposed rename for the current entry.

e[-] NEW_NAME
     Change the proposed rename to NEW_NAME and then apply.

c
     Continue to the next entry.

s[-]
     Skip current directory.

S[-]
     Skip current directory and all subdirectories.

r[-]
     Rewind current directory.

R[-]
     Rewind all.

w[-]
     Save applied changes.

q
     Save applied changes and quit.

The optional - suffix inhibits the default behavior of continuing to the next entry, after the command completes successfully.

Tcl-Based Handler
=================

The Tcl-based filesystem-check handler allows fsck entries to be handled via Tcl scripting.

Handler name: tcl.

Handler-specific options:

script=PATH
     Path to the Tcl script file. This option is mandatory.

     The script is evaluated once after interpreter initialization. The evaluation result will be used as the command name for later executions.

     The following variables are set before script evaluation:

     $::bookmarkfs::fsck::isInteractive
          Equals to 1 if the -i option is given to fsck.bookmarkfs, 0 otherwise.

     $::bookmarkfs::fsck::isReadonly
          Equals to 0 if the -o repair option is given to fsck.bookmarkfs, 1 otherwise.

unsafe
     Enable unsafe Tcl interpreter features.

     Should be used in combination with the -o no_sandbox option if you wish to access extra system resources (e.g., open local files).

     Without this option, the Tcl interpreter has limited functionalities as if created with interp create -safe. See Safe Interpreters (https://www.tcl.tk/man/tcl8.6.13/TclCmd/interp.htm#M45).

Each time fsck.bookmarkfs finds an entry, or completes a handler-initiated operation, the command returned from the script is executed with a single list argument. The first element of the list is an integer indicating the reason for this handler call:

$::bookmarkfs::fsck::result::nameDuplicate

$::bookmarkfs::fsck::result::nameBadChar

$::bookmarkfs::fsck::result::nameBadLen

$::bookmarkfs::fsck::result::nameDotDot

$::bookmarkfs::fsck::result::nameInvalid
     See Filesystem-Check Result Code for the meaning of each value.

     The second element is a list of information about the current entry:

      0. The bookmark ID

      1. Extra information regarding the invalid name, equivalent to the extra field in structure bookmarkfs_fsck_data.

      2. Name of the bookmark

      3. ID of the parent directory

-1
     If the command is being executed for the first time, or the previous execution returns $::bookmarkfs::fsck::handler::next, this value never appears.

     If the previous execution of the command returns $::bookmarkfs::fsck::handler::userInput, the second element of the list is a string of user input.

     Otherwise, this value indicates that the previous operation is successfully performed on the entry.

The command should return a list indicating the operation to perform on the entry. First element of the list should be one of the following values:

$::bookmarkfs::fsck::handler::next
     Continue to the next entry.

$::bookmarkfs::fsck::handler::apply
     Apply change for the current entry. Not available in read-only mode.

     Second element of the list should be a string for the new name of the bookmark.

$::bookmarkfs::fsck::handler::userInput
     Request for user input. Only available in interactive mode.

     Second element of the list should be a string for Readline prompt, equivalent to the prompt=STR option of the built-in handler.

$::bookmarkfs::fsck::handler::save
     Save applied changes.

$::bookmarkfs::fsck::handler::stop
     Save applied changes and quit.

     Once this value is returned, the command will never be executed again.

$::bookmarkfs::fsck::handler::rewind
     Rewind current directory.

$::bookmarkfs::fsck::handler::skip
     Skip current directory.

$::bookmarkfs::fsck::handler::skipChildren
     Skip current directory and all subdirectories.

$::bookmarkfs::fsck::handler::reset
     Rewind all.

Here is an example Tcl script that simply prints each fsck entry (requires the unsafe option):

     chan configure stdout -encoding utf-8
     coroutine whatever apply {{} {
         set args [yield [info coroutine]]
         while 1 {
             lassign $args why data
             if {$why > -1} {
                 lassign $data id extra name parent
                 puts "id: $id, extra: $extra, name: $name, parent: $parent"
             }
             set args [yield [list $::bookmarkfs::fsck::handler::next]]
         }
     }}

Handler API
===========

The Filesystem-Check Handler API specifies how a fsck handler communicates with fsck.bookmarkfs.

     Currently BookmarkFS is experimental. The Handler API may change drastically without prior notice.

To implement the Filesystem-Check Handler API, a fsck handler library should expose a symbol with the following name:

     bookmarkfs_fsck_handler_${NAME}

Where ${NAME} is equivalent to the value given to the -o handler=NAME option of fsck.bookmarkfs.

The symbol should name a data object identifier of the following type:

     #include <bookmarkfs/fsck_handler.h>

     struct bookmarkfs_fsck_handler {
         bookmarkfs_fsck_handler_info_func *info;

         bookmarkfs_fsck_handler_create_func  *create;
         bookmarkfs_fsck_handler_destroy_func *destroy;

         bookmarkfs_fsck_handler_run_func *run;
     };

Each field is a function pointer provided by the fsck handler. Some can be optional and set to NULL, if the backend does not support the corresponding features.

Get Handler Information
-----------------------

If not NULL, the info function is called when the user instructs fsck.bookmarkfs to print information about the handler.

When this function is called, it should write a human-readable message of the corresponding information to standard output.

Type of the info function is defined as:

     typedef void (bookmarkfs_fsck_handler_info_func) (
         uint32_t flags
     );

Function arguments:

flags
     A bit array of the following flags:

     BOOKMARKFS_FSCK_HANDLER_INFO_HELP

     BOOKMARKFS_FSCK_HANDLER_INFO_VERSION
          Indicates that the function should print a help/version message.

          These flags are analogous to the corresponding flags in the backend_info function of the backend API. See Get Backend Information.

Create Handler Context
----------------------

A filesystem-check handler context maintains internal states for handling fsck entries.

To create a handler context, the create function is called. It must not be NULL.

Type of the create function is defined as:

     typedef int (bookmarkfs_fsck_handler_create_func) (
         struct bookmarkfs_conf_opt const  *opts,
         uint32_t                           flags,
         void                             **handler_ctx_ptr
     );

Function arguments:

opts
     Linked list of handler-specific options, NULL if there are none.

     Handler-specific option should be processed in the same way as backend-specific options. See Backend-Specific Options.

flags
     A bit array of the following flags:

     BOOKMARKFS_FSCK_HANDLER_INTERACTIVE
          Indicates that the -i option is given to fsck.bookmarkfs.

     BOOKMARKFS_FSCK_HANDLER_READONLY
          Indicates that the -o repair option is not given to fsck.bookmarkfs.

handler_ctx_ptr
     Pointer to an opaque pointer referring to the handler context.

     The initial value of that pointer is unspecified. It should be set by the handler when the handler context is successfully created.

The function should return 0 on success, and -1 on error.

Destroy Handler Context
-----------------------

When a handler context is no longer used, the destroy function is called. It must not be NULL.

Upon destruction, The handler should release all system resources associated with this context.

Type of the destroy function is defined as:

     typedef void (bookmarkfs_fsck_handler_destroy_func) (
         void *handler_ctx
     );

Function arguments:

handler_ctx
     The pointer referring to the handler context.

Run Handler
-----------

Each time fsck.bookmarkfs finds and entry, or completes a handler-initiated operation, the run function is called. It must not be NULL.

Type of the run function is defined as:

     typedef int (bookmarkfs_fsck_handler_run_func) (
         void                               *handler_ctx,
         int                                 why,
         union bookmarkfs_fsck_handler_data *data
     );

Function arguments:

handler_ctx
     The pointer referring to the handler context.

why
     Reason code for this handler call:

     Non-negative value
          The fsck.bookmarkfs program has found an entry.

          See Filesystem-Check Result Code for possible values. However, BOOKMARKFS_FSCK_RESULT_END is handled by fsck.bookmarkfs and never appears.

     -1
          Indicates that an operation instructed by the previous return value of this function is successfully performed.

          If run is being called for the first time, or the previous invocation returns BOOKMARKFS_FSCK_NEXT, this value never appears.

data
     Information for this handler call.

     The bookmarkfs_fsck_handler_data union is defined as:

          union bookmarkfs_fsck_handler_data {
              struct bookmarkfs_fsck_handler_entry entry;
              char *str;
          };

     entry
          Information for the bookmark entry. This field is set when why is non-negative.

          The bookmarkfs_fsck_handler_entry structure is defined as:

               struct bookmarkfs_fsck_handler_entry {
                   uint64_t parent_id;

                   struct bookmarkfs_fsck_data data;
               };

          parent_id
               ID of the bookmark folder that contains this bookmark entry.

          data
               See Online Filesystem Check for the definition of the bookmarkfs_fsck_data structure.

     str
          A NUL-terminated string.

          This field is set to the user input string when why is -1, and the previous invocation of run returns BOOKMARKFS_FSCK_USER_INPUT.

          The string is only guaranteed to be valid until the function returns.

On success, the function should return one of the following values, indicating the operation to perform:

BOOKMARKFS_FSCK_NEXT
     Continue to the next entry.

BOOKMARKFS_FSCK_APPLY
     Apply change for the current entry. Not available in read-only mode.

     The function should copy the new name for the bookmark to data->entry.data.name.

BOOKMARKFS_FSCK_USER_INPUT
     Request for user input. Only available in interactive mode.

     The function should set data->str to a prompt string for Readline. The string must be valid until the next function call on this handler context.

BOOKMARKFS_FSCK_SAVE
     Save applied changes.

BOOKMARKFS_FSCK_STOP
     Save applied changes and quit.

     The run function is guaranteed not to be called again for this context.

BOOKMARKFS_FSCK_REWIND
     Rewind current directory.

BOOKMARKFS_FSCK_SKIP
     Skip current directory.

BOOKMARKFS_FSCK_SKIP_CHILDREN
     Skip current directory and all subdirectories.

BOOKMARKFS_FSCK_RESET
     Rewind all.

On error, the function should return -1.

GNU Free Documentation License
******************************

     [file fdl.texi not included]