/rust/registry/src/index.crates.io-6f17d22bba15001f/p9-0.3.2/src/server/mod.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2018 The ChromiumOS Authors |
2 | | // Use of this source code is governed by a BSD-style license that can be |
3 | | // found in the LICENSE file. |
4 | | |
5 | | mod read_dir; |
6 | | |
7 | | use std::cmp::min; |
8 | | use std::collections::btree_map; |
9 | | use std::collections::BTreeMap; |
10 | | use std::ffi::CStr; |
11 | | use std::ffi::CString; |
12 | | use std::fs::File; |
13 | | use std::io; |
14 | | use std::io::Cursor; |
15 | | use std::io::Read; |
16 | | use std::io::Write; |
17 | | use std::mem; |
18 | | use std::mem::MaybeUninit; |
19 | | use std::ops::Deref; |
20 | | use std::os::unix::ffi::OsStrExt; |
21 | | use std::os::unix::fs::FileExt; |
22 | | use std::os::unix::io::AsRawFd; |
23 | | use std::os::unix::io::FromRawFd; |
24 | | use std::os::unix::io::RawFd; |
25 | | use std::path::Path; |
26 | | use std::str::FromStr; |
27 | | |
28 | | #[cfg(target_os = "android")] |
29 | | use libc::__fsid_t as fsid_t; |
30 | | #[cfg(not(target_os = "android"))] |
31 | | use libc::fsid_t; |
32 | | use read_dir::read_dir; |
33 | | use serde::Deserialize; |
34 | | use serde::Serialize; |
35 | | |
36 | | use crate::protocol::*; |
37 | | use crate::syscall; |
38 | | |
39 | | // Tlopen and Tlcreate flags. Taken from "include/net/9p/9p.h" in the linux tree. |
40 | | const P9_RDONLY: u32 = 0o00000000; |
41 | | const P9_WRONLY: u32 = 0o00000001; |
42 | | const P9_RDWR: u32 = 0o00000002; |
43 | | const P9_NOACCESS: u32 = 0o00000003; |
44 | | const P9_CREATE: u32 = 0o00000100; |
45 | | const P9_EXCL: u32 = 0o00000200; |
46 | | const P9_NOCTTY: u32 = 0o00000400; |
47 | | const P9_TRUNC: u32 = 0o00001000; |
48 | | const P9_APPEND: u32 = 0o00002000; |
49 | | const P9_NONBLOCK: u32 = 0o00004000; |
50 | | const P9_DSYNC: u32 = 0o00010000; |
51 | | const P9_FASYNC: u32 = 0o00020000; |
52 | | const P9_DIRECT: u32 = 0o00040000; |
53 | | const P9_LARGEFILE: u32 = 0o00100000; |
54 | | const P9_DIRECTORY: u32 = 0o00200000; |
55 | | const P9_NOFOLLOW: u32 = 0o00400000; |
56 | | const P9_NOATIME: u32 = 0o01000000; |
57 | | const _P9_CLOEXEC: u32 = 0o02000000; |
58 | | const P9_SYNC: u32 = 0o04000000; |
59 | | |
60 | | // Mapping from 9P flags to libc flags. |
61 | | const MAPPED_FLAGS: [(u32, i32); 16] = [ |
62 | | (P9_WRONLY, libc::O_WRONLY), |
63 | | (P9_RDWR, libc::O_RDWR), |
64 | | (P9_CREATE, libc::O_CREAT), |
65 | | (P9_EXCL, libc::O_EXCL), |
66 | | (P9_NOCTTY, libc::O_NOCTTY), |
67 | | (P9_TRUNC, libc::O_TRUNC), |
68 | | (P9_APPEND, libc::O_APPEND), |
69 | | (P9_NONBLOCK, libc::O_NONBLOCK), |
70 | | (P9_DSYNC, libc::O_DSYNC), |
71 | | (P9_FASYNC, 0), // Unsupported |
72 | | (P9_DIRECT, libc::O_DIRECT), |
73 | | (P9_LARGEFILE, libc::O_LARGEFILE), |
74 | | (P9_DIRECTORY, libc::O_DIRECTORY), |
75 | | (P9_NOFOLLOW, libc::O_NOFOLLOW), |
76 | | (P9_NOATIME, libc::O_NOATIME), |
77 | | (P9_SYNC, libc::O_SYNC), |
78 | | ]; |
79 | | |
80 | | // 9P Qid types. Taken from "include/net/9p/9p.h" in the linux tree. |
81 | | const P9_QTDIR: u8 = 0x80; |
82 | | const _P9_QTAPPEND: u8 = 0x40; |
83 | | const _P9_QTEXCL: u8 = 0x20; |
84 | | const _P9_QTMOUNT: u8 = 0x10; |
85 | | const _P9_QTAUTH: u8 = 0x08; |
86 | | const _P9_QTTMP: u8 = 0x04; |
87 | | const P9_QTSYMLINK: u8 = 0x02; |
88 | | const _P9_QTLINK: u8 = 0x01; |
89 | | const P9_QTFILE: u8 = 0x00; |
90 | | |
91 | | // Bitmask values for the getattr request. |
92 | | const _P9_GETATTR_MODE: u64 = 0x00000001; |
93 | | const _P9_GETATTR_NLINK: u64 = 0x00000002; |
94 | | const _P9_GETATTR_UID: u64 = 0x00000004; |
95 | | const _P9_GETATTR_GID: u64 = 0x00000008; |
96 | | const _P9_GETATTR_RDEV: u64 = 0x00000010; |
97 | | const _P9_GETATTR_ATIME: u64 = 0x00000020; |
98 | | const _P9_GETATTR_MTIME: u64 = 0x00000040; |
99 | | const _P9_GETATTR_CTIME: u64 = 0x00000080; |
100 | | const _P9_GETATTR_INO: u64 = 0x00000100; |
101 | | const _P9_GETATTR_SIZE: u64 = 0x00000200; |
102 | | const _P9_GETATTR_BLOCKS: u64 = 0x00000400; |
103 | | |
104 | | const _P9_GETATTR_BTIME: u64 = 0x00000800; |
105 | | const _P9_GETATTR_GEN: u64 = 0x00001000; |
106 | | const _P9_GETATTR_DATA_VERSION: u64 = 0x00002000; |
107 | | |
108 | | const P9_GETATTR_BASIC: u64 = 0x000007ff; /* Mask for fields up to BLOCKS */ |
109 | | const _P9_GETATTR_ALL: u64 = 0x00003fff; /* Mask for All fields above */ |
110 | | |
111 | | // Bitmask values for the setattr request. |
112 | | const P9_SETATTR_MODE: u32 = 0x00000001; |
113 | | const P9_SETATTR_UID: u32 = 0x00000002; |
114 | | const P9_SETATTR_GID: u32 = 0x00000004; |
115 | | const P9_SETATTR_SIZE: u32 = 0x00000008; |
116 | | const P9_SETATTR_ATIME: u32 = 0x00000010; |
117 | | const P9_SETATTR_MTIME: u32 = 0x00000020; |
118 | | const P9_SETATTR_CTIME: u32 = 0x00000040; |
119 | | const P9_SETATTR_ATIME_SET: u32 = 0x00000080; |
120 | | const P9_SETATTR_MTIME_SET: u32 = 0x00000100; |
121 | | |
122 | | // 9p lock constants. Taken from "include/net/9p/9p.h" in the linux kernel. |
123 | | const _P9_LOCK_TYPE_RDLCK: u8 = 0; |
124 | | const _P9_LOCK_TYPE_WRLCK: u8 = 1; |
125 | | const P9_LOCK_TYPE_UNLCK: u8 = 2; |
126 | | const _P9_LOCK_FLAGS_BLOCK: u8 = 1; |
127 | | const _P9_LOCK_FLAGS_RECLAIM: u8 = 2; |
128 | | const P9_LOCK_SUCCESS: u8 = 0; |
129 | | const _P9_LOCK_BLOCKED: u8 = 1; |
130 | | const _P9_LOCK_ERROR: u8 = 2; |
131 | | const _P9_LOCK_GRACE: u8 = 3; |
132 | | |
133 | | // Minimum and maximum message size that we'll expect from the client. |
134 | | const MIN_MESSAGE_SIZE: u32 = 256; |
135 | | const MAX_MESSAGE_SIZE: u32 = 64 * 1024 + 24; // 64 KiB of payload plus some extra for the header |
136 | | |
137 | | #[derive(PartialEq, Eq)] |
138 | | enum FileType { |
139 | | Regular, |
140 | | Directory, |
141 | | Other, |
142 | | } |
143 | | |
144 | | impl From<libc::mode_t> for FileType { |
145 | 0 | fn from(mode: libc::mode_t) -> Self { |
146 | 0 | match mode & libc::S_IFMT { |
147 | 0 | libc::S_IFREG => FileType::Regular, |
148 | 0 | libc::S_IFDIR => FileType::Directory, |
149 | 0 | _ => FileType::Other, |
150 | | } |
151 | 0 | } |
152 | | } |
153 | | |
154 | | // Represents state that the server is holding on behalf of a client. Fids are somewhat like file |
155 | | // descriptors but are not restricted to open files and directories. Fids are identified by a unique |
156 | | // 32-bit number chosen by the client. Most messages sent by clients include a fid on which to |
157 | | // operate. The fid in a Tattach message represents the root of the file system tree that the client |
158 | | // is allowed to access. A client can create more fids by walking the directory tree from that fid. |
159 | | struct Fid { |
160 | | path: File, |
161 | | file: Option<File>, |
162 | | filetype: FileType, |
163 | | } |
164 | | |
165 | | impl From<libc::stat64> for Qid { |
166 | 0 | fn from(st: libc::stat64) -> Qid { |
167 | 0 | let ty = match st.st_mode & libc::S_IFMT { |
168 | 0 | libc::S_IFDIR => P9_QTDIR, |
169 | 0 | libc::S_IFREG => P9_QTFILE, |
170 | 0 | libc::S_IFLNK => P9_QTSYMLINK, |
171 | 0 | _ => 0, |
172 | | }; |
173 | | |
174 | 0 | Qid { |
175 | 0 | ty, |
176 | 0 | // TODO: deal with the 2038 problem before 2038 |
177 | 0 | version: st.st_mtime as u32, |
178 | 0 | path: st.st_ino, |
179 | 0 | } |
180 | 0 | } |
181 | | } |
182 | | |
183 | 0 | fn statat(d: &File, name: &CStr, flags: libc::c_int) -> io::Result<libc::stat64> { |
184 | 0 | let mut st = MaybeUninit::<libc::stat64>::zeroed(); |
185 | 0 |
|
186 | 0 | // Safe because the kernel will only write data in `st` and we check the return |
187 | 0 | // value. |
188 | 0 | let res = unsafe { |
189 | 0 | libc::fstatat64( |
190 | 0 | d.as_raw_fd(), |
191 | 0 | name.as_ptr(), |
192 | 0 | st.as_mut_ptr(), |
193 | 0 | flags | libc::AT_SYMLINK_NOFOLLOW, |
194 | 0 | ) |
195 | 0 | }; |
196 | 0 | if res >= 0 { |
197 | | // Safe because the kernel guarantees that the struct is now fully initialized. |
198 | 0 | Ok(unsafe { st.assume_init() }) |
199 | | } else { |
200 | 0 | Err(io::Error::last_os_error()) |
201 | | } |
202 | 0 | } |
203 | | |
204 | 0 | fn stat(f: &File) -> io::Result<libc::stat64> { |
205 | 0 | // Safe because this is a constant value and a valid C string. |
206 | 0 | let pathname = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") }; |
207 | 0 |
|
208 | 0 | statat(f, pathname, libc::AT_EMPTY_PATH) |
209 | 0 | } |
210 | | |
211 | 0 | fn string_to_cstring(s: String) -> io::Result<CString> { |
212 | 0 | CString::new(s).map_err(|_| io::Error::from_raw_os_error(libc::EINVAL)) |
213 | 0 | } |
214 | | |
215 | 0 | fn error_to_rmessage(err: io::Error) -> Rmessage { |
216 | 0 | let errno = if let Some(errno) = err.raw_os_error() { |
217 | 0 | errno |
218 | | } else { |
219 | | // Make a best-effort guess based on the kind. |
220 | 0 | match err.kind() { |
221 | 0 | io::ErrorKind::NotFound => libc::ENOENT, |
222 | 0 | io::ErrorKind::PermissionDenied => libc::EPERM, |
223 | 0 | io::ErrorKind::ConnectionRefused => libc::ECONNREFUSED, |
224 | 0 | io::ErrorKind::ConnectionReset => libc::ECONNRESET, |
225 | 0 | io::ErrorKind::ConnectionAborted => libc::ECONNABORTED, |
226 | 0 | io::ErrorKind::NotConnected => libc::ENOTCONN, |
227 | 0 | io::ErrorKind::AddrInUse => libc::EADDRINUSE, |
228 | 0 | io::ErrorKind::AddrNotAvailable => libc::EADDRNOTAVAIL, |
229 | 0 | io::ErrorKind::BrokenPipe => libc::EPIPE, |
230 | 0 | io::ErrorKind::AlreadyExists => libc::EEXIST, |
231 | 0 | io::ErrorKind::WouldBlock => libc::EWOULDBLOCK, |
232 | 0 | io::ErrorKind::InvalidInput => libc::EINVAL, |
233 | 0 | io::ErrorKind::InvalidData => libc::EINVAL, |
234 | 0 | io::ErrorKind::TimedOut => libc::ETIMEDOUT, |
235 | 0 | io::ErrorKind::WriteZero => libc::EIO, |
236 | 0 | io::ErrorKind::Interrupted => libc::EINTR, |
237 | 0 | io::ErrorKind::Other => libc::EIO, |
238 | 0 | io::ErrorKind::UnexpectedEof => libc::EIO, |
239 | 0 | _ => libc::EIO, |
240 | | } |
241 | | }; |
242 | | |
243 | 0 | Rmessage::Lerror(Rlerror { |
244 | 0 | ecode: errno as u32, |
245 | 0 | }) |
246 | 0 | } |
247 | | |
248 | | // Sigh.. Cow requires the underlying type to implement Clone. |
249 | | enum MaybeOwned<'b, T> { |
250 | | Borrowed(&'b T), |
251 | | Owned(T), |
252 | | } |
253 | | |
254 | | impl<'a, T> Deref for MaybeOwned<'a, T> { |
255 | | type Target = T; |
256 | 0 | fn deref(&self) -> &Self::Target { |
257 | | use MaybeOwned::*; |
258 | 0 | match *self { |
259 | 0 | Borrowed(borrowed) => borrowed, |
260 | 0 | Owned(ref owned) => owned, |
261 | | } |
262 | 0 | } |
263 | | } |
264 | | |
265 | | impl<'a, T> AsRef<T> for MaybeOwned<'a, T> { |
266 | 0 | fn as_ref(&self) -> &T { |
267 | | use MaybeOwned::*; |
268 | 0 | match self { |
269 | 0 | Borrowed(borrowed) => borrowed, |
270 | 0 | Owned(ref owned) => owned, |
271 | | } |
272 | 0 | } |
273 | | } |
274 | | |
275 | 0 | fn ebadf() -> io::Error { |
276 | 0 | io::Error::from_raw_os_error(libc::EBADF) |
277 | 0 | } |
278 | | |
279 | | pub type ServerIdMap<T> = BTreeMap<T, T>; |
280 | | pub type ServerUidMap = ServerIdMap<libc::uid_t>; |
281 | | pub type ServerGidMap = ServerIdMap<libc::gid_t>; |
282 | | |
283 | 0 | fn map_id_from_host<T: Clone + Ord>(map: &ServerIdMap<T>, id: T) -> T { |
284 | 0 | map.get(&id).map_or(id.clone(), |v| v.clone()) |
285 | 0 | } |
286 | | |
287 | | // Performs an ascii case insensitive lookup and returns an O_PATH fd for the entry, if found. |
288 | 0 | fn ascii_casefold_lookup(proc: &File, parent: &File, name: &[u8]) -> io::Result<File> { |
289 | 0 | let mut dir = open_fid(proc, parent, P9_DIRECTORY)?; |
290 | 0 | let mut dirents = read_dir(&mut dir, 0)?; |
291 | | |
292 | 0 | while let Some(entry) = dirents.next().transpose()? { |
293 | 0 | if name.eq_ignore_ascii_case(entry.name.as_bytes()) { |
294 | 0 | return lookup(parent, entry.name.as_c_str()); |
295 | 0 | } |
296 | | } |
297 | | |
298 | 0 | Err(io::Error::from_raw_os_error(libc::ENOENT)) |
299 | 0 | } |
300 | | |
301 | 0 | fn lookup(parent: &File, name: &CStr) -> io::Result<File> { |
302 | | // Safe because this doesn't modify any memory and we check the return value. |
303 | 0 | let fd = syscall!(unsafe { |
304 | 0 | libc::openat64( |
305 | 0 | parent.as_raw_fd(), |
306 | 0 | name.as_ptr(), |
307 | 0 | libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC, |
308 | 0 | ) |
309 | 0 | })?; |
310 | | |
311 | | // Safe because we just opened this fd. |
312 | 0 | Ok(unsafe { File::from_raw_fd(fd) }) |
313 | 0 | } |
314 | | |
315 | 0 | fn do_walk( |
316 | 0 | proc: &File, |
317 | 0 | wnames: Vec<P9String>, |
318 | 0 | start: &File, |
319 | 0 | ascii_casefold: bool, |
320 | 0 | mds: &mut Vec<libc::stat64>, |
321 | 0 | ) -> io::Result<File> { |
322 | 0 | let mut current = MaybeOwned::Borrowed(start); |
323 | | |
324 | 0 | for wname in wnames { |
325 | 0 | current = MaybeOwned::Owned(lookup(current.as_ref(), wname.as_c_str()).or_else(|e| { |
326 | 0 | if ascii_casefold { |
327 | 0 | if let Some(libc::ENOENT) = e.raw_os_error() { |
328 | 0 | return ascii_casefold_lookup(proc, current.as_ref(), wname.as_bytes()); |
329 | 0 | } |
330 | 0 | } |
331 | | |
332 | 0 | Err(e) |
333 | 0 | })?); |
334 | 0 | mds.push(stat(¤t)?); |
335 | | } |
336 | | |
337 | 0 | match current { |
338 | 0 | MaybeOwned::Owned(owned) => Ok(owned), |
339 | 0 | MaybeOwned::Borrowed(borrowed) => borrowed.try_clone(), |
340 | | } |
341 | 0 | } |
342 | | |
343 | 0 | fn open_fid(proc: &File, path: &File, p9_flags: u32) -> io::Result<File> { |
344 | 0 | let pathname = string_to_cstring(format!("self/fd/{}", path.as_raw_fd()))?; |
345 | | |
346 | | // We always open files with O_CLOEXEC. |
347 | 0 | let mut flags: i32 = libc::O_CLOEXEC; |
348 | 0 | for &(p9f, of) in &MAPPED_FLAGS { |
349 | 0 | if (p9_flags & p9f) != 0 { |
350 | 0 | flags |= of; |
351 | 0 | } |
352 | | } |
353 | | |
354 | 0 | if p9_flags & P9_NOACCESS == P9_RDONLY { |
355 | 0 | flags |= libc::O_RDONLY; |
356 | 0 | } |
357 | | |
358 | | // Safe because this doesn't modify any memory and we check the return value. We need to |
359 | | // clear the O_NOFOLLOW flag because we want to follow the proc symlink. |
360 | 0 | let fd = syscall!(unsafe { |
361 | 0 | libc::openat64( |
362 | 0 | proc.as_raw_fd(), |
363 | 0 | pathname.as_ptr(), |
364 | 0 | flags & !libc::O_NOFOLLOW, |
365 | 0 | ) |
366 | 0 | })?; |
367 | | |
368 | | // Safe because we just opened this fd and we know it is valid. |
369 | 0 | Ok(unsafe { File::from_raw_fd(fd) }) |
370 | 0 | } |
371 | | |
372 | 0 | #[derive(Clone, Serialize, Deserialize)] Unexecuted instantiation: <<p9::server::Config as serde::de::Deserialize>::deserialize::__FieldVisitor as serde::de::Visitor>::expecting Unexecuted instantiation: <<p9::server::Config as serde::de::Deserialize>::deserialize::__Visitor as serde::de::Visitor>::expecting Unexecuted instantiation: <<p9::server::Config as serde::de::Deserialize>::deserialize::__FieldVisitor as serde::de::Visitor>::visit_u64::<_> Unexecuted instantiation: <<p9::server::Config as serde::de::Deserialize>::deserialize::__FieldVisitor as serde::de::Visitor>::visit_str::<_> Unexecuted instantiation: <<p9::server::Config as serde::de::Deserialize>::deserialize::__FieldVisitor as serde::de::Visitor>::visit_bytes::<_> Unexecuted instantiation: <<p9::server::Config as serde::de::Deserialize>::deserialize::__Field as serde::de::Deserialize>::deserialize::<_> Unexecuted instantiation: <<p9::server::Config as serde::de::Deserialize>::deserialize::__Visitor as serde::de::Visitor>::visit_seq::<_> Unexecuted instantiation: <<p9::server::Config as serde::de::Deserialize>::deserialize::__Visitor as serde::de::Visitor>::visit_map::<_> |
373 | | pub struct Config { |
374 | | pub root: Box<Path>, |
375 | | pub msize: u32, |
376 | | |
377 | | pub uid_map: ServerUidMap, |
378 | | pub gid_map: ServerGidMap, |
379 | | |
380 | | pub ascii_casefold: bool, |
381 | | } |
382 | | |
383 | | impl FromStr for Config { |
384 | | type Err = &'static str; |
385 | | |
386 | 0 | fn from_str(params: &str) -> Result<Self, Self::Err> { |
387 | 0 | let mut cfg = Self::default(); |
388 | 0 | if params.is_empty() { |
389 | 0 | return Ok(cfg); |
390 | 0 | } |
391 | 0 | for opt in params.split(':') { |
392 | 0 | let mut o = opt.splitn(2, '='); |
393 | 0 | let kind = o.next().ok_or("`cfg` options mut not be empty")?; |
394 | 0 | let value = o |
395 | 0 | .next() |
396 | 0 | .ok_or("`cfg` options must be of the form `kind=value`")?; |
397 | 0 | match kind { |
398 | 0 | "ascii_casefold" => { |
399 | 0 | let ascii_casefold = value |
400 | 0 | .parse() |
401 | 0 | .map_err(|_| "`ascii_casefold` must be a boolean")?; |
402 | 0 | cfg.ascii_casefold = ascii_casefold; |
403 | | } |
404 | 0 | _ => return Err("unrecognized option for p9 config"), |
405 | | } |
406 | | } |
407 | 0 | Ok(cfg) |
408 | 0 | } |
409 | | } |
410 | | |
411 | | impl Default for Config { |
412 | 0 | fn default() -> Config { |
413 | 0 | Config { |
414 | 0 | root: Path::new("/").into(), |
415 | 0 | msize: MAX_MESSAGE_SIZE, |
416 | 0 | uid_map: Default::default(), |
417 | 0 | gid_map: Default::default(), |
418 | 0 | ascii_casefold: false, |
419 | 0 | } |
420 | 0 | } |
421 | | } |
422 | | pub struct Server { |
423 | | fids: BTreeMap<u32, Fid>, |
424 | | proc: File, |
425 | | cfg: Config, |
426 | | } |
427 | | |
428 | | impl Server { |
429 | 0 | pub fn new<P: Into<Box<Path>>>( |
430 | 0 | root: P, |
431 | 0 | uid_map: ServerUidMap, |
432 | 0 | gid_map: ServerGidMap, |
433 | 0 | ) -> io::Result<Server> { |
434 | 0 | Server::with_config(Config { |
435 | 0 | root: root.into(), |
436 | 0 | msize: MAX_MESSAGE_SIZE, |
437 | 0 | uid_map, |
438 | 0 | gid_map, |
439 | 0 | ascii_casefold: false, |
440 | 0 | }) |
441 | 0 | } |
442 | | |
443 | 0 | pub fn with_config(cfg: Config) -> io::Result<Server> { |
444 | 0 | // Safe because this is a valid c-string. |
445 | 0 | let proc_cstr = unsafe { CStr::from_bytes_with_nul_unchecked(b"/proc\0") }; |
446 | | |
447 | | // Safe because this doesn't modify any memory and we check the return value. |
448 | 0 | let fd = syscall!(unsafe { |
449 | 0 | libc::openat64( |
450 | 0 | libc::AT_FDCWD, |
451 | 0 | proc_cstr.as_ptr(), |
452 | 0 | libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC, |
453 | 0 | ) |
454 | 0 | })?; |
455 | | |
456 | | // Safe because we just opened this fd and we know it is valid. |
457 | 0 | let proc = unsafe { File::from_raw_fd(fd) }; |
458 | 0 | Ok(Server { |
459 | 0 | fids: BTreeMap::new(), |
460 | 0 | proc, |
461 | 0 | cfg, |
462 | 0 | }) |
463 | 0 | } |
464 | | |
465 | 0 | pub fn keep_fds(&self) -> Vec<RawFd> { |
466 | 0 | vec![self.proc.as_raw_fd()] |
467 | 0 | } |
468 | | |
469 | 0 | pub fn handle_message<R: Read, W: Write>( |
470 | 0 | &mut self, |
471 | 0 | reader: &mut R, |
472 | 0 | writer: &mut W, |
473 | 0 | ) -> io::Result<()> { |
474 | 0 | let Tframe { tag, msg } = WireFormat::decode(&mut reader.take(self.cfg.msize as u64))?; |
475 | | |
476 | 0 | let rmsg = match msg { |
477 | 0 | Ok(Tmessage::Version(ref version)) => self.version(version).map(Rmessage::Version), |
478 | 0 | Ok(Tmessage::Flush(ref flush)) => self.flush(flush).and(Ok(Rmessage::Flush)), |
479 | 0 | Ok(Tmessage::Walk(walk)) => self.walk(walk).map(Rmessage::Walk), |
480 | 0 | Ok(Tmessage::Read(ref read)) => self.read(read).map(Rmessage::Read), |
481 | 0 | Ok(Tmessage::Write(ref write)) => self.write(write).map(Rmessage::Write), |
482 | 0 | Ok(Tmessage::Clunk(ref clunk)) => self.clunk(clunk).and(Ok(Rmessage::Clunk)), |
483 | 0 | Ok(Tmessage::Remove(ref remove)) => self.remove(remove).and(Ok(Rmessage::Remove)), |
484 | 0 | Ok(Tmessage::Attach(ref attach)) => self.attach(attach).map(Rmessage::Attach), |
485 | 0 | Ok(Tmessage::Auth(ref auth)) => self.auth(auth).map(Rmessage::Auth), |
486 | 0 | Ok(Tmessage::Statfs(ref statfs)) => self.statfs(statfs).map(Rmessage::Statfs), |
487 | 0 | Ok(Tmessage::Lopen(ref lopen)) => self.lopen(lopen).map(Rmessage::Lopen), |
488 | 0 | Ok(Tmessage::Lcreate(lcreate)) => self.lcreate(lcreate).map(Rmessage::Lcreate), |
489 | 0 | Ok(Tmessage::Symlink(ref symlink)) => self.symlink(symlink).map(Rmessage::Symlink), |
490 | 0 | Ok(Tmessage::Mknod(ref mknod)) => self.mknod(mknod).map(Rmessage::Mknod), |
491 | 0 | Ok(Tmessage::Rename(ref rename)) => self.rename(rename).and(Ok(Rmessage::Rename)), |
492 | 0 | Ok(Tmessage::Readlink(ref readlink)) => self.readlink(readlink).map(Rmessage::Readlink), |
493 | 0 | Ok(Tmessage::GetAttr(ref get_attr)) => self.get_attr(get_attr).map(Rmessage::GetAttr), |
494 | 0 | Ok(Tmessage::SetAttr(ref set_attr)) => { |
495 | 0 | self.set_attr(set_attr).and(Ok(Rmessage::SetAttr)) |
496 | | } |
497 | 0 | Ok(Tmessage::XattrWalk(ref xattr_walk)) => { |
498 | 0 | self.xattr_walk(xattr_walk).map(Rmessage::XattrWalk) |
499 | | } |
500 | 0 | Ok(Tmessage::XattrCreate(ref xattr_create)) => self |
501 | 0 | .xattr_create(xattr_create) |
502 | 0 | .and(Ok(Rmessage::XattrCreate)), |
503 | 0 | Ok(Tmessage::Readdir(ref readdir)) => self.readdir(readdir).map(Rmessage::Readdir), |
504 | 0 | Ok(Tmessage::Fsync(ref fsync)) => self.fsync(fsync).and(Ok(Rmessage::Fsync)), |
505 | 0 | Ok(Tmessage::Lock(ref lock)) => self.lock(lock).map(Rmessage::Lock), |
506 | 0 | Ok(Tmessage::GetLock(ref get_lock)) => self.get_lock(get_lock).map(Rmessage::GetLock), |
507 | 0 | Ok(Tmessage::Link(link)) => self.link(link).and(Ok(Rmessage::Link)), |
508 | 0 | Ok(Tmessage::Mkdir(mkdir)) => self.mkdir(mkdir).map(Rmessage::Mkdir), |
509 | 0 | Ok(Tmessage::RenameAt(rename_at)) => { |
510 | 0 | self.rename_at(rename_at).and(Ok(Rmessage::RenameAt)) |
511 | | } |
512 | 0 | Ok(Tmessage::UnlinkAt(unlink_at)) => { |
513 | 0 | self.unlink_at(unlink_at).and(Ok(Rmessage::UnlinkAt)) |
514 | | } |
515 | 0 | Err(e) => { |
516 | 0 | // The header was successfully decoded, but the body failed to decode - send an |
517 | 0 | // error response for this tag. |
518 | 0 | let error = format!("Tframe message decode failed: {}", e); |
519 | 0 | Err(io::Error::new(io::ErrorKind::InvalidData, error)) |
520 | | } |
521 | | }; |
522 | | |
523 | | // Errors while handling requests are never fatal. |
524 | 0 | let response = Rframe { |
525 | 0 | tag, |
526 | 0 | msg: rmsg.unwrap_or_else(error_to_rmessage), |
527 | 0 | }; |
528 | 0 |
|
529 | 0 | response.encode(writer)?; |
530 | 0 | writer.flush() |
531 | 0 | } Unexecuted instantiation: <p9::server::Server>::handle_message::<devices::virtio::descriptor_utils::Reader, devices::virtio::descriptor_utils::Writer> Unexecuted instantiation: <p9::server::Server>::handle_message::<_, _> |
532 | | |
533 | 0 | fn auth(&mut self, _auth: &Tauth) -> io::Result<Rauth> { |
534 | 0 | // Returning an error for the auth message means that the server does not require |
535 | 0 | // authentication. |
536 | 0 | Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) |
537 | 0 | } |
538 | | |
539 | 0 | fn attach(&mut self, attach: &Tattach) -> io::Result<Rattach> { |
540 | 0 | // TODO: Check attach parameters |
541 | 0 | match self.fids.entry(attach.fid) { |
542 | 0 | btree_map::Entry::Vacant(entry) => { |
543 | 0 | let root = CString::new(self.cfg.root.as_os_str().as_bytes()) |
544 | 0 | .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; |
545 | | |
546 | | // Safe because this doesn't modify any memory and we check the return value. |
547 | 0 | let fd = syscall!(unsafe { |
548 | 0 | libc::openat64( |
549 | 0 | libc::AT_FDCWD, |
550 | 0 | root.as_ptr(), |
551 | 0 | libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC, |
552 | 0 | ) |
553 | 0 | })?; |
554 | | |
555 | 0 | let root_path = unsafe { File::from_raw_fd(fd) }; |
556 | 0 | let st = stat(&root_path)?; |
557 | | |
558 | 0 | let fid = Fid { |
559 | 0 | // Safe because we just opened this fd. |
560 | 0 | path: root_path, |
561 | 0 | file: None, |
562 | 0 | filetype: st.st_mode.into(), |
563 | 0 | }; |
564 | 0 | let response = Rattach { qid: st.into() }; |
565 | 0 | entry.insert(fid); |
566 | 0 | Ok(response) |
567 | | } |
568 | 0 | btree_map::Entry::Occupied(_) => Err(io::Error::from_raw_os_error(libc::EBADF)), |
569 | | } |
570 | 0 | } |
571 | | |
572 | 0 | fn version(&mut self, version: &Tversion) -> io::Result<Rversion> { |
573 | 0 | if version.msize < MIN_MESSAGE_SIZE { |
574 | 0 | return Err(io::Error::from_raw_os_error(libc::EINVAL)); |
575 | 0 | } |
576 | 0 |
|
577 | 0 | // A Tversion request clunks all open fids and terminates any pending I/O. |
578 | 0 | self.fids.clear(); |
579 | 0 | self.cfg.msize = min(self.cfg.msize, version.msize); |
580 | 0 |
|
581 | 0 | Ok(Rversion { |
582 | 0 | msize: self.cfg.msize, |
583 | 0 | version: if version.version.as_bytes() == b"9P2000.L" { |
584 | 0 | P9String::new(b"9P2000.L")? |
585 | | } else { |
586 | 0 | P9String::new(b"unknown")? |
587 | | }, |
588 | | }) |
589 | 0 | } |
590 | | |
591 | | #[allow(clippy::unnecessary_wraps)] |
592 | 0 | fn flush(&mut self, _flush: &Tflush) -> io::Result<()> { |
593 | 0 | // TODO: Since everything is synchronous we can't actually flush requests. |
594 | 0 | Ok(()) |
595 | 0 | } |
596 | | |
597 | 0 | fn walk(&mut self, walk: Twalk) -> io::Result<Rwalk> { |
598 | 0 | // `newfid` must not currently be in use unless it is the same as `fid`. |
599 | 0 | if walk.fid != walk.newfid && self.fids.contains_key(&walk.newfid) { |
600 | 0 | return Err(io::Error::from_raw_os_error(libc::EBADF)); |
601 | 0 | } |
602 | | |
603 | | // We need to walk the tree. First get the starting path. |
604 | 0 | let start = &self.fids.get(&walk.fid).ok_or_else(ebadf)?.path; |
605 | | |
606 | | // Now walk the tree and break on the first error, if any. |
607 | 0 | let expected_len = walk.wnames.len(); |
608 | 0 | let mut mds = Vec::with_capacity(expected_len); |
609 | 0 | match do_walk( |
610 | 0 | &self.proc, |
611 | 0 | walk.wnames, |
612 | 0 | start, |
613 | 0 | self.cfg.ascii_casefold, |
614 | 0 | &mut mds, |
615 | 0 | ) { |
616 | 0 | Ok(end) => { |
617 | 0 | // Store the new fid if the full walk succeeded. |
618 | 0 | if mds.len() == expected_len { |
619 | 0 | let st = mds.last().copied().map(Ok).unwrap_or_else(|| stat(&end))?; |
620 | 0 | self.fids.insert( |
621 | 0 | walk.newfid, |
622 | 0 | Fid { |
623 | 0 | path: end, |
624 | 0 | file: None, |
625 | 0 | filetype: st.st_mode.into(), |
626 | 0 | }, |
627 | 0 | ); |
628 | 0 | } |
629 | | } |
630 | 0 | Err(e) => { |
631 | 0 | // Only return an error if it occurred on the first component. |
632 | 0 | if mds.is_empty() { |
633 | 0 | return Err(e); |
634 | 0 | } |
635 | | } |
636 | | } |
637 | | |
638 | 0 | Ok(Rwalk { |
639 | 0 | wqids: mds.into_iter().map(Qid::from).collect(), |
640 | 0 | }) |
641 | 0 | } |
642 | | |
643 | 0 | fn read(&mut self, read: &Tread) -> io::Result<Rread> { |
644 | | // Thankfully, `read` cannot be used to read directories in 9P2000.L. |
645 | 0 | let file = self |
646 | 0 | .fids |
647 | 0 | .get_mut(&read.fid) |
648 | 0 | .and_then(|fid| fid.file.as_mut()) |
649 | 0 | .ok_or_else(ebadf)?; |
650 | | |
651 | | // Use an empty Rread struct to figure out the overhead of the header. |
652 | 0 | let header_size = Rframe { |
653 | 0 | tag: 0, |
654 | 0 | msg: Rmessage::Read(Rread { |
655 | 0 | data: Data(Vec::new()), |
656 | 0 | }), |
657 | 0 | } |
658 | 0 | .byte_size(); |
659 | 0 |
|
660 | 0 | let capacity = min(self.cfg.msize - header_size, read.count); |
661 | 0 | let mut buf = Data(vec![0u8; capacity as usize]); |
662 | | |
663 | 0 | let count = file.read_at(&mut buf, read.offset)?; |
664 | 0 | buf.truncate(count); |
665 | 0 |
|
666 | 0 | Ok(Rread { data: buf }) |
667 | 0 | } |
668 | | |
669 | 0 | fn write(&mut self, write: &Twrite) -> io::Result<Rwrite> { |
670 | 0 | let file = self |
671 | 0 | .fids |
672 | 0 | .get_mut(&write.fid) |
673 | 0 | .and_then(|fid| fid.file.as_mut()) |
674 | 0 | .ok_or_else(ebadf)?; |
675 | | |
676 | 0 | let count = file.write_at(&write.data, write.offset)?; |
677 | 0 | Ok(Rwrite { |
678 | 0 | count: count as u32, |
679 | 0 | }) |
680 | 0 | } |
681 | | |
682 | 0 | fn clunk(&mut self, clunk: &Tclunk) -> io::Result<()> { |
683 | 0 | match self.fids.entry(clunk.fid) { |
684 | 0 | btree_map::Entry::Vacant(_) => Err(io::Error::from_raw_os_error(libc::EBADF)), |
685 | 0 | btree_map::Entry::Occupied(entry) => { |
686 | 0 | entry.remove(); |
687 | 0 | Ok(()) |
688 | | } |
689 | | } |
690 | 0 | } |
691 | | |
692 | 0 | fn remove(&mut self, _remove: &Tremove) -> io::Result<()> { |
693 | 0 | // Since a file could be linked into multiple locations, there is no way to know exactly |
694 | 0 | // which path we are supposed to unlink. Linux uses unlink_at anyway, so we can just return |
695 | 0 | // an error here. |
696 | 0 | Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) |
697 | 0 | } |
698 | | |
699 | 0 | fn statfs(&mut self, statfs: &Tstatfs) -> io::Result<Rstatfs> { |
700 | 0 | let fid = self.fids.get(&statfs.fid).ok_or_else(ebadf)?; |
701 | 0 | let mut buf = MaybeUninit::zeroed(); |
702 | 0 |
|
703 | 0 | // Safe because this will only modify `out` and we check the return value. |
704 | 0 | syscall!(unsafe { libc::fstatfs64(fid.path.as_raw_fd(), buf.as_mut_ptr()) })?; |
705 | | |
706 | | // Safe because this only has integer types and any value is valid. |
707 | 0 | let out = unsafe { buf.assume_init() }; |
708 | 0 | Ok(Rstatfs { |
709 | 0 | ty: out.f_type as u32, |
710 | 0 | bsize: out.f_bsize as u32, |
711 | 0 | blocks: out.f_blocks, |
712 | 0 | bfree: out.f_bfree, |
713 | 0 | bavail: out.f_bavail, |
714 | 0 | files: out.f_files, |
715 | 0 | ffree: out.f_ffree, |
716 | 0 | // Safe because the fsid has only integer fields and the compiler will verify that is |
717 | 0 | // the same width as the `fsid` field in Rstatfs. |
718 | 0 | fsid: unsafe { mem::transmute::<fsid_t, u64>(out.f_fsid) }, |
719 | 0 | namelen: out.f_namelen as u32, |
720 | 0 | }) |
721 | 0 | } |
722 | | |
723 | 0 | fn lopen(&mut self, lopen: &Tlopen) -> io::Result<Rlopen> { |
724 | 0 | let fid = self.fids.get_mut(&lopen.fid).ok_or_else(ebadf)?; |
725 | | |
726 | 0 | let file = open_fid(&self.proc, &fid.path, lopen.flags)?; |
727 | 0 | let st = stat(&file)?; |
728 | | |
729 | 0 | fid.file = Some(file); |
730 | 0 | Ok(Rlopen { |
731 | 0 | qid: st.into(), |
732 | 0 | iounit: 0, // Allow the client to send requests up to the negotiated max message size. |
733 | 0 | }) |
734 | 0 | } |
735 | | |
736 | 0 | fn lcreate(&mut self, lcreate: Tlcreate) -> io::Result<Rlcreate> { |
737 | 0 | let fid = self.fids.get_mut(&lcreate.fid).ok_or_else(ebadf)?; |
738 | | |
739 | 0 | if fid.filetype != FileType::Directory { |
740 | 0 | return Err(io::Error::from_raw_os_error(libc::ENOTDIR)); |
741 | 0 | } |
742 | 0 |
|
743 | 0 | let mut flags: i32 = libc::O_CLOEXEC | libc::O_CREAT | libc::O_EXCL; |
744 | 0 | for &(p9f, of) in &MAPPED_FLAGS { |
745 | 0 | if (lcreate.flags & p9f) != 0 { |
746 | 0 | flags |= of; |
747 | 0 | } |
748 | | } |
749 | 0 | if lcreate.flags & P9_NOACCESS == P9_RDONLY { |
750 | 0 | flags |= libc::O_RDONLY; |
751 | 0 | } |
752 | | |
753 | | // Safe because this doesn't modify any memory and we check the return value. |
754 | 0 | let fd = syscall!(unsafe { |
755 | 0 | libc::openat64( |
756 | 0 | fid.path.as_raw_fd(), |
757 | 0 | lcreate.name.as_ptr(), |
758 | 0 | flags, |
759 | 0 | lcreate.mode, |
760 | 0 | ) |
761 | 0 | })?; |
762 | | |
763 | | // Safe because we just opened this fd and we know it is valid. |
764 | 0 | let file = unsafe { File::from_raw_fd(fd) }; |
765 | 0 | let st = stat(&file)?; |
766 | | |
767 | 0 | fid.file = Some(file); |
768 | 0 | fid.filetype = FileType::Regular; |
769 | | |
770 | | // This fid now refers to the newly created file so we need to update the O_PATH fd for it |
771 | | // as well. |
772 | 0 | fid.path = lookup(&fid.path, lcreate.name.as_c_str())?; |
773 | | |
774 | 0 | Ok(Rlcreate { |
775 | 0 | qid: st.into(), |
776 | 0 | iounit: 0, // Allow the client to send requests up to the negotiated max message size. |
777 | 0 | }) |
778 | 0 | } |
779 | | |
780 | 0 | fn symlink(&mut self, _symlink: &Tsymlink) -> io::Result<Rsymlink> { |
781 | 0 | // symlinks are not allowed. |
782 | 0 | Err(io::Error::from_raw_os_error(libc::EACCES)) |
783 | 0 | } |
784 | | |
785 | 0 | fn mknod(&mut self, _mknod: &Tmknod) -> io::Result<Rmknod> { |
786 | 0 | // No nodes either. |
787 | 0 | Err(io::Error::from_raw_os_error(libc::EACCES)) |
788 | 0 | } |
789 | | |
790 | 0 | fn rename(&mut self, _rename: &Trename) -> io::Result<()> { |
791 | 0 | // We cannot support this as an inode may be linked into multiple directories but we don't |
792 | 0 | // know which one the client wants us to rename. Linux uses rename_at anyway, so we don't |
793 | 0 | // need to worry about this. |
794 | 0 | Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) |
795 | 0 | } |
796 | | |
797 | 0 | fn readlink(&mut self, readlink: &Treadlink) -> io::Result<Rreadlink> { |
798 | 0 | let fid = self.fids.get(&readlink.fid).ok_or_else(ebadf)?; |
799 | | |
800 | 0 | let mut link = vec![0; libc::PATH_MAX as usize]; |
801 | | |
802 | | // Safe because this will only modify `link` and we check the return value. |
803 | 0 | let len = syscall!(unsafe { |
804 | 0 | libc::readlinkat( |
805 | 0 | fid.path.as_raw_fd(), |
806 | 0 | [0].as_ptr(), |
807 | 0 | link.as_mut_ptr() as *mut libc::c_char, |
808 | 0 | link.len(), |
809 | 0 | ) |
810 | 0 | })? as usize; |
811 | 0 | link.truncate(len); |
812 | 0 | let target = P9String::new(link)?; |
813 | 0 | Ok(Rreadlink { target }) |
814 | 0 | } |
815 | | |
816 | | #[allow(clippy::unnecessary_cast)] // nlink_t is u32 on 32-bit platforms |
817 | 0 | fn get_attr(&mut self, get_attr: &Tgetattr) -> io::Result<Rgetattr> { |
818 | 0 | let fid = self.fids.get_mut(&get_attr.fid).ok_or_else(ebadf)?; |
819 | | |
820 | 0 | let st = stat(&fid.path)?; |
821 | | |
822 | 0 | Ok(Rgetattr { |
823 | 0 | valid: P9_GETATTR_BASIC, |
824 | 0 | qid: st.into(), |
825 | 0 | mode: st.st_mode, |
826 | 0 | uid: map_id_from_host(&self.cfg.uid_map, st.st_uid), |
827 | 0 | gid: map_id_from_host(&self.cfg.gid_map, st.st_gid), |
828 | 0 | nlink: st.st_nlink as u64, |
829 | 0 | rdev: st.st_rdev, |
830 | 0 | size: st.st_size as u64, |
831 | 0 | blksize: st.st_blksize as u64, |
832 | 0 | blocks: st.st_blocks as u64, |
833 | 0 | atime_sec: st.st_atime as u64, |
834 | 0 | atime_nsec: st.st_atime_nsec as u64, |
835 | 0 | mtime_sec: st.st_mtime as u64, |
836 | 0 | mtime_nsec: st.st_mtime_nsec as u64, |
837 | 0 | ctime_sec: st.st_ctime as u64, |
838 | 0 | ctime_nsec: st.st_ctime_nsec as u64, |
839 | 0 | btime_sec: 0, |
840 | 0 | btime_nsec: 0, |
841 | 0 | gen: 0, |
842 | 0 | data_version: 0, |
843 | 0 | }) |
844 | 0 | } |
845 | | |
846 | 0 | fn set_attr(&mut self, set_attr: &Tsetattr) -> io::Result<()> { |
847 | 0 | let fid = self.fids.get(&set_attr.fid).ok_or_else(ebadf)?; |
848 | 0 | let path = string_to_cstring(format!("self/fd/{}", fid.path.as_raw_fd()))?; |
849 | | |
850 | 0 | if set_attr.valid & P9_SETATTR_MODE != 0 { |
851 | | // Safe because this doesn't modify any memory and we check the return value. |
852 | 0 | syscall!(unsafe { |
853 | 0 | libc::fchmodat(self.proc.as_raw_fd(), path.as_ptr(), set_attr.mode, 0) |
854 | 0 | })?; |
855 | 0 | } |
856 | | |
857 | 0 | if set_attr.valid & (P9_SETATTR_UID | P9_SETATTR_GID) != 0 { |
858 | 0 | let uid = if set_attr.valid & P9_SETATTR_UID != 0 { |
859 | 0 | set_attr.uid |
860 | | } else { |
861 | 0 | -1i32 as u32 |
862 | | }; |
863 | 0 | let gid = if set_attr.valid & P9_SETATTR_GID != 0 { |
864 | 0 | set_attr.gid |
865 | | } else { |
866 | 0 | -1i32 as u32 |
867 | | }; |
868 | | |
869 | | // Safe because this doesn't modify any memory and we check the return value. |
870 | 0 | syscall!(unsafe { libc::fchownat(self.proc.as_raw_fd(), path.as_ptr(), uid, gid, 0) })?; |
871 | 0 | } |
872 | | |
873 | 0 | if set_attr.valid & P9_SETATTR_SIZE != 0 { |
874 | 0 | let file = if fid.filetype == FileType::Directory { |
875 | 0 | return Err(io::Error::from_raw_os_error(libc::EISDIR)); |
876 | 0 | } else if let Some(ref file) = fid.file { |
877 | 0 | MaybeOwned::Borrowed(file) |
878 | | } else { |
879 | 0 | MaybeOwned::Owned(open_fid(&self.proc, &fid.path, P9_NONBLOCK | P9_RDWR)?) |
880 | | }; |
881 | | |
882 | 0 | file.set_len(set_attr.size)?; |
883 | 0 | } |
884 | | |
885 | 0 | if set_attr.valid & (P9_SETATTR_ATIME | P9_SETATTR_MTIME) != 0 { |
886 | 0 | let times = [ |
887 | | libc::timespec { |
888 | 0 | tv_sec: set_attr.atime_sec as _, |
889 | 0 | tv_nsec: if set_attr.valid & P9_SETATTR_ATIME == 0 { |
890 | 0 | libc::UTIME_OMIT |
891 | 0 | } else if set_attr.valid & P9_SETATTR_ATIME_SET == 0 { |
892 | 0 | libc::UTIME_NOW |
893 | | } else { |
894 | 0 | set_attr.atime_nsec as _ |
895 | | }, |
896 | | }, |
897 | | libc::timespec { |
898 | 0 | tv_sec: set_attr.mtime_sec as _, |
899 | 0 | tv_nsec: if set_attr.valid & P9_SETATTR_MTIME == 0 { |
900 | 0 | libc::UTIME_OMIT |
901 | 0 | } else if set_attr.valid & P9_SETATTR_MTIME_SET == 0 { |
902 | 0 | libc::UTIME_NOW |
903 | | } else { |
904 | 0 | set_attr.mtime_nsec as _ |
905 | | }, |
906 | | }, |
907 | | ]; |
908 | | |
909 | | // Safe because file is valid and we have initialized times fully. |
910 | 0 | let ret = unsafe { |
911 | 0 | libc::utimensat( |
912 | 0 | self.proc.as_raw_fd(), |
913 | 0 | path.as_ptr(), |
914 | 0 | × as *const libc::timespec, |
915 | 0 | 0, |
916 | 0 | ) |
917 | 0 | }; |
918 | 0 | if ret < 0 { |
919 | 0 | return Err(io::Error::last_os_error()); |
920 | 0 | } |
921 | 0 | } |
922 | | |
923 | | // The ctime would have been updated by any of the above operations so we only |
924 | | // need to change it if it was the only option given. |
925 | 0 | if set_attr.valid & P9_SETATTR_CTIME != 0 && set_attr.valid & (!P9_SETATTR_CTIME) == 0 { |
926 | | // Setting -1 as the uid and gid will not actually change anything but will |
927 | | // still update the ctime. |
928 | 0 | let ret = unsafe { |
929 | 0 | libc::fchownat( |
930 | 0 | self.proc.as_raw_fd(), |
931 | 0 | path.as_ptr(), |
932 | 0 | libc::uid_t::MAX, |
933 | 0 | libc::gid_t::MAX, |
934 | 0 | 0, |
935 | 0 | ) |
936 | 0 | }; |
937 | 0 | if ret < 0 { |
938 | 0 | return Err(io::Error::last_os_error()); |
939 | 0 | } |
940 | 0 | } |
941 | | |
942 | 0 | Ok(()) |
943 | 0 | } |
944 | | |
945 | 0 | fn xattr_walk(&mut self, _xattr_walk: &Txattrwalk) -> io::Result<Rxattrwalk> { |
946 | 0 | Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) |
947 | 0 | } |
948 | | |
949 | 0 | fn xattr_create(&mut self, _xattr_create: &Txattrcreate) -> io::Result<()> { |
950 | 0 | Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) |
951 | 0 | } |
952 | | |
953 | 0 | fn readdir(&mut self, readdir: &Treaddir) -> io::Result<Rreaddir> { |
954 | 0 | let fid = self.fids.get_mut(&readdir.fid).ok_or_else(ebadf)?; |
955 | | |
956 | 0 | if fid.filetype != FileType::Directory { |
957 | 0 | return Err(io::Error::from_raw_os_error(libc::ENOTDIR)); |
958 | 0 | } |
959 | 0 |
|
960 | 0 | // Use an empty Rreaddir struct to figure out the maximum number of bytes that |
961 | 0 | // can be returned. |
962 | 0 | let header_size = Rframe { |
963 | 0 | tag: 0, |
964 | 0 | msg: Rmessage::Readdir(Rreaddir { |
965 | 0 | data: Data(Vec::new()), |
966 | 0 | }), |
967 | 0 | } |
968 | 0 | .byte_size(); |
969 | 0 | let count = min(self.cfg.msize - header_size, readdir.count); |
970 | 0 | let mut cursor = Cursor::new(Vec::with_capacity(count as usize)); |
971 | | |
972 | 0 | let dir = fid.file.as_mut().ok_or_else(ebadf)?; |
973 | 0 | let mut dirents = read_dir(dir, readdir.offset as libc::c_long)?; |
974 | 0 | while let Some(dirent) = dirents.next().transpose()? { |
975 | 0 | let st = statat(&fid.path, dirent.name.as_c_str(), 0)?; |
976 | | |
977 | 0 | let entry = Dirent { |
978 | 0 | qid: st.into(), |
979 | 0 | offset: dirent.offset, |
980 | 0 | ty: dirent.type_, |
981 | 0 | name: dirent.name, |
982 | 0 | }; |
983 | 0 |
|
984 | 0 | let byte_size = entry.byte_size() as usize; |
985 | 0 |
|
986 | 0 | if cursor.get_ref().capacity() - cursor.get_ref().len() < byte_size { |
987 | | // No more room in the buffer. |
988 | 0 | break; |
989 | 0 | } |
990 | 0 |
|
991 | 0 | entry.encode(&mut cursor)?; |
992 | | } |
993 | | |
994 | 0 | Ok(Rreaddir { |
995 | 0 | data: Data(cursor.into_inner()), |
996 | 0 | }) |
997 | 0 | } |
998 | | |
999 | 0 | fn fsync(&mut self, fsync: &Tfsync) -> io::Result<()> { |
1000 | 0 | let file = self |
1001 | 0 | .fids |
1002 | 0 | .get(&fsync.fid) |
1003 | 0 | .and_then(|fid| fid.file.as_ref()) |
1004 | 0 | .ok_or_else(ebadf)?; |
1005 | | |
1006 | 0 | if fsync.datasync == 0 { |
1007 | 0 | file.sync_all()?; |
1008 | | } else { |
1009 | 0 | file.sync_data()?; |
1010 | | } |
1011 | 0 | Ok(()) |
1012 | 0 | } |
1013 | | |
1014 | | /// Implement posix byte range locking code. |
1015 | | /// Our implementation mirrors that of QEMU/9p - that is to say, |
1016 | | /// we essentially punt on mirroring lock state between client/server |
1017 | | /// and defer lock semantics to the VFS layer on the client side. Aside |
1018 | | /// from fd existence check we always return success. QEMU reference: |
1019 | | /// <https://github.com/qemu/qemu/blob/754f756cc4c6d9d14b7230c62b5bb20f9d655888/hw/9pfs/9p.c#L3669> |
1020 | | /// |
1021 | | /// NOTE: this means that files locked on the client may be interefered with |
1022 | | /// from either the server's side, or from other clients (guests). This |
1023 | | /// tracks with QEMU implementation, and will be obviated if crosvm decides |
1024 | | /// to drop 9p in favor of virtio-fs. QEMU only allows for a single client, |
1025 | | /// and we leave it to users of the crate to provide actual lock handling. |
1026 | 0 | fn lock(&mut self, lock: &Tlock) -> io::Result<Rlock> { |
1027 | | // Ensure fd passed in TLOCK request exists and has a mapping. |
1028 | 0 | let fd = self |
1029 | 0 | .fids |
1030 | 0 | .get(&lock.fid) |
1031 | 0 | .and_then(|fid| fid.file.as_ref()) |
1032 | 0 | .ok_or_else(ebadf)? |
1033 | 0 | .as_raw_fd(); |
1034 | 0 |
|
1035 | 0 | syscall!(unsafe { |
1036 | 0 | // Safe because zero-filled libc::stat is a valid value, fstat |
1037 | 0 | // populates the struct fields. |
1038 | 0 | let mut stbuf: libc::stat64 = std::mem::zeroed(); |
1039 | 0 | // Safe because this doesn't modify memory and we check the return value. |
1040 | 0 | libc::fstat64(fd, &mut stbuf) |
1041 | 0 | })?; |
1042 | | |
1043 | 0 | Ok(Rlock { |
1044 | 0 | status: P9_LOCK_SUCCESS, |
1045 | 0 | }) |
1046 | 0 | } |
1047 | | |
1048 | | /// |
1049 | | /// Much like lock(), defer locking semantics to VFS and return success. |
1050 | | /// |
1051 | 0 | fn get_lock(&mut self, get_lock: &Tgetlock) -> io::Result<Rgetlock> { |
1052 | | // Ensure fd passed in GETTLOCK request exists and has a mapping. |
1053 | 0 | let fd = self |
1054 | 0 | .fids |
1055 | 0 | .get(&get_lock.fid) |
1056 | 0 | .and_then(|fid| fid.file.as_ref()) |
1057 | 0 | .ok_or_else(ebadf)? |
1058 | 0 | .as_raw_fd(); |
1059 | 0 |
|
1060 | 0 | // Safe because this doesn't modify memory and we check the return value. |
1061 | 0 | syscall!(unsafe { |
1062 | 0 | let mut stbuf: libc::stat64 = std::mem::zeroed(); |
1063 | 0 | libc::fstat64(fd, &mut stbuf) |
1064 | 0 | })?; |
1065 | | |
1066 | 0 | Ok(Rgetlock { |
1067 | 0 | type_: P9_LOCK_TYPE_UNLCK, |
1068 | 0 | start: get_lock.start, |
1069 | 0 | length: get_lock.length, |
1070 | 0 | proc_id: get_lock.proc_id, |
1071 | 0 | client_id: get_lock.client_id.clone(), |
1072 | 0 | }) |
1073 | 0 | } |
1074 | | |
1075 | 0 | fn link(&mut self, link: Tlink) -> io::Result<()> { |
1076 | 0 | let target = self.fids.get(&link.fid).ok_or_else(ebadf)?; |
1077 | 0 | let path = string_to_cstring(format!("self/fd/{}", target.path.as_raw_fd()))?; |
1078 | | |
1079 | 0 | let dir = self.fids.get(&link.dfid).ok_or_else(ebadf)?; |
1080 | | |
1081 | | // Safe because this doesn't modify any memory and we check the return value. |
1082 | 0 | syscall!(unsafe { |
1083 | 0 | libc::linkat( |
1084 | 0 | self.proc.as_raw_fd(), |
1085 | 0 | path.as_ptr(), |
1086 | 0 | dir.path.as_raw_fd(), |
1087 | 0 | link.name.as_ptr(), |
1088 | 0 | libc::AT_SYMLINK_FOLLOW, |
1089 | 0 | ) |
1090 | 0 | })?; |
1091 | 0 | Ok(()) |
1092 | 0 | } |
1093 | | |
1094 | 0 | fn mkdir(&mut self, mkdir: Tmkdir) -> io::Result<Rmkdir> { |
1095 | 0 | let fid = self.fids.get(&mkdir.dfid).ok_or_else(ebadf)?; |
1096 | | |
1097 | | // Safe because this doesn't modify any memory and we check the return value. |
1098 | 0 | syscall!(unsafe { libc::mkdirat(fid.path.as_raw_fd(), mkdir.name.as_ptr(), mkdir.mode) })?; |
1099 | | Ok(Rmkdir { |
1100 | 0 | qid: statat(&fid.path, mkdir.name.as_c_str(), 0).map(Qid::from)?, |
1101 | | }) |
1102 | 0 | } |
1103 | | |
1104 | 0 | fn rename_at(&mut self, rename_at: Trenameat) -> io::Result<()> { |
1105 | 0 | let olddir = self.fids.get(&rename_at.olddirfid).ok_or_else(ebadf)?; |
1106 | 0 | let newdir = self.fids.get(&rename_at.newdirfid).ok_or_else(ebadf)?; |
1107 | | |
1108 | | // Safe because this doesn't modify any memory and we check the return value. |
1109 | 0 | syscall!(unsafe { |
1110 | 0 | libc::renameat( |
1111 | 0 | olddir.path.as_raw_fd(), |
1112 | 0 | rename_at.oldname.as_ptr(), |
1113 | 0 | newdir.path.as_raw_fd(), |
1114 | 0 | rename_at.newname.as_ptr(), |
1115 | 0 | ) |
1116 | 0 | })?; |
1117 | | |
1118 | 0 | Ok(()) |
1119 | 0 | } |
1120 | | |
1121 | 0 | fn unlink_at(&mut self, unlink_at: Tunlinkat) -> io::Result<()> { |
1122 | 0 | let dir = self.fids.get(&unlink_at.dirfd).ok_or_else(ebadf)?; |
1123 | | |
1124 | 0 | syscall!(unsafe { |
1125 | 0 | libc::unlinkat( |
1126 | 0 | dir.path.as_raw_fd(), |
1127 | 0 | unlink_at.name.as_ptr(), |
1128 | 0 | unlink_at.flags as libc::c_int, |
1129 | 0 | ) |
1130 | 0 | })?; |
1131 | | |
1132 | 0 | Ok(()) |
1133 | 0 | } |
1134 | | } |
1135 | | |
1136 | | #[cfg(test)] |
1137 | | mod tests; |