liteboxfs/lib.rs
1//! LiteboxFS is a modern POSIX filesystem in a SQLite database.
2//!
3//! ```
4//! use std::io::{Read, Write, Seek, SeekFrom};
5//!
6//! use liteboxfs::{Connection, CreateOptions, FileKind, Owner};
7//!
8//! fn main() -> liteboxfs::Result<()> {
9//! let mut conn = Connection::open_in_memory(&CreateOptions::new())?;
10//!
11//! let buf = conn.exec(|fs| {
12//! let mut file = fs.create("example.txt", FileKind::Regular, Owner::current())?;
13//!
14//! file.write_all(b"Hello, world!")?;
15//! file.seek(SeekFrom::Start(0))?;
16//!
17//! let mut buf = Vec::new();
18//! file.read_to_end(&mut buf)?;
19//!
20//! liteboxfs::Result::Ok(buf)
21//! })?;
22//!
23//! assert_eq!(buf, b"Hello, world!");
24//!
25//! Ok(())
26//! }
27//! ```
28//!
29//! # Getting Started
30//!
31//! A litebox is a SQLite database containing a filesystem.
32//!
33//! [`Filesystem`] is the main type you will use to interact with LiteboxFS. To access the
34//! filesystem, you will first need to open a [`Connection`] and start a transaction.
35//!
36//! # Roots
37//!
38//! Unlike a traditional POSIX filesystem, LiteboxFS supports multiple root directories, each
39//! identified by a universally unique [`RootId`]. This allows for multiple independent logical
40//! filesystems to coexist within the same database and deduplicate data between them. This feature
41//! can also be used to create copy-on-write snapshots of a filesystem.
42//!
43//! In addition to their UUID, roots can optionally have a name, which is a UTF-8 string that is
44//! unique within the litebox.
45//!
46//! When you create a litebox, it will automatically create a default root with name
47//! [`DEFAULT_ROOT_NAME`]. The default root always exists; it cannot be deleted.
48//!
49//! # Metadata
50//!
51//! A litebox can have [user-defined metadata](crate::UserMetadata) associated with it in the form
52//! of key-value pairs. This metadata is stored persistently in the database and is not touched by
53//! LiteboxFS, so applications or downstream libraries can use it for any purpose.
54//!
55//! Each root can also have its own user-defined metadata.
56//!
57//! # Compression and Deduplication
58//!
59//! LiteboxFS has two optional features to improve storage efficiency at the cost of performance:
60//! [transparent compression](crate::CreateOptions::compression) and [content-defined
61//! chunking](crate::CreateOptions::chunking).
62//!
63//! Files in a litebox are deduplicated at the block level, including between roots. When enabled,
64//! content-defined chunking can improve deduplication ratios between files that are similar but
65//! not identical.
66//!
67//! These features are configurable per-litebox, not per-root. Both these features are
68//! feature-gated and disabled by default.
69//!
70//! # Transactions and Concurrency
71//!
72//! LiteboxFS is _transactional_, with the same fundamental guarantees and caveats as SQLite.
73//!
74//! This API makes transaction explicit, which means you can use them to maintain invariants in
75//! your application, just like you would with a database.
76//!
77//! Internally, LiteboxFS uses savepoints to ensure that individual filesystem operations like
78//! copying a file, deleting a file, writing data to a file, etc. are atomic and consistent, with
79//! the transaction providing the isolation and durability.
80//!
81//! Like SQLite, the concurrency model of LiteboxFS depends on the [journal
82//! mode](crate::JournalMode).
83//!
84//! - In the default mode, readers block writers and writers block readers.
85//! - In [WAL mode](crate::JournalMode::Wal), reading and writing can happen concurrently.
86//!
87//! In both cases, you can only have a single write transaction active at a time, which means that
88//! holding a long-running write transaction will block other connections from writing (and
89//! possibly reading) to the litebox.
90//!
91//! Like SQLite, once a write happens, the transaction is implicitly upgraded to a write
92//! transaction and stays that way until it is committed or rolled back. LiteboxFS generally tries
93//! to avoid writing to the database unless you tell it to; opening and reading a file should not
94//! require a write transaction, for example.
95//!
96//! # Encryption
97//!
98//! LiteboxFS supports optional encryption via [SQLCipher](https://github.com/sqlcipher/sqlcipher).
99//! If SQLCipher support is enabled (via Cargo features), you can specify either a password or an
100//! encryption key when creating or opening a litebox. The password/key can be changed later.
101//!
102//! # FUSE Support
103//!
104//! On Linux, a litebox can be mounted in the host filesystem via FUSE. This allows for accessing
105//! the contents of a litebox root as if it were any other directory on the host system.
106//!
107//! Mounting a litebox acquires an exclusive lock on the database for the duration of the mount.
108//!
109//! You can mount a litebox root using [`Connection::mount`] or via the CLI.
110//!
111//! # Features
112//!
113//! | Feature | Default | Description |
114//! | --- | --- | --- |
115//! | `fs` | Yes | Enable features that require interacting with the host filesystem. |
116//! | `compression` | No | Enable support for [transparent compression](crate::CreateOptions::compression) of file contents using Zstandard (zstd). |
117//! | `chunking` | No | Enable support for [content-defined chunking](crate::CreateOptions::chunking) of file contents using FastCDC. |
118//! | `fuse` | No | Enable support for mounting a litebox as a FUSE filesystem. |
119//! | `bundled-sqlite` | No | Use a bundled version of SQLite instead of linking to it. |
120//! | `sqlcipher` | No | Link to SQLCipher instead of SQLite. This searches for and links against a system-installed crypto library. |
121//! | `bundled-sqlcipher` | No | Use a bundled version of SQLCipher instead of linking to it. This searches for and links against a system-installed crypto library. |
122//! | `bundled-sqlcipher-vendored-openssl` | No | Use a bundled version of SQLCipher with a vendored version of OpenSSL. |
123
124// You can define lints in `Cargo.toml`, but then it applies to integration tests as well, which
125// isn't what we want.
126#![forbid(unsafe_code)]
127#![warn(missing_docs)]
128#![warn(missing_debug_implementations)]
129#![warn(clippy::indexing_slicing)]
130#![cfg_attr(test, allow(clippy::indexing_slicing))]
131#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
132
133mod acl;
134mod archive;
135mod block;
136mod cache;
137#[cfg(feature = "chunking")]
138mod cdc;
139mod chunker;
140#[cfg(feature = "compression")]
141mod compression;
142mod connection;
143mod errors;
144mod file_metadata;
145mod files;
146mod filesystem;
147#[cfg(all(feature = "fuse", target_os = "linux"))]
148mod fuse;
149mod hash;
150#[cfg(all(feature = "fs", target_family = "unix"))]
151mod host;
152mod id;
153mod lock;
154mod merkle;
155mod path;
156mod settings;
157mod sql;
158mod state;
159mod stats;
160mod transaction;
161mod tree;
162mod user_metadata;
163mod util;
164
165#[cfg(test)]
166mod testing;
167#[cfg(test)]
168mod tests;
169
170/// Types for representing file metadata.
171pub mod metadata {
172 pub use super::file_metadata::{
173 ACCESS_ACL_XATTR_NAME, Acl, AclIntoIter, AclIter, AclMode, AclQualifier,
174 DEFAULT_ACL_XATTR_NAME, Device, FileKind, FileMetadata, FileMode, Gid, Owner, Uid, Xattrs,
175 XattrsIntoIter, XattrsIter,
176 };
177 pub use super::sql::FileDiscriminant;
178 pub use super::stats::FilesystemStats;
179}
180
181/// Types for user-defined metadata associated with filesystems and roots.
182pub mod user {
183 pub use super::user_metadata::{UserMetadata, UserMetadataIntoIter, UserMetadataIter};
184}
185
186/// Types for working with filesystem roots.
187pub mod root {
188 pub use super::id::{DEFAULT_ROOT_NAME, Root, RootBy, RootId, Roots};
189}
190
191/// Error types.
192pub mod error {
193 pub use super::errors::{Error, ErrorReason, Result, SqliteErrorCode};
194}
195
196/// Types for database connections, transactions, and the filesystem.
197pub mod db {
198 #[cfg(feature = "_encryption")]
199 pub use super::connection::EncryptionSecret;
200 pub use super::connection::{Connection, CreateOptions, JournalMode, OpenOptions};
201 pub use super::filesystem::Filesystem;
202 pub use super::id::FilesystemId;
203 pub use super::transaction::{Transaction, TransactionBehavior};
204}
205
206/// Types for working with files and file contents.
207pub mod file {
208 pub use super::files::File;
209 pub use super::id::{ContentId, FileId};
210 #[cfg(all(feature = "fs", target_os = "linux"))]
211 pub use super::lock::RawFd;
212 pub use super::path::{FileBy, FileOrigin};
213}
214
215/// Types for walking the filesystem.
216pub mod walk {
217 pub use super::tree::{
218 Children, Descendants, DirectoryEntry, WalkEntry, WalkOptions, WalkPredicate, WalkVisit,
219 };
220}
221
222/// Types for interacting with the host filesystem.
223pub mod fs {
224 pub use super::archive::{ArchiveOptions, ExtractOptions};
225 #[cfg(feature = "fuse")]
226 pub use super::fuse::MountOption;
227}
228
229pub use connection::Connection;
230pub use errors::{Error, Result};
231pub use files::File;
232pub use filesystem::Filesystem;
233
234pub use db::*;
235pub use error::*;
236pub use file::*;
237pub use fs::*;
238pub use metadata::*;
239pub use root::*;
240pub use user::*;
241pub use walk::*;