1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
use futures_util::future::LocalBoxFuture;
use std::io;
use std::path::Path;
/// Recursively create a directory and all of its parent components if they are missing.
///
/// # Examples
///
/// ```no_run
/// tokio_uring::start(async {
/// tokio_uring::fs::create_dir_all("/some/dir").await.unwrap();
/// });
/// ```
pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
DirBuilder::new()
.recursive(true)
.create(path.as_ref())
.await
}
/// A builder used to create directories in various manners, based on uring async operations.
///
/// This builder supports the Linux specific option `mode` and may support `at` in the future.
#[derive(Debug)]
pub struct DirBuilder {
inner: fs_imp::DirBuilder,
recursive: bool,
}
impl Default for DirBuilder {
fn default() -> Self {
Self::new()
}
}
impl DirBuilder {
/// Creates a new set of options with default mode/security settings for all
/// platforms and also non-recursive.
///
/// # Examples
///
/// ```
/// let builder = tokio_uring::fs::DirBuilder::new();
/// ```
#[must_use]
pub fn new() -> DirBuilder {
DirBuilder {
inner: fs_imp::DirBuilder::new(),
recursive: false,
}
}
/// Indicates that directories should be created recursively, creating all
/// parent directories. Parents that do not exist are created with the same
/// security and permissions settings.
///
/// This option defaults to `false`.
///
/// # Examples
///
/// ```
/// let mut builder = tokio_uring::fs::DirBuilder::new();
/// builder.recursive(true);
/// ```
#[must_use]
pub fn recursive(&mut self, recursive: bool) -> &mut Self {
self.recursive = recursive;
self
}
/// Sets the mode to create new directories with. This option defaults to 0o777.
///
/// This option defaults to 0o777.
///
/// # Examples
///
/// ```
/// let mut builder = tokio_uring::fs::DirBuilder::new();
/// builder.mode(0o700);
/// ```
#[must_use]
pub fn mode(&mut self, mode: u32) -> &mut Self {
self.inner.set_mode(mode);
self
}
/// Creates the specified directory with the options configured in this
/// builder.
///
/// It is considered an error if the directory already exists unless
/// recursive mode is enabled.
///
/// # Examples
///
/// ```no_run
/// tokio_uring::start(async {
/// let path = "/tmp/foo/bar/baz";
/// tokio_uring::fs::DirBuilder::new()
/// .recursive(true)
/// .mode(0o700) // user-only mode: drwx------
/// .create(path).await.unwrap();
///
/// // TODO change with tokio_uring version
/// assert!(std::fs::metadata(path).unwrap().is_dir());
/// })
/// ```
pub async fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
self._create(path.as_ref()).await
}
async fn _create(&self, path: &Path) -> io::Result<()> {
if self.recursive {
self.recurse_create_dir_all(path).await
} else {
self.inner.mkdir(path).await
}
}
// This recursive function is very closely modeled after the std library version.
//
// A recursive async function requires a Boxed Future. TODO There may be an implementation that
// is less costly in terms of heap allocations. Maybe a non-recursive version is possible given
// we even know the path separator for Linux. Or maybe expand the first level to avoid
// recursion when only the first level of the directory needs to be built. For now, this serves
// its purpose.
fn recurse_create_dir_all<'a>(&'a self, path: &'a Path) -> LocalBoxFuture<io::Result<()>> {
Box::pin(async move {
if path == Path::new("") {
return Ok(());
}
match self.inner.mkdir(path).await {
Ok(()) => return Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
Err(_) if is_dir(path).await => return Ok(()),
Err(e) => return Err(e),
}
match path.parent() {
Some(p) => self.recurse_create_dir_all(p).await?,
None => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"failed to create whole tree",
));
/* TODO build own allocation free error some day like the std library does.
return Err(io::const_io_error!(
io::ErrorKind::Uncategorized,
"failed to create whole tree",
));
*/
}
}
match self.inner.mkdir(path).await {
Ok(()) => Ok(()),
Err(_) if is_dir(path).await => Ok(()),
Err(e) => Err(e),
}
})
}
}
// TODO this DirBuilder and this fs_imp module is modeled after the std library's. Here there is
// only Linux supported so is it worth to continue this separation?
mod fs_imp {
use crate::runtime::driver::op::Op;
use libc::mode_t;
use std::path::Path;
#[derive(Debug)]
pub struct DirBuilder {
mode: mode_t,
}
impl DirBuilder {
pub fn new() -> DirBuilder {
DirBuilder { mode: 0o777 }
}
pub async fn mkdir(&self, p: &Path) -> std::io::Result<()> {
Op::make_dir(p, self.mode)?.await
}
pub fn set_mode(&mut self, mode: u32) {
self.mode = mode as mode_t;
}
}
}
// Returns true if the path represents a directory.
//
// Uses one asynchronous uring call to determine this.
async fn is_dir<P: AsRef<Path>>(path: P) -> bool {
let mut builder = crate::fs::StatxBuilder::new();
if builder.mask(libc::STATX_TYPE).pathname(path).is_err() {
return false;
}
let res = builder.statx().await;
match res {
Ok(statx) => (u32::from(statx.stx_mode) & libc::S_IFMT) == libc::S_IFDIR,
Err(_) => false,
}
}