1use crate::{
2 file::OpenOptions,
3 lowlevel::{self, Extensions},
4 metadata::{MetaData, MetaDataBuilder, Permissions},
5 Auxiliary, Buffer, Error, Id, OwnedHandle, WriteEnd, WriteEndWithCachedId,
6};
7
8use std::{
9 borrow::Cow,
10 cmp::min,
11 convert::TryInto,
12 path::{Path, PathBuf},
13};
14
15use bytes::BytesMut;
16
17mod dir;
18pub use dir::{DirEntry, ReadDir};
19
20type AwaitableStatus = lowlevel::AwaitableStatus<Buffer>;
21type AwaitableAttrs = lowlevel::AwaitableAttrs<Buffer>;
22type SendLinkingRequest =
23 fn(&mut WriteEnd, Id, Cow<'_, Path>, Cow<'_, Path>) -> Result<AwaitableStatus, Error>;
24
25type SendRmRequest = fn(&mut WriteEnd, Id, Cow<'_, Path>) -> Result<AwaitableStatus, Error>;
26type SendMetadataRequest = fn(&mut WriteEnd, Id, Cow<'_, Path>) -> Result<AwaitableAttrs, Error>;
27
28#[derive(Debug, Clone)]
30pub struct Fs {
31 write_end: WriteEndWithCachedId,
32 cwd: Box<Path>,
33}
34
35impl Fs {
36 pub(super) fn new(write_end: WriteEndWithCachedId, cwd: PathBuf) -> Self {
37 Self {
38 write_end,
39 cwd: cwd.into_boxed_path(),
40 }
41 }
42
43 fn get_auxiliary(&self) -> &Auxiliary {
44 self.write_end.get_auxiliary()
45 }
46
47 pub fn cwd(&self) -> &Path {
49 &self.cwd
50 }
51
52 pub fn set_cwd(&mut self, cwd: impl Into<PathBuf>) {
58 self.cwd = cwd.into().into_boxed_path();
59 }
60
61 fn concat_path_if_needed<'path>(&self, path: &'path Path) -> Cow<'path, Path> {
62 if path.is_absolute() || self.cwd.as_os_str().is_empty() {
63 Cow::Borrowed(path)
64 } else {
65 Cow::Owned(self.cwd.join(path))
66 }
67 }
68}
69
70impl Fs {
71 pub async fn open_dir(&mut self, path: impl AsRef<Path>) -> Result<Dir, Error> {
73 async fn inner(this: &mut Fs, path: &Path) -> Result<Dir, Error> {
74 let path = this.concat_path_if_needed(path);
75
76 this.write_end
77 .send_request(|write_end, id| Ok(write_end.send_opendir_request(id, path)?.wait()))
78 .await
79 .map(|handle| Dir(OwnedHandle::new(this.write_end.clone(), handle)))
80 }
81
82 inner(self, path.as_ref()).await
83 }
84
85 pub fn dir_builder(&mut self) -> DirBuilder<'_> {
87 DirBuilder {
88 fs: self,
89 metadata_builder: MetaDataBuilder::new(),
90 }
91 }
92
93 pub async fn create_dir(&mut self, path: impl AsRef<Path>) -> Result<(), Error> {
95 async fn inner(this: &mut Fs, path: &Path) -> Result<(), Error> {
96 this.dir_builder().create(path).await
97 }
98
99 inner(self, path.as_ref()).await
100 }
101
102 async fn remove_impl(&mut self, path: &Path, f: SendRmRequest) -> Result<(), Error> {
103 let path = self.concat_path_if_needed(path);
104
105 self.write_end
106 .send_request(|write_end, id| Ok(f(write_end, id, path)?.wait()))
107 .await
108 }
109
110 pub async fn remove_dir(&mut self, path: impl AsRef<Path>) -> Result<(), Error> {
112 self.remove_impl(path.as_ref(), WriteEnd::send_rmdir_request)
113 .await
114 }
115
116 pub async fn remove_file(&mut self, path: impl AsRef<Path>) -> Result<(), Error> {
118 self.remove_impl(path.as_ref(), WriteEnd::send_remove_request)
119 .await
120 }
121
122 pub async fn canonicalize(&mut self, path: impl AsRef<Path>) -> Result<PathBuf, Error> {
129 async fn inner(this: &mut Fs, path: &Path) -> Result<PathBuf, Error> {
130 let path = this.concat_path_if_needed(path);
131
132 let f = if this
133 .get_auxiliary()
134 .extensions()
135 .contains(Extensions::EXPAND_PATH)
136 {
137 WriteEnd::send_expand_path_request
143 } else {
144 WriteEnd::send_realpath_request
145 };
146
147 this.write_end
148 .send_request(|write_end, id| Ok(f(write_end, id, path)?.wait()))
149 .await
150 .map(Into::into)
151 }
152
153 inner(self, path.as_ref()).await
154 }
155
156 async fn linking_impl(
157 &mut self,
158 src: &Path,
159 dst: &Path,
160 f: SendLinkingRequest,
161 ) -> Result<(), Error> {
162 let src = self.concat_path_if_needed(src);
163 let dst = self.concat_path_if_needed(dst);
164
165 self.write_end
166 .send_request(|write_end, id| Ok(f(write_end, id, src, dst)?.wait()))
167 .await
168 }
169
170 pub async fn hard_link(
178 &mut self,
179 src: impl AsRef<Path>,
180 dst: impl AsRef<Path>,
181 ) -> Result<(), Error> {
182 async fn inner(this: &mut Fs, src: &Path, dst: &Path) -> Result<(), Error> {
183 if !this
184 .get_auxiliary()
185 .extensions()
186 .contains(Extensions::HARDLINK)
187 {
188 return Err(Error::UnsupportedExtension(&"hardlink"));
189 }
190
191 this.linking_impl(src, dst, WriteEnd::send_hardlink_request)
192 .await
193 }
194
195 inner(self, src.as_ref(), dst.as_ref()).await
196 }
197
198 pub async fn symlink(
200 &mut self,
201 src: impl AsRef<Path>,
202 dst: impl AsRef<Path>,
203 ) -> Result<(), Error> {
204 self.linking_impl(src.as_ref(), dst.as_ref(), WriteEnd::send_symlink_request)
205 .await
206 }
207
208 pub async fn rename(
215 &mut self,
216 from: impl AsRef<Path>,
217 to: impl AsRef<Path>,
218 ) -> Result<(), Error> {
219 async fn inner(this: &mut Fs, from: &Path, to: &Path) -> Result<(), Error> {
220 let f = if this
221 .get_auxiliary()
222 .extensions()
223 .contains(Extensions::POSIX_RENAME)
224 {
225 WriteEnd::send_posix_rename_request
227 } else {
228 WriteEnd::send_rename_request
229 };
230
231 this.linking_impl(from, to, f).await
232 }
233
234 inner(self, from.as_ref(), to.as_ref()).await
235 }
236
237 pub async fn read_link(&mut self, path: impl AsRef<Path>) -> Result<PathBuf, Error> {
239 async fn inner(this: &mut Fs, path: &Path) -> Result<PathBuf, Error> {
240 let path = this.concat_path_if_needed(path);
241
242 this.write_end
243 .send_request(|write_end, id| Ok(write_end.send_readlink_request(id, path)?.wait()))
244 .await
245 .map(Into::into)
246 }
247
248 inner(self, path.as_ref()).await
249 }
250
251 async fn set_metadata_impl(&mut self, path: &Path, metadata: MetaData) -> Result<(), Error> {
252 let path = self.concat_path_if_needed(path);
253
254 self.write_end
255 .send_request(|write_end, id| {
256 Ok(write_end
257 .send_setstat_request(id, path, metadata.into_inner())?
258 .wait())
259 })
260 .await
261 }
262
263 pub async fn set_metadata(
265 &mut self,
266 path: impl AsRef<Path>,
267 metadata: MetaData,
268 ) -> Result<(), Error> {
269 self.set_metadata_impl(path.as_ref(), metadata).await
270 }
271
272 pub async fn set_permissions(
274 &mut self,
275 path: impl AsRef<Path>,
276 perm: Permissions,
277 ) -> Result<(), Error> {
278 async fn inner(this: &mut Fs, path: &Path, perm: Permissions) -> Result<(), Error> {
279 this.set_metadata_impl(path, MetaDataBuilder::new().permissions(perm).create())
280 .await
281 }
282
283 inner(self, path.as_ref(), perm).await
284 }
285
286 async fn metadata_impl(
287 &mut self,
288 path: &Path,
289 f: SendMetadataRequest,
290 ) -> Result<MetaData, Error> {
291 let path = self.concat_path_if_needed(path);
292
293 self.write_end
294 .send_request(|write_end, id| Ok(f(write_end, id, path)?.wait()))
295 .await
296 .map(MetaData::new)
297 }
298
299 pub async fn metadata(&mut self, path: impl AsRef<Path>) -> Result<MetaData, Error> {
302 self.metadata_impl(path.as_ref(), WriteEnd::send_stat_request)
303 .await
304 }
305
306 pub async fn symlink_metadata(&mut self, path: impl AsRef<Path>) -> Result<MetaData, Error> {
308 self.metadata_impl(path.as_ref(), WriteEnd::send_lstat_request)
309 .await
310 }
311
312 pub async fn read(&mut self, path: impl AsRef<Path>) -> Result<BytesMut, Error> {
314 async fn inner(this: &mut Fs, path: &Path) -> Result<BytesMut, Error> {
315 let path = this.concat_path_if_needed(path);
316
317 let mut file = OpenOptions::open_inner(
318 lowlevel::OpenOptions::new().read(true),
319 false,
320 false,
321 false,
322 path.as_ref(),
323 this.write_end.clone(),
324 )
325 .await?;
326 let max_read_len = file.max_read_len_impl();
327
328 let cap_to_reserve: usize = if let Some(len) = file.metadata().await?.len() {
329 len.saturating_add(300)
332 .try_into()
333 .unwrap_or(max_read_len as usize)
334 } else {
335 max_read_len as usize
336 };
337
338 let mut buffer = BytesMut::with_capacity(cap_to_reserve);
339
340 loop {
341 let cnt = buffer.len();
342
343 let n: u32 = if cnt <= cap_to_reserve {
344 (cap_to_reserve - cnt)
347 .saturating_add(300)
348 .try_into()
349 .map(|n| min(n, max_read_len))
350 .unwrap_or(max_read_len)
351 } else {
352 max_read_len
353 };
354 buffer.reserve(n.try_into().unwrap_or(usize::MAX));
355
356 if let Some(bytes) = file.read(n, buffer.split_off(cnt)).await? {
357 buffer.unsplit(bytes);
358 } else {
359 break Ok(buffer);
361 }
362 }
363 }
364
365 inner(self, path.as_ref()).await
366 }
367
368 pub async fn write(
370 &mut self,
371 path: impl AsRef<Path>,
372 content: impl AsRef<[u8]>,
373 ) -> Result<(), Error> {
374 async fn inner(this: &mut Fs, path: &Path, content: &[u8]) -> Result<(), Error> {
375 let path = this.concat_path_if_needed(path);
376
377 OpenOptions::open_inner(
378 lowlevel::OpenOptions::new().write(true),
379 true,
380 true,
381 false,
382 path.as_ref(),
383 this.write_end.clone(),
384 )
385 .await?
386 .write_all(content)
387 .await
388 }
389
390 inner(self, path.as_ref(), content.as_ref()).await
391 }
392}
393
394#[repr(transparent)]
396#[derive(Debug, Clone)]
397pub struct Dir(OwnedHandle);
398
399impl Dir {
400 pub fn read_dir(self) -> ReadDir {
402 ReadDir::new(self)
403 }
404
405 pub async fn close(self) -> Result<(), Error> {
407 self.0.close().await
408 }
409}
410
411#[derive(Debug)]
413pub struct DirBuilder<'a> {
414 fs: &'a mut Fs,
415 metadata_builder: MetaDataBuilder,
416}
417
418impl DirBuilder<'_> {
419 pub fn reset(&mut self) -> &mut Self {
421 self.metadata_builder = MetaDataBuilder::new();
422 self
423 }
424
425 pub fn id(&mut self, (uid, gid): (u32, u32)) -> &mut Self {
427 self.metadata_builder.id((uid, gid));
428 self
429 }
430
431 pub fn permissions(&mut self, perm: Permissions) -> &mut Self {
433 self.metadata_builder.permissions(perm);
434 self
435 }
436}
437
438impl DirBuilder<'_> {
439 pub async fn create(&mut self, path: impl AsRef<Path>) -> Result<(), Error> {
441 async fn inner(this: &mut DirBuilder<'_>, path: &Path) -> Result<(), Error> {
442 let fs = &mut this.fs;
443
444 let path = fs.concat_path_if_needed(path);
445 let attrs = this.metadata_builder.create().into_inner();
446
447 fs.write_end
448 .send_request(|write_end, id| {
449 Ok(write_end.send_mkdir_request(id, path, attrs)?.wait())
450 })
451 .await
452 }
453
454 inner(self, path.as_ref()).await
455 }
456}