diff options
author | Andrew Walbran <qwandor@google.com> | 2021-08-17 11:01:06 +0000 |
---|---|---|
committer | Andrew Walbran <qwandor@google.com> | 2021-08-17 11:29:49 +0000 |
commit | 658aeeec4911704d0d88e2e37471db56904fc6d8 (patch) | |
tree | 7d93335ae26074aedbcb8f5ef80d0e0dce2ec772 | |
parent | 99069084d493c67942db4fe03f22310dc59ae596 (diff) | |
download | p9-android-t-preview-1.tar.gz |
Remove everything except OWNERSHEADandroid-t-preview-2android-t-preview-1android-t-beta-3android-s-v2-preview-2android-s-v2-preview-1android-s-v2-beta-3android-s-v2-beta-2android-s-qpr3-beta-1android-s-beta-5mastermainandroid-t-preview-1android-s-v2-preview-1android-s-v2-beta-3android-s-qpr3-beta-1android-s-beta-5
p9 is now part of the crosvm repository.
Test: m crosvm
Change-Id: I3c46b745bd3f216df8403385027699a94f3762a4
-rw-r--r-- | .clang-format | 19 | ||||
-rw-r--r-- | .gitignore | 70 | ||||
-rw-r--r-- | Android.bp | 82 | ||||
-rw-r--r-- | Cargo.toml | 13 | ||||
-rw-r--r-- | LICENSE | 27 | ||||
-rw-r--r-- | METADATA | 14 | ||||
-rw-r--r-- | MODULE_LICENSE_BSD | 0 | ||||
-rw-r--r-- | README.md | 21 | ||||
-rw-r--r-- | fuzz/Cargo.toml | 19 | ||||
-rw-r--r-- | fuzz/OWNERS | 3 | ||||
-rw-r--r-- | fuzz/tframe_decode.rs | 12 | ||||
-rw-r--r-- | src/fuzzing.rs | 13 | ||||
-rw-r--r-- | src/lib.rs | 16 | ||||
-rw-r--r-- | src/protocol/messages.rs | 840 | ||||
-rw-r--r-- | src/protocol/mod.rs | 9 | ||||
-rw-r--r-- | src/protocol/wire_format.rs | 704 | ||||
-rw-r--r-- | src/server/mod.rs | 977 | ||||
-rw-r--r-- | src/server/tests.rs | 1147 | ||||
-rw-r--r-- | wire_format_derive/Android.bp | 22 | ||||
-rw-r--r-- | wire_format_derive/Cargo.toml | 13 | ||||
-rw-r--r-- | wire_format_derive/wire_format_derive.rs | 303 |
21 files changed, 0 insertions, 4324 deletions
diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 4fdfdbe..0000000 --- a/.clang-format +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2017 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Defines the Chromium OS style for automatic reformatting. -# http://clang.llvm.org/docs/ClangFormatStyleOptions.html -# Please keep all directives after this one sorted alphabetically. -BasedOnStyle: Chromium - -# This is permitted by the Google and Chromium style guides, and existing code -# uses it heavily. -AllowAllParametersOfDeclarationOnNextLine: true - -# NOLINT(reason) is used heavily by existing code. -CommentPragmas: 'NOLINT:.*' - -# cpplint.py does smarter #include sorting than clang-format (the former ignores -# case and changes '-' to '_'). -SortIncludes: false diff --git a/.gitignore b/.gitignore deleted file mode 100644 index de0d2e3..0000000 --- a/.gitignore +++ /dev/null @@ -1,70 +0,0 @@ -# -# NOTE! Please use 'git ls-files -i --exclude-standard' -# command after changing this file, to see if there are -# any tracked files which get ignored after the change. -# - -# Temp files (e.g. editors). -*~ -*.sw[op] - -# Compiled objects. -*.a -*.o -*.l[ao] -*.so -*.exe - -*.d -*.depends -.deps -.libs - -*.gch -*.gcda -*.gcno - -# Common output files. -*.dump -*.out -*.test - -# Protobuf files. -*.pb.cc -*.pb.h - -# Python files. -*.pyc - -# Debug (e.g. gdb). -.gdb_history -.gdbinit - -core -cscope.* -tags -tags_sorted_by_file - -# Patch files. -*.diff -*.orig -*.rej - -# Nested git repos. -/glbench/images/ - -# Cargo lock file. -Cargo.lock - -# Cargo build directories. -target/ - -# VSCode source dirs -.vscode/ - -# Intellij project dirs -.idea - -# clangd cache dir -.clangd -.cache diff --git a/Android.bp b/Android.bp deleted file mode 100644 index fe386a1..0000000 --- a/Android.bp +++ /dev/null @@ -1,82 +0,0 @@ -// This file is generated by cargo2android.py --run --device --tests --dependencies --global_defaults=crosvm_defaults. - -package { - default_applicable_licenses: ["external_vm_tools_p9_license"], -} - -// Added automatically by a large-scale-change -// See: http://go/android-license-faq -license { - name: "external_vm_tools_p9_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-BSD", - ], - license_text: [ - "LICENSE", - ], -} - -rust_library { - name: "libp9", - defaults: ["crosvm_defaults"], - host_supported: true, - crate_name: "p9", - srcs: ["src/lib.rs"], - edition: "2018", - rustlibs: [ - "liblibc", - "liblibchromeos", - ], - proc_macros: ["libwire_format_derive"], -} - -rust_defaults { - name: "p9_defaults", - defaults: ["crosvm_defaults"], - crate_name: "p9", - srcs: ["src/lib.rs"], - test_suites: ["general-tests"], - auto_gen_config: true, - edition: "2018", - rustlibs: [ - "liblibc", - "liblibchromeos", - ], - proc_macros: ["libwire_format_derive"], -} - -rust_test_host { - name: "p9_host_test_src_lib", - defaults: ["p9_defaults"], -} - -rust_test { - name: "p9_device_test_src_lib", - defaults: ["p9_defaults"], -} - -// dependent_library ["feature_list"] -// ../../crosvm/assertions/src/lib.rs -// ../../crosvm/data_model/src/lib.rs -// ../../libchromeos-rs/src/lib.rs -// autocfg-1.0.1 -// cfg-if-0.1.10 -// futures-0.3.9 "alloc" -// futures-channel-0.3.9 "alloc,futures-sink,sink" -// futures-core-0.3.9 "alloc" -// futures-io-0.3.9 -// futures-sink-0.3.9 "alloc" -// futures-task-0.3.9 "alloc" -// futures-util-0.3.9 "alloc,futures-sink,sink" -// intrusive-collections-0.9.0 "alloc,default" -// libc-0.2.82 "default,std" -// log-0.4.11 -// memoffset-0.5.6 "default" -// pin-project-lite-0.2.1 -// pin-utils-0.1.0 -// proc-macro2-1.0.24 "default,proc-macro" -// protobuf-2.20.0 -// quote-1.0.8 "default,proc-macro" -// syn-1.0.58 "clone-impls,default,derive,parsing,printing,proc-macro,quote" -// unicode-xid-0.2.1 "default" diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 11ea913..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "p9" -version = "0.1.0" -authors = ["The Chromium OS Authors"] -edition = "2018" - -[dependencies] -libc = "*" -libchromeos = { path = "../../libchromeos-rs" } # provided by ebuild -wire_format_derive = { path = "wire_format_derive", version = "*" } - -[features] -trace = [] diff --git a/LICENSE b/LICENSE deleted file mode 100644 index b9e779f..0000000 --- a/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/METADATA b/METADATA deleted file mode 100644 index 4626851..0000000 --- a/METADATA +++ /dev/null @@ -1,14 +0,0 @@ -name: "p9" -description: - "Server implementation of the [9p] file system protocol. Taken from " - "chromium/platform2, vm_tools/p9." - -third_party { - url { - type: GIT - value: "https://chromium.googlesource.com/chromiumos/platform2" - } - version: "2c3e8252c684673e83278a0124a998e997dbbcc2" - last_upgrade_date { year: 2020 month: 12 day: 3 } - license_type: NOTICE -} diff --git a/MODULE_LICENSE_BSD b/MODULE_LICENSE_BSD deleted file mode 100644 index e69de29..0000000 --- a/MODULE_LICENSE_BSD +++ /dev/null diff --git a/README.md b/README.md deleted file mode 100644 index b85cc15..0000000 --- a/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# p9 - Server implementation of the [9p] file system protocol - -This directory contains the protocol definition and a server implementation of -the [9p] file system protocol. - -* [wire_format_derive] - A [procedural macro] that derives the serialization - and de-serialization implementation for a struct into the [9p] wire - format. -* [src/protocol] - Defines all the messages used in the [9p] protocol. Also - implements serialization and de-serialization for some base types - (integers, strings, vectors) that form the foundation of all [9p] - messages. Wire format implementations for all other messages are derived - using the `wire_format_derive` macro. -* [src/server.rs] - Implements a full [9p] server, carrying out file system - requests on behalf of clients. - -[9p]: http://man.cat-v.org/plan_9/5/intro -[procedural macro]: https://doc.rust-lang.org/proc_macro/index.html -[src/protocol]: src/protocol/ -[src/server.rs]: src/server.rs -[wire_format_derive]: wire_format_derive/ diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml deleted file mode 100644 index 1220681..0000000 --- a/fuzz/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "p9-fuzz" -version = "0.1.0" -authors = ["The Chromium OS Authors"] -edition = "2018" - -[dependencies] -p9 = { path = "../" } -cros_fuzz = "*" - -[workspace] -members = ["."] - -[[bin]] -name = "p9_tframe_decode_fuzzer" -path = "tframe_decode.rs" - -[patch.crates-io] -wire_format_derive = { path = "../wire_format_derive" }
\ No newline at end of file diff --git a/fuzz/OWNERS b/fuzz/OWNERS deleted file mode 100644 index cef66ae..0000000 --- a/fuzz/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# ANDROID: Remove because Gerrit does not recognize these emails and reject the upload. -#chirantan@chromium.org -#dgreid@chromium.org
\ No newline at end of file diff --git a/fuzz/tframe_decode.rs b/fuzz/tframe_decode.rs deleted file mode 100644 index 6cdeae3..0000000 --- a/fuzz/tframe_decode.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2018 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#![no_main] - -use cros_fuzz::fuzz_target; -use p9::fuzzing::tframe_decode; - -fuzz_target!(|bytes: &[u8]| { - tframe_decode(bytes); -}); diff --git a/src/fuzzing.rs b/src/fuzzing.rs deleted file mode 100644 index 0ba8e23..0000000 --- a/src/fuzzing.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2018 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::io::Cursor; - -use crate::protocol::{Tframe, WireFormat}; - -pub fn tframe_decode(bytes: &[u8]) { - let mut cursor = Cursor::new(bytes); - - while let Ok(_) = Tframe::decode(&mut cursor) {} -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index b00c706..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2018 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -extern crate libc; - -#[macro_use] -extern crate wire_format_derive; - -mod protocol; -mod server; - -#[cfg(fuzzing)] -pub mod fuzzing; - -pub use server::*; diff --git a/src/protocol/messages.rs b/src/protocol/messages.rs deleted file mode 100644 index b5a03c0..0000000 --- a/src/protocol/messages.rs +++ /dev/null @@ -1,840 +0,0 @@ -// Copyright 2018 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::io::{self, ErrorKind, Read, Write}; -use std::mem; -use std::string::String; -use std::vec::Vec; - -use crate::protocol::wire_format::{Data, WireFormat}; - -// Message type constants. Taken from "include/net/9p/9p.h" in the linux kernel -// tree. The protocol specifies each R* message to be the corresponding T* -// message plus one. -const TLERROR: u8 = 6; -const RLERROR: u8 = TLERROR + 1; -const TSTATFS: u8 = 8; -const RSTATFS: u8 = TSTATFS + 1; -const TLOPEN: u8 = 12; -const RLOPEN: u8 = TLOPEN + 1; -const TLCREATE: u8 = 14; -const RLCREATE: u8 = TLCREATE + 1; -const TSYMLINK: u8 = 16; -const RSYMLINK: u8 = TSYMLINK + 1; -const TMKNOD: u8 = 18; -const RMKNOD: u8 = TMKNOD + 1; -const TRENAME: u8 = 20; -const RRENAME: u8 = TRENAME + 1; -const TREADLINK: u8 = 22; -const RREADLINK: u8 = TREADLINK + 1; -const TGETATTR: u8 = 24; -const RGETATTR: u8 = TGETATTR + 1; -const TSETATTR: u8 = 26; -const RSETATTR: u8 = TSETATTR + 1; -const TXATTRWALK: u8 = 30; -const RXATTRWALK: u8 = TXATTRWALK + 1; -const TXATTRCREATE: u8 = 32; -const RXATTRCREATE: u8 = TXATTRCREATE + 1; -const TREADDIR: u8 = 40; -const RREADDIR: u8 = TREADDIR + 1; -const TFSYNC: u8 = 50; -const RFSYNC: u8 = TFSYNC + 1; -const TLOCK: u8 = 52; -const RLOCK: u8 = TLOCK + 1; -const TGETLOCK: u8 = 54; -const RGETLOCK: u8 = TGETLOCK + 1; -const TLINK: u8 = 70; -const RLINK: u8 = TLINK + 1; -const TMKDIR: u8 = 72; -const RMKDIR: u8 = TMKDIR + 1; -const TRENAMEAT: u8 = 74; -const RRENAMEAT: u8 = TRENAMEAT + 1; -const TUNLINKAT: u8 = 76; -const RUNLINKAT: u8 = TUNLINKAT + 1; -const TVERSION: u8 = 100; -const RVERSION: u8 = TVERSION + 1; -const TAUTH: u8 = 102; -const RAUTH: u8 = TAUTH + 1; -const TATTACH: u8 = 104; -const RATTACH: u8 = TATTACH + 1; -const _TERROR: u8 = 106; -const _RERROR: u8 = _TERROR + 1; -const TFLUSH: u8 = 108; -const RFLUSH: u8 = TFLUSH + 1; -const TWALK: u8 = 110; -const RWALK: u8 = TWALK + 1; -const _TOPEN: u8 = 112; -const _ROPEN: u8 = _TOPEN + 1; -const _TCREATE: u8 = 114; -const _RCREATE: u8 = _TCREATE + 1; -const TREAD: u8 = 116; -const RREAD: u8 = TREAD + 1; -const TWRITE: u8 = 118; -const RWRITE: u8 = TWRITE + 1; -const TCLUNK: u8 = 120; -const RCLUNK: u8 = TCLUNK + 1; -const TREMOVE: u8 = 122; -const RREMOVE: u8 = TREMOVE + 1; -const _TSTAT: u8 = 124; -const _RSTAT: u8 = _TSTAT + 1; -const _TWSTAT: u8 = 126; -const _RWSTAT: u8 = _TWSTAT + 1; - -/// A message sent from a 9P client to a 9P server. -#[derive(Debug)] -pub enum Tmessage { - Version(Tversion), - Flush(Tflush), - Walk(Twalk), - Read(Tread), - Write(Twrite), - Clunk(Tclunk), - Remove(Tremove), - Attach(Tattach), - Auth(Tauth), - Statfs(Tstatfs), - Lopen(Tlopen), - Lcreate(Tlcreate), - Symlink(Tsymlink), - Mknod(Tmknod), - Rename(Trename), - Readlink(Treadlink), - GetAttr(Tgetattr), - SetAttr(Tsetattr), - XattrWalk(Txattrwalk), - XattrCreate(Txattrcreate), - Readdir(Treaddir), - Fsync(Tfsync), - Lock(Tlock), - GetLock(Tgetlock), - Link(Tlink), - Mkdir(Tmkdir), - RenameAt(Trenameat), - UnlinkAt(Tunlinkat), -} - -#[derive(Debug)] -pub struct Tframe { - pub tag: u16, - pub msg: Tmessage, -} - -impl WireFormat for Tframe { - fn byte_size(&self) -> u32 { - let msg_size = match self.msg { - Tmessage::Version(ref version) => version.byte_size(), - Tmessage::Flush(ref flush) => flush.byte_size(), - Tmessage::Walk(ref walk) => walk.byte_size(), - Tmessage::Read(ref read) => read.byte_size(), - Tmessage::Write(ref write) => write.byte_size(), - Tmessage::Clunk(ref clunk) => clunk.byte_size(), - Tmessage::Remove(ref remove) => remove.byte_size(), - Tmessage::Attach(ref attach) => attach.byte_size(), - Tmessage::Auth(ref auth) => auth.byte_size(), - Tmessage::Statfs(ref statfs) => statfs.byte_size(), - Tmessage::Lopen(ref lopen) => lopen.byte_size(), - Tmessage::Lcreate(ref lcreate) => lcreate.byte_size(), - Tmessage::Symlink(ref symlink) => symlink.byte_size(), - Tmessage::Mknod(ref mknod) => mknod.byte_size(), - Tmessage::Rename(ref rename) => rename.byte_size(), - Tmessage::Readlink(ref readlink) => readlink.byte_size(), - Tmessage::GetAttr(ref getattr) => getattr.byte_size(), - Tmessage::SetAttr(ref setattr) => setattr.byte_size(), - Tmessage::XattrWalk(ref xattrwalk) => xattrwalk.byte_size(), - Tmessage::XattrCreate(ref xattrcreate) => xattrcreate.byte_size(), - Tmessage::Readdir(ref readdir) => readdir.byte_size(), - Tmessage::Fsync(ref fsync) => fsync.byte_size(), - Tmessage::Lock(ref lock) => lock.byte_size(), - Tmessage::GetLock(ref getlock) => getlock.byte_size(), - Tmessage::Link(ref link) => link.byte_size(), - Tmessage::Mkdir(ref mkdir) => mkdir.byte_size(), - Tmessage::RenameAt(ref renameat) => renameat.byte_size(), - Tmessage::UnlinkAt(ref unlinkat) => unlinkat.byte_size(), - }; - - // size + type + tag + message size - (mem::size_of::<u32>() + mem::size_of::<u8>() + mem::size_of::<u16>()) as u32 + msg_size - } - - fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> { - self.byte_size().encode(writer)?; - - let ty = match self.msg { - Tmessage::Version(_) => TVERSION, - Tmessage::Flush(_) => TFLUSH, - Tmessage::Walk(_) => TWALK, - Tmessage::Read(_) => TREAD, - Tmessage::Write(_) => TWRITE, - Tmessage::Clunk(_) => TCLUNK, - Tmessage::Remove(_) => TREMOVE, - Tmessage::Attach(_) => TATTACH, - Tmessage::Auth(_) => TAUTH, - Tmessage::Statfs(_) => TSTATFS, - Tmessage::Lopen(_) => TLOPEN, - Tmessage::Lcreate(_) => TLCREATE, - Tmessage::Symlink(_) => TSYMLINK, - Tmessage::Mknod(_) => TMKNOD, - Tmessage::Rename(_) => TRENAME, - Tmessage::Readlink(_) => TREADLINK, - Tmessage::GetAttr(_) => TGETATTR, - Tmessage::SetAttr(_) => TSETATTR, - Tmessage::XattrWalk(_) => TXATTRWALK, - Tmessage::XattrCreate(_) => TXATTRCREATE, - Tmessage::Readdir(_) => TREADDIR, - Tmessage::Fsync(_) => TFSYNC, - Tmessage::Lock(_) => TLOCK, - Tmessage::GetLock(_) => TGETLOCK, - Tmessage::Link(_) => TLINK, - Tmessage::Mkdir(_) => TMKDIR, - Tmessage::RenameAt(_) => TRENAMEAT, - Tmessage::UnlinkAt(_) => TUNLINKAT, - }; - - ty.encode(writer)?; - self.tag.encode(writer)?; - - match self.msg { - Tmessage::Version(ref version) => version.encode(writer), - Tmessage::Flush(ref flush) => flush.encode(writer), - Tmessage::Walk(ref walk) => walk.encode(writer), - Tmessage::Read(ref read) => read.encode(writer), - Tmessage::Write(ref write) => write.encode(writer), - Tmessage::Clunk(ref clunk) => clunk.encode(writer), - Tmessage::Remove(ref remove) => remove.encode(writer), - Tmessage::Attach(ref attach) => attach.encode(writer), - Tmessage::Auth(ref auth) => auth.encode(writer), - Tmessage::Statfs(ref statfs) => statfs.encode(writer), - Tmessage::Lopen(ref lopen) => lopen.encode(writer), - Tmessage::Lcreate(ref lcreate) => lcreate.encode(writer), - Tmessage::Symlink(ref symlink) => symlink.encode(writer), - Tmessage::Mknod(ref mknod) => mknod.encode(writer), - Tmessage::Rename(ref rename) => rename.encode(writer), - Tmessage::Readlink(ref readlink) => readlink.encode(writer), - Tmessage::GetAttr(ref getattr) => getattr.encode(writer), - Tmessage::SetAttr(ref setattr) => setattr.encode(writer), - Tmessage::XattrWalk(ref xattrwalk) => xattrwalk.encode(writer), - Tmessage::XattrCreate(ref xattrcreate) => xattrcreate.encode(writer), - Tmessage::Readdir(ref readdir) => readdir.encode(writer), - Tmessage::Fsync(ref fsync) => fsync.encode(writer), - Tmessage::Lock(ref lock) => lock.encode(writer), - Tmessage::GetLock(ref getlock) => getlock.encode(writer), - Tmessage::Link(ref link) => link.encode(writer), - Tmessage::Mkdir(ref mkdir) => mkdir.encode(writer), - Tmessage::RenameAt(ref renameat) => renameat.encode(writer), - Tmessage::UnlinkAt(ref unlinkat) => unlinkat.encode(writer), - } - } - - fn decode<R: Read>(reader: &mut R) -> io::Result<Self> { - let byte_size: u32 = WireFormat::decode(reader)?; - - // byte_size includes the size of byte_size so remove that from the - // expected length of the message. Also make sure that byte_size is at least - // that long to begin with. - if byte_size < mem::size_of::<u32>() as u32 { - return Err(io::Error::new( - ErrorKind::InvalidData, - format!("byte_size(= {}) is less than 4 bytes", byte_size), - )); - } - - let reader = &mut reader.take((byte_size - mem::size_of::<u32>() as u32) as u64); - - let mut ty = [0u8]; - reader.read_exact(&mut ty)?; - - let tag: u16 = WireFormat::decode(reader)?; - - let msg = match ty[0] { - TVERSION => Ok(Tmessage::Version(WireFormat::decode(reader)?)), - TFLUSH => Ok(Tmessage::Flush(WireFormat::decode(reader)?)), - TWALK => Ok(Tmessage::Walk(WireFormat::decode(reader)?)), - TREAD => Ok(Tmessage::Read(WireFormat::decode(reader)?)), - TWRITE => Ok(Tmessage::Write(WireFormat::decode(reader)?)), - TCLUNK => Ok(Tmessage::Clunk(WireFormat::decode(reader)?)), - TREMOVE => Ok(Tmessage::Remove(WireFormat::decode(reader)?)), - TATTACH => Ok(Tmessage::Attach(WireFormat::decode(reader)?)), - TAUTH => Ok(Tmessage::Auth(WireFormat::decode(reader)?)), - TSTATFS => Ok(Tmessage::Statfs(WireFormat::decode(reader)?)), - TLOPEN => Ok(Tmessage::Lopen(WireFormat::decode(reader)?)), - TLCREATE => Ok(Tmessage::Lcreate(WireFormat::decode(reader)?)), - TSYMLINK => Ok(Tmessage::Symlink(WireFormat::decode(reader)?)), - TMKNOD => Ok(Tmessage::Mknod(WireFormat::decode(reader)?)), - TRENAME => Ok(Tmessage::Rename(WireFormat::decode(reader)?)), - TREADLINK => Ok(Tmessage::Readlink(WireFormat::decode(reader)?)), - TGETATTR => Ok(Tmessage::GetAttr(WireFormat::decode(reader)?)), - TSETATTR => Ok(Tmessage::SetAttr(WireFormat::decode(reader)?)), - TXATTRWALK => Ok(Tmessage::XattrWalk(WireFormat::decode(reader)?)), - TXATTRCREATE => Ok(Tmessage::XattrCreate(WireFormat::decode(reader)?)), - TREADDIR => Ok(Tmessage::Readdir(WireFormat::decode(reader)?)), - TFSYNC => Ok(Tmessage::Fsync(WireFormat::decode(reader)?)), - TLOCK => Ok(Tmessage::Lock(WireFormat::decode(reader)?)), - TGETLOCK => Ok(Tmessage::GetLock(WireFormat::decode(reader)?)), - TLINK => Ok(Tmessage::Link(WireFormat::decode(reader)?)), - TMKDIR => Ok(Tmessage::Mkdir(WireFormat::decode(reader)?)), - TRENAMEAT => Ok(Tmessage::RenameAt(WireFormat::decode(reader)?)), - TUNLINKAT => Ok(Tmessage::UnlinkAt(WireFormat::decode(reader)?)), - err => Err(io::Error::new( - ErrorKind::InvalidData, - format!("unknown message type {}", err), - )), - }?; - - Ok(Tframe { tag, msg }) - } -} - -#[derive(Debug, P9WireFormat)] -pub struct Tversion { - pub msize: u32, - pub version: String, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tflush { - pub oldtag: u16, -} - -#[derive(Debug, P9WireFormat)] -pub struct Twalk { - pub fid: u32, - pub newfid: u32, - pub wnames: Vec<String>, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tread { - pub fid: u32, - pub offset: u64, - pub count: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Twrite { - pub fid: u32, - pub offset: u64, - pub data: Data, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tclunk { - pub fid: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tremove { - pub fid: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tauth { - pub afid: u32, - pub uname: String, - pub aname: String, - pub n_uname: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tattach { - pub fid: u32, - pub afid: u32, - pub uname: String, - pub aname: String, - pub n_uname: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tstatfs { - pub fid: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tlopen { - pub fid: u32, - pub flags: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tlcreate { - pub fid: u32, - pub name: String, - pub flags: u32, - pub mode: u32, - pub gid: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tsymlink { - pub fid: u32, - pub name: String, - pub symtgt: String, - pub gid: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tmknod { - pub dfid: u32, - pub name: String, - pub mode: u32, - pub major: u32, - pub minor: u32, - pub gid: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Trename { - pub fid: u32, - pub dfid: u32, - pub name: String, -} - -#[derive(Debug, P9WireFormat)] -pub struct Treadlink { - pub fid: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tgetattr { - pub fid: u32, - pub request_mask: u64, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tsetattr { - pub fid: u32, - pub valid: u32, - pub mode: u32, - pub uid: u32, - pub gid: u32, - pub size: u64, - pub atime_sec: u64, - pub atime_nsec: u64, - pub mtime_sec: u64, - pub mtime_nsec: u64, -} - -#[derive(Debug, P9WireFormat)] -pub struct Txattrwalk { - pub fid: u32, - pub newfid: u32, - pub name: String, -} - -#[derive(Debug, P9WireFormat)] -pub struct Txattrcreate { - pub fid: u32, - pub name: String, - pub attr_size: u64, - pub flags: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Treaddir { - pub fid: u32, - pub offset: u64, - pub count: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tfsync { - pub fid: u32, - pub datasync: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tlock { - pub fid: u32, - pub type_: u8, - pub flags: u32, - pub start: u64, - pub length: u64, - pub proc_id: u32, - pub client_id: String, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tgetlock { - pub fid: u32, - pub type_: u8, - pub start: u64, - pub length: u64, - pub proc_id: u32, - pub client_id: String, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tlink { - pub dfid: u32, - pub fid: u32, - pub name: String, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tmkdir { - pub dfid: u32, - pub name: String, - pub mode: u32, - pub gid: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Trenameat { - pub olddirfid: u32, - pub oldname: String, - pub newdirfid: u32, - pub newname: String, -} - -#[derive(Debug, P9WireFormat)] -pub struct Tunlinkat { - pub dirfd: u32, - pub name: String, - pub flags: u32, -} - -/// A message sent from a 9P server to a 9P client in response to a request from -/// that client. Encapsulates a full frame. -#[derive(Debug)] -pub enum Rmessage { - Version(Rversion), - Flush, - Walk(Rwalk), - Read(Rread), - Write(Rwrite), - Clunk, - Remove, - Attach(Rattach), - Auth(Rauth), - Statfs(Rstatfs), - Lopen(Rlopen), - Lcreate(Rlcreate), - Symlink(Rsymlink), - Mknod(Rmknod), - Rename, - Readlink(Rreadlink), - GetAttr(Rgetattr), - SetAttr, - XattrWalk(Rxattrwalk), - XattrCreate, - Readdir(Rreaddir), - Fsync, - Lock(Rlock), - GetLock(Rgetlock), - Link, - Mkdir(Rmkdir), - RenameAt, - UnlinkAt, - Lerror(Rlerror), -} - -#[derive(Debug)] -pub struct Rframe { - pub tag: u16, - pub msg: Rmessage, -} - -impl WireFormat for Rframe { - fn byte_size(&self) -> u32 { - let msg_size = match self.msg { - Rmessage::Version(ref version) => version.byte_size(), - Rmessage::Flush => 0, - Rmessage::Walk(ref walk) => walk.byte_size(), - Rmessage::Read(ref read) => read.byte_size(), - Rmessage::Write(ref write) => write.byte_size(), - Rmessage::Clunk => 0, - Rmessage::Remove => 0, - Rmessage::Attach(ref attach) => attach.byte_size(), - Rmessage::Auth(ref auth) => auth.byte_size(), - Rmessage::Statfs(ref statfs) => statfs.byte_size(), - Rmessage::Lopen(ref lopen) => lopen.byte_size(), - Rmessage::Lcreate(ref lcreate) => lcreate.byte_size(), - Rmessage::Symlink(ref symlink) => symlink.byte_size(), - Rmessage::Mknod(ref mknod) => mknod.byte_size(), - Rmessage::Rename => 0, - Rmessage::Readlink(ref readlink) => readlink.byte_size(), - Rmessage::GetAttr(ref getattr) => getattr.byte_size(), - Rmessage::SetAttr => 0, - Rmessage::XattrWalk(ref xattrwalk) => xattrwalk.byte_size(), - Rmessage::XattrCreate => 0, - Rmessage::Readdir(ref readdir) => readdir.byte_size(), - Rmessage::Fsync => 0, - Rmessage::Lock(ref lock) => lock.byte_size(), - Rmessage::GetLock(ref getlock) => getlock.byte_size(), - Rmessage::Link => 0, - Rmessage::Mkdir(ref mkdir) => mkdir.byte_size(), - Rmessage::RenameAt => 0, - Rmessage::UnlinkAt => 0, - Rmessage::Lerror(ref lerror) => lerror.byte_size(), - }; - - // size + type + tag + message size - (mem::size_of::<u32>() + mem::size_of::<u8>() + mem::size_of::<u16>()) as u32 + msg_size - } - - fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> { - self.byte_size().encode(writer)?; - - let ty = match self.msg { - Rmessage::Version(_) => RVERSION, - Rmessage::Flush => RFLUSH, - Rmessage::Walk(_) => RWALK, - Rmessage::Read(_) => RREAD, - Rmessage::Write(_) => RWRITE, - Rmessage::Clunk => RCLUNK, - Rmessage::Remove => RREMOVE, - Rmessage::Attach(_) => RATTACH, - Rmessage::Auth(_) => RAUTH, - Rmessage::Statfs(_) => RSTATFS, - Rmessage::Lopen(_) => RLOPEN, - Rmessage::Lcreate(_) => RLCREATE, - Rmessage::Symlink(_) => RSYMLINK, - Rmessage::Mknod(_) => RMKNOD, - Rmessage::Rename => RRENAME, - Rmessage::Readlink(_) => RREADLINK, - Rmessage::GetAttr(_) => RGETATTR, - Rmessage::SetAttr => RSETATTR, - Rmessage::XattrWalk(_) => RXATTRWALK, - Rmessage::XattrCreate => RXATTRCREATE, - Rmessage::Readdir(_) => RREADDIR, - Rmessage::Fsync => RFSYNC, - Rmessage::Lock(_) => RLOCK, - Rmessage::GetLock(_) => RGETLOCK, - Rmessage::Link => RLINK, - Rmessage::Mkdir(_) => RMKDIR, - Rmessage::RenameAt => RRENAMEAT, - Rmessage::UnlinkAt => RUNLINKAT, - Rmessage::Lerror(_) => RLERROR, - }; - - ty.encode(writer)?; - self.tag.encode(writer)?; - - match self.msg { - Rmessage::Version(ref version) => version.encode(writer), - Rmessage::Flush => Ok(()), - Rmessage::Walk(ref walk) => walk.encode(writer), - Rmessage::Read(ref read) => read.encode(writer), - Rmessage::Write(ref write) => write.encode(writer), - Rmessage::Clunk => Ok(()), - Rmessage::Remove => Ok(()), - Rmessage::Attach(ref attach) => attach.encode(writer), - Rmessage::Auth(ref auth) => auth.encode(writer), - Rmessage::Statfs(ref statfs) => statfs.encode(writer), - Rmessage::Lopen(ref lopen) => lopen.encode(writer), - Rmessage::Lcreate(ref lcreate) => lcreate.encode(writer), - Rmessage::Symlink(ref symlink) => symlink.encode(writer), - Rmessage::Mknod(ref mknod) => mknod.encode(writer), - Rmessage::Rename => Ok(()), - Rmessage::Readlink(ref readlink) => readlink.encode(writer), - Rmessage::GetAttr(ref getattr) => getattr.encode(writer), - Rmessage::SetAttr => Ok(()), - Rmessage::XattrWalk(ref xattrwalk) => xattrwalk.encode(writer), - Rmessage::XattrCreate => Ok(()), - Rmessage::Readdir(ref readdir) => readdir.encode(writer), - Rmessage::Fsync => Ok(()), - Rmessage::Lock(ref lock) => lock.encode(writer), - Rmessage::GetLock(ref getlock) => getlock.encode(writer), - Rmessage::Link => Ok(()), - Rmessage::Mkdir(ref mkdir) => mkdir.encode(writer), - Rmessage::RenameAt => Ok(()), - Rmessage::UnlinkAt => Ok(()), - Rmessage::Lerror(ref lerror) => lerror.encode(writer), - } - } - - fn decode<R: Read>(reader: &mut R) -> io::Result<Self> { - let byte_size: u32 = WireFormat::decode(reader)?; - - // byte_size includes the size of byte_size so remove that from the - // expected length of the message. - let reader = &mut reader.take((byte_size - mem::size_of::<u32>() as u32) as u64); - - let mut ty = [0u8]; - reader.read_exact(&mut ty)?; - - let tag: u16 = WireFormat::decode(reader)?; - - let msg = match ty[0] { - RVERSION => Ok(Rmessage::Version(WireFormat::decode(reader)?)), - RFLUSH => Ok(Rmessage::Flush), - RWALK => Ok(Rmessage::Walk(WireFormat::decode(reader)?)), - RREAD => Ok(Rmessage::Read(WireFormat::decode(reader)?)), - RWRITE => Ok(Rmessage::Write(WireFormat::decode(reader)?)), - RCLUNK => Ok(Rmessage::Clunk), - RREMOVE => Ok(Rmessage::Remove), - RATTACH => Ok(Rmessage::Attach(WireFormat::decode(reader)?)), - RAUTH => Ok(Rmessage::Auth(WireFormat::decode(reader)?)), - RSTATFS => Ok(Rmessage::Statfs(WireFormat::decode(reader)?)), - RLOPEN => Ok(Rmessage::Lopen(WireFormat::decode(reader)?)), - RLCREATE => Ok(Rmessage::Lcreate(WireFormat::decode(reader)?)), - RSYMLINK => Ok(Rmessage::Symlink(WireFormat::decode(reader)?)), - RMKNOD => Ok(Rmessage::Mknod(WireFormat::decode(reader)?)), - RRENAME => Ok(Rmessage::Rename), - RREADLINK => Ok(Rmessage::Readlink(WireFormat::decode(reader)?)), - RGETATTR => Ok(Rmessage::GetAttr(WireFormat::decode(reader)?)), - RSETATTR => Ok(Rmessage::SetAttr), - RXATTRWALK => Ok(Rmessage::XattrWalk(WireFormat::decode(reader)?)), - RXATTRCREATE => Ok(Rmessage::XattrCreate), - RREADDIR => Ok(Rmessage::Readdir(WireFormat::decode(reader)?)), - RFSYNC => Ok(Rmessage::Fsync), - RLOCK => Ok(Rmessage::Lock(WireFormat::decode(reader)?)), - RGETLOCK => Ok(Rmessage::GetLock(WireFormat::decode(reader)?)), - RLINK => Ok(Rmessage::Link), - RMKDIR => Ok(Rmessage::Mkdir(WireFormat::decode(reader)?)), - RRENAMEAT => Ok(Rmessage::RenameAt), - RUNLINKAT => Ok(Rmessage::UnlinkAt), - RLERROR => Ok(Rmessage::Lerror(WireFormat::decode(reader)?)), - err => Err(io::Error::new( - ErrorKind::InvalidData, - format!("unknown message type {}", err), - )), - }?; - - Ok(Rframe { tag, msg }) - } -} - -#[derive(Debug, Copy, Clone, P9WireFormat)] -pub struct Qid { - pub ty: u8, - pub version: u32, - pub path: u64, -} - -#[derive(Debug, P9WireFormat)] -pub struct Dirent { - pub qid: Qid, - pub offset: u64, - pub ty: u8, - pub name: String, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rversion { - pub msize: u32, - pub version: String, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rwalk { - pub wqids: Vec<Qid>, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rread { - pub data: Data, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rwrite { - pub count: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rauth { - pub aqid: Qid, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rattach { - pub qid: Qid, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rlerror { - pub ecode: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rstatfs { - pub ty: u32, - pub bsize: u32, - pub blocks: u64, - pub bfree: u64, - pub bavail: u64, - pub files: u64, - pub ffree: u64, - pub fsid: u64, - pub namelen: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rlopen { - pub qid: Qid, - pub iounit: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rlcreate { - pub qid: Qid, - pub iounit: u32, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rsymlink { - pub qid: Qid, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rmknod { - pub qid: Qid, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rreadlink { - pub target: String, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rgetattr { - pub valid: u64, - pub qid: Qid, - pub mode: u32, - pub uid: u32, - pub gid: u32, - pub nlink: u64, - pub rdev: u64, - pub size: u64, - pub blksize: u64, - pub blocks: u64, - pub atime_sec: u64, - pub atime_nsec: u64, - pub mtime_sec: u64, - pub mtime_nsec: u64, - pub ctime_sec: u64, - pub ctime_nsec: u64, - pub btime_sec: u64, - pub btime_nsec: u64, - pub gen: u64, - pub data_version: u64, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rxattrwalk { - pub size: u64, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rreaddir { - pub data: Data, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rlock { - pub status: u8, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rgetlock { - pub ty: u8, - pub start: u64, - pub length: u64, - pub proc_id: u32, - pub client_id: String, -} - -#[derive(Debug, P9WireFormat)] -pub struct Rmkdir { - pub qid: Qid, -} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs deleted file mode 100644 index 9c278ee..0000000 --- a/src/protocol/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2018 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -mod messages; -mod wire_format; - -pub use self::messages::*; -pub use self::wire_format::{Data, WireFormat}; diff --git a/src/protocol/wire_format.rs b/src/protocol/wire_format.rs deleted file mode 100644 index 7120937..0000000 --- a/src/protocol/wire_format.rs +++ /dev/null @@ -1,704 +0,0 @@ -// Copyright 2018 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::fmt; -use std::io; -use std::io::{ErrorKind, Read, Write}; -use std::mem; -use std::ops::{Deref, DerefMut}; -use std::string::String; -use std::vec::Vec; - -/// A type that can be encoded on the wire using the 9P protocol. -pub trait WireFormat: std::marker::Sized { - /// Returns the number of bytes necessary to fully encode `self`. - fn byte_size(&self) -> u32; - - /// Encodes `self` into `writer`. - fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()>; - - /// Decodes `Self` from `reader`. - fn decode<R: Read>(reader: &mut R) -> io::Result<Self>; -} - -// This doesn't really _need_ to be a macro but unfortunately there is no trait bound to -// express "can be casted to another type", which means we can't write `T as u8` in a trait -// based implementation. So instead we have this macro, which is implemented for all the -// stable unsigned types with the added benefit of not being implemented for the signed -// types which are not allowed by the protocol. -macro_rules! uint_wire_format_impl { - ($Ty:ty) => { - impl WireFormat for $Ty { - fn byte_size(&self) -> u32 { - mem::size_of::<$Ty>() as u32 - } - - fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> { - let mut buf = [0u8; mem::size_of::<$Ty>()]; - - // Encode the bytes into the buffer in little endian order. - for idx in 0..mem::size_of::<$Ty>() { - buf[idx] = (self >> (8 * idx)) as u8; - } - - writer.write_all(&buf) - } - - fn decode<R: Read>(reader: &mut R) -> io::Result<Self> { - let mut buf = [0u8; mem::size_of::<$Ty>()]; - reader.read_exact(&mut buf)?; - - // Read bytes from the buffer in little endian order. - let mut result = 0; - for idx in 0..mem::size_of::<$Ty>() { - result |= (buf[idx] as $Ty) << (8 * idx); - } - - Ok(result) - } - } - }; -} -uint_wire_format_impl!(u8); -uint_wire_format_impl!(u16); -uint_wire_format_impl!(u32); -uint_wire_format_impl!(u64); - -// The 9P protocol requires that strings are UTF-8 encoded. The wire format is a u16 -// count |N|, encoded in little endian, followed by |N| bytes of UTF-8 data. -impl WireFormat for String { - fn byte_size(&self) -> u32 { - (mem::size_of::<u16>() + self.len()) as u32 - } - - fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> { - if self.len() > std::u16::MAX as usize { - return Err(io::Error::new( - ErrorKind::InvalidInput, - "string is too long", - )); - } - - (self.len() as u16).encode(writer)?; - writer.write_all(self.as_bytes()) - } - - fn decode<R: Read>(reader: &mut R) -> io::Result<Self> { - let len: u16 = WireFormat::decode(reader)?; - let mut result = String::with_capacity(len as usize); - reader.take(len as u64).read_to_string(&mut result)?; - Ok(result) - } -} - -// The wire format for repeated types is similar to that of strings: a little endian -// encoded u16 |N|, followed by |N| instances of the given type. -impl<T: WireFormat> WireFormat for Vec<T> { - fn byte_size(&self) -> u32 { - mem::size_of::<u16>() as u32 + self.iter().map(|elem| elem.byte_size()).sum::<u32>() - } - - fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> { - if self.len() > std::u16::MAX as usize { - return Err(io::Error::new( - ErrorKind::InvalidInput, - "too many elements in vector", - )); - } - - (self.len() as u16).encode(writer)?; - for elem in self { - elem.encode(writer)?; - } - - Ok(()) - } - - fn decode<R: Read>(reader: &mut R) -> io::Result<Self> { - let len: u16 = WireFormat::decode(reader)?; - let mut result = Vec::with_capacity(len as usize); - - for _ in 0..len { - result.push(WireFormat::decode(reader)?); - } - - Ok(result) - } -} - -/// A type that encodes an arbitrary number of bytes of data. Typically used for Rread -/// Twrite messages. This differs from a `Vec<u8>` in that it encodes the number of bytes -/// using a `u32` instead of a `u16`. -#[derive(PartialEq)] -pub struct Data(pub Vec<u8>); - -// The maximum length of a data buffer that we support. In practice the server's max message -// size should prevent us from reading too much data so this check is mainly to ensure a -// malicious client cannot trick us into allocating massive amounts of memory. -const MAX_DATA_LENGTH: u32 = 32 * 1024 * 1024; - -impl fmt::Debug for Data { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // There may be a lot of data and we don't want to spew it all out in a trace. Instead - // just print out the number of bytes in the buffer. - write!(f, "Data({} bytes)", self.len()) - } -} - -// Implement Deref and DerefMut so that we don't have to use self.0 everywhere. -impl Deref for Data { - type Target = Vec<u8>; - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl DerefMut for Data { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -// Same as Vec<u8> except that it encodes the length as a u32 instead of a u16. -impl WireFormat for Data { - fn byte_size(&self) -> u32 { - mem::size_of::<u32>() as u32 + self.iter().map(|elem| elem.byte_size()).sum::<u32>() - } - - fn encode<W: Write>(&self, writer: &mut W) -> io::Result<()> { - if self.len() > std::u32::MAX as usize { - return Err(io::Error::new(ErrorKind::InvalidInput, "data is too large")); - } - (self.len() as u32).encode(writer)?; - writer.write_all(self) - } - - fn decode<R: Read>(reader: &mut R) -> io::Result<Self> { - let len: u32 = WireFormat::decode(reader)?; - if len > MAX_DATA_LENGTH { - return Err(io::Error::new( - ErrorKind::InvalidData, - format!("data length ({} bytes) is too large", len), - )); - } - - let mut buf = Vec::with_capacity(len as usize); - reader.take(len as u64).read_to_end(&mut buf)?; - - if buf.len() == len as usize { - Ok(Data(buf)) - } else { - Err(io::Error::new( - ErrorKind::UnexpectedEof, - format!( - "unexpected end of data: want: {} bytes, got: {} bytes", - len, - buf.len() - ), - )) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use std::io::Cursor; - use std::mem; - use std::string::String; - - #[test] - fn integer_byte_size() { - assert_eq!(1, 0u8.byte_size()); - assert_eq!(2, 0u16.byte_size()); - assert_eq!(4, 0u32.byte_size()); - assert_eq!(8, 0u64.byte_size()); - } - - #[test] - fn integer_decode() { - let buf: [u8; 8] = [0xef, 0xbe, 0xad, 0xde, 0x0d, 0xf0, 0xad, 0x8b]; - - assert_eq!( - 0xef as u8, - WireFormat::decode(&mut Cursor::new(&buf)).unwrap() - ); - assert_eq!(0xbeef as u16, u16::decode(&mut Cursor::new(&buf)).unwrap()); - assert_eq!( - 0xdeadbeef as u32, - u32::decode(&mut Cursor::new(&buf)).unwrap() - ); - assert_eq!( - 0x8badf00d_deadbeef as u64, - u64::decode(&mut Cursor::new(&buf)).unwrap() - ); - } - - #[test] - fn integer_encode() { - let value: u64 = 0x8badf00d_deadbeef; - let expected: [u8; 8] = [0xef, 0xbe, 0xad, 0xde, 0x0d, 0xf0, 0xad, 0x8b]; - - let mut buf = vec![0; 8]; - - (value as u8).encode(&mut Cursor::new(&mut *buf)).unwrap(); - assert_eq!(expected[0..1], buf[0..1]); - - (value as u16).encode(&mut Cursor::new(&mut *buf)).unwrap(); - assert_eq!(expected[0..2], buf[0..2]); - - (value as u32).encode(&mut Cursor::new(&mut *buf)).unwrap(); - assert_eq!(expected[0..4], buf[0..4]); - - value.encode(&mut Cursor::new(&mut *buf)).unwrap(); - assert_eq!(expected[0..8], buf[0..8]); - } - - #[test] - fn string_byte_size() { - let values = [ - String::from("Google Video"), - String::from("网页 图片 资讯更多 »"), - String::from("Παγκόσμιος Ιστός"), - String::from("Поиск страниц на русском"), - String::from("전체서비스"), - ]; - - let exp = values - .iter() - .map(|v| (mem::size_of::<u16>() + v.len()) as u32); - - for (value, expected) in values.iter().zip(exp) { - assert_eq!(expected, value.byte_size()); - } - } - - #[test] - fn zero_length_string() { - let s = String::from(""); - assert_eq!(s.byte_size(), mem::size_of::<u16>() as u32); - - let mut buf = [0xffu8; 4]; - - s.encode(&mut Cursor::new(&mut buf[..])) - .expect("failed to encode empty string"); - assert_eq!(&[0, 0, 0xff, 0xff], &buf); - - assert_eq!( - s, - <String as WireFormat>::decode(&mut Cursor::new(&[0, 0, 0x61, 0x61][..])) - .expect("failed to decode empty string") - ); - } - - #[test] - fn string_encode() { - let values = [ - String::from("Google Video"), - String::from("网页 图片 资讯更多 »"), - String::from("Παγκόσμιος Ιστός"), - String::from("Поиск страниц на русском"), - String::from("전체서비스"), - ]; - - let expected = values.iter().map(|v| { - let len = v.as_bytes().len(); - let mut buf = Vec::with_capacity(len + mem::size_of::<u16>()); - - buf.push(len as u8); - buf.push((len >> 8) as u8); - - buf.extend_from_slice(v.as_bytes()); - - buf - }); - - for (val, exp) in values.iter().zip(expected) { - let mut buf = vec![0; exp.len()]; - - WireFormat::encode(val, &mut Cursor::new(&mut *buf)).unwrap(); - assert_eq!(exp, buf); - } - } - - #[test] - fn string_decode() { - assert_eq!( - String::from("Google Video"), - <String as WireFormat>::decode(&mut Cursor::new( - &[ - 0x0c, 0x00, 0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x20, 0x56, 0x69, 0x64, 0x65, - 0x6F, - ][..] - )) - .unwrap() - ); - assert_eq!( - String::from("网页 图片 资讯更多 »"), - <String as WireFormat>::decode(&mut Cursor::new( - &[ - 0x1d, 0x00, 0xE7, 0xBD, 0x91, 0xE9, 0xA1, 0xB5, 0x20, 0xE5, 0x9B, 0xBE, 0xE7, - 0x89, 0x87, 0x20, 0xE8, 0xB5, 0x84, 0xE8, 0xAE, 0xAF, 0xE6, 0x9B, 0xB4, 0xE5, - 0xA4, 0x9A, 0x20, 0xC2, 0xBB, - ][..] - )) - .unwrap() - ); - assert_eq!( - String::from("Παγκόσμιος Ιστός"), - <String as WireFormat>::decode(&mut Cursor::new( - &[ - 0x1f, 0x00, 0xCE, 0xA0, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xBA, 0xCF, 0x8C, 0xCF, - 0x83, 0xCE, 0xBC, 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x82, 0x20, 0xCE, 0x99, 0xCF, - 0x83, 0xCF, 0x84, 0xCF, 0x8C, 0xCF, 0x82, - ][..] - )) - .unwrap() - ); - assert_eq!( - String::from("Поиск страниц на русском"), - <String as WireFormat>::decode(&mut Cursor::new( - &[ - 0x2d, 0x00, 0xD0, 0x9F, 0xD0, 0xBE, 0xD0, 0xB8, 0xD1, 0x81, 0xD0, 0xBA, 0x20, - 0xD1, 0x81, 0xD1, 0x82, 0xD1, 0x80, 0xD0, 0xB0, 0xD0, 0xBD, 0xD0, 0xB8, 0xD1, - 0x86, 0x20, 0xD0, 0xBD, 0xD0, 0xB0, 0x20, 0xD1, 0x80, 0xD1, 0x83, 0xD1, 0x81, - 0xD1, 0x81, 0xD0, 0xBA, 0xD0, 0xBE, 0xD0, 0xBC, - ][..] - )) - .unwrap() - ); - assert_eq!( - String::from("전체서비스"), - <String as WireFormat>::decode(&mut Cursor::new( - &[ - 0x0f, 0x00, 0xEC, 0xA0, 0x84, 0xEC, 0xB2, 0xB4, 0xEC, 0x84, 0x9C, 0xEB, 0xB9, - 0x84, 0xEC, 0x8A, 0xA4, - ][..] - )) - .unwrap() - ); - } - - #[test] - fn invalid_string_decode() { - let _ = <String as WireFormat>::decode(&mut Cursor::new(&[ - 0x06, 0x00, 0xed, 0xa0, 0x80, 0xed, 0xbf, 0xbf, - ])) - .expect_err("surrogate code point"); - - let _ = <String as WireFormat>::decode(&mut Cursor::new(&[ - 0x05, 0x00, 0xf8, 0x80, 0x80, 0x80, 0xbf, - ])) - .expect_err("overlong sequence"); - - let _ = - <String as WireFormat>::decode(&mut Cursor::new(&[0x04, 0x00, 0xf4, 0x90, 0x80, 0x80])) - .expect_err("out of range"); - - let _ = - <String as WireFormat>::decode(&mut Cursor::new(&[0x04, 0x00, 0x63, 0x61, 0x66, 0xe9])) - .expect_err("ISO-8859-1"); - - let _ = - <String as WireFormat>::decode(&mut Cursor::new(&[0x04, 0x00, 0xb0, 0xa1, 0xb0, 0xa2])) - .expect_err("EUC-KR"); - } - - #[test] - fn vector_encode() { - let values: Vec<u32> = vec![291, 18_916, 2_497, 22, 797_162, 2_119_732, 3_213_929_716]; - let mut expected: Vec<u8> = - Vec::with_capacity(values.len() * mem::size_of::<u32>() + mem::size_of::<u16>()); - expected.push(values.len() as u8); - expected.push((values.len() >> 8) as u8); - - const MASK: u32 = 0xff; - for val in &values { - expected.push((val & MASK) as u8); - expected.push(((val >> 8) & MASK) as u8); - expected.push(((val >> 16) & MASK) as u8); - expected.push(((val >> 24) & MASK) as u8); - } - - let mut actual: Vec<u8> = vec![0; expected.len()]; - - WireFormat::encode(&values, &mut Cursor::new(&mut *actual)) - .expect("failed to encode vector"); - assert_eq!(expected, actual); - } - - #[test] - fn vector_decode() { - let expected: Vec<u32> = vec![ - 2_498, - 24, - 897, - 4_097_789_579, - 8_498_119, - 684_279, - 961_189_198, - 7, - ]; - let mut input: Vec<u8> = - Vec::with_capacity(expected.len() * mem::size_of::<u32>() + mem::size_of::<u16>()); - input.push(expected.len() as u8); - input.push((expected.len() >> 8) as u8); - - const MASK: u32 = 0xff; - for val in &expected { - input.push((val & MASK) as u8); - input.push(((val >> 8) & MASK) as u8); - input.push(((val >> 16) & MASK) as u8); - input.push(((val >> 24) & MASK) as u8); - } - - assert_eq!( - expected, - <Vec<u32> as WireFormat>::decode(&mut Cursor::new(&*input)) - .expect("failed to decode vector") - ); - } - - #[test] - fn data_encode() { - let values = Data(vec![169, 155, 79, 67, 182, 199, 25, 73, 129, 200]); - let mut expected: Vec<u8> = - Vec::with_capacity(values.len() * mem::size_of::<u8>() + mem::size_of::<u32>()); - expected.push(values.len() as u8); - expected.push((values.len() >> 8) as u8); - expected.push((values.len() >> 16) as u8); - expected.push((values.len() >> 24) as u8); - expected.extend_from_slice(&values); - - let mut actual: Vec<u8> = vec![0; expected.len()]; - - WireFormat::encode(&values, &mut Cursor::new(&mut *actual)) - .expect("failed to encode datar"); - assert_eq!(expected, actual); - } - - #[test] - fn data_decode() { - let expected = Data(vec![219, 15, 8, 155, 194, 129, 79, 91, 46, 53, 173]); - let mut input: Vec<u8> = - Vec::with_capacity(expected.len() * mem::size_of::<u8>() + mem::size_of::<u32>()); - input.push(expected.len() as u8); - input.push((expected.len() >> 8) as u8); - input.push((expected.len() >> 16) as u8); - input.push((expected.len() >> 24) as u8); - input.extend_from_slice(&expected); - - assert_eq!( - expected, - <Data as WireFormat>::decode(&mut Cursor::new(&mut *input)) - .expect("failed to decode data") - ); - } - - #[test] - fn error_cases() { - // string is too long. - let mut long_str = String::with_capacity(std::u16::MAX as usize); - while long_str.len() < std::u16::MAX as usize { - long_str.push_str("long"); - } - long_str.push_str("!"); - - let count = long_str.len() + mem::size_of::<u16>(); - let mut buf = vec![0; count]; - - long_str - .encode(&mut Cursor::new(&mut *buf)) - .expect_err("long string"); - - // vector is too long. - let mut long_vec: Vec<u32> = Vec::with_capacity(std::u16::MAX as usize); - while long_vec.len() < std::u16::MAX as usize { - long_vec.push(0x8bad_f00d); - } - long_vec.push(0x00ba_b10c); - - let count = long_vec.len() * mem::size_of::<u32>(); - let mut buf = vec![0; count]; - - WireFormat::encode(&long_vec, &mut Cursor::new(&mut *buf)).expect_err("long vector"); - } - - #[derive(Debug, PartialEq, P9WireFormat)] - struct Item { - a: u64, - b: String, - c: Vec<u16>, - buf: Data, - } - - #[test] - fn struct_encode() { - let item = Item { - a: 0xdead10cc_00bab10c, - b: String::from("冻住,不许走!"), - c: vec![359, 492, 8891], - buf: Data(vec![254, 129, 0, 62, 49, 172]), - }; - - let mut expected: Vec<u8> = vec![0x0c, 0xb1, 0xba, 0x00, 0xcc, 0x10, 0xad, 0xde]; - let strlen = item.b.len() as u16; - expected.push(strlen as u8); - expected.push((strlen >> 8) as u8); - expected.extend_from_slice(item.b.as_bytes()); - - let veclen = item.c.len() as u16; - expected.push(veclen as u8); - expected.push((veclen >> 8) as u8); - for val in &item.c { - expected.push(*val as u8); - expected.push((val >> 8) as u8); - } - - let buflen = item.buf.len() as u32; - expected.push(buflen as u8); - expected.push((buflen >> 8) as u8); - expected.push((buflen >> 16) as u8); - expected.push((buflen >> 24) as u8); - expected.extend_from_slice(&item.buf); - - let mut actual = vec![0; expected.len()]; - - WireFormat::encode(&item, &mut Cursor::new(&mut *actual)).expect("failed to encode item"); - - assert_eq!(expected, actual); - } - - #[test] - fn struct_decode() { - let expected = Item { - a: 0xface_b00c_0404_4b1d, - b: String::from("Огонь по готовности!"), - c: vec![20067, 32449, 549, 4972, 77, 1987], - buf: Data(vec![126, 236, 79, 59, 6, 159]), - }; - - let mut input: Vec<u8> = vec![0x1d, 0x4b, 0x04, 0x04, 0x0c, 0xb0, 0xce, 0xfa]; - let strlen = expected.b.len() as u16; - input.push(strlen as u8); - input.push((strlen >> 8) as u8); - input.extend_from_slice(expected.b.as_bytes()); - - let veclen = expected.c.len() as u16; - input.push(veclen as u8); - input.push((veclen >> 8) as u8); - for val in &expected.c { - input.push(*val as u8); - input.push((val >> 8) as u8); - } - - let buflen = expected.buf.len() as u32; - input.push(buflen as u8); - input.push((buflen >> 8) as u8); - input.push((buflen >> 16) as u8); - input.push((buflen >> 24) as u8); - input.extend_from_slice(&expected.buf); - - let actual: Item = - WireFormat::decode(&mut Cursor::new(input)).expect("failed to decode item"); - - assert_eq!(expected, actual); - } - - #[derive(Debug, PartialEq, P9WireFormat)] - struct Nested { - item: Item, - val: Vec<u64>, - } - - fn build_encoded_buffer(value: &Nested) -> Vec<u8> { - let mut result: Vec<u8> = Vec::new(); - - // encode a - result.push(value.item.a as u8); - result.push((value.item.a >> 8) as u8); - result.push((value.item.a >> 16) as u8); - result.push((value.item.a >> 24) as u8); - result.push((value.item.a >> 32) as u8); - result.push((value.item.a >> 40) as u8); - result.push((value.item.a >> 48) as u8); - result.push((value.item.a >> 56) as u8); - - // encode b - result.push(value.item.b.len() as u8); - result.push((value.item.b.len() >> 8) as u8); - result.extend_from_slice(value.item.b.as_bytes()); - - // encode c - result.push(value.item.c.len() as u8); - result.push((value.item.c.len() >> 8) as u8); - for val in &value.item.c { - result.push((val & 0xffu16) as u8); - result.push(((val >> 8) & 0xffu16) as u8); - } - - // encode buf - result.push(value.item.buf.len() as u8); - result.push((value.item.buf.len() >> 8) as u8); - result.push((value.item.buf.len() >> 16) as u8); - result.push((value.item.buf.len() >> 24) as u8); - result.extend_from_slice(&value.item.buf); - - // encode val - result.push(value.val.len() as u8); - result.push((value.val.len() >> 8) as u8); - for val in &value.val { - result.push(*val as u8); - result.push((val >> 8) as u8); - result.push((val >> 16) as u8); - result.push((val >> 24) as u8); - result.push((val >> 32) as u8); - result.push((val >> 40) as u8); - result.push((val >> 48) as u8); - result.push((val >> 56) as u8); - } - - result - } - - #[test] - fn nested_encode() { - let value = Nested { - item: Item { - a: 0xcafe_d00d_8bad_f00d, - b: String::from("龍が我が敵を喰らう!"), - c: vec![2679, 55_919, 44, 38_819, 792], - buf: Data(vec![129, 55, 200, 93, 7, 68]), - }, - val: vec![1954978, 59, 4519, 15679], - }; - - let expected = build_encoded_buffer(&value); - - let mut actual = vec![0; expected.len()]; - - WireFormat::encode(&value, &mut Cursor::new(&mut *actual)).expect("failed to encode value"); - assert_eq!(expected, actual); - } - - #[test] - fn nested_decode() { - let expected = Nested { - item: Item { - a: 0x0ff1ce, - b: String::from("龍神の剣を喰らえ!"), - c: vec![21687, 159, 55, 9217, 192], - buf: Data(vec![189, 22, 7, 59, 235]), - }, - val: vec![15679, 8619196, 319746, 123957, 77, 0, 492], - }; - - let input = build_encoded_buffer(&expected); - - assert_eq!( - expected, - <Nested as WireFormat>::decode(&mut Cursor::new(&*input)) - .expect("failed to decode value") - ); - } -} diff --git a/src/server/mod.rs b/src/server/mod.rs deleted file mode 100644 index 880a998..0000000 --- a/src/server/mod.rs +++ /dev/null @@ -1,977 +0,0 @@ -// Copyright 2018 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::cmp::min; -use std::collections::{btree_map, BTreeMap}; -use std::ffi::{CStr, CString}; -use std::fs::File; -use std::io::{self, Cursor, Read, Write}; -use std::mem::{self, MaybeUninit}; -use std::ops::Deref; -use std::os::unix::ffi::OsStrExt; -use std::os::unix::fs::FileExt; -use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; -use std::path::Path; - -use libchromeos::{read_dir, syscall}; - -use crate::protocol::*; - -// Tlopen and Tlcreate flags. Taken from "include/net/9p/9p.h" in the linux tree. -const P9_RDONLY: u32 = 0o00000000; -const P9_WRONLY: u32 = 0o00000001; -const P9_RDWR: u32 = 0o00000002; -const P9_NOACCESS: u32 = 0o00000003; -const P9_CREATE: u32 = 0o00000100; -const P9_EXCL: u32 = 0o00000200; -const P9_NOCTTY: u32 = 0o00000400; -const P9_TRUNC: u32 = 0o00001000; -const P9_APPEND: u32 = 0o00002000; -const P9_NONBLOCK: u32 = 0o00004000; -const P9_DSYNC: u32 = 0o00010000; -const P9_FASYNC: u32 = 0o00020000; -const P9_DIRECT: u32 = 0o00040000; -const P9_LARGEFILE: u32 = 0o00100000; -const P9_DIRECTORY: u32 = 0o00200000; -const P9_NOFOLLOW: u32 = 0o00400000; -const P9_NOATIME: u32 = 0o01000000; -const _P9_CLOEXEC: u32 = 0o02000000; -const P9_SYNC: u32 = 0o04000000; - -// Mapping from 9P flags to libc flags. -const MAPPED_FLAGS: [(u32, i32); 16] = [ - (P9_WRONLY, libc::O_WRONLY), - (P9_RDWR, libc::O_RDWR), - (P9_CREATE, libc::O_CREAT), - (P9_EXCL, libc::O_EXCL), - (P9_NOCTTY, libc::O_NOCTTY), - (P9_TRUNC, libc::O_TRUNC), - (P9_APPEND, libc::O_APPEND), - (P9_NONBLOCK, libc::O_NONBLOCK), - (P9_DSYNC, libc::O_DSYNC), - (P9_FASYNC, 0), // Unsupported - (P9_DIRECT, libc::O_DIRECT), - (P9_LARGEFILE, libc::O_LARGEFILE), - (P9_DIRECTORY, libc::O_DIRECTORY), - (P9_NOFOLLOW, libc::O_NOFOLLOW), - (P9_NOATIME, libc::O_NOATIME), - (P9_SYNC, libc::O_SYNC), -]; - -// 9P Qid types. Taken from "include/net/9p/9p.h" in the linux tree. -const P9_QTDIR: u8 = 0x80; -const _P9_QTAPPEND: u8 = 0x40; -const _P9_QTEXCL: u8 = 0x20; -const _P9_QTMOUNT: u8 = 0x10; -const _P9_QTAUTH: u8 = 0x08; -const _P9_QTTMP: u8 = 0x04; -const P9_QTSYMLINK: u8 = 0x02; -const _P9_QTLINK: u8 = 0x01; -const P9_QTFILE: u8 = 0x00; - -// Bitmask values for the getattr request. -const _P9_GETATTR_MODE: u64 = 0x00000001; -const _P9_GETATTR_NLINK: u64 = 0x00000002; -const _P9_GETATTR_UID: u64 = 0x00000004; -const _P9_GETATTR_GID: u64 = 0x00000008; -const _P9_GETATTR_RDEV: u64 = 0x00000010; -const _P9_GETATTR_ATIME: u64 = 0x00000020; -const _P9_GETATTR_MTIME: u64 = 0x00000040; -const _P9_GETATTR_CTIME: u64 = 0x00000080; -const _P9_GETATTR_INO: u64 = 0x00000100; -const _P9_GETATTR_SIZE: u64 = 0x00000200; -const _P9_GETATTR_BLOCKS: u64 = 0x00000400; - -const _P9_GETATTR_BTIME: u64 = 0x00000800; -const _P9_GETATTR_GEN: u64 = 0x00001000; -const _P9_GETATTR_DATA_VERSION: u64 = 0x00002000; - -const P9_GETATTR_BASIC: u64 = 0x000007ff; /* Mask for fields up to BLOCKS */ -const _P9_GETATTR_ALL: u64 = 0x00003fff; /* Mask for All fields above */ - -// Bitmask values for the setattr request. -const P9_SETATTR_MODE: u32 = 0x00000001; -const P9_SETATTR_UID: u32 = 0x00000002; -const P9_SETATTR_GID: u32 = 0x00000004; -const P9_SETATTR_SIZE: u32 = 0x00000008; -const P9_SETATTR_ATIME: u32 = 0x00000010; -const P9_SETATTR_MTIME: u32 = 0x00000020; -const P9_SETATTR_CTIME: u32 = 0x00000040; -const P9_SETATTR_ATIME_SET: u32 = 0x00000080; -const P9_SETATTR_MTIME_SET: u32 = 0x00000100; - -// Minimum and maximum message size that we'll expect from the client. -const MIN_MESSAGE_SIZE: u32 = 256; -const MAX_MESSAGE_SIZE: u32 = ::std::u16::MAX as u32; - -#[derive(PartialEq, Eq)] -enum FileType { - Regular, - Directory, - Other, -} - -impl From<libc::mode_t> for FileType { - fn from(mode: libc::mode_t) -> Self { - match mode & libc::S_IFMT { - libc::S_IFREG => FileType::Regular, - libc::S_IFDIR => FileType::Directory, - _ => FileType::Other, - } - } -} - -// Represents state that the server is holding on behalf of a client. Fids are somewhat like file -// descriptors but are not restricted to open files and directories. Fids are identified by a unique -// 32-bit number chosen by the client. Most messages sent by clients include a fid on which to -// operate. The fid in a Tattach message represents the root of the file system tree that the client -// is allowed to access. A client can create more fids by walking the directory tree from that fid. -struct Fid { - path: File, - file: Option<File>, - filetype: FileType, -} - -impl From<libc::stat64> for Qid { - fn from(st: libc::stat64) -> Qid { - let ty = match st.st_mode & libc::S_IFMT { - libc::S_IFDIR => P9_QTDIR, - libc::S_IFREG => P9_QTFILE, - libc::S_IFLNK => P9_QTSYMLINK, - _ => 0, - }; - - Qid { - ty, - // TODO: deal with the 2038 problem before 2038 - version: st.st_mtime as u32, - path: st.st_ino, - } - } -} - -fn statat(d: &File, name: &CStr, flags: libc::c_int) -> io::Result<libc::stat64> { - let mut st = MaybeUninit::<libc::stat64>::zeroed(); - - // Safe because the kernel will only write data in `st` and we check the return - // value. - let res = unsafe { - libc::fstatat64( - d.as_raw_fd(), - name.as_ptr(), - st.as_mut_ptr(), - flags | libc::AT_SYMLINK_NOFOLLOW, - ) - }; - if res >= 0 { - // Safe because the kernel guarantees that the struct is now fully initialized. - Ok(unsafe { st.assume_init() }) - } else { - Err(io::Error::last_os_error()) - } -} - -fn stat(f: &File) -> io::Result<libc::stat64> { - // Safe because this is a constant value and a valid C string. - let pathname = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") }; - - statat(f, pathname, libc::AT_EMPTY_PATH) -} - -fn string_to_cstring(s: String) -> io::Result<CString> { - CString::new(s).map_err(|_| io::Error::from_raw_os_error(libc::EINVAL)) -} - -fn error_to_rmessage(err: io::Error) -> Rmessage { - let errno = if let Some(errno) = err.raw_os_error() { - errno - } else { - // Make a best-effort guess based on the kind. - match err.kind() { - io::ErrorKind::NotFound => libc::ENOENT, - io::ErrorKind::PermissionDenied => libc::EPERM, - io::ErrorKind::ConnectionRefused => libc::ECONNREFUSED, - io::ErrorKind::ConnectionReset => libc::ECONNRESET, - io::ErrorKind::ConnectionAborted => libc::ECONNABORTED, - io::ErrorKind::NotConnected => libc::ENOTCONN, - io::ErrorKind::AddrInUse => libc::EADDRINUSE, - io::ErrorKind::AddrNotAvailable => libc::EADDRNOTAVAIL, - io::ErrorKind::BrokenPipe => libc::EPIPE, - io::ErrorKind::AlreadyExists => libc::EEXIST, - io::ErrorKind::WouldBlock => libc::EWOULDBLOCK, - io::ErrorKind::InvalidInput => libc::EINVAL, - io::ErrorKind::InvalidData => libc::EINVAL, - io::ErrorKind::TimedOut => libc::ETIMEDOUT, - io::ErrorKind::WriteZero => libc::EIO, - io::ErrorKind::Interrupted => libc::EINTR, - io::ErrorKind::Other => libc::EIO, - io::ErrorKind::UnexpectedEof => libc::EIO, - _ => libc::EIO, - } - }; - - Rmessage::Lerror(Rlerror { - ecode: errno as u32, - }) -} - -// Sigh.. Cow requires the underlying type to implement Clone. -enum MaybeOwned<'b, T> { - Borrowed(&'b T), - Owned(T), -} - -impl<'a, T> Deref for MaybeOwned<'a, T> { - type Target = T; - fn deref(&self) -> &Self::Target { - use MaybeOwned::*; - match *self { - Borrowed(borrowed) => borrowed, - Owned(ref owned) => owned, - } - } -} - -fn ebadf() -> io::Error { - io::Error::from_raw_os_error(libc::EBADF) -} - -pub type ServerIdMap<T> = BTreeMap<T, T>; -pub type ServerUidMap = ServerIdMap<libc::uid_t>; -pub type ServerGidMap = ServerIdMap<libc::gid_t>; - -fn map_id_from_host<T: Clone + Ord>(map: &ServerIdMap<T>, id: T) -> T { - map.get(&id).map_or(id.clone(), |v| v.clone()) -} - -// Performs an ascii case insensitive lookup and returns an O_PATH fd for the entry, if found. -fn ascii_casefold_lookup(proc: &File, parent: &File, name: &[u8]) -> io::Result<File> { - let mut dir = open_fid(proc, &parent, P9_DIRECTORY)?; - let mut dirents = read_dir(&mut dir, 0)?; - - while let Some(entry) = dirents.next().transpose()? { - if name.eq_ignore_ascii_case(entry.name.to_bytes()) { - return lookup(parent, entry.name); - } - } - - Err(io::Error::from_raw_os_error(libc::ENOENT)) -} - -fn lookup(parent: &File, name: &CStr) -> io::Result<File> { - // Safe because this doesn't modify any memory and we check the return value. - let fd = syscall!(unsafe { - libc::openat( - parent.as_raw_fd(), - name.as_ptr(), - libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC, - ) - })?; - - // Safe because we just opened this fd. - Ok(unsafe { File::from_raw_fd(fd) }) -} - -fn do_walk( - proc: &File, - wnames: Vec<String>, - start: File, - ascii_casefold: bool, - mds: &mut Vec<libc::stat64>, -) -> io::Result<File> { - let mut current = start; - - for wname in wnames { - let name = string_to_cstring(wname)?; - current = lookup(¤t, &name).or_else(|e| { - if ascii_casefold { - if let Some(libc::ENOENT) = e.raw_os_error() { - return ascii_casefold_lookup(proc, ¤t, name.to_bytes()); - } - } - - Err(e) - })?; - mds.push(stat(¤t)?); - } - - Ok(current) -} - -fn open_fid(proc: &File, path: &File, p9_flags: u32) -> io::Result<File> { - let pathname = string_to_cstring(format!("self/fd/{}", path.as_raw_fd()))?; - - // We always open files with O_CLOEXEC. - let mut flags: i32 = libc::O_CLOEXEC; - for &(p9f, of) in &MAPPED_FLAGS { - if (p9_flags & p9f) != 0 { - flags |= of; - } - } - - if p9_flags & P9_NOACCESS == P9_RDONLY { - flags |= libc::O_RDONLY; - } - - // Safe because this doesn't modify any memory and we check the return value. We need to - // clear the O_NOFOLLOW flag because we want to follow the proc symlink. - let fd = syscall!(unsafe { - libc::openat( - proc.as_raw_fd(), - pathname.as_ptr(), - flags & !libc::O_NOFOLLOW, - ) - })?; - - // Safe because we just opened this fd and we know it is valid. - Ok(unsafe { File::from_raw_fd(fd) }) -} - -#[derive(Clone)] -pub struct Config { - pub root: Box<Path>, - pub msize: u32, - - pub uid_map: ServerUidMap, - pub gid_map: ServerGidMap, - - pub ascii_casefold: bool, -} - -impl Default for Config { - fn default() -> Config { - Config { - root: Path::new("/").into(), - msize: MAX_MESSAGE_SIZE, - uid_map: Default::default(), - gid_map: Default::default(), - ascii_casefold: false, - } - } -} -pub struct Server { - fids: BTreeMap<u32, Fid>, - proc: File, - cfg: Config, -} - -impl Server { - pub fn new<P: Into<Box<Path>>>( - root: P, - uid_map: ServerUidMap, - gid_map: ServerGidMap, - ) -> io::Result<Server> { - Server::with_config(Config { - root: root.into(), - msize: MAX_MESSAGE_SIZE, - uid_map, - gid_map, - ascii_casefold: false, - }) - } - - pub fn with_config(cfg: Config) -> io::Result<Server> { - // Safe because this is a valid c-string. - let proc_cstr = unsafe { CStr::from_bytes_with_nul_unchecked(b"/proc\0") }; - - // Safe because this doesn't modify any memory and we check the return value. - let fd = syscall!(unsafe { - libc::openat( - libc::AT_FDCWD, - proc_cstr.as_ptr(), - libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC, - ) - })?; - - // Safe because we just opened this fd and we know it is valid. - let proc = unsafe { File::from_raw_fd(fd) }; - Ok(Server { - fids: BTreeMap::new(), - proc, - cfg, - }) - } - - pub fn keep_fds(&self) -> Vec<RawFd> { - vec![self.proc.as_raw_fd()] - } - - pub fn handle_message<R: Read, W: Write>( - &mut self, - reader: &mut R, - writer: &mut W, - ) -> io::Result<()> { - let Tframe { tag, msg } = WireFormat::decode(&mut reader.take(self.cfg.msize as u64))?; - - let rmsg = match msg { - Tmessage::Version(ref version) => self.version(version).map(Rmessage::Version), - Tmessage::Flush(ref flush) => self.flush(flush).and(Ok(Rmessage::Flush)), - Tmessage::Walk(walk) => self.walk(walk).map(Rmessage::Walk), - Tmessage::Read(ref read) => self.read(read).map(Rmessage::Read), - Tmessage::Write(ref write) => self.write(write).map(Rmessage::Write), - Tmessage::Clunk(ref clunk) => self.clunk(clunk).and(Ok(Rmessage::Clunk)), - Tmessage::Remove(ref remove) => self.remove(remove).and(Ok(Rmessage::Remove)), - Tmessage::Attach(ref attach) => self.attach(attach).map(Rmessage::Attach), - Tmessage::Auth(ref auth) => self.auth(auth).map(Rmessage::Auth), - Tmessage::Statfs(ref statfs) => self.statfs(statfs).map(Rmessage::Statfs), - Tmessage::Lopen(ref lopen) => self.lopen(lopen).map(Rmessage::Lopen), - Tmessage::Lcreate(lcreate) => self.lcreate(lcreate).map(Rmessage::Lcreate), - Tmessage::Symlink(ref symlink) => self.symlink(symlink).map(Rmessage::Symlink), - Tmessage::Mknod(ref mknod) => self.mknod(mknod).map(Rmessage::Mknod), - Tmessage::Rename(ref rename) => self.rename(rename).and(Ok(Rmessage::Rename)), - Tmessage::Readlink(ref readlink) => self.readlink(readlink).map(Rmessage::Readlink), - Tmessage::GetAttr(ref get_attr) => self.get_attr(get_attr).map(Rmessage::GetAttr), - Tmessage::SetAttr(ref set_attr) => self.set_attr(set_attr).and(Ok(Rmessage::SetAttr)), - Tmessage::XattrWalk(ref xattr_walk) => { - self.xattr_walk(xattr_walk).map(Rmessage::XattrWalk) - } - Tmessage::XattrCreate(ref xattr_create) => self - .xattr_create(xattr_create) - .and(Ok(Rmessage::XattrCreate)), - Tmessage::Readdir(ref readdir) => self.readdir(readdir).map(Rmessage::Readdir), - Tmessage::Fsync(ref fsync) => self.fsync(fsync).and(Ok(Rmessage::Fsync)), - Tmessage::Lock(ref lock) => self.lock(lock).map(Rmessage::Lock), - Tmessage::GetLock(ref get_lock) => self.get_lock(get_lock).map(Rmessage::GetLock), - Tmessage::Link(link) => self.link(link).and(Ok(Rmessage::Link)), - Tmessage::Mkdir(mkdir) => self.mkdir(mkdir).map(Rmessage::Mkdir), - Tmessage::RenameAt(rename_at) => self.rename_at(rename_at).and(Ok(Rmessage::RenameAt)), - Tmessage::UnlinkAt(unlink_at) => self.unlink_at(unlink_at).and(Ok(Rmessage::UnlinkAt)), - }; - - // Errors while handling requests are never fatal. - let response = Rframe { - tag, - msg: rmsg.unwrap_or_else(error_to_rmessage), - }; - - response.encode(writer)?; - writer.flush() - } - - fn auth(&mut self, _auth: &Tauth) -> io::Result<Rauth> { - // Returning an error for the auth message means that the server does not require - // authentication. - Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) - } - - fn attach(&mut self, attach: &Tattach) -> io::Result<Rattach> { - // TODO: Check attach parameters - match self.fids.entry(attach.fid) { - btree_map::Entry::Vacant(entry) => { - let root = CString::new(self.cfg.root.as_os_str().as_bytes()) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - - // Safe because this doesn't modify any memory and we check the return value. - let fd = syscall!(unsafe { - libc::openat( - libc::AT_FDCWD, - root.as_ptr(), - libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC, - ) - })?; - - let root_path = unsafe { File::from_raw_fd(fd) }; - let st = stat(&root_path)?; - - let fid = Fid { - // Safe because we just opened this fd. - path: root_path, - file: None, - filetype: st.st_mode.into(), - }; - let response = Rattach { qid: st.into() }; - entry.insert(fid); - Ok(response) - } - btree_map::Entry::Occupied(_) => Err(io::Error::from_raw_os_error(libc::EBADF)), - } - } - - fn version(&mut self, version: &Tversion) -> io::Result<Rversion> { - if version.msize < MIN_MESSAGE_SIZE { - return Err(io::Error::from_raw_os_error(libc::EINVAL)); - } - - // A Tversion request clunks all open fids and terminates any pending I/O. - self.fids.clear(); - self.cfg.msize = min(self.cfg.msize, version.msize); - - Ok(Rversion { - msize: self.cfg.msize, - version: if version.version == "9P2000.L" { - String::from("9P2000.L") - } else { - String::from("unknown") - }, - }) - } - - fn flush(&mut self, _flush: &Tflush) -> io::Result<()> { - // TODO: Since everything is synchronous we can't actually flush requests. - Ok(()) - } - - fn walk(&mut self, walk: Twalk) -> io::Result<Rwalk> { - // `newfid` must not currently be in use unless it is the same as `fid`. - if walk.fid != walk.newfid && self.fids.contains_key(&walk.newfid) { - return Err(io::Error::from_raw_os_error(libc::EBADF)); - } - - // We need to walk the tree. First get the starting path. - let start = self - .fids - .get(&walk.fid) - .ok_or_else(ebadf) - .and_then(|fid| fid.path.try_clone())?; - - // Now walk the tree and break on the first error, if any. - let expected_len = walk.wnames.len(); - let mut mds = Vec::with_capacity(expected_len); - match do_walk( - &self.proc, - walk.wnames, - start, - self.cfg.ascii_casefold, - &mut mds, - ) { - Ok(end) => { - // Store the new fid if the full walk succeeded. - if mds.len() == expected_len { - let st = mds.last().copied().map(Ok).unwrap_or_else(|| stat(&end))?; - self.fids.insert( - walk.newfid, - Fid { - path: end, - file: None, - filetype: st.st_mode.into(), - }, - ); - } - } - Err(e) => { - // Only return an error if it occurred on the first component. - if mds.is_empty() { - return Err(e); - } - } - } - - Ok(Rwalk { - wqids: mds.into_iter().map(Qid::from).collect(), - }) - } - - fn read(&mut self, read: &Tread) -> io::Result<Rread> { - // Thankfully, `read` cannot be used to read directories in 9P2000.L. - let file = self - .fids - .get_mut(&read.fid) - .and_then(|fid| fid.file.as_mut()) - .ok_or_else(ebadf)?; - - // Use an empty Rread struct to figure out the overhead of the header. - let header_size = Rframe { - tag: 0, - msg: Rmessage::Read(Rread { - data: Data(Vec::new()), - }), - } - .byte_size(); - - let capacity = min(self.cfg.msize - header_size, read.count); - let mut buf = Data(vec![0u8; capacity as usize]); - - let count = file.read_at(&mut buf, read.offset)?; - buf.truncate(count); - - Ok(Rread { data: buf }) - } - - fn write(&mut self, write: &Twrite) -> io::Result<Rwrite> { - let file = self - .fids - .get_mut(&write.fid) - .and_then(|fid| fid.file.as_mut()) - .ok_or_else(ebadf)?; - - let count = file.write_at(&write.data, write.offset)?; - Ok(Rwrite { - count: count as u32, - }) - } - - fn clunk(&mut self, clunk: &Tclunk) -> io::Result<()> { - match self.fids.entry(clunk.fid) { - btree_map::Entry::Vacant(_) => Err(io::Error::from_raw_os_error(libc::EBADF)), - btree_map::Entry::Occupied(entry) => { - entry.remove(); - Ok(()) - } - } - } - - fn remove(&mut self, _remove: &Tremove) -> io::Result<()> { - // Since a file could be linked into multiple locations, there is no way to know exactly - // which path we are supposed to unlink. Linux uses unlink_at anyway, so we can just return - // an error here. - Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) - } - - fn statfs(&mut self, statfs: &Tstatfs) -> io::Result<Rstatfs> { - let fid = self.fids.get(&statfs.fid).ok_or_else(ebadf)?; - let mut buf = MaybeUninit::zeroed(); - - // Safe because this will only modify `out` and we check the return value. - syscall!(unsafe { libc::fstatfs64(fid.path.as_raw_fd(), buf.as_mut_ptr()) })?; - - // Safe because this only has integer types and any value is valid. - let out = unsafe { buf.assume_init() }; - Ok(Rstatfs { - ty: out.f_type as u32, - bsize: out.f_bsize as u32, - blocks: out.f_blocks, - bfree: out.f_bfree, - bavail: out.f_bavail, - files: out.f_files, - ffree: out.f_ffree, - // Safe because the fsid has only integer fields and the compiler will verify that is - // the same width as the `fsid` field in Rstatfs. - fsid: unsafe { mem::transmute(out.f_fsid) }, - namelen: out.f_namelen as u32, - }) - } - - fn lopen(&mut self, lopen: &Tlopen) -> io::Result<Rlopen> { - let fid = self.fids.get_mut(&lopen.fid).ok_or_else(ebadf)?; - - let file = open_fid(&self.proc, &fid.path, lopen.flags)?; - let st = stat(&file)?; - - fid.file = Some(file); - let iounit = st.st_blksize as u32; - Ok(Rlopen { - qid: st.into(), - iounit, - }) - } - - fn lcreate(&mut self, lcreate: Tlcreate) -> io::Result<Rlcreate> { - let fid = self.fids.get_mut(&lcreate.fid).ok_or_else(ebadf)?; - - if fid.filetype != FileType::Directory { - return Err(io::Error::from_raw_os_error(libc::ENOTDIR)); - } - - let mut flags: i32 = libc::O_CLOEXEC | libc::O_CREAT | libc::O_EXCL; - for &(p9f, of) in &MAPPED_FLAGS { - if (lcreate.flags & p9f) != 0 { - flags |= of; - } - } - if lcreate.flags & P9_NOACCESS == P9_RDONLY { - flags |= libc::O_RDONLY; - } - - let name = string_to_cstring(lcreate.name)?; - - // Safe because this doesn't modify any memory and we check the return value. - let fd = syscall!(unsafe { - libc::openat(fid.path.as_raw_fd(), name.as_ptr(), flags, lcreate.mode) - })?; - - // Safe because we just opened this fd and we know it is valid. - let file = unsafe { File::from_raw_fd(fd) }; - let st = stat(&file)?; - let iounit = st.st_blksize as u32; - - fid.file = Some(file); - - // This fid now refers to the newly created file so we need to update the O_PATH fd for it - // as well. - fid.path = lookup(&fid.path, &name)?; - - Ok(Rlcreate { - qid: st.into(), - iounit, - }) - } - - fn symlink(&mut self, _symlink: &Tsymlink) -> io::Result<Rsymlink> { - // symlinks are not allowed. - Err(io::Error::from_raw_os_error(libc::EACCES)) - } - - fn mknod(&mut self, _mknod: &Tmknod) -> io::Result<Rmknod> { - // No nodes either. - Err(io::Error::from_raw_os_error(libc::EACCES)) - } - - fn rename(&mut self, _rename: &Trename) -> io::Result<()> { - // We cannot support this as an inode may be linked into multiple directories but we don't - // know which one the client wants us to rename. Linux uses rename_at anyway, so we don't - // need to worry about this. - Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) - } - - fn readlink(&mut self, _readlink: &Treadlink) -> io::Result<Rreadlink> { - // symlinks are not allowed - Err(io::Error::from_raw_os_error(libc::EACCES)) - } - - fn get_attr(&mut self, get_attr: &Tgetattr) -> io::Result<Rgetattr> { - let fid = self.fids.get_mut(&get_attr.fid).ok_or_else(ebadf)?; - - let st = stat(&fid.path)?; - - Ok(Rgetattr { - valid: P9_GETATTR_BASIC, - qid: st.into(), - mode: st.st_mode, - uid: map_id_from_host(&self.cfg.uid_map, st.st_uid), - gid: map_id_from_host(&self.cfg.gid_map, st.st_gid), - nlink: st.st_nlink as u64, - rdev: st.st_rdev, - size: st.st_size as u64, - blksize: st.st_blksize as u64, - blocks: st.st_blocks as u64, - atime_sec: st.st_atime as u64, - atime_nsec: st.st_atime_nsec as u64, - mtime_sec: st.st_mtime as u64, - mtime_nsec: st.st_mtime_nsec as u64, - ctime_sec: st.st_ctime as u64, - ctime_nsec: st.st_ctime_nsec as u64, - btime_sec: 0, - btime_nsec: 0, - gen: 0, - data_version: 0, - }) - } - - fn set_attr(&mut self, set_attr: &Tsetattr) -> io::Result<()> { - let blocked_ops = P9_SETATTR_MODE | P9_SETATTR_UID | P9_SETATTR_GID; - if set_attr.valid & blocked_ops != 0 { - return Err(io::Error::from_raw_os_error(libc::EPERM)); - } - - let fid = self.fids.get(&set_attr.fid).ok_or_else(ebadf)?; - - let file = if let Some(ref file) = fid.file { - MaybeOwned::Borrowed(file) - } else { - let flags = match fid.filetype { - FileType::Regular => P9_RDWR, - FileType::Directory => P9_RDONLY | P9_DIRECTORY, - FileType::Other => P9_RDWR, - }; - MaybeOwned::Owned(open_fid(&self.proc, &fid.path, P9_NONBLOCK | flags)?) - }; - - if set_attr.valid & P9_SETATTR_SIZE != 0 { - file.set_len(set_attr.size)?; - } - - if set_attr.valid & (P9_SETATTR_ATIME | P9_SETATTR_MTIME) != 0 { - let times = [ - libc::timespec { - tv_sec: set_attr.atime_sec as _, - tv_nsec: if set_attr.valid & P9_SETATTR_ATIME == 0 { - libc::UTIME_OMIT - } else if set_attr.valid & P9_SETATTR_ATIME_SET == 0 { - libc::UTIME_NOW - } else { - set_attr.atime_nsec as _ - }, - }, - libc::timespec { - tv_sec: set_attr.mtime_sec as _, - tv_nsec: if set_attr.valid & P9_SETATTR_MTIME == 0 { - libc::UTIME_OMIT - } else if set_attr.valid & P9_SETATTR_MTIME_SET == 0 { - libc::UTIME_NOW - } else { - set_attr.mtime_nsec as _ - }, - }, - ]; - - // Safe because file is valid and we have initialized times fully. - let ret = unsafe { libc::futimens(file.as_raw_fd(), × as *const libc::timespec) }; - if ret < 0 { - return Err(io::Error::last_os_error()); - } - } - - // The ctime would have been updated by any of the above operations so we only - // need to change it if it was the only option given. - if set_attr.valid & P9_SETATTR_CTIME != 0 && set_attr.valid & (!P9_SETATTR_CTIME) == 0 { - // Setting -1 as the uid and gid will not actually change anything but will - // still update the ctime. - let ret = unsafe { - libc::fchown( - file.as_raw_fd(), - libc::uid_t::max_value(), - libc::gid_t::max_value(), - ) - }; - if ret < 0 { - return Err(io::Error::last_os_error()); - } - } - - Ok(()) - } - - fn xattr_walk(&mut self, _xattr_walk: &Txattrwalk) -> io::Result<Rxattrwalk> { - Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) - } - - fn xattr_create(&mut self, _xattr_create: &Txattrcreate) -> io::Result<()> { - Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) - } - - fn readdir(&mut self, readdir: &Treaddir) -> io::Result<Rreaddir> { - let fid = self.fids.get_mut(&readdir.fid).ok_or_else(ebadf)?; - - if fid.filetype != FileType::Directory { - return Err(io::Error::from_raw_os_error(libc::ENOTDIR)); - } - - // Use an empty Rreaddir struct to figure out the maximum number of bytes that - // can be returned. - let header_size = Rframe { - tag: 0, - msg: Rmessage::Readdir(Rreaddir { - data: Data(Vec::new()), - }), - } - .byte_size(); - let count = min(self.cfg.msize - header_size, readdir.count); - let mut cursor = Cursor::new(Vec::with_capacity(count as usize)); - - let dir = fid.file.as_mut().ok_or_else(ebadf)?; - let mut dirents = read_dir(dir, readdir.offset as libc::off64_t)?; - while let Some(dirent) = dirents.next().transpose()? { - let st = statat(&fid.path, &dirent.name, 0)?; - - let name = dirent - .name - .to_str() - .map(String::from) - .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; - - let entry = Dirent { - qid: st.into(), - offset: dirent.offset, - ty: dirent.type_, - name, - }; - - let byte_size = entry.byte_size() as usize; - - if cursor.get_ref().capacity() - cursor.get_ref().len() < byte_size { - // No more room in the buffer. - break; - } - - entry.encode(&mut cursor)?; - } - - Ok(Rreaddir { - data: Data(cursor.into_inner()), - }) - } - - fn fsync(&mut self, fsync: &Tfsync) -> io::Result<()> { - let file = self - .fids - .get(&fsync.fid) - .and_then(|fid| fid.file.as_ref()) - .ok_or_else(ebadf)?; - - if fsync.datasync == 0 { - file.sync_all()?; - } else { - file.sync_data()?; - } - Ok(()) - } - - fn lock(&mut self, _lock: &Tlock) -> io::Result<Rlock> { - // File locking is not supported. - Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) - } - fn get_lock(&mut self, _get_lock: &Tgetlock) -> io::Result<Rgetlock> { - // File locking is not supported. - Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) - } - - fn link(&mut self, link: Tlink) -> io::Result<()> { - let target = self.fids.get(&link.fid).ok_or_else(ebadf)?; - let path = string_to_cstring(format!("self/fd/{}", target.path.as_raw_fd()))?; - - let dir = self.fids.get(&link.dfid).ok_or_else(ebadf)?; - let name = string_to_cstring(link.name)?; - - // Safe because this doesn't modify any memory and we check the return value. - syscall!(unsafe { - libc::linkat( - self.proc.as_raw_fd(), - path.as_ptr(), - dir.path.as_raw_fd(), - name.as_ptr(), - libc::AT_SYMLINK_FOLLOW, - ) - })?; - Ok(()) - } - - fn mkdir(&mut self, mkdir: Tmkdir) -> io::Result<Rmkdir> { - let fid = self.fids.get(&mkdir.dfid).ok_or_else(ebadf)?; - let name = string_to_cstring(mkdir.name)?; - - // Safe because this doesn't modify any memory and we check the return value. - syscall!(unsafe { libc::mkdirat(fid.path.as_raw_fd(), name.as_ptr(), mkdir.mode) })?; - Ok(Rmkdir { - qid: statat(&fid.path, &name, 0).map(Qid::from)?, - }) - } - - fn rename_at(&mut self, rename_at: Trenameat) -> io::Result<()> { - let olddir = self.fids.get(&rename_at.olddirfid).ok_or_else(ebadf)?; - let oldname = string_to_cstring(rename_at.oldname)?; - - let newdir = self.fids.get(&rename_at.newdirfid).ok_or_else(ebadf)?; - let newname = string_to_cstring(rename_at.newname)?; - - // Safe because this doesn't modify any memory and we check the return value. - syscall!(unsafe { - libc::renameat( - olddir.path.as_raw_fd(), - oldname.as_ptr(), - newdir.path.as_raw_fd(), - newname.as_ptr(), - ) - })?; - - Ok(()) - } - - fn unlink_at(&mut self, unlink_at: Tunlinkat) -> io::Result<()> { - let dir = self.fids.get(&unlink_at.dirfd).ok_or_else(ebadf)?; - let name = string_to_cstring(unlink_at.name)?; - - syscall!(unsafe { - libc::unlinkat( - dir.path.as_raw_fd(), - name.as_ptr(), - unlink_at.flags as libc::c_int, - ) - })?; - - Ok(()) - } -} - -#[cfg(test)] -mod tests; diff --git a/src/server/tests.rs b/src/server/tests.rs deleted file mode 100644 index ee122dd..0000000 --- a/src/server/tests.rs +++ /dev/null @@ -1,1147 +0,0 @@ -// Copyright 2019 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use super::*; - -use std::borrow::Cow; -use std::collections::{HashSet, VecDeque}; -use std::env; -use std::ffi::{CString, OsString}; -use std::fs::{self, File}; -use std::io::{self, Cursor}; -use std::mem; -use std::ops::Deref; -use std::os::unix::ffi::OsStringExt; -use std::os::unix::fs::MetadataExt; -use std::path::{Component, Path, PathBuf}; -use std::u32; - -// Used to indicate that there is no fid associated with this message. -const P9_NOFID: u32 = u32::MAX; - -// The fid associated with the root directory of the server. -const ROOT_FID: u32 = 1; - -// How big we want the default buffer to be when running tests. -const DEFAULT_BUFFER_SIZE: u32 = 4096; - -// Joins `path` to `buf`. If `path` is '..', removes the last component from `buf` -// only if `buf` != `root` but does nothing if `buf` == `root`. Pushes `path` onto -// `buf` if it is a normal path component. -// -// Returns an error if `path` is absolute, has more than one component, or contains -// a '.' component. -fn join_path<P: AsRef<Path>, R: AsRef<Path>>( - mut buf: PathBuf, - path: P, - root: R, -) -> io::Result<PathBuf> { - let path = path.as_ref(); - let root = root.as_ref(); - debug_assert!(buf.starts_with(root)); - - if path.components().count() > 1 { - return Err(io::Error::from_raw_os_error(libc::EINVAL)); - } - - for component in path.components() { - match component { - // Prefix should only appear on windows systems. - Component::Prefix(_) => return Err(io::Error::from_raw_os_error(libc::EINVAL)), - // Absolute paths are not allowed. - Component::RootDir => return Err(io::Error::from_raw_os_error(libc::EINVAL)), - // '.' elements are not allowed. - Component::CurDir => return Err(io::Error::from_raw_os_error(libc::EINVAL)), - Component::ParentDir => { - // We only remove the parent path if we are not already at the root of the - // file system. - if buf != root { - buf.pop(); - } - } - Component::Normal(element) => buf.push(element), - } - } - - Ok(buf) -} - -// Automatically deletes the path it contains when it goes out of scope. -struct ScopedPath<P: AsRef<Path>>(P); - -impl<P: AsRef<Path>> AsRef<Path> for ScopedPath<P> { - fn as_ref(&self) -> &Path { - self.0.as_ref() - } -} - -impl<P: AsRef<Path>> Deref for ScopedPath<P> { - type Target = Path; - - fn deref(&self) -> &Self::Target { - self.0.as_ref() - } -} - -impl<P: AsRef<Path>> Drop for ScopedPath<P> { - fn drop(&mut self) { - if let Err(e) = fs::remove_dir_all(&**self) { - println!("Failed to remove {}: {}", self.display(), e); - } - } -} - -enum DirEntry<'a> { - File { - name: &'a str, - content: &'a [u8], - }, - Directory { - name: &'a str, - entries: &'a [DirEntry<'a>], - }, -} - -impl<'a> DirEntry<'a> { - // Creates `self` in the path given by `dir`. - fn create(&self, dir: &mut Cow<Path>) { - match *self { - DirEntry::File { name, content } => { - let mut f = File::create(dir.join(name)).expect("failed to create file"); - f.write_all(content).expect("failed to write file content"); - } - DirEntry::Directory { name, entries } => { - dir.to_mut().push(name); - - fs::create_dir_all(&**dir).expect("failed to create directory"); - for e in entries { - e.create(dir); - } - - assert!(dir.to_mut().pop()); - } - } - } -} - -// Creates a file with `name` in `dir` and fills it with random -// content. -fn create_local_file<P: AsRef<Path>>(dir: P, name: &str) -> Vec<u8> { - let mut content = Vec::new(); - File::open("/dev/urandom") - .and_then(|f| f.take(200).read_to_end(&mut content)) - .expect("failed to read from /dev/urandom"); - - let f = DirEntry::File { - name, - content: &content, - }; - f.create(&mut Cow::from(dir.as_ref())); - - content -} - -fn check_qid(qid: &Qid, md: &fs::Metadata) { - let ty = if md.is_dir() { - P9_QTDIR - } else if md.is_file() { - P9_QTFILE - } else if md.file_type().is_symlink() { - P9_QTSYMLINK - } else { - panic!("unknown file type: {:?}", md.file_type()); - }; - assert_eq!(qid.ty, ty); - assert_eq!(qid.version, md.mtime() as u32); - assert_eq!(qid.path, md.ino()); -} - -fn check_attr(server: &mut Server, fid: u32, md: &fs::Metadata) { - let tgetattr = Tgetattr { - fid, - request_mask: P9_GETATTR_BASIC, - }; - - let rgetattr = server.get_attr(&tgetattr).expect("failed to call get_attr"); - - let ty = if md.is_dir() { - P9_QTDIR - } else if md.is_file() { - P9_QTFILE - } else if md.file_type().is_symlink() { - P9_QTSYMLINK - } else { - panic!("unknown file type: {:?}", md.file_type()); - }; - assert_eq!(rgetattr.valid, P9_GETATTR_BASIC); - assert_eq!(rgetattr.qid.ty, ty); - assert_eq!(rgetattr.qid.version, md.mtime() as u32); - assert_eq!(rgetattr.qid.path, md.ino()); - assert_eq!(rgetattr.mode, md.mode()); - assert_eq!(rgetattr.uid, md.uid()); - assert_eq!(rgetattr.gid, md.gid()); - assert_eq!(rgetattr.nlink, md.nlink()); - assert_eq!(rgetattr.rdev, md.rdev()); - assert_eq!(rgetattr.size, md.size()); - assert_eq!(rgetattr.atime_sec, md.atime() as u64); - assert_eq!(rgetattr.atime_nsec, md.atime_nsec() as u64); - assert_eq!(rgetattr.mtime_sec, md.mtime() as u64); - assert_eq!(rgetattr.mtime_nsec, md.mtime_nsec() as u64); - assert_eq!(rgetattr.ctime_sec, md.ctime() as u64); - assert_eq!(rgetattr.ctime_nsec, md.ctime_nsec() as u64); - assert_eq!(rgetattr.btime_sec, 0); - assert_eq!(rgetattr.btime_nsec, 0); - assert_eq!(rgetattr.gen, 0); - assert_eq!(rgetattr.data_version, 0); -} - -fn check_content(server: &mut Server, content: &[u8], fid: u32) { - for offset in 0..content.len() { - let tread = Tread { - fid, - offset: offset as u64, - count: DEFAULT_BUFFER_SIZE, - }; - - let rread = server.read(&tread).expect("failed to read file"); - assert_eq!(content[offset..], rread.data[..]); - } -} - -fn walk<P: Into<PathBuf>>( - server: &mut Server, - start: P, - fid: u32, - newfid: u32, - names: Vec<String>, -) { - let mut mds = Vec::with_capacity(names.len()); - let mut buf = start.into(); - for name in &names { - buf.push(name); - mds.push( - buf.symlink_metadata() - .expect("failed to get metadata for path"), - ); - } - - let twalk = Twalk { - fid, - newfid, - wnames: names, - }; - - let rwalk = server.walk(twalk).expect("failed to walk directoy"); - assert_eq!(mds.len(), rwalk.wqids.len()); - for (md, qid) in mds.iter().zip(rwalk.wqids.iter()) { - check_qid(qid, md); - } -} - -fn open<P: Into<PathBuf>>( - server: &mut Server, - dir: P, - dir_fid: u32, - name: &str, - fid: u32, - flags: u32, -) -> io::Result<Rlopen> { - let wnames = if name.is_empty() { - vec![] - } else { - vec![String::from(name)] - }; - walk(server, dir, dir_fid, fid, wnames); - - let tlopen = Tlopen { fid, flags }; - - server.lopen(&tlopen) -} - -fn write<P: AsRef<Path>>(server: &mut Server, dir: P, name: &str, fid: u32, flags: u32) { - let file_path = dir.as_ref().join(name); - let file_len = if file_path.exists() { - fs::symlink_metadata(&file_path) - .expect("unable to get metadata for file") - .len() as usize - } else { - 0usize - }; - let mut new_content = Vec::new(); - File::open("/dev/urandom") - .and_then(|f| f.take(200).read_to_end(&mut new_content)) - .expect("failed to read from /dev/urandom"); - - let twrite = Twrite { - fid, - offset: 0, - data: Data(new_content), - }; - - let rwrite = server.write(&twrite).expect("failed to write file"); - assert_eq!(rwrite.count, twrite.data.len() as u32); - - let tfsync = Tfsync { fid, datasync: 0 }; - server.fsync(&tfsync).expect("failed to sync file contents"); - - let actual_content = fs::read(file_path).expect("failed to read back content from file"); - - // If the file was opened append-only, then the content should have been - // written to the end even though the offset was 0. - let idx = if flags & P9_APPEND == 0 { 0 } else { file_len }; - assert_eq!(actual_content[idx..], twrite.data[..]); -} - -fn create<P: Into<PathBuf>>( - server: &mut Server, - dir: P, - dir_fid: u32, - fid: u32, - name: &str, - flags: u32, - mode: u32, -) -> io::Result<Rlcreate> { - // The `fid` in the lcreate call initially points to the directory - // but is supposed to point to the newly created file after the call - // completes. Duplicate the fid so that we don't end up consuming the - // directory fid. - walk(server, dir, dir_fid, fid, Vec::new()); - - let tlcreate = Tlcreate { - fid, - name: String::from(name), - flags, - mode, - gid: 0, - }; - - server.lcreate(tlcreate) -} - -struct Readdir<'a> { - server: &'a mut Server, - fid: u32, - offset: u64, - cursor: Cursor<Vec<u8>>, -} - -impl<'a> Iterator for Readdir<'a> { - type Item = Dirent; - - fn next(&mut self) -> Option<Self::Item> { - if self.cursor.position() >= self.cursor.get_ref().len() as u64 { - let treaddir = Treaddir { - fid: self.fid, - offset: self.offset, - count: DEFAULT_BUFFER_SIZE, - }; - - let Rreaddir { data } = self - .server - .readdir(&treaddir) - .expect("failed to read directory"); - if data.is_empty() { - // No more entries. - return None; - } - - mem::drop(mem::replace(&mut self.cursor, Cursor::new(data.0))); - } - - let dirent: Dirent = WireFormat::decode(&mut self.cursor).expect("failed to decode dirent"); - self.offset = dirent.offset; - - Some(dirent) - } -} - -fn readdir(server: &mut Server, fid: u32) -> Readdir { - Readdir { - server, - fid, - offset: 0, - cursor: Cursor::new(Vec::new()), - } -} - -// Sets up the server to start handling messages. Creates a new temporary -// directory to act as the server root and sends an initial Tattach message. -// At the end of setup, fid 1 points to the root of the server. -fn setup<P: AsRef<Path>>(name: P) -> (ScopedPath<OsString>, Server) { - let mut test_dir = env::var_os("T") - .map(PathBuf::from) - .unwrap_or_else(env::temp_dir); - test_dir.push(name); - - let mut os_str = OsString::from(test_dir); - os_str.push(".XXXXXX"); - - // Create a c string and release ownership. This seems like the only way - // to get a *mut c_char. - let buf = CString::new(os_str.into_vec()) - .expect("failed to create CString") - .into_raw(); - - // Safe because this will only modify the contents of `buf`. - let ret = unsafe { libc::mkdtemp(buf) }; - - // Take ownership of the buffer back before checking the result. Safe because - // this was created by a call to into_raw() above and mkdtemp will not overwrite - // the trailing '\0'. - let buf = unsafe { CString::from_raw(buf) }; - - assert!(!ret.is_null()); - - let test_dir = ScopedPath(OsString::from_vec(buf.into_bytes())); - - // Create a basic file system hierarchy. - let entries = [ - DirEntry::Directory { - name: "subdir", - entries: &[ - DirEntry::File { - name: "b", - content: b"hello, world!", - }, - DirEntry::Directory { - name: "nested", - entries: &[DirEntry::File { - name: "Огонь по готовности!", - content: &[ - 0xe9u8, 0xbeu8, 0x8du8, 0xe3u8, 0x81u8, 0x8cu8, 0xe6u8, 0x88u8, 0x91u8, - 0xe3u8, 0x81u8, 0x8cu8, 0xe6u8, 0x95u8, 0xb5u8, 0xe3u8, 0x82u8, 0x92u8, - 0xe5u8, 0x96u8, 0xb0u8, 0xe3u8, 0x82u8, 0x89u8, 0xe3u8, 0x81u8, 0x86u8, - 0x21u8, - ], - }], - }, - ], - }, - DirEntry::File { - name: "世界.txt", - content: &[ - 0xe3u8, 0x81u8, 0x93u8, 0xe3u8, 0x82u8, 0x93u8, 0xe3u8, 0x81u8, 0xabu8, 0xe3u8, - 0x81u8, 0xa1u8, 0xe3u8, 0x81u8, 0xafu8, - ], - }, - ]; - - for e in &entries { - e.create(&mut Cow::from(&*test_dir)); - } - - let md = test_dir - .symlink_metadata() - .expect("failed to get metadata for root dir"); - - let mut server = Server::new(&*test_dir, Default::default(), Default::default()) - .expect("Failed to create server"); - - let tversion = Tversion { - msize: DEFAULT_BUFFER_SIZE, - version: String::from("9P2000.L"), - }; - - let rversion = server - .version(&tversion) - .expect("failed to get version from server"); - assert_eq!(rversion.msize, DEFAULT_BUFFER_SIZE); - assert_eq!(rversion.version, "9P2000.L"); - - let tattach = Tattach { - fid: ROOT_FID, - afid: P9_NOFID, - uname: String::from("unittest"), - aname: String::from(""), - n_uname: 1000, - }; - - let rattach = server.attach(&tattach).expect("failed to attach to server"); - check_qid(&rattach.qid, &md); - - (test_dir, server) -} - -#[test] -fn path_joins() { - let root = PathBuf::from("/a/b/c"); - let path = PathBuf::from("/a/b/c/d/e/f"); - - assert_eq!( - &join_path(path.clone(), "nested", &root).expect("normal"), - Path::new("/a/b/c/d/e/f/nested") - ); - - let p1 = join_path(path, "..", &root).expect("parent 1"); - assert_eq!(&p1, Path::new("/a/b/c/d/e/")); - - let p2 = join_path(p1, "..", &root).expect("parent 2"); - assert_eq!(&p2, Path::new("/a/b/c/d/")); - - let p3 = join_path(p2, "..", &root).expect("parent 3"); - assert_eq!(&p3, Path::new("/a/b/c/")); - - let p4 = join_path(p3, "..", &root).expect("parent of root"); - assert_eq!(&p4, Path::new("/a/b/c/")); -} - -#[test] -fn invalid_joins() { - let root = PathBuf::from("/a"); - let path = PathBuf::from("/a/b"); - - join_path(path.clone(), ".", &root).expect_err("current directory"); - join_path(path.clone(), "c/d/e", &root).expect_err("too many components"); - join_path(path, "/c/d/e", &root).expect_err("absolute path"); -} - -#[test] -fn clunk() { - let (_test_dir, mut server) = setup("clunk"); - - let tclunk = Tclunk { fid: ROOT_FID }; - server.clunk(&tclunk).expect("failed to clunk root fid"); -} - -#[test] -fn get_attr() { - let (test_dir, mut server) = setup("get_attr"); - - let md = test_dir - .symlink_metadata() - .expect("failed to get metadata for test dir"); - - check_attr(&mut server, ROOT_FID, &md); -} - -#[test] -fn tree_walk() { - let (test_dir, mut server) = setup("readdir"); - - let mut next_fid = ROOT_FID + 1; - - let mut dirs = VecDeque::new(); - dirs.push_back(test_dir.to_path_buf()); - - while let Some(dir) = dirs.pop_front() { - let dfid = next_fid; - next_fid += 1; - - let wnames: Vec<String> = dir - .strip_prefix(&test_dir) - .expect("test directory is not prefix of subdir") - .components() - .map(|c| Path::new(&c).to_string_lossy().to_string()) - .collect(); - walk(&mut server, &*test_dir, ROOT_FID, dfid, wnames); - - let md = dir.symlink_metadata().expect("failed to get metadata"); - - check_attr(&mut server, dfid, &md); - - let fid = next_fid; - next_fid += 1; - open(&mut server, &dir, dfid, "", fid, P9_DIRECTORY).expect("Failed to open directory"); - for dirent in readdir(&mut server, fid) { - if dirent.name == "." || dirent.name == ".." { - continue; - } - - let entry_path = dir.join(&dirent.name); - assert!( - entry_path.exists(), - "directory entry \"{}\" does not exist", - entry_path.display() - ); - let md = fs::symlink_metadata(&entry_path).expect("failed to get metadata for entry"); - - let ty = if md.is_dir() { - dirs.push_back(dir.join(dirent.name)); - libc::DT_DIR - } else if md.is_file() { - libc::DT_REG - } else if md.file_type().is_symlink() { - libc::DT_LNK - } else { - panic!("unknown file type: {:?}", md.file_type()); - }; - - assert_eq!(dirent.ty, ty); - check_qid(&dirent.qid, &md); - } - - let tclunk = Tclunk { fid }; - server.clunk(&tclunk).expect("failed to clunk fid"); - } -} - -#[test] -fn create_existing_file() { - let (test_dir, mut server) = setup("create_existing"); - - let name = "existing"; - create_local_file(&test_dir, name); - - let fid = ROOT_FID + 1; - create( - &mut server, - &*test_dir, - ROOT_FID, - fid, - name, - P9_APPEND, - 0o644, - ) - .expect_err("successfully created existing file"); -} - -enum SetAttrKind { - File, - Directory, -} - -fn set_attr_test<F>(kind: SetAttrKind, set_fields: F) -> io::Result<fs::Metadata> -where - F: FnOnce(&mut Tsetattr), -{ - let (test_dir, mut server) = setup("set_attr"); - - let name = "existing"; - match kind { - SetAttrKind::File => { - create_local_file(&test_dir, name); - } - SetAttrKind::Directory => { - let tmkdir = Tmkdir { - dfid: ROOT_FID, - name: String::from(name), - mode: 0o755, - gid: 0, - }; - - let rmkdir = server.mkdir(tmkdir).expect("failed to create directory"); - let md = fs::symlink_metadata(test_dir.join(name)) - .expect("failed to get metadata for directory"); - - assert!(md.is_dir()); - check_qid(&rmkdir.qid, &md); - } - }; - - let fid = ROOT_FID + 1; - walk( - &mut server, - &*test_dir, - ROOT_FID, - fid, - vec![String::from(name)], - ); - - let mut tsetattr = Tsetattr { - fid, - valid: 0, - mode: 0, - uid: 0, - gid: 0, - size: 0, - atime_sec: 0, - atime_nsec: 0, - mtime_sec: 0, - mtime_nsec: 0, - }; - - set_fields(&mut tsetattr); - server.set_attr(&tsetattr)?; - - fs::symlink_metadata(test_dir.join(name)) -} - -#[test] -fn set_len() { - let len = 661; - let md = set_attr_test(SetAttrKind::File, |tsetattr| { - tsetattr.valid = P9_SETATTR_SIZE; - tsetattr.size = len; - }) - .expect("failed to run set length of file"); - - assert_eq!(md.size(), len); -} - -#[test] -fn set_file_mode() { - let mode = 0o640; - let err = set_attr_test(SetAttrKind::File, |tsetattr| { - tsetattr.valid = P9_SETATTR_MODE; - tsetattr.mode = mode; - }) - .expect_err("successfully set mode"); - - assert_eq!(err.kind(), io::ErrorKind::PermissionDenied); -} - -#[test] -fn set_file_uid() { - let uid = 294; - let err = set_attr_test(SetAttrKind::File, |tsetattr| { - tsetattr.valid = P9_SETATTR_UID; - tsetattr.uid = uid; - }) - .expect_err("successfully set uid"); - - assert_eq!(err.kind(), io::ErrorKind::PermissionDenied); -} - -#[test] -fn set_file_gid() { - let gid = 9024; - let err = set_attr_test(SetAttrKind::File, |tsetattr| { - tsetattr.valid = P9_SETATTR_GID; - tsetattr.gid = gid; - }) - .expect_err("successfully set gid"); - - assert_eq!(err.kind(), io::ErrorKind::PermissionDenied); -} - -#[test] -fn set_file_mtime() { - let (secs, nanos) = (1245247825, 524617); - let md = set_attr_test(SetAttrKind::File, |tsetattr| { - tsetattr.valid = P9_SETATTR_MTIME | P9_SETATTR_MTIME_SET; - tsetattr.mtime_sec = secs; - tsetattr.mtime_nsec = nanos; - }) - .expect("failed to set mtime"); - - assert_eq!(md.mtime() as u64, secs); - assert_eq!(md.mtime_nsec() as u64, nanos); -} - -#[test] -fn set_file_atime() { - let (secs, nanos) = (9247605, 4016); - let md = set_attr_test(SetAttrKind::File, |tsetattr| { - tsetattr.valid = P9_SETATTR_ATIME | P9_SETATTR_ATIME_SET; - tsetattr.atime_sec = secs; - tsetattr.atime_nsec = nanos; - }) - .expect("failed to set atime"); - - assert_eq!(md.atime() as u64, secs); - assert_eq!(md.atime_nsec() as u64, nanos); -} - -#[test] -fn set_dir_mode() { - let mode = 0o640; - let err = set_attr_test(SetAttrKind::Directory, |tsetattr| { - tsetattr.valid = P9_SETATTR_MODE; - tsetattr.mode = mode; - }) - .expect_err("successfully set mode"); - - assert_eq!(err.kind(), io::ErrorKind::PermissionDenied); -} - -#[test] -fn set_dir_uid() { - let uid = 294; - let err = set_attr_test(SetAttrKind::Directory, |tsetattr| { - tsetattr.valid = P9_SETATTR_UID; - tsetattr.uid = uid; - }) - .expect_err("successfully set uid"); - - assert_eq!(err.kind(), io::ErrorKind::PermissionDenied); -} - -#[test] -fn set_dir_gid() { - let gid = 9024; - let err = set_attr_test(SetAttrKind::Directory, |tsetattr| { - tsetattr.valid = P9_SETATTR_GID; - tsetattr.gid = gid; - }) - .expect_err("successfully set gid"); - - assert_eq!(err.kind(), io::ErrorKind::PermissionDenied); -} - -#[test] -fn set_dir_mtime() { - let (secs, nanos) = (1245247825, 524617); - let md = set_attr_test(SetAttrKind::Directory, |tsetattr| { - tsetattr.valid = P9_SETATTR_MTIME | P9_SETATTR_MTIME_SET; - tsetattr.mtime_sec = secs; - tsetattr.mtime_nsec = nanos; - }) - .expect("failed to set mtime"); - - assert_eq!(md.mtime() as u64, secs); - assert_eq!(md.mtime_nsec() as u64, nanos); -} - -#[test] -fn set_dir_atime() { - let (secs, nanos) = (9247605, 4016); - let md = set_attr_test(SetAttrKind::Directory, |tsetattr| { - tsetattr.valid = P9_SETATTR_ATIME | P9_SETATTR_ATIME_SET; - tsetattr.atime_sec = secs; - tsetattr.atime_nsec = nanos; - }) - .expect("failed to set atime"); - - assert_eq!(md.atime() as u64, secs); - assert_eq!(md.atime_nsec() as u64, nanos); -} - -#[test] -fn huge_directory() { - let (test_dir, mut server) = setup("huge_directory"); - - let name = "newdir"; - let newdir = test_dir.join(name); - fs::create_dir(&newdir).expect("failed to create directory"); - - let dfid = ROOT_FID + 1; - walk( - &mut server, - &*test_dir, - ROOT_FID, - dfid, - vec![String::from(name)], - ); - - // Create ~4K files in the directory and then attempt to read them all. - let mut filenames = HashSet::with_capacity(4096); - for i in 0..4096 { - let name = format!("file_{}", i); - create_local_file(&newdir, &name); - assert!(filenames.insert(name)); - } - - let fid = dfid + 1; - open(&mut server, &newdir, dfid, "", fid, P9_DIRECTORY).expect("Failed to open directory"); - for f in readdir(&mut server, fid) { - let path = newdir.join(&f.name); - - let md = fs::symlink_metadata(path).expect("failed to get metadata for path"); - check_qid(&f.qid, &md); - - if f.name == "." || f.name == ".." { - assert_eq!(f.ty, libc::DT_DIR); - } else { - assert_eq!(f.ty, libc::DT_REG); - assert!(filenames.remove(&f.name)); - } - } - - assert!(filenames.is_empty()); -} - -#[test] -fn mkdir() { - let (test_dir, mut server) = setup("mkdir"); - - let name = "conan"; - let tmkdir = Tmkdir { - dfid: ROOT_FID, - name: String::from(name), - mode: 0o755, - gid: 0, - }; - - let rmkdir = server.mkdir(tmkdir).expect("failed to create directory"); - let md = - fs::symlink_metadata(test_dir.join(name)).expect("failed to get metadata for directory"); - - assert!(md.is_dir()); - check_qid(&rmkdir.qid, &md); -} - -#[test] -fn unlink_all() { - let (test_dir, mut server) = setup("readdir"); - - let mut next_fid = ROOT_FID + 1; - - let mut dirs = VecDeque::new(); - dirs.push_back((ROOT_FID, test_dir.to_path_buf())); - - // First iterate over the whole directory. - let mut unlinks = VecDeque::new(); - while let Some((dfid, dir)) = dirs.pop_front() { - let mut names = VecDeque::new(); - for entry in fs::read_dir(dir).expect("failed to read directory") { - let entry = entry.expect("unable to iterate over directory"); - let ft = entry - .file_type() - .expect("failed to get file type for entry"); - if ft.is_dir() { - let fid = next_fid; - next_fid += 1; - - let wnames: Vec<String> = entry - .path() - .strip_prefix(&test_dir) - .expect("test directory is not prefix of subdir") - .components() - .map(|c| Path::new(&c).to_string_lossy().to_string()) - .collect(); - walk(&mut server, &*test_dir, ROOT_FID, fid, wnames); - dirs.push_back((fid, entry.path())); - } - - names.push_back(( - entry - .file_name() - .into_string() - .expect("failed to convert entry name to string"), - if ft.is_dir() { - libc::AT_REMOVEDIR as u32 - } else { - 0 - }, - )); - } - - unlinks.push_back((dfid, names)); - } - - // Now remove everything in reverse order. - while let Some((dfid, names)) = unlinks.pop_back() { - for (name, flags) in names { - let tunlinkat = Tunlinkat { - dirfd: dfid, - name, - flags, - }; - - server.unlink_at(tunlinkat).expect("failed to unlink path"); - } - } -} - -#[test] -fn rename_at() { - let (test_dir, mut server) = setup("rename"); - - let name = "oldfile"; - let content = create_local_file(&test_dir, name); - - let newname = "newfile"; - let trename = Trenameat { - olddirfid: ROOT_FID, - oldname: String::from(name), - newdirfid: ROOT_FID, - newname: String::from(newname), - }; - - server.rename_at(trename).expect("failed to rename file"); - - assert!(!test_dir.join(name).exists()); - - let mut newcontent = Vec::with_capacity(content.len()); - let size = File::open(test_dir.join(newname)) - .expect("failed to open file") - .read_to_end(&mut newcontent) - .expect("failed to read new file content"); - assert_eq!(size, content.len()); - assert_eq!(newcontent, content); -} - -macro_rules! open_test { - ($name:ident, $flags:expr) => { - #[test] - fn $name() { - let (test_dir, mut server) = setup("open"); - - let fid = ROOT_FID + 1; - let name = "test.txt"; - let content = create_local_file(&test_dir, name); - - let rlopen = open(&mut server, &*test_dir, ROOT_FID, name, fid, $flags as u32) - .expect("failed to open file"); - - let md = - fs::symlink_metadata(test_dir.join(name)).expect("failed to get metadata for file"); - check_qid(&rlopen.qid, &md); - assert_eq!(rlopen.iounit, md.blksize() as u32); - - check_attr(&mut server, fid, &md); - - // Check that the file has the proper contents as long as we didn't - // truncate it first. - if $flags & P9_TRUNC == 0 && $flags & P9_WRONLY == 0 { - check_content(&mut server, &content, fid); - } - - // Check that we can write to the file. - if $flags & P9_RDWR != 0 || $flags & P9_WRONLY != 0 { - write(&mut server, &test_dir, name, fid, $flags); - } - - let tclunk = Tclunk { fid }; - server.clunk(&tclunk).expect("Unable to clunk file"); - } - }; - ($name:ident, $flags:expr, $expected_err:expr) => { - #[test] - fn $name() { - let (test_dir, mut server) = setup("open_fail"); - - let fid = ROOT_FID + 1; - let name = "test.txt"; - create_local_file(&test_dir, name); - - let err = open(&mut server, &*test_dir, ROOT_FID, name, fid, $flags as u32) - .expect_err("successfully opened file"); - assert_eq!(err.kind(), $expected_err); - - let tclunk = Tclunk { fid }; - server.clunk(&tclunk).expect("Unable to clunk file"); - } - }; -} - -open_test!(read_only_file_open, P9_RDONLY); -open_test!(read_write_file_open, P9_RDWR); -open_test!(write_only_file_open, P9_WRONLY); - -open_test!(create_read_only_file_open, P9_CREATE | P9_RDONLY); -open_test!(create_read_write_file_open, P9_CREATE | P9_RDWR); -open_test!(create_write_only_file_open, P9_CREATE | P9_WRONLY); - -open_test!(append_read_only_file_open, P9_APPEND | P9_RDONLY); -open_test!(append_read_write_file_open, P9_APPEND | P9_RDWR); -open_test!(append_write_only_file_open, P9_APPEND | P9_WRONLY); - -open_test!(trunc_read_only_file_open, P9_TRUNC | P9_RDONLY); -open_test!(trunc_read_write_file_open, P9_TRUNC | P9_RDWR); -open_test!(trunc_write_only_file_open, P9_TRUNC | P9_WRONLY); - -open_test!( - create_append_read_only_file_open, - P9_CREATE | P9_APPEND | P9_RDONLY -); -open_test!( - create_append_read_write_file_open, - P9_CREATE | P9_APPEND | P9_RDWR -); -open_test!( - create_append_wronly_file_open, - P9_CREATE | P9_APPEND | P9_WRONLY -); - -open_test!( - create_trunc_read_only_file_open, - P9_CREATE | P9_TRUNC | P9_RDONLY -); -open_test!( - create_trunc_read_write_file_open, - P9_CREATE | P9_TRUNC | P9_RDWR -); -open_test!( - create_trunc_wronly_file_open, - P9_CREATE | P9_TRUNC | P9_WRONLY -); - -open_test!( - append_trunc_read_only_file_open, - P9_APPEND | P9_TRUNC | P9_RDONLY -); -open_test!( - append_trunc_read_write_file_open, - P9_APPEND | P9_TRUNC | P9_RDWR -); -open_test!( - append_trunc_wronly_file_open, - P9_APPEND | P9_TRUNC | P9_WRONLY -); - -open_test!( - create_append_trunc_read_only_file_open, - P9_CREATE | P9_APPEND | P9_TRUNC | P9_RDONLY -); -open_test!( - create_append_trunc_read_write_file_open, - P9_CREATE | P9_APPEND | P9_TRUNC | P9_RDWR -); -open_test!( - create_append_trunc_wronly_file_open, - P9_CREATE | P9_APPEND | P9_TRUNC | P9_WRONLY -); - -open_test!( - create_excl_read_only_file_open, - P9_CREATE | P9_EXCL | P9_RDONLY, - io::ErrorKind::AlreadyExists -); -open_test!( - create_excl_read_write_file_open, - P9_CREATE | P9_EXCL | P9_RDWR, - io::ErrorKind::AlreadyExists -); -open_test!( - create_excl_wronly_file_open, - P9_CREATE | P9_EXCL | P9_WRONLY, - io::ErrorKind::AlreadyExists -); - -macro_rules! create_test { - ($name:ident, $flags:expr, $mode:expr) => { - #[test] - fn $name() { - let (test_dir, mut server) = setup("create"); - - let name = "foo.txt"; - let fid = ROOT_FID + 1; - let rlcreate = create(&mut server, &*test_dir, ROOT_FID, fid, name, $flags, $mode) - .expect("failed to create file"); - - let md = - fs::symlink_metadata(test_dir.join(name)).expect("failed to get metadata for file"); - assert_eq!(rlcreate.iounit, md.blksize() as u32); - check_qid(&rlcreate.qid, &md); - check_attr(&mut server, fid, &md); - - // Check that we can write to the file. - if $flags & P9_RDWR != 0 || $flags & P9_WRONLY != 0 { - write(&mut server, &test_dir, name, fid, $flags); - } - - let tclunk = Tclunk { fid }; - server.clunk(&tclunk).expect("Unable to clunk file"); - } - }; - ($name:ident, $flags:expr, $mode:expr, $expected_err:expr) => { - #[test] - fn $name() { - let (test_dir, mut server) = setup("create_fail"); - - let name = "foo.txt"; - // The `fid` in the lcreate call initially points to the directory - // but is supposed to point to the newly created file after the call - // completes. Duplicate the fid so that we don't end up consuming the - // root fid. - let fid = ROOT_FID + 1; - let err = create(&mut server, &*test_dir, ROOT_FID, fid, name, $flags, $mode) - .expect_err("successfully created file"); - assert_eq!(err.kind(), $expected_err); - } - }; -} - -create_test!(read_only_file_create, P9_RDONLY, 0o600u32); -create_test!(read_write_file_create, P9_RDWR, 0o600u32); -create_test!(write_only_file_create, P9_WRONLY, 0o600u32); - -create_test!( - append_read_only_file_create, - P9_APPEND | P9_RDONLY, - 0o600u32 -); -create_test!(append_read_write_file_create, P9_APPEND | P9_RDWR, 0o600u32); -create_test!(append_wronly_file_create, P9_APPEND | P9_WRONLY, 0o600u32); diff --git a/wire_format_derive/Android.bp b/wire_format_derive/Android.bp deleted file mode 100644 index b5908db..0000000 --- a/wire_format_derive/Android.bp +++ /dev/null @@ -1,22 +0,0 @@ -// This file is generated by cargo2android.py --run --device --tests --dependencies --patch=patches/Android.bp.patch. - -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "external_vm_tools_p9_license" - // to get the below license kinds: - // SPDX-license-identifier-BSD - default_applicable_licenses: ["external_vm_tools_p9_license"], -} - -rust_proc_macro { - name: "libwire_format_derive", - crate_name: "wire_format_derive", - srcs: ["wire_format_derive.rs"], - edition: "2015", - rustlibs: [ - "libproc_macro2", - "libquote", - "libsyn", - ], -} diff --git a/wire_format_derive/Cargo.toml b/wire_format_derive/Cargo.toml deleted file mode 100644 index 52ab6a4..0000000 --- a/wire_format_derive/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "wire_format_derive" -version = "0.1.0" -authors = ["The Chromium OS Authors"] - -[dependencies] -syn = "^1" -quote = "^1" -proc-macro2 = "^1" - -[lib] -proc-macro = true -path = "wire_format_derive.rs" diff --git a/wire_format_derive/wire_format_derive.rs b/wire_format_derive/wire_format_derive.rs deleted file mode 100644 index 290ffc5..0000000 --- a/wire_format_derive/wire_format_derive.rs +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2018 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! Derives a 9P wire format encoding for a struct by recursively calling -//! `WireFormat::encode` or `WireFormat::decode` on the fields of the struct. -//! This is only intended to be used from within the `p9` crate. - -#![recursion_limit = "256"] - -extern crate proc_macro; -extern crate proc_macro2; - -#[macro_use] -extern crate quote; - -#[macro_use] -extern crate syn; - -use proc_macro2::{Span, TokenStream}; -use syn::spanned::Spanned; -use syn::{Data, DeriveInput, Fields, Ident}; - -/// The function that derives the actual implementation. -#[proc_macro_derive(P9WireFormat)] -pub fn p9_wire_format(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = parse_macro_input!(input as DeriveInput); - p9_wire_format_inner(input).into() -} - -fn p9_wire_format_inner(input: DeriveInput) -> TokenStream { - if !input.generics.params.is_empty() { - return quote! { - compile_error!("derive(P9WireFormat) does not support generic parameters"); - }; - } - - let container = input.ident; - - let byte_size_impl = byte_size_sum(&input.data); - let encode_impl = encode_wire_format(&input.data); - let decode_impl = decode_wire_format(&input.data, &container); - - let scope = format!("wire_format_{}", container).to_lowercase(); - let scope = Ident::new(&scope, Span::call_site()); - quote! { - mod #scope { - extern crate std; - use self::std::io; - use self::std::result::Result::Ok; - - use super::#container; - - use protocol::WireFormat; - - impl WireFormat for #container { - fn byte_size(&self) -> u32 { - #byte_size_impl - } - - fn encode<W: io::Write>(&self, _writer: &mut W) -> io::Result<()> { - #encode_impl - } - - fn decode<R: io::Read>(_reader: &mut R) -> io::Result<Self> { - #decode_impl - } - } - } - } -} - -// Generate code that recursively calls byte_size on every field in the struct. -fn byte_size_sum(data: &Data) -> TokenStream { - if let Data::Struct(ref data) = *data { - if let Fields::Named(ref fields) = data.fields { - let fields = fields.named.iter().map(|f| { - let field = &f.ident; - let span = field.span(); - quote_spanned! {span=> - WireFormat::byte_size(&self.#field) - } - }); - - quote! { - 0 #(+ #fields)* - } - } else { - unimplemented!(); - } - } else { - unimplemented!(); - } -} - -// Generate code that recursively calls encode on every field in the struct. -fn encode_wire_format(data: &Data) -> TokenStream { - if let Data::Struct(ref data) = *data { - if let Fields::Named(ref fields) = data.fields { - let fields = fields.named.iter().map(|f| { - let field = &f.ident; - let span = field.span(); - quote_spanned! {span=> - WireFormat::encode(&self.#field, _writer)?; - } - }); - - quote! { - #(#fields)* - - Ok(()) - } - } else { - unimplemented!(); - } - } else { - unimplemented!(); - } -} - -// Generate code that recursively calls decode on every field in the struct. -fn decode_wire_format(data: &Data, container: &Ident) -> TokenStream { - if let Data::Struct(ref data) = *data { - if let Fields::Named(ref fields) = data.fields { - let values = fields.named.iter().map(|f| { - let field = &f.ident; - let span = field.span(); - quote_spanned! {span=> - let #field = WireFormat::decode(_reader)?; - } - }); - - let members = fields.named.iter().map(|f| { - let field = &f.ident; - quote! { - #field: #field, - } - }); - - quote! { - #(#values)* - - Ok(#container { - #(#members)* - }) - } - } else { - unimplemented!(); - } - } else { - unimplemented!(); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn byte_size() { - let input: DeriveInput = parse_quote! { - struct Item { - ident: u32, - with_underscores: String, - other: u8, - } - }; - - let expected = quote! { - 0 - + WireFormat::byte_size(&self.ident) - + WireFormat::byte_size(&self.with_underscores) - + WireFormat::byte_size(&self.other) - }; - - assert_eq!(byte_size_sum(&input.data).to_string(), expected.to_string()); - } - - #[test] - fn encode() { - let input: DeriveInput = parse_quote! { - struct Item { - ident: u32, - with_underscores: String, - other: u8, - } - }; - - let expected = quote! { - WireFormat::encode(&self.ident, _writer)?; - WireFormat::encode(&self.with_underscores, _writer)?; - WireFormat::encode(&self.other, _writer)?; - Ok(()) - }; - - assert_eq!( - encode_wire_format(&input.data).to_string(), - expected.to_string(), - ); - } - - #[test] - fn decode() { - let input: DeriveInput = parse_quote! { - struct Item { - ident: u32, - with_underscores: String, - other: u8, - } - }; - - let container = Ident::new("Item", Span::call_site()); - let expected = quote! { - let ident = WireFormat::decode(_reader)?; - let with_underscores = WireFormat::decode(_reader)?; - let other = WireFormat::decode(_reader)?; - Ok(Item { - ident: ident, - with_underscores: with_underscores, - other: other, - }) - }; - - assert_eq!( - decode_wire_format(&input.data, &container).to_string(), - expected.to_string(), - ); - } - - #[test] - fn end_to_end() { - let input: DeriveInput = parse_quote! { - struct Niijima_先輩 { - a: u8, - b: u16, - c: u32, - d: u64, - e: String, - f: Vec<String>, - g: Nested, - } - }; - - let expected = quote! { - mod wire_format_niijima_先輩 { - extern crate std; - use self::std::io; - use self::std::result::Result::Ok; - - use super::Niijima_先輩; - - use protocol::WireFormat; - - impl WireFormat for Niijima_先輩 { - fn byte_size(&self) -> u32 { - 0 - + WireFormat::byte_size(&self.a) - + WireFormat::byte_size(&self.b) - + WireFormat::byte_size(&self.c) - + WireFormat::byte_size(&self.d) - + WireFormat::byte_size(&self.e) - + WireFormat::byte_size(&self.f) - + WireFormat::byte_size(&self.g) - } - - fn encode<W: io::Write>(&self, _writer: &mut W) -> io::Result<()> { - WireFormat::encode(&self.a, _writer)?; - WireFormat::encode(&self.b, _writer)?; - WireFormat::encode(&self.c, _writer)?; - WireFormat::encode(&self.d, _writer)?; - WireFormat::encode(&self.e, _writer)?; - WireFormat::encode(&self.f, _writer)?; - WireFormat::encode(&self.g, _writer)?; - Ok(()) - } - fn decode<R: io::Read>(_reader: &mut R) -> io::Result<Self> { - let a = WireFormat::decode(_reader)?; - let b = WireFormat::decode(_reader)?; - let c = WireFormat::decode(_reader)?; - let d = WireFormat::decode(_reader)?; - let e = WireFormat::decode(_reader)?; - let f = WireFormat::decode(_reader)?; - let g = WireFormat::decode(_reader)?; - Ok(Niijima_先輩 { - a: a, - b: b, - c: c, - d: d, - e: e, - f: f, - g: g, - }) - } - } - } - }; - - assert_eq!( - p9_wire_format_inner(input).to_string(), - expected.to_string(), - ); - } -} |