1 use crate::host;
2 use std::fs::File;
3 use std::os::unix::prelude::{FileTypeExt, FromRawFd, IntoRawFd, RawFd};
4 use std::path::PathBuf;
5 
6 #[derive(Debug)]
7 pub struct FdEntry {
8     pub fd_object: FdObject,
9     pub rights_base: host::__wasi_rights_t,
10     pub rights_inheriting: host::__wasi_rights_t,
11     pub preopen_path: Option<PathBuf>,
12 }
13 
14 impl FdEntry {
from_file(file: File) -> FdEntry15     pub fn from_file(file: File) -> FdEntry {
16         unsafe { FdEntry::from_raw_fd(file.into_raw_fd()) }
17     }
from_raw_fd_for_io_desc(rawfd: RawFd, writable : bool) -> FdEntry18     pub unsafe fn from_raw_fd_for_io_desc(rawfd: RawFd, writable : bool) -> FdEntry {
19         let (ty, mut rights_base, rights_inheriting) =
20         (
21             host::__WASI_FILETYPE_CHARACTER_DEVICE,
22             host::RIGHTS_TTY_BASE,
23             host::RIGHTS_TTY_BASE,
24         );
25 
26         if !writable {
27             rights_base &= !host::__WASI_RIGHT_FD_WRITE as host::__wasi_rights_t;
28         } else {
29             rights_base &= !host::__WASI_RIGHT_FD_READ as host::__wasi_rights_t;
30         }
31 
32         FdEntry {
33             fd_object: FdObject {
34                 ty: ty as u8,
35                 rawfd,
36                 needs_close: true,
37             },
38             rights_base,
39             rights_inheriting,
40             preopen_path: None,
41         }
42     }
43 
44 }
45 
46 impl FromRawFd for FdEntry {
47     // TODO: make this a different function with error handling, rather than using the trait method
from_raw_fd(rawfd: RawFd) -> FdEntry48     unsafe fn from_raw_fd(rawfd: RawFd) -> FdEntry {
49         let (ty, mut rights_base, rights_inheriting) =
50             determine_type_rights(rawfd).expect("can determine file rights");
51 
52         use nix::fcntl::{fcntl, OFlag, F_GETFL};
53         let flags_bits = fcntl(rawfd, F_GETFL).expect("fcntl succeeds");
54         let flags = OFlag::from_bits_truncate(flags_bits);
55         let accmode = flags & OFlag::O_ACCMODE;
56         if accmode == OFlag::O_RDONLY {
57             rights_base &= !host::__WASI_RIGHT_FD_WRITE as host::__wasi_rights_t;
58         } else if accmode == OFlag::O_WRONLY {
59             rights_base &= !host::__WASI_RIGHT_FD_READ as host::__wasi_rights_t;
60         }
61 
62         FdEntry {
63             fd_object: FdObject {
64                 ty: ty as u8,
65                 rawfd,
66                 needs_close: true,
67             },
68             rights_base,
69             rights_inheriting,
70             preopen_path: None,
71         }
72     }
73 }
74 
75 // TODO: can probably make this safe by using fcntl directly rather than going through `File`
determine_type_rights( rawfd: RawFd, ) -> Result< ( host::__wasi_filetype_t, host::__wasi_rights_t, host::__wasi_rights_t, ), host::__wasi_errno_t, >76 pub unsafe fn determine_type_rights(
77     rawfd: RawFd,
78 ) -> Result<
79     (
80         host::__wasi_filetype_t,
81         host::__wasi_rights_t,
82         host::__wasi_rights_t,
83     ),
84     host::__wasi_errno_t,
85 > {
86     let (ty, rights_base, rights_inheriting) = {
87         let file = File::from_raw_fd(rawfd);
88         let ft = file.metadata().unwrap().file_type();
89         // we just make a `File` here for convenience; we don't want it to close when it drops
90         std::mem::forget(file);
91         if ft.is_block_device() {
92             (
93                 host::__WASI_FILETYPE_BLOCK_DEVICE,
94                 host::RIGHTS_BLOCK_DEVICE_BASE,
95                 host::RIGHTS_BLOCK_DEVICE_INHERITING,
96             )
97         } else if ft.is_char_device() {
98             if nix::unistd::isatty(rawfd).unwrap() {
99                 (
100                     host::__WASI_FILETYPE_CHARACTER_DEVICE,
101                     host::RIGHTS_TTY_BASE,
102                     host::RIGHTS_TTY_BASE,
103                 )
104             } else {
105                 (
106                     host::__WASI_FILETYPE_CHARACTER_DEVICE,
107                     host::RIGHTS_CHARACTER_DEVICE_BASE,
108                     host::RIGHTS_CHARACTER_DEVICE_INHERITING,
109                 )
110             }
111         } else if ft.is_dir() {
112             (
113                 host::__WASI_FILETYPE_DIRECTORY,
114                 host::RIGHTS_DIRECTORY_BASE,
115                 host::RIGHTS_DIRECTORY_INHERITING,
116             )
117         } else if ft.is_file() {
118             (
119                 host::__WASI_FILETYPE_REGULAR_FILE,
120                 host::RIGHTS_REGULAR_FILE_BASE,
121                 host::RIGHTS_REGULAR_FILE_INHERITING,
122             )
123         // } else if ft.is_socket() {
124         //     use nix::sys::socket;
125         //     match socket::getsockopt(rawfd, socket::sockopt::SockType).unwrap() {
126         //         socket::SockType::Datagram => (
127         //             host::__WASI_FILETYPE_SOCKET_DGRAM,
128         //             host::RIGHTS_SOCKET_BASE,
129         //             host::RIGHTS_SOCKET_INHERITING,
130         //         ),
131         //         socket::SockType::Stream => (
132         //             host::__WASI_FILETYPE_SOCKET_STREAM,
133         //             host::RIGHTS_SOCKET_BASE,
134         //             host::RIGHTS_SOCKET_INHERITING,
135         //         ),
136         //         _ => return Err(host::__WASI_EINVAL as host::__wasi_errno_t),
137         //     }
138         } else if ft.is_fifo() {
139             (
140                 host::__WASI_FILETYPE_SOCKET_STREAM,
141                 host::RIGHTS_SOCKET_BASE,
142                 host::RIGHTS_SOCKET_INHERITING,
143             )
144         } else {
145             return Err(host::__WASI_EINVAL as host::__wasi_errno_t);
146         }
147     };
148     Ok((
149         ty as host::__wasi_filetype_t,
150         rights_base,
151         rights_inheriting,
152     ))
153 }
154 
155 #[derive(Debug)]
156 pub struct FdObject {
157     pub ty: host::__wasi_filetype_t,
158     pub rawfd: RawFd,
159     pub needs_close: bool,
160     // TODO: directories
161 }
162 
163 impl Drop for FdObject {
drop(&mut self)164     fn drop(&mut self) {
165         if self.needs_close {
166             nix::unistd::close(self.rawfd).unwrap_or_else(|e| eprintln!("FdObject::drop(): {}", e));
167         }
168     }
169 }
170