cap_std/fs/
file.rs

1use crate::fs::{Metadata, OpenOptions, Permissions};
2use cap_primitives::fs::{is_file_read_write, open_ambient};
3use cap_primitives::AmbientAuthority;
4#[cfg(not(windows))]
5use io_extras::os::rustix::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
6#[cfg(not(windows))]
7use io_lifetimes::{AsFd, BorrowedFd, OwnedFd};
8#[cfg(windows)]
9use io_lifetimes::{AsHandle, BorrowedHandle, OwnedHandle};
10use std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
11use std::path::Path;
12use std::{fmt, fs, process};
13#[cfg(windows)]
14use {
15    io_extras::os::windows::{
16        AsHandleOrSocket, AsRawHandleOrSocket, BorrowedHandleOrSocket, IntoRawHandleOrSocket,
17        OwnedHandleOrSocket, RawHandleOrSocket,
18    },
19    std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle},
20};
21
22/// A reference to an open file on a filesystem.
23///
24/// This corresponds to [`std::fs::File`].
25///
26/// This `File` has no `open` or `create` methods. To open or create a file,
27/// first obtain a [`Dir`] containing the path, and then call [`Dir::open`] or
28/// [`Dir::create`].
29///
30/// [`Dir`]: crate::fs::Dir
31/// [`Dir::open`]: crate::fs::Dir::open
32/// [`Dir::create`]: crate::fs::Dir::create
33pub struct File {
34    pub(crate) std: fs::File,
35}
36
37impl File {
38    /// Constructs a new instance of `Self` from the given [`std::fs::File`].
39    ///
40    /// This grants access the resources the `std::fs::File` instance already
41    /// has access to.
42    #[inline]
43    pub fn from_std(std: fs::File) -> Self {
44        Self { std }
45    }
46
47    /// Consumes `self` and returns a [`std::fs::File`].
48    #[inline]
49    pub fn into_std(self) -> fs::File {
50        self.std
51    }
52
53    /// Attempts to sync all OS-internal metadata to disk.
54    ///
55    /// This corresponds to [`std::fs::File::sync_all`].
56    #[inline]
57    pub fn sync_all(&self) -> io::Result<()> {
58        self.std.sync_all()
59    }
60
61    /// This function is similar to `sync_all`, except that it may not
62    /// synchronize file metadata to a filesystem.
63    ///
64    /// This corresponds to [`std::fs::File::sync_data`].
65    #[inline]
66    pub fn sync_data(&self) -> io::Result<()> {
67        self.std.sync_data()
68    }
69
70    /// Truncates or extends the underlying file, updating the size of this
71    /// file to become size.
72    ///
73    /// This corresponds to [`std::fs::File::set_len`].
74    #[inline]
75    pub fn set_len(&self, size: u64) -> io::Result<()> {
76        self.std.set_len(size)
77    }
78
79    /// Queries metadata about the underlying file.
80    ///
81    /// This corresponds to [`std::fs::File::metadata`].
82    #[inline]
83    pub fn metadata(&self) -> io::Result<Metadata> {
84        metadata_from(&self.std)
85    }
86
87    /// Creates a new `File` instance that shares the same underlying file
88    /// handle as the existing `File` instance.
89    ///
90    /// This corresponds to [`std::fs::File::try_clone`].
91    #[inline]
92    pub fn try_clone(&self) -> io::Result<Self> {
93        let file = self.std.try_clone()?;
94        Ok(Self::from_std(file))
95    }
96
97    /// Changes the permissions on the underlying file.
98    ///
99    /// This corresponds to [`std::fs::File::set_permissions`].
100    #[inline]
101    pub fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
102        self.std
103            .set_permissions(permissions_into_std(&self.std, perm)?)
104    }
105
106    /// Constructs a new instance of `Self` in read-only mode by opening the
107    /// given path as a file using the host process' ambient authority.
108    ///
109    /// # Ambient Authority
110    ///
111    /// This function is not sandboxed and may access any path that the host
112    /// process has access to.
113    #[inline]
114    pub fn open_ambient<P: AsRef<Path>>(
115        path: P,
116        ambient_authority: AmbientAuthority,
117    ) -> io::Result<Self> {
118        let std = open_ambient(
119            path.as_ref(),
120            OpenOptions::new().read(true),
121            ambient_authority,
122        )?;
123        Ok(Self::from_std(std))
124    }
125
126    /// Constructs a new instance of `Self` in write-only mode by opening,
127    /// creating or truncating, the given path as a file using the host
128    /// process' ambient authority.
129    ///
130    /// # Ambient Authority
131    ///
132    /// This function is not sandboxed and may access any path that the host
133    /// process has access to.
134    #[inline]
135    pub fn create_ambient<P: AsRef<Path>>(
136        path: P,
137        ambient_authority: AmbientAuthority,
138    ) -> io::Result<Self> {
139        let std = open_ambient(
140            path.as_ref(),
141            OpenOptions::new().write(true).create(true).truncate(true),
142            ambient_authority,
143        )?;
144        Ok(Self::from_std(std))
145    }
146
147    /// Constructs a new instance of `Self` with the options specified by
148    /// `options` by opening the given path as a file using the host process'
149    /// ambient authority.
150    ///
151    /// # Ambient Authority
152    ///
153    /// This function is not sandboxed and may access any path that the host
154    /// process has access to.
155    #[inline]
156    pub fn open_ambient_with<P: AsRef<Path>>(
157        path: P,
158        options: &OpenOptions,
159        ambient_authority: AmbientAuthority,
160    ) -> io::Result<Self> {
161        let std = open_ambient(path.as_ref(), options, ambient_authority)?;
162        Ok(Self::from_std(std))
163    }
164
165    /// Returns a new `OpenOptions` object.
166    ///
167    /// This corresponds to [`std::fs::File::options`].
168    #[must_use]
169    #[inline]
170    pub fn options() -> OpenOptions {
171        OpenOptions::new()
172    }
173}
174
175#[inline]
176fn metadata_from(file: &fs::File) -> io::Result<Metadata> {
177    Metadata::from_file(file)
178}
179
180#[inline]
181fn permissions_into_std(file: &fs::File, permissions: Permissions) -> io::Result<fs::Permissions> {
182    permissions.into_std(file)
183}
184
185// Safety: `FilelikeViewType` is implemented for `std::fs::File`.
186unsafe impl io_lifetimes::views::FilelikeViewType for File {}
187
188#[cfg(not(windows))]
189impl FromRawFd for File {
190    #[inline]
191    unsafe fn from_raw_fd(fd: RawFd) -> Self {
192        Self::from_std(fs::File::from_raw_fd(fd))
193    }
194}
195
196#[cfg(not(windows))]
197impl From<OwnedFd> for File {
198    #[inline]
199    fn from(fd: OwnedFd) -> Self {
200        Self::from_std(fs::File::from(fd))
201    }
202}
203
204#[cfg(windows)]
205impl FromRawHandle for File {
206    #[inline]
207    unsafe fn from_raw_handle(handle: RawHandle) -> Self {
208        Self::from_std(fs::File::from_raw_handle(handle))
209    }
210}
211
212#[cfg(windows)]
213impl From<OwnedHandle> for File {
214    #[inline]
215    fn from(handle: OwnedHandle) -> Self {
216        Self::from_std(fs::File::from(handle))
217    }
218}
219
220#[cfg(not(windows))]
221impl AsRawFd for File {
222    #[inline]
223    fn as_raw_fd(&self) -> RawFd {
224        self.std.as_raw_fd()
225    }
226}
227
228#[cfg(not(windows))]
229impl AsFd for File {
230    #[inline]
231    fn as_fd(&self) -> BorrowedFd<'_> {
232        self.std.as_fd()
233    }
234}
235
236#[cfg(windows)]
237impl AsRawHandle for File {
238    #[inline]
239    fn as_raw_handle(&self) -> RawHandle {
240        self.std.as_raw_handle()
241    }
242}
243
244#[cfg(windows)]
245impl AsHandle for File {
246    #[inline]
247    fn as_handle(&self) -> BorrowedHandle<'_> {
248        self.std.as_handle()
249    }
250}
251
252#[cfg(windows)]
253impl AsRawHandleOrSocket for File {
254    #[inline]
255    fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
256        self.std.as_raw_handle_or_socket()
257    }
258}
259
260#[cfg(windows)]
261impl AsHandleOrSocket for File {
262    #[inline]
263    fn as_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
264        self.std.as_handle_or_socket()
265    }
266}
267
268#[cfg(not(windows))]
269impl IntoRawFd for File {
270    #[inline]
271    fn into_raw_fd(self) -> RawFd {
272        self.std.into_raw_fd()
273    }
274}
275
276#[cfg(not(windows))]
277impl From<File> for OwnedFd {
278    #[inline]
279    fn from(file: File) -> OwnedFd {
280        file.std.into()
281    }
282}
283
284#[cfg(windows)]
285impl IntoRawHandle for File {
286    #[inline]
287    fn into_raw_handle(self) -> RawHandle {
288        self.std.into_raw_handle()
289    }
290}
291
292#[cfg(windows)]
293impl From<File> for OwnedHandle {
294    #[inline]
295    fn from(file: File) -> OwnedHandle {
296        file.std.into()
297    }
298}
299
300#[cfg(windows)]
301impl IntoRawHandleOrSocket for File {
302    #[inline]
303    fn into_raw_handle_or_socket(self) -> RawHandleOrSocket {
304        self.std.into_raw_handle_or_socket()
305    }
306}
307
308#[cfg(windows)]
309impl From<File> for OwnedHandleOrSocket {
310    #[inline]
311    fn from(file: File) -> Self {
312        file.std.into()
313    }
314}
315
316impl Read for File {
317    #[inline]
318    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
319        self.std.read(buf)
320    }
321
322    #[inline]
323    fn read_vectored(&mut self, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
324        self.std.read_vectored(bufs)
325    }
326
327    #[inline]
328    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
329        self.std.read_exact(buf)
330    }
331
332    #[inline]
333    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
334        self.std.read_to_end(buf)
335    }
336
337    #[inline]
338    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
339        self.std.read_to_string(buf)
340    }
341
342    #[cfg(can_vector)]
343    #[inline]
344    fn is_read_vectored(&self) -> bool {
345        self.std.is_read_vectored()
346    }
347}
348
349impl Read for &File {
350    #[inline]
351    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
352        (&mut &self.std).read(buf)
353    }
354
355    #[inline]
356    fn read_vectored(&mut self, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
357        (&mut &self.std).read_vectored(bufs)
358    }
359
360    #[inline]
361    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
362        (&mut &self.std).read_exact(buf)
363    }
364
365    #[inline]
366    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
367        (&mut &self.std).read_to_end(buf)
368    }
369
370    #[inline]
371    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
372        (&mut &self.std).read_to_string(buf)
373    }
374
375    #[cfg(can_vector)]
376    #[inline]
377    fn is_read_vectored(&self) -> bool {
378        self.std.is_read_vectored()
379    }
380}
381
382impl Write for File {
383    #[inline]
384    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
385        self.std.write(buf)
386    }
387
388    #[inline]
389    fn flush(&mut self) -> io::Result<()> {
390        self.std.flush()
391    }
392
393    #[inline]
394    fn write_vectored(&mut self, bufs: &[IoSlice]) -> io::Result<usize> {
395        self.std.write_vectored(bufs)
396    }
397
398    #[inline]
399    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
400        self.std.write_all(buf)
401    }
402
403    #[cfg(can_vector)]
404    #[inline]
405    fn is_write_vectored(&self) -> bool {
406        self.std.is_write_vectored()
407    }
408
409    #[cfg(write_all_vectored)]
410    #[inline]
411    fn write_all_vectored(&mut self, bufs: &mut [IoSlice]) -> io::Result<()> {
412        self.std.write_all_vectored(bufs)
413    }
414}
415
416impl Write for &File {
417    #[inline]
418    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
419        (&mut &self.std).write(buf)
420    }
421
422    #[inline]
423    fn flush(&mut self) -> io::Result<()> {
424        (&mut &self.std).flush()
425    }
426
427    #[inline]
428    fn write_vectored(&mut self, bufs: &[IoSlice]) -> io::Result<usize> {
429        (&mut &self.std).write_vectored(bufs)
430    }
431
432    #[inline]
433    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
434        (&mut &self.std).write_all(buf)
435    }
436
437    #[cfg(can_vector)]
438    #[inline]
439    fn is_write_vectored(&self) -> bool {
440        self.std.is_write_vectored()
441    }
442
443    #[cfg(write_all_vectored)]
444    #[inline]
445    fn write_all_vectored(&mut self, bufs: &mut [IoSlice]) -> io::Result<()> {
446        (&mut &self.std).write_all_vectored(bufs)
447    }
448}
449
450impl Seek for File {
451    #[inline]
452    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
453        self.std.seek(pos)
454    }
455
456    #[inline]
457    fn stream_position(&mut self) -> io::Result<u64> {
458        self.std.stream_position()
459    }
460}
461
462impl Seek for &File {
463    #[inline]
464    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
465        (&mut &self.std).seek(pos)
466    }
467
468    #[inline]
469    fn stream_position(&mut self) -> io::Result<u64> {
470        (&mut &self.std).stream_position()
471    }
472}
473
474impl From<File> for process::Stdio {
475    #[inline]
476    fn from(file: File) -> Self {
477        From::<fs::File>::from(file.std)
478    }
479}
480
481#[cfg(unix)]
482impl crate::fs::FileExt for File {
483    #[inline]
484    fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
485        std::os::unix::fs::FileExt::read_at(&self.std, buf, offset)
486    }
487
488    #[inline]
489    fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
490        std::os::unix::fs::FileExt::write_at(&self.std, buf, offset)
491    }
492
493    #[inline]
494    fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
495        std::os::unix::fs::FileExt::read_exact_at(&self.std, buf, offset)
496    }
497
498    #[inline]
499    fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
500        std::os::unix::fs::FileExt::write_all_at(&self.std, buf, offset)
501    }
502}
503
504#[cfg(target_os = "wasi")]
505impl crate::fs::FileExt for File {
506    #[inline]
507    fn read_at(&self, bufs: &mut [u8], offset: u64) -> io::Result<usize> {
508        std::os::wasi::fs::FileExt::read_at(&self.std, bufs, offset)
509    }
510
511    #[inline]
512    fn write_at(&self, bufs: &[u8], offset: u64) -> io::Result<usize> {
513        std::os::wasi::fs::FileExt::write_at(&self.std, bufs, offset)
514    }
515
516    #[inline]
517    fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
518        std::os::wasi::fs::FileExt::read_vectored_at(&self.std, bufs, offset)
519    }
520
521    #[inline]
522    fn write_vectored_at(&self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
523        std::os::wasi::fs::FileExt::write_vectored_at(&self.std, bufs, offset)
524    }
525
526    #[inline]
527    fn tell(&self) -> std::result::Result<u64, io::Error> {
528        std::os::wasi::fs::FileExt::tell(&self.std)
529    }
530
531    #[inline]
532    fn fdstat_set_flags(&self, flags: u16) -> std::result::Result<(), io::Error> {
533        std::os::wasi::fs::FileExt::fdstat_set_flags(&self.std, flags)
534    }
535
536    #[inline]
537    fn fdstat_set_rights(
538        &self,
539        rights: u64,
540        inheriting: u64,
541    ) -> std::result::Result<(), io::Error> {
542        std::os::wasi::fs::FileExt::fdstat_set_rights(&self.std, rights, inheriting)
543    }
544
545    #[inline]
546    fn advise(&self, offset: u64, len: u64, advice: u8) -> std::result::Result<(), io::Error> {
547        std::os::wasi::fs::FileExt::advise(&self.std, offset, len, advice)
548    }
549
550    #[inline]
551    fn allocate(&self, offset: u64, len: u64) -> std::result::Result<(), io::Error> {
552        std::os::wasi::fs::FileExt::allocate(&self.std, offset, len)
553    }
554
555    #[inline]
556    fn create_directory<P: AsRef<Path>>(&self, dir: P) -> std::result::Result<(), io::Error> {
557        std::os::wasi::fs::FileExt::create_directory(&self.std, dir)
558    }
559
560    #[inline]
561    fn read_link<P: AsRef<Path>>(
562        &self,
563        path: P,
564    ) -> std::result::Result<std::path::PathBuf, io::Error> {
565        std::os::wasi::fs::FileExt::read_link(&self.std, path)
566    }
567
568    #[inline]
569    fn metadata_at<P: AsRef<Path>>(
570        &self,
571        lookup_flags: u32,
572        path: P,
573    ) -> std::result::Result<std::fs::Metadata, io::Error> {
574        std::os::wasi::fs::FileExt::metadata_at(&self.std, lookup_flags, path)
575    }
576
577    #[inline]
578    fn remove_file<P: AsRef<Path>>(&self, path: P) -> std::result::Result<(), io::Error> {
579        std::os::wasi::fs::FileExt::remove_file(&self.std, path)
580    }
581
582    #[inline]
583    fn remove_directory<P: AsRef<Path>>(&self, path: P) -> std::result::Result<(), io::Error> {
584        std::os::wasi::fs::FileExt::remove_directory(&self.std, path)
585    }
586}
587
588#[cfg(windows)]
589impl crate::fs::FileExt for File {
590    #[inline]
591    fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
592        std::os::windows::fs::FileExt::seek_read(&self.std, buf, offset)
593    }
594
595    #[inline]
596    fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
597        std::os::windows::fs::FileExt::seek_write(&self.std, buf, offset)
598    }
599}
600
601impl fmt::Debug for File {
602    // Like libstd's version, but doesn't print the path.
603    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
604        let mut b = f.debug_struct("File");
605        #[cfg(not(windows))]
606        b.field("fd", &self.std.as_raw_fd());
607        #[cfg(windows)]
608        b.field("handle", &self.std.as_raw_handle());
609        if let Ok((read, write)) = is_file_read_write(&self.std) {
610            b.field("read", &read).field("write", &write);
611        }
612        b.finish()
613    }
614}