mirror of
https://git.sr.ht/~cismonx/bookmarkfs
synced 2025-07-22 09:08:51 +00:00
1273 lines
37 KiB
Text
1273 lines
37 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
|
|
|
|
@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
|
|
|
|
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
|
|
@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.
|
|
|
|
|
|
@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.
|
|
|
|
Example 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 Programs
|
|
@chapter Programs
|
|
|
|
|
|
@node mount.bookmarkfs
|
|
@section @command{mount.bookmarkfs}
|
|
|
|
@example
|
|
mount.bookmarkfs [@var{options}] @var{src} @var{target}
|
|
@end example
|
|
|
|
The @command{mount.bookmarkfs} program mounts a BookmarkFS filesystem to the
|
|
location specified by @var{target}.
|
|
|
|
The @var{src} argument is presumably the pathname of a file that stores
|
|
bookmark data.
|
|
Its exact interpretation is backend-defined.
|
|
|
|
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.
|
|
|
|
Available options:
|
|
|
|
@table @option
|
|
@item -o backend=@var{name}
|
|
The backend used by the filesystem (@pxref{Backends}).
|
|
This option is mandatory.
|
|
|
|
@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 @code{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 mtime
|
|
ctime only updates when mtime does.
|
|
|
|
@item ctime
|
|
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 cache file attributes, making ctime appear more ``correct''
|
|
than what we claim.
|
|
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 @code{32768}.
|
|
|
|
@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.
|
|
|
|
By default, the filesystem is mounted read-only.
|
|
This behavior won't change in the future, since the hackish nature of
|
|
BookmarkFS makes it forever unstable.
|
|
|
|
@quotation Warning
|
|
Always backup the bookmarks before mounting it read/write, or risk losing
|
|
your data!
|
|
@end quotation
|
|
|
|
@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
|
|
The name that appears as the @code{fs_spec} field in @linuxmanpage{fstab, 5}.
|
|
Defaults to the backend name.
|
|
|
|
@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 can still update atime explicitly (e.g. with
|
|
@posixfuncmanpage{futimens}).
|
|
@end table
|
|
|
|
|
|
@node fsck.bookmarkfs
|
|
@section @command{fsck.bookmarkfs}
|
|
|
|
@example
|
|
fsck.bookmarkfs [@var{options}] @var{pathname}
|
|
@end example
|
|
|
|
The @command{fsck.bookmarkfs} program checks and optionally repairs a
|
|
BookmarkFS filesystem.
|
|
|
|
Filesystem check on BookmarkFS has a different purpose compared to
|
|
on-disk filesystems.
|
|
@xref{Filesystem Check}.
|
|
|
|
Available options:
|
|
|
|
@table @option
|
|
@item -o backend=@var{name}
|
|
The backend used by the filesystem (@pxref{Backends}).
|
|
|
|
If this option is not provided, or @var{name} is empty, performs online fsck.
|
|
|
|
@table @asis
|
|
@item Online Mode
|
|
In online mode, fsck is performed on a mounted BookmarkFS filesystem
|
|
using @code{ioctl()} (@pxref{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 @code{@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
|
|
|
|
@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}).
|
|
|
|
If this option is not provided, or @var{name} is empty,
|
|
a built-in handler will be used.
|
|
@xref{Built-in Filesystem-Check 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 bookmarks before repairing, or risk losing your data!
|
|
@end quotation
|
|
|
|
@item -o rl_app=@var{name}
|
|
Readline application name in interactive mode.
|
|
Defaults to ``fsck.bookmarkfs''.
|
|
|
|
@item -o type=bookmark|tag|keyword
|
|
Subsystem type (@pxref{Hierarchy}).
|
|
Defaults to ``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 (@pxref{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}
|
|
|
|
@example
|
|
mkfs.bookmarkfs [@var{options}] @var{pathname}
|
|
@end example
|
|
|
|
The @command{mkfs.bookmarkfs} program creates a BookmarkFS filesystem
|
|
on the file specified by @var{pathname}.
|
|
|
|
Available options:
|
|
|
|
@table @option
|
|
@item -o backend=@var{name}
|
|
The backend used by the filesystem (@pxref{Backends}).
|
|
This option is mandatory.
|
|
|
|
@item -o @@@var{key}[=@var{value}]
|
|
A backend-specific option.
|
|
This option can be provided multiple times.
|
|
|
|
@item -o force
|
|
Overwrite existing files when creating the filesystem.
|
|
|
|
@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}
|
|
|
|
@example
|
|
bookmarkctl @var{subcmd} [@var{args}]
|
|
@end example
|
|
|
|
The @command{bookmarkctl} program is a command-line wrapper for various
|
|
I/O controls of a BookmarkFS filesystem.
|
|
|
|
@example
|
|
bookmarkctl permd @var{pathname} @var{op} @var{name1} @var{name2}
|
|
@end example
|
|
|
|
The @command{permd} sub-command re-arranges the order of the directory entries
|
|
returned from @code{readdir()} calls.
|
|
@xref{Permute Directory Entries}.
|
|
|
|
The @var{pathname} argument is the path to the directory.
|
|
The @var{name1} and @var{name2} arguments are filenames under that directory.
|
|
|
|
The @var{op} argument is the operation to perform on the directory:
|
|
|
|
@table @code
|
|
@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
|
|
|
|
@example
|
|
bookmarkctl fsck @var{pathname} @var{op}
|
|
@end example
|
|
|
|
The @command{fsck} sub-command checks for errors within a
|
|
BookmarkFS filesystem.
|
|
@xref{Filesystem Check}.
|
|
|
|
The @var{pathname} argument is the path to the directory to perform checks on.
|
|
|
|
The @var{op} argument is the operation to perform on the directory:
|
|
|
|
@table @code
|
|
@item list
|
|
Display a list of errors found under the given directory.
|
|
Will not recurse into subdirectories.
|
|
@end table
|
|
|
|
For the full fsck functionalities, @pxref{fsck.bookmarkfs}.
|
|
|
|
@example
|
|
bookmarkctl help
|
|
@end example
|
|
|
|
Print help text, and then exit.
|
|
|
|
@example
|
|
bookmarkctl version
|
|
@end example
|
|
|
|
Print version information, and then exit.
|
|
|
|
|
|
@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 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
|
|
|
|
@example
|
|
@var{$@{mountpoint@}}/bookmarks/@var{$@{bookmark_dir...@}}/@var{$@{bookmark@}}
|
|
@end example
|
|
|
|
The ``bookmarks'' subsystem maintains the hierarchical structure, names, URLs
|
|
and other information of a bookmark storage.
|
|
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
|
|
does not exist on the filesystem.
|
|
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 @code{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 (@pxref{Extended Attributes}).
|
|
|
|
|
|
@node Tags
|
|
@subsection Tags
|
|
|
|
@example
|
|
@var{$@{mountpoint@}}/tags/@var{$@{tag_dir@}}/@var{$@{bookmark@}}
|
|
@end example
|
|
|
|
The ``tags'' subsystem maintains a many-to-many mapping between bookmarks and
|
|
their alternative names.
|
|
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
|
|
|
|
@example
|
|
@var{$@{mountpoint@}}/keywords/@var{$@{keyword_name@}}
|
|
@end example
|
|
|
|
The ``keywords'' subsystem maintains a one-to-one mapping between bookmarks
|
|
and their alternative names, independent from tag names.
|
|
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's 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 makes 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 @code{user.} name prefix on Linux, and should be
|
|
accessed with @code{EXTATTR_NAMESPACE_USER} on FreeBSD.
|
|
All attributes have a @code{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 Entry Ordering
|
|
@section Directory Entry Ordering
|
|
|
|
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.
|
|
|
|
|
|
@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 @code{0}.
|
|
Otherwise, it returns @code{-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 slash character).
|
|
|
|
@item ENOENT
|
|
The directory does not contain entries named @code{name1} or @code{name2}.
|
|
|
|
@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 slash characters
|
|
@item must not be empty or longer than @code{NAME_MAX}
|
|
@item must not duplicate with another file in the same 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.
|
|
|
|
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 ``/'' 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 ``.'' or ``..''.
|
|
|
|
Value of @code{extra} is unspecified.
|
|
@end table
|
|
|
|
On failure, @code{ioctl()} returns @code{-1}, and sets @code{errno}:
|
|
|
|
@table @code
|
|
@item EACCES
|
|
Read permission is denied for the directory.
|
|
|
|
@item EPERM
|
|
The backend does not support fsck for this directory.
|
|
@end table
|
|
|
|
@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} on the directory,
|
|
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 @code{-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 @code{0}.
|
|
Otherwise, it returns @code{-1}, and sets @code{errno}.
|
|
@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{$@{short_name@}}@var{$@{shlib_suffix@}}
|
|
@end example
|
|
|
|
Where @var{$@{short_name@}} is the name passed to the frontend program, and
|
|
@var{$@{shlib_suffix@}} is the common filename extension for shared library
|
|
files on the current platform (e.g. @file{.so} on GNU/Linux and FreeBSD).
|
|
|
|
Currently, BookmarkFS ships with two backends.
|
|
One for Firefox, the other for Chromium.
|
|
If you which to add support for more backends,
|
|
you may submit a feature request or implement one using the Backend API.
|
|
|
|
|
|
@node Firefox
|
|
@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-specific options:
|
|
|
|
@table @option
|
|
@item filename=title|guid
|
|
Whether to use the bookmark title or GUID as the bookmark file name.
|
|
Defaults to ``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 @option
|
|
@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
|
|
|
|
With @option{filename=title}, the GUID is available as an extended attribute
|
|
(@pxref{Extended Attributes}), and vise versa.
|
|
|
|
@item 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
|
|
@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
|
|
|
|
|
|
@node Chromium
|
|
@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-specific options:
|
|
|
|
@table @option
|
|
@item 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:
|
|
|
|
@example
|
|
0bc5d13f-2cba-5d74-951f-3f233fe6c908
|
|
@end example
|
|
|
|
When creating a new file:
|
|
|
|
@table @option
|
|
@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
|
|
|
|
With @option{filename=title}, the GUID is available as an extended attribute
|
|
(@pxref{Extended Attributes}), and vise versa.
|
|
|
|
@item 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.
|
|
|
|
@table @option
|
|
@item native
|
|
Watch for file changes using platform-specific features.
|
|
|
|
On 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.
|
|
|
|
On FreeBSD, @freebsdmanpage{kevent, 2} with @code{EVFILT_VNODE} is used.
|
|
|
|
@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
|
|
|
|
This backend does not scale well with large bookmark storage,
|
|
since it keeps everything in memory, and has to parse the entire JSON file
|
|
whenever a reload is required.
|
|
|
|
Another limitation is the lack of tags (@pxref{Tags}) and keywords
|
|
(@pxref{Keywords}) support.
|
|
Chromium does not have such concepts.
|
|
|
|
|
|
@node Backend API
|
|
@section Backend API
|
|
|
|
|
|
@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
|
|
|
|
Where @var{$@{name@}} is the name passed to the @option{-o handler=@var{name}}
|
|
option of @command{fsck.bookmarkfs}, and @var{$@{shlib_suffix@}} is the
|
|
common filename extension for shared library files on the current platform
|
|
(e.g. @file{.so} on GNU/Linux and FreeBSD).
|
|
|
|
|
|
@node Built-in Filesystem-Check Handler
|
|
@section Built-in Handler
|
|
|
|
The built-in handler is shipped with 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 ``@t{% }''.
|
|
|
|
This option is ignored in non-interactive mode.
|
|
|
|
@item translit=@var{char}
|
|
Transliterate bad characters into @var{char}.
|
|
Defaults to ``@t{_}''.
|
|
@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 ``@t{_@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 ``@t{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 ``@t{-}'' suffix inhibits the default behavior of continuing
|
|
to the next entry, after the command completes successfully.
|
|
|
|
|
|
@node Tcl-Based Filesystem-Check Handler
|
|
@section Tcl-Based Handler
|
|
|
|
|
|
@node Filesystem-Check Handler API
|
|
@section Handler API
|
|
|
|
|
|
@node General Index
|
|
@appendix General Index
|
|
|
|
@printindex cp
|
|
|
|
|
|
@node GNU Free Documentation License
|
|
@appendix GNU Free Documentation License
|
|
|
|
@include fdl.texi
|
|
|
|
|
|
@bye
|