2022-11-13 21:26:04 +00:00
|
|
|
#+TITLE: Impermanence
|
2020-06-04 19:47:46 +00:00
|
|
|
|
2023-11-19 11:21:57 +00:00
|
|
|
Lets you choose what files and directories you want to keep between
|
|
|
|
reboots - the rest are thrown away.
|
2020-06-05 16:54:15 +00:00
|
|
|
|
2023-11-19 11:21:57 +00:00
|
|
|
Why would you want this?
|
2020-06-05 16:54:15 +00:00
|
|
|
|
2023-11-19 11:21:57 +00:00
|
|
|
- It keeps your system clean by default.
|
2020-06-05 16:54:15 +00:00
|
|
|
|
2023-11-19 11:21:57 +00:00
|
|
|
- It forces you to declare settings you want to keep.
|
2020-06-05 16:54:15 +00:00
|
|
|
|
2023-11-19 11:21:57 +00:00
|
|
|
- It lets you experiment with new software without cluttering up
|
|
|
|
your system.
|
2020-06-05 16:54:15 +00:00
|
|
|
|
2023-11-19 11:21:57 +00:00
|
|
|
There are a few different things to set up for this to work:
|
|
|
|
|
|
|
|
- A root filesystem which somehow gets wiped on reboot. There are a
|
2024-01-30 16:08:36 +00:00
|
|
|
few ways to achieve this. See the [[#system-setup][System setup]] section for more info.
|
2023-11-19 11:21:57 +00:00
|
|
|
|
|
|
|
- At least one mounted volume where the files and directories you
|
|
|
|
want to keep are stored permanently.
|
|
|
|
|
|
|
|
- At least one of the modules in this repository, which take care of
|
|
|
|
linking or bind mounting files between the persistent storage
|
2024-01-30 16:08:36 +00:00
|
|
|
mount point and the root file system. See the [[#module-usage][Module usage]] section
|
2023-11-19 11:21:57 +00:00
|
|
|
for more info.
|
2022-11-13 21:26:04 +00:00
|
|
|
|
|
|
|
* Contact
|
|
|
|
|
|
|
|
Join the [[https://matrix.to/#/#impermanence:nixos.org][matrix room]] to chat about the project.
|
|
|
|
|
2023-11-19 11:21:57 +00:00
|
|
|
* System setup
|
|
|
|
|
|
|
|
There are many ways to wipe your root partition between boots. This
|
|
|
|
section lists a few common ways to accomplish this, but is by no
|
|
|
|
means an exhaustive list.
|
|
|
|
|
|
|
|
*** tmpfs
|
|
|
|
|
2023-12-26 23:08:27 +00:00
|
|
|
The easiest method is to use a tmpfs filesystem for the
|
2023-11-19 11:21:57 +00:00
|
|
|
root. This is the easiest way to set up impermanence on systems
|
|
|
|
which currently use a traditional filesystem (ext4, xfs, etc) as
|
|
|
|
the root filesystem, since you don't have to repartition.
|
|
|
|
|
|
|
|
All data stored in tmpfs only resides in system memory, not on
|
|
|
|
disk. This automatically takes care of cleaning up between boots,
|
|
|
|
but also comes with some pretty significant drawbacks:
|
|
|
|
|
|
|
|
- Downloading big files or trying programs that generate large
|
|
|
|
amounts of data can easily result in either an out-of-memory or
|
|
|
|
disk-full scenario.
|
|
|
|
|
|
|
|
- If the system crashes or loses power before you've had a chance
|
|
|
|
to move files you want to keep to persistent storage, they're
|
|
|
|
gone forever.
|
|
|
|
|
|
|
|
Using tmpfs as the root filesystem, the filesystem setup would
|
|
|
|
look something like this:
|
|
|
|
|
|
|
|
#+begin_src nix
|
|
|
|
{
|
|
|
|
fileSystems."/" = {
|
|
|
|
device = "none";
|
|
|
|
fsType = "tmpfs";
|
|
|
|
options = [ "defaults" "size=25%" "mode=755" ];
|
|
|
|
};
|
|
|
|
|
|
|
|
fileSystems."/persistent" = {
|
|
|
|
device = "/dev/root_vg/root";
|
|
|
|
neededForBoot = true;
|
|
|
|
fsType = "btrfs";
|
|
|
|
options = [ "subvol=persistent" ];
|
|
|
|
};
|
|
|
|
|
|
|
|
fileSystems."/nix" = {
|
|
|
|
device = "/dev/root_vg/root";
|
|
|
|
fsType = "btrfs";
|
|
|
|
options = [ "subvol=nix" ];
|
|
|
|
};
|
|
|
|
|
|
|
|
fileSystems."/boot" = {
|
|
|
|
device = "/dev/disk/by-uuid/XXXX-XXXX";
|
|
|
|
fsType = "vfat";
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
where the ~size~ option determines how much system memory is allowed
|
|
|
|
to be used by the filesystem.
|
|
|
|
|
|
|
|
*** BTRFS subvolumes
|
|
|
|
|
|
|
|
A more advanced solution which doesn't have the same drawbacks as
|
|
|
|
using tmpfs is to use a regular filesystem, but clean it up
|
|
|
|
between boots. A relatively easy way to do this is to use BTRFS
|
|
|
|
and create a new subvolume to use as root on boot. This also
|
|
|
|
allows you to keep a number of old roots around, in case of
|
|
|
|
crashes, power outages or other accidents.
|
|
|
|
|
2024-07-16 01:41:53 +00:00
|
|
|
A setup which would automatically remove roots that are
|
2023-11-19 11:21:57 +00:00
|
|
|
older than 30 days could look like this:
|
|
|
|
|
|
|
|
#+begin_src nix
|
|
|
|
{
|
|
|
|
fileSystems."/" = {
|
|
|
|
device = "/dev/root_vg/root";
|
|
|
|
fsType = "btrfs";
|
|
|
|
options = [ "subvol=root" ];
|
|
|
|
};
|
|
|
|
|
|
|
|
boot.initrd.postDeviceCommands = lib.mkAfter ''
|
|
|
|
mkdir /btrfs_tmp
|
|
|
|
mount /dev/root_vg/root /btrfs_tmp
|
|
|
|
if [[ -e /btrfs_tmp/root ]]; then
|
|
|
|
mkdir -p /btrfs_tmp/old_roots
|
|
|
|
timestamp=$(date --date="@$(stat -c %Y /btrfs_tmp/root)" "+%Y-%m-%-d_%H:%M:%S")
|
|
|
|
mv /btrfs_tmp/root "/btrfs_tmp/old_roots/$timestamp"
|
|
|
|
fi
|
|
|
|
|
|
|
|
delete_subvolume_recursively() {
|
|
|
|
IFS=$'\n'
|
|
|
|
for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
|
|
|
|
delete_subvolume_recursively "/btrfs_tmp/$i"
|
|
|
|
done
|
|
|
|
btrfs subvolume delete "$1"
|
|
|
|
}
|
|
|
|
|
|
|
|
for i in $(find /btrfs_tmp/old_roots/ -maxdepth 1 -mtime +30); do
|
|
|
|
delete_subvolume_recursively "$i"
|
|
|
|
done
|
|
|
|
|
|
|
|
btrfs subvolume create /btrfs_tmp/root
|
|
|
|
umount /btrfs_tmp
|
|
|
|
'';
|
|
|
|
|
|
|
|
fileSystems."/persistent" = {
|
|
|
|
device = "/dev/root_vg/root";
|
|
|
|
neededForBoot = true;
|
|
|
|
fsType = "btrfs";
|
|
|
|
options = [ "subvol=persistent" ];
|
|
|
|
};
|
|
|
|
|
|
|
|
fileSystems."/nix" = {
|
|
|
|
device = "/dev/root_vg/root";
|
|
|
|
fsType = "btrfs";
|
|
|
|
options = [ "subvol=nix" ];
|
|
|
|
};
|
|
|
|
|
|
|
|
fileSystems."/boot" = {
|
|
|
|
device = "/dev/disk/by-uuid/XXXX-XXXX";
|
|
|
|
fsType = "vfat";
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#+end_src
|
|
|
|
|
|
|
|
This assumes the BTRFS filesystem can be found in an LVM volume
|
|
|
|
group called ~root_vg~. Adjust the path as necessary.
|
|
|
|
|
|
|
|
* Module usage
|
2022-11-13 21:26:04 +00:00
|
|
|
|
2020-06-05 16:54:15 +00:00
|
|
|
There are currently two modules: one for ~NixOS~ and one for ~home-manager~.
|
|
|
|
|
|
|
|
*** NixOS
|
|
|
|
|
|
|
|
To use the module, import it into your configuration with
|
|
|
|
|
|
|
|
#+begin_src nix
|
2020-06-07 04:58:23 +00:00
|
|
|
{
|
|
|
|
imports = [ /path/to/impermanence/nixos.nix ];
|
|
|
|
}
|
2020-06-05 16:54:15 +00:00
|
|
|
#+end_src
|
|
|
|
|
2023-11-19 11:21:57 +00:00
|
|
|
or use the provided ~nixosModules.impermanence~ flake output:
|
|
|
|
|
|
|
|
#+begin_src nix
|
|
|
|
{
|
|
|
|
inputs = {
|
|
|
|
impermanence.url = "github:nix-community/impermanence";
|
|
|
|
};
|
|
|
|
|
|
|
|
outputs = { self, nixpkgs, impermanence, ... }:
|
|
|
|
{
|
|
|
|
nixosConfigurations.sythe = nixpkgs.lib.nixosSystem {
|
|
|
|
system = "x86_64-linux";
|
|
|
|
modules = [
|
|
|
|
impermanence.nixosModules.impermanence
|
|
|
|
./machines/sythe/configuration.nix
|
|
|
|
];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#+end_src
|
|
|
|
|
2020-06-05 16:54:15 +00:00
|
|
|
This adds the ~environment.persistence~ option, which is an
|
|
|
|
attribute set of submodules, where the attribute name is the path
|
|
|
|
to persistent storage.
|
|
|
|
|
|
|
|
Usage is shown best with an example:
|
|
|
|
|
|
|
|
#+begin_src nix
|
2020-06-07 04:58:23 +00:00
|
|
|
{
|
|
|
|
environment.persistence."/persistent" = {
|
2024-06-09 11:22:16 +00:00
|
|
|
enable = true; # NB: Defaults to true, not needed
|
2022-02-04 02:40:25 +00:00
|
|
|
hideMounts = true;
|
2020-06-07 04:58:23 +00:00
|
|
|
directories = [
|
|
|
|
"/var/log"
|
|
|
|
"/var/lib/bluetooth"
|
2023-02-02 16:23:19 +00:00
|
|
|
"/var/lib/nixos"
|
2020-06-07 04:58:23 +00:00
|
|
|
"/var/lib/systemd/coredump"
|
|
|
|
"/etc/NetworkManager/system-connections"
|
2022-01-28 11:58:14 +00:00
|
|
|
{ directory = "/var/lib/colord"; user = "colord"; group = "colord"; mode = "u=rwx,g=rx,o="; }
|
2020-06-07 04:58:23 +00:00
|
|
|
];
|
|
|
|
files = [
|
|
|
|
"/etc/machine-id"
|
2023-11-19 11:21:57 +00:00
|
|
|
{ file = "/var/keys/secret_file"; parentDirectory = { mode = "u=rwx,g=,o="; }; }
|
2020-06-07 04:58:23 +00:00
|
|
|
];
|
2022-01-16 17:59:03 +00:00
|
|
|
users.talyz = {
|
|
|
|
directories = [
|
|
|
|
"Downloads"
|
|
|
|
"Music"
|
|
|
|
"Pictures"
|
|
|
|
"Documents"
|
|
|
|
"Videos"
|
|
|
|
"VirtualBox VMs"
|
2022-01-28 11:58:14 +00:00
|
|
|
{ directory = ".gnupg"; mode = "0700"; }
|
|
|
|
{ directory = ".ssh"; mode = "0700"; }
|
|
|
|
{ directory = ".nixops"; mode = "0700"; }
|
|
|
|
{ directory = ".local/share/keyrings"; mode = "0700"; }
|
2022-01-16 17:59:03 +00:00
|
|
|
".local/share/direnv"
|
|
|
|
];
|
|
|
|
files = [
|
|
|
|
".screenrc"
|
|
|
|
];
|
|
|
|
};
|
2020-06-07 04:58:23 +00:00
|
|
|
};
|
|
|
|
}
|
2020-06-05 16:54:15 +00:00
|
|
|
#+end_src
|
|
|
|
|
|
|
|
- ~"/persistent"~ is the path to your persistent storage location
|
|
|
|
|
2022-01-16 17:59:03 +00:00
|
|
|
This allows for multiple different persistent storage
|
|
|
|
locations. If you, for example, have one location you back up
|
|
|
|
and one you don't, you can use both by defining two separate
|
|
|
|
attributes under ~environment.persistence~.
|
|
|
|
|
2024-06-09 11:22:16 +00:00
|
|
|
- ~enable~ determines whether the persistent storage location should
|
|
|
|
be enabled or not. Useful when sharing configurations between
|
2024-06-09 11:26:10 +00:00
|
|
|
systems with and without impermanence setups. Defaults to ~true~.
|
2024-06-09 11:22:16 +00:00
|
|
|
|
|
|
|
- ~hideMounts~ allows you to specify whether to hide the
|
|
|
|
bind mounts from showing up as mounted drives in the file
|
|
|
|
manager. If enabled, it sets the mount option ~x-gvfs-hide~
|
|
|
|
on all the bind mounts.
|
|
|
|
|
2022-01-16 17:59:03 +00:00
|
|
|
- ~directories~ are all directories you want to bind mount to
|
2022-01-28 11:58:14 +00:00
|
|
|
persistent storage. A directory can be represented either as a
|
|
|
|
string, simply denoting its path, or as a submodule. The
|
|
|
|
submodule representation is useful when the default assumptions,
|
|
|
|
mainly regarding permissions, are incorrect. The available
|
|
|
|
options are:
|
|
|
|
|
|
|
|
- ~directory~, the path to the directory you want to bind mount
|
|
|
|
to persistent storage. Only setting this option is
|
|
|
|
equivalent to the string representation.
|
|
|
|
|
|
|
|
- ~persistentStoragePath~, the path to persistent
|
|
|
|
storage. Defaults to the ~environment.persistence~ submodule
|
|
|
|
name, i.e. ~"/persistent"~ in the example. This should most
|
|
|
|
likely be left to its default value - don't change it unless
|
|
|
|
you're certain you really need to.
|
|
|
|
|
|
|
|
- ~user~, the user who should own the directory. If the directory
|
|
|
|
doesn't already exist in persistent storage, it will be
|
|
|
|
created and this user will be its owner. This also applies to
|
|
|
|
any parent directories which don't yet exist. Changing this
|
|
|
|
once the directory has been created has no effect.
|
|
|
|
|
|
|
|
- ~group~, the group who should own the directory. If the
|
|
|
|
directory doesn't already exist in persistent storage, it will
|
|
|
|
be created and this group will be its owner. This also applies
|
|
|
|
to any parent directories which don't yet exist. Changing this
|
|
|
|
once the directory has been created has no effect.
|
|
|
|
|
|
|
|
- ~mode~, the permissions to set for the directory. If the
|
|
|
|
directory doesn't already exist in persistent storage, it will
|
|
|
|
be created with this mode. Can be either an octal mode
|
2022-11-13 17:18:15 +00:00
|
|
|
(e.g. ~0700~) or a symbolic mode (e.g. ~u=rwx,g=,o=~). Parent
|
|
|
|
directories that don't yet exist are created with default
|
|
|
|
permissions. Changing this once the directory has been created
|
|
|
|
has no effect.
|
2022-01-16 17:59:03 +00:00
|
|
|
|
|
|
|
- ~files~ are all files you want to link or bind to persistent
|
2022-01-28 11:58:14 +00:00
|
|
|
storage. A file can be represented either as a string, simply
|
|
|
|
denoting its path, or as a submodule. The submodule
|
|
|
|
representation is useful when the default assumptions, mainly
|
|
|
|
regarding the permissions of its parent directory, are
|
|
|
|
incorrect. The available options are:
|
|
|
|
|
|
|
|
- ~file~, the path to the file you want to bind mount to
|
|
|
|
persistent storage. Only setting this option is equivalent to
|
|
|
|
the string representation.
|
|
|
|
|
|
|
|
- ~persistentStoragePath~, the path to persistent
|
|
|
|
storage. Defaults to the ~environment.persistence~ submodule
|
|
|
|
name, i.e. ~"/persistent"~ in the example. This should most
|
|
|
|
likely be left to its default value - don't change it unless
|
|
|
|
you're certain you really need to.
|
|
|
|
|
|
|
|
- ~parentDirectory~, the permissions that should be applied to the
|
|
|
|
file's parent directory, if it doesn't already
|
|
|
|
exist. Available options are ~user~, ~group~ and ~mode~. See their
|
|
|
|
definition in ~directories~ above.
|
2022-01-16 17:59:03 +00:00
|
|
|
|
|
|
|
If the file exists in persistent storage, it will be bind
|
|
|
|
mounted to the target path; otherwise it will be symlinked.
|
|
|
|
|
|
|
|
- ~users.talyz~ handles files and directories in ~talyz~'s home
|
|
|
|
directory
|
|
|
|
|
|
|
|
The ~users~ option defines a set of submodules which correspond to
|
|
|
|
the users' names. The ~directories~ and ~files~ options of each
|
|
|
|
submodule work like their root counterparts, but the paths are
|
|
|
|
automatically prefixed with with the user's home directory.
|
|
|
|
|
|
|
|
If the user has a non-standard home directory (i.e. not
|
|
|
|
~/home/<username>~), the ~users.<username>.home~ option has to be
|
|
|
|
set to this path - it can't currently be automatically deduced
|
|
|
|
due to a limitation in ~nixpkgs~.
|
2020-06-05 16:54:15 +00:00
|
|
|
|
2020-06-20 15:23:15 +00:00
|
|
|
/Important note:/ Make sure your persistent volumes are marked with
|
2020-06-05 16:54:15 +00:00
|
|
|
~neededForBoot~, otherwise you will run into problems.
|
|
|
|
|
|
|
|
*** home-manager
|
|
|
|
|
|
|
|
Usage of the ~home-manager~ module is very similar to the one of the
|
|
|
|
~NixOS~ module - the key differences are that the ~persistence~ option
|
|
|
|
is now under ~home~, rather than ~environment~, and the addition of
|
|
|
|
the submodule option ~removePrefixDirectory~.
|
|
|
|
|
2021-10-29 14:20:58 +00:00
|
|
|
/Important note:/ You have to use the ~home-manager~ ~NixOS~ module (in
|
|
|
|
the ~nixos~ directory of ~home-manager~'s repo) in order for this
|
|
|
|
module to work as intended.
|
|
|
|
|
2020-06-05 16:54:15 +00:00
|
|
|
To use the module, import it into your configuration with
|
|
|
|
|
|
|
|
#+begin_src nix
|
2020-06-07 04:58:23 +00:00
|
|
|
{
|
|
|
|
imports = [ /path/to/impermanence/home-manager.nix ];
|
|
|
|
}
|
2020-06-05 16:54:15 +00:00
|
|
|
#+end_src
|
|
|
|
|
|
|
|
This adds the ~home.persistence~ option, which is an attribute set
|
|
|
|
of submodules, where the attribute name is the path to persistent
|
|
|
|
storage.
|
|
|
|
|
|
|
|
Usage is shown best with an example:
|
|
|
|
|
|
|
|
#+begin_src nix
|
2020-06-07 04:58:23 +00:00
|
|
|
{
|
|
|
|
home.persistence."/persistent/home/talyz" = {
|
|
|
|
directories = [
|
|
|
|
"Downloads"
|
|
|
|
"Music"
|
|
|
|
"Pictures"
|
|
|
|
"Documents"
|
|
|
|
"Videos"
|
|
|
|
"VirtualBox VMs"
|
2021-01-24 22:34:16 +00:00
|
|
|
".gnupg"
|
2020-06-07 04:58:23 +00:00
|
|
|
".ssh"
|
|
|
|
".nixops"
|
|
|
|
".local/share/keyrings"
|
|
|
|
".local/share/direnv"
|
2022-07-14 01:57:29 +00:00
|
|
|
{
|
|
|
|
directory = ".local/share/Steam";
|
|
|
|
method = "symlink";
|
|
|
|
}
|
2020-06-07 04:58:23 +00:00
|
|
|
];
|
|
|
|
files = [
|
2021-01-24 22:34:16 +00:00
|
|
|
".screenrc"
|
2020-06-07 04:58:23 +00:00
|
|
|
];
|
2021-01-24 23:35:55 +00:00
|
|
|
allowOther = true;
|
2020-06-07 04:58:23 +00:00
|
|
|
};
|
|
|
|
}
|
2020-06-05 16:54:15 +00:00
|
|
|
#+end_src
|
|
|
|
|
|
|
|
- ~"/persistent/home/talyz"~ is the path to your persistent storage location
|
|
|
|
- ~directories~ are all directories you want to link to persistent storage
|
2022-07-14 01:57:29 +00:00
|
|
|
- It is possible to switch the linking ~method~ between bindfs (the
|
|
|
|
default) and symbolic links.
|
|
|
|
- ~files~ are all files you want to link to persistent storage. These are
|
|
|
|
symbolic links to their target location.
|
2021-01-24 23:35:55 +00:00
|
|
|
- ~allowOther~ allows other users, such as ~root~, to access files
|
|
|
|
through the bind mounted directories listed in
|
|
|
|
~directories~. Useful for ~sudo~ operations, Docker, etc. Requires
|
|
|
|
the NixOS configuration ~programs.fuse.userAllowOther = true~.
|
2020-06-05 16:54:15 +00:00
|
|
|
|
|
|
|
Additionally, the ~home-manager~ module allows for compatibility
|
|
|
|
with ~dotfiles~ repos structured for use with [[https://www.gnu.org/software/stow/][GNU Stow]], where the
|
|
|
|
files linked to are one level deeper than where they should end
|
|
|
|
up. This can be achieved by setting ~removePrefixDirectory~ to ~true~:
|
|
|
|
|
|
|
|
#+begin_src nix
|
2020-06-07 04:58:23 +00:00
|
|
|
{
|
|
|
|
home.persistence."/etc/nixos/home-talyz-nixpkgs/dotfiles" = {
|
|
|
|
removePrefixDirectory = true;
|
|
|
|
files = [
|
|
|
|
"screen/.screenrc"
|
|
|
|
];
|
|
|
|
directories = [
|
|
|
|
"fish/.config/fish"
|
|
|
|
];
|
|
|
|
};
|
|
|
|
}
|
2020-06-05 16:54:15 +00:00
|
|
|
#+end_src
|
|
|
|
|
|
|
|
In the example, the ~.screenrc~ file and ~.config/fish~ directory
|
|
|
|
should be linked to from the home directory; ~removePrefixDirectory~
|
|
|
|
removes the first part of the path when deciding where to put the
|
|
|
|
links.
|
2020-06-04 19:50:33 +00:00
|
|
|
|
2022-07-14 01:57:29 +00:00
|
|
|
/Note:/ When using ~bindfs~ fuse filesystem for directories, the names of
|
|
|
|
the directories you add will be visible in the ~/etc/mtab~ file and in the
|
|
|
|
output of ~mount~ to all users.
|
2020-06-20 15:23:15 +00:00
|
|
|
|
2020-06-09 03:53:07 +00:00
|
|
|
** Further reading
|
2020-06-20 15:23:15 +00:00
|
|
|
The following blog posts provide more information on the concept of ephemeral
|
|
|
|
roots:
|
2020-06-09 03:53:07 +00:00
|
|
|
|
2020-06-20 15:23:15 +00:00
|
|
|
- https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/ --- [[https://github.com/etu/][@etu]]'s blog post walks
|
|
|
|
the reader through a NixOS-on-tmpfs installation.
|
|
|
|
- https://grahamc.com/blog/erase-your-darlings --- [[https://github.com/grahamc/][@grahamc]]'s blog post details
|
|
|
|
why one would want to erase their state at every boot, as well as how to
|
|
|
|
achieve this using ZFS snapshots.
|
2024-02-02 04:02:28 +00:00
|
|
|
- https://willbush.dev/blog/impermanent-nixos/ --- [[https://github.com/willbush/][@willbush]]'s blog post
|
|
|
|
provides a detailed NixOS-on-tmpfs guide with optional LUKS encryption, and
|
|
|
|
utilizing nix flakes for an opinionated install.
|