···11+22+# Convert a list of strings to a regex that matches everything but those strings
33+# ... and it had to be a POSIX regex; no negative lookahead :(
44+# This is a workaround for erofs supporting only exclude regex, not an include list
55+66+import sys
77+import re
88+from collections import defaultdict
99+1010+# We can configure this script to match in different ways if we need to.
1111+# The regex got too long for the argument list, so we had to truncate the
1212+# hashes and use MATCH_STRING_PREFIX. That's less accurate, and might pick up some
1313+# garbage like .lock files, but only if the sandbox doesn't hide those. Even
1414+# then it should be harmless.
1515+1616+# Produce the negation of ^a$
1717+MATCH_EXACTLY = ".+"
1818+# Produce the negation of ^a
1919+MATCH_STRING_PREFIX = "//X" # //X should be epsilon regex instead. Not supported??
2020+# Produce the negation of ^a/?
2121+MATCH_SUBPATHS = "[^/].*$"
2222+2323+# match_end = MATCH_SUBPATHS
2424+match_end = MATCH_STRING_PREFIX
2525+# match_end = MATCH_EXACTLY
2626+2727+def chars_to_inverted_class(letters):
2828+ assert len(letters) > 0
2929+ letters = list(letters)
3030+3131+ s = "[^"
3232+3333+ if "]" in letters:
3434+ s += "]"
3535+ letters.remove("]")
3636+3737+ final = ""
3838+ if "-" in letters:
3939+ final = "-"
4040+ letters.remove("-")
4141+4242+ s += "".join(letters)
4343+4444+ s += final
4545+4646+ s += "]"
4747+4848+ return s
4949+5050+# There's probably at least one bug in here, but it seems to works well enough
5151+# for filtering store paths.
5252+def strings_to_inverted_regex(strings):
5353+ s = "("
5454+5555+ # Match anything that starts with the wrong character
5656+5757+ chars = defaultdict(list)
5858+5959+ for item in strings:
6060+ if item != "":
6161+ chars[item[0]].append(item[1:])
6262+6363+ if len(chars) == 0:
6464+ s += match_end
6565+ else:
6666+ s += chars_to_inverted_class(chars)
6767+6868+ # Now match anything that starts with the right char, but then goes wrong
6969+7070+ for char, sub in chars.items():
7171+ s += "|(" + re.escape(char) + strings_to_inverted_regex(sub) + ")"
7272+7373+ s += ")"
7474+ return s
7575+7676+if __name__ == "__main__":
7777+ stdin_lines = []
7878+ for line in sys.stdin:
7979+ if line.strip() != "":
8080+ stdin_lines.append(line.strip())
8181+8282+ print("^" + strings_to_inverted_regex(stdin_lines))
8383+8484+# Test:
8585+# (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 )"
8686+# should print ab, az, foo and their subpaths
+51-5
nixos/modules/virtualisation/qemu-vm.nix
···17171818 cfg = config.virtualisation;
19192020+ opt = options.virtualisation;
2121+2022 qemu = cfg.qemu.package;
21232224 consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles;
···122124 TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
123125 fi
124126125125- ${lib.optionalString cfg.useNixStoreImage
126126- ''
127127- # Create a writable copy/snapshot of the store image.
128128- ${qemu}/bin/qemu-img create -f qcow2 -F qcow2 -b ${storeImage}/nixos.qcow2 "$TMPDIR"/store.img
129129- ''}
127127+ ${lib.optionalString (cfg.useNixStoreImage)
128128+ (if cfg.writableStore
129129+ then ''
130130+ # Create a writable copy/snapshot of the store image.
131131+ ${qemu}/bin/qemu-img create -f qcow2 -F qcow2 -b ${storeImage}/nixos.qcow2 "$TMPDIR"/store.img
132132+ ''
133133+ else ''
134134+ (
135135+ cd ${builtins.storeDir}
136136+ ${pkgs.erofs-utils}/bin/mkfs.erofs \
137137+ --force-uid=0 \
138138+ --force-gid=0 \
139139+ -U eb176051-bd15-49b7-9e6b-462e0b467019 \
140140+ -T 0 \
141141+ --exclude-regex="$(
142142+ <${pkgs.closureInfo { rootPaths = [ config.system.build.toplevel regInfo ]; }}/store-paths \
143143+ sed -e 's^.*/^^g' \
144144+ | cut -c -10 \
145145+ | ${pkgs.python3}/bin/python ${./includes-to-excludes.py} )" \
146146+ "$TMPDIR"/store.img \
147147+ . \
148148+ </dev/null >/dev/null
149149+ )
150150+ ''
151151+ )
152152+ }
130153131154 # Create a directory for exchanging data with the VM.
132155 mkdir -p "$TMPDIR/xchg"
···746769 }
747770 ]));
748771772772+ warnings =
773773+ optional (
774774+ cfg.writableStore &&
775775+ cfg.useNixStoreImage &&
776776+ opt.writableStore.highestPrio > lib.modules.defaultPriority)
777777+ ''
778778+ You have enabled ${opt.useNixStoreImage} = true,
779779+ without setting ${opt.writableStore} = false.
780780+781781+ This causes a store image to be written to the store, which is
782782+ costly, especially for the binary cache, and because of the need
783783+ for more frequent garbage collection.
784784+785785+ If you really need this combination, you can set ${opt.writableStore}
786786+ explicitly to true, incur the cost and make this warning go away.
787787+ Otherwise, we recommend
788788+789789+ ${opt.writableStore} = false;
790790+ '';
791791+749792 # Note [Disk layout with `useBootLoader`]
750793 #
751794 # If `useBootLoader = true`, we configure 2 drives:
···768811 else cfg.bootDevice
769812 );
770813 boot.loader.grub.gfxmodeBios = with cfg.resolution; "${toString x}x${toString y}";
814814+815815+ boot.initrd.kernelModules = optionals (cfg.useNixStoreImage && !cfg.writableStore) [ "erofs" ];
771816772817 boot.initrd.extraUtilsCommands = lib.mkIf (cfg.useDefaultFilesystems && !config.boot.initrd.systemd.enable)
773818 ''
···905950 name = "nix-store";
906951 file = ''"$TMPDIR"/store.img'';
907952 deviceExtraOpts.bootindex = if cfg.useBootLoader then "3" else "2";
953953+ driveExtraOpts.format = if cfg.writableStore then "qcow2" else "raw";
908954 }])
909955 (mkIf cfg.useBootLoader [
910956 # The order of this list determines the device names, see