{ config, pkgs, ... }:
with pkgs.lib;
let
fileSystems = config.fileSystems;
mount = config.system.sbin.mount;
task =
''
PATH=${pkgs.e2fsprogs}/sbin:${pkgs.utillinuxng}/sbin:$PATH
newDevices=1
# If we mount any file system, we repeat this loop, because new
# mount opportunities may have become available (such as images
# for loopback mounts).
while test -n "$newDevices"; do
newDevices=
${flip concatMapStrings fileSystems
(fs: ''
mountPoint='${fs.mountPoint}'
device='${if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}"}'
fsType='${fs.fsType}'
# A device is a pseudo-device (i.e. not an actual device
# node) if it's not an absolute path (e.g. an NFS server
# such as machine:/path), if it starts with // (a CIFS FS),
# a known pseudo filesystem (such as tmpfs), or the device
# is a directory (e.g. a bind mount).
isPseudo=
test "''${device:0:1}" != / -o "''${device:0:2}" = // -o "$fsType" = "tmpfs" \
-o -d "$device" && isPseudo=1
if ! test -n "$isPseudo" -o -e "$device"; then
echo "skipping $device, doesn't exist (yet)"
continue
fi
# !!! quick hack: if the mount point is already mounted, try
# a remount to change the options but nothing else.
if cat /proc/mounts | grep -F -q " $mountPoint "; then
if test "''${device:0:2}" != //; then
echo "remounting $device on $mountPoint"
${mount}/bin/mount -t "$fsType" \
-o remount,"${fs.options}" \
"$device" "$mountPoint" || true
fi
continue
fi
# If $device is already mounted somewhere else, unmount it first.
# !!! Note: we use /etc/mtab, not /proc/mounts, because mtab
# contains more accurate info when using loop devices.
if test -z "$isPseudo"; then
device=$(readlink -f "$device")
prevMountPoint=$(
cat /etc/mtab \
| grep "^$device " \
| sed 's|^[^ ]\+ \+\([^ ]\+\).*|\1|' \
)
if test "$prevMountPoint" = "$mountPoint"; then
echo "remounting $device on $mountPoint"
${mount}/bin/mount -t "$fsType" \
-o remount,"${fs.options}" \
"$device" "$mountPoint" || true
continue
fi
if test -n "$prevMountPoint"; then
echo "unmount $device from $prevMountPoint"
${mount}/bin/umount "$prevMountPoint" || true
fi
fi
echo "mounting $device on $mountPoint"
# !!! should do something with the result; also prevent repeated fscks.
if test -z "$isPseudo"; then
fsck -a "$device" || true
fi
${optionalString fs.autocreate
''
mkdir -p "$mountPoint"
''
}
if ${mount}/bin/mount -t "$fsType" -o "$options" "$device" "$mountPoint"; then
newDevices=1
fi
'')
}
done
'';
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 (mountPoint = \"/\"). Each
entry in the list is an attribute set with the following fields:
mountPoint, device,
fsType (a file system type recognised by
mount; defaults to
\"auto\"), and options
(the mount options passed to mount using the
flag; defaults to \"defaults\").
Instead of specifying device, you can also
specify a volume label (label) for file
systems that support it, such as ext2/ext3 (see mke2fs
-L).
autocreate forces mountPoint to be created with
mkdir -p .
";
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 = "
Option used to mount the file system.
";
};
autocreate = mkOption {
default = false;
type = types.bool;
description = "
Automatically create the mount point defined in
.
";
};
};
};
system.sbin.mount = mkOption {
internal = true;
default = pkgs.utillinuxng;
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.mount_cifs pkgs.nfsUtils];
jobs.filesystems =
{ startOn = [ "startup" "new-devices" "ip-up" ];
script = task;
task = true;
};
};
}