ckb_gdb_remote_protocol/fs.rs
1//! Filesystem APIs for GDB, vFile
2
3use bitflags::bitflags;
4use std::io::{self, prelude::*};
5
6/// Errno values for Host I/O operations.
7#[derive(Debug)]
8pub enum HostErrno {
9 /// Operation not permitted (POSIX.1-2001).
10 EPERM = 1,
11 /// No such file or directory (POSIX.1-2001).
12 ///
13 /// Typically, this error results when a specified pathname does not exist,
14 /// or one of the components in the directory prefix of a pathname does not
15 /// exist, or the specified pathname is a dangling symbolic link.
16 ENOENT = 2,
17 /// Interrupted function call (POSIX.1-2001); see signal(7).
18 EINTR = 4,
19 /// Bad file descriptor (POSIX.1-2001).
20 EBADF = 9,
21 /// Permission denied (POSIX.1-2001).
22 EACCES = 13,
23 /// Bad address (POSIX.1-2001).
24 EFAULT = 14,
25 /// Device or resource busy (POSIX.1-2001).
26 EBUSY = 16,
27 /// File exists (POSIX.1-2001).
28 EEXIST = 17,
29 /// No such device (POSIX.1-2001).
30 ENODEV = 19,
31 /// Not a directory (POSIX.1-2001).
32 ENOTDIR = 20,
33 /// Is a directory (POSIX.1-2001).
34 EISDIR = 21,
35 /// Invalid argument (POSIX.1-2001).
36 EINVAL = 22,
37 /// Too many open files in system (POSIX.1-2001). On Linux, this is probably
38 /// a result of encountering the /proc/sys/fs/file-max limit (see proc(5)).
39 ENFILE = 23,
40 /// Too many open files (POSIX.1-2001). Commonly caused by exceeding the
41 /// RLIMIT_NOFILE resource limit described in getrlimit(2).
42 EMFILE = 24,
43 /// File too large (POSIX.1-2001).
44 EFBIG = 27,
45 /// No space left on device (POSIX.1-2001).
46 ENOSPC = 28,
47 /// Invalid seek (POSIX.1-2001).
48 ESPIPE = 29,
49 /// Read-only filesystem (POSIX.1-2001).
50 EROFS = 30,
51 /// Filename too long (POSIX.1-2001).
52 ENAMETOOLONG = 91,
53 /// Unknown errno - there may not be a GDB mapping for this value
54 EUNKNOWN = 9999,
55}
56
57/// The result type for host I/O operations. Return error if the operation
58/// in question is not implemented. Otherwise, the success type indicates
59/// whether the operation succeeded, with `HostErrno` values for failure.
60pub type IOResult<T> = Result<Result<T, HostErrno>, ()>;
61
62bitflags! {
63 /// Host file permissions.
64 pub struct HostMode: u32 {
65 /// A regular file.
66 const S_IFREG = 0o100000;
67 /// A directory.
68 const S_IFDIR = 0o40000;
69 /// User read permissions.
70 const S_IRUSR = 0o400;
71 /// User write permissions.
72 const S_IWUSR = 0o200;
73 /// User execute permissions.
74 const S_IXUSR = 0o100;
75 /// Group read permissions.
76 const S_IRGRP = 0o40;
77 /// Group write permissions
78 const S_IWGRP = 0o20;
79 /// Group execute permissions.
80 const S_IXGRP = 0o10;
81 /// World read permissions.
82 const S_IROTH = 0o4;
83 /// World write permissions
84 const S_IWOTH = 0o2;
85 /// World execute permissions.
86 const S_IXOTH = 0o1;
87 }
88}
89
90bitflags! {
91 // The read/write flags below may look a little weird, but that is the way
92 // they are defined in the protocol.
93 /// Host flags for opening files.
94 pub struct HostOpenFlags: u32 {
95 /// A read-only file.
96 const O_RDONLY = 0x0;
97 /// A write-only file.
98 const O_WRONLY = 0x1;
99 /// A read-write file.
100 const O_RDWR = 0x2;
101 /// Append to an existing file.
102 const O_APPEND = 0x8;
103 /// Create a non-existent file.
104 const O_CREAT = 0x200;
105 /// Truncate an existing file.
106 const O_TRUNC = 0x400;
107 /// Exclusive access.
108 const O_EXCL = 0x800;
109 }
110}
111
112/// Data returned by a host fstat request. The members of this structure are
113/// specified by the remote protocol; conversion of actual host stat
114/// information into this structure may therefore require truncation of some
115/// members.
116#[derive(Debug)]
117pub struct HostStat {
118 /// The device.
119 pub st_dev: u32,
120 /// The inode.
121 pub st_ino: u32,
122 /// Protection bits.
123 pub st_mode: HostMode,
124 /// The number of hard links.
125 pub st_nlink: u32,
126 /// The user id of the owner.
127 pub st_uid: u32,
128 /// The group id of the owner.
129 pub st_gid: u32,
130 /// The device type, if an inode device.
131 pub st_rdev: u32,
132 /// The size of the file in bytes.
133 pub st_size: u64,
134 /// The blocksize for the filesystem.
135 pub st_blksize: u64,
136 /// The number of blocks allocated.
137 pub st_blocks: u64,
138 /// The last time the file was accessed, in seconds since the epoch.
139 pub st_atime: u32,
140 /// The last time the file was modified, in seconds since the epoch.
141 pub st_mtime: u32,
142 /// The last time the file was changed, in seconds since the epoch.
143 pub st_ctime: u32,
144}
145
146// Having to write out all the fields for these two operations is annoying,
147// but the alternatives are even more annoying. For instance, we could
148// represent HostStat as a big array, with fields at appropriate offsets, but
149// we'd have to write a bunch of accessor methods. Note that #[repr(C)]
150// isn't quite good enough, since that might introduce C-mandated padding
151// into the structure.
152pub fn write_stat<W>(writer: &mut W, stat: HostStat) -> io::Result<()>
153where
154 W: Write,
155{
156 use byteorder::{BigEndian, WriteBytesExt};
157
158 writer.write_u32::<BigEndian>(stat.st_dev)?;
159 writer.write_u32::<BigEndian>(stat.st_ino)?;
160 writer.write_u32::<BigEndian>(stat.st_mode.bits())?;
161 writer.write_u32::<BigEndian>(stat.st_nlink)?;
162 writer.write_u32::<BigEndian>(stat.st_uid)?;
163 writer.write_u32::<BigEndian>(stat.st_gid)?;
164 writer.write_u32::<BigEndian>(stat.st_rdev)?;
165 writer.write_u64::<BigEndian>(stat.st_size)?;
166 writer.write_u64::<BigEndian>(stat.st_blksize)?;
167 writer.write_u64::<BigEndian>(stat.st_blocks)?;
168 writer.write_u32::<BigEndian>(stat.st_atime)?;
169 writer.write_u32::<BigEndian>(stat.st_mtime)?;
170 writer.write_u32::<BigEndian>(stat.st_ctime)
171}
172
173#[allow(dead_code)]
174pub fn read_stat(v: &[u8]) -> io::Result<HostStat> {
175 use byteorder::{BigEndian, ReadBytesExt};
176 use std::io::Cursor;
177
178 let mut r = Cursor::new(v);
179 let st_dev = r.read_u32::<BigEndian>()?;
180 let st_ino = r.read_u32::<BigEndian>()?;
181 let st_mode = HostMode::from_bits_truncate(r.read_u32::<BigEndian>()?);
182 let st_nlink = r.read_u32::<BigEndian>()?;
183 let st_uid = r.read_u32::<BigEndian>()?;
184 let st_gid = r.read_u32::<BigEndian>()?;
185 let st_rdev = r.read_u32::<BigEndian>()?;
186 let st_size = r.read_u64::<BigEndian>()?;
187 let st_blksize = r.read_u64::<BigEndian>()?;
188 let st_blocks = r.read_u64::<BigEndian>()?;
189 let st_atime = r.read_u32::<BigEndian>()?;
190 let st_mtime = r.read_u32::<BigEndian>()?;
191 let st_ctime = r.read_u32::<BigEndian>()?;
192
193 Ok(HostStat {
194 st_dev,
195 st_ino,
196 st_mode,
197 st_nlink,
198 st_uid,
199 st_gid,
200 st_rdev,
201 st_size,
202 st_blksize,
203 st_blocks,
204 st_atime,
205 st_mtime,
206 st_ctime,
207 })
208}
209
210/// TODO: doc
211pub trait FileSystem {
212 /// Open a file on the remote stub's current filesystem.
213 fn host_open(&self, _filename: Vec<u8>, _flags: HostOpenFlags, _mode: HostMode) -> IOResult<u64> {
214 Err(())
215 }
216
217 /// Close a file opened with `host_open`.
218 fn host_close(&self, _fd: u64) -> IOResult<()> {
219 Err(())
220 }
221
222 /// Read data from an open file at the given offset.
223 fn host_pread(&self, _fd: u64, _count: u64, _offset: u64) -> IOResult<Vec<u8>> {
224 Err(())
225 }
226
227 /// Write data to an open file at the given offset.
228 fn host_pwrite(&self, _fd: u64, _offset: u64, _data: Vec<u8>) -> IOResult<u64> {
229 Err(())
230 }
231
232 /// Return a `HostStat` describing the attributes of the given open file.
233 fn host_fstat(&self, _fd: u64) -> IOResult<HostStat> {
234 Err(())
235 }
236
237 /// Remove a file from the remote stub's current filesystem.
238 fn host_unlink(&self, _filename: Vec<u8>) -> IOResult<()> {
239 Err(())
240 }
241
242 /// Read the contents of a symbolic link on the remote stub's current filesystem.
243 fn host_readlink(&self, _filename: Vec<u8>) -> IOResult<Vec<u8>> {
244 Err(())
245 }
246
247 /// Set the current filesystem for subsequent host I/O requests. If the
248 /// given pid is 0, select the filesystem of the remote stub. Otherwise,
249 /// select the filesystem as seen by the process with the given pid.
250 fn host_setfs(&self, _pid: u64) -> IOResult<()> {
251 Err(())
252 }
253}
254
255#[test]
256fn stat_size() {
257 // See https://sourceware.org/gdb/onlinedocs/gdb/struct-stat.html#struct-stat
258 // 10 int fields (32 bits, 4 bytes)
259 // 3 long fields (64 bits, 8 bytes)
260 use std::mem;
261 assert_eq!(mem::size_of::<HostStat>(), 4 * 10 + 8 * 3);
262}