2020-04-24 23:36:52 +00:00
{ config , lib , pkgs , utils , . . . }:
with lib ;
with utils ;
let
addCheckDesc = desc : elemType : check : types . addCheck elemType check
// { description = " ${ elemType . description } ( w i t h c h e c k : ${ desc } ) " ; } ;
2021-02-05 17:12:51 +00:00
isNonEmpty = s : ( builtins . match " [ \t \n ] * " s ) == null ;
nonEmptyStr = addCheckDesc " n o n - e m p t y " types . str isNonEmpty ;
2020-04-24 23:36:52 +00:00
fileSystems' = toposort fsBefore ( attrValues config . fileSystems ) ;
fileSystems = if fileSystems' ? result
then # use topologically sorted fileSystems everywhere
fileSystems' . result
else # the assertion below will catch this,
# but we fall back to the original order
# anyway so that other modules could check
# their assertions too
( attrValues config . fileSystems ) ;
specialFSTypes = [ " p r o c " " s y s f s " " t m p f s " " r a m f s " " d e v t m p f s " " d e v p t s " ] ;
2021-06-28 23:13:55 +00:00
nonEmptyWithoutTrailingSlash = addCheckDesc " n o n - e m p t y w i t h o u t t r a i l i n g s l a s h " types . str
( s : isNonEmpty s && ( builtins . match " . + / " s ) == null ) ;
2020-04-24 23:36:52 +00:00
coreFileSystemOpts = { name , config , . . . }: {
options = {
mountPoint = mkOption {
example = " / m n t / u s b " ;
2021-06-28 23:13:55 +00:00
type = nonEmptyWithoutTrailingSlash ;
2024-04-21 15:54:59 +00:00
description = " L o c a t i o n o f t h e m o u n t e d f i l e s y s t e m . " ;
2020-04-24 23:36:52 +00:00
} ;
2023-07-15 17:15:38 +00:00
stratis . poolUuid = lib . mkOption {
type = types . uniq ( types . nullOr types . str ) ;
2024-04-21 15:54:59 +00:00
description = ''
2023-07-15 17:15:38 +00:00
UUID of the stratis pool that the fs is located in
'' ;
example = " 0 4 c 6 8 0 6 3 - 9 0 a 5 - 4 2 3 5 - b 9 d d - 6 1 8 0 0 9 8 a 2 0 d 9 " ;
default = null ;
} ;
2020-04-24 23:36:52 +00:00
device = mkOption {
default = null ;
example = " / d e v / s d a " ;
type = types . nullOr nonEmptyStr ;
2024-04-21 15:54:59 +00:00
description = " L o c a t i o n o f t h e d e v i c e . " ;
2020-04-24 23:36:52 +00:00
} ;
fsType = mkOption {
default = " a u t o " ;
example = " e x t 3 " ;
type = nonEmptyStr ;
2024-04-21 15:54:59 +00:00
description = " T y p e o f t h e f i l e s y s t e m . " ;
2020-04-24 23:36:52 +00:00
} ;
options = mkOption {
default = [ " d e f a u l t s " ] ;
example = [ " d a t a = j o u r n a l " ] ;
2024-04-21 15:54:59 +00:00
description = " O p t i o n s u s e d t o m o u n t t h e f i l e s y s t e m . " ;
2022-12-28 21:21:41 +00:00
type = types . nonEmptyListOf nonEmptyStr ;
2020-04-24 23:36:52 +00:00
} ;
2021-06-28 23:13:55 +00:00
depends = mkOption {
default = [ ] ;
example = [ " / p e r s i s t " ] ;
type = types . listOf nonEmptyWithoutTrailingSlash ;
2024-04-21 15:54:59 +00:00
description = ''
2021-06-28 23:13:55 +00:00
List of paths that should be mounted before this one . This filesystem's
2022-08-12 12:06:08 +00:00
{ option } ` device ` and { option } ` mountPoint ` are always
2021-06-28 23:13:55 +00:00
checked and do not need to be included explicitly . If a path is added
to this list , any other filesystem whose mount point is a parent of
the path will be mounted before this filesystem . The paths do not need
2022-08-12 12:06:08 +00:00
to actually be the { option } ` mountPoint ` of some other filesystem .
2021-06-28 23:13:55 +00:00
'' ;
} ;
2020-04-24 23:36:52 +00:00
} ;
config = {
mountPoint = mkDefault name ;
device = mkIf ( elem config . fsType specialFSTypes ) ( mkDefault config . fsType ) ;
} ;
} ;
fileSystemOpts = { config , . . . }: {
options = {
label = mkOption {
default = null ;
example = " r o o t - p a r t i t i o n " ;
type = types . nullOr nonEmptyStr ;
2024-04-21 15:54:59 +00:00
description = " L a b e l o f t h e d e v i c e ( i f a n y ) . " ;
2020-04-24 23:36:52 +00:00
} ;
autoFormat = mkOption {
default = false ;
type = types . bool ;
2024-04-21 15:54:59 +00:00
description = ''
2020-04-24 23:36:52 +00:00
If the device does not currently contain a filesystem ( as
2024-04-21 15:54:59 +00:00
determined by { command } ` blkid ` ) , then automatically
2020-04-24 23:36:52 +00:00
format it with the filesystem type specified in
2022-08-12 12:06:08 +00:00
{ option } ` fsType ` . Use with caution .
2020-04-24 23:36:52 +00:00
'' ;
} ;
formatOptions = mkOption {
2023-07-15 17:15:38 +00:00
visible = false ;
type = types . unspecified ;
default = null ;
2020-04-24 23:36:52 +00:00
} ;
autoResize = mkOption {
default = false ;
type = types . bool ;
2024-04-21 15:54:59 +00:00
description = ''
2020-04-24 23:36:52 +00:00
If set , the filesystem is grown to its maximum size before
being mounted . ( This is typically the size of the containing
partition . ) This is currently only supported for ext2/3/4
filesystems that are mounted during early boot .
'' ;
} ;
noCheck = mkOption {
default = false ;
type = types . bool ;
2024-04-21 15:54:59 +00:00
description = " D i s a b l e r u n n i n g f s c k o n t h i s f i l e s y s t e m . " ;
2020-04-24 23:36:52 +00:00
} ;
} ;
2023-07-15 17:15:38 +00:00
config . options = mkMerge [
( mkIf config . autoResize [ " x - s y s t e m d . g r o w f s " ] )
( mkIf config . autoFormat [ " x - s y s t e m d . m a k e f s " ] )
( mkIf ( utils . fsNeededForBoot config ) [ " x - i n i t r d . m o u n t " ] )
] ;
2020-04-24 23:36:52 +00:00
} ;
# Makes sequence of `specialMount device mountPoint options fsType` commands.
# `systemMount` should be defined in the sourcing script.
makeSpecialMounts = mounts :
pkgs . writeText " m o u n t s . s h " ( concatMapStringsSep " \n " ( mount : ''
specialMount " ${ mount . device } " " ${ mount . mountPoint } " " ${ concatStringsSep " , " mount . options } " " ${ mount . fsType } "
'' ) m o u n t s ) ;
2022-08-21 13:32:41 +00:00
makeFstabEntries =
let
2023-03-15 16:39:30 +00:00
fsToSkipCheck = [
" n o n e "
" a u t o "
" o v e r l a y "
" i s o 9 6 6 0 "
" b i n d f s "
" u d f "
" b t r f s "
" z f s "
" t m p f s "
" b c a c h e f s "
" n f s "
" n f s 4 "
" n i l f s 2 "
" v b o x s f "
" s q u a s h f s "
" g l u s t e r f s "
" a p f s "
" 9 p "
" c i f s "
" p r l _ f s "
" v m h g f s "
] ++ lib . optionals ( ! config . boot . initrd . checkJournalingFS ) [
" e x t 3 "
" e x t 4 "
" r e i s e r f s "
" x f s "
" j f s "
" f 2 f s "
] ;
2022-08-21 13:32:41 +00:00
isBindMount = fs : builtins . elem " b i n d " fs . options ;
skipCheck = fs : fs . noCheck || fs . device == " n o n e " || builtins . elem fs . fsType fsToSkipCheck || isBindMount fs ;
# https://wiki.archlinux.org/index.php/fstab#Filepath_spaces
escape = string : builtins . replaceStrings [ " " " \t " ] [ " \\ 0 4 0 " " \\ 0 1 1 " ] string ;
2023-10-09 19:29:22 +00:00
in fstabFileSystems : { }: concatMapStrings ( fs :
( if fs . device != null then escape fs . device
2022-08-21 13:32:41 +00:00
else if fs . label != null then " / d e v / d i s k / b y - l a b e l / ${ escape fs . label } "
else throw " N o d e v i c e s p e c i f i e d f o r m o u n t p o i n t ‘ ${ fs . mountPoint } ’ . " )
2023-03-15 16:39:30 +00:00
+ " " + escape fs . mountPoint
2022-08-21 13:32:41 +00:00
+ " " + fs . fsType
2023-07-15 17:15:38 +00:00
+ " " + escape ( builtins . concatStringsSep " , " fs . options )
2023-03-15 16:39:30 +00:00
+ " 0 " + ( if skipCheck fs then " 0 " else if fs . mountPoint == " / " then " 1 " else " 2 " )
2022-08-21 13:32:41 +00:00
+ " \n "
) fstabFileSystems ;
2023-10-09 19:29:22 +00:00
initrdFstab = pkgs . writeText " i n i t r d - f s t a b " ( makeFstabEntries ( filter utils . fsNeededForBoot fileSystems ) { } ) ;
2022-08-21 13:32:41 +00:00
2020-04-24 23:36:52 +00:00
in
{
###### interface
options = {
fileSystems = mkOption {
default = { } ;
2021-10-06 13:57:05 +00:00
example = literalExpression ''
2020-04-24 23:36:52 +00:00
{
" / " . device = " / d e v / h d a 1 " ;
" / d a t a " = {
device = " / d e v / h d a 2 " ;
fsType = " e x t 3 " ;
options = [ " d a t a = j o u r n a l " ] ;
} ;
" / b i g d i s k " . label = " b i g d i s k " ;
}
'' ;
2020-09-25 04:45:31 +00:00
type = types . attrsOf ( types . submodule [ coreFileSystemOpts fileSystemOpts ] ) ;
2024-04-21 15:54:59 +00:00
description = ''
2020-04-24 23:36:52 +00:00
The file systems to be mounted . It must include an entry for
2022-08-21 13:32:41 +00:00
the root directory ( ` mountPoint = " / " ` ) . Each
2020-04-24 23:36:52 +00:00
entry in the list is an attribute set with the following fields :
2022-08-21 13:32:41 +00:00
` mountPoint ` , ` device ` ,
` fsType ` ( a file system type recognised by
{ command } ` mount ` ; defaults to
` " a u t o " ` ) , and ` options `
( the mount options passed to { command } ` mount ` using the
{ option } ` - o ` flag ; defaults to ` [ " d e f a u l t s " ] ` ) .
Instead of specifying ` device ` , you can also
specify a volume label ( ` label ` ) for file
systems that support it , such as ext2/ext3 ( see { command } ` mke2fs - L ` ) .
2020-04-24 23:36:52 +00:00
'' ;
} ;
system . fsPackages = mkOption {
internal = true ;
default = [ ] ;
2024-04-21 15:54:59 +00:00
description = " P a c k a g e s s u p p l y i n g f i l e s y s t e m m o u n t e r s a n d c h e c k e r s . " ;
2020-04-24 23:36:52 +00:00
} ;
boot . supportedFilesystems = mkOption {
2024-02-29 20:09:43 +00:00
default = { } ;
example = lib . literalExpression ''
{
btrfs = true ;
zfs = lib . mkForce false ;
}
'' ;
type = types . coercedTo
( types . listOf types . str )
( enabled : lib . listToAttrs ( map ( fs : lib . nameValuePair fs true ) enabled ) )
( types . attrsOf types . bool ) ;
2024-04-21 15:54:59 +00:00
description = ''
2024-02-29 20:09:43 +00:00
Names of supported filesystem types , or an attribute set of file system types
and their state . The set form may be used together with ` lib . mkForce ` to
explicitly disable support for specific filesystems , e . g . to disable ZFS
with an unsupported kernel .
'' ;
2020-04-24 23:36:52 +00:00
} ;
boot . specialFileSystems = mkOption {
default = { } ;
2020-09-25 04:45:31 +00:00
type = types . attrsOf ( types . submodule coreFileSystemOpts ) ;
2020-04-24 23:36:52 +00:00
internal = true ;
2024-04-21 15:54:59 +00:00
description = ''
2020-04-24 23:36:52 +00:00
Special filesystems that are mounted very early during boot .
'' ;
} ;
2022-03-30 09:31:56 +00:00
boot . devSize = mkOption {
default = " 5 % " ;
example = " 3 2 m " ;
type = types . str ;
2024-04-21 15:54:59 +00:00
description = ''
2022-03-30 09:31:56 +00:00
Size limit for the /dev tmpfs . Look at mount ( 8 ) , tmpfs size option ,
for the accepted syntax .
'' ;
} ;
boot . devShmSize = mkOption {
default = " 5 0 % " ;
example = " 2 5 6 m " ;
type = types . str ;
2024-04-21 15:54:59 +00:00
description = ''
2022-03-30 09:31:56 +00:00
Size limit for the /dev/shm tmpfs . Look at mount ( 8 ) , tmpfs size option ,
for the accepted syntax .
'' ;
} ;
boot . runSize = mkOption {
default = " 2 5 % " ;
example = " 2 5 6 m " ;
type = types . str ;
2024-04-21 15:54:59 +00:00
description = ''
2022-03-30 09:31:56 +00:00
Size limit for the /run tmpfs . Look at mount ( 8 ) , tmpfs size option ,
for the accepted syntax .
'' ;
} ;
2020-04-24 23:36:52 +00:00
} ;
###### implementation
config = {
assertions = let
ls = sep : concatMapStringsSep sep ( x : x . mountPoint ) ;
2023-07-15 17:15:38 +00:00
resizableFSes = [
" e x t 3 "
" e x t 4 "
" b t r f s "
" x f s "
] ;
notAutoResizable = fs : fs . autoResize && ! ( builtins . elem fs . fsType resizableFSes ) ;
2020-04-24 23:36:52 +00:00
in [
{ assertion = ! ( fileSystems' ? cycle ) ;
message = " T h e ‘ f i l e S y s t e m s ’ o p t i o n c a n ' t b e t o p o l o g i c a l l y s o r t e d : m o u n t p o i n t d e p e n d e n c y p a t h ${ ls " - > " fileSystems' . cycle } l o o p s t o ${ ls " , " fileSystems' . loops } " ;
}
{ assertion = ! ( any notAutoResizable fileSystems ) ;
message = let
fs = head ( filter notAutoResizable fileSystems ) ;
2023-07-15 17:15:38 +00:00
in ''
Mountpoint ' $ { fs . mountPoint } ' : ' autoResize = true' is not supported for ' fsType = " ${ fs . fsType } " '
$ { optionalString ( fs . fsType == " a u t o " ) " f s T y p e h a s t o b e e x p l i c i t l y s e t a n d " }
only the following support it : $ { lib . concatStringsSep " , " resizableFSes } .
'' ;
}
{
assertion = ! ( any ( fs : fs . formatOptions != null ) fileSystems ) ;
message = let
fs = head ( filter ( fs : fs . formatOptions != null ) fileSystems ) ;
in ''
' fileSystems . <name> . formatOptions' has been removed , since
systemd-makefs does not support any way to provide formatting
options .
'' ;
2020-04-24 23:36:52 +00:00
}
] ;
# Export for use in other modules
system . build . fileSystems = fileSystems ;
system . build . earlyMountScript = makeSpecialMounts ( toposort fsBefore ( attrValues config . boot . specialFileSystems ) ) . result ;
boot . supportedFilesystems = map ( fs : fs . fsType ) fileSystems ;
# Add the mount helpers to the system path so that `mount' can find them.
system . fsPackages = [ pkgs . dosfstools ] ;
environment . systemPackages = with pkgs ; [ fuse3 fuse ] ++ config . system . fsPackages ;
environment . etc . fstab . text =
let
2021-06-28 23:13:55 +00:00
swapOptions = sw : concatStringsSep " , " (
2021-07-24 12:14:16 +00:00
sw . options
2021-06-28 23:13:55 +00:00
++ optional ( sw . priority != null ) " p r i = ${ toString sw . priority } "
++ optional ( sw . discardPolicy != null ) " d i s c a r d ${ optionalString ( sw . discardPolicy != " b o t h " ) " = ${ toString sw . discardPolicy } " } "
) ;
2020-04-24 23:36:52 +00:00
in ''
# This is a generated file. Do not edit!
#
# To make changes, edit the fileSystems and swapDevices NixOS options
# in your /etc/nixos/configuration.nix file.
2021-08-27 14:25:00 +00:00
#
# <file system> <mount point> <type> <options> <dump> <pass>
2020-04-24 23:36:52 +00:00
# Filesystems.
2022-08-21 13:32:41 +00:00
$ { makeFstabEntries fileSystems { } }
2020-04-24 23:36:52 +00:00
# Swap devices.
$ { flip concatMapStrings config . swapDevices ( sw :
2021-03-20 04:20:00 +00:00
" ${ sw . realDevice } n o n e s w a p ${ swapOptions sw } \n "
2020-04-24 23:36:52 +00:00
) }
'' ;
2023-03-15 16:39:30 +00:00
boot . initrd . systemd . storePaths = [ initrdFstab ] ;
boot . initrd . systemd . managerEnvironment . SYSTEMD_SYSROOT_FSTAB = initrdFstab ;
boot . initrd . systemd . services . initrd-parse-etc . environment . SYSTEMD_SYSROOT_FSTAB = initrdFstab ;
2022-08-21 13:32:41 +00:00
2020-04-24 23:36:52 +00:00
# Provide a target that pulls in all filesystems.
systemd . targets . fs =
{ description = " A l l F i l e S y s t e m s " ;
wants = [ " l o c a l - f s . t a r g e t " " r e m o t e - f s . t a r g e t " ] ;
} ;
2023-07-15 17:15:38 +00:00
systemd . services = {
2021-05-20 23:08:51 +00:00
# Mount /sys/fs/pstore for evacuating panic logs and crashdumps from persistent storage onto the disk using systemd-pstore.
# This cannot be done with the other special filesystems because the pstore module (which creates the mount point) is not loaded then.
" m o u n t - p s t o r e " = {
serviceConfig = {
Type = " o n e s h o t " ;
2021-08-05 21:33:18 +00:00
# skip on kernels without the pstore module
ExecCondition = " ${ pkgs . kmod } / b i n / m o d p r o b e - b p s t o r e " ;
ExecStart = pkgs . writeShellScript " m o u n t - p s t o r e . s h " ''
2021-05-20 23:08:51 +00:00
set - eu
2021-08-05 21:33:18 +00:00
# if the pstore module is builtin it will have mounted the persistent store automatically. it may also be already mounted for other reasons.
$ { pkgs . util-linux } /bin/mountpoint - q /sys/fs/pstore || $ { pkgs . util-linux } /bin/mount - t pstore - o nosuid , noexec , nodev pstore /sys/fs/pstore
# wait up to 1.5 seconds for the backend to be registered and the files to appear. a systemd path unit cannot detect this happening; and succeeding after a restart would not start dependent units.
TRIES = 15
while [ " $ ( c a t / s y s / m o d u l e / p s t o r e / p a r a m e t e r s / b a c k e n d ) " = " ( n u l l ) " ] ; do
if ( ( $ TRIES ) ) ; then
sleep 0 .1
TRIES = $ ( ( TRIES-1 ) )
else
echo " P e r s i s t e n t S t o r a g e b a c k e n d w a s n o t r e g i s t e r e d i n t i m e . " > & 2
break
fi
2021-05-20 23:08:51 +00:00
done
'' ;
RemainAfterExit = true ;
} ;
unitConfig = {
ConditionVirtualization = " ! c o n t a i n e r " ;
DefaultDependencies = false ; # needed to prevent a cycle
} ;
2024-01-02 11:29:13 +00:00
before = [ " s y s t e m d - p s t o r e . s e r v i c e " " s h u t d o w n . t a r g e t " ] ;
conflicts = [ " s h u t d o w n . t a r g e t " ] ;
2021-05-20 23:08:51 +00:00
wantedBy = [ " s y s t e m d - p s t o r e . s e r v i c e " ] ;
} ;
} ;
2020-04-24 23:36:52 +00:00
systemd . tmpfiles . rules = [
" d / r u n / k e y s 0 7 5 0 r o o t ${ toString config . ids . gids . keys } "
" z / r u n / k e y s 0 7 5 0 r o o t ${ toString config . ids . gids . keys } "
] ;
# Sync mount options with systemd's src/core/mount-setup.c: mount_table.
boot . specialFileSystems = {
" / p r o c " = { fsType = " p r o c " ; options = [ " n o s u i d " " n o e x e c " " n o d e v " ] ; } ;
" / r u n " = { fsType = " t m p f s " ; options = [ " n o s u i d " " n o d e v " " s t r i c t a t i m e " " m o d e = 7 5 5 " " s i z e = ${ config . boot . runSize } " ] ; } ;
" / d e v " = { fsType = " d e v t m p f s " ; options = [ " n o s u i d " " s t r i c t a t i m e " " m o d e = 7 5 5 " " s i z e = ${ config . boot . devSize } " ] ; } ;
" / d e v / s h m " = { fsType = " t m p f s " ; options = [ " n o s u i d " " n o d e v " " s t r i c t a t i m e " " m o d e = 1 7 7 7 " " s i z e = ${ config . boot . devShmSize } " ] ; } ;
" / d e v / p t s " = { fsType = " d e v p t s " ; options = [ " n o s u i d " " n o e x e c " " m o d e = 6 2 0 " " p t m x m o d e = 0 6 6 6 " " g i d = ${ toString config . ids . gids . tty } " ] ; } ;
# To hold secrets that shouldn't be written to disk
" / r u n / k e y s " = { fsType = " r a m f s " ; options = [ " n o s u i d " " n o d e v " " m o d e = 7 5 0 " ] ; } ;
} // optionalAttrs ( ! config . boot . isContainer ) {
# systemd-nspawn populates /sys by itself, and remounting it causes all
# kinds of weird issues (most noticeably, waiting for host disk device
# nodes).
" / s y s " = { fsType = " s y s f s " ; options = [ " n o s u i d " " n o e x e c " " n o d e v " ] ; } ;
} ;
} ;
}