diff --git a/flake.lock b/flake.lock index 2c7be871..4c9127a3 100644 --- a/flake.lock +++ b/flake.lock @@ -101,7 +101,7 @@ "original": { "owner": "oxalica", "repo": "rust-overlay", - "rev": "162ab0edc2936508470199b2e8e6c444a2535019", + "rev": "19b70f147b9c67a759e35824b241f1ed92e46694", "type": "github" } } 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; diff --git a/nix/compile.nix b/nix/compile.nix index 2755f5be..8c722e3f 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; @@ -62,13 +108,35 @@ let # [1] 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 { name = "libsodium-sys"; + overrideArgs = old: { features = [ ]; # [3] }; + + # 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 { @@ -77,6 +145,17 @@ let features = [ ]; # [3] }; }) + + (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. @@ -123,11 +202,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; 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/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); } 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, diff --git a/src/rpc/system.rs b/src/rpc/system.rs index 753d8c8d..5ac662a6 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; @@ -866,22 +877,37 @@ 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) -> 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 { 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"); + } } }