How to edit file in /nix/store?

There’s a bug in Lutris, for which I have found a temporary fix: Steam updated this morning. Lutris doesn't work · Issue #4175 · lutris/lutris · GitHub

But I am unable to apply it, as I cannot write the file, even with sudo. Is there anyway to write the file, until the nixpkgs contain a more recent version of Lutris?
Relevant issue:Lutris fix · Issue #173712 · NixOS/nixpkgs · GitHub

The stuff in /nix/store is read-only, intentionally. Changing it by hand is throwing the entire concept of nixos out the window, and may cause all kinds of issues. I won’t tell you how to undo the read-only bit, just in case you actually try, or someone else reading this does. Please don’t even try to hack around it :slight_smile:

Rather, you want to modify the package that provides that file downstream (read: for your configuration). It’s natural that you’d like to hand-modify the files, because that’s how other distros work, given they make changing package contents or writing your own painfully cumbersome.

But this is where the really cool part of NixOS comes in, this is much easier in our world; you can do all of that with some changes to configuration.nix.

Let’s start with what this change actually is. We’ll look at the commit that was made upstream: contentstatsid key might not be present · lutris/lutris@072e72a · GitHub

It doesn’t really matter what that change does, the important bit is that it is a change to the lutris source code. If we can apply the same change to the source of the lutris you’re using, and rebuild it, it should all magically work.

Nix allows you to take an existing package, and override how it is built. We can use that to apply the above change to the source code. In this case, we can do that like so (in configuration.nix):

{pkgs, lib, ...}: let
  # lutris-unwrapped is the "real" lutris. The lutris package that is
  # installed "wraps" the "real" lutris to make it actually work on
  # NixOS, but the source is compiled here.
  #
  # We use `overrideAttrs` to change the way the package is actually
  # built. In particular, this is because we want to override
  # something in the `stdenv.mkDerivation` call, this to be specific:
  #
  # https://github.com/NixOS/nixpkgs/blob/685d243d971c4f9655c981036b9c7bafdb728a0d/pkgs/applications/misc/lutris/default.nix#L131
  lutris-unwrapped = pkgs.lutris-unwrapped.overrideAttrs (oldAttrs: {
    patches = oldAttrs.patches ++ [
      # Work around https://github.com/NixOS/nixpkgs/issues/173712
      #
      # TODO: Remove once updated upstream
      (pkgs.fetchpatch {
        name = "fix-lutris-config.patch";
        # Note: Putting `.diff` at the end of a GitHub commit URL will
        # give us a nice patch we can just apply. Very handy!
        url = "https://github.com/lutris/lutris/commit/072e72a4aefd91101b79dd05d8ce9f100a4b6b0c.diff";
        sha256 = lib.fakeSha256; # Put the checksum that nix complains to you about here
      })
    ];
  });
  # This is the lutris package we can actually install. It takes
  # lutris-unwrapped as an input, and then creates the usable package.
  #
  # We therefore need to override one of its arguments, rather than
  # its actual contents. We use `.override` for that. We override part
  # of this line:
  #
  # https://github.com/NixOS/nixpkgs/blob/685d243d971c4f9655c981036b9c7bafdb728a0d/pkgs/applications/misc/lutris/fhsenv.nix#L1
  lutris = pkgs.lutris.override {inherit lutris-unwrapped;};
in {
  environment.systemPackages = [
    # This is the lutris we created above. If you use `with pkgs;`, this will still work, `with` is confusing,
    # but in a nutshell it doesn't override anything we ourselves declare in the outer "scope".
    lutris
  ];
}

Note you can scroll that code block. Also don’t just copy that to the end of configuration.nix, of course, weave it in so it makes sense. The let bit should come before the big {} with all your options, just as it is here, and reuse the environment.systemPackackages you already have. My comments are very verbose for the purpose of explanation, don’t skim past them.

And that’s how you reproducibly and declaratively fix a bug in a package. This is literally impossible with any other distribution (well, except guix I guess, and some other even more niche distros, but they’re all just copying NixOS).

This will also create a new set of files in /nix/store, but with that change applied, mind you.

Hope that’s enough detail, it’s the best description of overriding things in NixOS I can come up with. If you still need a hand, or my untested code doesn’t actually work, ask!

21 Likes

Thanks for the super detailed explanation! I understand everything, but when i put your code in my config, I’m getting the error: error: undefined variable ‘fetchpatch’ at /etc/nixos/configuration.nix:21:8
I’m sure this is because I need to import something, but I can’t figure out.

This line should be pkgs.fetchpatch, got it from the man page, Thanks so much! Lutris is now working as expected.

1 Like

Fixed that in my snippet too, sorry about that. One day I’ll write a snippet and have it function without testing…

2 Likes

Thanks for the explanation and example TL! I haven’t gotten to learning overrides and overlays yet, so this is my first time putting one in my config. However, I’m getting errors, probably because I’m not sure exactly how or where to include it. For example, in the following config.nix skeleton outline, where would I insert your code?

{ config, pkgs, ... }:

{

  # lots of config

  environment.systemPackages = with pkgs; [
    ...
    lutris-unwrapped
    ..
  ];

} # end of config.nix

I’m trying the following, and getting some errors:

{ config, pkgs, ... }:

{ # start of configuration.nix

  # lots of config

  # lutris patch
  {pkgs}: let
    lutris-unwrapped = pkgs.lutris-unwrapped.overrideAttrs (oldAttrs: {
      patches = oldAttrs.patches ++ [
        # Work around https://github.com/NixOS/nixpkgs/issues/173712
        # TODO: Remove once updated upstream
        (pkgs.fetchpatch {
          name = "fix-lutris-config.patch";
          url = "https://github.com/lutris/lutris/commit/072e72a4aefd91101b79dd05d8ce9f100a4b6b0c.diff";
          sha256 = ""; # Put the checksum that nix complains to you about here
        })
      ];
    });

    lutris = pkgs.lutris.override {inherit lutris-unwrapped;};
    in {
      environment.systemPackages = with pkgs; [
        ...
        lutris-unwrapped
        ..
      ];
    } # end of lutris patch

  } # end of config.nix

The error is:

     |
1361 |   {pkgs}: let
     |   ^
unexpected '{'
expecting expression

or this when I test without {pkgs}:

     |
1361 |   let
     |   ^
unexpected 'l'
expecting expression

I’d put it into a seperate file, which then gets added to the imports list in your main configuration.

It would require at least one change though!

You need to make it {pkgs, ...}: rather than {pkgs}.

2 Likes

edit: Figured it out. I had been writing the hash from nix-prefetch in this manner

sha256 = "0223wq5fv7dw10yii4y296d0zlj6cch2qdgpa2cpkdryk43wi5wa";

but nix, or maybe the Lutris package specifically, expected it in this base64 format prefixed with the hashing algorithm (which seems to be colloquially referred to as an “SRI hash”).

sha256 = "sha256-ax+WiJtc0Yk6eg1kmjw5swPCWqkfYG+ojXB+onQE+2A=";
my post

I’m trying to solve the same problem using TLATER’s example but I’m having issues building the patch derivation. Without specifying the patch’s hash for fetchpatch I get a warning about the hash, and then a cryptic error about “/build/” being a directory.

$ sudo nixos-rebuild build --show-trace
building Nix...
building the system configuration...
warning: found empty hash, assuming 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='
these 17 derivations will be built:
  /nix/store/ab3bd9rqsvdafzlm4zzsda7j426ssz3a-fix-lutris-config.patch.drv
  /nix/store/k1b24r47bfbk7lyvjc0wnjpn9zp904yl-lutris-original-0.5.9.1.drv
  /nix/store/5lzjvcfbwjzfhqr1wwi6kz9xfnb8b6zx-lutris-usr-target.drv
  /nix/store/g90zn2aypysijw1fdghbc5mfc6kglnsm-lutris-fhs.drv
  /nix/store/3h7abbilaaylni9nbwqahm59lvmi13gf-lutris.drv
  /nix/store/674c693shlv6aswzavj81bi7whvbl7w5-lutris.drv
  /nix/store/m616k0magcd91zbkvsvqd7yc6n4i6g49-system-path.drv
  /nix/store/favmfl71mbgmpwjbp6q7m2n1mki5vic8-dbus-1.drv
  /nix/store/fsi3lnjhz66h79b3v4ml4h8whvfk8pcs-unit-dbus.service.drv
  /nix/store/arajs1np54nkvsrw3g5img8wcr8qm0av-user-units.drv
  /nix/store/ba975l2a38srrvbvp3h2c5psfn2afhv3-unit-accounts-daemon.service.drv
  /nix/store/d9g5pzf4jzx7hq48pr7m8g2pr8rpibaj-unit-systemd-fsck-.service.drv
  /nix/store/djy5wjhwpj2qbc6fnv5v153kfm9fa4ph-unit-polkit.service.drv
  /nix/store/fqggjydg2a01ghr4bqzv9mppclszrjas-unit-dbus.service.drv
  /nix/store/cm34ll61nrdv996bn4r15mkqqvin5x27-system-units.drv
  /nix/store/wmllhj7r5i30jfszanwj3c179vfsp8f6-etc.drv
  /nix/store/l947hga7rpy9pxd9n2k338qp4fkawp7y-nixos-system-cerberus-21.11.337635.cbd40c72b26.drv
building '/nix/store/ab3bd9rqsvdafzlm4zzsda7j426ssz3a-fix-lutris-config.patch.drv'...

trying https://github.com/lutris/lutris/commit/072e72a4aefd91101b79dd05d8ce9f100a4b6b0c.diff
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   705  100   705    0     0   2758      0 --:--:-- --:--:-- --:--:--  2764
/nix/store/8930hgla9ajgq9k7r8jinqwbn1dqwapw-stdenv-linux/setup: line 91: /build/: Is a directory
error: builder for '/nix/store/ab3bd9rqsvdafzlm4zzsda7j426ssz3a-fix-lutris-config.patch.drv' failed with exit code 1
error: 1 dependencies of derivation '/nix/store/k1b24r47bfbk7lyvjc0wnjpn9zp904yl-lutris-original-0.5.9.1.drv' failed to build
error: 1 dependencies of derivation '/nix/store/5lzjvcfbwjzfhqr1wwi6kz9xfnb8b6zx-lutris-usr-target.drv' failed to build
error: 1 dependencies of derivation '/nix/store/g90zn2aypysijw1fdghbc5mfc6kglnsm-lutris-fhs.drv' failed to build
error: 1 dependencies of derivation '/nix/store/3h7abbilaaylni9nbwqahm59lvmi13gf-lutris.drv' failed to build
error: 2 dependencies of derivation '/nix/store/674c693shlv6aswzavj81bi7whvbl7w5-lutris.drv' failed to build
error: 1 dependencies of derivation '/nix/store/m616k0magcd91zbkvsvqd7yc6n4i6g49-system-path.drv' failed to build
error: 1 dependencies of derivation '/nix/store/favmfl71mbgmpwjbp6q7m2n1mki5vic8-dbus-1.drv' failed to build
error: 1 dependencies of derivation '/nix/store/ba975l2a38srrvbvp3h2c5psfn2afhv3-unit-accounts-daemon.service.drv' failed to build
error: 1 dependencies of derivation '/nix/store/djy5wjhwpj2qbc6fnv5v153kfm9fa4ph-unit-polkit.service.drv' failed to build
error: 1 dependencies of derivation '/nix/store/d9g5pzf4jzx7hq48pr7m8g2pr8rpibaj-unit-systemd-fsck-.service.drv' failed to build
error: 1 dependencies of derivation '/nix/store/fqggjydg2a01ghr4bqzv9mppclszrjas-unit-dbus.service.drv' failed to build
error: 1 dependencies of derivation '/nix/store/fsi3lnjhz66h79b3v4ml4h8whvfk8pcs-unit-dbus.service.drv' failed to build
error: 4 dependencies of derivation '/nix/store/cm34ll61nrdv996bn4r15mkqqvin5x27-system-units.drv' failed to build
error: 1 dependencies of derivation '/nix/store/arajs1np54nkvsrw3g5img8wcr8qm0av-user-units.drv' failed to build
error: 4 dependencies of derivation '/nix/store/wmllhj7r5i30jfszanwj3c179vfsp8f6-etc.drv' failed to build
error: 2 dependencies of derivation '/nix/store/l947hga7rpy9pxd9n2k338qp4fkawp7y-nixos-system-cerberus-21.11.337635.cbd40c72b26.drv' failed to build

None of the output seems to include a correction of what the hash should be. I instead tried to get the hash with nix-prefetch-url

$ nix-prefetch-url https://github.com/lutris/lutris/commit/072e72a4aefd91101b79dd05d8ce9f100a4b6b0c.diff
path is '/nix/store/5jfsqm2ydgdkc4fbaql8gqbaxg794s1q-072e72a4aefd91101b79dd05d8ce9f100a4b6b0c.diff'
0223wq5fv7dw10yii4y296d0zlj6cch2qdgpa2cpkdryk43wi5wa

Upon inserting that hash I get a different error

$ sudo nixos-rebuild build --show-trace
<snipped repetition from above>
building '/nix/store/6iwhxr8rc74n4xzlqx4sl67k0r7kvr16-fix-lutris-config.patch.drv'...

trying https://github.com/lutris/lutris/commit/072e72a4aefd91101b79dd05d8ce9f100a4b6b0c.diff
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   705  100   705    0     0   4129      0 --:--:-- --:--:-- --:--:--  4147
error: hash mismatch in fixed-output derivation '/nix/store/6iwhxr8rc74n4xzlqx4sl67k0r7kvr16-fix-lutris-config.patch.drv':
         specified: sha256-ipfIB5k+t3mZUPc1LCBjRtIPmknCkxg9CLyd7QrmQwg=
            got:    sha256-ax+WiJtc0Yk6eg1kmjw5swPCWqkfYG+ojXB+onQE+2A=
error: 1 dependencies of derivation '/nix/store/64fcyng7yib6d14fhkwgdjsxpiff4dvs-lutris-original-0.5.9.1.drv' failed to build
<snip>

What am I not understanding?

1 Like

That’s actually a bit unexpected! I suspect rather than it requiring a hash in that format, what is happening is that nix-prefetch-url gives you a hash of before fetchpatch has modified it. fetchpatch tries to ensure that patches follow a regular format, so it probably makes a change to the file, and because of how a fixed-output derivation works, and how nix reports hashes, you then end up with a different hash reported in that format.

And now you mention it, I’ve had that happen to me with fetchpatch before too. fetchurl works fine with an empty hash, fetchpatch does not. I’ll update my snippet to use lib.fakeSha256 instead, but this might be worth an upstream issue at some point.

@NobbZ is (as usual) completely right, but to spell it out in the simplest way possible (because clearly @Ice-Cube69 isn’t the only one who needs this at the minute):

# lutris-fix.nix
{pkgs, lib, ...}: let
  lutris-unwrapped = pkgs.lutris-unwrapped.overrideAttrs (oldAttrs: {
    patches = oldAttrs.patches ++ [
      # Work around https://github.com/NixOS/nixpkgs/issues/173712
      #
      # TODO: Remove once updated upstream
      (pkgs.fetchpatch {
        name = "fix-lutris-config.patch";
        url = "https://github.com/lutris/lutris/commit/072e72a4aefd91101b79dd05d8ce9f100a4b6b0c.diff";
        sha256 = lib.fakeSha256; # Put the checksum that nix complains to you about here
      })
    ];
  });
  lutris = pkgs.lutris.override {inherit lutris-unwrapped;};
in {
  environment.systemPackages = [
    lutris
  ];
}
# configuration.nix
{config, pkgs, ...}:

{
  imports = [
    ./lutris-fix.nix
    # Other imports
  ];
  # As much config as you want
}

You’d put lutris-fix.nix right next to configuration.nix. You could put it in configuration.nix too, if you put the let bit right after the {config, pkgs} bit, but it’s a bit trickier to explain how to do that correctly without explaining the nix syntax.

All that said, many ways lead to Rome, which is why I didn’t want to prescribe a method. Technically I think the cleanest solution would involve something with pkgs.callPackage, but this solution should work just as well, and I think this thread already has enough learning material :wink:

2 Likes

Thanks a lot for this!
When using flake-compat overlay in Home-Manager, here’s how you can do it with @sjustinas’s solution:

hyprlandPatched = pkgs.applyPatches {
  name = "hyprland";
  src = builtins.fetchTarball "https://github.com/hyprwm/Hyprland/archive/a34b74766193c21803e2bc6a51fa6c13d82eb39f.tar.gz";
  patches = [
    (pkgs.fetchpatch {
      name = "nix-fix-meson.patch";
      url = "https://github.com/hyprwm/Hyprland/commit/b602ac0970cbf5f5a9331853a5b96ed394a84bf1.diff";
      sha256 = "YmFz01HSKbi8h8sJclM1y+D7CXHx/iELyYJnbaI6Qoo=";
    })
    (pkgs.fetchpatch {
      name = "nix-fix-hidpi.patch";
      url = "https://github.com/hyprwm/Hyprland/commit/c1217066d1e7c6cd12f43134f9ef1145787f7905.diff";
      sha256 = "9L8vS0xE4M2Lx61ZNg6y4wQhSHlWz2XPNaTlQmxWD3g=";
    })
  ];
};
hyprland = (import flake-compat {
  src = hyprlandPatched;
}).defaultNix;

This with nixos-rebuild switch --rollback made it really easy to manually patch and test broken commits

2 Likes