Coverage Report

Created: 2025-07-23 06:18

/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(&current)?);
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
                    &times 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;