Skip to main content

linuxutils_system/
fadvise.rs

1use linuxutils_common::man::ManContent;
2
3pub const MAN: ManContent = ManContent::empty();
4
5use clap::Parser;
6use rustix::{
7    fd::BorrowedFd,
8    fs::{self, Advice},
9};
10use std::{fs::File, os::unix::io::AsRawFd, process::ExitCode};
11
12/// Advise the kernel about file access patterns via posix_fadvise(2).
13///
14/// Most commonly used with `dontneed` to drop a file's pages from the
15/// page cache.
16#[derive(Parser)]
17#[command(
18    name = "fadvise",
19    about = "Advise the kernel about file access patterns"
20)]
21pub struct Args {
22    /// Advice to apply: normal, sequential, random, noreuse, willneed, dontneed
23    #[arg(short = 'a', long, default_value = "dontneed")]
24    advice: String,
25
26    /// Beginning offset in bytes
27    #[arg(short = 'o', long, default_value = "0")]
28    offset: u64,
29
30    /// Length of range in bytes (0 = to end of file)
31    #[arg(short = 'l', long, default_value = "0")]
32    length: u64,
33
34    /// Operate on an already-open file descriptor
35    #[arg(short = 'd', long = "fd")]
36    file_descriptor: Option<i32>,
37
38    /// File to advise on
39    filename: Option<String>,
40}
41
42fn parse_advice(s: &str) -> Result<Advice, String> {
43    match s {
44        "normal" => Ok(Advice::Normal),
45        "sequential" => Ok(Advice::Sequential),
46        "random" => Ok(Advice::Random),
47        "noreuse" => Ok(Advice::NoReuse),
48        "willneed" => Ok(Advice::WillNeed),
49        "dontneed" => Ok(Advice::DontNeed),
50        other => Err(format!("unknown advice: {other}")),
51    }
52}
53
54pub fn run(args: Args) -> ExitCode {
55    let advice = match parse_advice(&args.advice) {
56        Ok(a) => a,
57        Err(e) => {
58            eprintln!("fadvise: {e}");
59            return ExitCode::FAILURE;
60        }
61    };
62
63    if let Some(raw_fd) = args.file_descriptor {
64        let fd = unsafe { BorrowedFd::borrow_raw(raw_fd) };
65        if let Err(e) = fs::fadvise(
66            fd,
67            args.offset,
68            std::num::NonZeroU64::new(args.length),
69            advice,
70        ) {
71            eprintln!("fadvise: fd {raw_fd}: {}", std::io::Error::from(e));
72            return ExitCode::FAILURE;
73        }
74    } else if let Some(ref path) = args.filename {
75        let file = match File::open(path) {
76            Ok(f) => f,
77            Err(e) => {
78                eprintln!("fadvise: {path}: {e}");
79                return ExitCode::FAILURE;
80            }
81        };
82        let fd = unsafe { BorrowedFd::borrow_raw(file.as_raw_fd()) };
83        if let Err(e) = fs::fadvise(
84            fd,
85            args.offset,
86            std::num::NonZeroU64::new(args.length),
87            advice,
88        ) {
89            eprintln!("fadvise: {path}: {}", std::io::Error::from(e));
90            return ExitCode::FAILURE;
91        }
92    } else {
93        eprintln!("fadvise: no filename or file descriptor specified");
94        return ExitCode::FAILURE;
95    }
96
97    ExitCode::SUCCESS
98}