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::{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::panic::{BadFdPanic, InvalidOpPanic, Panic, UnexpectedErrorPanic};
15use crate::fs::{Abs, Directory, Fd, Metadata, Path, Rel};
16use crate::fs::error::{IOError, InterruptError, LockMemError, StorageExhaustedError, SyncUnsupportedError, WouldBlockError};
17use crate::util;
18
19/// An open file, allowing for reading and writing according to the associated [`AccessMode`]. The
20/// underlying file is guaranteed to exist for the lifetime of the `File`.
21// TODO: More docs here.
22pub struct File<Access: AccessMode = ReadWrite> {
23    pub(crate) _access: PhantomData<fn() -> Access>,
24    pub(crate) fd: Fd,
25}
26
27impl<A: AccessMode> File<A> {
28    pub fn options() -> OpenOptions<A> {
29        OpenOptions::<A>::new()
30    }
31    
32    pub fn metadata(&self) -> Result<Metadata, MetadataError> {
33        self.fd.metadata()
34    }
35
36    pub fn close(self) -> Result<(), CloseError> {
37        self.fd.close()
38    }
39
40    pub fn sync(&self) -> Result<(), SyncError> {
41        // SAFETY: There is no memory management here and any returned errors are handled.
42        if unsafe { libc::fsync(*self.fd) } == -1 {
43            match util::fs::err_no() {
44                libc::EBADF => BadFdPanic.panic(),
45                libc::EINTR => Err(InterruptError)?,
46                libc::EIO => Err(IOError)?,
47                libc::EROFS | libc::EINVAL => Err(SyncUnsupportedError)?,
48                libc::ENOSPC | libc::EDQUOT => Err(StorageExhaustedError)?,
49                e => UnexpectedErrorPanic(e).panic(),
50            }
51        }
52        Ok(())
53    }
54
55    // TODO: pub fn seek(&self, )
56
57    // TODO: applicable metadata setters
58
59    pub fn try_clone(&self) -> Result<File, CloneError> {
60        self.fd.try_clone().map(|new_fd| File {
61            _access: PhantomData,
62            fd: new_fd,
63        })
64    }
65
66    pub(crate) fn flock_raw(&self, flags: c_int) -> Result<(), LockError> {
67        if unsafe { libc::flock(*self.fd, flags) } == -1 {
68            match util::fs::err_no() {
69                libc::EBADF => BadFdPanic.panic(),
70                libc::EINTR => Err(InterruptError)?,
71                libc::EINVAL => InvalidOpPanic.panic(),
72                libc::ENOLCK => Err(LockMemError)?,
73                // EWOULDBLOCK gets grouped under unexpected.
74                e => UnexpectedErrorPanic(e).panic(),
75            }
76        }
77        Ok(())
78    }
79
80    pub fn lock(&self) -> Result<(), LockError> {
81        self.flock_raw(libc::LOCK_EX)
82    }
83
84    pub fn lock_shared(&self) -> Result<(), LockError> {
85        self.flock_raw(libc::LOCK_SH)
86    }
87
88    pub(crate) fn try_flock_raw(&self, flags: c_int) -> Result<(), TryLockError> {
89        if unsafe { libc::flock(*self.fd, flags | libc::LOCK_NB) } == -1 {
90            match util::fs::err_no() {
91                libc::EBADF => BadFdPanic.panic(),
92                libc::EINTR => Err(InterruptError)?,
93                libc::EINVAL => InvalidOpPanic.panic(),
94                libc::ENOLCK => Err(LockMemError)?,
95                libc::EWOULDBLOCK => Err(WouldBlockError)?,
96                e => UnexpectedErrorPanic(e).panic(),
97            }
98        }
99        Ok(())
100    }
101
102    pub fn try_lock(&self) -> Result<(), TryLockError> {
103        self.try_flock_raw(libc::LOCK_EX)
104    }
105
106    pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
107        self.try_flock_raw(libc::LOCK_SH)
108    }
109    
110
111    pub fn unlock(&self) -> Result<(), LockError> {
112        self.flock_raw(libc::LOCK_UN)
113    }
114}
115
116impl File<ReadWrite> {
117    pub fn open<P: AsRef<Path<Abs>>>(
118        file_path: P,
119    ) -> Result<File<ReadWrite>, RawOsError> {
120        File::<ReadWrite>::options().open(file_path)
121    }
122
123    pub fn create<P: AsRef<Path<Abs>>>(
124        file_path: P,
125        file_mode: u16,
126    ) -> Result<File<ReadWrite>, RawOsError> {
127        File::<ReadWrite>::options()
128            .create_only()
129            .mode(file_mode)
130            .open(file_path)
131    }
132
133    pub fn open_or_create<P: AsRef<Path<Abs>>>(
134        file_path: P,
135        file_mode: u16,
136    ) -> Result<File<ReadWrite>, RawOsError> {
137        File::<ReadWrite>::options()
138            .create_if_absent()
139            .mode(file_mode)
140            .open(file_path)
141    }
142
143    pub fn open_rel<P: AsRef<Path<Rel>>>(
144        relative_to: &Directory,
145        file_path: P
146    ) -> Result<File<ReadWrite>, RawOsError> {
147        File::<ReadWrite>::options().open_rel(relative_to, file_path)
148    }
149
150    pub fn create_rel<P: AsRef<Path<Rel>>>(
151        relative_to: &Directory,
152        file_path: P,
153        file_mode: u16,
154    ) -> Result<File<ReadWrite>, RawOsError> {
155        File::<ReadWrite>::options()
156            .create_only()
157            .mode(file_mode)
158            .open_rel(relative_to, file_path)
159    }
160
161    pub fn open_or_create_rel<P: AsRef<Path<Rel>>>(
162        relative_to: &Directory,
163        file_path: P,
164        file_mode: u16,
165    ) -> Result<File<ReadWrite>, RawOsError> {
166        File::<ReadWrite>::options()
167            .create_if_absent()
168            .mode(file_mode)
169            .open_rel(relative_to, file_path)
170    }
171
172    pub fn create_temp() -> Result<File<ReadWrite>, RawOsError> {
173        File::<ReadWrite>::options()
174            .no_create()
175            .extra_flags(libc::O_TMPFILE | libc::O_EXCL)
176            .mode(0o700)
177            .open(unsafe { Path::<Abs>::from_unchecked("/tmp") })
178        // TODO: Error handling differences:
179        // * EISDIR: Means temp files are unsupported.
180        // * ENOENT: Can occur if temp files are unsupported.
181        // + EOPNOTSUPP: Fs doesn't support temp files.
182    }
183}
184
185
186impl<A: Read> File<A> {
187    pub(crate) fn read_raw(&self, buf: *mut c_void, size: usize) -> Result<usize, RawOsError> {
188        match unsafe { libc::read(*self.fd, buf, size) } {
189            -1 => Err(util::fs::err_no()),
190            count => Ok(count as usize),
191        }
192    }
193
194    pub fn read(&self, buf: &mut [u8]) -> Result<usize, RawOsError> {
195        self.read_raw(buf.as_mut_ptr().cast(), buf.len())
196    }
197
198    pub fn read_all_vec(&self) -> Result<Vector<u8>, RawOsError> {
199        // This doesn't read the terminating byte at the moment.
200        let size = self.metadata().unwrap().size as usize; // FIXME
201        let buf: Vector<u8> = Vector::with_cap(size);
202        let (ptr, len, cap) = buf.into_parts();
203
204        match self.read_raw(unsafe { ptr.as_ptr().add(len).cast() }, cap) {
205            Err(err) => {
206                unsafe { drop(Vector::from_parts(ptr, len, cap)); }
207                Err(err)
208            },
209            Ok(count) if size > count => todo!("Repeat until all bytes are read!"), // FIXME
210            Ok(count) => unsafe { Ok(Vector::from_parts(ptr, len + count, cap)) },
211        }
212    }
213
214    pub fn read_all_string(&self) -> Result<String, RawOsError> {
215        Ok(String::from_utf8(self.read_all_vec()?.into()).unwrap())
216    }
217}
218
219impl<A: Write> File<A> {
220    pub(crate) fn write_raw(&self, buf: *const c_void, size: usize) -> Result<usize, RawOsError> {
221        match unsafe { libc::write(*self.fd, buf, size) } {
222            -1 => Err(util::fs::err_no()),
223            count => Ok(count as usize),
224        }
225    }
226
227    pub fn write(&self, buf: &[u8]) -> Result<usize, RawOsError> {
228        self.write_raw(buf.as_ptr().cast(), buf.len())
229    }
230}
231
232impl<A: AccessMode> Debug for File<A> {
233    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
234        f.debug_struct("File")
235            .field("<access>", &util::fmt::raw_type_name::<A>())
236            .field("fd", &self.fd).finish()
237    }
238}