Skip to main content

compio_fs/utils/
mod.rs

1#[cfg(unix)]
2#[path = "unix.rs"]
3mod sys;
4
5#[cfg(windows)]
6#[path = "windows.rs"]
7mod sys;
8
9use std::{io, path::Path};
10
11use compio_buf::{BufResult, IoBuf, buf_try};
12use compio_io::{AsyncReadAtExt, AsyncWriteAtExt};
13
14use crate::{File, metadata};
15
16/// Removes a file from the filesystem.
17pub async fn remove_file(path: impl AsRef<Path>) -> io::Result<()> {
18    sys::remove_file(path).await
19}
20
21/// Removes an empty directory.
22pub async fn remove_dir(path: impl AsRef<Path>) -> io::Result<()> {
23    sys::remove_dir(path).await
24}
25
26/// Creates a new, empty directory at the provided path.
27pub async fn create_dir(path: impl AsRef<Path>) -> io::Result<()> {
28    DirBuilder::new().create(path).await
29}
30
31/// Recursively create a directory and all of its parent components if they are
32/// missing.
33pub async fn create_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
34    DirBuilder::new().recursive(true).create(path).await
35}
36
37/// Rename a file or directory to a new name, replacing the original file if
38/// `to` already exists.
39pub async fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
40    sys::rename(from, to).await
41}
42
43/// Creates a new symbolic link on the filesystem.
44#[cfg(unix)]
45pub async fn symlink(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
46    sys::symlink(original, link).await
47}
48
49/// Creates a new symlink to a non-directory file on the filesystem.
50#[cfg(windows)]
51pub async fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
52    sys::symlink_file(original, link).await
53}
54
55/// Creates a new symlink to a directory on the filesystem.
56#[cfg(windows)]
57pub async fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
58    sys::symlink_dir(original, link).await
59}
60
61/// Creates a new hard link on the filesystem.
62pub async fn hard_link(original: impl AsRef<Path>, link: impl AsRef<Path>) -> io::Result<()> {
63    sys::hard_link(original, link).await
64}
65
66/// Write a slice as the entire contents of a file.
67///
68/// This function will create a file if it does not exist,
69/// and will entirely replace its contents if it does.
70pub async fn write<P: AsRef<Path>, B: IoBuf>(path: P, buf: B) -> BufResult<(), B> {
71    let (mut file, buf) = buf_try!(File::create(path).await, buf);
72    file.write_all_at(buf, 0).await
73}
74
75/// Read the entire contents of a file into a bytes vector.
76pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
77    let file = File::open(path).await?;
78    let BufResult(res, buffer) = file.read_to_end_at(Vec::new(), 0).await;
79    res?;
80    Ok(buffer)
81}
82
83/// Reads an extended attribute from a path, following the final symbolic link.
84///
85/// This has `getxattr` semantics, not `lgetxattr` semantics. To read from an
86/// already open file without resolving its pathname again, use
87/// [`File::get_xattr`].
88///
89/// The value is written from the start of `buffer`, using its full capacity.
90/// On success, the result is the value's length and the buffer's initialized
91/// length advances to at least that length. With zero capacity, only the
92/// required length is returned and the buffer remains empty. The attribute
93/// may change between a sizing query and a subsequent read.
94///
95/// A missing attribute returns `ENODATA`; insufficient nonzero capacity returns
96/// `ERANGE`. Other OS errors are preserved. A path or name containing a NUL
97/// byte returns [`io::ErrorKind::InvalidInput`]. Errors return the original
98/// buffer without advancing its initialized length.
99///
100/// The submitted operation owns the path, name, and buffer until completion,
101/// even if this future is dropped. Cancellation does not return the buffer to
102/// the caller.
103#[cfg(any(target_os = "linux", target_os = "android"))]
104pub async fn get_xattr<T: compio_buf::IoBufMut>(
105    path: impl AsRef<Path>,
106    name: impl AsRef<std::ffi::OsStr>,
107    buffer: T,
108) -> BufResult<usize, T> {
109    use std::{ffi::CString, os::unix::ffi::OsStrExt};
110
111    use compio_buf::{IntoInner, IoBufMutExt};
112    use compio_driver::op::{BufResultExt, GetXattr};
113
114    let (path, buffer) = buf_try!(crate::path_string(path), buffer);
115    let (name, mut buffer) = buf_try!(
116        CString::new(name.as_ref().as_bytes()).map_err(io::Error::from),
117        buffer
118    );
119    let query_size = buffer.buf_capacity() == 0;
120    let op = GetXattr::new(path, name, buffer);
121    let res = compio_runtime::submit(op).await.into_inner();
122    if query_size {
123        res
124    } else {
125        // SAFETY: A successful nonzero-capacity getxattr initializes exactly
126        // the returned byte count, bounded by the supplied buffer
127        // capacity. Size-only queries are excluded because they do not
128        // write bytes.
129        unsafe { res.map_advanced() }
130    }
131}
132
133/// A builder used to create directories in various manners.
134pub struct DirBuilder {
135    inner: sys::DirBuilder,
136    recursive: bool,
137}
138
139impl Default for DirBuilder {
140    fn default() -> Self {
141        Self::new()
142    }
143}
144
145impl DirBuilder {
146    /// Creates a new set of options with default mode/security settings for all
147    /// platforms and also non-recursive.
148    pub fn new() -> Self {
149        Self {
150            inner: sys::DirBuilder::new(),
151            recursive: false,
152        }
153    }
154
155    /// Indicates that directories should be created recursively, creating all
156    /// parent directories. Parents that do not exist are created with the same
157    /// security and permissions settings.
158    pub fn recursive(&mut self, recursive: bool) -> &mut Self {
159        self.recursive = recursive;
160        self
161    }
162
163    /// Creates the specified directory with the options configured in this
164    /// builder.
165    pub async fn create(&self, path: impl AsRef<Path>) -> io::Result<()> {
166        let path = path.as_ref();
167        if self.recursive {
168            self.create_dir_all(path).await
169        } else {
170            self.inner.create(path).await
171        }
172    }
173
174    async fn create_dir_all(&self, path: &Path) -> io::Result<()> {
175        if path == Path::new("") {
176            return Ok(());
177        }
178
179        match self.inner.create(path).await {
180            Ok(()) => return Ok(()),
181            Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
182            Err(_) if metadata(path).await.map(|m| m.is_dir()).unwrap_or_default() => return Ok(()),
183            Err(e) => return Err(e),
184        }
185        match path.parent() {
186            Some(p) => Box::pin(self.create_dir_all(p)).await?,
187            None => {
188                return Err(io::Error::other("failed to create whole tree"));
189            }
190        }
191        match self.inner.create(path).await {
192            Ok(()) => Ok(()),
193            Err(_) if metadata(path).await.map(|m| m.is_dir()).unwrap_or_default() => Ok(()),
194            Err(e) => Err(e),
195        }
196    }
197
198    #[cfg(dirfd)]
199    pub(crate) async fn create_at(&self, dir: &File, path: &Path) -> io::Result<()> {
200        if path.is_absolute() {
201            self.create(path).await
202        } else if self.recursive {
203            self.create_dir_all_at(dir, path).await
204        } else {
205            self.inner.create_at(dir, path).await
206        }
207    }
208
209    #[cfg(dirfd)]
210    async fn create_dir_all_at(&self, dir: &File, path: &Path) -> io::Result<()> {
211        use crate::metadata_at;
212
213        if path == Path::new("") {
214            return Ok(());
215        }
216        match self.inner.create_at(dir, path).await {
217            Ok(()) => return Ok(()),
218            Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
219            Err(_)
220                if metadata_at(dir, path)
221                    .await
222                    .map(|m| m.is_dir())
223                    .unwrap_or_default() =>
224            {
225                return Ok(());
226            }
227            Err(e) => return Err(e),
228        }
229        match path.parent() {
230            Some(p) => Box::pin(self.create_dir_all_at(dir, p)).await?,
231            None => {
232                return Err(io::Error::other("failed to create whole tree"));
233            }
234        }
235        match self.inner.create_at(dir, path).await {
236            Ok(()) => Ok(()),
237            Err(_)
238                if metadata_at(dir, path)
239                    .await
240                    .map(|m| m.is_dir())
241                    .unwrap_or_default() =>
242            {
243                Ok(())
244            }
245            Err(e) => Err(e),
246        }
247    }
248}
249
250#[cfg(unix)]
251impl std::os::unix::fs::DirBuilderExt for DirBuilder {
252    fn mode(&mut self, mode: u32) -> &mut Self {
253        self.inner.mode(mode);
254        self
255    }
256}