Skip to content

Fix crash on first run when presets are installed read-only - #15764

Open
nevesenin wants to merge 1 commit into
prusa3d:masterfrom
nevesenin:fix/bundle-loader-writable-preset-copies
Open

Fix crash on first run when presets are installed read-only#15764
nevesenin wants to merge 1 commit into
prusa3d:masterfrom
nevesenin:fix/bundle-loader-writable-preset-copies

Conversation

@nevesenin

Copy link
Copy Markdown
  • PrusaSlicer 3.0.0-alpha11 — source build from master @ 6f51012
  • NixOS 26.11 (Zokor)
  • GNOME Shell 50.4, Wayland
  • GTK 3.24.52
  • wxWidgets 3.3.3.1, GTK3 backend
  • fontconfig 2.18.2

What happens

On a fresh datadir the application aborts during first run, before the main
window appears. The last line logged is Populate vendor <repo>/<vendor> from
populate_local_bundle(), immediately before the copy.

The abort is an uncaught boost::filesystem exception — std::terminate, not a
handled error. Narrowing it down inside that function: fs::copy() with
recursive creates each destination directory with mkdir(dest, source_mode),
so the copy inherits the permissions of the installed resources rather than
the permissions a writable working copy needs. Where the resources carry no
owner-write bit, the destination directory is created without one too, and the
next copy_file() into it throws EACCES. The parent survives only because
create_directories() applies the umask.

Note that adding a permission fixup after fs::copy() does not help: the throw
happens inside fs::copy() itself.

Why it does not reproduce on most installs

The trigger is the source's mode bits, not whether the install is read-only.
Distribution packages under /usr/share, and Flatpak, Snap and AppImage, all
ship 0755 directories — their read-only-ness is a mount property, which
fs::copy never consults — so mkdir(0755) leaves owner-write and the copy
succeeds. Nix and Guix are the exception: every store entry is r-xr-xr-x /
r--r--r--, because stripping the write bit is how those systems enforce store
immutability. That is where this fires.

The fix

Copy the tree by hand instead of with fs::copy(), so the destination
permissions are chosen rather than inherited: create_directories() for
directories (umask → 0755), copy_file() plus an explicit owner-write for files
(→ 0644). Same for the .idx, which would otherwise be copied 0444 and could
not be rewritten later.

18 added lines in one file. On installs that already ship 0755/0644 resources
this changes nothing observable — it only stops the copy from depending on the
source permissions.

Testing

Built from this branch on NixOS — clang 21.1.8, wxWidgets 3.3.3.1 (GTK3),
system dependencies rather than the deps/ superbuild.

  • Before, from an empty datadir: aborts during first run.
  • After, from an empty datadir: the vendor bundle populates, the application
    starts, and the copied presets are writable.

When retesting, delete the datadir between runs. populate_local_bundle() is
guarded by if (!fs::exists(dest_path / "vendor.yaml")), and a failed run
leaves a partial tree, so a subsequent run skips the copy entirely and a broken
build can look fixed (or a fixed build can look untested).

Linux only. The change is platform independent, but I have not built Windows or
macOS.

populate_local_bundle() materialises a user-writable copy of the bundled
vendor presets with fs::copy(recursive | overwrite_existing). fs::copy
creates each destination directory with mkdir(dest, source_mode), so the
copy inherits the permissions of the installed resources rather than the
permissions a writable working copy needs.

When the resources are installed without the owner-write bit -- as on Nix
and Guix, where every store entry is r-xr-xr-x / r--r--r-- -- the
destination directory is created 0555 and the next copy_file into it
throws EACCES. Nothing catches it, so the application terminates on first
run. The parent directory survives only because create_directories()
applies the umask.

Note that a permission fixup after fs::copy does not help: the throw
happens inside fs::copy itself. Copy the tree by hand instead, so
directories go through create_directories() (umask -> 0755) and files get
owner-write added explicitly (-> 0644). Do the same for the .idx file,
which would otherwise be copied 0444 and could not be rewritten later.

On installations that already ship 0755/0644 resources this changes
nothing observable; it only stops the copy from depending on the source
permissions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant