From 6a436267a473a1bbcfa58484e6eac7ae59752611 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Thu, 9 Jan 2025 22:05:41 +0100 Subject: [PATCH 1/7] Allow passing system into common.nix, rather than always using builtin.currentSystem --- nix/common.nix | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nix/common.nix b/nix/common.nix index 1ad809bb..b922dd39 100644 --- a/nix/common.nix +++ b/nix/common.nix @@ -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 rec { pkgsSrc = flake.defaultNix.inputs.nixpkgs; -- 2.45.2 From c2b3cf521aeeb9a29c5b8c2f065eaf300aab009c Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Tue, 14 Jan 2025 10:29:28 +0100 Subject: [PATCH 2/7] windows: Don't set private key perms or update disk usage metrics --- src/rpc/system.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/rpc/system.rs b/src/rpc/system.rs index d94d4eec..df0e785b 100644 --- a/src/rpc/system.rs +++ b/src/rpc/system.rs @@ -219,11 +219,21 @@ pub fn gen_node_key(metadata_dir: &Path) -> Result { 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[..])?; } @@ -805,6 +815,7 @@ impl NodeStatus { } } + #[cfg(not(windows))] fn update_disk_usage(&mut self, meta_dir: &Path, data_dir: &DataDirEnum) { use nix::sys::statvfs::statvfs; let mount_avail = |path: &Path| match statvfs(path) { @@ -842,6 +853,11 @@ 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 -- 2.45.2 From ee4c19844bd0dd90f66069edf722af0b339a3c73 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Tue, 14 Jan 2025 10:43:10 +0100 Subject: [PATCH 3/7] windows: Don't set mode(0) when syncing directory --- src/block/manager.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/block/manager.rs b/src/block/manager.rs index 40b177a2..4532cacd 100644 --- a/src/block/manager.rs +++ b/src/block/manager.rs @@ -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); } -- 2.45.2 From 4049ba217fd16b7e5c7b40c0f4631a4753744e42 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Tue, 14 Jan 2025 10:50:18 +0100 Subject: [PATCH 4/7] windows: remove support for unix socket listening --- src/api/generic_server.rs | 21 ++++++++++++++++----- src/web/web_server.rs | 18 ++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/api/generic_server.rs b/src/api/generic_server.rs index 283abdd4..1bd62212 100644 --- a/src/api/generic_server.rs +++ b/src/api/generic_server.rs @@ -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; @@ -18,7 +16,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}; @@ -119,7 +117,13 @@ impl ApiServer { 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)? } @@ -135,6 +139,11 @@ impl ApiServer { 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"); + } } } @@ -264,11 +273,13 @@ 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))] #[async_trait] 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() diff --git a/src/web/web_server.rs b/src/web/web_server.rs index 69939f65..4b2c60d7 100644 --- a/src/web/web_server.rs +++ b/src/web/web_server.rs @@ -1,8 +1,6 @@ -use std::fs::{self, Permissions}; -use std::os::unix::prelude::PermissionsExt; use std::{convert::Infallible, sync::Arc}; -use tokio::net::{TcpListener, UnixListener}; +use tokio::net::TcpListener; use tokio::sync::watch; use hyper::{ @@ -20,7 +18,7 @@ use opentelemetry::{ use crate::error::*; -use garage_api::generic_server::{server_loop, UnixListenerOn}; +use garage_api::generic_server::server_loop; use garage_api::helpers::*; use garage_api::s3::cors::{add_cors_headers, find_matching_cors_rule, handle_options_for_bucket}; use garage_api::s3::error::{ @@ -96,7 +94,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 std::fs::{self, Permissions}; + use std::os::unix::prelude::PermissionsExt; + use tokio::net::UnixListener; + use garage_api::generic_server::UnixListenerOn; + if path.exists() { fs::remove_file(path)? } @@ -110,6 +115,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"); + } } } -- 2.45.2 From 3e65289b8599c02b0ce3efd8bd055fbb0d06dfcb Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Tue, 14 Jan 2025 11:09:24 +0100 Subject: [PATCH 5/7] windows: fix allow-world-readable-secrets config compile error --- src/garage/secrets.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/garage/secrets.rs b/src/garage/secrets.rs index 8d2ff475..698799be 100644 --- a/src/garage/secrets.rs +++ b/src/garage/secrets.rs @@ -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 { + #[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, -- 2.45.2 From 7221ac5e88ded08b9da1ba970d13e5053b0bdda1 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Tue, 14 Jan 2025 12:32:51 +0100 Subject: [PATCH 6/7] windows: don't discover default IP, removes winpcap dependency --- src/rpc/system.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/rpc/system.rs b/src/rpc/system.rs index df0e785b..ca07d3d4 100644 --- a/src/rpc/system.rs +++ b/src/rpc/system.rs @@ -863,17 +863,27 @@ impl NodeStatus { /// 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) -> Option { + // 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()) + .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 { -- 2.45.2 From 8edf5aab228d93b79e2c6b6a3f2402fffdc4c4ab Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Fri, 10 Jan 2025 09:21:00 +0100 Subject: [PATCH 7/7] windows: compile.nix fixes --- flake.lock | 1 + nix/compile.nix | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/flake.lock b/flake.lock index a8ebe3c2..8a7132ff 100644 --- a/flake.lock +++ b/flake.lock @@ -114,6 +114,7 @@ "original": { "owner": "oxalica", "repo": "rust-overlay", + "rev": "19b70f147b9c67a759e35824b241f1ed92e46694", "type": "github" } } diff --git a/nix/compile.nix b/nix/compile.nix index c9cbebd5..32f0e076 100644 --- a/nix/compile.nix +++ b/nix/compile.nix @@ -4,6 +4,52 @@ let log = v: builtins.trace v v; + targetIsWindows = target == "x86_64-w64-mingw32"; + + pkgsNative = import pkgsSrc { inherit system; }; + + # 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 + ''; + }; + + # HACK: winapi contains a bunch of statically linked libraries embedded in the + # repo. Its build.rs would normally correctly find these files, but I guess + # cargo2nix gets in the way somehow. We download the repo manually so we can + # link into it. + # + # Btw... where did these static library files come from? We just trust them? + winapi-rs = pkgsNative.fetchFromGitHub { + owner = "retep998"; + repo = "winapi-rs"; + rev = "0.3.9"; # Must match the version found in Cargo.lock + hash = "sha256-/Qoz8kNsjnDEqkH/vciuzGAT1dpL7d94nbQnQh5sGQw="; + }; + + # HACK: see winapi-rs comment, same applies here but for the windows crate. + windows-rs-0-48-5 = pkgsNative.fetchFromGitHub { + owner = "microsoft"; + repo = "windows-rs"; + rev = "0.48.5"; # Must match the version found in Cargo.lock + hash = "sha256-24c1TBaNu742eZUlzo0ChVlln2tJALc8KUs+fbqP9po="; + }; + + # HACK: see winapi-rs comment, same applies here but for the windows crate. + windows-rs-0-52-0 = pkgsNative.fetchFromGitHub { + owner = "microsoft"; + repo = "windows-rs"; + rev = "0.52.0"; # Must match the version found in Cargo.lock + hash = "sha256-ZhsIAtiVPuBeNlkYnPEMrZh4FZJKnQyynXws5zv+8KI="; + }; + pkgs = if target != null then import pkgsSrc { inherit system; @@ -80,6 +126,17 @@ let # [2] hardeningDisable = [ "pie" ]; }; + + overrideArgs = old: + if targetIsWindows then { + rustcLinkFlags = old.rustcLinkFlags or [] ++ [ + "-L${pkgs.windows.pthreads}/lib" + "-L${empty-libgcc_eh}/lib" + "-L${winapi-rs}/x86_64/lib" + "-L${windows-rs-0-48-5}/crates/targets/x86_64_gnu/lib" + "-L${windows-rs-0-52-0}/crates/targets/x86_64_gnu/lib" + ]; + } else {}; }) (pkgs.rustBuilder.rustLib.makeOverride { @@ -147,9 +204,20 @@ let (pkgs.rustBuilder.rustLib.makeOverride { name = "libsodium-sys"; + overrideArgs = old: { features = [ ]; # [4] }; + + # 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. + overrideAttrs = drv: if targetIsWindows then { + setBuildEnv = '' + ${drv.setBuildEnv} + export SODIUM_LIB_DIR=${pkgs.libsodium}/lib + ''; + } else {}; }) (pkgs.rustBuilder.rustLib.makeOverride { @@ -158,6 +226,17 @@ let features = [ ]; # [4] }; }) + + (pkgs.rustBuilder.rustLib.makeOverride { + name = "timeago"; + overrideArgs = old: + if targetIsWindows then { + rustcLinkFlags = old.rustcLinkFlags or [] ++ [ + "-L${pkgs.windows.pthreads}/lib" + "-L${empty-libgcc_eh}/lib" + ]; + } else {}; + }) ]; /* We ship some parts of the code disabled by default by putting them behind a flag. @@ -204,11 +283,18 @@ let ]; # segfault with static-pie "x86_64-unknown-linux-musl" = [ "target-feature=+crt-static" "link-arg=-static-pie" ]; + "x86_64-pc-windows-gnu" = [ + "target-feature=+crt-static" + "link-arg=-static-pie" + ]; }; + # 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; -- 2.45.2