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
31 ///
32 /// WARNING: This only guarantees file creation. When there's another thread
33 /// writing a file and you're waiting for the file, reading the file should be
34 /// synchronized with other mechanism than just waiting for the creation.
wait_for_file<P: AsRef<Path> + Debug>(path: P, timeout: Duration) -> Result<File>35 pub fn wait_for_file<P: AsRef<Path> + Debug>(path: P, timeout: Duration) -> Result<File> {
36 debug!("waiting for {:?}...", path);
37 let begin = Instant::now();
38 loop {
39 match File::open(&path) {
40 Ok(file) => return Ok(file),
41 Err(error) => {
42 if error.kind() != io::ErrorKind::NotFound {
43 return Err(anyhow!(error));
44 }
45 if begin.elapsed() > timeout {
46 return Err(anyhow!(io::Error::from(io::ErrorKind::NotFound)));
47 }
48 thread::sleep(SLEEP_DURATION);
49 }
50 }
51 }
52 }
53
54 // From include/uapi/linux/fs.h
55 const BLK: u8 = 0x12;
56 const BLKFLSBUF: u8 = 97;
57 nix::ioctl_none!(_blkflsbuf, BLK, BLKFLSBUF);
58
blkflsbuf(f: &mut File) -> Result<()>59 pub fn blkflsbuf(f: &mut File) -> Result<()> {
60 if !f.metadata()?.file_type().is_block_device() {
61 bail!("{:?} is not a block device", f.as_raw_fd());
62 }
63 // SAFETY: The file is kept open until the end of this function.
64 unsafe { _blkflsbuf(f.as_raw_fd()) }?;
65 Ok(())
66 }
67
68 #[cfg(test)]
69 mod tests {
70 use super::*;
71 use std::fs::rename;
72 use std::io::{Read, Write};
73
74 #[test]
test_wait_for_file() -> Result<()>75 fn test_wait_for_file() -> Result<()> {
76 let test_dir = tempfile::TempDir::new().unwrap();
77 let test_file = test_dir.path().join("test.txt");
78 let temp_file = test_dir.path().join("test.txt~");
79 thread::spawn(move || -> io::Result<()> {
80 // Sleep to ensure that `wait_for_file` actually waits
81 thread::sleep(Duration::from_secs(1));
82 // Write to a temp file and then rename it to avoid the race between
83 // write and read.
84 File::create(&temp_file)?.write_all(b"test")?;
85 rename(temp_file, test_file)
86 });
87
88 let test_file = test_dir.path().join("test.txt");
89 let mut file = wait_for_file(test_file, Duration::from_secs(5))?;
90 let mut buffer = String::new();
91 file.read_to_string(&mut buffer)?;
92 assert_eq!("test", buffer);
93 Ok(())
94 }
95
96 #[test]
test_wait_for_file_fails()97 fn test_wait_for_file_fails() {
98 let test_dir = tempfile::TempDir::new().unwrap();
99 let test_file = test_dir.path().join("test.txt");
100 let file = wait_for_file(test_file, Duration::from_secs(1));
101 assert!(file.is_err());
102 assert_eq!(
103 io::ErrorKind::NotFound,
104 file.unwrap_err().root_cause().downcast_ref::<io::Error>().unwrap().kind()
105 );
106 }
107 }
108