b5dc3d520e
As reported by Bryce L Nordgren. Multi-disk btrfs filesystems need to get assembled first before they become mountable. Enable this by explicitly assigning fsType = "btrfs" in the filesystems list in configuration.nix svn path=/nixos/trunk/; revision=32682
255 lines
7.2 KiB
Nix
255 lines
7.2 KiB
Nix
{ config, pkgs, ... }:
|
||
|
||
with pkgs.lib;
|
||
|
||
let
|
||
needsBtrfsProgs = any (fs: fs.fsType == "btrfs") config.fileSystems;
|
||
|
||
# Packages that provide fsck backends.
|
||
fsPackages = [ pkgs.e2fsprogs pkgs.reiserfsprogs pkgs.dosfstools ]
|
||
++ optional needsBtrfsProgs pkgs.btrfsProgs;
|
||
|
||
|
||
in
|
||
|
||
{
|
||
|
||
###### interface
|
||
|
||
options = {
|
||
|
||
fileSystems = mkOption {
|
||
example = [
|
||
{ mountPoint = "/";
|
||
device = "/dev/hda1";
|
||
}
|
||
{ mountPoint = "/data";
|
||
device = "/dev/hda2";
|
||
fsType = "ext3";
|
||
options = "data=journal";
|
||
}
|
||
{ mountPoint = "/bigdisk";
|
||
label = "bigdisk";
|
||
}
|
||
];
|
||
|
||
description = "
|
||
The file systems to be mounted. It must include an entry for
|
||
the root directory (<literal>mountPoint = \"/\"</literal>). Each
|
||
entry in the list is an attribute set with the following fields:
|
||
<literal>mountPoint</literal>, <literal>device</literal>,
|
||
<literal>fsType</literal> (a file system type recognised by
|
||
<command>mount</command>; defaults to
|
||
<literal>\"auto\"</literal>), and <literal>options</literal>
|
||
(the mount options passed to <command>mount</command> using the
|
||
<option>-o</option> flag; defaults to <literal>\"defaults\"</literal>).
|
||
|
||
Instead of specifying <literal>device</literal>, you can also
|
||
specify a volume label (<literal>label</literal>) for file
|
||
systems that support it, such as ext2/ext3 (see <command>mke2fs
|
||
-L</command>).
|
||
|
||
<literal>autocreate</literal> forces <literal>mountPoint</literal> to be created with
|
||
<command>mkdir -p</command> .
|
||
";
|
||
|
||
type = types.nullOr (types.list types.optionSet);
|
||
|
||
options = {
|
||
|
||
mountPoint = mkOption {
|
||
example = "/mnt/usb";
|
||
type = types.uniq types.string;
|
||
description = "
|
||
Location of the mounted the file system.
|
||
";
|
||
};
|
||
|
||
device = mkOption {
|
||
default = null;
|
||
example = "/dev/sda";
|
||
type = types.uniq (types.nullOr types.string);
|
||
description = "
|
||
Location of the device.
|
||
";
|
||
};
|
||
|
||
label = mkOption {
|
||
default = null;
|
||
example = "root-partition";
|
||
type = types.uniq (types.nullOr types.string);
|
||
description = "
|
||
Label of the device (if any).
|
||
";
|
||
};
|
||
|
||
fsType = mkOption {
|
||
default = "auto";
|
||
example = "ext3";
|
||
type = types.uniq types.string;
|
||
description = "
|
||
Type of the file system.
|
||
";
|
||
};
|
||
|
||
options = mkOption {
|
||
default = "defaults,relatime";
|
||
example = "data=journal";
|
||
type = types.string;
|
||
merge = pkgs.lib.concatStringsSep ",";
|
||
description = "Options used to mount the file system.";
|
||
};
|
||
|
||
autocreate = mkOption {
|
||
default = false;
|
||
type = types.bool;
|
||
description = "
|
||
Automatically create the mount point defined in
|
||
<option>fileSystems.*.mountPoint</option>.
|
||
";
|
||
};
|
||
|
||
noCheck = mkOption {
|
||
default = false;
|
||
type = types.bool;
|
||
description = "Disable running fsck on this filesystem.";
|
||
};
|
||
};
|
||
};
|
||
|
||
system.sbin.mount = mkOption {
|
||
internal = true;
|
||
default = pkgs.utillinux;
|
||
description = "
|
||
Package containing mount and umount.
|
||
";
|
||
};
|
||
|
||
};
|
||
|
||
|
||
###### implementation
|
||
|
||
config = {
|
||
|
||
# Add the mount helpers to the system path so that `mount' can find them.
|
||
environment.systemPackages =
|
||
[ pkgs.ntfs3g pkgs.cifs_utils pkgs.nfsUtils pkgs.mountall ]
|
||
++ fsPackages;
|
||
|
||
environment.etc = singleton
|
||
{ source = pkgs.writeText "fstab"
|
||
''
|
||
# This is a generated file. Do not edit!
|
||
|
||
# Filesystems.
|
||
${flip concatMapStrings config.fileSystems (fs:
|
||
(if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}")
|
||
+ " " + fs.mountPoint
|
||
+ " " + fs.fsType
|
||
+ " " + fs.options
|
||
+ " 0"
|
||
+ " " + (if fs.fsType == "none" || fs.noCheck then "0" else
|
||
if fs.mountPoint == "/" then "1" else "2")
|
||
+ "\n"
|
||
)}
|
||
|
||
# Swap devices.
|
||
${flip concatMapStrings config.swapDevices (sw:
|
||
"${sw.device} none swap\n"
|
||
)}
|
||
'';
|
||
target = "fstab";
|
||
};
|
||
|
||
jobs.mountall =
|
||
{ startOn = "started udev"
|
||
# !!! The `started nfs-kernel-statd' condition shouldn't be
|
||
# here. The `nfs-kernel-statd' job should have a `starting
|
||
# mountall' condition. However, that doesn't work if
|
||
# `mountall' is restarted due to an apparent bug in Upstart:
|
||
# `mountall' hangs forever in the `start/starting' state.
|
||
+ optionalString config.services.nfsKernel.client.enable " and started nfs-kernel-statd";
|
||
|
||
task = true;
|
||
|
||
script =
|
||
''
|
||
exec > /dev/console 2>&1
|
||
echo "mounting filesystems..."
|
||
export PATH=${config.system.sbin.mount}/bin:${makeSearchPath "sbin" ([pkgs.utillinux] ++ fsPackages)}:$PATH
|
||
${optionalString needsBtrfsProgs "${pkgs.btrfsProgs}/bin/btrfs device scan"}
|
||
${pkgs.mountall}/sbin/mountall
|
||
'';
|
||
};
|
||
|
||
# The `mount-failed' event is emitted synchronously, but we don't
|
||
# want `mountall' to wait for the emergency shell. So use this
|
||
# intermediate job to make the event asynchronous.
|
||
jobs.mount_failed =
|
||
{ name = "mount-failed";
|
||
task = true;
|
||
startOn = "mount-failed";
|
||
script =
|
||
''
|
||
[ -n "$MOUNTPOINT" ] || exit 0
|
||
start --no-wait emergency-shell \
|
||
DEVICE="$DEVICE" MOUNTPOINT="$MOUNTPOINT"
|
||
'';
|
||
};
|
||
|
||
# On an `ip-up' event, notify mountall so that it retries mounting
|
||
# remote filesystems.
|
||
jobs.mountall_ip_up =
|
||
{
|
||
name = "mountall-ip-up";
|
||
task = true;
|
||
startOn = "ip-up";
|
||
script =
|
||
''
|
||
${pkgs.procps}/bin/pkill -USR1 -u root mountall || true
|
||
'';
|
||
};
|
||
|
||
jobs.emergency_shell =
|
||
{ name = "emergency-shell";
|
||
|
||
task = true;
|
||
|
||
extraConfig = "console owner";
|
||
|
||
script =
|
||
''
|
||
[ -n "$MOUNTPOINT" ] || exit 0
|
||
|
||
exec < /dev/console > /dev/console 2>&1
|
||
|
||
cat <<EOF
|
||
|
||
[1;31m<<< Emergency shell >>>[0m
|
||
|
||
The filesystem \`$DEVICE' could not be mounted on \`$MOUNTPOINT'.
|
||
|
||
Please do one of the following:
|
||
|
||
- Repair the filesystem (\`fsck $DEVICE') and exit the emergency
|
||
shell to resume booting.
|
||
|
||
- Ignore any failed filesystems and continue booting by running
|
||
\`initctl emit filesystem'.
|
||
|
||
- Remove the failed filesystem from the system configuration in
|
||
/etc/nixos/configuration.nix and run \`nixos-rebuild switch'.
|
||
|
||
EOF
|
||
|
||
${pkgs.shadow}/bin/login root || false
|
||
|
||
initctl start --no-wait mountall
|
||
'';
|
||
};
|
||
|
||
};
|
||
|
||
}
|