mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-24 10:02:26 +03:00
Merge branch 'master' into Convert-cloud-api-object-to-class
This commit is contained in:
commit
eb93713d69
68
Cargo.lock
generated
68
Cargo.lock
generated
@ -1194,7 +1194,7 @@ checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1217,7 +1217,7 @@ checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1506,7 +1506,7 @@ dependencies = [
|
||||
"cc",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1574,7 +1574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1887,7 +1887,7 @@ dependencies = [
|
||||
"gobject-sys",
|
||||
"libc",
|
||||
"system-deps 6.1.1",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2008,6 +2008,8 @@ dependencies = [
|
||||
"sysinfo",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"winapi 0.3.9",
|
||||
"windows-named-pipe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2284,7 +2286,7 @@ checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"match_cfg",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2679,6 +2681,16 @@ dependencies = [
|
||||
"treediff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
dependencies = [
|
||||
"winapi 0.2.8",
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue"
|
||||
version = "1.0.8"
|
||||
@ -3135,7 +3147,7 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3145,7 +3157,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||
dependencies = [
|
||||
"overload",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3389,7 +3401,7 @@ checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3499,7 +3511,7 @@ dependencies = [
|
||||
"libc",
|
||||
"redox_syscall 0.2.16",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3986,7 +3998,7 @@ dependencies = [
|
||||
"raw-cpuid",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5010,7 +5022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5538,7 +5550,7 @@ dependencies = [
|
||||
"serde",
|
||||
"tauri",
|
||||
"time",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6107,7 +6119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d"
|
||||
dependencies = [
|
||||
"tempfile",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6479,6 +6491,12 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@ -6489,6 +6507,12 @@ dependencies = [
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
@ -6501,7 +6525,7 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6591,6 +6615,16 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278"
|
||||
|
||||
[[package]]
|
||||
name = "windows-named-pipe"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "808ba65b3d86cc5465971ad08ee3850e197cc8d5719277611fabb27827c02388"
|
||||
dependencies = [
|
||||
"kernel32-sys",
|
||||
"winapi 0.2.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
@ -7001,7 +7035,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd"
|
||||
dependencies = [
|
||||
"nix 0.26.4",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7044,7 +7078,7 @@ dependencies = [
|
||||
"static_assertions",
|
||||
"tracing",
|
||||
"uds_windows",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
"xdg-home",
|
||||
"zbus_macros",
|
||||
"zbus_names",
|
||||
|
@ -2665,7 +2665,7 @@ pub fn push(
|
||||
with_force,
|
||||
credentials,
|
||||
None,
|
||||
askpass,
|
||||
askpass.clone(),
|
||||
)?;
|
||||
|
||||
vbranch.upstream = Some(remote_branch.clone());
|
||||
@ -2673,6 +2673,11 @@ pub fn push(
|
||||
branch_writer
|
||||
.write(&mut vbranch)
|
||||
.context("failed to write target branch after push")?;
|
||||
project_repository.fetch(
|
||||
remote_branch.remote(),
|
||||
credentials,
|
||||
askpass.map(|(broker, _)| (broker, "modal".to_string())),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -8,11 +8,11 @@ path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "gitbutler-git-askpass"
|
||||
path = "src/cli/bin/askpass.rs"
|
||||
path = "src/bin/askpass.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "gitbutler-git-setsid"
|
||||
path = "src/cli/bin/setsid.rs"
|
||||
path = "src/bin/setsid.rs"
|
||||
|
||||
[features]
|
||||
default = ["serde", "tokio"]
|
||||
@ -29,3 +29,8 @@ sysinfo = "0.30.5"
|
||||
|
||||
[target."cfg(unix)".dependencies]
|
||||
nix = { version = "0.27.1", features = ["process", "socket", "user"] }
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
winapi = { version = "0.3.9", features = ["winbase", "namedpipeapi"] }
|
||||
# synchronous named pipes for the askpass utility
|
||||
windows-named-pipe = "0.1.0"
|
||||
|
@ -1,2 +0,0 @@
|
||||
#[cfg(feature = "cli")]
|
||||
pub mod cli;
|
49
gitbutler-git/src/bin/askpass.rs
Normal file
49
gitbutler-git/src/bin/askpass.rs
Normal file
@ -0,0 +1,49 @@
|
||||
#[cfg(unix)]
|
||||
#[path = "askpass/unix.rs"]
|
||||
mod unix;
|
||||
#[cfg(windows)]
|
||||
#[path = "askpass/windows.rs"]
|
||||
mod windows;
|
||||
|
||||
#[cfg(windows)]
|
||||
use self::windows::UnixCompatibility;
|
||||
|
||||
use std::io::{BufRead, BufReader, BufWriter, Write};
|
||||
|
||||
pub fn main() {
|
||||
let pipe_name = std::env::var("GITBUTLER_ASKPASS_PIPE").expect("do not run this binary yourself; it's only meant to be run by GitButler (missing GITBUTLER_ASKPASS_PIPE env var)");
|
||||
let pipe_secret = std::env::var("GITBUTLER_ASKPASS_SECRET").expect("do not run this binary yourself; it's only meant to be run by GitButler (missing GITBUTLER_ASKPASS_SECRET env var)");
|
||||
let prompt = std::env::args().nth(1).expect("do not run this binary yourself; it's only meant to be run by GitButler (missing prompt arg)");
|
||||
|
||||
#[cfg(unix)]
|
||||
let raw_stream = self::unix::establish(&pipe_name);
|
||||
#[cfg(windows)]
|
||||
let raw_stream = self::windows::establish(&pipe_name);
|
||||
|
||||
let mut reader = BufReader::new(raw_stream.try_clone().unwrap());
|
||||
let mut writer = BufWriter::new(raw_stream.try_clone().unwrap());
|
||||
|
||||
// Write the secret.
|
||||
writeln!(writer, "{pipe_secret}").expect("write(secret):");
|
||||
|
||||
// Write the prompt that Git gave us.
|
||||
writeln!(writer, "{prompt}").expect("write(prompt):");
|
||||
|
||||
writer.flush().expect("flush():");
|
||||
|
||||
// Clear the timeout (it's now time for the user to provide a response)
|
||||
raw_stream
|
||||
.set_read_timeout(None)
|
||||
.expect("set_read_timeout(None):");
|
||||
|
||||
// Wait for the response.
|
||||
let mut password = String::new();
|
||||
let nread = reader.read_line(&mut password).expect("read_line():");
|
||||
if nread == 0 {
|
||||
panic!("read_line() returned 0");
|
||||
}
|
||||
|
||||
// Write the response back to Git.
|
||||
// `password` already has a newline at the end.
|
||||
write!(std::io::stdout(), "{password}").expect("write(password):");
|
||||
}
|
5
gitbutler-git/src/bin/askpass/unix.rs
Normal file
5
gitbutler-git/src/bin/askpass/unix.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
pub fn establish(sock_path: &str) -> UnixStream {
|
||||
UnixStream::connect(sock_path).expect("connect():")
|
||||
}
|
59
gitbutler-git/src/bin/askpass/windows.rs
Normal file
59
gitbutler-git/src/bin/askpass/windows.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use std::{
|
||||
io,
|
||||
os::windows::io::{AsRawHandle, FromRawHandle},
|
||||
time::Duration,
|
||||
};
|
||||
use windows_named_pipe::PipeStream;
|
||||
|
||||
pub fn establish(sock_path: &str) -> PipeStream {
|
||||
PipeStream::connect(sock_path).unwrap()
|
||||
}
|
||||
|
||||
/// There are some methods we need in order to run askpass correctly,
|
||||
/// and those methods are not available out of the box on windows.
|
||||
/// We stub them using this trait so we don't have to newtype
|
||||
/// the pipestream itself (which would be extensive and un-DRY).
|
||||
pub trait UnixCompatibility: Sized {
|
||||
fn try_clone(&self) -> Option<Self>;
|
||||
fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl UnixCompatibility for PipeStream {
|
||||
fn try_clone(&self) -> Option<Self> {
|
||||
Some(unsafe { Self::from_raw_handle(self.as_raw_handle()) })
|
||||
}
|
||||
|
||||
fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
|
||||
// NOTE(qix-): Technically, this shouldn't work (and probably doesn't).
|
||||
// NOTE(qix-): The documentation states:
|
||||
// NOTE(qix-):
|
||||
// NOTE(qix-): > This parameter must be NULL if . . . client and server
|
||||
// NOTE(qix-): > processes are on the same computer.
|
||||
// NOTE(qix-):
|
||||
// NOTE(qix-): This is indeed the case here, but we try to make it work
|
||||
// NOTE(qix-): anyway.
|
||||
#[allow(unused_assignments)]
|
||||
let mut timeout_ms: winapi::shared::minwindef::DWORD = 0;
|
||||
let timeout_ptr: winapi::shared::minwindef::LPDWORD = if let Some(timeout) = timeout {
|
||||
timeout_ms = timeout.as_millis() as winapi::shared::minwindef::DWORD;
|
||||
&mut timeout_ms as *mut _
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
};
|
||||
|
||||
let r = unsafe {
|
||||
winapi::um::namedpipeapi::SetNamedPipeHandleState(
|
||||
self.as_raw_handle(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
timeout_ptr,
|
||||
)
|
||||
};
|
||||
|
||||
if r == 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
10
gitbutler-git/src/bin/setsid.rs
Normal file
10
gitbutler-git/src/bin/setsid.rs
Normal file
@ -0,0 +1,10 @@
|
||||
// NOTE(qix-): Cargo doesn't let us specify binaries based on the platform,
|
||||
// NOTE(qix-): unfortunately. This utility is not used on Windows but is
|
||||
// NOTE(qix-): build anyway. We'll address this at a later time.
|
||||
// NOTE(qix-):
|
||||
// NOTE(qix-): For now, we just stub out the main function on windows and panic.
|
||||
|
||||
#[cfg(unix)]
|
||||
include!("setsid/unix.rs");
|
||||
#[cfg(windows)]
|
||||
include!("setsid/windows.rs");
|
@ -1,16 +1,9 @@
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use nix::{
|
||||
libc::{c_int, wait, EXIT_FAILURE, WEXITSTATUS, WIFEXITED, WIFSIGNALED, WTERMSIG},
|
||||
unistd::{fork, setsid, ForkResult},
|
||||
};
|
||||
use std::{os::unix::process::CommandExt, process};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn main() {
|
||||
panic!("This binary is only meant to be run on Unix-like systems. It exists on Windows only because Cargo cannot switch off bins based on target platform.");
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn main() {
|
||||
let has_pipe_var = std::env::var("GITBUTLER_ASKPASS_PIPE")
|
||||
.map(|v| !v.is_empty())
|
3
gitbutler-git/src/bin/setsid/windows.rs
Normal file
3
gitbutler-git/src/bin/setsid/windows.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub fn main() {
|
||||
panic!("This binary is only meant to be run on Unix-like systems. It exists on Windows only because Cargo cannot switch off bins based on target platform.");
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
//! CLI-based (fork/exec) backend implementation,
|
||||
//! executing the `git` command-line tool available
|
||||
//! on `$PATH`.
|
||||
|
||||
mod executor;
|
||||
mod repository;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub use self::executor::Uid;
|
||||
|
||||
pub use self::{
|
||||
executor::{AskpassServer, FileStat, GitExecutor, Pid, Socket},
|
||||
repository::{fetch, push},
|
||||
};
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
pub use self::executor::tokio;
|
@ -1,38 +0,0 @@
|
||||
use std::io::{BufRead, BufReader, BufWriter, Write};
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
pub fn main(sock_path: &str, secret: &str, prompt: &str) {
|
||||
let raw_stream = UnixStream::connect(sock_path).expect("connect():");
|
||||
|
||||
// Set a timer for 10s.
|
||||
raw_stream
|
||||
.set_read_timeout(Some(std::time::Duration::from_secs(10)))
|
||||
.expect("set_read_timeout(Some):");
|
||||
|
||||
let mut reader = BufReader::new(raw_stream.try_clone().unwrap());
|
||||
let mut writer = BufWriter::new(raw_stream.try_clone().unwrap());
|
||||
|
||||
// Write the secret.
|
||||
writeln!(writer, "{secret}").expect("write(secret):");
|
||||
|
||||
// Write the prompt that Git gave us.
|
||||
writeln!(writer, "{prompt}").expect("write(prompt):");
|
||||
|
||||
writer.flush().expect("flush():");
|
||||
|
||||
// Clear the timeout (it's now time for the user to provide a response)
|
||||
raw_stream
|
||||
.set_read_timeout(None)
|
||||
.expect("set_read_timeout(None):");
|
||||
|
||||
// Wait for the response.
|
||||
let mut password = String::new();
|
||||
let nread = reader.read_line(&mut password).expect("read_line():");
|
||||
if nread == 0 {
|
||||
panic!("read_line() returned 0");
|
||||
}
|
||||
|
||||
// Write the response back to Git.
|
||||
// `password` already has a newline at the end.
|
||||
write!(std::io::stdout(), "{password}").expect("write(password):");
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[path = "askpass-unix.rs"]
|
||||
mod unix;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
compile_error!("Windows support is not yet implemented.");
|
||||
|
||||
pub fn main() {
|
||||
let pipe_name = std::env::var("GITBUTLER_ASKPASS_PIPE").expect("do not run this binary yourself; it's only meant to be run by GitButler (missing GITBUTLER_ASKPASS_PIPE env var)");
|
||||
let pipe_secret = std::env::var("GITBUTLER_ASKPASS_SECRET").expect("do not run this binary yourself; it's only meant to be run by GitButler (missing GITBUTLER_ASKPASS_SECRET env var)");
|
||||
let prompt = std::env::args().nth(1).expect("do not run this binary yourself; it's only meant to be run by GitButler (missing prompt arg)");
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
unix::main(&pipe_name, &pipe_secret, &prompt);
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
//! A [Tokio](https://tokio.rs)-based [`super::GitExecutor`] implementation.
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||
use std::{collections::HashMap, fs::Permissions, path::Path, time::Duration};
|
||||
use tokio::process::Command;
|
||||
|
||||
/// A [`super::GitExecutor`] implementation using the `git` command-line tool
|
||||
/// via [`tokio::process::Command`].
|
||||
pub struct TokioExecutor;
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl super::GitExecutor for TokioExecutor {
|
||||
type Error = std::io::Error;
|
||||
type ServerHandle = TokioAskpassServer;
|
||||
|
||||
async fn execute_raw<P: AsRef<Path>>(
|
||||
&self,
|
||||
args: &[&str],
|
||||
cwd: P,
|
||||
envs: Option<HashMap<String, String>>,
|
||||
) -> Result<(usize, String, String), Self::Error> {
|
||||
let mut cmd = Command::new("git");
|
||||
|
||||
// Output the command being executed to stderr, for debugging purposes
|
||||
// (only on test configs).
|
||||
#[cfg(any(test, debug_assertions))]
|
||||
{
|
||||
let mut envs_str = String::new();
|
||||
if let Some(envs) = &envs {
|
||||
for (key, value) in envs.iter() {
|
||||
envs_str.push_str(&format!("{key}={value:?} "));
|
||||
}
|
||||
}
|
||||
let args_str = args
|
||||
.iter()
|
||||
.map(|s| format!("{s:?}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
eprintln!("env {envs_str} git {args_str}");
|
||||
}
|
||||
|
||||
cmd.kill_on_drop(true);
|
||||
cmd.args(args);
|
||||
cmd.current_dir(cwd);
|
||||
|
||||
if let Some(envs) = envs {
|
||||
cmd.envs(envs);
|
||||
}
|
||||
|
||||
let output = cmd.output().await?;
|
||||
|
||||
#[cfg(any(test, debug_assertions))]
|
||||
{
|
||||
eprintln!(
|
||||
"\n\n GIT STDOUT:\n\n{}\n\nGIT STDERR:\n\n{}\n\nGIT EXIT CODE: {}\n",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr),
|
||||
output.status.code().unwrap_or(127) as usize
|
||||
);
|
||||
}
|
||||
|
||||
Ok((
|
||||
output.status.code().unwrap_or(127) as usize,
|
||||
String::from_utf8_lossy(&output.stdout).trim().into(),
|
||||
String::from_utf8_lossy(&output.stderr).trim().into(),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
async unsafe fn create_askpass_server(&self) -> Result<Self::ServerHandle, Self::Error> {
|
||||
let connection_string =
|
||||
std::env::temp_dir().join(format!("gitbutler-askpass-{}", rand::random::<u64>()));
|
||||
|
||||
let listener = tokio::net::UnixListener::bind(&connection_string)?;
|
||||
|
||||
tokio::fs::set_permissions(&connection_string, Permissions::from_mode(0o0600)).await?;
|
||||
|
||||
Ok(TokioAskpassServer {
|
||||
server: Some(listener),
|
||||
connection_string: connection_string.to_string_lossy().into(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn stat(&self, path: &str) -> Result<super::FileStat, Self::Error> {
|
||||
let metadata = tokio::fs::symlink_metadata(path).await?;
|
||||
|
||||
Ok(super::FileStat {
|
||||
dev: metadata.dev(),
|
||||
ino: metadata.ino(),
|
||||
is_regular_file: metadata.is_file(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl super::Socket for tokio::io::BufStream<tokio::net::UnixStream> {
|
||||
type Error = std::io::Error;
|
||||
|
||||
fn pid(&self) -> Result<super::Pid, Self::Error> {
|
||||
self.get_ref()
|
||||
.peer_cred()
|
||||
.unwrap()
|
||||
.pid()
|
||||
.ok_or(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"no pid available for peer connection",
|
||||
))
|
||||
}
|
||||
|
||||
fn uid(&self) -> Result<super::Uid, Self::Error> {
|
||||
Ok(self.get_ref().peer_cred().unwrap().uid())
|
||||
}
|
||||
|
||||
async fn read_line(&mut self) -> Result<String, Self::Error> {
|
||||
let mut buf = String::new();
|
||||
<Self as tokio::io::AsyncBufReadExt>::read_line(self, &mut buf).await?;
|
||||
Ok(buf.trim_end_matches(|c| c == '\r' || c == '\n').into())
|
||||
}
|
||||
|
||||
async fn write_line(&mut self, line: &str) -> Result<(), Self::Error> {
|
||||
<Self as tokio::io::AsyncWriteExt>::write_all(self, line.as_bytes()).await?;
|
||||
<Self as tokio::io::AsyncWriteExt>::write_all(self, b"\n").await?;
|
||||
<Self as tokio::io::AsyncWriteExt>::flush(self).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A tokio-based [`super::AskpassServer`] implementation.
|
||||
#[cfg(unix)]
|
||||
pub struct TokioAskpassServer {
|
||||
// Always Some until dropped.
|
||||
server: Option<tokio::net::UnixListener>,
|
||||
connection_string: String,
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl super::AskpassServer for TokioAskpassServer {
|
||||
type Error = std::io::Error;
|
||||
#[cfg(unix)]
|
||||
type SocketHandle = tokio::io::BufStream<tokio::net::UnixStream>;
|
||||
|
||||
async fn accept(&self, timeout: Option<Duration>) -> Result<Self::SocketHandle, Self::Error> {
|
||||
let res = if let Some(timeout) = timeout {
|
||||
tokio::time::timeout(timeout, self.server.as_ref().unwrap().accept()).await?
|
||||
} else {
|
||||
self.server.as_ref().unwrap().accept().await
|
||||
};
|
||||
|
||||
res.map(|(s, _)| tokio::io::BufStream::new(s))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl core::fmt::Display for TokioAskpassServer {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
self.connection_string.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl Drop for TokioAskpassServer {
|
||||
fn drop(&mut self) {
|
||||
drop(self.server.take());
|
||||
// best-effort
|
||||
std::fs::remove_file(&self.connection_string).ok();
|
||||
}
|
||||
}
|
@ -142,7 +142,7 @@ pub unsafe trait GitExecutor {
|
||||
/// during askpass authentication.
|
||||
///
|
||||
/// **Do not follow symbolic links.**
|
||||
async fn stat(&self, path: &str) -> Result<FileStat, Self::Error>;
|
||||
async fn stat<P: AsRef<Path>>(&self, path: P) -> Result<FileStat, Self::Error>;
|
||||
}
|
||||
|
||||
/// Stats for a file on the filesystem.
|
108
gitbutler-git/src/executor/tokio.rs
Normal file
108
gitbutler-git/src/executor/tokio.rs
Normal file
@ -0,0 +1,108 @@
|
||||
//! A [Tokio](https://tokio.rs)-based Git executor implementation.
|
||||
|
||||
#[cfg(unix)]
|
||||
mod unix;
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
|
||||
use std::{collections::HashMap, path::Path};
|
||||
use tokio::process::Command;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub use self::unix::TokioAskpassServer;
|
||||
#[cfg(windows)]
|
||||
pub use self::windows::TokioAskpassServer;
|
||||
|
||||
/// A Git executor implementation using the `git` command-line tool
|
||||
/// via [`tokio::process::Command`].
|
||||
pub struct TokioExecutor;
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl super::GitExecutor for TokioExecutor {
|
||||
type Error = std::io::Error;
|
||||
type ServerHandle = TokioAskpassServer;
|
||||
|
||||
async fn execute_raw<P: AsRef<Path>>(
|
||||
&self,
|
||||
args: &[&str],
|
||||
cwd: P,
|
||||
envs: Option<HashMap<String, String>>,
|
||||
) -> Result<(usize, String, String), Self::Error> {
|
||||
let mut cmd = Command::new({
|
||||
#[cfg(unix)]
|
||||
{
|
||||
"git"
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
"git.exe"
|
||||
}
|
||||
});
|
||||
|
||||
// Output the command being executed to stderr, for debugging purposes
|
||||
// (only on test configs).
|
||||
#[cfg(any(test, debug_assertions))]
|
||||
{
|
||||
let mut envs_str = String::new();
|
||||
if let Some(envs) = &envs {
|
||||
for (key, value) in envs.iter() {
|
||||
envs_str.push_str(&format!("{key}={value:?} "));
|
||||
}
|
||||
}
|
||||
let args_str = args
|
||||
.iter()
|
||||
.map(|s| format!("{s:?}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
eprintln!("env {envs_str} git {args_str}");
|
||||
}
|
||||
|
||||
cmd.kill_on_drop(true);
|
||||
cmd.args(args);
|
||||
cmd.current_dir(cwd);
|
||||
|
||||
if let Some(envs) = envs {
|
||||
cmd.envs(envs);
|
||||
}
|
||||
|
||||
let output = cmd.output().await?;
|
||||
|
||||
#[cfg(any(test, debug_assertions))]
|
||||
{
|
||||
eprintln!(
|
||||
"\n\n GIT STDOUT:\n\n{}\n\nGIT STDERR:\n\n{}\n\nGIT EXIT CODE: {}\n",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr),
|
||||
output.status.code().unwrap_or(127) as usize
|
||||
);
|
||||
}
|
||||
|
||||
Ok((
|
||||
output.status.code().unwrap_or(127) as usize,
|
||||
String::from_utf8_lossy(&output.stdout).trim().into(),
|
||||
String::from_utf8_lossy(&output.stderr).trim().into(),
|
||||
))
|
||||
}
|
||||
|
||||
async unsafe fn create_askpass_server(&self) -> Result<Self::ServerHandle, Self::Error> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
Self::ServerHandle::new().await
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
Self::ServerHandle::new()
|
||||
}
|
||||
}
|
||||
|
||||
async fn stat<P: AsRef<Path>>(&self, path: P) -> Result<super::FileStat, Self::Error> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
self::unix::stat(path).await
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
self::windows::stat(path).await
|
||||
}
|
||||
}
|
||||
}
|
106
gitbutler-git/src/executor/tokio/unix.rs
Normal file
106
gitbutler-git/src/executor/tokio/unix.rs
Normal file
@ -0,0 +1,106 @@
|
||||
use crate::executor::{AskpassServer, FileStat, Pid, Socket, Uid};
|
||||
use std::{
|
||||
fs::Permissions,
|
||||
os::unix::fs::{MetadataExt, PermissionsExt},
|
||||
path::Path,
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::{
|
||||
io::{AsyncBufReadExt, AsyncWriteExt, BufStream},
|
||||
net::{UnixListener, UnixStream},
|
||||
};
|
||||
|
||||
impl Socket for BufStream<UnixStream> {
|
||||
type Error = std::io::Error;
|
||||
|
||||
fn pid(&self) -> Result<Pid, Self::Error> {
|
||||
self.get_ref()
|
||||
.peer_cred()
|
||||
.unwrap()
|
||||
.pid()
|
||||
.ok_or(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"no pid available for peer connection",
|
||||
))
|
||||
}
|
||||
|
||||
fn uid(&self) -> Result<Uid, Self::Error> {
|
||||
Ok(self.get_ref().peer_cred().unwrap().uid())
|
||||
}
|
||||
|
||||
async fn read_line(&mut self) -> Result<String, Self::Error> {
|
||||
let mut buf = String::new();
|
||||
<Self as AsyncBufReadExt>::read_line(self, &mut buf).await?;
|
||||
Ok(buf.trim_end_matches(|c| c == '\r' || c == '\n').into())
|
||||
}
|
||||
|
||||
async fn write_line(&mut self, line: &str) -> Result<(), Self::Error> {
|
||||
<Self as AsyncWriteExt>::write_all(self, line.as_bytes()).await?;
|
||||
<Self as AsyncWriteExt>::write_all(self, b"\n").await?;
|
||||
<Self as AsyncWriteExt>::flush(self).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A tokio-based askpass server implementation.
|
||||
pub struct TokioAskpassServer {
|
||||
// Always Some until dropped.
|
||||
server: Option<UnixListener>,
|
||||
connection_string: String,
|
||||
}
|
||||
|
||||
impl TokioAskpassServer {
|
||||
pub(crate) async fn new() -> Result<Self, std::io::Error> {
|
||||
let connection_string =
|
||||
std::env::temp_dir().join(format!("gitbutler-askpass-{}", rand::random::<u64>()));
|
||||
|
||||
let listener = UnixListener::bind(&connection_string)?;
|
||||
|
||||
tokio::fs::set_permissions(&connection_string, Permissions::from_mode(0o0600)).await?;
|
||||
|
||||
Ok(TokioAskpassServer {
|
||||
server: Some(listener),
|
||||
connection_string: connection_string.to_string_lossy().into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AskpassServer for TokioAskpassServer {
|
||||
type Error = std::io::Error;
|
||||
type SocketHandle = BufStream<UnixStream>;
|
||||
|
||||
async fn accept(&self, timeout: Option<Duration>) -> Result<Self::SocketHandle, Self::Error> {
|
||||
let res = if let Some(timeout) = timeout {
|
||||
tokio::time::timeout(timeout, self.server.as_ref().unwrap().accept()).await?
|
||||
} else {
|
||||
self.server.as_ref().unwrap().accept().await
|
||||
};
|
||||
|
||||
res.map(|(s, _)| BufStream::new(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for TokioAskpassServer {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
self.connection_string.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TokioAskpassServer {
|
||||
fn drop(&mut self) {
|
||||
drop(self.server.take());
|
||||
// best-effort
|
||||
std::fs::remove_file(&self.connection_string).ok();
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn stat<P: AsRef<Path>>(path: P) -> Result<FileStat, std::io::Error> {
|
||||
let metadata = tokio::fs::symlink_metadata(path).await?;
|
||||
|
||||
Ok(FileStat {
|
||||
dev: metadata.dev(),
|
||||
ino: metadata.ino(),
|
||||
is_regular_file: metadata.is_file(),
|
||||
})
|
||||
}
|
131
gitbutler-git/src/executor/tokio/windows.rs
Normal file
131
gitbutler-git/src/executor/tokio/windows.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use crate::executor::{AskpassServer, FileStat, Pid, Socket};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
os::windows::{fs::MetadataExt, io::AsRawHandle},
|
||||
path::Path,
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::{
|
||||
io::{AsyncBufReadExt, AsyncWriteExt, BufStream},
|
||||
net::windows::named_pipe::{NamedPipeServer, ServerOptions},
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
const ASKPASS_PIPE_PREFIX: &str = r"\\.\pipe\gitbutler-askpass-";
|
||||
|
||||
impl Socket for BufStream<NamedPipeServer> {
|
||||
type Error = std::io::Error;
|
||||
|
||||
fn pid(&self) -> Result<Pid, Self::Error> {
|
||||
let raw_handle = self.get_ref().as_raw_handle();
|
||||
let mut out_pid: winapi::shared::minwindef::ULONG = 0;
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
let r = unsafe {
|
||||
winapi::um::winbase::GetNamedPipeClientProcessId(
|
||||
// We need the `as` here to make rustdoc shut up
|
||||
// about winapi using different type defs for docs.
|
||||
raw_handle as winapi::um::winnt::HANDLE,
|
||||
&mut out_pid,
|
||||
)
|
||||
};
|
||||
|
||||
if r == 0 {
|
||||
Err(std::io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(Pid::from(out_pid))
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_line(&mut self) -> Result<String, Self::Error> {
|
||||
let mut buf = String::new();
|
||||
<Self as AsyncBufReadExt>::read_line(self, &mut buf).await?;
|
||||
Ok(buf.trim_end_matches(|c| c == '\r' || c == '\n').into())
|
||||
}
|
||||
|
||||
async fn write_line(&mut self, line: &str) -> Result<(), Self::Error> {
|
||||
<Self as AsyncWriteExt>::write_all(self, line.as_bytes()).await?;
|
||||
<Self as AsyncWriteExt>::write_all(self, b"\n").await?;
|
||||
<Self as AsyncWriteExt>::flush(self).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A server for the `askpass` protocol using Tokio.
|
||||
pub struct TokioAskpassServer {
|
||||
server: Mutex<RefCell<NamedPipeServer>>,
|
||||
connection_string: String,
|
||||
}
|
||||
|
||||
impl TokioAskpassServer {
|
||||
pub(crate) fn new() -> Result<Self, std::io::Error> {
|
||||
let connection_string = format!("{ASKPASS_PIPE_PREFIX}{}", rand::random::<u64>());
|
||||
|
||||
let server = Mutex::new(RefCell::new(
|
||||
ServerOptions::new()
|
||||
.first_pipe_instance(true)
|
||||
.create(&connection_string)?,
|
||||
));
|
||||
|
||||
Ok(TokioAskpassServer {
|
||||
server,
|
||||
connection_string,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AskpassServer for TokioAskpassServer {
|
||||
type Error = std::io::Error;
|
||||
type SocketHandle = BufStream<NamedPipeServer>;
|
||||
|
||||
// We can ignore clippy here since we locked the mutex.
|
||||
#[allow(clippy::await_holding_refcell_ref)]
|
||||
async fn accept(&self, timeout: Option<Duration>) -> Result<Self::SocketHandle, Self::Error> {
|
||||
let server = self.server.lock().await;
|
||||
|
||||
if let Some(timeout) = timeout {
|
||||
tokio::time::timeout(timeout, server.borrow().connect()).await??;
|
||||
} else {
|
||||
server.borrow().connect().await?;
|
||||
}
|
||||
|
||||
// Windows is weird. The server becomes the peer connection,
|
||||
// and before we use the new connection, we first create
|
||||
// a new server to listen for the next connection.
|
||||
let client = server.replace(ServerOptions::new().create(&self.connection_string)?);
|
||||
|
||||
Ok(tokio::io::BufStream::new(client))
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for TokioAskpassServer {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
self.connection_string.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TokioAskpassServer {
|
||||
fn drop(&mut self) {
|
||||
// Best effort
|
||||
let _ = self.server.get_mut().get_mut().disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn stat<P: AsRef<Path>>(path: P) -> Result<FileStat, std::io::Error> {
|
||||
let metadata = tokio::fs::symlink_metadata(path).await?;
|
||||
|
||||
// NOTE(qix-): We can safely unwrap here since the docs say:
|
||||
// NOTE(qix-):
|
||||
// NOTE(qix-): > This will return `None`` if the Metadata instance was created
|
||||
// NOTE(qix-): > from a call to `DirEntry::metadata`. If this `Metadata` was created
|
||||
// NOTE(qix-): > by using `fs::metadata` or `File::metadata`, then this will return `Some`.
|
||||
// NOTE(qix-):
|
||||
// NOTE(qix-): Thus, since we're not using directory entries, these are guaranteed to
|
||||
// NOTE(qix-): return `Some`.
|
||||
Ok(FileStat {
|
||||
dev: metadata.volume_serial_number().unwrap().into(),
|
||||
ino: metadata.file_index().unwrap(),
|
||||
is_regular_file: metadata.is_file(),
|
||||
})
|
||||
}
|
@ -7,14 +7,19 @@
|
||||
#![deny(missing_docs, unsafe_code)]
|
||||
#![allow(async_fn_in_trait)]
|
||||
#![cfg_attr(test, feature(async_closure))]
|
||||
#![cfg_attr(windows, feature(windows_by_handle))]
|
||||
#![feature(impl_trait_in_assoc_type)]
|
||||
|
||||
mod cli;
|
||||
mod error;
|
||||
pub(crate) mod executor;
|
||||
mod refspec;
|
||||
mod repository;
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
pub use self::executor::tokio;
|
||||
|
||||
pub use self::{
|
||||
cli::*,
|
||||
error::Error,
|
||||
refspec::{Error as RefSpecError, RefSpec},
|
||||
repository::{fetch, push},
|
||||
};
|
||||
|
@ -88,11 +88,20 @@ where
|
||||
let path = path.parent().unwrap();
|
||||
|
||||
let askpath_path = path
|
||||
.with_file_name("gitbutler-git-askpass")
|
||||
.with_file_name({
|
||||
#[cfg(unix)]
|
||||
{
|
||||
"gitbutler-git-askpass"
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
"gitbutler-git-askpass.exe"
|
||||
}
|
||||
})
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[cfg(unix)]
|
||||
let setsid_path = path
|
||||
.with_file_name("gitbutler-git-setsid")
|
||||
.to_string_lossy()
|
||||
@ -103,7 +112,7 @@ where
|
||||
.await
|
||||
.map_err(Error::<E>::Exec)?;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[cfg(unix)]
|
||||
let setsid_stat = executor
|
||||
.stat(&setsid_path)
|
||||
.await
|
||||
@ -150,11 +159,11 @@ where
|
||||
format!(
|
||||
"{}{base_ssh_command} -o StrictHostKeyChecking=accept-new -o KbdInteractiveAuthentication=no{}",
|
||||
{
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[cfg(unix)]
|
||||
{
|
||||
format!("'{setsid_path}' ")
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(windows)]
|
||||
{
|
||||
""
|
||||
}
|
||||
@ -198,6 +207,9 @@ where
|
||||
// TODO(qix-): see if dropping sysinfo for a more bespoke implementation is worth it.
|
||||
let mut system = sysinfo::System::new();
|
||||
system.refresh_processes();
|
||||
|
||||
// We can ignore clippy here since the type is different depending on the platform.
|
||||
#[allow(clippy::useless_conversion)]
|
||||
let peer_path = system
|
||||
.process(sysinfo::Pid::from_u32(peer_pid.try_into().map_err(|_| Error::<E>::NoSuchPid(peer_pid))?))
|
||||
.and_then(|p| p.exe().map(|exe| exe.to_string_lossy().into_owned()))
|
||||
@ -206,15 +218,28 @@ where
|
||||
// stat the askpass executable that is being invoked
|
||||
let peer_stat = executor.stat(&peer_path).await.map_err(Error::<E>::Exec)?;
|
||||
|
||||
if peer_stat.ino == askpath_stat.ino {
|
||||
let valid_executable = if peer_stat.ino == askpath_stat.ino {
|
||||
if peer_stat.dev != askpath_stat.dev {
|
||||
return Err(Error::<E>::AskpassDeviceMismatch)?;
|
||||
}
|
||||
} else if peer_stat.ino == setsid_stat.ino {
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
let valid_executable = valid_executable || if peer_stat.ino == setsid_stat.ino {
|
||||
if peer_stat.dev != setsid_stat.dev {
|
||||
return Err(Error::<E>::AskpassDeviceMismatch)?;
|
||||
}
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !valid_executable {
|
||||
return Err(Error::<E>::AskpassExecutableMismatch)?;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user