cap_async_std/fs/
file.rs

1use crate::fs::{Metadata, OpenOptions, Permissions};
2use async_std::fs;
3use async_std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
4#[cfg(unix)]
5use async_std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
6#[cfg(target_os = "wasi")]
7use async_std::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
8use async_std::task::{spawn_blocking, Context, Poll};
9use cap_primitives::fs::{is_file_read_write, open_ambient};
10use cap_primitives::AmbientAuthority;
11use io_lifetimes::AsFilelike;
12#[cfg(not(windows))]
13use io_lifetimes::{AsFd, BorrowedFd, OwnedFd};
14#[cfg(windows)]
15use io_lifetimes::{AsHandle, BorrowedHandle, OwnedHandle};
16use std::fmt;
17use std::path::Path;
18use std::pin::Pin;
19#[cfg(windows)]
20use {
21    async_std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle},
22    io_extras::os::windows::{
23        AsHandleOrSocket, AsRawHandleOrSocket, BorrowedHandleOrSocket, IntoRawHandleOrSocket,
24        OwnedHandleOrSocket, RawHandleOrSocket,
25    },
26};
27
28/// A reference to an open file on a filesystem.
29///
30/// This corresponds to [`async_std::fs::File`].
31///
32/// This `File` has no `open` or `create` methods. To open or create a file,
33/// first obtain a [`Dir`] containing the path, and then call [`Dir::open`] or
34/// [`Dir::create`].
35///
36/// [`Dir`]: crate::fs::Dir
37/// [`Dir::open`]: crate::fs::Dir::open
38/// [`Dir::create`]: crate::fs::Dir::create
39#[derive(Clone)]
40pub struct File {
41    pub(crate) std: fs::File,
42}
43
44impl File {
45    /// Constructs a new instance of `Self` from the given
46    /// `async_std::fs::File`.
47    ///
48    /// This grants access the resources the `async_std::fs::File` instance
49    /// already has access to.
50    #[inline]
51    pub fn from_std(std: fs::File) -> Self {
52        Self { std }
53    }
54
55    /// Consumes `self` and returns an `async_std::fs::File`.
56    #[inline]
57    pub fn into_std(self) -> fs::File {
58        self.std
59    }
60
61    /// Attempts to sync all OS-internal metadata to disk.
62    ///
63    /// This corresponds to [`async_std::fs::File::sync_all`].
64    #[inline]
65    pub async fn sync_all(&self) -> io::Result<()> {
66        self.std.sync_all().await
67    }
68
69    /// This function is similar to `sync_all`, except that it may not
70    /// synchronize file metadata to a filesystem.
71    ///
72    /// This corresponds to [`async_std::fs::File::sync_data`].
73    #[inline]
74    pub async fn sync_data(&self) -> io::Result<()> {
75        self.std.sync_data().await
76    }
77
78    /// Truncates or extends the underlying file, updating the size of this
79    /// file to become size.
80    ///
81    /// This corresponds to [`async_std::fs::File::set_len`].
82    #[inline]
83    pub async fn set_len(&self, size: u64) -> io::Result<()> {
84        self.std.set_len(size).await
85    }
86
87    /// Queries metadata about the underlying file.
88    ///
89    /// This corresponds to [`async_std::fs::File::metadata`].
90    #[inline]
91    pub async fn metadata(&self) -> io::Result<Metadata> {
92        let clone = self.clone();
93        spawn_blocking(move || metadata_from(&*clone.std.as_filelike_view::<std::fs::File>())).await
94    }
95
96    // async_std doesn't have `try_clone`.
97
98    /// Changes the permissions on the underlying file.
99    ///
100    /// This corresponds to [`async_std::fs::File::set_permissions`].
101    #[inline]
102    pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
103        let sync = self.std.as_filelike_view::<std::fs::File>();
104        self.std
105            .set_permissions(permissions_into_std(&sync, perm)?)
106            .await
107    }
108
109    /// Constructs a new instance of `Self` in read-only mode by opening the
110    /// given path as a file using the host process' ambient authority.
111    ///
112    /// # Ambient Authority
113    ///
114    /// This function is not sandboxed and may access any path that the host
115    /// process has access to.
116    #[inline]
117    pub async fn open_ambient<P: AsRef<Path>>(
118        path: P,
119        ambient_authority: AmbientAuthority,
120    ) -> io::Result<Self> {
121        let path = path.as_ref().to_path_buf();
122        spawn_blocking(move || {
123            open_ambient(
124                path.as_ref(),
125                OpenOptions::new().read(true),
126                ambient_authority,
127            )
128        })
129        .await
130        .map(|f| Self::from_std(f.into()))
131    }
132
133    /// Constructs a new instance of `Self` in write-only mode by opening,
134    /// creating or truncating, the given path as a file using the host
135    /// process' ambient authority.
136    ///
137    /// # Ambient Authority
138    ///
139    /// This function is not sandboxed and may access any path that the host
140    /// process has access to.
141    #[inline]
142    pub async fn create_ambient<P: AsRef<Path>>(
143        path: P,
144        ambient_authority: AmbientAuthority,
145    ) -> io::Result<Self> {
146        let path = path.as_ref().to_path_buf();
147        spawn_blocking(move || {
148            open_ambient(
149                path.as_ref(),
150                OpenOptions::new().write(true).create(true).truncate(true),
151                ambient_authority,
152            )
153        })
154        .await
155        .map(|f| Self::from_std(f.into()))
156    }
157
158    /// Constructs a new instance of `Self` with the options specified by
159    /// `options` by opening the given path as a file using the host process'
160    /// ambient authority.
161    ///
162    /// # Ambient Authority
163    ///
164    /// This function is not sandboxed and may access any path that the host
165    /// process has access to.
166    #[inline]
167    pub async fn open_ambient_with<P: AsRef<Path>>(
168        path: P,
169        options: &OpenOptions,
170        ambient_authority: AmbientAuthority,
171    ) -> io::Result<Self> {
172        let path = path.as_ref().to_path_buf();
173        let options = options.clone();
174        spawn_blocking(move || open_ambient(path.as_ref(), &options, ambient_authority))
175            .await
176            .map(|f| Self::from_std(f.into()))
177    }
178
179    /// Returns a new `OpenOptions` object.
180    ///
181    /// This corresponds to [`async_std::fs::File::options`].
182    #[must_use]
183    #[inline]
184    pub fn options() -> OpenOptions {
185        OpenOptions::new()
186    }
187}
188
189#[cfg(not(target_os = "wasi"))]
190#[inline]
191fn metadata_from(file: &std::fs::File) -> io::Result<Metadata> {
192    Metadata::from_file(file)
193}
194
195#[cfg(target_os = "wasi")]
196#[inline]
197fn metadata_from(file: &std::fs::File) -> io::Result<Metadata> {
198    file.metadata()
199}
200
201#[cfg(not(target_os = "wasi"))]
202#[inline]
203fn permissions_into_std(
204    file: &std::fs::File,
205    permissions: Permissions,
206) -> io::Result<fs::Permissions> {
207    permissions.into_std(file)
208}
209
210#[cfg(target_os = "wasi")]
211#[inline]
212fn permissions_into_std(
213    _file: &std::fs::File,
214    permissions: Permissions,
215) -> io::Result<fs::Permissions> {
216    permissions
217}
218
219// Safety: `FilelikeViewType` is implemented for `std::fs::File`.
220unsafe impl io_lifetimes::views::FilelikeViewType for File {}
221
222#[cfg(not(windows))]
223impl FromRawFd for File {
224    #[inline]
225    unsafe fn from_raw_fd(fd: RawFd) -> Self {
226        Self::from_std(fs::File::from_raw_fd(fd))
227    }
228}
229
230#[cfg(not(windows))]
231impl From<OwnedFd> for File {
232    #[inline]
233    fn from(fd: OwnedFd) -> Self {
234        Self::from_std(fs::File::from(fd))
235    }
236}
237
238#[cfg(windows)]
239impl FromRawHandle for File {
240    #[inline]
241    unsafe fn from_raw_handle(handle: RawHandle) -> Self {
242        Self::from_std(fs::File::from_raw_handle(handle))
243    }
244}
245
246#[cfg(windows)]
247impl From<OwnedHandle> for File {
248    #[inline]
249    fn from(handle: OwnedHandle) -> Self {
250        Self::from_std(fs::File::from(handle))
251    }
252}
253
254#[cfg(not(windows))]
255impl AsRawFd for File {
256    #[inline]
257    fn as_raw_fd(&self) -> RawFd {
258        self.std.as_raw_fd()
259    }
260}
261
262#[cfg(not(windows))]
263impl AsFd for File {
264    #[inline]
265    fn as_fd(&self) -> BorrowedFd<'_> {
266        self.std.as_fd()
267    }
268}
269
270#[cfg(windows)]
271impl AsRawHandle for File {
272    #[inline]
273    fn as_raw_handle(&self) -> RawHandle {
274        self.std.as_raw_handle()
275    }
276}
277
278#[cfg(windows)]
279impl AsHandle for File {
280    #[inline]
281    fn as_handle(&self) -> BorrowedHandle<'_> {
282        self.std.as_handle()
283    }
284}
285
286#[cfg(windows)]
287impl AsRawHandleOrSocket for File {
288    #[inline]
289    fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
290        self.std.as_raw_handle_or_socket()
291    }
292}
293
294#[cfg(windows)]
295impl AsHandleOrSocket for File {
296    #[inline]
297    fn as_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
298        self.std.as_handle_or_socket()
299    }
300}
301
302#[cfg(not(windows))]
303impl IntoRawFd for File {
304    #[inline]
305    fn into_raw_fd(self) -> RawFd {
306        self.std.into_raw_fd()
307    }
308}
309
310#[cfg(not(windows))]
311impl From<File> for OwnedFd {
312    #[inline]
313    fn from(file: File) -> OwnedFd {
314        file.std.into()
315    }
316}
317
318#[cfg(windows)]
319impl IntoRawHandle for File {
320    #[inline]
321    fn into_raw_handle(self) -> RawHandle {
322        self.std.into_raw_handle()
323    }
324}
325
326#[cfg(windows)]
327impl From<File> for OwnedHandle {
328    #[inline]
329    fn from(file: File) -> OwnedHandle {
330        file.std.into()
331    }
332}
333
334#[cfg(windows)]
335impl IntoRawHandleOrSocket for File {
336    #[inline]
337    fn into_raw_handle_or_socket(self) -> RawHandleOrSocket {
338        self.std.into_raw_handle_or_socket()
339    }
340}
341
342#[cfg(windows)]
343impl From<File> for OwnedHandleOrSocket {
344    #[inline]
345    fn from(file: File) -> Self {
346        file.std.into()
347    }
348}
349
350impl Read for File {
351    #[inline]
352    fn poll_read(
353        mut self: Pin<&mut Self>,
354        cx: &mut Context,
355        buf: &mut [u8],
356    ) -> Poll<io::Result<usize>> {
357        Read::poll_read(Pin::new(&mut self.std), cx, buf)
358    }
359
360    #[inline]
361    fn poll_read_vectored(
362        mut self: Pin<&mut Self>,
363        cx: &mut Context,
364        bufs: &mut [IoSliceMut],
365    ) -> Poll<io::Result<usize>> {
366        Read::poll_read_vectored(Pin::new(&mut self.std), cx, bufs)
367    }
368
369    // async_std doesn't have `is_read_vectored`.
370
371    // async_std doesn't have `initializer`.
372}
373
374impl Read for &File {
375    #[inline]
376    fn poll_read(
377        self: Pin<&mut Self>,
378        cx: &mut Context,
379        buf: &mut [u8],
380    ) -> Poll<io::Result<usize>> {
381        Read::poll_read(Pin::new(&mut &self.std), cx, buf)
382    }
383
384    #[inline]
385    fn poll_read_vectored(
386        self: Pin<&mut Self>,
387        cx: &mut Context,
388        bufs: &mut [IoSliceMut],
389    ) -> Poll<io::Result<usize>> {
390        Read::poll_read_vectored(Pin::new(&mut &self.std), cx, bufs)
391    }
392
393    // async_std doesn't have `is_read_vectored`.
394
395    // async_std doesn't have `initializer`.
396}
397
398impl Write for File {
399    #[inline]
400    fn poll_write(
401        mut self: Pin<&mut Self>,
402        cx: &mut Context,
403        buf: &[u8],
404    ) -> Poll<io::Result<usize>> {
405        Write::poll_write(Pin::new(&mut self.std), cx, buf)
406    }
407
408    #[inline]
409    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
410        Write::poll_flush(Pin::new(&mut self.std), cx)
411    }
412
413    #[inline]
414    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
415        Write::poll_close(Pin::new(&mut self.std), cx)
416    }
417
418    #[inline]
419    fn poll_write_vectored(
420        mut self: Pin<&mut Self>,
421        cx: &mut Context,
422        bufs: &[IoSlice],
423    ) -> Poll<io::Result<usize>> {
424        Write::poll_write_vectored(Pin::new(&mut self.std), cx, bufs)
425    }
426
427    // async_std doesn't have `is_write_vectored`.
428
429    // async_std doesn't have `write_all_vectored`.
430}
431
432impl Write for &File {
433    #[inline]
434    fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll<io::Result<usize>> {
435        Write::poll_write(Pin::new(&mut &self.std), cx, buf)
436    }
437
438    #[inline]
439    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
440        Write::poll_flush(Pin::new(&mut &self.std), cx)
441    }
442
443    #[inline]
444    fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
445        Write::poll_close(Pin::new(&mut &self.std), cx)
446    }
447
448    #[inline]
449    fn poll_write_vectored(
450        self: Pin<&mut Self>,
451        cx: &mut Context,
452        bufs: &[IoSlice],
453    ) -> Poll<io::Result<usize>> {
454        Write::poll_write_vectored(Pin::new(&mut &self.std), cx, bufs)
455    }
456
457    // async_std doesn't have `is_write_vectored`.
458
459    // async_std doesn't have `write_all_vectored`.
460}
461
462impl Seek for File {
463    #[inline]
464    fn poll_seek(
465        mut self: Pin<&mut Self>,
466        cx: &mut Context,
467        pos: SeekFrom,
468    ) -> Poll<io::Result<u64>> {
469        Seek::poll_seek(Pin::new(&mut self.std), cx, pos)
470    }
471}
472
473impl Seek for &File {
474    #[inline]
475    fn poll_seek(self: Pin<&mut Self>, cx: &mut Context, pos: SeekFrom) -> Poll<io::Result<u64>> {
476        Seek::poll_seek(Pin::new(&mut &self.std), cx, pos)
477    }
478}
479
480// TODO: Can async_std implement `From<File>` for `process::Stdio`?
481
482// async_std doesn't have `FileExt`.
483
484impl fmt::Debug for File {
485    // Like libstd's version, but doesn't print the path.
486    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487        let mut b = f.debug_struct("File");
488        let file = self.std.as_filelike_view::<std::fs::File>();
489        #[cfg(not(windows))]
490        b.field("fd", &file.as_raw_fd());
491        #[cfg(windows)]
492        b.field("handle", &file.as_raw_handle());
493        if let Ok((read, write)) = is_file_read_write(&file) {
494            b.field("read", &read).field("write", &write);
495        }
496        b.finish()
497    }
498}