1use rustix::fs::{
2 AtFlags, Mode, OFlags, StatxFlags, ioctl_blkpbszget, ioctl_blksszget, open, statx,
3};
4use smol_str::format_smolstr;
5
6use std::{io, path::Path};
7
8use super::{
9 super::DirectInfo, ABS_BLOCK_PATH, BLOCK_PATH, PHYSICAL_BLOCK_SIZE_FILE_NAME, QUEUE_PATH,
10 read_block_size,
11};
12
13const LOGICAL_BLOCK_SIZE_FILE_NAME: &str = "logical_block_size";
14
15pub fn fetch<P: AsRef<Path>>(path: P) -> io::Result<DirectInfo> {
17 let path = path.as_ref();
18 let fd = open(path.as_os_str(), OFlags::RDONLY, Mode::empty())?;
19 match ioctl_blksszget(&fd) {
20 Ok(logical_blk_size) => {
21 let physical_blk_size = ioctl_blkpbszget(&fd).unwrap_or(logical_blk_size);
22 Ok(DirectInfo::new(logical_blk_size, physical_blk_size))
23 }
24 Err(_) => {
25 let abs = path.canonicalize()?;
26 let parent_dir = abs.parent().ok_or_else(|| {
27 io::Error::new(
28 io::ErrorKind::InvalidInput,
29 format!("path '{}' has no parent directory", abs.display()),
30 )
31 })?;
32
33 let parent_fd = open(parent_dir, OFlags::RDONLY, Mode::empty())?;
34
35 let file_name = abs.file_name().ok_or_else(|| {
36 io::Error::new(
37 io::ErrorKind::InvalidInput,
38 format!("path '{}' has no file name", abs.display()),
39 )
40 })?;
41
42 let stx = statx(
43 parent_fd,
44 file_name,
45 AtFlags::empty(),
46 StatxFlags::BASIC_STATS,
47 )?;
48 let major = stx.stx_dev_major;
49 let minor = stx.stx_dev_minor;
50
51 let mut current = {
52 let path = format_smolstr!("{ABS_BLOCK_PATH}/{major}:{minor}");
53 let path: &str = path.as_ref();
54 let path: &Path = path.as_ref();
55 path.canonicalize()?
56 };
57
58 loop {
59 let queue = current.join(QUEUE_PATH);
60 let logical_blk_path = queue.join(LOGICAL_BLOCK_SIZE_FILE_NAME);
61 if logical_blk_path.exists() {
62 let logical_blk_size = read_block_size(&logical_blk_path)?;
63
64 let physical_blk_path = queue.join(PHYSICAL_BLOCK_SIZE_FILE_NAME);
65 let physical_blk_size = read_block_size(&physical_blk_path).unwrap_or(logical_blk_size);
66 return Ok(DirectInfo::new(logical_blk_size, physical_blk_size));
67 }
68
69 match current.parent() {
70 Some(p) if p.file_name().is_some_and(|n| n.ne(BLOCK_PATH)) => current = p.to_path_buf(),
71 _ => {
72 return Err(io::Error::new(
73 io::ErrorKind::InvalidInput,
74 "cannot find the logical and physical block size information",
75 ));
76 }
77 }
78 }
79 }
80 }
81}