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