cleanup comments, update translations, bump to 0.4.1
This commit is contained in:
parent
9edcc9841b
commit
7d8ab87eee
28 changed files with 412 additions and 1114 deletions
|
|
@ -1,36 +1,15 @@
|
|||
'use strict'
|
||||
|
||||
// Symlink and permission healing, shared by wine builds, prefixes, game
|
||||
// imports and LAN sync.
|
||||
//
|
||||
// Two things break when a tree is copied between machines (or the launcher is
|
||||
// just moved):
|
||||
// - symlinks stored ABSOLUTE keep pointing at the path they had on the
|
||||
// machine that wrote them. A Proton prefix is full of them (fonts, built-in
|
||||
// program stubs, dlls, all pointing into its build), and a wine build's
|
||||
// bundled default prefix has the same problem.
|
||||
// - the executable bit is dropped by every content-piping copy
|
||||
// (createWriteStream, fs.copyFile), which makes an imported `proton` or
|
||||
// `wine` binary unrunnable - the launcher then cannot start that build at
|
||||
// all.
|
||||
//
|
||||
// Everything here rewires a link to a RELATIVE path when the file it points at
|
||||
// can be found locally, so the tree stays relocatable forever, and restores
|
||||
// the executable bit on the binaries a wine/proton build needs. An absolute
|
||||
// link is always rewritten; a relative one is only touched when it no longer
|
||||
// resolves, because a tree that changed layout (a prefix whose contents moved
|
||||
// one level up, see prefixes.ensureUnifiedPrefix) keeps relative links written
|
||||
// against the old depth and every one of them breaks at once.
|
||||
// imports and LAN sync. Rewires links to relative paths (absolute always;
|
||||
// relative only when broken) and restores exec bits on build binaries.
|
||||
|
||||
const fsp = require('fs').promises
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { pathExists: exists } = require('./fsutil')
|
||||
|
||||
// dosdevices/ holds the prefix's drive mappings (c:, z:, d:, com1 ...). They
|
||||
// are *supposed* to point outside the prefix - at the filesystem root, a
|
||||
// mounted CD or a serial port - and wine rewrites them itself, so they are
|
||||
// never touched.
|
||||
// dosdevices/ mappings are supposed to point outside the prefix; never touched.
|
||||
const SKIP_DIRS = new Set(['dosdevices'])
|
||||
|
||||
async function collectSymlinks(dir, out) {
|
||||
|
|
@ -47,18 +26,12 @@ async function collectSymlinks(dir, out) {
|
|||
}
|
||||
}
|
||||
|
||||
// A target that does not exist here YET can only be something PLWL itself
|
||||
// creates later: Proton links files/data/fake-steam/appcache into its prefix,
|
||||
// a prefix links into files/wines/<build>/... (see the fallback pass below).
|
||||
// Restricting the fallback to these trees keeps an unrelated /usr/lib/... link
|
||||
// from being mistaken for a wine build's own files/lib/ directory.
|
||||
// A missing target can only be something PLWL creates later (fake-steam,
|
||||
// files/wines/...); the restriction keeps /usr/lib/... links alone.
|
||||
const ANCHOR_DIRS = new Set(['wines', 'wineprefixes', 'apps', 'data', 'translations'])
|
||||
|
||||
// True when `candidate` sits under `root` and its deepest EXISTING parent is at
|
||||
// least two components inside it. `files/data/fake-steam/appcache` passes even
|
||||
// before appcache is created, because files/data/fake-steam is already there;
|
||||
// `files/bin/<x>` does not, because a single-component parent is no evidence
|
||||
// that the link really belongs to the launcher.
|
||||
// True when `candidate` sits under `root` and its deepest existing parent is
|
||||
// at least two components inside it.
|
||||
function anchoredInside(root, candidate) {
|
||||
let p = candidate
|
||||
while (p.length > root.length + 1) {
|
||||
|
|
@ -69,12 +42,8 @@ function anchoredInside(root, candidate) {
|
|||
return false
|
||||
}
|
||||
|
||||
// The new target for an absolute link: the same file, reached from the link's
|
||||
// own directory. `roots` are the trees the target may live in - the tree being
|
||||
// healed first, then (for a prefix linking into its build) the launcher's
|
||||
// files/ root. When the target is not directly inside one of them, the longest
|
||||
// suffix that exists here is used instead, which is what makes a link written
|
||||
// on another machine work on this one.
|
||||
// The new target for an absolute link, relative to the link's own directory;
|
||||
// falls back to the longest suffix that exists locally.
|
||||
function relativeTarget(link, target, roots) {
|
||||
const dir = path.dirname(link)
|
||||
for (const root of roots) {
|
||||
|
|
@ -84,9 +53,7 @@ function relativeTarget(link, target, roots) {
|
|||
if (rel && !path.isAbsolute(rel)) return rel
|
||||
}
|
||||
}
|
||||
// Second chance for links whose file is not here yet (or whose parent chain
|
||||
// only partially exists): the relative path is still the right one, and
|
||||
// writing it now beats leaving a link that can never resolve.
|
||||
// second chance for links whose file is not here yet
|
||||
for (const requireLeaf of [true, false]) {
|
||||
for (const root of roots) {
|
||||
if (!root) continue
|
||||
|
|
@ -112,11 +79,8 @@ function relativeTarget(link, target, roots) {
|
|||
return null
|
||||
}
|
||||
|
||||
// Rewire the symlinks under `root` that can be expressed relative to `roots`
|
||||
// (usually [root, DIRS.FILES]): absolute ones always, relative ones only when
|
||||
// they no longer resolve. Returns how many links changed. A link may only
|
||||
// become rewirable once another link it chains through has been fixed, so the
|
||||
// pass repeats until nothing changes.
|
||||
// Rewire rewritable symlinks under `root`; returns how many changed. Repeats
|
||||
// until nothing changes (links can chain).
|
||||
async function rewireSymlinks(root, roots = []) {
|
||||
if (!root) return 0
|
||||
const candidates = [root, ...roots.filter(r => r && r !== root)]
|
||||
|
|
@ -133,9 +97,7 @@ async function rewireSymlinks(root, roots = []) {
|
|||
continue
|
||||
}
|
||||
const absolute = path.isAbsolute(target)
|
||||
// a relative link that still resolves is already relocatable - only a
|
||||
// broken one is worth touching, and the suffix search in relativeTarget
|
||||
// finds the file it was pointing at before the layout changed
|
||||
// a relative link that still resolves is already relocatable
|
||||
if (!absolute && exists(path.resolve(path.dirname(link), target))) continue
|
||||
const from = absolute ? path.normalize(target) : path.resolve(path.dirname(link), target)
|
||||
const next = relativeTarget(link, from, candidates)
|
||||
|
|
@ -152,15 +114,11 @@ async function rewireSymlinks(root, roots = []) {
|
|||
return fixed
|
||||
}
|
||||
|
||||
// Binaries a wine/proton build has to be able to execute. A copy that pipes
|
||||
// file contents (the LAN sync, fs.copyFile) drops the bit, and without it
|
||||
// `spawn` fails with EACCES and the build can never run. Windows .exe files
|
||||
// are NOT in here on purpose: wine starts those itself, they need no bit.
|
||||
// Binaries a wine/proton build must be able to execute; content copies drop
|
||||
// the bit and spawn fails with EACCES. Windows .exe files need no bit.
|
||||
const EXEC_NAMES = new Set(['proton', 'wine', 'wine64', 'wine-preloader', 'wine64-preloader', 'wineserver', 'winetricks', 'wineboot'])
|
||||
|
||||
// Give the exec bit back to a build's binaries (and anything in a bin/ dir).
|
||||
// Used after a sync or an import - and to repair a build that was synced
|
||||
// before the sync knew about permissions, without re-copying it.
|
||||
// Give the exec bit back to a build's binaries and bin/ dirs.
|
||||
async function restoreExecutableBits(root) {
|
||||
if (!root) return 0
|
||||
let fixed = 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue