From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 876411FF136 for ; Mon, 23 Mar 2026 12:32:51 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id C86F3136F7; Mon, 23 Mar 2026 12:33:07 +0100 (CET) From: Kefu Chai To: pve-devel@lists.proxmox.com Subject: [PATCH pve-cluster v3 01/13] pmxcfs-rs: add pmxcfs-api-types crate Date: Mon, 23 Mar 2026 19:32:16 +0800 Message-ID: <20260323113239.942866-2-k.chai@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260323113239.942866-1-k.chai@proxmox.com> References: <20260323113239.942866-1-k.chai@proxmox.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1774265533523 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.344 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: WMM6FHJACMLLFG4G4BPIOJ6SO5OFPNYU X-Message-ID-Hash: WMM6FHJACMLLFG4G4BPIOJ6SO5OFPNYU X-MailFrom: k.chai@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Kefu Chai X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Add pmxcfs-api-types crate which provides foundational shared types: - PmxcfsError: Error type for startup/configuration errors (System and Configuration variants only) - errno module: io::Error constructors (enoent, eexist, eacces, etc.) - VmType: VM type enum (Qemu, Lxc) - VmEntry: VM registry entry (vm_id, vm_type, node, version) - MemberInfo: Corosync cluster member information - NodeSyncInfo: Per-node DFSM state snapshot for cluster sync Also adds workspace root files: Cargo.toml, Makefile, .gitignore, rustfmt.toml. All other crates depend on these shared type definitions. Signed-off-by: Kefu Chai --- src/pmxcfs-rs/.cargo/config.toml | 8 ++ src/pmxcfs-rs/.gitignore | 2 + src/pmxcfs-rs/Cargo.toml | 39 +++++++++ src/pmxcfs-rs/Makefile | 39 +++++++++ src/pmxcfs-rs/pmxcfs-api-types/Cargo.toml | 19 ++++ src/pmxcfs-rs/pmxcfs-api-types/README.md | 17 ++++ src/pmxcfs-rs/pmxcfs-api-types/src/errno.rs | 75 ++++++++++++++++ src/pmxcfs-rs/pmxcfs-api-types/src/error.rs | 15 ++++ src/pmxcfs-rs/pmxcfs-api-types/src/lib.rs | 97 +++++++++++++++++++++ src/pmxcfs-rs/rustfmt.toml | 1 + 10 files changed, 312 insertions(+) create mode 100644 src/pmxcfs-rs/.cargo/config.toml create mode 100644 src/pmxcfs-rs/.gitignore create mode 100644 src/pmxcfs-rs/Cargo.toml create mode 100644 src/pmxcfs-rs/Makefile create mode 100644 src/pmxcfs-rs/pmxcfs-api-types/Cargo.toml create mode 100644 src/pmxcfs-rs/pmxcfs-api-types/README.md create mode 100644 src/pmxcfs-rs/pmxcfs-api-types/src/errno.rs create mode 100644 src/pmxcfs-rs/pmxcfs-api-types/src/error.rs create mode 100644 src/pmxcfs-rs/pmxcfs-api-types/src/lib.rs create mode 100644 src/pmxcfs-rs/rustfmt.toml diff --git a/src/pmxcfs-rs/.cargo/config.toml b/src/pmxcfs-rs/.cargo/config.toml new file mode 100644 index 000000000..a439c97b2 --- /dev/null +++ b/src/pmxcfs-rs/.cargo/config.toml @@ -0,0 +1,8 @@ +[source] +[source.debian-packages] +directory = "/usr/share/cargo/registry" +[source.crates-io] +replace-with = "debian-packages" + +[profile.release] +debug=true diff --git a/src/pmxcfs-rs/.gitignore b/src/pmxcfs-rs/.gitignore new file mode 100644 index 000000000..1e7caa9ea --- /dev/null +++ b/src/pmxcfs-rs/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +target/ diff --git a/src/pmxcfs-rs/Cargo.toml b/src/pmxcfs-rs/Cargo.toml new file mode 100644 index 000000000..da2b9440a --- /dev/null +++ b/src/pmxcfs-rs/Cargo.toml @@ -0,0 +1,39 @@ +# Workspace root for pmxcfs Rust implementation +[workspace] +members = [ + "pmxcfs-api-types", # Shared types and error definitions +] +resolver = "2" + +[workspace.package] +version = "9.0.6" +edition = "2024" +authors = ["Proxmox Support Team "] +license = "AGPL-3.0" +repository = "https://git.proxmox.com/?p=pve-cluster.git" +rust-version = "1.85" + +[workspace.dependencies] +# Internal workspace dependencies +pmxcfs-api-types = { path = "pmxcfs-api-types" } + +# Error handling +thiserror = "2.0" + +# System integration +libc = "0.2" + +[workspace.lints.clippy] +uninlined_format_args = "warn" + +[profile.release] +lto = true +codegen-units = 1 +opt-level = 3 +strip = true + +[profile.dev] +opt-level = 1 +debug = true + +[patch.crates-io] diff --git a/src/pmxcfs-rs/Makefile b/src/pmxcfs-rs/Makefile new file mode 100644 index 000000000..eaa96317f --- /dev/null +++ b/src/pmxcfs-rs/Makefile @@ -0,0 +1,39 @@ +.PHONY: all test lint clippy fmt check build clean help + +# Default target +all: check build + +# Run all tests +test: + cargo test --workspace + +# Lint with clippy (using proxmox-backup style: only fail on correctness issues) +clippy: + cargo clippy --workspace -- -A clippy::all -D clippy::correctness + +# Check code formatting +fmt: + cargo fmt --all --check + +# Full quality check (format + lint + test) +check: fmt clippy test + +# Build release version +build: + cargo build --workspace --release + +# Clean build artifacts +clean: + cargo clean + +# Show available targets +help: + @echo "Available targets:" + @echo " all - Run check and build (default)" + @echo " test - Run all tests" + @echo " clippy - Run clippy linter" + @echo " fmt - Check code formatting" + @echo " check - Run fmt + clippy + test" + @echo " build - Build release version" + @echo " clean - Clean build artifacts" + @echo " help - Show this help message" diff --git a/src/pmxcfs-rs/pmxcfs-api-types/Cargo.toml b/src/pmxcfs-rs/pmxcfs-api-types/Cargo.toml new file mode 100644 index 000000000..cdce7951a --- /dev/null +++ b/src/pmxcfs-rs/pmxcfs-api-types/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "pmxcfs-api-types" +description = "Shared types and error definitions for pmxcfs" + +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +# Error handling +thiserror.workspace = true + +# System integration +libc.workspace = true diff --git a/src/pmxcfs-rs/pmxcfs-api-types/README.md b/src/pmxcfs-rs/pmxcfs-api-types/README.md new file mode 100644 index 000000000..d89902c22 --- /dev/null +++ b/src/pmxcfs-rs/pmxcfs-api-types/README.md @@ -0,0 +1,17 @@ +# pmxcfs-api-types + +This crate provides shared types used across all pmxcfs crates. Having +them in a dedicated crate with no internal dependencies avoids circular +dependencies between the higher-level crates. + +`VmType` does not include OpenVZ (historically `VMTYPE_OPENVZ = 2` in +the C implementation); it was dropped in PVE 4.0 in favour of LXC. + +`PmxcfsError` covers startup and configuration errors only. FUSE-layer +errno values are expressed as `std::io::Error` using the helpers in the +`errno` submodule. + +## References + +- [`src/pmxcfs/status.h`](../pmxcfs/status.h) — `VMTYPE_*` constants, `CFS_MAX_STATUS_SIZE` +- [`src/pmxcfs/dfsm.h`](../pmxcfs/dfsm.h) — `dfsm_node_info_t` (maps to `NodeSyncInfo`) diff --git a/src/pmxcfs-rs/pmxcfs-api-types/src/errno.rs b/src/pmxcfs-rs/pmxcfs-api-types/src/errno.rs new file mode 100644 index 000000000..7e735c111 --- /dev/null +++ b/src/pmxcfs-rs/pmxcfs-api-types/src/errno.rs @@ -0,0 +1,75 @@ +//! Convenience constructors for POSIX errno values as io::Error. +//! +//! Replaces the repeated `std::io::Error::from_raw_os_error(libc::ENOENT).into()` +//! pattern in operational methods throughout the workspace. +use std::io; + +/// ENOENT — No such file or directory +#[must_use] +#[inline] +pub fn enoent() -> io::Error { + io::Error::from_raw_os_error(libc::ENOENT) +} + +/// EEXIST — File already exists +#[must_use] +#[inline] +pub fn eexist() -> io::Error { + io::Error::from_raw_os_error(libc::EEXIST) +} + +/// EACCES — Permission denied +#[must_use] +#[inline] +pub fn eacces() -> io::Error { + io::Error::from_raw_os_error(libc::EACCES) +} + +/// ENOTEMPTY — Directory not empty +#[must_use] +#[inline] +pub fn enotempty() -> io::Error { + io::Error::from_raw_os_error(libc::ENOTEMPTY) +} + +/// EIO — Input/output error +#[must_use] +#[inline] +pub fn eio() -> io::Error { + io::Error::from_raw_os_error(libc::EIO) +} + +/// EISDIR — Is a directory +#[must_use] +#[inline] +pub fn eisdir() -> io::Error { + io::Error::from_raw_os_error(libc::EISDIR) +} + +/// EINVAL — Invalid argument +#[must_use] +#[inline] +pub fn einval() -> io::Error { + io::Error::from_raw_os_error(libc::EINVAL) +} + +/// EPERM — Operation not permitted +#[must_use] +#[inline] +pub fn eperm() -> io::Error { + io::Error::from_raw_os_error(libc::EPERM) +} + +/// EFBIG — File too large +#[must_use] +#[inline] +pub fn efbig() -> io::Error { + io::Error::from_raw_os_error(libc::EFBIG) +} + +/// Arbitrary errno value +#[must_use] +#[inline] +pub fn os_err(errno: libc::c_int) -> io::Error { + io::Error::from_raw_os_error(errno) +} diff --git a/src/pmxcfs-rs/pmxcfs-api-types/src/error.rs b/src/pmxcfs-rs/pmxcfs-api-types/src/error.rs new file mode 100644 index 000000000..1d3ec01f1 --- /dev/null +++ b/src/pmxcfs-rs/pmxcfs-api-types/src/error.rs @@ -0,0 +1,15 @@ +use thiserror::Error; + +/// Error types for pmxcfs operations +/// +/// Only variants that are actively used in the codebase are defined here. +/// Operational/FUSE errors use `std::io::Error` with errno helpers from +/// `pmxcfs_api_types::errno` instead. +#[derive(Error, Debug)] +pub enum PmxcfsError { + #[error("Configuration error: {0}")] + Configuration(String), + + #[error("System error: {0}")] + System(String), +} diff --git a/src/pmxcfs-rs/pmxcfs-api-types/src/lib.rs b/src/pmxcfs-rs/pmxcfs-api-types/src/lib.rs new file mode 100644 index 000000000..76c2cea84 --- /dev/null +++ b/src/pmxcfs-rs/pmxcfs-api-types/src/lib.rs @@ -0,0 +1,97 @@ +mod error; + +pub mod errno; + +pub use error::PmxcfsError; + +/// Current unix timestamp in seconds, truncated to u32 to match C's `time_t` usage. +pub fn unix_now_secs() -> u32 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs() as u32 +} + +/// Maximum size for status data (matches C implementation) +/// From status.h: #define CFS_MAX_STATUS_SIZE (32 * 1024) +pub const CFS_MAX_STATUS_SIZE: usize = 32 * 1024; + +/// VM/CT types +/// +/// Note: OpenVZ was historically supported (VMTYPE_OPENVZ = 2 in C implementation) +/// but was removed in PVE 4.0 in favor of LXC. Only QEMU and LXC are currently supported. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum VmType { + Qemu, + Lxc, +} + +impl VmType { + /// Returns the directory name where config files are stored + #[must_use] + #[inline] + pub fn config_dir(&self) -> &'static str { + match self { + Self::Qemu => "qemu-server", + Self::Lxc => "lxc", + } + } +} + +impl std::fmt::Display for VmType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Qemu => write!(f, "qemu"), + Self::Lxc => write!(f, "lxc"), + } + } +} + +impl TryFrom for VmType { + type Error = std::io::Error; + + fn try_from(value: u32) -> std::io::Result { + match value { + 1 => Ok(VmType::Qemu), + 3 => Ok(VmType::Lxc), + _ => Err(std::io::Error::other(format!("unknown VM type: {value}"))), + } + } +} + +/// VM/CT entry for vmlist +#[derive(Debug, Clone)] +pub struct VmEntry { + pub vm_id: u32, + pub vm_type: VmType, + pub node: String, + /// Per-VM version counter (increments when this VM's config changes) + pub version: u32, +} + +/// Information about a cluster member +/// +/// This is a shared type used by both cluster and DFSM modules +#[derive(Debug, Clone)] +pub struct MemberInfo { + pub node_id: u32, + pub pid: u32, + pub joined_at: u64, +} + +/// Node synchronization info for DFSM state sync +/// +/// Used during DFSM synchronization to track which nodes have provided state +/// and whether synchronization has completed for that node. +#[derive(Debug, Clone)] +pub struct NodeSyncInfo { + pub node_id: u32, + pub pid: u32, + /// Serialized DFSM state snapshot for this node, exchanged during cluster synchronization. + /// Variable-size; `None` until this node's state has been received. + /// Maps to C's `dfsm_node_info_t.state + state_len` in `dfsm.h`. + pub state: Option>, + /// Whether this node has been marked as synchronized. + /// Set by the DFSM after `process_state_update` confirms the node is up to date. + pub synced: bool, +} diff --git a/src/pmxcfs-rs/rustfmt.toml b/src/pmxcfs-rs/rustfmt.toml new file mode 100644 index 000000000..f216078d9 --- /dev/null +++ b/src/pmxcfs-rs/rustfmt.toml @@ -0,0 +1 @@ +edition = "2024" -- 2.47.3