1#![allow(clippy::missing_panics_doc)]
3
4use 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
22pub 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 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 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 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 let size = self.metadata().unwrap().size as usize; 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!"), 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}