blob: 8ac37128b350c989b13f4081c5fb5e283ebadc4b [file] [log] [blame]
Jooyung Hanf48ceb42021-06-01 18:00:04 +09001// 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
Jiyong Parka999a322022-02-07 15:10:32 +090017use anyhow::{anyhow, bail, Result};
Jooyung Han311b1202021-09-14 22:00:16 +090018use log::debug;
19use std::fmt::Debug;
Jooyung Hanf48ceb42021-06-01 18:00:04 +090020use std::fs::File;
21use std::io;
Jiyong Parka999a322022-02-07 15:10:32 +090022use std::os::unix::fs::FileTypeExt;
23use std::os::unix::io::AsRawFd;
Jooyung Hanf48ceb42021-06-01 18:00:04 +090024use std::path::Path;
25use std::thread;
26use std::time::{Duration, Instant};
27
28const SLEEP_DURATION: Duration = Duration::from_millis(5);
29
30/// waits for a file with a timeout and returns it
Jooyung Han311b1202021-09-14 22:00:16 +090031pub fn wait_for_file<P: AsRef<Path> + Debug>(path: P, timeout: Duration) -> Result<File> {
32 debug!("waiting for {:?}...", path);
Jooyung Hanf48ceb42021-06-01 18:00:04 +090033 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 {
Jooyung Hana6d11eb2021-09-10 11:48:05 +090039 return Err(anyhow!(error));
Jooyung Hanf48ceb42021-06-01 18:00:04 +090040 }
41 if begin.elapsed() > timeout {
Jooyung Hana6d11eb2021-09-10 11:48:05 +090042 return Err(anyhow!(io::Error::from(io::ErrorKind::NotFound)));
Jooyung Hanf48ceb42021-06-01 18:00:04 +090043 }
44 thread::sleep(SLEEP_DURATION);
45 }
46 }
47 }
48}
49
Jiyong Parka999a322022-02-07 15:10:32 +090050// From include/uapi/linux/fs.h
51const BLK: u8 = 0x12;
52const BLKFLSBUF: u8 = 97;
53nix::ioctl_none!(_blkflsbuf, BLK, BLKFLSBUF);
54
55pub 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
Jooyung Hanf48ceb42021-06-01 18:00:04 +090064#[cfg(test)]
65mod tests {
66 use super::*;
67 use std::io::{Read, Write};
68
69 #[test]
Jooyung Hana6d11eb2021-09-10 11:48:05 +090070 fn test_wait_for_file() -> Result<()> {
Jooyung Hanf48ceb42021-06-01 18:00:04 +090071 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]
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());
Jooyung Hana6d11eb2021-09-10 11:48:05 +090092 assert_eq!(
93 io::ErrorKind::NotFound,
94 file.unwrap_err().root_cause().downcast_ref::<io::Error>().unwrap().kind()
95 );
Jooyung Hanf48ceb42021-06-01 18:00:04 +090096 }
97}