1 { config, lib, pkgs, utils, ... }@moduleArgs:
8 addCheckDesc = desc: elemType: check: types.addCheck elemType check
9 // { description = "${elemType.description} (with check: ${desc})"; };
11 isNonEmpty = s: (builtins.match "[ \t\n]*" s) == null;
12 nonEmptyStr = addCheckDesc "non-empty" types.str isNonEmpty;
14 fileSystems' = toposort fsBefore (attrValues config.fileSystems);
16 fileSystems = if fileSystems' ? result
17 then # use topologically sorted fileSystems everywhere
19 else # the assertion below will catch this,
20 # but we fall back to the original order
21 # anyway so that other modules could check
22 # their assertions too
23 (attrValues config.fileSystems);
25 specialFSTypes = [ "proc" "sysfs" "tmpfs" "ramfs" "devtmpfs" "devpts" ];
27 nonEmptyWithoutTrailingSlash = addCheckDesc "non-empty without trailing slash" types.str
28 (s: isNonEmpty s && (builtins.match ".+/" s) == null);
30 coreFileSystemOpts = { name, config, ... }: {
33 mountPoint = mkOption {
35 type = nonEmptyWithoutTrailingSlash;
36 description = "Location of the mounted file system.";
39 stratis.poolUuid = lib.mkOption {
40 type = types.uniq (types.nullOr types.str);
42 UUID of the stratis pool that the fs is located in
44 example = "04c68063-90a5-4235-b9dd-6180098a20d9";
51 type = types.nullOr nonEmptyStr;
52 description = "Location of the device.";
59 description = "Type of the file system.";
63 default = [ "defaults" ];
64 example = [ "data=journal" ];
65 description = "Options used to mount the file system.";
66 type = types.nonEmptyListOf nonEmptyStr;
71 example = [ "/persist" ];
72 type = types.listOf nonEmptyWithoutTrailingSlash;
74 List of paths that should be mounted before this one. This filesystem's
75 {option}`device` and {option}`mountPoint` are always
76 checked and do not need to be included explicitly. If a path is added
77 to this list, any other filesystem whose mount point is a parent of
78 the path will be mounted before this filesystem. The paths do not need
79 to actually be the {option}`mountPoint` of some other filesystem.
86 mountPoint = mkDefault name;
87 device = mkIf (elem config.fsType specialFSTypes) (mkDefault config.fsType);
92 fileSystemOpts = { config, ... }: {
98 example = "root-partition";
99 type = types.nullOr nonEmptyStr;
100 description = "Label of the device (if any).";
103 autoFormat = mkOption {
107 If the device does not currently contain a filesystem (as
108 determined by {command}`blkid`), then automatically
109 format it with the filesystem type specified in
110 {option}`fsType`. Use with caution.
114 formatOptions = mkOption {
116 type = types.unspecified;
120 autoResize = mkOption {
124 If set, the filesystem is grown to its maximum size before
125 being mounted. (This is typically the size of the containing
126 partition.) This is currently only supported for ext2/3/4
127 filesystems that are mounted during early boot.
134 description = "Disable running fsck on this filesystem.";
140 inInitrd = utils.fsNeededForBoot config;
142 (mkIf config.autoResize [ "x-systemd.growfs" ])
143 (mkIf config.autoFormat [ "x-systemd.makefs" ])
144 (mkIf (utils.fsNeededForBoot config) [ "x-initrd.mount" ])
146 # With scripted stage 1, depends is implemented by sorting 'config.system.build.fileSystems'
147 (lib.length config.depends > 0 && (inInitrd -> moduleArgs.config.boot.initrd.systemd.enable))
150 x: "x-systemd.requires-mounts-for=${optionalString inInitrd "/sysroot"}${x}"
158 # Makes sequence of `specialMount device mountPoint options fsType` commands.
159 # `systemMount` should be defined in the sourcing script.
160 makeSpecialMounts = mounts:
161 pkgs.writeText "mounts.sh" (concatMapStringsSep "\n" (mount: ''
162 specialMount "${mount.device}" "${mount.mountPoint}" "${concatStringsSep "," mount.options}" "${mount.fsType}"
189 ] ++ lib.optionals (!config.boot.initrd.checkJournalingFS) [
197 isBindMount = fs: builtins.elem "bind" fs.options;
198 skipCheck = fs: fs.noCheck || fs.device == "none" || builtins.elem fs.fsType fsToSkipCheck || isBindMount fs;
199 # https://wiki.archlinux.org/index.php/fstab#Filepath_spaces
200 escape = string: builtins.replaceStrings [ " " "\t" ] [ "\\040" "\\011" ] string;
201 in fstabFileSystems: { }: concatMapStrings (fs:
202 (if fs.device != null then escape fs.device
203 else if fs.label != null then "/dev/disk/by-label/${escape fs.label}"
204 else throw "No device specified for mount point ‘${fs.mountPoint}’.")
205 + " " + escape fs.mountPoint
207 + " " + escape (builtins.concatStringsSep "," fs.options)
208 + " 0 " + (if skipCheck fs then "0" else if fs.mountPoint == "/" then "1" else "2")
212 initrdFstab = pkgs.writeText "initrd-fstab" (makeFstabEntries (filter utils.fsNeededForBoot fileSystems) { });
222 fileSystems = mkOption {
224 example = literalExpression ''
226 "/".device = "/dev/hda1";
228 device = "/dev/hda2";
230 options = [ "data=journal" ];
232 "/bigdisk".label = "bigdisk";
235 type = types.attrsOf (types.submodule [coreFileSystemOpts fileSystemOpts]);
237 The file systems to be mounted. It must include an entry for
238 the root directory (`mountPoint = "/"`). Each
239 entry in the list is an attribute set with the following fields:
240 `mountPoint`, `device`,
241 `fsType` (a file system type recognised by
242 {command}`mount`; defaults to
243 `"auto"`), and `options`
244 (the mount options passed to {command}`mount` using the
245 {option}`-o` flag; defaults to `[ "defaults" ]`).
247 Instead of specifying `device`, you can also
248 specify a volume label (`label`) for file
249 systems that support it, such as ext2/ext3 (see {command}`mke2fs -L`).
253 system.fsPackages = mkOption {
256 description = "Packages supplying file system mounters and checkers.";
259 boot.supportedFilesystems = mkOption {
261 example = lib.literalExpression ''
264 zfs = lib.mkForce false;
267 type = types.coercedTo
268 (types.listOf types.str)
269 (enabled: lib.listToAttrs (map (fs: lib.nameValuePair fs true) enabled))
270 (types.attrsOf types.bool);
272 Names of supported filesystem types, or an attribute set of file system types
273 and their state. The set form may be used together with `lib.mkForce` to
274 explicitly disable support for specific filesystems, e.g. to disable ZFS
275 with an unsupported kernel.
279 boot.specialFileSystems = mkOption {
281 type = types.attrsOf (types.submodule coreFileSystemOpts);
284 Special filesystems that are mounted very early during boot.
288 boot.devSize = mkOption {
293 Size limit for the /dev tmpfs. Look at mount(8), tmpfs size option,
294 for the accepted syntax.
298 boot.devShmSize = mkOption {
303 Size limit for the /dev/shm tmpfs. Look at mount(8), tmpfs size option,
304 for the accepted syntax.
308 boot.runSize = mkOption {
313 Size limit for the /run tmpfs. Look at mount(8), tmpfs size option,
314 for the accepted syntax.
320 ###### implementation
325 ls = sep: concatMapStringsSep sep (x: x.mountPoint);
332 notAutoResizable = fs: fs.autoResize && !(builtins.elem fs.fsType resizableFSes);
334 { assertion = ! (fileSystems' ? cycle);
335 message = "The ‘fileSystems’ option can't be topologically sorted: mountpoint dependency path ${ls " -> " fileSystems'.cycle} loops to ${ls ", " fileSystems'.loops}";
337 { assertion = ! (any notAutoResizable fileSystems);
339 fs = head (filter notAutoResizable fileSystems);
341 Mountpoint '${fs.mountPoint}': 'autoResize = true' is not supported for 'fsType = "${fs.fsType}"'
342 ${optionalString (fs.fsType == "auto") "fsType has to be explicitly set and"}
343 only the following support it: ${lib.concatStringsSep ", " resizableFSes}.
347 assertion = ! (any (fs: fs.formatOptions != null) fileSystems);
349 fs = head (filter (fs: fs.formatOptions != null) fileSystems);
351 'fileSystems.<name>.formatOptions' has been removed, since
352 systemd-makefs does not support any way to provide formatting
358 # Export for use in other modules
359 system.build.fileSystems = fileSystems;
360 system.build.earlyMountScript = makeSpecialMounts (toposort fsBefore (attrValues config.boot.specialFileSystems)).result;
362 boot.supportedFilesystems = map (fs: fs.fsType) fileSystems;
364 # Add the mount helpers to the system path so that `mount' can find them.
365 system.fsPackages = [ pkgs.dosfstools ];
367 environment.systemPackages = with pkgs; [ fuse3 fuse ] ++ config.system.fsPackages;
369 environment.etc.fstab.text =
371 swapOptions = sw: concatStringsSep "," (
373 ++ optional (sw.priority != null) "pri=${toString sw.priority}"
374 ++ optional (sw.discardPolicy != null) "discard${optionalString (sw.discardPolicy != "both") "=${toString sw.discardPolicy}"}"
377 # This is a generated file. Do not edit!
379 # To make changes, edit the fileSystems and swapDevices NixOS options
380 # in your /etc/nixos/configuration.nix file.
382 # <file system> <mount point> <type> <options> <dump> <pass>
385 ${makeFstabEntries fileSystems {}}
388 ${flip concatMapStrings config.swapDevices (sw:
389 "${sw.realDevice} none swap ${swapOptions sw}\n"
393 boot.initrd.systemd.storePaths = [initrdFstab];
394 boot.initrd.systemd.managerEnvironment.SYSTEMD_SYSROOT_FSTAB = initrdFstab;
395 boot.initrd.systemd.services.initrd-parse-etc.environment.SYSTEMD_SYSROOT_FSTAB = initrdFstab;
397 # Provide a target that pulls in all filesystems.
399 { description = "All File Systems";
400 wants = [ "local-fs.target" "remote-fs.target" ];
404 # Mount /sys/fs/pstore for evacuating panic logs and crashdumps from persistent storage onto the disk using systemd-pstore.
405 # This cannot be done with the other special filesystems because the pstore module (which creates the mount point) is not loaded then.
409 # skip on kernels without the pstore module
410 ExecCondition = "${pkgs.kmod}/bin/modprobe -b pstore";
411 ExecStart = pkgs.writeShellScript "mount-pstore.sh" ''
413 # if the pstore module is builtin it will have mounted the persistent store automatically. it may also be already mounted for other reasons.
414 ${pkgs.util-linux}/bin/mountpoint -q /sys/fs/pstore || ${pkgs.util-linux}/bin/mount -t pstore -o nosuid,noexec,nodev pstore /sys/fs/pstore
415 # 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.
417 while [ "$(cat /sys/module/pstore/parameters/backend)" = "(null)" ]; do
418 if (( $TRIES )); then
422 echo "Persistent Storage backend was not registered in time." >&2
427 RemainAfterExit = true;
430 ConditionVirtualization = "!container";
431 DefaultDependencies = false; # needed to prevent a cycle
433 before = [ "systemd-pstore.service" "shutdown.target" ];
434 conflicts = [ "shutdown.target" ];
435 wantedBy = [ "systemd-pstore.service" ];
439 systemd.tmpfiles.rules = [
440 "d /run/keys 0750 root ${toString config.ids.gids.keys}"
441 "z /run/keys 0750 root ${toString config.ids.gids.keys}"
444 # Sync mount options with systemd's src/core/mount-setup.c: mount_table.
445 boot.specialFileSystems = {
446 "/proc" = { fsType = "proc"; options = [ "nosuid" "noexec" "nodev" ]; };
447 "/run" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=755" "size=${config.boot.runSize}" ]; };
448 "/dev" = { fsType = "devtmpfs"; options = [ "nosuid" "strictatime" "mode=755" "size=${config.boot.devSize}" ]; };
449 "/dev/shm" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=1777" "size=${config.boot.devShmSize}" ]; };
450 "/dev/pts" = { fsType = "devpts"; options = [ "nosuid" "noexec" "mode=620" "ptmxmode=0666" "gid=${toString config.ids.gids.tty}" ]; };
452 # To hold secrets that shouldn't be written to disk
453 "/run/keys" = { fsType = "ramfs"; options = [ "nosuid" "nodev" "mode=750" ]; };
454 } // optionalAttrs (!config.boot.isContainer) {
455 # systemd-nspawn populates /sys by itself, and remounting it causes all
456 # kinds of weird issues (most noticeably, waiting for host disk device
458 "/sys" = { fsType = "sysfs"; options = [ "nosuid" "noexec" "nodev" ]; };