blob: 84d81fe7ada7aa642939753a4cd76a40805ba886 [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Utilities for interacting with the disk.
use std::{
os::unix::prelude::OsStrExt,
path::{Path, PathBuf},
};
/// Get a disk partition device path.
///
/// This handles inserting a 'p' before the number if needed and special cases
/// for /dev/disk/by-id or /dev/disk/by-id.
/// Please note: The path needs to be a child of either:
/// - /dev
/// - /dev/disk/by-path
/// - /dev/disk/by-id
pub fn get_partition_device<P: AsRef<Path>>(disk_device: P, num: u32) -> Option<PathBuf> {
return match disk_device.as_ref().parent() {
Some(path) => {
if path.as_os_str() == "/dev" {
return Some(get_partition_device_dev(disk_device, num));
} else if path.as_os_str() == "/dev/disk/by-id"
|| path.as_os_str() == "/dev/disk/by-path"
{
return Some(get_partition_device_by_path_or_id(disk_device, num));
}
None
}
None => None,
};
}
fn get_partition_device_dev<P: AsRef<Path>>(disk_device: P, num: u32) -> PathBuf {
let mut buf = disk_device.as_ref().as_os_str().to_os_string();
// If the disk path ends in a number, e.g. "/dev/nvme0n1", append
// a "p" before the partition number.
if let Some(byte) = buf.as_bytes().last() {
if byte.is_ascii_digit() {
buf.push("p");
}
}
buf.push(num.to_string());
PathBuf::from(buf)
}
fn get_partition_device_by_path_or_id<P: AsRef<Path>>(disk_device: P, num: u32) -> PathBuf {
let mut buf = disk_device.as_ref().as_os_str().to_os_string();
// Both by-path and by-id have a -partX postfix.
// E.g. partition 4 of `/dev/disk/by-id/nvme-eui.e8238fa6bf530001001b448b4566aa1a` is:
// `/dev/disk/by-id/nvme-eui.e8238fa6bf530001001b448b4566aa1a-part4`
buf.push(format!("-part{}", num));
PathBuf::from(buf)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_partition_device() {
// Testing /dev variants.
let result = get_partition_device(Path::new("/dev/nvme0n1"), 4);
assert!(result.is_some());
assert_eq!(Path::new("/dev/nvme0n1p4").to_path_buf(), result.unwrap());
let result = get_partition_device(Path::new("/dev/loop0"), 1);
assert!(result.is_some());
assert_eq!(Path::new("/dev/loop0p1").to_path_buf(), result.unwrap());
let result = get_partition_device(Path::new("/dev/mmcblk0"), 2);
assert!(result.is_some());
assert_eq!(Path::new("/dev/mmcblk0p2").to_path_buf(), result.unwrap());
let result = get_partition_device(Path::new("/dev/sda"), 3);
assert!(result.is_some());
assert_eq!(Path::new("/dev/sda3").to_path_buf(), result.unwrap());
// Testing /dev/disk/by-id variants.
let result = get_partition_device(
Path::new("/dev/disk/by-id/nvme-eui.e8238fa6bf530001001b448b4566aa1a"),
3,
);
assert!(result.is_some());
assert_eq!(
Path::new("/dev/disk/by-id/nvme-eui.e8238fa6bf530001001b448b4566aa1a-part3")
.to_path_buf(),
result.unwrap()
);
let result = get_partition_device(Path::new("/dev/disk/by-id/scsi-0PersistentDisk"), 2);
assert!(result.is_some());
assert_eq!(
Path::new("/dev/disk/by-id/scsi-0PersistentDisk-part2").to_path_buf(),
result.unwrap()
);
// Testing /dev/disk/by-path variants.
let result = get_partition_device(Path::new("/dev/disk/by-path/pci-0000:00:1f.2-ata-1"), 1);
assert!(result.is_some());
assert_eq!(
Path::new("/dev/disk/by-path/pci-0000:00:1f.2-ata-1-part1").to_path_buf(),
result.unwrap()
);
let result =
get_partition_device(Path::new("/dev/disk/by-path/pci-0000:04:00.0-nvme-1"), 4);
assert!(result.is_some());
assert_eq!(
Path::new("/dev/disk/by-path/pci-0000:04:00.0-nvme-1-part4").to_path_buf(),
result.unwrap()
);
}
}