at 16.09-beta 10 kB view raw
1{ config, lib, pkgs, utils, ... }: 2 3with lib; 4with utils; 5 6let 7 8 fileSystems' = toposort fsBefore (attrValues config.fileSystems); 9 10 fileSystems = if fileSystems' ? "result" 11 then # use topologically sorted fileSystems everywhere 12 fileSystems'.result 13 else # the assertion below will catch this, 14 # but we fall back to the original order 15 # anyway so that other modules could check 16 # their assertions too 17 (attrValues config.fileSystems); 18 19 prioOption = prio: optionalString (prio != null) " pri=${toString prio}"; 20 21 specialFSTypes = [ "proc" "sysfs" "tmpfs" "devtmpfs" "devpts" ]; 22 23 coreFileSystemOpts = { name, config, ... }: { 24 25 options = { 26 27 mountPoint = mkOption { 28 example = "/mnt/usb"; 29 type = types.str; 30 description = "Location of the mounted the file system."; 31 }; 32 33 device = mkOption { 34 default = null; 35 example = "/dev/sda"; 36 type = types.nullOr types.str; 37 description = "Location of the device."; 38 }; 39 40 fsType = mkOption { 41 default = "auto"; 42 example = "ext3"; 43 type = types.str; 44 description = "Type of the file system."; 45 }; 46 47 options = mkOption { 48 default = [ "defaults" ]; 49 example = [ "data=journal" ]; 50 description = "Options used to mount the file system."; 51 type = types.listOf types.str; 52 }; 53 54 }; 55 56 config = { 57 mountPoint = mkDefault name; 58 device = mkIf (elem config.fsType specialFSTypes) (mkDefault config.fsType); 59 }; 60 61 }; 62 63 fileSystemOpts = { config, ... }: { 64 65 options = { 66 67 label = mkOption { 68 default = null; 69 example = "root-partition"; 70 type = types.nullOr types.str; 71 description = "Label of the device (if any)."; 72 }; 73 74 autoFormat = mkOption { 75 default = false; 76 type = types.bool; 77 description = '' 78 If the device does not currently contain a filesystem (as 79 determined by <command>blkid</command>, then automatically 80 format it with the filesystem type specified in 81 <option>fsType</option>. Use with caution. 82 ''; 83 }; 84 85 formatOptions = mkOption { 86 default = ""; 87 type = types.str; 88 description = '' 89 If <option>autoFormat</option> option is set specifies 90 extra options passed to mkfs. 91 ''; 92 }; 93 94 autoResize = mkOption { 95 default = false; 96 type = types.bool; 97 description = '' 98 If set, the filesystem is grown to its maximum size before 99 being mounted. (This is typically the size of the containing 100 partition.) This is currently only supported for ext2/3/4 101 filesystems that are mounted during early boot. 102 ''; 103 }; 104 105 noCheck = mkOption { 106 default = false; 107 type = types.bool; 108 description = "Disable running fsck on this filesystem."; 109 }; 110 111 }; 112 113 config = { 114 options = mkIf config.autoResize [ "x-nixos.autoresize" ]; 115 116 # -F needed to allow bare block device without partitions 117 formatOptions = mkIf ((builtins.substring 0 3 config.fsType) == "ext") (mkDefault "-F"); 118 }; 119 120 }; 121 122 # Makes sequence of `specialMount device mountPoint options fsType` commands. 123 # `systemMount` should be defined in the sourcing script. 124 makeSpecialMounts = mounts: 125 pkgs.writeText "mounts.sh" (concatMapStringsSep "\n" (mount: '' 126 specialMount "${mount.device}" "${mount.mountPoint}" "${concatStringsSep "," mount.options}" "${mount.fsType}" 127 '') mounts); 128 129in 130 131{ 132 133 ###### interface 134 135 options = { 136 137 fileSystems = mkOption { 138 default = {}; 139 example = literalExample '' 140 { 141 "/".device = "/dev/hda1"; 142 "/data" = { 143 device = "/dev/hda2"; 144 fsType = "ext3"; 145 options = [ "data=journal" ]; 146 }; 147 "/bigdisk".label = "bigdisk"; 148 } 149 ''; 150 type = types.loaOf (types.submodule [coreFileSystemOpts fileSystemOpts]); 151 description = '' 152 The file systems to be mounted. It must include an entry for 153 the root directory (<literal>mountPoint = "/"</literal>). Each 154 entry in the list is an attribute set with the following fields: 155 <literal>mountPoint</literal>, <literal>device</literal>, 156 <literal>fsType</literal> (a file system type recognised by 157 <command>mount</command>; defaults to 158 <literal>"auto"</literal>), and <literal>options</literal> 159 (the mount options passed to <command>mount</command> using the 160 <option>-o</option> flag; defaults to <literal>[ "defaults" ]</literal>). 161 162 Instead of specifying <literal>device</literal>, you can also 163 specify a volume label (<literal>label</literal>) for file 164 systems that support it, such as ext2/ext3 (see <command>mke2fs 165 -L</command>). 166 ''; 167 }; 168 169 system.fsPackages = mkOption { 170 internal = true; 171 default = [ ]; 172 description = "Packages supplying file system mounters and checkers."; 173 }; 174 175 boot.supportedFilesystems = mkOption { 176 default = [ ]; 177 example = [ "btrfs" ]; 178 type = types.listOf types.str; 179 description = "Names of supported filesystem types."; 180 }; 181 182 boot.specialFileSystems = mkOption { 183 default = {}; 184 type = types.loaOf (types.submodule coreFileSystemOpts); 185 internal = true; 186 description = '' 187 Special filesystems that are mounted very early during boot. 188 ''; 189 }; 190 191 }; 192 193 194 ###### implementation 195 196 config = { 197 198 assertions = let 199 ls = sep: concatMapStringsSep sep (x: x.mountPoint); 200 in [ 201 { assertion = ! (fileSystems' ? "cycle"); 202 message = "The fileSystems option can't be topologically sorted: mountpoint dependency path ${ls " -> " fileSystems'.cycle} loops to ${ls ", " fileSystems'.loops}"; 203 } 204 ]; 205 206 # Export for use in other modules 207 system.build.fileSystems = fileSystems; 208 system.build.earlyMountScript = makeSpecialMounts (toposort fsBefore (attrValues config.boot.specialFileSystems)).result; 209 210 boot.supportedFilesystems = map (fs: fs.fsType) fileSystems; 211 212 # Add the mount helpers to the system path so that `mount' can find them. 213 system.fsPackages = [ pkgs.dosfstools ]; 214 215 environment.systemPackages = [ pkgs.fuse ] ++ config.system.fsPackages; 216 217 environment.etc.fstab.text = 218 let 219 fsToSkipCheck = [ "none" "btrfs" "zfs" "tmpfs" "nfs" "vboxsf" ]; 220 skipCheck = fs: fs.noCheck || fs.device == "none" || builtins.elem fs.fsType fsToSkipCheck; 221 in '' 222 # This is a generated file. Do not edit! 223 # 224 # To make changes, edit the fileSystems and swapDevices NixOS options 225 # in your /etc/nixos/configuration.nix file. 226 227 # Filesystems. 228 ${concatMapStrings (fs: 229 (if fs.device != null then fs.device 230 else if fs.label != null then "/dev/disk/by-label/${fs.label}" 231 else throw "No device specified for mount point ${fs.mountPoint}.") 232 + " " + fs.mountPoint 233 + " " + fs.fsType 234 + " " + builtins.concatStringsSep "," fs.options 235 + " 0" 236 + " " + (if skipCheck fs then "0" else 237 if fs.mountPoint == "/" then "1" else "2") 238 + "\n" 239 ) fileSystems} 240 241 # Swap devices. 242 ${flip concatMapStrings config.swapDevices (sw: 243 "${sw.realDevice} none swap${prioOption sw.priority}\n" 244 )} 245 ''; 246 247 # Provide a target that pulls in all filesystems. 248 systemd.targets.fs = 249 { description = "All File Systems"; 250 wants = [ "local-fs.target" "remote-fs.target" ]; 251 }; 252 253 # Emit systemd services to format requested filesystems. 254 systemd.services = 255 let 256 257 formatDevice = fs: 258 let 259 mountPoint' = "${escapeSystemdPath fs.mountPoint}.mount"; 260 device' = escapeSystemdPath fs.device; 261 device'' = "${device}.device"; 262 in nameValuePair "mkfs-${device'}" 263 { description = "Initialisation of Filesystem ${fs.device}"; 264 wantedBy = [ mountPoint' ]; 265 before = [ mountPoint' "systemd-fsck@${device'}.service" ]; 266 requires = [ device'' ]; 267 after = [ device'' ]; 268 path = [ pkgs.utillinux ] ++ config.system.fsPackages; 269 script = 270 '' 271 if ! [ -e "${fs.device}" ]; then exit 1; fi 272 # FIXME: this is scary. The test could be more robust. 273 type=$(blkid -p -s TYPE -o value "${fs.device}" || true) 274 if [ -z "$type" ]; then 275 echo "creating ${fs.fsType} filesystem on ${fs.device}..." 276 mkfs.${fs.fsType} ${fs.formatOptions} "${fs.device}" 277 fi 278 ''; 279 unitConfig.RequiresMountsFor = [ "${dirOf fs.device}" ]; 280 unitConfig.DefaultDependencies = false; # needed to prevent a cycle 281 serviceConfig.Type = "oneshot"; 282 }; 283 284 in listToAttrs (map formatDevice (filter (fs: fs.autoFormat) fileSystems)); 285 286 # Sync mount options with systemd's src/core/mount-setup.c: mount_table. 287 boot.specialFileSystems = { 288 "/proc" = { fsType = "proc"; options = [ "nosuid" "noexec" "nodev" ]; }; 289 "/sys" = { fsType = "sysfs"; options = [ "nosuid" "noexec" "nodev" ]; }; 290 "/run" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=755" "size=${config.boot.runSize}" ]; }; 291 "/dev" = { fsType = "devtmpfs"; options = [ "nosuid" "strictatime" "mode=755" "size=${config.boot.devSize}" ]; }; 292 "/dev/shm" = { fsType = "tmpfs"; options = [ "nosuid" "nodev" "strictatime" "mode=1777" "size=${config.boot.devShmSize}" ]; }; 293 "/dev/pts" = { fsType = "devpts"; options = [ "nosuid" "noexec" "mode=620" "gid=${toString config.ids.gids.tty}" ]; }; 294 }; 295 296 }; 297 298}