at v206 7.0 kB view raw
1{ config, lib, pkgs, utils, ... }: 2 3with lib; 4with utils; 5 6let 7 8 fileSystems = attrValues config.fileSystems; 9 10 prioOption = prio: optionalString (prio != null) " pri=${toString prio}"; 11 12 fileSystemOpts = { name, config, ... }: { 13 14 options = { 15 16 mountPoint = mkOption { 17 example = "/mnt/usb"; 18 type = types.str; 19 description = "Location of the mounted the file system."; 20 }; 21 22 device = mkOption { 23 default = null; 24 example = "/dev/sda"; 25 type = types.nullOr types.str; 26 description = "Location of the device."; 27 }; 28 29 label = mkOption { 30 default = null; 31 example = "root-partition"; 32 type = types.nullOr types.str; 33 description = "Label of the device (if any)."; 34 }; 35 36 fsType = mkOption { 37 default = "auto"; 38 example = "ext3"; 39 type = types.str; 40 description = "Type of the file system."; 41 }; 42 43 options = mkOption { 44 default = "defaults"; 45 example = "data=journal"; 46 type = types.commas; # FIXME: should be a list 47 description = "Options used to mount the file system."; 48 }; 49 50 autoFormat = mkOption { 51 default = false; 52 type = types.bool; 53 description = '' 54 If the device does not currently contain a filesystem (as 55 determined by <command>blkid</command>, then automatically 56 format it with the filesystem type specified in 57 <option>fsType</option>. Use with caution. 58 ''; 59 }; 60 61 autoResize = mkOption { 62 default = false; 63 type = types.bool; 64 description = '' 65 If set, the filesystem is grown to its maximum size before 66 being mounted. (This is typically the size of the containing 67 partition.) This is currently only supported for ext2/3/4 68 filesystems that are mounted during early boot. 69 ''; 70 }; 71 72 noCheck = mkOption { 73 default = false; 74 type = types.bool; 75 description = "Disable running fsck on this filesystem."; 76 }; 77 78 }; 79 80 config = { 81 mountPoint = mkDefault name; 82 device = mkIf (config.fsType == "tmpfs") (mkDefault config.fsType); 83 options = mkIf config.autoResize "x-nixos.autoresize"; 84 }; 85 86 }; 87 88in 89 90{ 91 92 ###### interface 93 94 options = { 95 96 fileSystems = mkOption { 97 default = {}; 98 example = { 99 "/".device = "/dev/hda1"; 100 "/data" = { 101 device = "/dev/hda2"; 102 fsType = "ext3"; 103 options = "data=journal"; 104 }; 105 "/bigdisk".label = "bigdisk"; 106 }; 107 type = types.loaOf types.optionSet; 108 options = [ fileSystemOpts ]; 109 description = '' 110 The file systems to be mounted. It must include an entry for 111 the root directory (<literal>mountPoint = "/"</literal>). Each 112 entry in the list is an attribute set with the following fields: 113 <literal>mountPoint</literal>, <literal>device</literal>, 114 <literal>fsType</literal> (a file system type recognised by 115 <command>mount</command>; defaults to 116 <literal>"auto"</literal>), and <literal>options</literal> 117 (the mount options passed to <command>mount</command> using the 118 <option>-o</option> flag; defaults to <literal>"defaults"</literal>). 119 120 Instead of specifying <literal>device</literal>, you can also 121 specify a volume label (<literal>label</literal>) for file 122 systems that support it, such as ext2/ext3 (see <command>mke2fs 123 -L</command>). 124 ''; 125 }; 126 127 system.fsPackages = mkOption { 128 internal = true; 129 default = [ ]; 130 description = "Packages supplying file system mounters and checkers."; 131 }; 132 133 boot.supportedFilesystems = mkOption { 134 default = [ ]; 135 example = [ "btrfs" ]; 136 type = types.listOf types.str; 137 description = "Names of supported filesystem types."; 138 }; 139 140 }; 141 142 143 ###### implementation 144 145 config = { 146 147 boot.supportedFilesystems = map (fs: fs.fsType) fileSystems; 148 149 # Add the mount helpers to the system path so that `mount' can find them. 150 system.fsPackages = [ pkgs.dosfstools ]; 151 152 environment.systemPackages = [ pkgs.fuse ] ++ config.system.fsPackages; 153 154 environment.etc.fstab.text = 155 let 156 fsToSkipCheck = [ "none" "btrfs" "zfs" "tmpfs" "nfs" "vboxsf" ]; 157 skipCheck = fs: fs.noCheck || fs.device == "none" || builtins.elem fs.fsType fsToSkipCheck; 158 in '' 159 # This is a generated file. Do not edit! 160 161 # Filesystems. 162 ${flip concatMapStrings fileSystems (fs: 163 (if fs.device != null then fs.device 164 else if fs.label != null then "/dev/disk/by-label/${fs.label}" 165 else throw "No device specified for mount point ${fs.mountPoint}.") 166 + " " + fs.mountPoint 167 + " " + fs.fsType 168 + " " + fs.options 169 + " 0" 170 + " " + (if skipCheck fs then "0" else 171 if fs.mountPoint == "/" then "1" else "2") 172 + "\n" 173 )} 174 175 # Swap devices. 176 ${flip concatMapStrings config.swapDevices (sw: 177 "${sw.device} none swap${prioOption sw.priority}\n" 178 )} 179 ''; 180 181 # Provide a target that pulls in all filesystems. 182 systemd.targets.fs = 183 { description = "All File Systems"; 184 wants = [ "local-fs.target" "remote-fs.target" ]; 185 }; 186 187 # Emit systemd services to format requested filesystems. 188 systemd.services = 189 let 190 191 formatDevice = fs: 192 let 193 mountPoint' = escapeSystemdPath fs.mountPoint; 194 device' = escapeSystemdPath fs.device; 195 # -F needed to allow bare block device without partitions 196 mkfsOpts = optional ((builtins.substring 0 3 fs.fsType) == "ext") "-F"; 197 in nameValuePair "mkfs-${device'}" 198 { description = "Initialisation of Filesystem ${fs.device}"; 199 wantedBy = [ "${mountPoint'}.mount" ]; 200 before = [ "${mountPoint'}.mount" "systemd-fsck@${device'}.service" ]; 201 requires = [ "${device'}.device" ]; 202 after = [ "${device'}.device" ]; 203 path = [ pkgs.utillinux ] ++ config.system.fsPackages; 204 script = 205 '' 206 if ! [ -e "${fs.device}" ]; then exit 1; fi 207 # FIXME: this is scary. The test could be more robust. 208 type=$(blkid -p -s TYPE -o value "${fs.device}" || true) 209 if [ -z "$type" ]; then 210 echo "creating ${fs.fsType} filesystem on ${fs.device}..." 211 mkfs.${fs.fsType} ${concatStringsSep " " mkfsOpts} "${fs.device}" 212 fi 213 ''; 214 unitConfig.RequiresMountsFor = [ "${dirOf fs.device}" ]; 215 unitConfig.DefaultDependencies = false; # needed to prevent a cycle 216 serviceConfig.Type = "oneshot"; 217 }; 218 219 in listToAttrs (map formatDevice (filter (fs: fs.autoFormat) fileSystems)); 220 221 }; 222 223}