cargo: 'cargo metadata' fails on read-only file system

Problem

I first reported this with rust-analyzer at https://github.com/rust-analyzer/rust-analyzer/issues/10792: when running cargo metadata on a read-only file system, it fails saying

rust-analyzer failed to load workspace: Failed to read Cargo metadata for Rust sources: Failed to run `cargo metadata --manifest-path /home/r/.rustup/toolchains/miri/lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml`: `cargo metadata` exited with an error:     Updating crates.io index
error: failed to write /home/r/.rustup/toolchains/miri/lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.lock

Caused by:
  failed to open: /home/r/.rustup/toolchains/miri/lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.lock

Caused by:
  Read-only file system (os error 30)
rust-analyzer failed to load workspace: Failed to read Cargo metadata for Rust sources: Failed to run `cargo metadata --manifest-path /home/r/.rustup/toolchains/miri/lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml`: `cargo metadata` exited with an error:     Updating crates.io index
error: failed to write /home/r/.rustup/toolchains/miri/lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.lock

Caused by:
  failed to open: /home/r/.rustup/toolchains/miri/lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.lock

Caused by:
  Read-only file system (os error 30)

However, the action RA is trying to perform is fundamentally a read-only operation (querying for information about the rustc compiler crates).

I don’t think the currently available flags provide any way to make this work on a read-only file system either (I tried --frozen but as expected it refuses to download required crates from the network). Even if my toolchain was on a read-write file system I would not want RA’s cargo metadata to change toolchain files, after all.

Proposed Solution

cargo metadata should either handle read-only file systems by falling back to not creating/updating the lock file – or (IMO the right fix) it should not even attempt to mutate the workspace when what it is doing is a read-only operation like just determining the metadata.

Notes

No response

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 11
  • Comments: 19 (13 by maintainers)

Commits related to this issue

Most upvoted comments

In addition cargo build is conceptually just cargo metadata followed by deriving a list of build commands from the result. If cargo metadata didn’t write the lockfile, cargo build wouldn’t either.

I see no good reason why it has to be like that. It certainly does not match my mental model at all.

cargo build has good reasons to fail when called on a crate stored on a read-only FS. cargo metadata IMO does not.

Anything else may need to download crates from crates.io and store them in ~/.cargo.

Populating a cache is (conceptually) not a side-effect (formally speaking: a memoized function is observably pure). So I don’t mind it changing ~/.cargo. That is categorically different from mutating the crate it is working on.

I would say even more important than being idempotent is it being a read-only operation. It is merely querying some information and state from a crate. If that information depends on things like the crate registry, I am not surprised by multiple queries returning different results. (I would not expect curl to be idempotent, either.) However, I am severely surprised by such a command changing the on-disk state of my project, and even more surprised when there is no fallback for when performing that unexpected mutation fails.

I don’t think a read-only fs ahould be considered ephemeral. It can be compiled (or get metadata retrieved) twice in whoch case the same dependency versions should be used. I see rust-analyzer only reloading cargo metadata when Cargo.* changes as an optimization. cargo metadata should be an idempotent operation. That is running it twice will result in the same result. If there is no Cargo.lock that will not be true. The real fix would be to ship a correct Cargo.lock file as part of the rustc-src component.

The rustc-src rustup component doesn’t have the root Cargo.toml so the root Cargo.lock isn’t used. Rust-analyzer runs cargo metadata for the compiler/rustc crate which normally doesn’t need it’s own Cargo.lock as it uses the workspace’s version, but because the root Cargo.toml is missing, this isn’t possible.