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}