1{ pkgs, lib, stdenv, fetchFromGitHub, fetchpatch
2, autoreconfHook269, util-linux, nukeReferences, coreutils
3, perl, nixosTests
4, configFile ? "all"
5
6# Userspace dependencies
7, zlib, libuuid, python3, attr, openssl
8, libtirpc
9, nfs-utils, samba
10, gawk, gnugrep, gnused, systemd
11, smartmontools, enableMail ? false
12, sysstat, pkg-config
13, curl
14, pam
15
16# Kernel dependencies
17, kernel ? null
18, enablePython ? true
19, ...
20}:
21
22{ version
23, sha256
24, extraPatches ? []
25, rev ? "zfs-${version}"
26, isUnstable ? false
27, latestCompatibleLinuxPackages
28, kernelCompatible ? null
29}:
30
31let
32 inherit (lib) any optionalString optionals optional makeBinPath;
33
34 smartmon = smartmontools.override { inherit enableMail; };
35
36 buildKernel = any (n: n == configFile) [ "kernel" "all" ];
37 buildUser = any (n: n == configFile) [ "user" "all" ];
38
39 # XXX: You always want to build kernel modules with the same stdenv as the
40 # kernel was built with. However, since zfs can also be built for userspace we
41 # need to correctly pick between the provided/default stdenv, and the one used
42 # by the kernel.
43 # If you don't do this your ZFS builds will fail on any non-standard (e.g.
44 # clang-built) kernels.
45 stdenv' = if kernel == null then stdenv else kernel.stdenv;
46in
47
48stdenv'.mkDerivation {
49 name = "zfs-${configFile}-${version}${optionalString buildKernel "-${kernel.version}"}";
50
51 src = fetchFromGitHub {
52 owner = "openzfs";
53 repo = "zfs";
54 inherit rev sha256;
55 };
56
57 patches = [
58 (fetchpatch {
59 name = "musl.patch";
60 url = "https://github.com/openzfs/zfs/commit/1f19826c9ac85835cbde61a7439d9d1fefe43a4a.patch";
61 sha256 = "XEaK227ubfOwlB2s851UvZ6xp/QOtYUWYsKTkEHzmo0=";
62 })
63 ] ++ extraPatches;
64
65 postPatch = optionalString buildKernel ''
66 patchShebangs scripts
67 # The arrays must remain the same length, so we repeat a flag that is
68 # already part of the command and therefore has no effect.
69 substituteInPlace ./module/os/linux/zfs/zfs_ctldir.c \
70 --replace '"/usr/bin/env", "umount"' '"${util-linux}/bin/umount", "-n"' \
71 --replace '"/usr/bin/env", "mount"' '"${util-linux}/bin/mount", "-n"'
72 '' + optionalString buildUser ''
73 substituteInPlace ./lib/libshare/os/linux/nfs.c --replace "/usr/sbin/exportfs" "${
74 # We don't *need* python support, but we set it like this to minimize closure size:
75 # If it's disabled by default, no need to enable it, even if we have python enabled
76 # And if it's enabled by default, only change that if we explicitly disable python to remove python from the closure
77 nfs-utils.override (old: { enablePython = old.enablePython or true && enablePython; })
78 }/bin/exportfs"
79 substituteInPlace ./lib/libshare/smb.h --replace "/usr/bin/net" "${samba}/bin/net"
80 # Disable dynamic loading of libcurl
81 substituteInPlace ./config/user-libfetch.m4 --replace "curl-config --built-shared" "true"
82 substituteInPlace ./config/user-systemd.m4 --replace "/usr/lib/modules-load.d" "$out/etc/modules-load.d"
83 substituteInPlace ./config/zfs-build.m4 --replace "\$sysconfdir/init.d" "$out/etc/init.d" \
84 --replace "/etc/default" "$out/etc/default"
85 substituteInPlace ./etc/zfs/Makefile.am --replace "\$(sysconfdir)" "$out/etc"
86
87 substituteInPlace ./contrib/initramfs/hooks/Makefile.am \
88 --replace "/usr/share/initramfs-tools/hooks" "$out/usr/share/initramfs-tools/hooks"
89 substituteInPlace ./contrib/initramfs/Makefile.am \
90 --replace "/usr/share/initramfs-tools" "$out/usr/share/initramfs-tools"
91 substituteInPlace ./contrib/initramfs/scripts/Makefile.am \
92 --replace "/usr/share/initramfs-tools/scripts" "$out/usr/share/initramfs-tools/scripts"
93 substituteInPlace ./contrib/initramfs/scripts/local-top/Makefile.am \
94 --replace "/usr/share/initramfs-tools/scripts/local-top" "$out/usr/share/initramfs-tools/scripts/local-top"
95 substituteInPlace ./contrib/initramfs/scripts/Makefile.am \
96 --replace "/usr/share/initramfs-tools/scripts" "$out/usr/share/initramfs-tools/scripts"
97 substituteInPlace ./contrib/initramfs/scripts/local-top/Makefile.am \
98 --replace "/usr/share/initramfs-tools/scripts/local-top" "$out/usr/share/initramfs-tools/scripts/local-top"
99 substituteInPlace ./etc/systemd/system/Makefile.am \
100 --replace '$(DESTDIR)$(systemdunitdir)' "$out"'$(DESTDIR)$(systemdunitdir)'
101
102 substituteInPlace ./contrib/initramfs/conf.d/Makefile.am \
103 --replace "/usr/share/initramfs-tools/conf.d" "$out/usr/share/initramfs-tools/conf.d"
104 substituteInPlace ./contrib/initramfs/conf-hooks.d/Makefile.am \
105 --replace "/usr/share/initramfs-tools/conf-hooks.d" "$out/usr/share/initramfs-tools/conf-hooks.d"
106
107 substituteInPlace ./cmd/vdev_id/vdev_id \
108 --replace "PATH=/bin:/sbin:/usr/bin:/usr/sbin" \
109 "PATH=${makeBinPath [ coreutils gawk gnused gnugrep systemd ]}"
110 '';
111
112 nativeBuildInputs = [ autoreconfHook269 nukeReferences ]
113 ++ optionals buildKernel (kernel.moduleBuildDependencies ++ [ perl ])
114 ++ optional buildUser pkg-config;
115 buildInputs = optionals buildUser [ zlib libuuid attr libtirpc pam ]
116 ++ optional buildUser openssl
117 ++ optional buildUser curl
118 ++ optional (buildUser && enablePython) python3;
119
120 # for zdb to get the rpath to libgcc_s, needed for pthread_cancel to work
121 NIX_CFLAGS_LINK = "-lgcc_s";
122
123 hardeningDisable = [ "fortify" "stackprotector" "pic" ];
124
125 configureFlags = [
126 "--with-config=${configFile}"
127 "--with-tirpc=1"
128 (lib.withFeatureAs (buildUser && enablePython) "python" python3.interpreter)
129 ] ++ optionals buildUser [
130 "--with-dracutdir=$(out)/lib/dracut"
131 "--with-udevdir=$(out)/lib/udev"
132 "--with-systemdunitdir=$(out)/etc/systemd/system"
133 "--with-systemdpresetdir=$(out)/etc/systemd/system-preset"
134 "--with-systemdgeneratordir=$(out)/lib/systemd/system-generator"
135 "--with-mounthelperdir=$(out)/bin"
136 "--libexecdir=$(out)/libexec"
137 "--sysconfdir=/etc"
138 "--localstatedir=/var"
139 "--enable-systemd"
140 "--enable-pam"
141 ] ++ optionals buildKernel ([
142 "--with-linux=${kernel.dev}/lib/modules/${kernel.modDirVersion}/source"
143 "--with-linux-obj=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build"
144 ] ++ kernel.makeFlags);
145
146 makeFlags = optionals buildKernel kernel.makeFlags;
147
148 enableParallelBuilding = true;
149
150 installFlags = [
151 "sysconfdir=\${out}/etc"
152 "DEFAULT_INITCONF_DIR=\${out}/default"
153 "INSTALL_MOD_PATH=\${out}"
154 ];
155
156 # Enabling BTF causes zfs to be build with debug symbols.
157 # Since zfs compress kernel modules on installation, our strip hooks skip stripping them.
158 # Hence we strip modules prior to compression.
159 postBuild = optionalString buildKernel ''
160 find . -name "*.ko" -print0 | xargs -0 -P$NIX_BUILD_CORES ${stdenv.cc.targetPrefix}strip --strip-debug
161 '';
162
163 postInstall = optionalString buildKernel ''
164 # Add reference that cannot be detected due to compressed kernel module
165 mkdir -p "$out/nix-support"
166 echo "${util-linux}" >> "$out/nix-support/extra-refs"
167 '' + optionalString buildUser ''
168 # Remove provided services as they are buggy
169 rm $out/etc/systemd/system/zfs-import-*.service
170
171 sed -i '/zfs-import-scan.service/d' $out/etc/systemd/system/*
172
173 for i in $out/etc/systemd/system/*; do
174 substituteInPlace $i --replace "zfs-import-cache.service" "zfs-import.target"
175 done
176
177 # Remove tests because they add a runtime dependency on gcc
178 rm -rf $out/share/zfs/zfs-tests
179
180 # Add Bash completions.
181 install -v -m444 -D -t $out/share/bash-completion/completions contrib/bash_completion.d/zfs
182 (cd $out/share/bash-completion/completions; ln -s zfs zpool)
183 '';
184
185 postFixup = let
186 path = "PATH=${makeBinPath [ coreutils gawk gnused gnugrep util-linux smartmon sysstat ]}:$PATH";
187 in ''
188 for i in $out/libexec/zfs/zpool.d/*; do
189 sed -i '2i${path}' $i
190 done
191 '';
192
193 outputs = [ "out" ] ++ optionals buildUser [ "dev" ];
194
195 passthru = {
196 inherit enableMail latestCompatibleLinuxPackages;
197
198 tests =
199 if isUnstable then [
200 nixosTests.zfs.unstable
201 ] else [
202 nixosTests.zfs.installer
203 nixosTests.zfs.stable
204 ];
205 };
206
207 meta = {
208 description = "ZFS Filesystem Linux Kernel module";
209 longDescription = ''
210 ZFS is a filesystem that combines a logical volume manager with a
211 Copy-On-Write filesystem with data integrity detection and repair,
212 snapshotting, cloning, block devices, deduplication, and more.
213 '';
214 homepage = "https://github.com/openzfs/zfs";
215 changelog = "https://github.com/openzfs/zfs/releases/tag/zfs-${version}";
216 license = lib.licenses.cddl;
217
218 # The case-block for TARGET_CPU has branches for only five CPU families,
219 # which prevents ZFS from building on any other platform. Since the NixOS
220 # `boot.zfs.enabled` property is `readOnly`, excluding platforms where ZFS
221 # does not build is the only way to produce a NixOS installer on such
222 # platforms.
223 # https://github.com/openzfs/zfs/blob/6a6bd493988c75331deab06e5352a9bed035a87d/config/always-arch.m4#L16
224 platforms =
225 with lib.systems.inspect.patterns;
226 map (p: p // isLinux) [ isx86_32 isx86_64 isPower isAarch64 isSparc ];
227
228 maintainers = with lib.maintainers; [ jcumming jonringer globin raitobezarius ];
229 mainProgram = "zfs";
230 # If your Linux kernel version is not yet supported by zfs, try zfsUnstable.
231 # On NixOS set the option boot.zfs.enableUnstable.
232 broken = buildKernel && (kernelCompatible != null) && !kernelCompatible;
233 };
234}