• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! IO utilities
16 
17 use anyhow::{anyhow, bail, Result};
18 use log::debug;
19 use std::fmt::Debug;
20 use std::fs::File;
21 use std::io;
22 use std::os::unix::fs::FileTypeExt;
23 use std::os::unix::io::AsRawFd;
24 use std::path::Path;
25 use std::thread;
26 use std::time::{Duration, Instant};
27 
28 const SLEEP_DURATION: Duration = Duration::from_millis(5);
29 
30 /// waits for a file with a timeout and returns it
wait_for_file<P: AsRef<Path> + Debug>(path: P, timeout: Duration) -> Result<File>31 pub fn wait_for_file<P: AsRef<Path> + Debug>(path: P, timeout: Duration) -> Result<File> {
32     debug!("waiting for {:?}...", path);
33     let begin = Instant::now();
34     loop {
35         match File::open(&path) {
36             Ok(file) => return Ok(file),
37             Err(error) => {
38                 if error.kind() != io::ErrorKind::NotFound {
39                     return Err(anyhow!(error));
40                 }
41                 if begin.elapsed() > timeout {
42                     return Err(anyhow!(io::Error::from(io::ErrorKind::NotFound)));
43                 }
44                 thread::sleep(SLEEP_DURATION);
45             }
46         }
47     }
48 }
49 
50 // From include/uapi/linux/fs.h
51 const BLK: u8 = 0x12;
52 const BLKFLSBUF: u8 = 97;
53 nix::ioctl_none!(_blkflsbuf, BLK, BLKFLSBUF);
54 
blkflsbuf(f: &mut File) -> Result<()>55 pub fn blkflsbuf(f: &mut File) -> Result<()> {
56     if !f.metadata()?.file_type().is_block_device() {
57         bail!("{:?} is not a block device", f.as_raw_fd());
58     }
59     // SAFETY: The file is kept open until the end of this function.
60     unsafe { _blkflsbuf(f.as_raw_fd()) }?;
61     Ok(())
62 }
63 
64 #[cfg(test)]
65 mod tests {
66     use super::*;
67     use std::io::{Read, Write};
68 
69     #[test]
test_wait_for_file() -> Result<()>70     fn test_wait_for_file() -> Result<()> {
71         let test_dir = tempfile::TempDir::new().unwrap();
72         let test_file = test_dir.path().join("test.txt");
73         thread::spawn(move || -> io::Result<()> {
74             thread::sleep(Duration::from_secs(1));
75             File::create(test_file)?.write_all(b"test")
76         });
77 
78         let test_file = test_dir.path().join("test.txt");
79         let mut file = wait_for_file(&test_file, Duration::from_secs(5))?;
80         let mut buffer = String::new();
81         file.read_to_string(&mut buffer)?;
82         assert_eq!("test", buffer);
83         Ok(())
84     }
85 
86     #[test]
test_wait_for_file_fails()87     fn test_wait_for_file_fails() {
88         let test_dir = tempfile::TempDir::new().unwrap();
89         let test_file = test_dir.path().join("test.txt");
90         let file = wait_for_file(&test_file, Duration::from_secs(1));
91         assert!(file.is_err());
92         assert_eq!(
93             io::ErrorKind::NotFound,
94             file.unwrap_err().root_cause().downcast_ref::<io::Error>().unwrap().kind()
95         );
96     }
97 }
98