bookmarkfs/doc/bookmarkfs.texi
2025-02-04 20:55:29 +08:00

3005 lines
84 KiB
Text

\input texinfo @c -*-texinfo-*-
@setfilename bookmarkfs.info
@include version.texi
@settitle BookmarkFS User Manual
@macro manpage {name, section, url}
@uref{\url\,, @code{\name\(\section\)}}
@end macro
@macro linuxmanpage {name, section}
@manpage{\name\, \section\,
https://man7.org/linux/man-pages/man\section\/\name\.\section\.html}
@end macro
@macro freebsdmanpage {name, section}
@manpage{\name\, \section\,
https://man.freebsd.org/cgi/man.cgi?\name\(\section\)}
@end macro
@macro posixfuncmanpage {name}
@manpage{\name\, 3p,
https://pubs.opengroup.org/onlinepubs/9799919799/functions/\name\.html}
@end macro
@macro linuxdoc {name, path}
@uref{https://docs.kernel.org/\path\, \name\}
@end macro
@macro rfcdoc {name, path}
@uref{https://datatracker.ietf.org/doc/html/\path\, \name\}
@end macro
@macro tcldoc {name, path}
@uref{https://www.tcl.tk/man/tcl8.6.13/\path\, \name\}
@end macro
@copying
This manual is for BookmarkFS, version @value{VERSION}.
@quotation
Copyright @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''.
@end quotation
@end copying
@titlepage
@title BookmarkFS
@subtitle version @value{VERSION}
@author CismonX
@page
@vskip 0pt plus 1filll
@insertcopying
@end titlepage
@summarycontents
@contents
@node Top
@top BookmarkFS User Manual
@insertcopying
@node Overview
@chapter 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:
@itemize @bullet{}
@item Firefox
@item Chromium
@end itemize
To install BookmarkFS, refer to the @file{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 @uref{https://www.gnu.org/licenses/}.
BookmarkFS is
@uref{https://savannah.nongnu.org/projects/bookmarkfs, hosted on Savannah}.
Write to the
@uref{https://savannah.nongnu.org/mail/?group=bookmarkfs, mailing lists}
for bug reports, feature requests, and other discussions.
@node Porting BookmarkFS
@section 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 @linuxdoc{FUSE, 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
@uref{https://libfuse.github.io/doxygen/fuse__lowlevel_8h.html, low-level API}.
For a similar reason, @uref{https://github.com/winfsp/winfsp, WinFsp}
won't work if you're trying to port BookmarkFS to Microsoft Windows.
Other notable portability issues:
@table @asis
@item 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 @option{-o no_sandbox} option to
explicitly disable sandboxing.
Also @pxref{Sandboxing}.
@end table
@node Limitations on FreeBSD
@section Limitations on FreeBSD
Currently, the FreeBSD @freebsdmanpage{fusefs, 5} implementation does not
support @code{FUSE_IOCTL}.
All custom @freebsdmanpage{ioctl, 2} calls on a FUSE filesystem fail
with @code{ENOTTY} without the requests being sent to the FUSE server.
Thus, BookmarkFS features that depend on @code{ioctl()} do not work
on FreeBSD, which includes:
@itemize @bullet{}
@item @ref{Permute Directory Entries}
@item @ref{Online Filesystem Check}
@end itemize
Meanwhile, FreeBSD does not support @code{FUSE_READDIRPLUS} and directory
entry caching, which makes listing directory entries less efficient.
@node Sandboxing
@section 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:
@itemize @bullet{}
@item access local files other than the bookmark storage
@item establish socket connections
@item execute other files
@end itemize
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:
@itemize @bullet{}
@item Bookmark files that are @emph{not} created by the user using a trusted
program (e.g., a file obtained from some random person on the internet).
@item Filesystem calls from untrusted programs.
The program itself may be isolated, but it has a chance to escape
the isolated environment if it can exploit BookmarkFS.
@end itemize
On Linux, sandboxing is achieved using @linuxmanpage{seccomp, 2} and
@linuxmanpage{landlock, 7}.
On FreeBSD, @freebsdmanpage{capsicum, 4} is used.
@node The Utility Library
@section 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 (@pxref{Backends}) and the @command{mount.bookmarkfs} program.
Typically, the library is built into a shared object, and installed as:
@example
@var{$@{libdir@}}/bookmarkfs_util@var{$@{shlib_suffix@}}
@end example
@table @var
@item $@{libdir@}
Presumably @file{@var{$@{prefix@}}/lib}.
@xref{Uniform,, The Uniform Naming Scheme, automake, GNU Automake}.
@item $@{shlib_suffix@}
The common filename extension for shared library files on the current platform
(e.g., @file{.so} on GNU/Linux and FreeBSD).
@end table
Public headers are installed under @file{@var{$@{pkgincludedir@}}}
(presumably @file{@var{$@{prefix@}}/include/bookmarkfs}).
To use the library functions, include the following headers as needed:
@table @file
@item hash.h
Non-cryptographic hash function.
@item hashmap.h
A simple hashtable implementation.
@item prng.h
Non-cryptographic pseudo-random number generator.
@item sandbox.h
A simple sandbox implementation. @xref{Sandboxing}.
@item version.h
Get version and feature information of the library.
@item watcher.h
Single-file filesystem watcher.
@end table
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.
@node Programs
@chapter 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.
@xref{Backends}.
@node mount.bookmarkfs
@section @command{mount.bookmarkfs}
The @command{mount.bookmarkfs} program mounts a BookmarkFS filesystem.
@example
mount.bookmarkfs [@var{options}] @var{src} @var{target}
@end example
@table @var
@item src
The bookmark storage, presumably the pathname of a regular file
that contains bookmark data.
The exact interpretation of this option is backend-defined.
@item target
Pathname of the directory where the filesystem shall be mounted to
(i.e., the ``mountpoint'').
@end table
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 @samp{vfs.usermount}
@freebsdmanpage{sysctl, 8} to @t{1} for unprivileged mounts.
To unmount a BookmarkFS filesystem, run @linuxmanpage{fusermount3, 1} or
@linuxmanpage{umount, 8} on @var{target}.
The daemon process will automatically dismount the filesystem upon
@code{SIGINT} or @code{SIGTERM} receipt, however, it only works in
non-sandbox mode.
Options:
@table @option
@item -o backend=@var{name}
The backend used by the filesystem (@pxref{Backends}).
This option is mandatory.
@anchor{Alternative Backend Name}
Alternatively, @var{name} could be specified in
@samp{@var{lib_path}:@var{sym_name}} format, where:
@table @var
@item lib_path@
Presumably the pathname to the backend library.
Its exact interpretation is equivalent to the first argument for
@posixfuncmanpage{dlopen}.
@item sym_name
@xref{Backend Symbol Name Format}.
@end table
@item -o @@@var{key}[=@var{value}]
A backend-specific option.
This option can be provided multiple times.
@item -o accmode=@var{mode}
File access mode.
Defaults to @t{0700}.
This option applies to both directories and regular files.
Execution bits on regular files are masked off.
Should be used in combination with @option{-o allow_other} for other users to
access the files.
@anchor{File Modification/Change Time}
@cindex File Modification/Change Time
@item -o ctime
Maintain file change time, while modification time follows change time.
If this option is not provided, maintain file modification time instead.
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:
@table @asis
@item modification time
ctime only updates when mtime does.
@item 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.
@end table
The kernel may automatically update and cache ctime,
making it more ``correct'' than what we expect.
However, this behavior should not be relied upon.
@item -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).
@item -o file_max=@var{bytes}
Max file size limit.
Defaults to @t{32768}.
This limit also applies to extended attribute values.
@item -o no_sandbox
Do not enable sandboxing features (@pxref{Sandboxing}).
@anchor{Disabling Landlock}
@item -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.
@item -F
Stay in the foreground, do not daemonize.
@item -h
Print help text, and then exit.
@item -V
Print version and feature information, and then exit.
@end table
Unrecognized options specified with @option{-o} are passed to libfuse
(and subsequently to the kernel, if applicable) as-is.
Notable options:
@table @option
@item -o rw
Mount the filesystem read/write.
@quotation Warning
Always backup the bookmark storage before mounting it read/write,
or risk losing your data!
@end quotation
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.
@item -o debug
Set libfuse log level to @code{FUSE_LOG_DEBUG}.
Log messages related to each FUSE request will be printed to standard error.
@item -o fsname=@var{name}
Name for the filesystem.
Defaults to the backend name.
This name is equivalent to the @code{fs_spec} field in @linuxmanpage{fstab, 5},
and appears as the @samp{SOURCE} column in @linuxmanpage{findmnt, 8} output.
@item -o atime,diratime,relatime
These options (and other atime-related ones) are ignored.
BookmarkFS only supports @option{noatime} mounts,
since the ``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 @posixfuncmanpage{futimens}).
@end table
@node fsck.bookmarkfs
@section @command{fsck.bookmarkfs}
The @command{fsck.bookmarkfs} program checks and optionally repairs a
BookmarkFS filesystem.
@example
fsck.bookmarkfs [@var{options}] @var{pathname}
@end example
Filesystem check on BookmarkFS has a different purpose compared to
on-disk filesystems.
@xref{Filesystem Check}.
Depending on the options specified, filesystem check works either in
online or offline mode:
@table @asis
@item Online Mode
In online mode, fsck is performed on a mounted BookmarkFS filesystem
using @code{ioctl()}.
@xref{Online Filesystem Check}.
The @var{pathname} argument refers to the directory to operate on.
@item Offline Mode
In offline mode, fsck is performed directly on the bookmark storage via the
corresponding backend.
The @var{pathname} argument is the path to the bookmark storage, equivalent to
the @var{src} argument given to @command{mount.bookmarkfs}.
Alternatively, @var{pathname} could be specified in @samp{@var{src}:@var{dir}}
format, where @var{dir} refers to the directory to operate on,
relative to the root directory of the subsystem.
@end table
Options:
@table @option
@item -o backend=@var{name}
The backend used by the filesystem.
@xref{Backends}.
Value of @var{name} could be specified in an alternative format.
@xref{Alternative Backend Name}.
If this option is not provided, or @var{name} is empty, performs online fsck.
Otherwise perform offline fsck.
@item -o @@@var{key}[=@var{value}]
A backend-specific option.
This option can be provided multiple times.
@item -o handler=@var{name}
The handler for resolving errors found during fsck
(@pxref{Filesystem-Check Handlers}).
Alternatively, @var{name} could be specified in
@samp{@var{lib_path}:@var{sym_name}} format, where:
@table @var
@item lib_path@
Presumably the pathname to the fsck handler library.
Its exact interpretation is equivalent to the first argument for
@posixfuncmanpage{dlopen}.
@item sym_name
@xref{Filesystem-Check Handler Symbol Name Format}.
@end table
If this option is not provided, or @var{name} is empty,
a built-in handler will be used.
@xref{Built-in Handler}.
@item -o %@var{key}[=@var{value}]
A handler-specific option.
This option can be provided multiple times.
@item -o repair
Attempt to repair errors found during fsck.
@quotation Warning
Always backup the bookmark storage before repairing, or risk losing your data!
@end quotation
@item -o rl_app=@var{name}
Readline application name in interactive mode.
Defaults to @samp{fsck.bookmarkfs}.
@xref{Conditional Init Constructs,,, readline, GNU Readline Library}.
@item -o type=bookmark|tag|keyword
Subsystem type (@pxref{Filesystem Hierarchy}).
Defaults to @samp{bookmark}.
This option is ignored when performing online fsck.
@item -i
Enable interactive mode.
@item -R
Perform fsck on subdirectories recursively.
This option is ignored when performing fsck on tags or keywords.
@item -o no_sandbox
Do not enable sandboxing features.
@xref{Sandboxing}.
@item -o no_landlock
Do not use Landlock for sandboxing.
This option is ignored on non-Linux platforms.
Also @pxref{Disabling Landlock}.
@item -h
Print help text, and then exit.
@item -V
Print version and feature information, and then exit.
@end table
@node mkfs.bookmarkfs
@section @command{mkfs.bookmarkfs}
The @command{mkfs.bookmarkfs} program creates a new BookmarkFS filesystem.
@example
mkfs.bookmarkfs [@var{options}] @var{pathname}
@end example
@table @var
@item pathname
The underlying bookmark storage for the new filesystem.
This option is equivalent to the @var{src} argument for
@command{mount.bookmarkfs}.
@end table
Options:
@table @option
@item -o backend=@var{name}
The backend used by the filesystem (@pxref{Backends}).
This option is mandatory.
Value of @var{name} could be specified in an alternative format.
@xref{Alternative Backend Name}.
@item -o @@@var{key}[=@var{value}]
A backend-specific option.
This option can be provided multiple times.
@item -o force
If the file referred to by @var{pathname} already exists, overwrite it.
@item -h
Print help text, and then exit.
@item -V
Print version and feature information, and then exit.
@end table
@node bookmarkctl
@section @command{bookmarkctl}
The @command{bookmarkctl} program is a command-line wrapper for various
I/O controls of a BookmarkFS filesystem.
@example
bookmarkctl @var{subcmd} [@var{args}]
@end example
Sub-commands:
@table @command
@item permd
Re-arranges the order of the directory entries obtained from @code{readdir()}.
@xref{Permute Directory Entries}.
@example
bookmarkctl permd @var{pathname} @var{op} @var{name1} @var{name2}
@end example
@table @var
@item pathname
Path to the directory.
@item name1
@item name2
Filename of entries under the directory.
@item op
Operation to perform on the directory:
@table @samp
@item swap
Exchange the positions of the directory entries represented by @var{name1}
and @var{name2}.
@item move-before
Move the directory entry represented by @var{name1} to the position just
@emph{before} the one represented by @var{name2}.
@item move-after
Move the directory entry represented by @var{name1} to the position just
@emph{after} the one represented by @var{name2}.
@end table
@end table
@item fsck
Check for errors within a BookmarkFS filesystem.
@xref{Filesystem Check}.
@example
bookmarkctl fsck @var{pathname} @var{op}
@end example
@table @var
@item pathname
Path to the directory to perform checks on.
@item op
Operation to perform on the directory:
@table @samp
@item list
Display a list of errors found under the given directory.
Will not recurse into subdirectories.
@end table
@end table
For the full fsck functionalities, @pxref{fsck.bookmarkfs}.
@item help
Print help text, and then exit.
@item version
Print version information, and then exit.
@end table
@node Filesystem
@chapter The Filesystem
When a BookmarkFS filesystem is mounted using the @command{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, @pxref{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.
@node Filesystem Hierarchy
@section Filesystem Hierarchy
BookmarkFS has multiple subsystems.
Each one appears as a directory under the mountpoint:
@example
@var{$@{mountpoint@}}/bookmarks
@var{$@{mountpoint@}}/tags
@var{$@{mountpoint@}}/keywords
@end example
If the backend does not support a subsystem, the corresponding directory
does not exist.
Currently all subsystem definitions are hard-coded within the
@command{mount.bookmarkfs} program, and cannot be extended by the backend.
@node Bookmarks
@subsection Bookmarks
The ``bookmarks'' subsystem maintains the hierarchical structure, names, URLs
and other information of a bookmark storage.
@example
@var{$@{mountpoint@}}/bookmarks/@var{$@{bookmark_dir...@}}/@var{$@{bookmark@}}
@end example
Each bookmark folder name appears as the filename for directory
@var{$@{bookmark_dir@}}, and each @var{$@{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 @code{readdir()} calls.
To deal with such bookmarks, @pxref{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:
@table @code
@item st_ino
ID of the bookmark (stored as lower bits).
@item st_size
Length of the bookmark URL in bytes.
Always @t{0} for directories.
@item st_atim
Last access time of the bookmark.
@item st_mtim
Last modification time of the bookmark.
@xref{File Modification/Change Time}.
@end table
Additional information of a bookmark or bookmark folder can be accessed via
the extended attributes of the corresponding file, for backends that
supports it.
@xref{Extended Attributes}.
@node Tags
@subsection Tags
The ``tags'' subsystem maintains a many-to-many mapping between bookmarks and
their alternative names.
@example
@var{$@{mountpoint@}}/tags/@var{$@{tag_dir@}}/@var{$@{bookmark@}}
@end example
Each tag name appears as the filename for directory @var{$@{tag_dir@}},
and @var{$@{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 @code{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 @posixfuncmanpage{link}:
@example c
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!
@end example
Make sure that the two files have identical names, otherwise @code{link()}
fails with @code{EPERM}.
@node Keywords
@subsection Keywords
The ``keywords'' subsystem maintains a one-to-one mapping between bookmarks
and their alternative names, independent from tag names.
@example
@var{$@{mountpoint@}}/keywords/@var{$@{keyword_name@}}
@end example
Each keyword name appears as the filename for regular file
@var{$@{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 @code{link()} like we do with tags.
If the original file is already associated with another keyword,
@code{link()} fails with @code{EEXIST}.
@node Error Codes
@section 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:
@table @code
@item EPERM
Attempting to perform an unsupported operation.
For example:
@itemize @bullet{}
@item @code{chmod()}, @code{chown()}, and other operations that make no sense
for web browser bookmarks.
@item Moving a file across subsystems.
@item Creating a bookmark file with a name that is not valid UTF-8
(on Chromium backend).
@end itemize
@item 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.
@item 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.
@end table
Other BookmarkFS-specific errors may occur.
See the corresponding manual section for details.
@node Extended Attributes
@section Extended Attributes
BookmarkFS uses extended attributes to manage additional information associated
with a bookmark.
Extended attributes is a platform-specific feature.
On Linux, see @linuxmanpage{xattr, 7}.
On FreeBSD, see @freebsdmanpage{extattr, 2}.
All BookmarkFS extended attributes fall under the ``user'' namespace,
which means they have a @samp{user.} name prefix on Linux, and should be
accessed with @code{EXTATTR_NAMESPACE_USER} on FreeBSD.
All attributes have a @samp{bookmarkfs.} name prefix.
For example, to get the GUID of a bookmark file (Firefox backend):
@example c
// Linux
len = fgetxattr(fd, "user.bookmarkfs.guid", buf, sizeof(buf));
// FreeBSD
len = extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, "bookmarkfs.guid",
buf, sizeof(buf));
@end example
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.
@node Directory Entries
@section Directory Entries
@table @asis
@item The @samp{.} and @samp{..} entries
POSIX consider @samp{.} and @samp{..} 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 (@pxref{Filesystem Check}).
@anchor{Directory Entry Ordering}
@cindex Directory Entry Ordering
@item Ordering of directory entries
POSIX does not specify the ordering of the directory entries retrieved from
the directory stream using @posixfuncmanpage{readdir}.
It only guarantees that if an entry is not added or removed from the directory
after the most recent call to @posixfuncmanpage{opendir} or
@posixfuncmanpage{rewinddir}, 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
@linuxdoc{htree, 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, @pxref{Permute Directory Entries}.
@end table
@node Permute Directory Entries
@subsection Permute Directory Entries
BookmarkFS provides an I/O control for rearranging directory entries:
@example c
#include <bookmarkfs/ioctl.h>
int ioctl (int dirfd, BOOKMARKFS_IOC_PERMD,
struct bookmarkfs_permd_data const *argp);
@end example
The @code{bookmarkfs_permd_data} structure is defined as:
@example c
struct bookmarkfs_permd_data @{
enum bookmarkfs_permd_op op;
char name1[NAME_MAX + 1];
char name2[NAME_MAX + 1];
@};
@end example
The @code{op} field denotes the operation to perform on the directory:
@table @code
@item BOOKMARKFS_PERMD_OP_SWAP
Exchange the positions of the directory entries represented by @code{name1}
and @code{name2}.
@item BOOKMARKFS_PERMD_OP_MOVE_BEFORE
Move the directory entry represented by @code{name1} to the position just
@emph{before} the one represented by @code{name2}.
@item BOOKMARKFS_PERMD_OP_MOVE_AFTER
Move the directory entry represented by @code{name1} to the position just
@emph{after} the one represented by @code{name2}.
@end table
On success, @code{ioctl()} returns @t{0}.
Otherwise, it returns @t{-1} and sets @code{errno}:
@table @code
@item EACCES
Write or search permission is denied for the directory.
@item EINVAL
@code{op} is not one of the values defined in enum @code{bookmarkfs_permd_op}.
@item EINVAL
@code{name1} or @code{name2} is not a valid filename
(e.g., empty string; contains @samp{/} character).
@item ENOENT
The directory does not contain entries named @code{name1} or @code{name2}.
@item ENOTTY
The kernel does not support @code{FUSE_IOCTL}.
@xref{Limitations on FreeBSD}.
@item EPERM
The backend does not support rearranging entries for this directory.
@end table
To ensure that the order change is visible to further @code{readdir()} calls,
@code{fsync()} or @code{close()} the directory.
@node Filesystem Check
@section 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:
@itemize @bullet{}
@item must not contain @samp{/} characters
@item must not be empty or longer than @code{NAME_MAX}
@item must not duplicate with another file in the same directory
@item @samp{.} and @samp{..} must refer to the current and parent directory
@end itemize
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.
@node Online Filesystem Check
@subsection Online Filesystem Check
To perform filesystem check on a mounted BookmarkFS filesystem,
use the following I/O controls:
@example c
#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);
@end example
The @code{bookmarkfs_fsck_data} structure is defined as:
@example c
struct bookmarkfs_fsck_data @{
uint64_t id;
uint64_t extra;
char name[NAME_MAX + 1];
@};
@end example
Filesystem-check commands:
@table @code
@item BOOKMARKFS_IOC_FSCK_NEXT
Find the next bookmark with invalid name under the directory.
@anchor{Filesystem-Check Result Code}
@cindex Filesystem-Check Result Code
On success, @code{ioctl()} updates @var{argp}, and returns
one of the values defined in enum @code{bookmarkfs_fsck_result}:
@table @code
@item BOOKMARKFS_FSCK_RESULT_END
There are no more bookmarks with invalid name under the directory.
Fields in @var{argp} have unspecified values.
@item BOOKMARKFS_FSCK_RESULT_NAME_DUPLICATE
The bookmark name duplicates with another bookmark which appears earlier
in the directory stream.
Value of @code{extra} is the ID of the other bookmark.
@item BOOKMARKFS_FSCK_RESULT_NAME_BADCHAR
The bookmark contains a bad character (i.e., the ASCII @samp{/} character).
Value of @code{extra} is the byte offset where the bad character first appears
in @code{name}.
@item BOOKMARKFS_FSCK_RESULT_NAME_BADLEN
The bookmark name is either longer than @code{NAME_MAX}, or an empty string.
Value of @code{extra} is the length of the bookmark name, and @code{name}
is truncated if longer than @code{NAME_MAX}.
@item BOOKMARKFS_FSCK_RESULT_NAME_DOTDOT
The bookmark name is either @samp{.} or @samp{..}.
Value of @code{extra} is unspecified.
@end table
On failure, @code{ioctl()} returns @t{-1}, and sets @code{errno}.
@item BOOKMARKFS_IOC_FSCK_APPLY
``Repair'' a bookmark by renaming it.
The @code{id} field must be set to a value previously obtained from
@code{BOOKMARKFS_IOC_FSCK_NEXT} with the same @var{dirfd},
otherwise the behavior is undefined.
The @code{name} field should be set to the new name for the bookmark.
The @code{extra} field is unused.
On success, @code{ioctl()} updates @var{argp}, and returns
one of the values defined in enum @code{bookmarkfs_fsck_result},
like with @code{BOOKMARKFS_IOC_FSCK_NEXT}.
Additionally, it may also return:
@table @code
@item BOOKMARKFS_FSCK_RESULT_NAME_INVALID
The new name is not a valid bookmark name.
Value of @code{extra} is a backend-specific reason code
explaining why the bookmark name is invalid.
It may equal to one of the following predefined values:
@table @code
@item BOOKMARKFS_NAME_INVALID_REASON_NOTUTF8
The name is not valid UTF-8.
@end table
@end table
On failure, @code{ioctl()} returns @t{-1}, and sets @code{errno}:
@table @code
@item EACCES
Write or search permission is denied for the directory.
@end table
To ensure that the rename is visible to further lookups and @code{readdir()}
calls, @code{fsync()} or @code{close()} the directory.
@item BOOKMARKFS_IOC_FSCK_REWIND
Reset the fsck state for the directory.
Further @code{BOOKMARKFS_IOC_FSCK_NEXT} requests will start from the
beginning of the directory stream.
On success, @code{ioctl()} returns @t{0}.
Otherwise, it returns @t{-1}, and sets @code{errno}.
@end table
Common error codes for all filesystem-check ioctls:
@table @code
@item ENOTTY
The kernel does not support @code{FUSE_IOCTL}.
@xref{Limitations on FreeBSD}.
@item EPERM
The backend does not support fsck for this directory.
@end table
@node Backends
@chapter 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:
@example
@var{$@{pkglibdir@}}/backend_@var{$@{name@}}@var{$@{shlib_suffix@}}
@end example
@table @var
@item $@{pkglibdir@}
Presumably @file{@var{$@{prefix@}}/lib/bookmarkfs}.
@xref{Uniform,, The Uniform Naming Scheme, automake, GNU Automake}.
@item $@{name@}
The backend name, equivalent to the value given to the
@option{-o backend=@var{name}} option of frontend programs.
@item $@{shlib_suffix@}
The common filename extension for shared library files on the current platform
(e.g., @file{.so} on GNU/Linux and FreeBSD).
@end table
@node Firefox Backend
@section Firefox Backend
The Firefox backend provides access to the bookmark data of the web browser
@uref{https://www.mozilla.org/en-US/firefox/new/, Mozilla Firefox}
and its derivatives, notably @uref{https://www.torproject.org/, Tor Browser}
and @uref{https://librewolf.net/, Librewolf}.
Firefox bookmarks are stored in a SQLite database under the profile directory:
@example
~/.mozilla/firefox/@var{$@{profile_name@}}/places.sqlite
@end example
When mounting the filesystem, this pathname shall be passed as the @var{src}
argument (@pxref{mount.bookmarkfs}).
Actual path for the profile directories may differ across distributions.
Backend name: @samp{firefox}.
Backend-specific options (@command{mount.bookmarkfs} only):
@table @option
@item filename=title|guid
Whether to use the bookmark title or GUID as the bookmark file name.
Defaults to @samp{title}.
A bookmark GUID is a @rfcdoc{base64url-encoded, rfc4648#section-5}
128-bit string uniquely associated with a bookmark or bookmark folder.
When creating a new file:
@table @samp
@item title
The GUID is randomly generated by the backend.
@item guid
The filename must be a valid GUID, and must not duplicate with other files
on the same filesystem, otherwise @code{open()} or @code{mkdir()} fails
with @code{EPERM}.
Also set the GUID string as the bookmark title.
@end table
@item lock=exclusive|normal
The database connection locking mode for the bookmark storage.
Defaults to @samp{normal} when the filesystem is mounted read-only,
@samp{exclusive} otherwise.
This option corresponds to the
@uref{https://www.sqlite.org/pragma.html#pragma_locking_mode,
@code{locking_mode}} pragma on SQLite.
With @option{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 @code{storage.sqlite.exclusiveLock.enabled} browser preference
to @code{false}.
@item 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 @option{filename=guid}.
This option may improve @code{readdir()} performance, however,
making a false assumption results in a directory entry with duplicate names.
It is recommended to perform a full filesystem check (@pxref{Filesystem Check})
on the bookmark storage before mounting with this option.
@end table
If launched from @command{fsck.bookmarkfs}, all backend-specific options
are ignored, and always enforces the @option{lock=exclusive} option.
Backend-specific options (@command{mkfs.bookmarkfs} only):
@table @option
@item date_added=@var{timestamp}
File creation time for the predefined bookmark directories.
Defaults to the current time.
Format of @var{timestamp} is equivalent to the @samp{date_added}
extended attribute (see below).
@end table
Extended attributes:
@table @samp
@item title
The bookmark title.
Only available with @option{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.
@item guid
The bookmark GUID.
Only available with @option{filename=title}.
@item description
An arbitrary text associated with the bookmark.
Usually, when bookmarking a page in the browser, this value is
set to the @code{content} attribute of the @code{<meta>} element
whose @code{name} is @samp{description}.
@item date_added
The bookmark creation time.
Value is a decimal integer representing number of microseconds since
the Unix epoch.
@end table
@node Chromium Backend
@section Chromium Backend
The Chromium backend provides access to the bookmark data of the web browser
@uref{https://www.chromium.org/Home/, Chromium} and its derivatives,
notably @uref{https://github.com/ungoogled-software/ungoogled-chromium,
ungoogled-chromium}.
Chromium bookmarks are stored in a text file (in JSON format)
under the profile directory:
@example
~/.config/chromium/@var{$@{profile_name@}}/Bookmarks
@end example
When mounting the filesystem, this pathname shall be passed as the @var{src}
argument (@pxref{mount.bookmarkfs}).
Actual path for the profile directories may differ across distributions.
Backend name: @samp{chromium}.
Backend-specific options (@command{mount.bookmarkfs} only):
@table @option
@item filename=title|guid
Whether to use the bookmark title or GUID as the bookmark file name.
Defaults to @samp{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:
@example
0bc5d13f-2cba-5d74-951f-3f233fe6c908
@end example
When creating a new file:
@table @samp
@item title
The GUID is randomly generated by the backend.
It is guaranteed to be a valid version 4 UUID as specified by
@rfcdoc{RFC 4122, rfc4122}.
@item guid
The filename must be a valid GUID, and must not duplicate with other files
on the same filesystem, otherwise @code{open()} or @code{mkdir()} fails
with @code{EPERM}.
Also set the GUID string as the bookmark title.
@end table
@item watcher=native|fallback|none
The file watcher to use for the bookmark storage.
Defaults to @samp{native} when the filesystem is mounted read-only,
@samp{none} otherwise.
@table @samp
@item native
Watch for file changes using platform-specific features:
@table @asis
@item Linux
@linuxmanpage{fanotify, 7} is used.
Requires kernel version 5.13 or later for unprivileged users.
@linuxmanpage{inotify, 7} does not have this limitation, however,
it is incompatible with our sandboxing design.
@item FreeBSD
@freebsdmanpage{kevent, 2} with @code{EVFILT_VNODE} is used.
@end table
@item fallback
Watch for file changes by checking @code{st_ino} and @code{st_mtim} attributes
with @code{fstatat()} periodically.
Less efficient than ``native'' implementations, but should work on any
POSIX-compatible system.
@item none
Do not watch for file changes.
With @option{watcher=none}, changes on the bookmark storage are not visible
to the filesystem.
@end table
@end table
If launched from @command{fsck.bookmarkfs}, all backend-specific options
are ignored, and always enforces the @option{watcher=none} option.
Backend-specific options (@command{mkfs.bookmarkfs} only):
@table @option
@item date_added=@var{timestamp}
File creation time for the predefined bookmark directories.
Defaults to the current time.
Format of @var{timestamp} is equivalent to the @samp{date_added}
extended attribute (see below).
@end table
Extended attributes:
@table @samp
@item title
The bookmark title.
Only available with @option{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.
@item guid
The bookmark GUID.
Only available with @option{filename=title}.
@item date_added
The bookmark creation time.
Value is a decimal integer representing number of microseconds since
the Windows @code{FILETIME} epoch (134774 days ahead of the Unix epoch).
@end table
Notable limitations:
@itemize @bullet{}
@item 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.
@item No support for tags (@pxref{Tags}) and keywords (@pxref{Keywords}),
since Chromium does not have such concepts.
@end itemize
@node Backend API
@section Backend API
The Backend API specifies how a BookmarkFS backend communicates with a
frontend program (e.g., @command{mount.bookmarkfs}).
@quotation Warning
Currently BookmarkFS is experimental.
The Backend API may change drastically without prior notice.
@end quotation
@anchor{Backend Symbol Name Format}
@cindex Backend Symbol Name Format
To implement the Backend API, a backend library should expose a symbol
with the following name:
@example
bookmarkfs_backend_@var{$@{name@}}
@end example
Where @var{$@{name@}} is equivalent to the value given to the
@option{-o backend=@var{name}} option of frontend programs.
The symbol should name a data object identifier of the following type:
@example c
#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_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_fsck_func *bookmark_fsck;
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;
@};
@end example
Each field is a function pointer provided by the backend.
Some can be optional and set to @code{NULL}, if the backend
does not support the corresponding features.
@node Initialize Backend
@subsection Initialize Backend
The @code{backend_init} function is called before any other functions
(except for @code{backend_info}).
If not @code{NULL}, it is guaranteed to be called exactly once
throughout the lifetime of the process.
Type of the @code{backend_init} function is defined as:
@example c
typedef int (bookmarkfs_backend_init_func) (
uint32_t flags
);
@end example
Function arguments:
@table @code
@item flags
A bit array of the following flags:
@table @code
@item BOOKMARKFS_BACKEND_LIB_READY
Indicates that the utility library is already initialized.
@xref{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.
@item BOOKMARKFS_FRONTEND_FSCK
@item BOOKMARKFS_FRONTEND_MOUNT
@item BOOKMARKFS_FRONTEND_MKFS
Denotes the frontend program that launches the backend.
These flags are mutually exclusive.
@end table
@end table
The function should return @t{0} on success, and @t{-1} on error.
@node Get Backend Information
@subsection Get Backend Information
The @code{backend_info} function is called to print information
about the backend.
It can be @code{NULL}.
When this function is called, the backend should write a human-readable
message of the corresponding information to standard output.
Type of the @code{backend_info} function is defined as:
@example c
typedef void (bookmarkfs_backend_info_func) (
uint32_t flags
);
@end example
Function arguments:
@table @code
@item flags
A bit array of the following flags:
@table @code
@item BOOKMARKFS_BACKEND_INFO_HELP
Indicates that the backend should print a help message.
Mutually exclusive with @code{BOOKMARKFS_BACKEND_INFO_VERSION}.
The message should contain a brief description of this backend,
as well as a list of backend-specific options.
@item BOOKMARKFS_BACKEND_INFO_VERSION
Indicates that the backend should print a version message.
Mutually exclusive with @code{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.
@item BOOKMARKFS_FRONTEND_FSCK
@item BOOKMARKFS_FRONTEND_MOUNT
@item BOOKMARKFS_FRONTEND_MKFS
Denotes the frontend program that launches the backend.
These flags are mutually exclusive.
@end table
@end table
@node Create Backend Context
@subsection Create Backend Context
A backend context maintains internal states for manipulating a specific
bookmark storage.
Each BookmarkFS filesystem mounted by @command{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 @code{backend_create} function is called.
It must not be @code{NULL}.
Type of the @code{backend_create} function is defined as:
@example c
typedef int (bookmarkfs_backend_create_func) (
struct bookmarkfs_backend_conf const *conf,
struct bookmarkfs_backend_create_resp *resp
);
@end example
Function arguments:
@table @code
@item conf
Backend configuration items.
@anchor{Backend Configuration}
The @code{bookmarkfs_backend_conf} structure is defined as:
@example c
struct bookmarkfs_backend_conf @{
uint32_t version;
uint32_t flags;
char *store_path;
struct bookmarkfs_conf_opt *opts;
@};
@end example
@table @code
@item version
Version number of the frontend program, equivalent to the
@code{BOOKMARKFS_VERNUM} macro in @file{@var{$@{pkgincludedir@}}/version.h}.
@item flags
A bit array of the following flags:
@table @code
@item BOOKMARKFS_BACKEND_READONLY
Indicates that the filesystem is mounted read-only, and the frontend program
will not call functions that may write to the bookmark storage.
@item BOOKMARKFS_BACKEND_CTIME
Indicates that the @option{-o ctime} option is given to
@code{mount.bookmarkfs}.
@item BOOKMARKFS_BACKEND_NO_SANDBOX
Indicates that the @option{-o no_sandbox} option is given to
@code{mount.bookmarkfs}.
If the backend does not support sandboxing, @code{backend_create} should fail.
Otherwise, function @code{backend_sandbox} should be implemented.
@xref{Enter Sandbox}.
@item BOOKMARKFS_BACKEND_NO_LANDLOCK
Indicates that the @option{-o no_landlock} option is given to
@code{mount.bookmarkfs}.
@item BOOKMARKFS_BACKEND_FSCK_ONLY
Indicates that the backend is launched from @command{fsck.bookmarkfs}.
The backend must claim exclusive access to the bookmark storage, and only
the @code{bookmark_lookup}, @code{bookmark_list}, @code{bookmark_fsck}
and @code{bookmark_sync} functions will be called on the bookmarks.
@end table
@item store_path
Path to the bookmark storage, equivalent to the @var{src} argument passed to
@command{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.
@item opts
Linked list of backend-specific options, @code{NULL} if there are none.
@anchor{Backend-Specific Options}
The @code{bookmarkfs_conf_opt} structure is defined as:
@example c
struct bookmarkfs_conf_opt @{
char *key;
char *val;
struct bookmarkfs_conf_opt *next;
@};
@end example
@table @code
@item key
@item val
Key and value of the current option.
If the option does not have a value (no @samp{=} character present),
@code{val} is @code{NULL}.
When not @code{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.
@item next
Pointer to the next option, @code{NULL} if there are no more options.
@end table
The backend must not re-assign the fields in @code{opt}.
@end table
@item resp
Information of the new backend context.
The initial content of this argument is unspecified.
When the backend context is successfully created, the backend should
populate this argument with appropriate values.
@anchor{Backend-Create Response}
The @code{bookmarkfs_backend_create_resp} structure is defined as:
@example c
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;
@};
@end example
@table @code
@item name
Name of the backend context, used as default value for the
@option{-o fsname=@var{name}} option of @command{mount.bookmarkfs}.
Must be a NUL-terminated string valid at least until the next function call
on this backend context.
@item backend_ctx
An opaque pointer referring to the backend context.
The pointer will be passed to further function calls on this context.
@item bookmarks_root_id
ID of the bookmarks root directory
(i.e., @file{@var{$@{mountpoint@}}/bookmarks}).
Must not be greater than @code{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 @code{UINT64_MAX}.
@item tags_root_id
ID of the tags root directory (i.e., @file{@var{$@{mountpoint@}}/tags}).
Must not be greater than @code{BOOKMARKFS_MAX_ID}.
This field should be left as-is or set to @code{UINT64_MAX},
if one of the following conditions is met:
@itemize @bullet{}
@item Sandboxing is requested, and the ID cannot be determined in a safe way
before entering sandbox.
@item The backend does not support tags for this context.
@xref{Tags}.
@end itemize
@item bookmark_attrs
Names of extended attributes (@pxref{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.
@item flags
A bit array of the following flags:
@table @code
@item 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 frontend program may perform optimizations based on
this assumption.
@item BOOKMARKFS_BACKEND_HAS_KEYWORD
Indicates that the backend supports keywords for this context.
@xref{Keywords}.
@end table
@end table
@end table
The function should return @t{0} on success, and @t{-1} on error.
@node Destroy Backend Context
@subsection Destroy Backend Context
When a backend context is no longer used, the @code{backend_destroy} function
is called.
It must not be @code{NULL}.
Upon destruction, the backend should release all system resources
associated with this context.
If changes have been made to the bookmark storage, the backend should
try to persist them.
Type of the @code{backend_destroy} function is defined as:
@example c
typedef void (bookmarkfs_backend_destroy_func) (
void *backend_ctx
);
@end example
Function arguments:
@table @code
@item backend_ctx
The pointer referring to the backend context.
@end table
@node Enter Sandbox
@subsection Enter Sandbox
The @code{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.
@xref{Sandboxing}.
This function is only called if the @code{BOOKMARKFS_BACKEND_NO_SANDBOX} flag
is @emph{not} given to @code{backend_create} when creating the current context.
It can be @code{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
@command{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 (@pxref{The Utility Library}).
It may require some tweaking to be used for other backends.
Type of the @code{backend_sandbox} function is defined as:
@example c
typedef int (bookmarkfs_backend_sandbox_func) (
void *backend_ctx,
struct bookmarkfs_backend_create_resp *resp
);
@end example
Function arguments:
@table @code
@item backend_ctx
The pointer referring to the backend context.
@item resp
@xref{Backend-Create Response} for the definition of the
@code{bookmarkfs_backend_create_resp} structure.
Fields in this argument should be left as-is, except for:
@table @code
@item bookmarks_root_id
This field must be set to the ID of the bookmarks root directory,
if not already done so in @code{backend_create}.
@item tags_root_id
This field should be set to the ID of the tags root directory,
if not already done so in @code{backend_create}.
If the backend does not support tags for this context, this field should be
left as-is or set to @code{UINT64_MAX}.
@end table
@end table
The function should return @t{0} on success, and @t{-1} on error.
Once this function fails, no further function calls will be performed
on the backend except for @code{backend_destroy}.
@node Lookup Bookmark
@subsection Lookup Bookmark
The @code{bookmark_lookup} function is called to obtain the attributes of
a bookmark, or a bookmark-related object.
It must not be @code{NULL}.
Type of the @code{bookmark_lookup} function is defined as:
@example c
typedef int (bookmarkfs_bookmark_lookup_func) (
void *backend_ctx,
uint64_t id,
char const *name,
uint32_t flags,
struct bookmarkfs_bookmark_stat *stat_buf
);
@end example
Function arguments:
@table @code
@item backend_ctx
The pointer referring to the backend context.
@item id
The subsystem object ID to be used for lookup.
It could either be a bookmark ID or a tag ID,
depending on the value of @code{flags}.
@item name
Name of the subsystem object to lookup.
It could be a bookmark name, tag name or keyword name,
depending on the value of @code{flags}.
When not @code{NULL}, @code{name} is guaranteed to be a valid filename,
which should be used to lookup an object under the directory referred to
by @code{id}.
Otherwise, the function should operate on the object referred to
by @code{id} itself.
@item flags
A bit array of the following flags:
@table @code
@item BOOKMARKFS_BOOKMARK_TYPE_MASK
The subsystem to operate on.
Equals to one of the following values (after shifting):
@table @code
@item BOOKMARKFS_BOOKMARK_TYPE_BOOKMARK
Lookup a bookmark or bookmark folder.
@xref{Bookmarks}.
@code{id} and @code{name} refers to bookmark ID and bookmark name.
@item BOOKMARKFS_BOOKMARK_TYPE_TAG
Lookup a tag, or a bookmark under a tag.
@xref{Tags}.
@code{id} and @code{name} refers to tag ID and tag name.
@item BOOKMARKFS_BOOKMARK_TYPE_KEYWORD
Lookup a keyword.
@xref{Keywords}.
@code{name} refers to keyword name (must not be @code{NULL}).
The value of @code{id} is unspecified.
@end table
@end table
@item stat_buf
Attributes of the object to lookup.
The initial content of this argument is unspecified.
When the object is successfully found, the backend should populate
this argument with appropriate values.
@anchor{Bookmark Attributes}
The @code{bookmarkfs_bookmark_stat} structure is defined as:
@example c
struct bookmarkfs_bookmark_stat @{
uint64_t id;
ssize_t value_len;
struct timespec atime;
struct timespec mtime;
@};
@end example
@table @code
@item id
ID of the object.
@item value_len
Length of the object value in bytes.
@t{-1} if the object refers to a directory.
@item atime
@item mtime
Last access and modification time of the object.
Values should be the time elapsed since the Unix epoch.
@end table
@end table
On success, the function should return @t{0}.
Otherwise, it should return a negated @code{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 @pxref{Error Codes}.
@node List Bookmarks
@subsection List Bookmarks
The @code{bookmark_list} function is called to list the bookmarks or
bookmark-related objects under a directory.
It must not be @code{NULL}.
Type of the @code{bookmark_list} function is defined as:
@example c
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
);
@end example
Function arguments:
@table @code
@item backend_ctx
The pointer referring to the backend context.
@item id
The subsystem object ID of the directory.
It could either be a bookmark ID or a tag ID,
depending on the value of @code{flags}.
@item off
Position of the current @code{bookmark_list} operation, equivalent to
the second argument for @posixfuncmanpage{seekdir}.
Initially, the value is always @t{0}.
Subsequent calls (with the same cookie) use the values obtained from
@code{entry->off} of @code{callback} function calls.
If not, the position is unspecified.
A subsequent call with a @t{0} value indicates @code{rewinddir()}.
@item flags
A bit array of the following flags:
@table @code
@item BOOKMARKFS_BOOKMARK_LIST_WITHSTAT
Indicates that the caller requires the attributes of the objects in the list.
@item BOOKMARKFS_BOOKMARK_TYPE_MASK
The subsystem to operate on.
Equals to one of the following values (after shifting):
@table @code
@item BOOKMARKFS_BOOKMARK_TYPE_BOOKMARK
List bookmarks (and bookmark folders) under a bookmark folder.
@xref{Bookmarks}.
Value of @code{id} refers to the ID of a bookmark folder.
@item BOOKMARKFS_BOOKMARK_TYPE_TAG
List tags, or bookmarks under a tag.
@xref{Tags}.
Value of @code{id} refers to the ID of a tag folder.
@item BOOKMARKFS_BOOKMARK_TYPE_KEYWORD
List keywords.
@xref{Keywords}.
Value of @code{id} is unspecified.
@end table
@end table
@item callback
Function to be called for each object found under the directory.
If @code{NULL}, @code{bookmark_list} should return immediately
without changing current position.
Ordering of the objects should follow the ordering of which they appear
in the browser.
@xref{Directory Entry Ordering}.
Type of the @code{callback} function is defined as:
@example c
typedef int (bookmarkfs_bookmark_list_cb) (
void *user_data,
struct bookmarkfs_bookmark_entry const *entry
);
@end example
Function arguments:
@table @code
@item user_data
Must be equal to the @code{user_data} argument value for the current
@code{bookmark_list} call.
@item entry
Information of this object.
The @code{bookmarkfs_bookmark_entry} structure is defined as:
@example c
struct bookmarkfs_bookmark_entry @{
char const *name;
off_t off;
struct bookmarkfs_bookmark_stat stat;
@};
@end example
@table @code
@item name
Name of the object entry.
Must be a valid filename.
Objects with invalid name should be exempted from the list.
@xref{Filesystem Check}.
@item off
Position of the current entry, to be used for subsequent calls to
@code{bookmark_list}.
@item stat
Attributes of the object entry.
@xref{Bookmark Attributes} for the definition of the
@code{bookmarkfs_bookmark_stat} structure.
If the @code{BOOKMARKFS_BOOKMARK_LIST_WITHSTAT} flag is @emph{not} given,
the caller ignores @code{atime} and @code{mtime}, and only use @code{value_len}
to determine whether the entry refers to a directory.
@end table
Values of @code{entry} and @code{name} only need to be valid
until the @code{callback} function returns.
@end table
Return value:
@table @asis
@item @t{0}
Continue to the next entry, if one exists.
@item Non-zero value
Stop listing entries, and use this value as the return value for
the current @code{bookmark_list} call.
@end table
@item user_data
An opaque pointer which should be passed as-is to the @code{callback} function.
@item cookie_ptr
Pointer to the ``cookie'' for the current @code{bookmark_list} operation.
@code{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 @code{cookie_ptr} is @code{NULL}, it indicates
that this is an initial operation.
The backend should set the value to a pointer which is not @code{NULL}.
The backend must not change the cookie value.
@end table
Return value:
@table @asis
@item @t{0}
All entries in the directory have been listed.
@item Negated @code{errno}
An error occurred.
@item Arbitrary non-zero value
The function is terminated by a call to @code{callback} that
returns a non-zero value.
@end table
If returning a negative value, the position of the current
@code{bookmark_list} operation is unspecified.
@node Get Bookmark Content
@subsection Get Bookmark Content
The @code{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.
@xref{Bookmarks}.
Type of the @code{bookmark_get} function is defined as:
@example c
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
);
@end example
Function arguments:
@table @code
@item backend_ctx
The pointer referring to the backend context.
@item id
ID of the bookmark (could be a bookmark folder if @code{attr_key}
is not @code{NULL}).
@item attr_key
Name of an extended attribute.
If not @code{NULL}, @code{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.
@item callback
Function to be called for the required bookmark data.
It is guaranteed not to be @code{NULL}.
Type of the @code{callback} function is defined as:
@example c
typedef int (bookmarkfs_bookmark_get_cb) (
void *user_data,
void const *value,
size_t value_len
);
@end example
Function arguments:
@table @code
@item user_data
Must be equal to the @code{user_data} argument value of the current
@code{bookmark_get} call.
@item value
Bookmark content (or extended attribute) data.
Must not be @code{NULL}.
It may contain arbitrary bytes, and only need to be valid until the
@code{callback} function returns.
@item value_len
Length of @code{value} in bytes.
@end table
The function returns @t{0} on success.
Otherwise, the function returns a nagated @code{errno} which should be
used as the return value for the current @code{bookmark_get} call.
@item user_data
An opaque pointer which should be passed as-is to the @code{callback} function.
@item cookie_ptr
Pointer to the ``cookie'' for the current @code{bookmark_get} operation.
@code{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 @code{attr_key})
to determine whether the corresponding data has changed.
If the value pointered to by @code{cookie_ptr} is @code{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 @code{NULL}.
The backend must not change the cookie value.
@end table
On success, the function should return @t{0}.
Otherwise, it should return a negated @code{errno} indicating the error
encountered.
Notable error codes:
@table @code
@item EAGAIN
Bookmark data has not been changed @emph{externally} since
the last call to @code{bookmark_get} with the same cookie.
If this error code is returned, @code{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 @code{bookmark_list}
is always called with a @code{cookie_ptr} of @code{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).
@end table
@node Free Cookie
@subsection Free Cookie
When a cookie obtained from @code{bookmark_list},
@code{bookmark_get} or @code{bookmark_fsck} is no longer used,
the @code{cookie_free} function is called.
It must not be @code{NULL}.
The backend should release all system resources associated with this cookie.
Type of the @code{cookie_free} function is defined as:
@example c
typedef void (bookmarkfs_cookie_free_func) (
void *backend_ctx,
void *cookie,
enum bookmarkfs_cookie_type cookie_type
);
@end example
Function arguments:
@table @code
@item backend_ctx
The pointer referring to the backend context.
@item cookie
Cookie to be freed.
@item cookie_type
Type of the cookie.
It must be one of the values defined in enum @code{bookmarkfs_cookie_type}:
@table @code
@item BOOKMARKFS_COOKIE_TYPE_WATCH
Indicates that the cookie is obtained from @code{bookmark_get}.
@item BOOKMARKFS_COOKIE_TYPE_LIST
Indicates that the cookie is obtained from @code{bookmark_list} or
@code{bookmark_fsck}.
@end table
@end table
@node Sync Bookmarks
@subsection Sync Bookmarks
The @code{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 @code{bookmark_sync} function is defined as:
@example c
typedef int (bookmarkfs_bookmark_sync_func) (
void *backend_ctx
);
@end example
Function arguments:
@table @code
@item backend_ctx
The pointer referring to the backend context.
@end table
On success, the function should return @t{0}.
Otherwise, it should return a negated @code{errno} indicating the error
encountered.
@node Create Bookmark Storage
@subsection Create Bookmark Storage
The @code{backend_mkfs} function is called by the @command{mkfs.bookmarkfs}
program to create a new bookmark storage.
It can be @code{NULL}.
The new bookmark storage should be able to be opened directly, by both
@code{backend_create} and the application that owns the bookmark storage
(typically a web browser).
Type of the @code{backend_mkfs} function is defined as:
@example c
typedef int (bookmarkfs_backend_mkfs_func) (
struct bookmarkfs_backend_conf const *conf
);
@end example
Function arguments:
@table @code
@item conf
@xref{Backend Configuration} for the definition of the
@code{bookmarkfs_backend_conf} structure.
Some fields should be handled differently from @code{backend_create}:
@table @code
@item flags
A bit array of the following flags:
@table @code
@item BOOKMARKFS_BACKEND_MKFS_FORCE
Indicates that the @option{-o force} option is given to
@command{mkfs.bookmarkfs}.
@end table
@item store_path
Path to the bookmark storage to be created, equivalent to the
@code{store_path} passed to @code{backend_create}.
The backend should not overwrite existing bookmark storage, unless
the @code{BOOKMARKFS_BACKEND_MKFS_FORCE} flag is given.
@end table
@end table
The function should return @t{0} on success, and @t{-1} on error.
@node Filesystem-Check Handlers
@chapter Filesystem-Check Handlers
The filesystem-check handler is responsible for instructing the
@command{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:
@example
@var{$@{pkglibdir@}}/fsck_handler_@var{$@{name@}}@var{$@{shlib_suffix@}}
@end example
@table @var
@item $@{pkglibdir@}
Presumably @file{@var{$@{prefix@}}/lib/bookmarkfs}.
@xref{Uniform,, The Uniform Naming Scheme, automake, GNU Automake}.
@item $@{name@}
The handler name, equivalent to the value given to the
@option{-o handler=@var{name}} option of @command{fsck.bookmarkfs}.
@item $@{shlib_suffix@}
The common filename extension for shared library files on the current platform
(e.g., @file{.so} on GNU/Linux and FreeBSD).
@end table
@node Built-in Handler
@section Built-in Handler
The built-in filesystem-check handler is linked into the
@command{fsck.bookmarkfs} program.
It has minimal functionalities, but is capable enough to handle most
common fsck scenarios.
Handler-specific options:
@table @option
@item prompt=@var{str}
Readline prompt string.
Defaults to @samp{% }.
This option is ignored in non-interactive mode.
@item translit=@var{char}
Transliterate bad (@samp{/}) characters into @var{char}
(must be a single ASCII character).
Defaults to @samp{_}.
@end table
For each bookmark entry, the built-in handler prints a message
to standard output explaining why the bookmark name is invalid.
When the @option{-o repair} option is given, the handler renames the bookmark
according to the following rules:
@table @code
@item BOOKMARKFS_FSCK_RESULT_NAME_DUPLICATE
Append a @samp{_@var{$@{counter@}}} suffix to the name, where
@var{$@{counter@}} is a self-incrementing 32-bit integer in decimal format.
If appending the suffix would exceed max name length, truncate the name first.
@item BOOKMARKFS_FSCK_RESULT_NAME_BADCHAR
Transliterate the bad character into another character.
@item BOOKMARKFS_FSCK_RESULT_NAME_BADLEN
If the name is too long, truncate it to @code{NAME_MAX} bytes.
If the name is empty, see below:
@item BOOKMARKFS_FSCK_RESULT_NAME_DOTDOT
@item BOOKMARKFS_FSCK_RESULT_NAME_INVALID
Rename to @samp{fsck-@var{$@{id@}}}, where @var{$@{id@}} is the bookmark ID
in decimal format.
@end table
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:
@table @command
@item p
Print the ID and new name of current entry.
@item a[-]
Apply the proposed rename for the current entry.
@item e[-] @var{new_name}
Change the proposed rename to @var{new_name} and then apply.
@item c
Continue to the next entry.
@item s[-]
Skip current directory.
@item S[-]
Skip current directory and all subdirectories.
@item r[-]
Rewind current directory.
@item R[-]
Rewind all.
@item w[-]
Save applied changes.
@item q
Save applied changes and quit.
@end table
The optional @samp{-} suffix inhibits the default behavior of continuing
to the next entry, after the command completes successfully.
@node Tcl-Based Handler
@section Tcl-Based Handler
The Tcl-based filesystem-check handler allows fsck entries to be handled
via Tcl scripting.
Handler name: @samp{tcl}.
Handler-specific options:
@table @option
@item script=@var{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:
@table @code
@item $::bookmarkfs::fsck::isInteractive
Equals to @t{1} if the @option{-i} option is given to @code{fsck.bookmarkfs},
@t{0} otherwise.
@item $::bookmarkfs::fsck::isReadonly
Equals to @t{0} if the @option{-o repair} option is given to
@code{fsck.bookmarkfs}, @t{1} otherwise.
@end table
@item unsafe
Enable unsafe Tcl interpreter features.
Should be used in combination with the @option{-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 @code{interp create -safe}.
See @tcldoc{Safe Interpreters, TclCmd/interp.htm#M45}.
@end table
Each time @command{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:
@table @code
@item $::bookmarkfs::fsck::result::nameDuplicate
@item $::bookmarkfs::fsck::result::nameBadChar
@item $::bookmarkfs::fsck::result::nameBadLen
@item $::bookmarkfs::fsck::result::nameDotDot
@item $::bookmarkfs::fsck::result::nameInvalid
@xref{Filesystem-Check Result Code} for the meaning of each value.
The second element is a list of information about the current entry:
@enumerate 0
@item The bookmark ID
@item Extra information regarding the invalid name, equivalent to the
@code{extra} field in structure @code{bookmarkfs_fsck_data}.
@item Name of the bookmark
@item ID of the parent directory
@end enumerate
@item -1
If the command is being executed for the first time, or the previous execution
returns @code{$::bookmarkfs::fsck::handler::next}, this value never appears.
If the previous execution of the command returns
@code{$::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.
@end table
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:
@table @code
@item $::bookmarkfs::fsck::handler::next
Continue to the next entry.
@item $::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.
@item $::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 @option{prompt=@var{str}} option of the built-in handler.
@item $::bookmarkfs::fsck::handler::save
Save applied changes.
@item $::bookmarkfs::fsck::handler::stop
Save applied changes and quit.
Once this value is returned, the command will never be executed again.
@item $::bookmarkfs::fsck::handler::rewind
Rewind current directory.
@item $::bookmarkfs::fsck::handler::skip
Skip current directory.
@item $::bookmarkfs::fsck::handler::skipChildren
Skip current directory and all subdirectories.
@item $::bookmarkfs::fsck::handler::reset
Rewind all.
@end table
Here is an example Tcl script that simply prints each fsck entry
(requires the @option{unsafe} option):
@example tcl
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]]
@}
@}@}
@end example
@node Handler API
@section Handler API
The Filesystem-Check Handler API specifies how a fsck handler communicates with
@command{fsck.bookmarkfs}.
@quotation Warning
Currently BookmarkFS is experimental.
The Handler API may change drastically without prior notice.
@end quotation
@anchor{Filesystem-Check Handler Symbol Name Format}
@cindex Filesystem-Check Handler Symbol Name Format
To implement the Filesystem-Check Handler API, a fsck handler library
should expose a symbol with the following name:
@example
bookmarkfs_fsck_handler_@var{$@{name@}}
@end example
Where @var{$@{name@}} is equivalent to the value given to the
@option{-o handler=@var{name}} option of @command{fsck.bookmarkfs}.
The symbol should name a data object identifier of the following type:
@example c
#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;
@};
@end example
Each field is a function pointer provided by the fsck handler.
Some can be optional and set to @code{NULL}, if the backend
does not support the corresponding features.
@node Get Handler Information
@subsection Get Handler Information
If not @code{NULL}, the @code{info} function is called when the user
instructs @command{fsck.bookmarkfs} to print information about the handler.
When this function is called, the handler should write a human-readable
message of the corresponding information to standard output.
Type of the @code{info} function is defined as:
@example c
typedef void (bookmarkfs_fsck_handler_info_func) (
uint32_t flags
);
@end example
Function arguments:
@table @code
@item flags
A bit array of the following flags:
@table @code
@item BOOKMARKFS_FSCK_HANDLER_INFO_HELP
@item BOOKMARKFS_FSCK_HANDLER_INFO_VERSION
Indicates that the backend should print a help/version message.
These flags are analogous to the corresponding flags in the
@code{backend_info} function of the backend API.
@xref{Get Backend Information}.
@end table
@end table
@node Create Handler Context
@subsection Create Handler Context
A filesystem-check handler context maintains internal states for
handling fsck entries.
To create a handler context, the @code{create} function is called.
It must not be @code{NULL}.
Type of the @code{create} function is defined as:
@example c
typedef int (bookmarkfs_fsck_handler_create_func) (
struct bookmarkfs_conf_opt const *opts,
uint32_t flags,
void **handler_ctx_ptr
);
@end example
Function arguments:
@table @code
@item opts
Linked list of handler-specific options, @code{NULL} if there are none.
Handler-specific option should be processed in the same way as
backend-specific options.
@xref{Backend-Specific Options}.
@item flags
A bit array of the following flags:
@table @code
@item BOOKMARKFS_FSCK_HANDLER_INTERACTIVE
Indicates that the @option{-i} option is given to @command{fsck.bookmarkfs}.
@item BOOKMARKFS_FSCK_HANDLER_READONLY
Indicates that the @option{-o repair} option is @emph{not} given to
@command{fsck.bookmarkfs}.
@end table
@item 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.
@end table
The function should return @t{0} on success, and @t{-1} on error.
@node Destroy Handler Context
@subsection Destroy Handler Context
When a handler context is no longer used, the @code{destroy} function
is called.
It must not be @code{NULL}.
Upon destruction, The handler should release all system resources
associated with this context.
Type of the @code{destroy} function is defined as:
@example c
typedef void (bookmarkfs_fsck_handler_destroy_func) (
void *handler_ctx
);
@end example
Function arguments:
@table @code
@item handler_ctx
The pointer referring to the handler context.
@end table
@node Run Handler
@subsection Run Handler
Each time @command{fsck.bookmarkfs} finds and entry, or completes a
handler-initiated operation, the @code{run} function is called.
It must not be @code{NULL}.
Type of the @code{run} function is defined as:
@example c
typedef int (bookmarkfs_fsck_handler_run_func) (
void *handler_ctx,
int why,
union bookmarkfs_fsck_handler_data *data
);
@end example
Function arguments:
@table @code
@item handler_ctx
The pointer referring to the handler context.
@item why
Reason code for this handler call:
@table @asis
@item Non-negative value
The @command{fsck.bookmarkfs} program has found an entry.
@xref{Filesystem-Check Result Code} for possible values.
However, @code{BOOKMARKFS_FSCK_RESULT_END} is handled by
@command{fsck.bookmarkfs} and never appears.
@item @t{-1}
Indicates that an operation instructed by the previous return value
of this function is successfully performed.
If @code{run} is being called for the first time, or the previous invocation
returns @code{BOOKMARKFS_FSCK_NEXT}, this value never appears.
@end table
@item data
Information for this handler call.
The @code{bookmarkfs_fsck_handler_data} union is defined as:
@example c
union bookmarkfs_fsck_handler_data @{
struct bookmarkfs_fsck_handler_entry entry;
char *str;
@};
@end example
@table @code
@item entry
Information for the bookmark entry.
This field is set when @code{why} is non-negative.
The @code{bookmarkfs_fsck_handler_entry} structure is defined as:
@example c
struct bookmarkfs_fsck_handler_entry @{
uint64_t parent_id;
struct bookmarkfs_fsck_data data;
@};
@end example
@table @code
@item parent_id
ID of the bookmark folder that contains this bookmark entry.
@item data
@xref{Online Filesystem Check} for the definition of the
@code{bookmarkfs_fsck_data} structure.
@end table
@item str
A NUL-terminated string.
This field is set to the user input string when @code{why} is @t{-1}, and the
previous invocation of @code{run} returns @code{BOOKMARKFS_FSCK_USER_INPUT}.
The string is only guaranteed to be valid until the function returns.
@end table
@end table
On success, the function should return one of the following values,
indicating the operation to perform:
@table @code
@item BOOKMARKFS_FSCK_NEXT
Continue to the next entry.
@item BOOKMARKFS_FSCK_APPLY
Apply change for the current entry.
Not available in read-only mode.
The handler should copy the new name for the bookmark to
@code{data->entry.data.name}.
@item BOOKMARKFS_FSCK_USER_INPUT
Request for user input.
Only available in interactive mode.
The handler should set @code{data->str} to a prompt string for Readline.
The string must be valid until the next function call on this handler context.
@item BOOKMARKFS_FSCK_SAVE
Save applied changes.
@item BOOKMARKFS_FSCK_STOP
Save applied changes and quit.
The @code{run} function is guaranteed not to be called again for this context.
@item BOOKMARKFS_FSCK_REWIND
Rewind current directory.
@item BOOKMARKFS_FSCK_SKIP
Skip current directory.
@item BOOKMARKFS_FSCK_SKIP_CHILDREN
Skip current directory and all subdirectories.
@item BOOKMARKFS_FSCK_RESET
Rewind all.
@end table
On error, the function should return @t{-1}.
@node General Index
@appendix General Index
@printindex cp
@node GNU Free Documentation License
@appendix GNU Free Documentation License
@include fdl.texi
@bye