mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-30 11:32:04 +03:00
Fix askpass pipe on windows
This commit is contained in:
parent
50e3131040
commit
28668e1b2c
@ -203,7 +203,7 @@ This paragraph is about crosscompilation to x86_64-MSVC from ARM Windows,
|
||||
a configuration typical for people with Apple Silicon and Parallels VMs,
|
||||
which only allow ARM Windows to be used.
|
||||
|
||||
The `winapi` dependency on `gitbutler-git` doesn't currently compile on ARM,
|
||||
The `windows` dependency on `gitbutler-git` doesn't currently compile on ARM,
|
||||
which means cross-compilation to x86-64 is required to workaround that. Besides,
|
||||
most users will probably still be on INTEL machines, making this capability
|
||||
a common requirement.
|
||||
|
@ -44,10 +44,16 @@ sysinfo = "0.30.12"
|
||||
nix = { version = "0.29.0", features = ["process", "socket", "user"] }
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
winapi = { version = "0.3.9", features = ["winbase", "namedpipeapi"] }
|
||||
windows = { version = "0.57.0", features = [
|
||||
"Win32",
|
||||
"Win32_System",
|
||||
"Win32_System_Pipes",
|
||||
"Win32_Storage",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_Security",
|
||||
"Win32_System_IO",
|
||||
] }
|
||||
tokio = { workspace = true, optional = true, features = ["sync"] }
|
||||
# synchronous named pipes for the askpass utility
|
||||
windows-named-pipe = "0.1.0"
|
||||
|
||||
[lints.clippy]
|
||||
all = "deny"
|
||||
|
124
crates/gitbutler-git/src/bin/askpass/windows-pipe.rs
Normal file
124
crates/gitbutler-git/src/bin/askpass/windows-pipe.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use std::ffi::OsString;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
|
||||
use std::path::Path;
|
||||
use windows::core::PWSTR;
|
||||
use windows::Win32::Foundation::{
|
||||
ERROR_PIPE_NOT_CONNECTED, GENERIC_READ, GENERIC_WRITE, HANDLE, WIN32_ERROR,
|
||||
};
|
||||
use windows::Win32::Storage::FileSystem::{
|
||||
CreateFileW, FlushFileBuffers, ReadFile, WriteFile, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
|
||||
FILE_SHARE_WRITE, OPEN_EXISTING,
|
||||
};
|
||||
use windows::Win32::System::Pipes::{WaitNamedPipeW, NMPWAIT_USE_DEFAULT_WAIT};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Handle {
|
||||
inner: HANDLE,
|
||||
}
|
||||
|
||||
unsafe impl Send for Handle {}
|
||||
unsafe impl Sync for Handle {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Pipe {
|
||||
handle: Handle,
|
||||
}
|
||||
|
||||
impl Pipe {
|
||||
pub fn connect(path: &Path) -> io::Result<Pipe> {
|
||||
let mut os_str: OsString = path.as_os_str().into();
|
||||
os_str.push("\x00");
|
||||
let mut wide_path: Vec<u16> = os_str.encode_wide().collect();
|
||||
|
||||
let pwstr_path = PWSTR(wide_path.as_mut_ptr());
|
||||
let _ = unsafe { WaitNamedPipeW(pwstr_path, NMPWAIT_USE_DEFAULT_WAIT) };
|
||||
let handle_res = unsafe {
|
||||
CreateFileW(
|
||||
pwstr_path,
|
||||
GENERIC_READ.0 | GENERIC_WRITE.0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
None,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
None,
|
||||
)
|
||||
};
|
||||
|
||||
match handle_res {
|
||||
Ok(handle) => Ok(Pipe {
|
||||
handle: Handle { inner: handle },
|
||||
}),
|
||||
Err(err) => Err(io::Error::from_raw_os_error(err.code().0)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_handle(&self) -> HANDLE {
|
||||
self.handle.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Pipe {
|
||||
fn drop(&mut self) {
|
||||
let _ = unsafe { FlushFileBuffers(self.handle.inner) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Pipe {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let mut bytes_read = 0u32;
|
||||
let res = unsafe { ReadFile(self.handle.inner, Some(buf), Some(&mut bytes_read), None) };
|
||||
match res {
|
||||
Ok(_) => Ok(bytes_read as usize),
|
||||
Err(err) => match WIN32_ERROR::from_error(&err) {
|
||||
Some(ERROR_PIPE_NOT_CONNECTED) => Ok(0),
|
||||
_ => Err(io::Error::from_raw_os_error(err.code().0)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Pipe {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let mut bytes_written = 0u32;
|
||||
|
||||
let res =
|
||||
unsafe { WriteFile(self.handle.inner, Some(buf), Some(&mut bytes_written), None) };
|
||||
|
||||
match res {
|
||||
Ok(_) => Ok(bytes_written as usize),
|
||||
Err(err) => Err(io::Error::from_raw_os_error(err.code().0)),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
let res = unsafe { FlushFileBuffers(self.handle.inner) };
|
||||
|
||||
match res {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => Err(io::Error::from_raw_os_error(err.code().0)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawHandle for Pipe {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
self.handle.inner.0 as RawHandle
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawHandle for Pipe {
|
||||
fn into_raw_handle(self) -> RawHandle {
|
||||
self.handle.inner.0 as RawHandle
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawHandle for Pipe {
|
||||
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
|
||||
let handle = HANDLE(handle as isize);
|
||||
Pipe {
|
||||
handle: Handle { inner: handle },
|
||||
}
|
||||
}
|
||||
}
|
@ -3,10 +3,13 @@ use std::{
|
||||
os::windows::io::{AsRawHandle, FromRawHandle},
|
||||
time::Duration,
|
||||
};
|
||||
use windows_named_pipe::PipeStream;
|
||||
use windows::Win32::System::Pipes::SetNamedPipeHandleState;
|
||||
#[path = "windows-pipe.rs"]
|
||||
mod windows_pipe;
|
||||
use windows_pipe::Pipe;
|
||||
|
||||
pub fn establish(sock_path: &str) -> PipeStream {
|
||||
PipeStream::connect(sock_path).unwrap()
|
||||
pub fn establish(sock_path: &str) -> Pipe {
|
||||
Pipe::connect(std::path::Path::new(sock_path)).unwrap()
|
||||
}
|
||||
|
||||
/// There are some methods we need in order to run askpass correctly,
|
||||
@ -18,7 +21,7 @@ pub trait UnixCompatibility: Sized {
|
||||
fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl UnixCompatibility for PipeStream {
|
||||
impl UnixCompatibility for Pipe {
|
||||
fn try_clone(&self) -> Option<Self> {
|
||||
Some(unsafe { Self::from_raw_handle(self.as_raw_handle()) })
|
||||
}
|
||||
@ -32,28 +35,14 @@ impl UnixCompatibility for PipeStream {
|
||||
// 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 timeout_ms: Option<*const u32> =
|
||||
timeout.map(|timeout| timeout.as_millis() as *const u32);
|
||||
|
||||
let r = unsafe {
|
||||
winapi::um::namedpipeapi::SetNamedPipeHandleState(
|
||||
self.as_raw_handle() as winapi::um::winnt::HANDLE,
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
timeout_ptr,
|
||||
)
|
||||
};
|
||||
let r = unsafe { SetNamedPipeHandleState(self.get_handle(), None, None, timeout_ms) };
|
||||
|
||||
if r == 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(())
|
||||
match r {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => Err(io::Error::from_raw_os_error(err.code().0)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,30 +10,25 @@ use tokio::{
|
||||
net::windows::named_pipe::{NamedPipeServer, ServerOptions},
|
||||
sync::Mutex,
|
||||
};
|
||||
use windows::Win32::{Foundation::HANDLE, System::Pipes::GetNamedPipeClientProcessId};
|
||||
|
||||
const ASKPASS_PIPE_PREFIX: &str = r"\\.\pipe\gitbutler-askpass-";
|
||||
// Slashes instead of backslashes to prevent any issues with escaping.
|
||||
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;
|
||||
let handle: HANDLE = HANDLE(raw_handle as isize);
|
||||
let mut out_pid: u32 = 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,
|
||||
)
|
||||
};
|
||||
let r = unsafe { GetNamedPipeClientProcessId(handle, &mut out_pid) };
|
||||
|
||||
if r == 0 {
|
||||
Err(std::io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(Pid::from(out_pid))
|
||||
match r {
|
||||
Err(err) => Err(std::io::Error::from_raw_os_error(err.code().0)),
|
||||
Ok(_) => Ok(Pid::from(out_pid)),
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user