WIP: Windows support #925

Draft
mediocregopher wants to merge 11 commits from mediocregopher/garage:windows-v1 into main
7 changed files with 116 additions and 32 deletions

View file

@ -1,4 +1,6 @@
let
{
system ? builtins.currentSystem,
}: let
lock = builtins.fromJSON (builtins.readFile ../flake.lock);
inherit (lock.nodes.flake-compat.locked) owner repo rev narHash;
@ -8,7 +10,7 @@ let
sha256 = narHash;
};
flake = (import flake-compat { system = builtins.currentSystem; src = ../.; });
flake = (import flake-compat { inherit system; src = ../.; });
in
{

View file

@ -16,9 +16,13 @@
let
log = v: builtins.trace v v;
targetIsWindows = target == "x86_64-w64-mingw32";
# NixOS and Rust/Cargo triples do not match for ARM, fix it here.
rustTarget = if target == "armv6l-unknown-linux-musleabihf" then
"arm-unknown-linux-musleabihf"
else if targetIsWindows then
"x86_64-pc-windows-gnu"
else
target;
@ -27,6 +31,7 @@ let
"aarch64-unknown-linux-musl" = "AARCH64_UNKNOWN_LINUX_MUSL";
"i686-unknown-linux-musl" = "I686_UNKNOWN_LINUX_MUSL";
"arm-unknown-linux-musleabihf" = "ARM_UNKNOWN_LINUX_MUSLEABIHF";
"x86_64-pc-windows-gnu" = "X86_64_PC_WINDOWS_GNU";
};
pkgsNative = import nixpkgs {
@ -102,12 +107,29 @@ let
"target-feature=+crt-static"
"link-arg=-static"
]; # compile as dynamic with static-pie
"x86_64-w64-mingw32" = [
"target-feature=+crt-static"
"link-arg=-static-pie"
];
};
codegenOpts = if target != null then codegenOptsMap.${target} else [
"link-arg=-fuse-ld=mold"
];
# HACK: work around https://github.com/NixOS/nixpkgs/issues/177129
# Though this is an issue between Clang and GCC,
# so it may not get fixed anytime soon...
empty-libgcc_eh = pkgsNative.stdenv.mkDerivation {
pname = "empty-libgcc_eh";
version = "0";
dontUnpack = true;
installPhase = ''
mkdir -p "$out"/lib
"${pkgsNative.binutils}"/bin/ar r "$out"/lib/libgcc_eh.a
'';
};
commonArgs =
{
inherit src;
@ -126,6 +148,11 @@ let
pkgs.mold
];
buildInputs = lib.optionals targetIsWindows [
empty-libgcc_eh
pkgs.windows.pthreads
];
CARGO_PROFILE = if release then "release" else "dev";
CARGO_BUILD_RUSTFLAGS =
lib.concatStringsSep
@ -142,7 +169,14 @@ let
TARGET_CC = "${stdenv.cc.targetPrefix}cc";
} else {
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER = "clang";
});
})
//
(if targetIsWindows then {
# libsodium-sys doesn't consider windows-gnu to be a "supported" build
# target, even though libsodium itself releases official mingw builds. Use
# nix's own libsodium, via a special envvar, instead.
SODIUM_LIB_DIR = "${pkgs.libsodium}/lib";
} else {});
in rec {
toolchain = toolchainFn pkgs;

View file

@ -1,6 +1,4 @@
use std::convert::Infallible;
use std::fs::{self, Permissions};
use std::os::unix::fs::PermissionsExt;
use std::sync::Arc;
use std::time::Duration;
@ -16,7 +14,7 @@ use hyper::{HeaderMap, StatusCode};
use hyper_util::rt::TokioIo;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::net::{TcpListener, TcpStream, UnixListener, UnixStream};
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::watch;
use tokio::time::{sleep_until, Instant};
@ -116,7 +114,13 @@ impl<A: ApiHandler> ApiServer<A> {
let handler = move |request, socketaddr| self.clone().handler(request, socketaddr);
server_loop(server_name, listener, handler, must_exit).await
}
#[cfg(not(windows))]
UnixOrTCPSocketAddress::UnixSocket(ref path) => {
use std::fs::{self, Permissions};
use std::os::unix::fs::PermissionsExt;
use tokio::net::UnixListener;
if path.exists() {
fs::remove_file(path)?
}
@ -132,6 +136,11 @@ impl<A: ApiHandler> ApiServer<A> {
let handler = move |request, socketaddr| self.clone().handler(request, socketaddr);
server_loop(server_name, listener, handler, must_exit).await
}
#[cfg(windows)]
UnixOrTCPSocketAddress::UnixSocket(ref _path) => {
panic!("Unix domain sockets are not supported on windows yet: https://github.com/tokio-rs/tokio/issues/2201");
}
}
}
@ -259,10 +268,12 @@ impl Accept for TcpListener {
}
}
pub struct UnixListenerOn(pub UnixListener, pub String);
#[cfg(not(windows))]
pub struct UnixListenerOn(pub tokio::net::UnixListener, pub String);
#[cfg(not(windows))]
impl Accept for UnixListenerOn {
type Stream = UnixStream;
type Stream = tokio::net::UnixStream;
async fn accept(&self) -> std::io::Result<(Self::Stream, String)> {
self.0
.accept()

View file

@ -792,11 +792,7 @@ impl BlockManagerLocked {
// Now, we do an fsync on the containing directory, to ensure that the rename
// is persisted properly. See:
// http://thedjbway.b0llix.net/qmail/syncdir.html
let dir = fs::OpenOptions::new()
.read(true)
.mode(0)
.open(directory)
.await?;
let dir = fs::OpenOptions::new().read(true).open(directory).await?;
dir.sync_all().await?;
drop(dir);
}

View file

@ -52,10 +52,14 @@ pub struct Secrets {
/// from config or CLI param or env variable or read from a file specified in config or CLI
/// param or env variable)
pub fn fill_secrets(mut config: Config, secrets: Secrets) -> Result<Config, Error> {
#[cfg(unix)]
let allow_world_readable = secrets
.allow_world_readable_secrets
.unwrap_or(config.allow_world_readable_secrets);
#[cfg(not(unix))]
let allow_world_readable = config.allow_world_readable_secrets;
fill_secret(
&mut config.rpc_secret,
&config.rpc_secret_file,

View file

@ -218,11 +218,21 @@ pub fn gen_node_key(metadata_dir: &Path) -> Result<NodeKey, Error> {
let (pubkey, key) = ed25519::gen_keypair();
{
use std::os::unix::fs::PermissionsExt;
let mut f = std::fs::File::create(key_file.as_path())?;
let mut perm = f.metadata()?.permissions();
perm.set_mode(0o600);
std::fs::set_permissions(key_file.as_path(), perm)?;
#[cfg(not(windows))]
{
use std::os::unix::fs::PermissionsExt;
let mut perm = f.metadata()?.permissions();
perm.set_mode(0o600);
std::fs::set_permissions(key_file.as_path(), perm)?;
}
#[cfg(windows)]
{
// TODO(mediocregopher): set permissions on windows
}
f.write_all(&key[..])?;
}
@ -803,6 +813,7 @@ impl NodeStatus {
}
}
#[cfg(not(windows))]
fn update_disk_usage(&mut self, meta_dir: &Path, data_dir: &DataDirEnum) {
use nix::sys::statvfs::statvfs;
@ -864,22 +875,38 @@ impl NodeStatus {
})(),
};
}
#[cfg(windows)]
fn update_disk_usage(&mut self, _meta_dir: &Path, _data_dir: &DataDirEnum) {
// TODO(mediocregopher): update disk usage on windows, only used for metrics
}
}
/// Obtain the list of currently available IP addresses on all non-loopback
/// interfaces, optionally filtering them to be inside a given IpNet.
fn get_default_ip(filter_ipnet: Option<ipnet::IpNet>) -> Option<IpAddr> {
pnet_datalink::interfaces()
.into_iter()
// filter down and loopback interfaces
.filter(|i| i.is_up() && !i.is_loopback())
// get all IPs
.flat_map(|e| e.ips)
// optionally, filter to be inside filter_ipnet
.find(|ipn| {
filter_ipnet.is_some_and(|ipnet| ipnet.contains(&ipn.ip())) || filter_ipnet.is_none()
})
.map(|ipn| ipn.ip())
// pnet creates a dependency on winpcap, which is a giant pain to get working with
// cross-compilation.
#[cfg(windows)]
{
None
}
#[cfg(not(windows))]
{
pnet_datalink::interfaces()
.into_iter()
// filter down and loopback interfaces
.filter(|i| i.is_up() && !i.is_loopback())
// get all IPs
.flat_map(|e| e.ips)
// optionally, filter to be inside filter_ipnet
.find(|ipn| {
filter_ipnet.is_some_and(|ipnet| ipnet.contains(&ipn.ip()))
|| filter_ipnet.is_none()
})
.map(|ipn| ipn.ip())
}
}
fn get_rpc_public_addr(config: &Config) -> Option<SocketAddr> {

View file

@ -1,8 +1,6 @@
use std::fs::{self, Permissions};
use std::os::unix::prelude::PermissionsExt;
use std::sync::Arc;
use tokio::net::{TcpListener, UnixListener};
use tokio::net::TcpListener;
use tokio::sync::watch;
use hyper::{
@ -23,7 +21,7 @@ use crate::error::*;
use garage_api_common::cors::{
add_cors_headers, find_matching_cors_rule, handle_options_for_bucket,
};
use garage_api_common::generic_server::{server_loop, UnixListenerOn};
use garage_api_common::generic_server::server_loop;
use garage_api_common::helpers::*;
use garage_api_s3::error::{
CommonErrorDerivative, Error as ApiError, OkOrBadRequest, OkOrInternalError,
@ -102,7 +100,14 @@ impl WebServer {
move |stream, socketaddr| self.clone().handle_request(stream, socketaddr);
server_loop(server_name, listener, handler, must_exit).await
}
#[cfg(not(windows))]
UnixOrTCPSocketAddress::UnixSocket(ref path) => {
use garage_api_common::generic_server::UnixListenerOn;
use std::fs::{self, Permissions};
use std::os::unix::prelude::PermissionsExt;
use tokio::net::UnixListener;
if path.exists() {
fs::remove_file(path)?
}
@ -116,6 +121,11 @@ impl WebServer {
move |stream, socketaddr| self.clone().handle_request(stream, socketaddr);
server_loop(server_name, listener, handler, must_exit).await
}
#[cfg(windows)]
UnixOrTCPSocketAddress::UnixSocket(ref _path) => {
panic!("Unix domain sockets are not supported on windows yet: https://github.com/tokio-rs/tokio/issues/2201");
}
}
}