Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create presets #108

Draft
wants to merge 18 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions README.org
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
* Impermanence
#+TITLE: Impermanence

Modules to help you handle persistent state on systems with
ephemeral root storage.
Expand All @@ -14,6 +14,13 @@
storage, so that specified files and folders persist between
reboots


* Contact

Join the [[https://matrix.to/#/#impermanence:nixos.org][matrix room]] to chat about the project.

* Usage

There are currently two modules: one for ~NixOS~ and one for ~home-manager~.

*** NixOS
Expand All @@ -30,6 +37,20 @@
attribute set of submodules, where the attribute name is the path
to persistent storage.

The easiest way to use the module is to enable community-defined presets:

#+begin_src nix
{
environment.persistence."/backed-up" = {
presets.essential.enable = true;
};

environment.persistence."/not-backed-up" = {
presets.system.enable = true;
};
}
#+end_src

Usage is shown best with an example:

#+begin_src nix
Expand All @@ -39,6 +60,7 @@
directories = [
"/var/log"
"/var/lib/bluetooth"
"/var/lib/nixos"
"/var/lib/systemd/coredump"
"/etc/NetworkManager/system-connections"
{ directory = "/var/lib/colord"; user = "colord"; group = "colord"; mode = "u=rwx,g=rx,o="; }
Expand Down Expand Up @@ -108,10 +130,10 @@
- ~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
(e.g. ~0700~) or a symbolic mode (e.g. ~u=rwx,g=,o=~). This also
applies to any parent directories which don't yet exist.
Changing this once the directory has been created has no
effect.
(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.

- ~files~ are all files you want to link or bind to persistent
storage. A file can be represented either as a string, simply
Expand Down Expand Up @@ -260,9 +282,3 @@
- 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.

** About the name
: Impermanence, also known as the philosophical problem of change, is a
: philosophical concept that is addressed in a variety of religions and
: philosophies. In Eastern philosophy it is best known for its role in the
: Buddhist three marks of existence. It also is an element of Hinduism.
43 changes: 8 additions & 35 deletions create-directories.bash
Original file line number Diff line number Diff line change
Expand Up @@ -47,44 +47,17 @@ fi
sourceBase="${sourceBase%/}"
target="${target%/}"

# check that the source exists and warn the user if it doesn't
# check that the source exists and warn the user if it doesn't, then
# create them with the specified permissions
realSource="$(realpath -m "$sourceBase$target")"
if [[ ! -d "$realSource" ]]; then
printf "Warning: Source directory '%s' does not exist; it will be created for you with the following permissions: owner: '%s:%s', mode: '%s'.\n" "$realSource" "$user" "$group" "$mode"
mkdir --mode="$mode" "$realSource"
chown "$user:$group" "$realSource"
fi

# iterate over each part of the target path, e.g. var, lib, iwd
previousPath="/"
[[ -d "$target" ]] || mkdir "$target"

OLD_IFS=$IFS
IFS=/ # split the path on /
for pathPart in $target; do
IFS=$OLD_IFS

# skip empty parts caused by the prefix slash and multiple
# consecutive slashes
[[ "$pathPart" == "" ]] && continue

# construct the incremental path, e.g. /var, /var/lib, /var/lib/iwd
currentTargetPath="$previousPath$pathPart/"

# construct the source path, e.g. /state/var, /state/var/lib, ...
currentSourcePath="$sourceBase$currentTargetPath"

# create the source and target directories if they don't exist
if [[ ! -d "$currentSourcePath" ]]; then
mkdir --mode="$mode" "$currentSourcePath"
chown "$user:$group" "$currentSourcePath"
fi
[[ -d "$currentTargetPath" ]] || mkdir "$currentTargetPath"

# resolve the source path to avoid symlinks
currentRealSourcePath="$(realpath -m "$currentSourcePath")"

# synchronize perms between source and target
chown --reference="$currentRealSourcePath" "$currentTargetPath"
chmod --reference="$currentRealSourcePath" "$currentTargetPath"

# lastly we update the previousPath to continue down the tree
previousPath="$currentTargetPath"
done
# synchronize perms between source and target
chown --reference="$realSource" "$target"
chmod --reference="$realSource" "$target"
13 changes: 10 additions & 3 deletions home-manager.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ let
isBindfs = v: (getDirMethod v) == "bindfs";
isSymlink = v: (getDirMethod v) == "symlink";

inherit (pkgs.callPackage ./lib.nix { }) splitPath dirListToPath concatPaths sanitizeName;
inherit (pkgs.callPackage ./lib.nix { })
splitPath
dirListToPath
concatPaths
sanitizeName
;

mount = "${pkgs.util-linux}/bin/mount";
unmountScript = mountPoint: tries: sleep: ''
Expand Down Expand Up @@ -394,15 +399,16 @@ in

in
mkMerge [
(mkIf (any (path: cfg.${path}.directories != [ ]) persistentStoragePaths) {
(mkIf (any (path: (filter isSymlink cfg.${path}.directories) != [ ]) persistentStoragePaths) {
# Clean up existing empty directories in the way of links
cleanEmptyLinkTargets =
dag.entryBefore
[ "checkLinkTargets" ]
''
${concatMapStrings mkLinkCleanupForPath persistentStoragePaths}
'';

})
(mkIf (any (path: (filter isBindfs cfg.${path}.directories) != [ ]) persistentStoragePaths) {
createAndMountPersistentStoragePaths =
dag.entryBefore
[ "writeBoundary" ]
Expand All @@ -415,6 +421,7 @@ in
dag.entryBefore
[ "createAndMountPersistentStoragePaths" ]
''
PATH=$PATH:/run/wrappers/bin
unmountBindMounts() {
${concatMapStrings mkUnmountsForPath persistentStoragePaths}
}
Expand Down
48 changes: 44 additions & 4 deletions lib.nix
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
{ lib }:
let
inherit (lib) filter concatMap concatStringsSep hasPrefix head
replaceStrings optionalString removePrefix foldl' elem;
inherit (lib.strings) sanitizeDerivationName;
inherit (lib)
filter
concatMap
concatStringsSep
hasPrefix
head
replaceStrings
optionalString
removePrefix
foldl'
elem
take
length
last
;
inherit (lib.strings)
sanitizeDerivationName
;

# ["/home/user/" "/.screenrc"] -> ["home" "user" ".screenrc"]
splitPath = paths:
Expand All @@ -22,6 +37,24 @@ let
in
prefix + path;


parentsOf = path:
let
prefix = optionalString (hasPrefix "/" path) "/";
split = splitPath [ path ];
parents = take ((length split) - 1) split;
in
foldl'
(state: item:
state ++ [
(concatPaths [
(if state != [ ] then last state else prefix)
item
])
])
[ ]
parents;

sanitizeName = name:
replaceStrings
[ "." ] [ "" ]
Expand All @@ -47,5 +80,12 @@ let
result.duplicates;
in
{
inherit splitPath dirListToPath concatPaths sanitizeName duplicates;
inherit
splitPath
dirListToPath
concatPaths
parentsOf
sanitizeName
duplicates
;
}
9 changes: 7 additions & 2 deletions mount-file.bash
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@ mountPoint="$1"
targetFile="$2"
debug="$3"

trace() {
if (( "$debug" )); then
echo "$@"
fi
}
if (( "$debug" )); then
set -o xtrace
fi

if [[ -L "$mountPoint" && $(readlink -f "$mountPoint") == "$targetFile" ]]; then
echo "$mountPoint already links to $targetFile, ignoring"
trace "$mountPoint already links to $targetFile, ignoring"
elif mount | grep -F "$mountPoint"' ' >/dev/null && ! mount | grep -F "$mountPoint"/ >/dev/null; then
echo "mount already exists at $mountPoint, ignoring"
trace "mount already exists at $mountPoint, ignoring"
elif [[ -e "$mountPoint" ]]; then
echo "A file already exists at $mountPoint!" >&2
exit 1
Expand Down
Loading