From 2bbb28bb888a12644e9f19843b740294f55f87b8 Mon Sep 17 00:00:00 2001
From: Jason Volk <jason@zemos.net>
Date: Mon, 30 Dec 2024 09:06:47 +0000
Subject: [PATCH] get device info with libc using major/minor

Signed-off-by: Jason Volk <jason@zemos.net>
---
 Cargo.lock                     | 75 +++-------------------------------
 Cargo.toml                     |  6 +--
 src/core/Cargo.toml            |  2 +-
 src/core/utils/sys/storage.rs  | 72 +++++++++++++++++++++-----------
 src/database/pool/configure.rs | 12 ++++--
 5 files changed, 64 insertions(+), 103 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 5b00a313..0c1890c4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -726,6 +726,7 @@ dependencies = [
  "image",
  "ipaddress",
  "itertools 0.13.0",
+ "libc",
  "libloading",
  "log",
  "nix",
@@ -739,7 +740,6 @@ dependencies = [
  "serde_json",
  "serde_regex",
  "serde_yaml",
- "sysinfo",
  "thiserror 2.0.7",
  "tikv-jemalloc-ctl",
  "tikv-jemalloc-sys",
@@ -1674,7 +1674,7 @@ checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba"
 dependencies = [
  "cfg-if",
  "libc",
- "windows 0.52.0",
+ "windows",
 ]
 
 [[package]]
@@ -4076,18 +4076,6 @@ dependencies = [
  "syn 2.0.90",
 ]
 
-[[package]]
-name = "sysinfo"
-version = "0.33.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "948512566b1895f93b1592c7574baeb2de842f224f2aab158799ecadb8ebbb46"
-dependencies = [
- "core-foundation-sys",
- "libc",
- "serde",
- "windows 0.57.0",
-]
-
 [[package]]
 name = "tendril"
 version = "0.4.3"
@@ -4945,17 +4933,7 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
 dependencies = [
- "windows-core 0.52.0",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows"
-version = "0.57.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
-dependencies = [
- "windows-core 0.57.0",
+ "windows-core",
  "windows-targets 0.52.6",
 ]
 
@@ -4968,60 +4946,17 @@ dependencies = [
  "windows-targets 0.52.6",
 ]
 
-[[package]]
-name = "windows-core"
-version = "0.57.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
-dependencies = [
- "windows-implement",
- "windows-interface",
- "windows-result 0.1.2",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-implement"
-version = "0.57.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.90",
-]
-
-[[package]]
-name = "windows-interface"
-version = "0.57.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.90",
-]
-
 [[package]]
 name = "windows-registry"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
 dependencies = [
- "windows-result 0.2.0",
+ "windows-result",
  "windows-strings",
  "windows-targets 0.52.6",
 ]
 
-[[package]]
-name = "windows-result"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
 [[package]]
 name = "windows-result"
 version = "0.2.0"
@@ -5037,7 +4972,7 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
 dependencies = [
- "windows-result 0.2.0",
+ "windows-result",
  "windows-targets 0.52.6",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index 24e6eb79..022baaa3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -498,10 +498,8 @@ version = "1.3.0"
 [workspace.dependencies.core_affinity]
 version = "0.8.1"
 
-[workspace.dependencies.sysinfo]
-version = "0.33.0"
-default-features = false
-features = ["disk", "serde"]
+[workspace.dependencies.libc]
+version = "0.2"
 
 #
 # Patches
diff --git a/src/core/Cargo.toml b/src/core/Cargo.toml
index d249f647..2873a05d 100644
--- a/src/core/Cargo.toml
+++ b/src/core/Cargo.toml
@@ -75,6 +75,7 @@ http.workspace = true
 image.workspace = true
 ipaddress.workspace = true
 itertools.workspace = true
+libc.workspace = true
 libloading.workspace = true
 log.workspace = true
 rand.workspace = true
@@ -87,7 +88,6 @@ serde_json.workspace = true
 serde_regex.workspace = true
 serde_yaml.workspace = true
 serde.workspace = true
-sysinfo.workspace = true
 thiserror.workspace = true
 tikv-jemallocator.optional = true
 tikv-jemallocator.workspace = true
diff --git a/src/core/utils/sys/storage.rs b/src/core/utils/sys/storage.rs
index 8dc75236..25b17904 100644
--- a/src/core/utils/sys/storage.rs
+++ b/src/core/utils/sys/storage.rs
@@ -1,14 +1,20 @@
 //! System utilities related to devices/peripherals
 
 use std::{
-	ffi::{OsStr, OsString},
+	ffi::OsStr,
 	fs,
 	fs::{read_to_string, FileType},
 	iter::IntoIterator,
-	path::Path,
+	path::{Path, PathBuf},
 };
 
-use crate::{result::FlatOk, Result};
+use libc::dev_t;
+
+use crate::{
+	result::FlatOk,
+	utils::{result::LogDebugErr, string::SplitInfallible},
+	Result,
+};
 
 /// Device characteristics useful for random access throughput
 #[derive(Clone, Debug, Default)]
@@ -35,16 +41,12 @@ pub struct Queue {
 
 /// Get device characteristics useful for random access throughput by name.
 #[must_use]
-pub fn parallelism(name: &OsStr) -> Parallelism {
-	let name = name
-		.to_str()
-		.expect("device name expected to be utf-8 representable");
+pub fn parallelism(path: &Path) -> Parallelism {
+	let dev_id = dev_from_path(path).log_debug_err().unwrap_or_default();
 
-	let block_path = Path::new("/").join("sys/").join("block/");
+	let mq_path = block_path(dev_id).join("mq/");
 
-	let mq_path = Path::new(&block_path).join(format!("{name}/mq/"));
-
-	let nr_requests_path = Path::new(&block_path).join(format!("{name}/queue/nr_requests"));
+	let nr_requests_path = block_path(dev_id).join("queue/nr_requests");
 
 	Parallelism {
 		nr_requests: read_to_string(&nr_requests_path)
@@ -96,17 +98,39 @@ fn queue_parallelism(dir: &Path) -> Queue {
 	}
 }
 
-/// Get the name of the device on which Path is mounted.
-#[must_use]
-pub fn name_from_path(path: &Path) -> Option<OsString> {
-	sysinfo::Disks::new_with_refreshed_list()
-		.into_iter()
-		.filter(|disk| path.starts_with(disk.mount_point()))
-		.max_by(|a, b| {
-			let a = a.mount_point().ancestors().count();
-			let b = b.mount_point().ancestors().count();
-			a.cmp(&b)
-		})
-		.map(|disk| Path::new(disk.name()))
-		.and_then(|path| path.file_name().map(ToOwned::to_owned))
+/// Get the name of the block device on which Path is mounted.
+pub fn name_from_path(path: &Path) -> Result<String> {
+	use std::io::{Error, ErrorKind::NotFound};
+
+	let (major, minor) = dev_from_path(path)?;
+	let path = block_path((major, minor)).join("uevent");
+	read_to_string(path)
+		.iter()
+		.map(String::as_str)
+		.flat_map(str::lines)
+		.map(|line| line.split_once_infallible("="))
+		.find_map(|(key, val)| (key == "DEVNAME").then_some(val))
+		.ok_or_else(|| Error::new(NotFound, "DEVNAME not found."))
+		.map_err(Into::into)
+		.map(Into::into)
+}
+
+/// Get the (major, minor) of the block device on which Path is mounted.
+#[allow(clippy::useless_conversion, clippy::unnecessary_fallible_conversions)]
+pub fn dev_from_path(path: &Path) -> Result<(dev_t, dev_t)> {
+	#[cfg(target_family = "unix")]
+	use std::os::unix::fs::MetadataExt;
+
+	let stat = fs::metadata(path)?;
+	let dev_id = stat.dev().try_into()?;
+
+	// SAFETY: These functions may not need to be marked as unsafe.
+	// see: https://github.com/rust-lang/libc/issues/3759
+	let (major, minor) = unsafe { (libc::major(dev_id), libc::minor(dev_id)) };
+
+	Ok((major.try_into()?, minor.try_into()?))
+}
+
+fn block_path((major, minor): (dev_t, dev_t)) -> PathBuf {
+	format!("/sys/dev/block/{major}:{minor}/").into()
 }
diff --git a/src/database/pool/configure.rs b/src/database/pool/configure.rs
index 9361a534..2a192a9c 100644
--- a/src/database/pool/configure.rs
+++ b/src/database/pool/configure.rs
@@ -1,9 +1,10 @@
-use std::{ffi::OsStr, sync::Arc};
+use std::{path::PathBuf, sync::Arc};
 
 use conduwuit::{
 	debug, debug_info, expected,
 	utils::{
 		math::usize_from_f64,
+		result::LogDebugErr,
 		stream,
 		stream::WIDTH_LIMIT,
 		sys::{compute::is_core_available, storage},
@@ -20,8 +21,12 @@ pub(super) fn configure(server: &Arc<Server>) -> (usize, Vec<usize>, Vec<usize>)
 	// This finds the block device and gathers all the properties we need.
 	let (device_name, device_prop) = config
 		.db_pool_affinity
-		.and_then(|| storage::name_from_path(&config.database_path))
-		.map(|device_name| (device_name.clone(), storage::parallelism(&device_name)))
+		.and_then(|| {
+			let path: PathBuf = config.database_path.clone();
+			let name = storage::name_from_path(&path).log_debug_err().ok();
+			let prop = storage::parallelism(&path);
+			name.map(|name| (name, prop))
+		})
 		.unzip();
 
 	// The default worker count is masked-on if we didn't find better information.
@@ -104,7 +109,6 @@ pub(super) fn configure(server: &Arc<Server>) -> (usize, Vec<usize>, Vec<usize>)
 	debug_info!(
 		device_name = ?device_name
 			.as_deref()
-			.and_then(OsStr::to_str)
 			.unwrap_or("None"),
 		?worker_counts,
 		?queue_sizes,