disktest_rawio/
lib.rs

1// -*- coding: utf-8 -*-
2//
3// disktest - Storage tester
4//
5// Copyright 2020-2024 Michael Büsch <m@bues.ch>
6//
7// Licensed under the Apache License version 2.0
8// or the MIT license, at your option.
9// SPDX-License-Identifier: Apache-2.0 OR MIT
10//
11
12#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "windows")))]
13std::compile_error!(
14    "Your operating system is not supported, yet. \
15     Please open an issue on GitHub: \
16     https://github.com/mbuesch/disktest/issues"
17);
18
19use anyhow as ah;
20use std::path::Path;
21
22#[cfg(any(target_os = "linux", target_os = "android"))]
23mod linux;
24
25#[cfg(target_os = "windows")]
26mod windows;
27
28pub const DEFAULT_SECTOR_SIZE: u32 = 512;
29
30/// OS interface for raw I/O.
31trait RawIoOsIntf {
32    fn get_sector_size(&self) -> Option<u32>;
33    fn drop_file_caches(&mut self, offset: u64, size: u64) -> ah::Result<()>;
34    fn close(&mut self) -> ah::Result<()>;
35    fn sync(&mut self) -> ah::Result<()>;
36    fn set_len(&mut self, size: u64) -> ah::Result<()>;
37    fn seek(&mut self, offset: u64) -> ah::Result<u64>;
38    fn read(&mut self, buffer: &mut [u8]) -> ah::Result<RawIoResult>;
39    fn write(&mut self, buffer: &[u8]) -> ah::Result<RawIoResult>;
40}
41
42/// Raw I/O operation result code.
43pub enum RawIoResult {
44    /// Ok, number of processed bytes.
45    Ok(usize),
46    /// Out of disk space.
47    Enospc,
48}
49
50/// Raw device I/O abstraction.
51pub struct RawIo {
52    os: Box<dyn RawIoOsIntf>,
53}
54
55impl RawIo {
56    /// Open a file or device.
57    pub fn new(path: &Path, create: bool, read: bool, write: bool) -> ah::Result<Self> {
58        #[cfg(any(target_os = "linux", target_os = "android"))]
59        let os = Box::new(linux::RawIoLinux::new(path, create, read, write)?);
60
61        #[cfg(target_os = "windows")]
62        let os = Box::new(windows::RawIoWindows::new(path, create, read, write)?);
63
64        Ok(Self { os })
65    }
66
67    /// Get the physical sector size of the file or device.
68    /// Returns None, if this is not a raw device.
69    pub fn get_sector_size(&self) -> Option<u32> {
70        self.os.get_sector_size()
71    }
72
73    /// Close the file, flush all buffers and drop all caches.
74    /// This function ensures that subsequent reads are not read from RAM cache.
75    pub fn drop_file_caches(mut self, offset: u64, size: u64) -> ah::Result<()> {
76        self.os.drop_file_caches(offset, size)
77    }
78
79    /// Close the file and flush all buffers.
80    /// (This does not affect the caches).
81    pub fn close(&mut self) -> ah::Result<()> {
82        self.os.close()
83    }
84
85    /// Flush all buffers.
86    /// (This does not affect the caches).
87    pub fn sync(&mut self) -> ah::Result<()> {
88        self.os.sync()
89    }
90
91    #[cfg_attr(not(test), allow(dead_code))]
92    pub fn set_len(&mut self, size: u64) -> ah::Result<()> {
93        self.os.set_len(size)
94    }
95
96    /// Seek to a file offset.
97    pub fn seek(&mut self, offset: u64) -> ah::Result<u64> {
98        self.os.seek(offset)
99    }
100
101    /// Read a chunk of data.
102    pub fn read(&mut self, buffer: &mut [u8]) -> ah::Result<RawIoResult> {
103        self.os.read(buffer)
104    }
105
106    /// Write a chunk of data.
107    pub fn write(&mut self, buffer: &[u8]) -> ah::Result<RawIoResult> {
108        self.os.write(buffer)
109    }
110}
111
112// vim: ts=4 sw=4 expandtab