1
1
use std:: ffi:: OsString ;
2
- use std:: path:: Path ;
2
+ use std:: path:: { Path , PathBuf } ;
3
+
4
+ use once_cell:: sync:: Lazy ;
3
5
4
6
/// `usr`-like directory component names that MSYS2 may provide, other than for `/usr` itself.
5
7
///
@@ -25,20 +27,48 @@ use std::path::Path;
25
27
/// Second, we don't recognize `usr` itself here, even though is a plausible prefix. In MSYS2, it
26
28
/// is the prefix for MSYS2 non-native programs, i.e. those that use `msys-2.0.dll`. But unlike the
27
29
/// `<platform>` names we recognize, `usr` also has an effectively unbounded range of plausible
28
- /// meanings on non-Unix systems, which may occasionally relate to subdirectories whose contents
29
- /// are controlled by different user accounts.
30
+ /// meanings on non-Unix systems (for example, what should we take `Z:\usr` to mean?), which might
31
+ /// occasionally relate to subdirectories with contents controlled by different * user accounts* .
30
32
///
31
33
/// If we start with a `libexec/git-core` directory that we already use and trust, and it is in a
32
34
/// directory with a name like `mingw64`, we infer that this `mingw64` directory has the expected
33
- /// meaning and that its `usr` sibling, if present, is acceptable to treat as though it is a
34
- /// first-level directory inside an MSYS2-like tree. So we are willing to traverse down to
35
- /// `usr/sh.exe` and attempt to use it. But if the `libexec/git-core` we use and trust is inside a
35
+ /// meaning and accordingly infer that its `usr` sibling, if present, is acceptable to treat as
36
+ /// though it is a first-level directory inside an MSYS2-like tree. So we are willing to traverse
37
+ /// down to `usr/sh.exe` and try to use it. But if the `libexec/git-core` we use and trust is in a
36
38
/// directory named `usr`, that `usr` directory may still not have the meaning we expect of `usr`.
37
39
///
38
- /// The conditions for a privilege escalation attack or other serious malfunction seem unlikely . If
39
- /// research indicates the risk is low enough, `usr` may be added. But for now it is omitted.
40
+ /// Conditions for a privilege escalation attack or other serious malfunction seem far-fetched . If
41
+ /// further research finds the risk is low enough, `usr` may be added. But for now it is omitted.
40
42
const MSYS_USR_VARIANTS : & [ & str ] = & [ "mingw64" , "mingw32" , "clangarm64" , "clang64" , "clang32" , "ucrt64" ] ;
41
43
44
+ /// Find a Git for Windows installation directory based on `git --exec-path` output.
45
+ ///
46
+ /// Currently this is used only for finding the path to an `sh.exe` associated with Git. This is
47
+ /// separate from `installation_config()` and `installation_config_prefix()` in `gix_path::env`,
48
+ /// which guess where `etc/gitconfig` is based on `EXEPATH` or the location of the highest-scope
49
+ /// config file.
50
+ ///
51
+ /// The techniques might be combined or unified in some way in the future. The techniques those
52
+ /// functions currently use shouldn't be used to find `sh.exe`, because `EXEPATH` can take on other
53
+ /// values in some environments, and the highest scope config file may be unavailable or in another
54
+ /// location if `GIT_CONFIG_SYSTEM` or `GIT_CONFIG_NOSYSTEM` are set or if there are no variables
55
+ /// of system scope. Then paths found relative to it could be different. In contrast, the technique
56
+ /// used here may be usable for those functions, though may need to cover more directory layouts.
57
+ fn git_for_windows_root ( ) -> Option < & ' static Path > {
58
+ static GIT_ROOT : Lazy < Option < PathBuf > > = Lazy :: new ( || {
59
+ super :: core_dir ( )
60
+ . filter ( |core| core. is_absolute ( ) && core. ends_with ( "libexec/git-core" ) )
61
+ . and_then ( |core| core. ancestors ( ) . nth ( 2 ) )
62
+ . filter ( |prefix| {
63
+ // Only use `libexec/git-core` from inside something `usr`-like, such as `mingw64`.
64
+ MSYS_USR_VARIANTS . iter ( ) . any ( |name| prefix. ends_with ( name) )
65
+ } )
66
+ . and_then ( |prefix| prefix. parent ( ) )
67
+ . map ( Into :: into)
68
+ } ) ;
69
+ GIT_ROOT . as_deref ( )
70
+ }
71
+
42
72
/// Shell path fragments to concatenate to the root of a Git for Windows or MSYS2 installation.
43
73
///
44
74
/// These look like absolute Unix-style paths, but the leading `/` separators are present because
@@ -56,16 +86,9 @@ fn raw_join(path: &Path, raw_suffix: &str) -> OsString {
56
86
raw_path
57
87
}
58
88
59
- ///
89
+ /// Obtain a path to a `sh.exe` on Windows associated with Git, if one can be found.
60
90
pub ( super ) fn find_sh_on_windows ( ) -> Option < OsString > {
61
- super :: core_dir ( )
62
- . filter ( |core| core. is_absolute ( ) && core. ends_with ( "libexec/git-core" ) )
63
- . and_then ( |core| core. ancestors ( ) . nth ( 2 ) )
64
- . filter ( |prefix| {
65
- // Only use `libexec/git-core` from inside something `usr`-like, such as `mingw64`.
66
- MSYS_USR_VARIANTS . iter ( ) . any ( |name| prefix. ends_with ( name) )
67
- } )
68
- . and_then ( |prefix| prefix. parent ( ) )
91
+ git_for_windows_root ( )
69
92
. into_iter ( )
70
93
. flat_map ( |git_root| {
71
94
// Enumerate locations where `sh.exe` usually is. To avoid breaking scripts that assume the
0 commit comments