standard_lib/fs/file/
file.rs

1// TODO: This is only temporary
2#![allow(clippy::missing_panics_doc)]
3
4// FIXME: What happens to the CStrings that I've been using indirectly? I bet they aren't dropped.
5
6use std::fmt::{self, Debug, Formatter};
7use std::io::RawOsError;
8use std::marker::PhantomData;
9
10use libc::{EBADF, EDQUOT, EINTR, EINVAL, EIO, ENOLCK, ENOSPC, EROFS, EWOULDBLOCK, LOCK_EX, LOCK_SH, LOCK_UN, c_int, c_void};
11
12use super::{AccessMode, CloneError, CloseError, LockError, MetadataError, OpenOptions, Read, ReadWrite, SyncError, TryLockError, Write};
13use crate::collections::contiguous::Vector;
14use crate::fs::file::{CreateError, NoCreate, OpenError, TempError};
15use crate::fs::panic::{BadFdPanic, InvalidOpPanic, Panic, UnexpectedErrorPanic};
16use crate::fs::{Abs, Directory, Fd, Metadata, OwnedPath, Path, Rel};
17use crate::fs::error::{IOError, InterruptError, LockMemError, StorageExhaustedError, SyncUnsupportedError, WouldBlockError};
18use crate::util;
19
20pub(crate) const DEF_FILE_MODE: c_int = 0o666;
21
22/// An open file, allowing for reading and writing according to the associated [`AccessMode`]. The
23/// underlying file is guaranteed to exist for the lifetime of the `File`.
24// TODO: More docs here.
25pub struct File<Access: AccessMode = ReadWrite> {
26    pub(crate) _access: PhantomData<fn() -> Access>,
27    pub(crate) fd: Fd,
28}
29
30impl File {
31    pub fn options() -> OpenOptions<ReadWrite, NoCreate> {
32        OpenOptions::<ReadWrite, NoCreate>::new()
33    }
34}
35
36impl<A: AccessMode> File<A> {
37    pub fn metadata(&self) -> Result<Metadata, MetadataError> {
38        self.fd.metadata()
39    }
40
41    pub fn close(self) -> Result<(), CloseError> {
42        self.fd.close()
43    }
44
45    pub fn sync(&self) -> Result<(), SyncError> {
46        // SAFETY: *self.fd is a valid, open file descriptor (guaranteed by File's ownership of Fd,
47        // which maintains the invariant). fsync synchronizes the file's in-core state with storage
48        // and returns -1 on error.
49        if unsafe { libc::fsync(*self.fd) } == -1 {
50            match util::fs::err_no() {
51                EBADF           => BadFdPanic.panic(),
52                EINTR           => Err(InterruptError)?,
53                EIO             => Err(IOError)?,
54                EROFS | EINVAL  => Err(SyncUnsupportedError)?,
55                ENOSPC | EDQUOT => Err(StorageExhaustedError)?,
56                e               => UnexpectedErrorPanic(e).panic(),
57            }
58        }
59        Ok(())
60    }
61
62    // TODO: pub fn seek(&self, )
63
64    // TODO: applicable metadata setters
65
66    pub fn try_clone(&self) -> Result<File, CloneError> {
67        self.fd.try_clone().map(|new_fd| File {
68            _access: PhantomData,
69            fd: new_fd,
70        })
71    }
72
73    pub(crate) fn flock_raw(&self, flags: c_int) -> Result<(), LockError> {
74        // SAFETY: *self.fd is a valid, open file descriptor (guaranteed by File's ownership of Fd).
75        // The flags parameter is validated by the caller to be one of the valid flock operations
76        // (LOCK_EX, LOCK_SH, LOCK_UN).
77        if unsafe { libc::flock(*self.fd, flags) } == -1 {
78            match util::fs::err_no() {
79                EBADF  => BadFdPanic.panic(),
80                EINTR  => Err(InterruptError)?,
81                EINVAL => InvalidOpPanic.panic(),
82                ENOLCK => Err(LockMemError)?,
83                // EWOULDBLOCK gets grouped under unexpected.
84                e      => UnexpectedErrorPanic(e).panic(),
85            }
86        }
87        Ok(())
88    }
89
90    pub fn lock(&self) -> Result<(), LockError> {
91        self.flock_raw(LOCK_EX)
92    }
93
94    pub fn lock_shared(&self) -> Result<(), LockError> {
95        self.flock_raw(LOCK_SH)
96    }
97
98    pub(crate) fn try_flock_raw(&self, flags: c_int) -> Result<(), TryLockError> {
99        // SAFETY: *self.fd is a valid, open file descriptor (guaranteed by File's ownership of Fd).
100        // The flags parameter combined with LOCK_NB is validated by the caller to be one of the
101        // valid non-blocking flock operations.
102        if unsafe { libc::flock(*self.fd, flags | libc::LOCK_NB) } == -1 {
103            match util::fs::err_no() {
104                EBADF       => BadFdPanic.panic(),
105                EINTR       => Err(InterruptError)?,
106                EINVAL      => InvalidOpPanic.panic(),
107                ENOLCK      => Err(LockMemError)?,
108                EWOULDBLOCK => Err(WouldBlockError)?,
109                e           => UnexpectedErrorPanic(e).panic(),
110            }
111        }
112        Ok(())
113    }
114
115    pub fn try_lock(&self) -> Result<(), TryLockError> {
116        self.try_flock_raw(LOCK_EX)
117    }
118
119    pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
120        self.try_flock_raw(LOCK_SH)
121    }
122
123    pub fn unlock(&self) -> Result<(), LockError> {
124        self.flock_raw(LOCK_UN)
125    }
126}
127
128impl File<ReadWrite> {
129    pub fn open<P: Into<OwnedPath<Abs>>>(
130        file_path: P,
131    ) -> Result<File<ReadWrite>, OpenError> {
132        File::options()
133            .open(file_path)
134    }
135
136    pub fn create<P: Into<OwnedPath<Abs>>>(
137        file_path: P,
138        file_mode: u16,
139    ) -> Result<File<ReadWrite>, CreateError> {
140        File::options()
141            .create()
142            .mode(file_mode)
143            .open(file_path)
144    }
145
146    pub fn open_or_create<P: Into<OwnedPath<Abs>>>(
147        file_path: P,
148        file_mode: u16,
149    ) -> Result<File<ReadWrite>, OpenError> {
150        File::options()
151            .create_if_missing()
152            .mode(file_mode)
153            .open(file_path)
154    }
155
156    pub fn open_rel<P: Into<OwnedPath<Rel>>>(
157        relative_to: &Directory,
158        file_path: P
159    ) -> Result<File<ReadWrite>, OpenError> {
160        File::options()
161            .open_rel(relative_to, file_path)
162    }
163
164    pub fn create_rel<P: Into<OwnedPath<Rel>>>(
165        relative_to: &Directory,
166        file_path: P,
167        file_mode: u16,
168    ) -> Result<File<ReadWrite>, CreateError> {
169        File::options()
170            .create()
171            .mode(file_mode)
172            .open_rel(relative_to, file_path)
173    }
174
175    pub fn open_or_create_rel<P: Into<OwnedPath<Rel>>>(
176        relative_to: &Directory,
177        file_path: P,
178        file_mode: u16,
179    ) -> Result<File<ReadWrite>, OpenError> {
180        File::options()
181            .create_if_missing()
182            .mode(file_mode)
183            .open_rel(relative_to, file_path)
184    }
185
186    pub fn create_temp() -> Result<File<ReadWrite>, TempError> {
187        File::<ReadWrite>::options()
188            .create_temp()
189            .mode(0o700)
190            .open(unsafe { Path::<Abs>::from_unchecked("/tmp") })
191    }
192}
193
194
195impl<A: Read> File<A> {
196    pub(crate) fn read_raw(&self, buf: *mut c_void, size: usize) -> Result<usize, RawOsError> {
197        // SAFETY:
198        // - *self.fd is a valid, open file descriptor (guaranteed by File's ownership of Fd).
199        // - buf must point to valid, writable memory of at least `size` bytes. This is the caller's
200        //   responsibility to ensure.
201        // - size must not exceed the buffer's actual capacity. This is the caller's responsibility.
202        match unsafe { libc::read(*self.fd, buf, size) } {
203            -1 => Err(util::fs::err_no()),
204            count => Ok(count as usize),
205        }
206    }
207
208    pub fn read(&self, buf: &mut [u8]) -> Result<usize, RawOsError> {
209        self.read_raw(buf.as_mut_ptr().cast(), buf.len())
210    }
211
212    pub fn read_all_vec(&self) -> Result<Vector<u8>, RawOsError> {
213        // This doesn't read the terminating byte at the moment.
214        let size = self.metadata().unwrap().size as usize; // FIXME
215        let buf: Vector<u8> = Vector::with_cap(size);
216        let (ptr, len, cap) = buf.into_parts();
217
218        match self.read_raw(unsafe { ptr.as_ptr().add(len).cast() }, cap) {
219            Err(err) => {
220                unsafe { drop(Vector::from_parts(ptr, len, cap)); }
221                Err(err)
222            },
223            Ok(count) if size > count => {
    ::core::panicking::panic_fmt(format_args!("not yet implemented: {0}",
            format_args!("Repeat until all bytes are read!")));
}todo!("Repeat until all bytes are read!"), // FIXME
224            Ok(count) => unsafe { Ok(Vector::from_parts(ptr, len + count, cap)) },
225        }
226    }
227
228    pub fn read_all_string(&self) -> Result<String, RawOsError> {
229        Ok(String::from_utf8(self.read_all_vec()?.into()).unwrap())
230    }
231}
232
233impl<A: Write> File<A> {
234    pub(crate) fn write_raw(&self, buf: *const c_void, size: usize) -> Result<usize, RawOsError> {
235        // SAFETY:
236        // - *self.fd is a valid, open file descriptor (guaranteed by File's ownership of Fd).
237        // - buf must point to valid, readable memory of at least `size` bytes. This is the caller's
238        //   responsibility to ensure.
239        // - size must not exceed the buffer's actual length. This is the caller's responsibility.
240        match unsafe { libc::write(*self.fd, buf, size) } {
241            -1 => Err(util::fs::err_no()),
242            count => Ok(count as usize),
243        }
244    }
245
246    pub fn write(&self, buf: &[u8]) -> Result<usize, RawOsError> {
247        self.write_raw(buf.as_ptr().cast(), buf.len())
248    }
249}
250
251impl<A: AccessMode> Debug for File<A> {
252    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
253        f.debug_struct("File")
254            .field("<access>", &util::fmt::raw_type_name::<A>())
255            .field("fd", &self.fd).finish()
256    }
257}