···1+2+# Convert a list of strings to a regex that matches everything but those strings
3+# ... and it had to be a POSIX regex; no negative lookahead :(
4+# This is a workaround for erofs supporting only exclude regex, not an include list
5+6+import sys
7+import re
8+from collections import defaultdict
9+10+# We can configure this script to match in different ways if we need to.
11+# The regex got too long for the argument list, so we had to truncate the
12+# hashes and use MATCH_STRING_PREFIX. That's less accurate, and might pick up some
13+# garbage like .lock files, but only if the sandbox doesn't hide those. Even
14+# then it should be harmless.
15+16+# Produce the negation of ^a$
17+MATCH_EXACTLY = ".+"
18+# Produce the negation of ^a
19+MATCH_STRING_PREFIX = "//X" # //X should be epsilon regex instead. Not supported??
20+# Produce the negation of ^a/?
21+MATCH_SUBPATHS = "[^/].*$"
22+23+# match_end = MATCH_SUBPATHS
24+match_end = MATCH_STRING_PREFIX
25+# match_end = MATCH_EXACTLY
26+27+def chars_to_inverted_class(letters):
28+ assert len(letters) > 0
29+ letters = list(letters)
30+31+ s = "[^"
32+33+ if "]" in letters:
34+ s += "]"
35+ letters.remove("]")
36+37+ final = ""
38+ if "-" in letters:
39+ final = "-"
40+ letters.remove("-")
41+42+ s += "".join(letters)
43+44+ s += final
45+46+ s += "]"
47+48+ return s
49+50+# There's probably at least one bug in here, but it seems to works well enough
51+# for filtering store paths.
52+def strings_to_inverted_regex(strings):
53+ s = "("
54+55+ # Match anything that starts with the wrong character
56+57+ chars = defaultdict(list)
58+59+ for item in strings:
60+ if item != "":
61+ chars[item[0]].append(item[1:])
62+63+ if len(chars) == 0:
64+ s += match_end
65+ else:
66+ s += chars_to_inverted_class(chars)
67+68+ # Now match anything that starts with the right char, but then goes wrong
69+70+ for char, sub in chars.items():
71+ s += "|(" + re.escape(char) + strings_to_inverted_regex(sub) + ")"
72+73+ s += ")"
74+ return s
75+76+if __name__ == "__main__":
77+ stdin_lines = []
78+ for line in sys.stdin:
79+ if line.strip() != "":
80+ stdin_lines.append(line.strip())
81+82+ print("^" + strings_to_inverted_regex(stdin_lines))
83+84+# Test:
85+# (echo foo; echo fo/; echo foo/; echo foo/ba/r; echo b; echo az; echo az/; echo az/a; echo ab; echo ab/a; echo ab/; echo abc; echo abcde; echo abb; echo ac; echo b) | grep -vE "$((echo ab; echo az; echo foo;) | python includes-to-excludes.py | tee /dev/stderr )"
86+# should print ab, az, foo and their subpaths
+51-5
nixos/modules/virtualisation/qemu-vm.nix
···1718 cfg = config.virtualisation;
190020 qemu = cfg.qemu.package;
2122 consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles;
···122 TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
123 fi
124125- ${lib.optionalString cfg.useNixStoreImage
126- ''
127- # Create a writable copy/snapshot of the store image.
128- ${qemu}/bin/qemu-img create -f qcow2 -F qcow2 -b ${storeImage}/nixos.qcow2 "$TMPDIR"/store.img
129- ''}
000000000000000000000130131 # Create a directory for exchanging data with the VM.
132 mkdir -p "$TMPDIR/xchg"
···746 }
747 ]));
74800000000000000000000749 # Note [Disk layout with `useBootLoader`]
750 #
751 # If `useBootLoader = true`, we configure 2 drives:
···768 else cfg.bootDevice
769 );
770 boot.loader.grub.gfxmodeBios = with cfg.resolution; "${toString x}x${toString y}";
00771772 boot.initrd.extraUtilsCommands = lib.mkIf (cfg.useDefaultFilesystems && !config.boot.initrd.systemd.enable)
773 ''
···905 name = "nix-store";
906 file = ''"$TMPDIR"/store.img'';
907 deviceExtraOpts.bootindex = if cfg.useBootLoader then "3" else "2";
0908 }])
909 (mkIf cfg.useBootLoader [
910 # The order of this list determines the device names, see
···1718 cfg = config.virtualisation;
1920+ opt = options.virtualisation;
21+22 qemu = cfg.qemu.package;
2324 consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles;
···124 TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
125 fi
126127+ ${lib.optionalString (cfg.useNixStoreImage)
128+ (if cfg.writableStore
129+ then ''
130+ # Create a writable copy/snapshot of the store image.
131+ ${qemu}/bin/qemu-img create -f qcow2 -F qcow2 -b ${storeImage}/nixos.qcow2 "$TMPDIR"/store.img
132+ ''
133+ else ''
134+ (
135+ cd ${builtins.storeDir}
136+ ${pkgs.erofs-utils}/bin/mkfs.erofs \
137+ --force-uid=0 \
138+ --force-gid=0 \
139+ -U eb176051-bd15-49b7-9e6b-462e0b467019 \
140+ -T 0 \
141+ --exclude-regex="$(
142+ <${pkgs.closureInfo { rootPaths = [ config.system.build.toplevel regInfo ]; }}/store-paths \
143+ sed -e 's^.*/^^g' \
144+ | cut -c -10 \
145+ | ${pkgs.python3}/bin/python ${./includes-to-excludes.py} )" \
146+ "$TMPDIR"/store.img \
147+ . \
148+ </dev/null >/dev/null
149+ )
150+ ''
151+ )
152+ }
153154 # Create a directory for exchanging data with the VM.
155 mkdir -p "$TMPDIR/xchg"
···769 }
770 ]));
771772+ warnings =
773+ optional (
774+ cfg.writableStore &&
775+ cfg.useNixStoreImage &&
776+ opt.writableStore.highestPrio > lib.modules.defaultPriority)
777+ ''
778+ You have enabled ${opt.useNixStoreImage} = true,
779+ without setting ${opt.writableStore} = false.
780+781+ This causes a store image to be written to the store, which is
782+ costly, especially for the binary cache, and because of the need
783+ for more frequent garbage collection.
784+785+ If you really need this combination, you can set ${opt.writableStore}
786+ explicitly to true, incur the cost and make this warning go away.
787+ Otherwise, we recommend
788+789+ ${opt.writableStore} = false;
790+ '';
791+792 # Note [Disk layout with `useBootLoader`]
793 #
794 # If `useBootLoader = true`, we configure 2 drives:
···811 else cfg.bootDevice
812 );
813 boot.loader.grub.gfxmodeBios = with cfg.resolution; "${toString x}x${toString y}";
814+815+ boot.initrd.kernelModules = optionals (cfg.useNixStoreImage && !cfg.writableStore) [ "erofs" ];
816817 boot.initrd.extraUtilsCommands = lib.mkIf (cfg.useDefaultFilesystems && !config.boot.initrd.systemd.enable)
818 ''
···950 name = "nix-store";
951 file = ''"$TMPDIR"/store.img'';
952 deviceExtraOpts.bootindex = if cfg.useBootLoader then "3" else "2";
953+ driveExtraOpts.format = if cfg.writableStore then "qcow2" else "raw";
954 }])
955 (mkIf cfg.useBootLoader [
956 # The order of this list determines the device names, see