Skip to content

Commit

Permalink
nixos/storage: Generate UUID for each device spec
Browse files Browse the repository at this point in the history
We want to have deterministic UUIDs for every device specification in
order to avoid the need to manually set labels all over the place.

Of course, we could internally set labels instead of precomputing UUIDs,
but labels have different length restrictions for every file system (for
example XFS has a maximum of 12 bytes, for ext4 it's 16 bytes). In
addition to that we remove the ability for people to set their own
labels during runtime.

The UUIDs generated here are based on version 5:

https://tools.ietf.org/html/rfc4122#section-4.1.3

Our variant deviates from this a bit in that we use string concatenation
to build up the input for the SHA1 hash instead of binaries. The results
however are pretty much the same and in our part the most important
aspect is determinism rather than having a truly unique value across the
whole planet.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
  • Loading branch information
aszlig committed Jan 6, 2017
1 parent c0c04ca commit c469816
Showing 1 changed file with 41 additions and 1 deletion.
42 changes: 41 additions & 1 deletion nixos/modules/tasks/storage/default.nix
Expand Up @@ -32,6 +32,46 @@ let
};
};

genericOptions = deviceSpec: { name, ... }: {
options.uuid = mkOption {
internal = true;
description = ''
The UUID of this device specification used for device formats, such as
file systems and other containers.
This is a generated value and shouldn't be set outside of this module.
It identifies the same device from within nixpart and from within a
NixOS system for mounting.
By default, every file system gets a random UUID, but we need to have
this deterministic so that we always get the same UUID for the same
device specification. So we hash the full device specification (eg.
<literal>partition.foo</literal>) along with a
<literal>nixpart</literal> namespace with sha1 and truncate it to 128
bits, similar to version 5 of the UUID specification:
<link xlink:href="https://tools.ietf.org/html/rfc4122#section-4.1.3"/>
Note that instead of a binary namespace ID, we simply use string
concatenation in the form of
<literal>[namespace]:[spectype].[specname]</literal>, so for example the
device specification of <literal>partition.foo</literal> gets a hash
from <literal>nixpart:partition.foo</literal>.
'';
};
config.uuid = let
inherit (builtins) hashString substring;
baseHash = hashString "sha1" "nixpart:${deviceSpec.name}.${name}";
splitted = [
(substring 0 8 baseHash)
(substring 8 4 baseHash)
(substring 12 4 baseHash)
(substring 16 4 baseHash)
(substring 20 12 baseHash)
];
in lib.concatStringsSep "-" splitted;
};

orderableOptions = deviceSpec: {
options.before = mkOption {
type = types.listOf (storageLib.types.deviceSpec [ deviceSpec.name ]);
Expand Down Expand Up @@ -199,7 +239,7 @@ in
orderable = attrs.orderable or false;
resizable = attrs.resizable or false;
in types.attrsOf (types.submodule {
imports = lib.singleton attrs.options
imports = [ attrs.options (genericOptions deviceSpec) ]
++ lib.optional orderable (orderableOptions deviceSpec)
++ lib.optional resizable (resizableOptions deviceSpec);
});
Expand Down

0 comments on commit c469816

Please sign in to comment.