diff --git a/.gitignore b/.gitignore index ef7a56eb..c8bd9441 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /pki **/*.rs.bk *.swp -/.direnv \ No newline at end of file +/.direnv +Packet.lib \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index fa313874..fa9b4375 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1544,6 +1544,7 @@ dependencies = [ "tokio", "tokio-stream", "tracing", + "winapi", ] [[package]] diff --git a/src/api/generic_server.rs b/src/api/generic_server.rs index 283abdd4..6d9c4865 100644 --- a/src/api/generic_server.rs +++ b/src/api/generic_server.rs @@ -1,6 +1,5 @@ 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 +17,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}; @@ -36,6 +35,9 @@ use garage_util::socket_address::UnixOrTCPSocketAddress; use crate::helpers::{BoxBody, ErrorBody}; +#[cfg(unix)] +use tokio::net::{UnixListener, UnixStream}; + pub(crate) trait ApiEndpoint: Send + Sync + 'static { fn name(&self) -> &'static str; fn add_span_attributes(&self, span: SpanRef<'_>); @@ -119,7 +121,9 @@ impl ApiServer { let handler = move |request, socketaddr| self.clone().handler(request, socketaddr); server_loop(server_name, listener, handler, must_exit).await } + #[cfg(unix)] UnixOrTCPSocketAddress::UnixSocket(ref path) => { + use std::os::unix::fs::PermissionsExt; if path.exists() { fs::remove_file(path)? } @@ -264,8 +268,10 @@ impl Accept for TcpListener { } } +#[cfg(unix)] pub struct UnixListenerOn(pub UnixListener, pub String); +#[cfg(unix)] #[async_trait] impl Accept for UnixListenerOn { type Stream = UnixStream; diff --git a/src/block/layout.rs b/src/block/layout.rs index e78f3f08..00e3debb 100644 --- a/src/block/layout.rs +++ b/src/block/layout.rs @@ -279,7 +279,8 @@ impl DataLayout { u16::from_be_bytes([ hash.as_slice()[HASH_DRIVE_BYTES.0], hash.as_slice()[HASH_DRIVE_BYTES.1], - ]) as usize % DRIVE_NPART + ]) as usize + % DRIVE_NPART } fn block_dir_from(&self, hash: &Hash, dir: &PathBuf) -> PathBuf { diff --git a/src/block/manager.rs b/src/block/manager.rs index 40b177a2..04b1547a 100644 --- a/src/block/manager.rs +++ b/src/block/manager.rs @@ -792,11 +792,16 @@ 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 mut dir = fs::OpenOptions::new(); + dir.read(true); + + // TODO: Windows open options? + #[cfg(unix)] + { + dir.mode(0); + } + + let dir = dir.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/model/helper/locked.rs b/src/model/helper/locked.rs index f8e06add..b541d548 100644 --- a/src/model/helper/locked.rs +++ b/src/model/helper/locked.rs @@ -279,7 +279,8 @@ impl<'a> LockedHelper<'a> { .local_aliases .get(alias_name) .cloned() - .flatten() != Some(bucket_id) + .flatten() + != Some(bucket_id) { return Err(GarageError::Message(format!( "Bucket {:?} does not have alias {} in namespace of key {}", diff --git a/src/rpc/Cargo.toml b/src/rpc/Cargo.toml index acde0911..aceb9505 100644 --- a/src/rpc/Cargo.toml +++ b/src/rpc/Cargo.toml @@ -51,7 +51,10 @@ tokio.workspace = true tokio-stream.workspace = true opentelemetry.workspace = true +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3.9", features = ["fileapi", "impl-default"] } + [features] -kubernetes-discovery = [ "kube", "k8s-openapi", "schemars" ] -consul-discovery = [ "reqwest", "err-derive" ] -system-libs = [ "sodiumoxide/use-pkg-config" ] +kubernetes-discovery = ["kube", "k8s-openapi", "schemars"] +consul-discovery = ["reqwest", "err-derive"] +system-libs = ["sodiumoxide/use-pkg-config"] diff --git a/src/rpc/system.rs b/src/rpc/system.rs index d94d4eec..c0958736 100644 --- a/src/rpc/system.rs +++ b/src/rpc/system.rs @@ -218,6 +218,7 @@ pub fn gen_node_key(metadata_dir: &Path) -> Result { info!("Generating new node key pair."); let (pubkey, key) = ed25519::gen_keypair(); + #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; let mut f = std::fs::File::create(key_file.as_path())?; @@ -227,6 +228,17 @@ pub fn gen_node_key(metadata_dir: &Path) -> Result { f.write_all(&key[..])?; } + #[cfg(windows)] + { + // use std::os::windows::io::AsHandle; + let mut f = std::fs::File::create(key_file.as_path())?; + // TODO: Set ACL here? + let mut perm = f.metadata()?.permissions(); + perm.set_readonly(true); + std::fs::set_permissions(key_file.as_path(), perm)?; + f.write_all(&key[..])?; + } + { let mut pubkey_file = metadata_dir.to_path_buf(); pubkey_file.push("node_key.pub"); @@ -805,6 +817,48 @@ impl NodeStatus { } } + #[cfg(windows)] + fn update_disk_usage(&mut self, meta_dir: &Path, data_dir: &DataDirEnum) { + use winapi::um::fileapi::GetDiskFreeSpaceExA; + use winapi::um::winnt::ULARGE_INTEGER; + + let mount_avail = |path: &Path| -> Option<(u64, u64)> { + let mut path = path.to_path_buf(); + path.push(""); // Ensure trailing slash + + let mut a: ULARGE_INTEGER = Default::default(); + let mut total: ULARGE_INTEGER = Default::default(); + let mut free: ULARGE_INTEGER = Default::default(); + + let path_ptr = path.as_os_str().as_encoded_bytes().as_ptr(); + let result = unsafe { + GetDiskFreeSpaceExA(path_ptr as *const i8, &mut a, &mut total, &mut free) + }; + + if result == 0 { + return None; + } + + let free = unsafe { *free.QuadPart() }; + let total = unsafe { *total.QuadPart() }; + + Some((free, total)) + }; + + self.meta_disk_avail = mount_avail(meta_dir); + self.data_disk_avail = match data_dir { + DataDirEnum::Single(path_buf) => mount_avail(path_buf), + + // TODO: THIS IS WRONG!! Does not take into account multiple dirs on the same partition + // Will have to deduplicate by the filesystem mount path + DataDirEnum::Multiple(dirs) => dirs + .into_iter() + .filter_map(|dir| mount_avail(&dir.path)) + .reduce(|(a1, b1), (a2, b2)| (a1 + a2, b1 + b2)), + }; + } + + #[cfg(unix)] 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) { diff --git a/src/util/socket_address.rs b/src/util/socket_address.rs index f01225f6..84411d95 100644 --- a/src/util/socket_address.rs +++ b/src/util/socket_address.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Deserializer}; #[derive(Debug, Clone)] pub enum UnixOrTCPSocketAddress { TCPSocket(SocketAddr), + #[cfg(unix)] UnixSocket(PathBuf), } @@ -16,6 +17,7 @@ impl Display for UnixOrTCPSocketAddress { fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result { match self { UnixOrTCPSocketAddress::TCPSocket(address) => write!(formatter, "http://{}", address), + #[cfg(unix)] UnixOrTCPSocketAddress::UnixSocket(path) => { write!(formatter, "http+unix://{}", path.to_string_lossy()) } @@ -31,14 +33,15 @@ impl<'de> Deserialize<'de> for UnixOrTCPSocketAddress { let string = String::deserialize(deserializer)?; let string = string.as_str(); + #[cfg(unix)] if string.starts_with("/") { - Ok(UnixOrTCPSocketAddress::UnixSocket( + return Ok(UnixOrTCPSocketAddress::UnixSocket( PathBuf::from_str(string).map_err(Error::custom)?, - )) - } else { - Ok(UnixOrTCPSocketAddress::TCPSocket( - SocketAddr::from_str(string).map_err(Error::custom)?, - )) + )); } + + Ok(UnixOrTCPSocketAddress::TCPSocket( + SocketAddr::from_str(string).map_err(Error::custom)?, + )) } } diff --git a/src/web/web_server.rs b/src/web/web_server.rs index 69939f65..f4cc157b 100644 --- a/src/web/web_server.rs +++ b/src/web/web_server.rs @@ -1,10 +1,12 @@ 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; +#[cfg(unix)] +use tokio::net::UnixListener; + use hyper::{ body::Incoming as IncomingBody, header::{HeaderValue, HOST}, @@ -20,7 +22,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 +98,11 @@ impl WebServer { move |stream, socketaddr| self.clone().handle_request(stream, socketaddr); server_loop(server_name, listener, handler, must_exit).await } + #[cfg(unix)] UnixOrTCPSocketAddress::UnixSocket(ref path) => { + use garage_api::generic_server::UnixListenerOn; + use std::os::unix::fs::PermissionsExt; + if path.exists() { fs::remove_file(path)? }