owned this note
owned this note
Published
Linked with GitHub
# muxfs internals
## Terminology
- muxfs - the muxfs project
- filesystem - a set of files
- `muxfs` - the muxfs package
- `mfs` - the muxfs CLI tool
- `muxfs.FS` - the `muxfs` filesystem structure
## Storage
Data is stored as blocks, in a [Content-addressable storage (CAS)](https://en.wikipedia.org/wiki/Content-addressable_storage), which includes metadata structures and file data.
A **block** is a chunk of data, with a size no larger than 8MiB. It is stored in a **blockstore**, which is a [Key-value database](https://en.wikipedia.org/wiki/Key-value_database). Its **key** is a 33byte ID, unique to that block, generated based on the block data.
The **key** has the following format: `[1byte type][32byte data]`.
We support the following key types:
- blake2s hash: `type = 01, data = blake2s(block data)`
To store files larger than 8MiB, we need to split them into smaller chunks. To do this, [Content Defined Chunking (CDC)](https://restic.net/blog/2015-09-12/restic-foundation1-cdc) is used, which provides the benefit of data deduplication.
## Filesystem
A **filesystem** is set of files, each having permissions, owners, dates, and data.
It can be loaded readonly from a **snapshot block**, or readonly/readwrite from a **filesystem block** (see [mounting](#Mounting)).
### Snapshot block
A **snapshot block** can be used to instantiate _only_ a **read-only** filesystem.
TODO: describe in words:
```protobuf
message Snapshot {
uint32 version = 1;
SnapshotMeta meta = 2;
repeated Owner users = 3;
repeated Owner groups = 4;
Dir root = 5;
BlockList extraRoots = 6; // blocks that contain `message Dir`
repeated BlockList fileBlocks = 7;
BlockList extraFileBlocks = 8; // extra blocks that contain `message BlockList`
}
```
### Filesystem block
A **filesystem block** (referenced using its hash) is **required** to instantiate a **read/write** filesystem _(but can also be used to instantiate a read-only FS)_.
The FS block is stored in the muxfs configuration file as a hash of its contents:
`fsBlockHash = Hash(fsBlock)`
```yaml
filesystems:
foo:
# block is the fsBlockHash
block: 1bc4b535227dff4fdec958f43ca92a8fe5dfa0dcc8fc4a954de08d199ff6f36a7
# ...other fields omitted...
```
When you mount filesystem **"foo"**, muxfs looks in the configuration file, searches the `filesystems` map for the "foo" entry, and gets the **FS block hash** for that filesystem.
If the FS block hash is empty, a new blank FS is instantiated. If it is not empty the FS block is fetched from the store, the latest snapshot _(last snapshot in the list)_ is also fetched from the store and used to instantiate the FS.
The FS block contains:
- all the filesystem's snapshots _(their hashes, which can be used to fetch the actual snapshots)_
- TODO(nuni): add more fields here
### Keeping filesystems up to date with their snapshots
When the FS changes and a new snapshot is created, that snapshot hash is appended to the FS block snapshots, the FS block hash is updated _(again, by hashing the FS block contents)_, and the configuration file is updated with the new FS block hash.
Thus, when you mount that FS again, the latest snapshot in the FS block will be the snapshot that was just created.
In the case of a read-only filesystem, no changes can be made, so no snapshots can be created, which means the FS block will remain the same.
## Snapshot
A snapshot is the full state of a filesystem, at a point in time.
Snapshots are created:
- automatically when [unmounting](#Unmounting) a FS
- according to the filesystems' [snapshot policies](#Snapshot-policy),
- or manually, using:
`mfs snapshot save <fs name> | <path>` -- (for a currently mounted filesystem).
Automatic snapshots are only created if the filesystem was changed since the last snapshot.
### Snapshot policy
Each filesystem can have multiple snapshot policies defined in the [configuration file](#Configuration).
```yaml
filesystems:
foofs:
snapshots:
- name: every_ten_minutes
capacity: 5
period: PT10M
- name: every_hour
capacity: 24
period: hourly
```
`name` describes the snapshot policy name: it can be anything you like, but it must be unique.
`capacity` determines how many snapshots should at most exist for this policy.
For example, if a policy has a `capacity` of 5, once the 6th snapshot for that policy is created, the 1st snapshot will be removed.
`period` describes how often the snapshots should be created, either:
- Static periods: `"hourly"`, `"daily"`, `"weekly"`, `"monthly"`, `"yearly"`, for example, at the start of every hour/month: 16:00, 17:00, or 1 Jan, 1 Feb, etc
- Or a custom [ISO8601](https://en.wikipedia.org/wiki/ISO_8601) time string. `"PT5M3S"` means every 5 minutes and 3 seconds.
## Managing filesystems
### Create
New filesystems can be created using the `mfs fs add` command.
There are 3 ways to create a FS:
1. A new, empty filesystem: `mfs fs add <name> new`
2. A new filesystem from an existing snapshot: `mfs fs add <name> <snapshot hash>`
3. A new filesystem from an existing filesystem: `mfs fs clone <fs name> <new fs name>`
### Destroy
`mfs fs destroy <fs name>`
**Warning**: this will remove EVERYTHING related to that file system (all snapshots and all files).
## Mounting
After [creating a FS](#Create) you can mount it and begin working with files:
There are multiple ways to create a mount:
1. Using `mfs mount [-readonly] <fs name> <path>`, which gets the FS's latest snapshot and mounts it at `path`.
1. If the mount is **writable**, the snapshot mechanism is enabled, and whenever a new snapshot is created (either manually, or automatically), [the snapshots for this FS are kept up to date](###Keeping-filesystems-up-to-date-with-snapshots).
2. If the mount is **read only**, files can only be browsed, but since files can't be modified, this means no new snapshots can be made.
2. Using the `mfs snapshot mount <hash> <path>` command, which creates a temporary, **read only** filesystem at `path`, using the contents of the snapshot specified by `hash`.
Method `1.2.` and method `3.` are almost the same, with the difference being that if you wanted to mount the **latest snapshot** of some filesystem as read only, you would use `mfs mount`, and if you wanted to mount **any snapshot** from the entire history of a FS, you would use `mfs snapshot preview`
After mounting a filesystem using any of the 3 methods listed above, you can browse to the path you used for mounting and begin viewing/editing the files.
## Unmounting
`mfs unmount <fs name | path>`
If the filesystem was mounted read/write, this will automatically create a new snapshot, saving the latest state of all the files in this FS.
## Configuration
TODO
~~~## Instantiating filesystems
A filesystem will have many snapshots, either created manually using `mfs snapshot save <fs>`, or automatically when a FS is unmounted.
The list of snapshots for a filesystem is kept in a structure called "History", which contains the hash for each snapshot block, with the last entry being the snapshot that was most recently created.
The History is updated and re-added to the store (under a different hash) every time a new snapshot is created. The hash of the History structure is kept in the muxfs config file, and every filesystem has a corresponding History hash. A filesystem without any snapshots does not have an associated History hash.
When instantiating a FS that has multiple snapshots, we actually want to instantiate the latest one. To achieve this, we get the FSs' corresponding History hash (from the muxfs config file), fetch the History structure, and then use the latest snapshot hash to instantiate the FS.
A filesystem can be instantiated in two ways:
- Creating a new, blank filesystem using `muxfs.New`
- Instancing an existing filesystem using `muxfs.FromSnapshot` which takes a snapshot hash, fetches it from the store, and uses it to instantiate the FS.~~~