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