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