-
Notifications
You must be signed in to change notification settings - Fork 13
/
libc_fs.rs
247 lines (228 loc) · 8.14 KB
/
libc_fs.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//! Filesystem APIs implemenation using libc
// Thanks, clippy, but it makes the code cleaner so it's not redundant.
#![allow(clippy::redundant_closure_call)]
use crate::fs::{FileSystem, HostErrno, HostMode, HostOpenFlags, HostStat, IOResult};
use std::{convert::TryFrom, ffi::CString, mem::MaybeUninit};
macro_rules! map_flags {
($inflags:ident: $intype:ident -> $resdefault:expr, $($inflag:ident => $outflag:expr,)*) => {{
let mut flags = $resdefault;
{
$(if $inflags & $intype::$inflag == $intype::$inflag {
flags |= $outflag;
})*
}
flags
}};
}
impl HostErrno {
/// Map an errno from `libc` to a cross-platform GDB `HostErrno`.
pub fn from_libc(errno: libc::c_int) -> Self {
match errno {
libc::EPERM => Self::EPERM,
libc::ENOENT => Self::ENOENT,
libc::EINTR => Self::EINTR,
libc::EBADF => Self::EBADF,
libc::EACCES => Self::EACCES,
libc::EFAULT => Self::EFAULT,
libc::EBUSY => Self::EBUSY,
libc::EEXIST => Self::EEXIST,
libc::ENODEV => Self::ENODEV,
libc::ENOTDIR => Self::ENOTDIR,
libc::EISDIR => Self::EISDIR,
libc::EINVAL => Self::EINVAL,
libc::ENFILE => Self::ENFILE,
libc::EMFILE => Self::EMFILE,
libc::EFBIG => Self::EFBIG,
libc::ENOSPC => Self::ENOSPC,
libc::ESPIPE => Self::ESPIPE,
libc::EROFS => Self::EROFS,
libc::ENAMETOOLONG => Self::ENAMETOOLONG,
_ => Self::EUNKNOWN,
}
}
}
impl HostMode {
/// Map a mode from `libc` to a cross-platform GDB `HostErrno`.
pub fn from_libc(mode: libc::mode_t) -> Self {
map_flags! {
mode: libc -> Self::empty(),
S_IFREG => Self::S_IFREG,
S_IFDIR => Self::S_IFDIR,
S_IRUSR => Self::S_IRUSR,
S_IWUSR => Self::S_IWUSR,
S_IXUSR => Self::S_IXUSR,
S_IRGRP => Self::S_IRGRP,
S_IWGRP => Self::S_IWGRP,
S_IXGRP => Self::S_IXGRP,
S_IROTH => Self::S_IROTH,
S_IWOTH => Self::S_IWOTH,
S_IXOTH => Self::S_IXOTH,
}
}
/// Map a mode from a cross-platform GDB `HostErrno` to the native `libc`
/// type.
pub fn into_libc(self) -> libc::mode_t {
map_flags! {
self: Self -> 0,
S_IFREG => libc::S_IFREG,
S_IFDIR => libc::S_IFDIR,
S_IRUSR => libc::S_IRUSR,
S_IWUSR => libc::S_IWUSR,
S_IXUSR => libc::S_IXUSR,
S_IRGRP => libc::S_IRGRP,
S_IWGRP => libc::S_IWGRP,
S_IXGRP => libc::S_IXGRP,
S_IROTH => libc::S_IROTH,
S_IWOTH => libc::S_IWOTH,
S_IXOTH => libc::S_IXOTH,
}
}
}
impl HostOpenFlags {
/// Map flags from `libc` to a cross-platform GDB `HostErrno`.
pub fn from_libc(flags: libc::c_int) -> Self {
map_flags! {
flags: libc -> Self::empty(),
// No O_RDONLY, it's zero and always set
O_WRONLY => Self::O_WRONLY,
O_RDWR => Self::O_RDWR,
O_APPEND => Self::O_APPEND,
O_CREAT => Self::O_CREAT,
O_TRUNC => Self::O_TRUNC,
O_EXCL => Self::O_EXCL,
}
}
/// Map flags from a cross-platform GDB `HostErrno` to the native `libc`
/// type.
pub fn into_libc(self) -> libc::c_int {
let mut res = map_flags! {
self: HostOpenFlags -> 0,
O_WRONLY => libc::O_WRONLY,
O_RDWR => libc::O_RDWR,
O_APPEND => libc::O_APPEND,
O_CREAT => libc::O_CREAT,
O_TRUNC => libc::O_TRUNC,
O_EXCL => libc::O_EXCL,
};
if !self.contains(Self::O_WRONLY) && !self.contains(Self::O_RDWR) {
res |= libc::O_RDONLY;
}
res
}
}
/// A filesystem implementation that delegates all calls to libc. Basically,
/// this lets you use the operating system's notion of files, which is probably
/// what you want.
#[derive(Debug, Default)]
pub struct LibcFS {
/// don't initiate this struct outside of this crate, for future backwards
/// compatibility
_private: (),
}
fn errno() -> HostErrno {
HostErrno::from_libc(unsafe { *libc::__errno_location() })
}
impl FileSystem for LibcFS {
fn host_open(&self, filename: Vec<u8>, flags: HostOpenFlags, mode: HostMode) -> IOResult<u64> {
Ok((|| {
let filename = CString::new(filename).map_err(|_| HostErrno::ENOENT)?;
let fd: libc::c_int =
unsafe { libc::open(filename.as_ptr(), flags.into_libc(), mode.into_libc()) };
if fd >= 0 {
Ok(u64::from(fd as u32))
} else {
Err(errno())
}
})())
}
fn host_close(&self, fd: u64) -> IOResult<()> {
Ok((|| {
if unsafe { libc::close(fd as libc::c_int) } == 0 {
Ok(())
} else {
Err(errno())
}
})())
}
fn host_pread(&self, fd: u64, count: u64, offset: u64) -> IOResult<Vec<u8>> {
Ok((|| {
// Allocate `count` (converted to usize) bytes
let count = usize::try_from(count).unwrap_or(std::usize::MAX);
let mut buf: Vec<u8> = Vec::with_capacity(count);
// Fill bytes as many as `read` bytes
let read: isize = unsafe {
libc::pread(
fd as libc::c_int,
buf.as_mut_ptr() as *mut libc::c_void,
count,
offset as libc::off_t,
)
};
if read >= 0 {
// Mark bytes up to `read` as initiated
unsafe {
buf.set_len(read as usize);
}
Ok(buf)
} else {
Err(errno())
}
})())
}
fn host_pwrite(&self, fd: u64, offset: u64, data: Vec<u8>) -> IOResult<u64> {
Ok((|| {
// Write data, as much as `written` bytes of data
let written: isize = unsafe {
libc::pwrite(
fd as libc::c_int,
data.as_ptr() as *const libc::c_void,
data.len(),
offset as libc::off_t,
)
};
if written >= 0 {
Ok(written as u64)
} else {
Err(errno())
}
})())
}
fn host_fstat(&self, fd: u64) -> IOResult<HostStat> {
Ok((|| {
let mut stat: MaybeUninit<libc::stat> = MaybeUninit::uninit();
if unsafe { libc::fstat(fd as libc::c_int, stat.as_mut_ptr()) } == 0 {
let stat = unsafe { stat.assume_init() };
Ok(HostStat {
// libc's st_dev is different than GDB's st_dev.
// st_dev in GDB is described by https://sourceware.org/gdb/onlinedocs/gdb/struct-stat.html#struct-stat:
// "A value of 0 represents a file, 1 the console."
// While libc's ID is more complex (see `man fstat`)
st_dev: 0,
st_ino: stat.st_ino as _,
st_mode: HostMode::from_libc(stat.st_mode),
st_nlink: stat.st_nlink as _,
st_uid: stat.st_uid as _,
st_gid: stat.st_gid as _,
st_rdev: stat.st_rdev as _,
st_size: stat.st_size as _,
st_blksize: stat.st_blksize as _,
st_blocks: stat.st_blocks as _,
st_atime: stat.st_atime as _,
st_mtime: stat.st_mtime as _,
st_ctime: stat.st_ctime as _,
})
} else {
Err(errno())
}
})())
}
fn host_unlink(&self, _filename: Vec<u8>) -> IOResult<()> {
Err(())
}
fn host_readlink(&self, _filename: Vec<u8>) -> IOResult<Vec<u8>> {
Err(())
}
fn host_setfs(&self, _pid: u64) -> IOResult<()> {
Err(())
}
}