vscode icon indicating copy to clipboard operation
vscode copied to clipboard

Read-only --extensions-dir break remote extension installation

Open bjornfor opened this issue 3 years ago • 17 comments

Does this issue occur when all extensions are disabled?: No. (The issue is about installing extensions on a remote system.)

  • VS Code Version: 1.67.2
  • OS Version: Ubuntu 18.04

Steps to Reproduce:

  1. Start VSCode with code --extensions-dir /path/to/read-only-tree-of-extensions. (Why read-only? Because that's how Nix does software deployment. This command just reproduces how vscode-with-extensions from Nixpkgs works.)
  2. Check that the extensions from the local tree is available.
  3. Open a remote ssh connection and try to install one of the local extensions to the remote system.
  4. VSCode errors out with Unable to write file '/home/$USER/.vscode-server/extensions/.6661fce6-c826-4679-8f01-8421850781b5/package.json' (EntryWriteLocked (FileSystemError): Error: EACCES: permission denied, open '/home/$USER/.vscode-server/extensions/.6661fce6-c826-4679-8f01-8421850781b5/package.json')

It seems VSCode first copies files from the local system /path/to/read-only-tree-of-extensions to ~/.vscode-server/ on the remote host and then tries to update ~/.vscode-server/extensions/.UUID/package.json (still on the remote), which fails because the file still has read-only permissions.

I tried to chmod the files myself, but it seems VSCode creates a new UUID every time, so it doesn't work.

When copying extensions/files from somewhere to $HOME on a remote machine, with the intention to update some of the files, I think it's reasonable to explicitly set the writeable bit on said files, after the copy operation completes.

bjornfor avatar Jun 08 '22 18:06 bjornfor

This is probably because when we install a local extension into remote, we zip the local extension and unzip it on remote. I suspect that while zipping we are also including file modes.

sandy081 avatar Jun 10 '22 07:06 sandy081

This seems like something a developer who is familiar with the code can fix quickly, right? Or alternatively, any hints as to where that code exists and I can try to fix it myself?

bjornfor avatar Jul 15 '22 11:07 bjornfor

I own this code and if you are interested to contribute the fix, you are welcome and appreciated. Here is the code pointer

https://github.com/microsoft/vscode/blob/1a7d1b4230ea418f2261b1efe716513bca1d7b32/src/vs/platform/extensionManagement/node/extensionManagementService.ts#L107-L112

sandy081 avatar Jul 25 '22 15:07 sandy081

Any luck so far? Also hitting this and interested in a fix.

pwaller avatar Aug 23 '22 07:08 pwaller

We closed this issue because we don't plan to address it in the foreseeable future. If you disagree and feel that this issue is crucial: we are happy to listen and to reconsider.

If you wonder what we are up to, please see our roadmap and issue reporting guidelines.

Thanks for your understanding, and happy coding!

vscodenpa avatar Dec 06 '22 13:12 vscodenpa

This is still important to me, please re-open. Without this there is no reproducible and/or offline installation option (AFAIK).

bjornfor avatar Dec 06 '22 14:12 bjornfor

@isidorn: How can a (presumably) trivial fix to chmod +w some files before changing them be out of scope? I understand it doesn't have high priority for you, but please re-open.

bjornfor avatar Jan 02 '23 21:01 bjornfor

@isidorn Sorry for the noise, but seconding this for visibility. Please reopen. It means that when I use vscode on nixos to access a remote, things are broken when they shouldn't be, and it's not straightforward to fix as a user.

pwaller avatar Jan 02 '23 22:01 pwaller

Let's reopen and for now leave on the backlog.

isidorn avatar Jan 03 '23 09:01 isidorn

This is my temporary hack until this gets fixed, run this on the server (requires pkgs.inotify-tools)

#!/usr/bin/env bash

inotifywait -m -q -r -e create --format '%w%f' "$HOME/.vscode-server/extensions" |
    while read -r path; do
        chmod +w "$path"
    done

It is racey, and does not work for extensions with many files, but for small extensions such as nix-ide it works.

newAM avatar Mar 06 '23 17:03 newAM

This workaround didn't work for me. I resorted to tarring up my local extensions directory, and extracting it over at .vscode-server/extensions on the remote, which worked.

What I don't understand is why updateMetadata is trying to write to package.json. Surely it can just leave that file alone? I don't think the solution is to modify the permission bits as they're represented in the zip.

pwaller avatar Apr 16 '23 11:04 pwaller

I am hitting this now. If you manage VS Code with Nix you cannot install extensions into a remote SSH VS Code server. I'll try the workaround but I'd love a proper solution.

jasonprado avatar Oct 31 '23 23:10 jasonprado

@sandy081 Do you have suggestions for how to reproduce this locally?

I would like to tackle this and have done the following:

  1. Set up the development environment.
  2. Run a development instance locally through "Run Without Debugging".
  3. Started a TestResolver.
  4. Side loaded an VSIX extension.

But now I am stuck. The extension I loaded locally is probably not going to emulate the read-only extension directory.

Analyzing it from the code itself has also proved difficult. I can see from your pointers where zipping likely happens, and can find callers -- but I'm not sure if changing that code makes sense. I'd prefer to understand what VSCode is trying to write to the remote file system and what is triggering it to know what a proper fix is. However, there is no stack trace from the error, since it is being swallowed by fileService.writeFile.

tjni avatar Feb 22 '25 05:02 tjni

I can reproduce this today with the mkhl.direnv extensions where both the client and the remote are nixos machines where only the client is configured with home-manager:

{ pkgs }: {
  programs.vscode = {
    enable = true;
    extensions = with pkgs.vscode-extensions; [ mkhl.direnv ];
  };
}

What this does is (on the client) unpack mkhl into a directory with no read permisson and then put a symlink to it such that it is readable via a couple of symlinks:

~/.vscode/extensions/mkhl.direnv -> /nix/store/xf5gagci1nmgybkpx55pf8nr9amnzl2v-home-manager-files/.vscode/extensions/mkhl.direnv
/nix/store/xf5gagci1nmgybkpx55pf8nr9amnzl2v-home-manager-files/.vscode/extensions/mkhl.direnv -> /nix/store/9hpk80lirvbjvwkf1qvsxbwvvfjzazgy-vscode-extension-mkhl-direnv-0.17.0/share/vscode/extensions/mkhl.direnv

~/.vscode/extensions is an ordinary directory with 'normal' rwx permissons. The paths under /nix/store/ have no write permissions. So to reproduce outside of nix, I would first install try installing direnv on the client, then chmod -wx -R ~/.vscode/extensions/mkhl.direnv. If that doesn't work, I would copy the directory for the extension (to anywhere else on the client) and then after making sure the permissions are (-r-xr-xr-x) and then symlink it into ~/.vscode/extensions.

One thing to note though is that I was unable to reproduce this reliably. It happened on the first attempt, but when I quit vscode and repeated the procedure it did not hit the problem. I was able to reproduce this by trying a second package though (tamasfe.even-better-toml). A different extension I tried didn't hit the problem at all.

pwaller avatar Feb 22 '25 07:02 pwaller

Start VSCode with code --extensions-dir /path/to/read-only-tree-of-extensions. (Why read-only? Because that's how Nix does software deployment. This command just reproduces how vscode-with-extensions from Nixpkgs works.)

@bjornfor I am wondering, how you could install extensions in to this folder if it is read only?

sandy081 avatar Mar 07 '25 09:03 sandy081

Start VSCode with code --extensions-dir /path/to/read-only-tree-of-extensions. (Why read-only? Because that's how Nix does software deployment. This command just reproduces how vscode-with-extensions from Nixpkgs works.)

@bjornfor I am wondering, how you could install extensions in to this folder if it is read only?

The (read-only) extensions directory is managed by the Nix package manager. So I don't put stuff in there manually, I just declare what plugins I want in a Nix file and let it build/realize that configuration.

Here's a stand-alone example that can be used on any Linux distro:

# file vscode.nix
let
  # branch nixos-24.11 @ 2025-03-05
  nixpkgs = builtins.fetchTarball {
    url = "https://github.com/NixOS/nixpkgs/archive/48913d8f9127ea6530a2a2f1bd4daa1b8685d8a3.tar.gz";
    sha256 = "0h3yzgn0mw74039xaqpvhvd2f924d923ax3kb8gh79f2m1jgla6i";
  };
  pkgs = import nixpkgs { config = {}; overlays = []; };
in
  with pkgs;
  vscode-with-extensions.override {
    vscodeExtensions = [
      vscode-extensions.asciidoctor.asciidoctor-vscode
      vscode-extensions.eamodio.gitlens
      vscode-extensions.ms-vscode.cmake-tools
    ];
  }

Install it with NIXPKGS_ALLOW_UNFREE=1 nix-env -i -f ./vscode.nix and observe that the result is a code script that runs the actual code program with the declared extensions in a read-only directory:

$ cat $(which code)
#! /nix/store/8vpg72ik2kgxfj05lc56hkqrdrfl8xi9-bash-5.2p37/bin/bash -e
exec "/nix/store/09k7b0bhiz2rjd9980lwjj22750jrm58-vscode-1.97.2/bin/code"  --extensions-dir /nix/store/129s8jlpljzfkfhclmzbma9nm2j6g46j-vscode-extensions/share/vscode/extensions "$@" 

See perms/ownership of the directory:

$ ls -ld /nix/store/129s8jlpljzfkfhclmzbma9nm2j6g46j-vscode-extensions
dr-xr-xr-x 3 root root 4096 Jan  1  1970 /nix/store/129s8jlpljzfkfhclmzbma9nm2j6g46j-vscode-extensions

bjornfor avatar Mar 07 '25 09:03 bjornfor

Yeah, and the fact it is read only is a non-negotiable feature of the nix store; things are put there to be read only, so you can be confident it has not been interfered with and you can reproduce different configurations. This enables answering questions like 'did my configuration change break something', across the whole system, because you can still run exactly the old configuration without worrying too much if you missed some file somewhere, with respect to things like this.

pwaller avatar Mar 07 '25 10:03 pwaller

I understand this feature of Nix but VS Code require a write-able extensions folder for managing extensions.

sandy081 avatar Mar 12 '25 11:03 sandy081

I understand this feature of Nix but VS Code require a write-able extensions folder for managing extensions.

Well, that's the whole point of this issue. Please read it again. And keep in mind the difference between the local and remote system.

bjornfor avatar Mar 12 '25 11:03 bjornfor

I understand this feature of Nix but VS Code require a write-able extensions folder for managing extensions.

This is the scenario: The local extensions directory is read-only and all files in it are read-only. When those files are copied to the remote extensions directory, they retain their read-only permissions. Then, when VS Code attempts to write to one of those files, the permission check fails.

Nix only imposes the constraint that the local extensions directory and its contents are read-only. The remote extensions directory may be read-write. The issue is that VS Code does not check or modify the permissions of the files after copying from local to remote.

Majiir avatar Mar 16 '25 17:03 Majiir

Yes, I understood that you are connecting to a remote system and want to stream extensions from local to remote. I assume this seems to be a workaround you are trying to use given that you cannot install extensions directly on remote.

Please understand that What I am trying to say is that we do not recommend to have a read only extensions directory even in local / remote. This wont work while installing or updating extensions. Hence I am not sure if I would like to support this scenario.

sandy081 avatar Mar 19 '25 09:03 sandy081

With Nix, the package manager is responsible with installing extensions on the local machine, when you want to add an extension - you add it using Nix. The extension directory is read-only when doing that, and vscode is not supposed to install extensions itself (and it can't, because of read-only directory), so it works as intended.

There is no reason it should also break installing extensions on remote machine.

CertainLach avatar Mar 19 '25 09:03 CertainLach

II think I need to repeat what I wrote in the initial post:

When copying extensions/files from somewhere to $HOME on a remote machine, with the intention to update some of the files, I think it's reasonable to explicitly set the writeable bit on said files, after the copy operation completes.

Further, let's describe the situation in a more generic way:

  1. Assume a sysadmin has installed some files on a local system, and made the files read-only for normal users (so they cannot mess with it).
  2. A user wants to take those files to another (remote) system and edit them.
  3. The user copies the files, but is unaware that their way of copying the files preserves the read-only perms.
  4. The user cannot edit the files on the remote system because they're read-only, when all it had to do was not preserve the read-only perms on the new files that they created. (Think cp -a vs cp -r.)

bjornfor avatar Mar 19 '25 09:03 bjornfor

@sandy081: Please read the first 3 posts. They have all the needed information.

bjornfor avatar Mar 19 '25 10:03 bjornfor