1use crate::fs::{Metadata, OpenOptions, Permissions};
2use async_std::fs;
3use async_std::io::{self, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
4#[cfg(unix)]
5use async_std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
6#[cfg(target_os = "wasi")]
7use async_std::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
8use async_std::task::{spawn_blocking, Context, Poll};
9use cap_primitives::fs::{is_file_read_write, open_ambient};
10use cap_primitives::AmbientAuthority;
11use io_lifetimes::AsFilelike;
12#[cfg(not(windows))]
13use io_lifetimes::{AsFd, BorrowedFd, OwnedFd};
14#[cfg(windows)]
15use io_lifetimes::{AsHandle, BorrowedHandle, OwnedHandle};
16use std::fmt;
17use std::path::Path;
18use std::pin::Pin;
19#[cfg(windows)]
20use {
21 async_std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle},
22 io_extras::os::windows::{
23 AsHandleOrSocket, AsRawHandleOrSocket, BorrowedHandleOrSocket, IntoRawHandleOrSocket,
24 OwnedHandleOrSocket, RawHandleOrSocket,
25 },
26};
27
28#[derive(Clone)]
40pub struct File {
41 pub(crate) std: fs::File,
42}
43
44impl File {
45 #[inline]
51 pub fn from_std(std: fs::File) -> Self {
52 Self { std }
53 }
54
55 #[inline]
57 pub fn into_std(self) -> fs::File {
58 self.std
59 }
60
61 #[inline]
65 pub async fn sync_all(&self) -> io::Result<()> {
66 self.std.sync_all().await
67 }
68
69 #[inline]
74 pub async fn sync_data(&self) -> io::Result<()> {
75 self.std.sync_data().await
76 }
77
78 #[inline]
83 pub async fn set_len(&self, size: u64) -> io::Result<()> {
84 self.std.set_len(size).await
85 }
86
87 #[inline]
91 pub async fn metadata(&self) -> io::Result<Metadata> {
92 let clone = self.clone();
93 spawn_blocking(move || metadata_from(&*clone.std.as_filelike_view::<std::fs::File>())).await
94 }
95
96 #[inline]
102 pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
103 let sync = self.std.as_filelike_view::<std::fs::File>();
104 self.std
105 .set_permissions(permissions_into_std(&sync, perm)?)
106 .await
107 }
108
109 #[inline]
117 pub async fn open_ambient<P: AsRef<Path>>(
118 path: P,
119 ambient_authority: AmbientAuthority,
120 ) -> io::Result<Self> {
121 let path = path.as_ref().to_path_buf();
122 spawn_blocking(move || {
123 open_ambient(
124 path.as_ref(),
125 OpenOptions::new().read(true),
126 ambient_authority,
127 )
128 })
129 .await
130 .map(|f| Self::from_std(f.into()))
131 }
132
133 #[inline]
142 pub async fn create_ambient<P: AsRef<Path>>(
143 path: P,
144 ambient_authority: AmbientAuthority,
145 ) -> io::Result<Self> {
146 let path = path.as_ref().to_path_buf();
147 spawn_blocking(move || {
148 open_ambient(
149 path.as_ref(),
150 OpenOptions::new().write(true).create(true).truncate(true),
151 ambient_authority,
152 )
153 })
154 .await
155 .map(|f| Self::from_std(f.into()))
156 }
157
158 #[inline]
167 pub async fn open_ambient_with<P: AsRef<Path>>(
168 path: P,
169 options: &OpenOptions,
170 ambient_authority: AmbientAuthority,
171 ) -> io::Result<Self> {
172 let path = path.as_ref().to_path_buf();
173 let options = options.clone();
174 spawn_blocking(move || open_ambient(path.as_ref(), &options, ambient_authority))
175 .await
176 .map(|f| Self::from_std(f.into()))
177 }
178
179 #[must_use]
183 #[inline]
184 pub fn options() -> OpenOptions {
185 OpenOptions::new()
186 }
187}
188
189#[cfg(not(target_os = "wasi"))]
190#[inline]
191fn metadata_from(file: &std::fs::File) -> io::Result<Metadata> {
192 Metadata::from_file(file)
193}
194
195#[cfg(target_os = "wasi")]
196#[inline]
197fn metadata_from(file: &std::fs::File) -> io::Result<Metadata> {
198 file.metadata()
199}
200
201#[cfg(not(target_os = "wasi"))]
202#[inline]
203fn permissions_into_std(
204 file: &std::fs::File,
205 permissions: Permissions,
206) -> io::Result<fs::Permissions> {
207 permissions.into_std(file)
208}
209
210#[cfg(target_os = "wasi")]
211#[inline]
212fn permissions_into_std(
213 _file: &std::fs::File,
214 permissions: Permissions,
215) -> io::Result<fs::Permissions> {
216 permissions
217}
218
219unsafe impl io_lifetimes::views::FilelikeViewType for File {}
221
222#[cfg(not(windows))]
223impl FromRawFd for File {
224 #[inline]
225 unsafe fn from_raw_fd(fd: RawFd) -> Self {
226 Self::from_std(fs::File::from_raw_fd(fd))
227 }
228}
229
230#[cfg(not(windows))]
231impl From<OwnedFd> for File {
232 #[inline]
233 fn from(fd: OwnedFd) -> Self {
234 Self::from_std(fs::File::from(fd))
235 }
236}
237
238#[cfg(windows)]
239impl FromRawHandle for File {
240 #[inline]
241 unsafe fn from_raw_handle(handle: RawHandle) -> Self {
242 Self::from_std(fs::File::from_raw_handle(handle))
243 }
244}
245
246#[cfg(windows)]
247impl From<OwnedHandle> for File {
248 #[inline]
249 fn from(handle: OwnedHandle) -> Self {
250 Self::from_std(fs::File::from(handle))
251 }
252}
253
254#[cfg(not(windows))]
255impl AsRawFd for File {
256 #[inline]
257 fn as_raw_fd(&self) -> RawFd {
258 self.std.as_raw_fd()
259 }
260}
261
262#[cfg(not(windows))]
263impl AsFd for File {
264 #[inline]
265 fn as_fd(&self) -> BorrowedFd<'_> {
266 self.std.as_fd()
267 }
268}
269
270#[cfg(windows)]
271impl AsRawHandle for File {
272 #[inline]
273 fn as_raw_handle(&self) -> RawHandle {
274 self.std.as_raw_handle()
275 }
276}
277
278#[cfg(windows)]
279impl AsHandle for File {
280 #[inline]
281 fn as_handle(&self) -> BorrowedHandle<'_> {
282 self.std.as_handle()
283 }
284}
285
286#[cfg(windows)]
287impl AsRawHandleOrSocket for File {
288 #[inline]
289 fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
290 self.std.as_raw_handle_or_socket()
291 }
292}
293
294#[cfg(windows)]
295impl AsHandleOrSocket for File {
296 #[inline]
297 fn as_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
298 self.std.as_handle_or_socket()
299 }
300}
301
302#[cfg(not(windows))]
303impl IntoRawFd for File {
304 #[inline]
305 fn into_raw_fd(self) -> RawFd {
306 self.std.into_raw_fd()
307 }
308}
309
310#[cfg(not(windows))]
311impl From<File> for OwnedFd {
312 #[inline]
313 fn from(file: File) -> OwnedFd {
314 file.std.into()
315 }
316}
317
318#[cfg(windows)]
319impl IntoRawHandle for File {
320 #[inline]
321 fn into_raw_handle(self) -> RawHandle {
322 self.std.into_raw_handle()
323 }
324}
325
326#[cfg(windows)]
327impl From<File> for OwnedHandle {
328 #[inline]
329 fn from(file: File) -> OwnedHandle {
330 file.std.into()
331 }
332}
333
334#[cfg(windows)]
335impl IntoRawHandleOrSocket for File {
336 #[inline]
337 fn into_raw_handle_or_socket(self) -> RawHandleOrSocket {
338 self.std.into_raw_handle_or_socket()
339 }
340}
341
342#[cfg(windows)]
343impl From<File> for OwnedHandleOrSocket {
344 #[inline]
345 fn from(file: File) -> Self {
346 file.std.into()
347 }
348}
349
350impl Read for File {
351 #[inline]
352 fn poll_read(
353 mut self: Pin<&mut Self>,
354 cx: &mut Context,
355 buf: &mut [u8],
356 ) -> Poll<io::Result<usize>> {
357 Read::poll_read(Pin::new(&mut self.std), cx, buf)
358 }
359
360 #[inline]
361 fn poll_read_vectored(
362 mut self: Pin<&mut Self>,
363 cx: &mut Context,
364 bufs: &mut [IoSliceMut],
365 ) -> Poll<io::Result<usize>> {
366 Read::poll_read_vectored(Pin::new(&mut self.std), cx, bufs)
367 }
368
369 }
373
374impl Read for &File {
375 #[inline]
376 fn poll_read(
377 self: Pin<&mut Self>,
378 cx: &mut Context,
379 buf: &mut [u8],
380 ) -> Poll<io::Result<usize>> {
381 Read::poll_read(Pin::new(&mut &self.std), cx, buf)
382 }
383
384 #[inline]
385 fn poll_read_vectored(
386 self: Pin<&mut Self>,
387 cx: &mut Context,
388 bufs: &mut [IoSliceMut],
389 ) -> Poll<io::Result<usize>> {
390 Read::poll_read_vectored(Pin::new(&mut &self.std), cx, bufs)
391 }
392
393 }
397
398impl Write for File {
399 #[inline]
400 fn poll_write(
401 mut self: Pin<&mut Self>,
402 cx: &mut Context,
403 buf: &[u8],
404 ) -> Poll<io::Result<usize>> {
405 Write::poll_write(Pin::new(&mut self.std), cx, buf)
406 }
407
408 #[inline]
409 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
410 Write::poll_flush(Pin::new(&mut self.std), cx)
411 }
412
413 #[inline]
414 fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
415 Write::poll_close(Pin::new(&mut self.std), cx)
416 }
417
418 #[inline]
419 fn poll_write_vectored(
420 mut self: Pin<&mut Self>,
421 cx: &mut Context,
422 bufs: &[IoSlice],
423 ) -> Poll<io::Result<usize>> {
424 Write::poll_write_vectored(Pin::new(&mut self.std), cx, bufs)
425 }
426
427 }
431
432impl Write for &File {
433 #[inline]
434 fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll<io::Result<usize>> {
435 Write::poll_write(Pin::new(&mut &self.std), cx, buf)
436 }
437
438 #[inline]
439 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
440 Write::poll_flush(Pin::new(&mut &self.std), cx)
441 }
442
443 #[inline]
444 fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
445 Write::poll_close(Pin::new(&mut &self.std), cx)
446 }
447
448 #[inline]
449 fn poll_write_vectored(
450 self: Pin<&mut Self>,
451 cx: &mut Context,
452 bufs: &[IoSlice],
453 ) -> Poll<io::Result<usize>> {
454 Write::poll_write_vectored(Pin::new(&mut &self.std), cx, bufs)
455 }
456
457 }
461
462impl Seek for File {
463 #[inline]
464 fn poll_seek(
465 mut self: Pin<&mut Self>,
466 cx: &mut Context,
467 pos: SeekFrom,
468 ) -> Poll<io::Result<u64>> {
469 Seek::poll_seek(Pin::new(&mut self.std), cx, pos)
470 }
471}
472
473impl Seek for &File {
474 #[inline]
475 fn poll_seek(self: Pin<&mut Self>, cx: &mut Context, pos: SeekFrom) -> Poll<io::Result<u64>> {
476 Seek::poll_seek(Pin::new(&mut &self.std), cx, pos)
477 }
478}
479
480impl fmt::Debug for File {
485 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487 let mut b = f.debug_struct("File");
488 let file = self.std.as_filelike_view::<std::fs::File>();
489 #[cfg(not(windows))]
490 b.field("fd", &file.as_raw_fd());
491 #[cfg(windows)]
492 b.field("handle", &file.as_raw_handle());
493 if let Ok((read, write)) = is_file_read_write(&file) {
494 b.field("read", &read).field("write", &write);
495 }
496 b.finish()
497 }
498}