blob: 772941d024816c72237a4801f08b71548659ae91 [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 Han697c8602023-07-24 18:02:36 +090031///
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.
Jooyung Han311b1202021-09-14 22:00:16 +090035pub fn wait_for_file<P: AsRef<Path> + Debug>(path: P, timeout: Duration) -> Result<File> {
36 debug!("waiting for {:?}...", path);
Jooyung Hanf48ceb42021-06-01 18:00:04 +090037 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 {
Jooyung Hana6d11eb2021-09-10 11:48:05 +090043 return Err(anyhow!(error));
Jooyung Hanf48ceb42021-06-01 18:00:04 +090044 }
45 if begin.elapsed() > timeout {
Jooyung Hana6d11eb2021-09-10 11:48:05 +090046 return Err(anyhow!(io::Error::from(io::ErrorKind::NotFound)));
Jooyung Hanf48ceb42021-06-01 18:00:04 +090047 }
48 thread::sleep(SLEEP_DURATION);
49 }
50 }
51 }
52}
53
Jiyong Parka999a322022-02-07 15:10:32 +090054// From include/uapi/linux/fs.h
55const BLK: u8 = 0x12;
56const BLKFLSBUF: u8 = 97;
57nix::ioctl_none!(_blkflsbuf, BLK, BLKFLSBUF);
58
59pub 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
Jooyung Hanf48ceb42021-06-01 18:00:04 +090068#[cfg(test)]
69mod tests {
70 use super::*;
Jooyung Han697c8602023-07-24 18:02:36 +090071 use std::fs::rename;
Jooyung Hanf48ceb42021-06-01 18:00:04 +090072 use std::io::{Read, Write};
73
74 #[test]
Jooyung Hana6d11eb2021-09-10 11:48:05 +090075 fn test_wait_for_file() -> Result<()> {
Jooyung Hanf48ceb42021-06-01 18:00:04 +090076 let test_dir = tempfile::TempDir::new().unwrap();
77 let test_file = test_dir.path().join("test.txt");
Jooyung Han697c8602023-07-24 18:02:36 +090078 let temp_file = test_dir.path().join("test.txt~");
Jooyung Hanf48ceb42021-06-01 18:00:04 +090079 thread::spawn(move || -> io::Result<()> {
Jooyung Han697c8602023-07-24 18:02:36 +090080 // Sleep to ensure that `wait_for_file` actually waits
Jooyung Hanf48ceb42021-06-01 18:00:04 +090081 thread::sleep(Duration::from_secs(1));
Jooyung Han697c8602023-07-24 18:02:36 +090082 // 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)
Jooyung Hanf48ceb42021-06-01 18:00:04 +090086 });
87
88 let test_file = test_dir.path().join("test.txt");
Charisee96113f32023-01-26 09:00:42 +000089 let mut file = wait_for_file(test_file, Duration::from_secs(5))?;
Jooyung Hanf48ceb42021-06-01 18:00:04 +090090 let mut buffer = String::new();
91 file.read_to_string(&mut buffer)?;
92 assert_eq!("test", buffer);
93 Ok(())
94 }
95
96 #[test]
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");
Charisee96113f32023-01-26 09:00:42 +0000100 let file = wait_for_file(test_file, Duration::from_secs(1));
Jooyung Hanf48ceb42021-06-01 18:00:04 +0900101 assert!(file.is_err());
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900102 assert_eq!(
103 io::ErrorKind::NotFound,
104 file.unwrap_err().root_cause().downcast_ref::<io::Error>().unwrap().kind()
105 );
Jooyung Hanf48ceb42021-06-01 18:00:04 +0900106 }
107}