├── .gitignore ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── OWNERS ├── README.md ├── p9_wire_format_derive ├── Cargo.toml ├── LICENSE └── src │ └── lib.rs └── src ├── fuzzing.rs ├── lib.rs ├── protocol ├── messages.rs ├── mod.rs └── wire_format.rs └── server ├── mod.rs ├── read_dir.rs └── tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We'd love to accept your patches and contributions to this project. 4 | 5 | ## Before you begin 6 | 7 | ### Sign our Contributor License Agreement 8 | 9 | Contributions to this project must be accompanied by a 10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 11 | You (or your employer) retain the copyright to your contribution; this simply 12 | gives us permission to use and redistribute your contributions as part of the 13 | project. 14 | 15 | If you or your current employer have already signed the Google CLA (even if it 16 | was for a different project), you probably don't need to do it again. 17 | 18 | Visit to see your current agreements or to 19 | sign a new one. 20 | 21 | ### Review our community guidelines 22 | 23 | This project follows 24 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 25 | 26 | ## Contribution process 27 | 28 | ### Code reviews 29 | 30 | All submissions, including submissions by project members, require review. We 31 | use GitHub pull requests for this purpose. Consult 32 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 33 | information on using pull requests. 34 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "libc" 7 | version = "0.2.147" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" 10 | 11 | [[package]] 12 | name = "p9" 13 | version = "0.3.2" 14 | dependencies = [ 15 | "libc", 16 | "p9_wire_format_derive", 17 | "serde", 18 | ] 19 | 20 | [[package]] 21 | name = "p9_wire_format_derive" 22 | version = "0.3.0" 23 | dependencies = [ 24 | "proc-macro2", 25 | "quote", 26 | "syn", 27 | ] 28 | 29 | [[package]] 30 | name = "proc-macro2" 31 | version = "1.0.66" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" 34 | dependencies = [ 35 | "unicode-ident", 36 | ] 37 | 38 | [[package]] 39 | name = "quote" 40 | version = "1.0.32" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" 43 | dependencies = [ 44 | "proc-macro2", 45 | ] 46 | 47 | [[package]] 48 | name = "serde" 49 | version = "1.0.177" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "63ba2516aa6bf82e0b19ca8b50019d52df58455d3cf9bdaf6315225fdd0c560a" 52 | dependencies = [ 53 | "serde_derive", 54 | ] 55 | 56 | [[package]] 57 | name = "serde_derive" 58 | version = "1.0.177" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "401797fe7833d72109fedec6bfcbe67c0eed9b99772f26eb8afd261f0abc6fd3" 61 | dependencies = [ 62 | "proc-macro2", 63 | "quote", 64 | "syn", 65 | ] 66 | 67 | [[package]] 68 | name = "syn" 69 | version = "2.0.27" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" 72 | dependencies = [ 73 | "proc-macro2", 74 | "quote", 75 | "unicode-ident", 76 | ] 77 | 78 | [[package]] 79 | name = "unicode-ident" 80 | version = "1.0.11" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" 83 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "p9" 3 | version = "0.3.2" 4 | authors = ["The ChromiumOS Authors"] 5 | edition = "2021" 6 | license = "BSD-3-Clause" 7 | description = "Server implementation of the 9p file system protocol" 8 | repository = "https://github.com/google/rust-p9" 9 | 10 | [target.'cfg(unix)'.dependencies] 11 | libc = "0.2" 12 | serde = { version = "1.0", features = ["derive"] } 13 | p9_wire_format_derive = { path = "p9_wire_format_derive", version = "0.3.0" } 14 | 15 | [features] 16 | trace = [] 17 | 18 | [workspace] 19 | members = ["p9_wire_format_derive"] 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The ChromiumOS Authors 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are 5 | // met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above 10 | // copyright notice, this list of conditions and the following disclaimer 11 | // in the documentation and/or other materials provided with the 12 | // distribution. 13 | // * Neither the name of Google Inc. nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | # This file exists so it can be passed to fuzzer_install in dev-rust/p9. 2 | denniskempin@google.com 3 | dverkamp@chromium.org 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # p9 - Server implementation of the [9p] file system protocol 2 | 3 | This directory contains the protocol definition and a server implementation of the [9p] file system 4 | protocol. 5 | 6 | - [wire_format_derive] - A [procedural macro] that derives the serialization and de-serialization 7 | implementation for a struct into the [9p] wire format. 8 | - [src/protocol] - Defines all the messages used in the [9p] protocol. Also implements serialization 9 | and de-serialization for some base types (integers, strings, vectors) that form the foundation of 10 | all [9p] messages. Wire format implementations for all other messages are derived using the 11 | `wire_format_derive` macro. 12 | - [src/server] - Implements a full [9p] server, carrying out file system requests on behalf of 13 | clients. 14 | 15 | [9p]: http://man.cat-v.org/plan_9/5/intro 16 | [procedural macro]: https://doc.rust-lang.org/proc_macro/index.html 17 | [src/protocol]: src/protocol/ 18 | [src/server]: src/server/ 19 | [wire_format_derive]: p9_wire_format_derive/ 20 | -------------------------------------------------------------------------------- /p9_wire_format_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "p9_wire_format_derive" 3 | version = "0.3.0" 4 | authors = ["The ChromiumOS Authors"] 5 | edition = "2021" 6 | license = "BSD-3-Clause" 7 | description = "Supporting proc-macro for the `p9` crate." 8 | repository = "https://github.com/google/rust-p9" 9 | readme = "../README.md" 10 | 11 | [dependencies] 12 | syn = "2" 13 | quote = "^1" 14 | proc-macro2 = "^1" 15 | 16 | [lib] 17 | proc-macro = true 18 | -------------------------------------------------------------------------------- /p9_wire_format_derive/LICENSE: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The ChromiumOS Authors 2 | // 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are 5 | // met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above 10 | // copyright notice, this list of conditions and the following disclaimer 11 | // in the documentation and/or other materials provided with the 12 | // distribution. 13 | // * Neither the name of Google Inc. nor the names of its 14 | // contributors may be used to endorse or promote products derived from 15 | // this software without specific prior written permission. 16 | // 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /p9_wire_format_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The ChromiumOS Authors 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | //! Derives a 9P wire format encoding for a struct by recursively calling 6 | //! `WireFormat::encode` or `WireFormat::decode` on the fields of the struct. 7 | //! This is only intended to be used from within the `p9` crate. 8 | 9 | #![recursion_limit = "256"] 10 | 11 | use proc_macro2::Span; 12 | use proc_macro2::TokenStream; 13 | use quote::quote; 14 | use quote::quote_spanned; 15 | use syn::parse_macro_input; 16 | use syn::spanned::Spanned; 17 | use syn::Data; 18 | use syn::DeriveInput; 19 | use syn::Fields; 20 | use syn::Ident; 21 | 22 | /// The function that derives the actual implementation. 23 | #[proc_macro_derive(P9WireFormat)] 24 | pub fn p9_wire_format(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 25 | let input = parse_macro_input!(input as DeriveInput); 26 | p9_wire_format_inner(input).into() 27 | } 28 | 29 | fn p9_wire_format_inner(input: DeriveInput) -> TokenStream { 30 | if !input.generics.params.is_empty() { 31 | return quote! { 32 | compile_error!("derive(P9WireFormat) does not support generic parameters"); 33 | }; 34 | } 35 | 36 | let container = input.ident; 37 | 38 | let byte_size_impl = byte_size_sum(&input.data); 39 | let encode_impl = encode_wire_format(&input.data); 40 | let decode_impl = decode_wire_format(&input.data, &container); 41 | 42 | let scope = format!("wire_format_{}", container).to_lowercase(); 43 | let scope = Ident::new(&scope, Span::call_site()); 44 | quote! { 45 | mod #scope { 46 | use ::std::io; 47 | use ::std::result::Result::Ok; 48 | 49 | use super::#container; 50 | 51 | use crate::protocol::WireFormat; 52 | 53 | impl WireFormat for #container { 54 | fn byte_size(&self) -> u32 { 55 | #byte_size_impl 56 | } 57 | 58 | fn encode(&self, _writer: &mut W) -> io::Result<()> { 59 | #encode_impl 60 | } 61 | 62 | fn decode(_reader: &mut R) -> io::Result { 63 | #decode_impl 64 | } 65 | } 66 | } 67 | } 68 | } 69 | 70 | // Generate code that recursively calls byte_size on every field in the struct. 71 | fn byte_size_sum(data: &Data) -> TokenStream { 72 | if let Data::Struct(ref data) = *data { 73 | if let Fields::Named(ref fields) = data.fields { 74 | let fields = fields.named.iter().map(|f| { 75 | let field = &f.ident; 76 | let span = field.span(); 77 | quote_spanned! {span=> 78 | WireFormat::byte_size(&self.#field) 79 | } 80 | }); 81 | 82 | quote! { 83 | 0 #(+ #fields)* 84 | } 85 | } else { 86 | unimplemented!(); 87 | } 88 | } else { 89 | unimplemented!(); 90 | } 91 | } 92 | 93 | // Generate code that recursively calls encode on every field in the struct. 94 | fn encode_wire_format(data: &Data) -> TokenStream { 95 | if let Data::Struct(ref data) = *data { 96 | if let Fields::Named(ref fields) = data.fields { 97 | let fields = fields.named.iter().map(|f| { 98 | let field = &f.ident; 99 | let span = field.span(); 100 | quote_spanned! {span=> 101 | WireFormat::encode(&self.#field, _writer)?; 102 | } 103 | }); 104 | 105 | quote! { 106 | #(#fields)* 107 | 108 | Ok(()) 109 | } 110 | } else { 111 | unimplemented!(); 112 | } 113 | } else { 114 | unimplemented!(); 115 | } 116 | } 117 | 118 | // Generate code that recursively calls decode on every field in the struct. 119 | fn decode_wire_format(data: &Data, container: &Ident) -> TokenStream { 120 | if let Data::Struct(ref data) = *data { 121 | if let Fields::Named(ref fields) = data.fields { 122 | let values = fields.named.iter().map(|f| { 123 | let field = &f.ident; 124 | let span = field.span(); 125 | quote_spanned! {span=> 126 | let #field = WireFormat::decode(_reader)?; 127 | } 128 | }); 129 | 130 | let members = fields.named.iter().map(|f| { 131 | let field = &f.ident; 132 | quote! { 133 | #field: #field, 134 | } 135 | }); 136 | 137 | quote! { 138 | #(#values)* 139 | 140 | Ok(#container { 141 | #(#members)* 142 | }) 143 | } 144 | } else { 145 | unimplemented!(); 146 | } 147 | } else { 148 | unimplemented!(); 149 | } 150 | } 151 | 152 | #[cfg(test)] 153 | mod tests { 154 | use syn::parse_quote; 155 | 156 | use super::*; 157 | 158 | #[test] 159 | fn byte_size() { 160 | let input: DeriveInput = parse_quote! { 161 | struct Item { 162 | ident: u32, 163 | with_underscores: String, 164 | other: u8, 165 | } 166 | }; 167 | 168 | let expected = quote! { 169 | 0 170 | + WireFormat::byte_size(&self.ident) 171 | + WireFormat::byte_size(&self.with_underscores) 172 | + WireFormat::byte_size(&self.other) 173 | }; 174 | 175 | assert_eq!(byte_size_sum(&input.data).to_string(), expected.to_string()); 176 | } 177 | 178 | #[test] 179 | fn encode() { 180 | let input: DeriveInput = parse_quote! { 181 | struct Item { 182 | ident: u32, 183 | with_underscores: String, 184 | other: u8, 185 | } 186 | }; 187 | 188 | let expected = quote! { 189 | WireFormat::encode(&self.ident, _writer)?; 190 | WireFormat::encode(&self.with_underscores, _writer)?; 191 | WireFormat::encode(&self.other, _writer)?; 192 | Ok(()) 193 | }; 194 | 195 | assert_eq!( 196 | encode_wire_format(&input.data).to_string(), 197 | expected.to_string(), 198 | ); 199 | } 200 | 201 | #[test] 202 | fn decode() { 203 | let input: DeriveInput = parse_quote! { 204 | struct Item { 205 | ident: u32, 206 | with_underscores: String, 207 | other: u8, 208 | } 209 | }; 210 | 211 | let container = Ident::new("Item", Span::call_site()); 212 | let expected = quote! { 213 | let ident = WireFormat::decode(_reader)?; 214 | let with_underscores = WireFormat::decode(_reader)?; 215 | let other = WireFormat::decode(_reader)?; 216 | Ok(Item { 217 | ident: ident, 218 | with_underscores: with_underscores, 219 | other: other, 220 | }) 221 | }; 222 | 223 | assert_eq!( 224 | decode_wire_format(&input.data, &container).to_string(), 225 | expected.to_string(), 226 | ); 227 | } 228 | 229 | #[test] 230 | fn end_to_end() { 231 | let input: DeriveInput = parse_quote! { 232 | struct Niijima_先輩 { 233 | a: u8, 234 | b: u16, 235 | c: u32, 236 | d: u64, 237 | e: String, 238 | f: Vec, 239 | g: Nested, 240 | } 241 | }; 242 | 243 | let expected = quote! { 244 | mod wire_format_niijima_先輩 { 245 | use ::std::io; 246 | use ::std::result::Result::Ok; 247 | 248 | use super::Niijima_先輩; 249 | 250 | use crate::protocol::WireFormat; 251 | 252 | impl WireFormat for Niijima_先輩 { 253 | fn byte_size(&self) -> u32 { 254 | 0 255 | + WireFormat::byte_size(&self.a) 256 | + WireFormat::byte_size(&self.b) 257 | + WireFormat::byte_size(&self.c) 258 | + WireFormat::byte_size(&self.d) 259 | + WireFormat::byte_size(&self.e) 260 | + WireFormat::byte_size(&self.f) 261 | + WireFormat::byte_size(&self.g) 262 | } 263 | 264 | fn encode(&self, _writer: &mut W) -> io::Result<()> { 265 | WireFormat::encode(&self.a, _writer)?; 266 | WireFormat::encode(&self.b, _writer)?; 267 | WireFormat::encode(&self.c, _writer)?; 268 | WireFormat::encode(&self.d, _writer)?; 269 | WireFormat::encode(&self.e, _writer)?; 270 | WireFormat::encode(&self.f, _writer)?; 271 | WireFormat::encode(&self.g, _writer)?; 272 | Ok(()) 273 | } 274 | fn decode(_reader: &mut R) -> io::Result { 275 | let a = WireFormat::decode(_reader)?; 276 | let b = WireFormat::decode(_reader)?; 277 | let c = WireFormat::decode(_reader)?; 278 | let d = WireFormat::decode(_reader)?; 279 | let e = WireFormat::decode(_reader)?; 280 | let f = WireFormat::decode(_reader)?; 281 | let g = WireFormat::decode(_reader)?; 282 | Ok(Niijima_先輩 { 283 | a: a, 284 | b: b, 285 | c: c, 286 | d: d, 287 | e: e, 288 | f: f, 289 | g: g, 290 | }) 291 | } 292 | } 293 | } 294 | }; 295 | 296 | assert_eq!( 297 | p9_wire_format_inner(input).to_string(), 298 | expected.to_string(), 299 | ); 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/fuzzing.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The ChromiumOS Authors 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std::io::Cursor; 6 | 7 | use crate::protocol::Tframe; 8 | use crate::protocol::WireFormat; 9 | 10 | pub fn tframe_decode(bytes: &[u8]) { 11 | let mut cursor = Cursor::new(bytes); 12 | 13 | while Tframe::decode(&mut cursor).is_ok() {} 14 | } 15 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The ChromiumOS Authors 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #![cfg(unix)] 6 | 7 | mod protocol; 8 | mod server; 9 | 10 | pub mod fuzzing; 11 | 12 | pub use server::*; 13 | 14 | #[macro_export] 15 | macro_rules! syscall { 16 | ($e:expr) => {{ 17 | let res = $e; 18 | if res < 0 { 19 | Err(std::io::Error::last_os_error()) 20 | } else { 21 | Ok(res) 22 | } 23 | }}; 24 | } 25 | -------------------------------------------------------------------------------- /src/protocol/messages.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The ChromiumOS Authors 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std::io; 6 | use std::io::ErrorKind; 7 | use std::io::Read; 8 | use std::io::Write; 9 | use std::mem; 10 | use std::vec::Vec; 11 | 12 | use p9_wire_format_derive::P9WireFormat; 13 | 14 | use crate::protocol::wire_format::Data; 15 | use crate::protocol::wire_format::P9String; 16 | use crate::protocol::wire_format::WireFormat; 17 | 18 | // Message type constants. Taken from "include/net/9p/9p.h" in the linux kernel 19 | // tree. The protocol specifies each R* message to be the corresponding T* 20 | // message plus one. 21 | const TLERROR: u8 = 6; 22 | const RLERROR: u8 = TLERROR + 1; 23 | const TSTATFS: u8 = 8; 24 | const RSTATFS: u8 = TSTATFS + 1; 25 | const TLOPEN: u8 = 12; 26 | const RLOPEN: u8 = TLOPEN + 1; 27 | const TLCREATE: u8 = 14; 28 | const RLCREATE: u8 = TLCREATE + 1; 29 | const TSYMLINK: u8 = 16; 30 | const RSYMLINK: u8 = TSYMLINK + 1; 31 | const TMKNOD: u8 = 18; 32 | const RMKNOD: u8 = TMKNOD + 1; 33 | const TRENAME: u8 = 20; 34 | const RRENAME: u8 = TRENAME + 1; 35 | const TREADLINK: u8 = 22; 36 | const RREADLINK: u8 = TREADLINK + 1; 37 | const TGETATTR: u8 = 24; 38 | const RGETATTR: u8 = TGETATTR + 1; 39 | const TSETATTR: u8 = 26; 40 | const RSETATTR: u8 = TSETATTR + 1; 41 | const TXATTRWALK: u8 = 30; 42 | const RXATTRWALK: u8 = TXATTRWALK + 1; 43 | const TXATTRCREATE: u8 = 32; 44 | const RXATTRCREATE: u8 = TXATTRCREATE + 1; 45 | const TREADDIR: u8 = 40; 46 | const RREADDIR: u8 = TREADDIR + 1; 47 | const TFSYNC: u8 = 50; 48 | const RFSYNC: u8 = TFSYNC + 1; 49 | const TLOCK: u8 = 52; 50 | const RLOCK: u8 = TLOCK + 1; 51 | const TGETLOCK: u8 = 54; 52 | const RGETLOCK: u8 = TGETLOCK + 1; 53 | const TLINK: u8 = 70; 54 | const RLINK: u8 = TLINK + 1; 55 | const TMKDIR: u8 = 72; 56 | const RMKDIR: u8 = TMKDIR + 1; 57 | const TRENAMEAT: u8 = 74; 58 | const RRENAMEAT: u8 = TRENAMEAT + 1; 59 | const TUNLINKAT: u8 = 76; 60 | const RUNLINKAT: u8 = TUNLINKAT + 1; 61 | const TVERSION: u8 = 100; 62 | const RVERSION: u8 = TVERSION + 1; 63 | const TAUTH: u8 = 102; 64 | const RAUTH: u8 = TAUTH + 1; 65 | const TATTACH: u8 = 104; 66 | const RATTACH: u8 = TATTACH + 1; 67 | const _TERROR: u8 = 106; 68 | const _RERROR: u8 = _TERROR + 1; 69 | const TFLUSH: u8 = 108; 70 | const RFLUSH: u8 = TFLUSH + 1; 71 | const TWALK: u8 = 110; 72 | const RWALK: u8 = TWALK + 1; 73 | const _TOPEN: u8 = 112; 74 | const _ROPEN: u8 = _TOPEN + 1; 75 | const _TCREATE: u8 = 114; 76 | const _RCREATE: u8 = _TCREATE + 1; 77 | const TREAD: u8 = 116; 78 | const RREAD: u8 = TREAD + 1; 79 | const TWRITE: u8 = 118; 80 | const RWRITE: u8 = TWRITE + 1; 81 | const TCLUNK: u8 = 120; 82 | const RCLUNK: u8 = TCLUNK + 1; 83 | const TREMOVE: u8 = 122; 84 | const RREMOVE: u8 = TREMOVE + 1; 85 | const _TSTAT: u8 = 124; 86 | const _RSTAT: u8 = _TSTAT + 1; 87 | const _TWSTAT: u8 = 126; 88 | const _RWSTAT: u8 = _TWSTAT + 1; 89 | 90 | /// A message sent from a 9P client to a 9P server. 91 | #[derive(Debug)] 92 | pub enum Tmessage { 93 | Version(Tversion), 94 | Flush(Tflush), 95 | Walk(Twalk), 96 | Read(Tread), 97 | Write(Twrite), 98 | Clunk(Tclunk), 99 | Remove(Tremove), 100 | Attach(Tattach), 101 | Auth(Tauth), 102 | Statfs(Tstatfs), 103 | Lopen(Tlopen), 104 | Lcreate(Tlcreate), 105 | Symlink(Tsymlink), 106 | Mknod(Tmknod), 107 | Rename(Trename), 108 | Readlink(Treadlink), 109 | GetAttr(Tgetattr), 110 | SetAttr(Tsetattr), 111 | XattrWalk(Txattrwalk), 112 | XattrCreate(Txattrcreate), 113 | Readdir(Treaddir), 114 | Fsync(Tfsync), 115 | Lock(Tlock), 116 | GetLock(Tgetlock), 117 | Link(Tlink), 118 | Mkdir(Tmkdir), 119 | RenameAt(Trenameat), 120 | UnlinkAt(Tunlinkat), 121 | } 122 | 123 | #[derive(Debug)] 124 | pub struct Tframe { 125 | pub tag: u16, 126 | pub msg: io::Result, 127 | } 128 | 129 | impl WireFormat for Tframe { 130 | fn byte_size(&self) -> u32 { 131 | let msg = self 132 | .msg 133 | .as_ref() 134 | .expect("tried to encode Tframe with invalid msg"); 135 | let msg_size = match msg { 136 | Tmessage::Version(ref version) => version.byte_size(), 137 | Tmessage::Flush(ref flush) => flush.byte_size(), 138 | Tmessage::Walk(ref walk) => walk.byte_size(), 139 | Tmessage::Read(ref read) => read.byte_size(), 140 | Tmessage::Write(ref write) => write.byte_size(), 141 | Tmessage::Clunk(ref clunk) => clunk.byte_size(), 142 | Tmessage::Remove(ref remove) => remove.byte_size(), 143 | Tmessage::Attach(ref attach) => attach.byte_size(), 144 | Tmessage::Auth(ref auth) => auth.byte_size(), 145 | Tmessage::Statfs(ref statfs) => statfs.byte_size(), 146 | Tmessage::Lopen(ref lopen) => lopen.byte_size(), 147 | Tmessage::Lcreate(ref lcreate) => lcreate.byte_size(), 148 | Tmessage::Symlink(ref symlink) => symlink.byte_size(), 149 | Tmessage::Mknod(ref mknod) => mknod.byte_size(), 150 | Tmessage::Rename(ref rename) => rename.byte_size(), 151 | Tmessage::Readlink(ref readlink) => readlink.byte_size(), 152 | Tmessage::GetAttr(ref getattr) => getattr.byte_size(), 153 | Tmessage::SetAttr(ref setattr) => setattr.byte_size(), 154 | Tmessage::XattrWalk(ref xattrwalk) => xattrwalk.byte_size(), 155 | Tmessage::XattrCreate(ref xattrcreate) => xattrcreate.byte_size(), 156 | Tmessage::Readdir(ref readdir) => readdir.byte_size(), 157 | Tmessage::Fsync(ref fsync) => fsync.byte_size(), 158 | Tmessage::Lock(ref lock) => lock.byte_size(), 159 | Tmessage::GetLock(ref getlock) => getlock.byte_size(), 160 | Tmessage::Link(ref link) => link.byte_size(), 161 | Tmessage::Mkdir(ref mkdir) => mkdir.byte_size(), 162 | Tmessage::RenameAt(ref renameat) => renameat.byte_size(), 163 | Tmessage::UnlinkAt(ref unlinkat) => unlinkat.byte_size(), 164 | }; 165 | 166 | // size + type + tag + message size 167 | (mem::size_of::() + mem::size_of::() + mem::size_of::()) as u32 + msg_size 168 | } 169 | 170 | fn encode(&self, writer: &mut W) -> io::Result<()> { 171 | let msg = match self.msg.as_ref() { 172 | Ok(msg) => msg, 173 | Err(_) => { 174 | return Err(io::Error::new( 175 | io::ErrorKind::InvalidData, 176 | "tried to encode Tframe with invalid msg", 177 | )) 178 | } 179 | }; 180 | 181 | self.byte_size().encode(writer)?; 182 | 183 | let ty = match msg { 184 | Tmessage::Version(_) => TVERSION, 185 | Tmessage::Flush(_) => TFLUSH, 186 | Tmessage::Walk(_) => TWALK, 187 | Tmessage::Read(_) => TREAD, 188 | Tmessage::Write(_) => TWRITE, 189 | Tmessage::Clunk(_) => TCLUNK, 190 | Tmessage::Remove(_) => TREMOVE, 191 | Tmessage::Attach(_) => TATTACH, 192 | Tmessage::Auth(_) => TAUTH, 193 | Tmessage::Statfs(_) => TSTATFS, 194 | Tmessage::Lopen(_) => TLOPEN, 195 | Tmessage::Lcreate(_) => TLCREATE, 196 | Tmessage::Symlink(_) => TSYMLINK, 197 | Tmessage::Mknod(_) => TMKNOD, 198 | Tmessage::Rename(_) => TRENAME, 199 | Tmessage::Readlink(_) => TREADLINK, 200 | Tmessage::GetAttr(_) => TGETATTR, 201 | Tmessage::SetAttr(_) => TSETATTR, 202 | Tmessage::XattrWalk(_) => TXATTRWALK, 203 | Tmessage::XattrCreate(_) => TXATTRCREATE, 204 | Tmessage::Readdir(_) => TREADDIR, 205 | Tmessage::Fsync(_) => TFSYNC, 206 | Tmessage::Lock(_) => TLOCK, 207 | Tmessage::GetLock(_) => TGETLOCK, 208 | Tmessage::Link(_) => TLINK, 209 | Tmessage::Mkdir(_) => TMKDIR, 210 | Tmessage::RenameAt(_) => TRENAMEAT, 211 | Tmessage::UnlinkAt(_) => TUNLINKAT, 212 | }; 213 | 214 | ty.encode(writer)?; 215 | self.tag.encode(writer)?; 216 | 217 | match msg { 218 | Tmessage::Version(ref version) => version.encode(writer), 219 | Tmessage::Flush(ref flush) => flush.encode(writer), 220 | Tmessage::Walk(ref walk) => walk.encode(writer), 221 | Tmessage::Read(ref read) => read.encode(writer), 222 | Tmessage::Write(ref write) => write.encode(writer), 223 | Tmessage::Clunk(ref clunk) => clunk.encode(writer), 224 | Tmessage::Remove(ref remove) => remove.encode(writer), 225 | Tmessage::Attach(ref attach) => attach.encode(writer), 226 | Tmessage::Auth(ref auth) => auth.encode(writer), 227 | Tmessage::Statfs(ref statfs) => statfs.encode(writer), 228 | Tmessage::Lopen(ref lopen) => lopen.encode(writer), 229 | Tmessage::Lcreate(ref lcreate) => lcreate.encode(writer), 230 | Tmessage::Symlink(ref symlink) => symlink.encode(writer), 231 | Tmessage::Mknod(ref mknod) => mknod.encode(writer), 232 | Tmessage::Rename(ref rename) => rename.encode(writer), 233 | Tmessage::Readlink(ref readlink) => readlink.encode(writer), 234 | Tmessage::GetAttr(ref getattr) => getattr.encode(writer), 235 | Tmessage::SetAttr(ref setattr) => setattr.encode(writer), 236 | Tmessage::XattrWalk(ref xattrwalk) => xattrwalk.encode(writer), 237 | Tmessage::XattrCreate(ref xattrcreate) => xattrcreate.encode(writer), 238 | Tmessage::Readdir(ref readdir) => readdir.encode(writer), 239 | Tmessage::Fsync(ref fsync) => fsync.encode(writer), 240 | Tmessage::Lock(ref lock) => lock.encode(writer), 241 | Tmessage::GetLock(ref getlock) => getlock.encode(writer), 242 | Tmessage::Link(ref link) => link.encode(writer), 243 | Tmessage::Mkdir(ref mkdir) => mkdir.encode(writer), 244 | Tmessage::RenameAt(ref renameat) => renameat.encode(writer), 245 | Tmessage::UnlinkAt(ref unlinkat) => unlinkat.encode(writer), 246 | } 247 | } 248 | 249 | fn decode(reader: &mut R) -> io::Result { 250 | let byte_size: u32 = WireFormat::decode(reader)?; 251 | 252 | // byte_size includes the size of byte_size so remove that from the 253 | // expected length of the message. Also make sure that byte_size is at least 254 | // that long to begin with. 255 | if byte_size < mem::size_of::() as u32 { 256 | return Err(io::Error::new( 257 | ErrorKind::InvalidData, 258 | format!("byte_size(= {}) is less than 4 bytes", byte_size), 259 | )); 260 | } 261 | 262 | let reader = &mut reader.take((byte_size - mem::size_of::() as u32) as u64); 263 | 264 | let mut ty = [0u8]; 265 | reader.read_exact(&mut ty)?; 266 | 267 | let tag: u16 = WireFormat::decode(reader)?; 268 | let msg = Self::decode_message(reader, ty[0]); 269 | 270 | Ok(Tframe { tag, msg }) 271 | } 272 | } 273 | 274 | impl Tframe { 275 | fn decode_message(reader: &mut R, ty: u8) -> io::Result { 276 | match ty { 277 | TVERSION => Ok(Tmessage::Version(WireFormat::decode(reader)?)), 278 | TFLUSH => Ok(Tmessage::Flush(WireFormat::decode(reader)?)), 279 | TWALK => Ok(Tmessage::Walk(WireFormat::decode(reader)?)), 280 | TREAD => Ok(Tmessage::Read(WireFormat::decode(reader)?)), 281 | TWRITE => Ok(Tmessage::Write(WireFormat::decode(reader)?)), 282 | TCLUNK => Ok(Tmessage::Clunk(WireFormat::decode(reader)?)), 283 | TREMOVE => Ok(Tmessage::Remove(WireFormat::decode(reader)?)), 284 | TATTACH => Ok(Tmessage::Attach(WireFormat::decode(reader)?)), 285 | TAUTH => Ok(Tmessage::Auth(WireFormat::decode(reader)?)), 286 | TSTATFS => Ok(Tmessage::Statfs(WireFormat::decode(reader)?)), 287 | TLOPEN => Ok(Tmessage::Lopen(WireFormat::decode(reader)?)), 288 | TLCREATE => Ok(Tmessage::Lcreate(WireFormat::decode(reader)?)), 289 | TSYMLINK => Ok(Tmessage::Symlink(WireFormat::decode(reader)?)), 290 | TMKNOD => Ok(Tmessage::Mknod(WireFormat::decode(reader)?)), 291 | TRENAME => Ok(Tmessage::Rename(WireFormat::decode(reader)?)), 292 | TREADLINK => Ok(Tmessage::Readlink(WireFormat::decode(reader)?)), 293 | TGETATTR => Ok(Tmessage::GetAttr(WireFormat::decode(reader)?)), 294 | TSETATTR => Ok(Tmessage::SetAttr(WireFormat::decode(reader)?)), 295 | TXATTRWALK => Ok(Tmessage::XattrWalk(WireFormat::decode(reader)?)), 296 | TXATTRCREATE => Ok(Tmessage::XattrCreate(WireFormat::decode(reader)?)), 297 | TREADDIR => Ok(Tmessage::Readdir(WireFormat::decode(reader)?)), 298 | TFSYNC => Ok(Tmessage::Fsync(WireFormat::decode(reader)?)), 299 | TLOCK => Ok(Tmessage::Lock(WireFormat::decode(reader)?)), 300 | TGETLOCK => Ok(Tmessage::GetLock(WireFormat::decode(reader)?)), 301 | TLINK => Ok(Tmessage::Link(WireFormat::decode(reader)?)), 302 | TMKDIR => Ok(Tmessage::Mkdir(WireFormat::decode(reader)?)), 303 | TRENAMEAT => Ok(Tmessage::RenameAt(WireFormat::decode(reader)?)), 304 | TUNLINKAT => Ok(Tmessage::UnlinkAt(WireFormat::decode(reader)?)), 305 | err => Err(io::Error::new( 306 | ErrorKind::InvalidData, 307 | format!("unknown message type {}", err), 308 | )), 309 | } 310 | } 311 | } 312 | 313 | #[derive(Debug, P9WireFormat)] 314 | pub struct Tversion { 315 | pub msize: u32, 316 | pub version: P9String, 317 | } 318 | 319 | #[derive(Debug, P9WireFormat)] 320 | pub struct Tflush { 321 | pub oldtag: u16, 322 | } 323 | 324 | #[derive(Debug, P9WireFormat)] 325 | pub struct Twalk { 326 | pub fid: u32, 327 | pub newfid: u32, 328 | pub wnames: Vec, 329 | } 330 | 331 | #[derive(Debug, P9WireFormat)] 332 | pub struct Tread { 333 | pub fid: u32, 334 | pub offset: u64, 335 | pub count: u32, 336 | } 337 | 338 | #[derive(Debug, P9WireFormat)] 339 | pub struct Twrite { 340 | pub fid: u32, 341 | pub offset: u64, 342 | pub data: Data, 343 | } 344 | 345 | #[derive(Debug, P9WireFormat)] 346 | pub struct Tclunk { 347 | pub fid: u32, 348 | } 349 | 350 | #[derive(Debug, P9WireFormat)] 351 | pub struct Tremove { 352 | pub fid: u32, 353 | } 354 | 355 | #[derive(Debug, P9WireFormat)] 356 | pub struct Tauth { 357 | pub afid: u32, 358 | pub uname: P9String, 359 | pub aname: P9String, 360 | pub n_uname: u32, 361 | } 362 | 363 | #[derive(Debug, P9WireFormat)] 364 | pub struct Tattach { 365 | pub fid: u32, 366 | pub afid: u32, 367 | pub uname: P9String, 368 | pub aname: P9String, 369 | pub n_uname: u32, 370 | } 371 | 372 | #[derive(Debug, P9WireFormat)] 373 | pub struct Tstatfs { 374 | pub fid: u32, 375 | } 376 | 377 | #[derive(Debug, P9WireFormat)] 378 | pub struct Tlopen { 379 | pub fid: u32, 380 | pub flags: u32, 381 | } 382 | 383 | #[derive(Debug, P9WireFormat)] 384 | pub struct Tlcreate { 385 | pub fid: u32, 386 | pub name: P9String, 387 | pub flags: u32, 388 | pub mode: u32, 389 | pub gid: u32, 390 | } 391 | 392 | #[derive(Debug, P9WireFormat)] 393 | pub struct Tsymlink { 394 | pub fid: u32, 395 | pub name: P9String, 396 | pub symtgt: P9String, 397 | pub gid: u32, 398 | } 399 | 400 | #[derive(Debug, P9WireFormat)] 401 | pub struct Tmknod { 402 | pub dfid: u32, 403 | pub name: P9String, 404 | pub mode: u32, 405 | pub major: u32, 406 | pub minor: u32, 407 | pub gid: u32, 408 | } 409 | 410 | #[derive(Debug, P9WireFormat)] 411 | pub struct Trename { 412 | pub fid: u32, 413 | pub dfid: u32, 414 | pub name: P9String, 415 | } 416 | 417 | #[derive(Debug, P9WireFormat)] 418 | pub struct Treadlink { 419 | pub fid: u32, 420 | } 421 | 422 | #[derive(Debug, P9WireFormat)] 423 | pub struct Tgetattr { 424 | pub fid: u32, 425 | pub request_mask: u64, 426 | } 427 | 428 | #[derive(Debug, P9WireFormat)] 429 | pub struct Tsetattr { 430 | pub fid: u32, 431 | pub valid: u32, 432 | pub mode: u32, 433 | pub uid: u32, 434 | pub gid: u32, 435 | pub size: u64, 436 | pub atime_sec: u64, 437 | pub atime_nsec: u64, 438 | pub mtime_sec: u64, 439 | pub mtime_nsec: u64, 440 | } 441 | 442 | #[derive(Debug, P9WireFormat)] 443 | pub struct Txattrwalk { 444 | pub fid: u32, 445 | pub newfid: u32, 446 | pub name: P9String, 447 | } 448 | 449 | #[derive(Debug, P9WireFormat)] 450 | pub struct Txattrcreate { 451 | pub fid: u32, 452 | pub name: P9String, 453 | pub attr_size: u64, 454 | pub flags: u32, 455 | } 456 | 457 | #[derive(Debug, P9WireFormat)] 458 | pub struct Treaddir { 459 | pub fid: u32, 460 | pub offset: u64, 461 | pub count: u32, 462 | } 463 | 464 | #[derive(Debug, P9WireFormat)] 465 | pub struct Tfsync { 466 | pub fid: u32, 467 | pub datasync: u32, 468 | } 469 | 470 | #[derive(Debug, P9WireFormat)] 471 | pub struct Tlock { 472 | pub fid: u32, 473 | pub type_: u8, 474 | pub flags: u32, 475 | pub start: u64, 476 | pub length: u64, 477 | pub proc_id: u32, 478 | pub client_id: P9String, 479 | } 480 | 481 | #[derive(Debug, P9WireFormat)] 482 | pub struct Tgetlock { 483 | pub fid: u32, 484 | pub type_: u8, 485 | pub start: u64, 486 | pub length: u64, 487 | pub proc_id: u32, 488 | pub client_id: P9String, 489 | } 490 | 491 | #[derive(Debug, P9WireFormat)] 492 | pub struct Tlink { 493 | pub dfid: u32, 494 | pub fid: u32, 495 | pub name: P9String, 496 | } 497 | 498 | #[derive(Debug, P9WireFormat)] 499 | pub struct Tmkdir { 500 | pub dfid: u32, 501 | pub name: P9String, 502 | pub mode: u32, 503 | pub gid: u32, 504 | } 505 | 506 | #[derive(Debug, P9WireFormat)] 507 | pub struct Trenameat { 508 | pub olddirfid: u32, 509 | pub oldname: P9String, 510 | pub newdirfid: u32, 511 | pub newname: P9String, 512 | } 513 | 514 | #[derive(Debug, P9WireFormat)] 515 | pub struct Tunlinkat { 516 | pub dirfd: u32, 517 | pub name: P9String, 518 | pub flags: u32, 519 | } 520 | 521 | /// A message sent from a 9P server to a 9P client in response to a request from 522 | /// that client. Encapsulates a full frame. 523 | #[derive(Debug)] 524 | pub enum Rmessage { 525 | Version(Rversion), 526 | Flush, 527 | Walk(Rwalk), 528 | Read(Rread), 529 | Write(Rwrite), 530 | Clunk, 531 | Remove, 532 | Attach(Rattach), 533 | Auth(Rauth), 534 | Statfs(Rstatfs), 535 | Lopen(Rlopen), 536 | Lcreate(Rlcreate), 537 | Symlink(Rsymlink), 538 | Mknod(Rmknod), 539 | Rename, 540 | Readlink(Rreadlink), 541 | GetAttr(Rgetattr), 542 | SetAttr, 543 | XattrWalk(Rxattrwalk), 544 | XattrCreate, 545 | Readdir(Rreaddir), 546 | Fsync, 547 | Lock(Rlock), 548 | GetLock(Rgetlock), 549 | Link, 550 | Mkdir(Rmkdir), 551 | RenameAt, 552 | UnlinkAt, 553 | Lerror(Rlerror), 554 | } 555 | 556 | #[derive(Debug)] 557 | pub struct Rframe { 558 | pub tag: u16, 559 | pub msg: Rmessage, 560 | } 561 | 562 | impl WireFormat for Rframe { 563 | fn byte_size(&self) -> u32 { 564 | let msg_size = match self.msg { 565 | Rmessage::Version(ref version) => version.byte_size(), 566 | Rmessage::Flush => 0, 567 | Rmessage::Walk(ref walk) => walk.byte_size(), 568 | Rmessage::Read(ref read) => read.byte_size(), 569 | Rmessage::Write(ref write) => write.byte_size(), 570 | Rmessage::Clunk => 0, 571 | Rmessage::Remove => 0, 572 | Rmessage::Attach(ref attach) => attach.byte_size(), 573 | Rmessage::Auth(ref auth) => auth.byte_size(), 574 | Rmessage::Statfs(ref statfs) => statfs.byte_size(), 575 | Rmessage::Lopen(ref lopen) => lopen.byte_size(), 576 | Rmessage::Lcreate(ref lcreate) => lcreate.byte_size(), 577 | Rmessage::Symlink(ref symlink) => symlink.byte_size(), 578 | Rmessage::Mknod(ref mknod) => mknod.byte_size(), 579 | Rmessage::Rename => 0, 580 | Rmessage::Readlink(ref readlink) => readlink.byte_size(), 581 | Rmessage::GetAttr(ref getattr) => getattr.byte_size(), 582 | Rmessage::SetAttr => 0, 583 | Rmessage::XattrWalk(ref xattrwalk) => xattrwalk.byte_size(), 584 | Rmessage::XattrCreate => 0, 585 | Rmessage::Readdir(ref readdir) => readdir.byte_size(), 586 | Rmessage::Fsync => 0, 587 | Rmessage::Lock(ref lock) => lock.byte_size(), 588 | Rmessage::GetLock(ref getlock) => getlock.byte_size(), 589 | Rmessage::Link => 0, 590 | Rmessage::Mkdir(ref mkdir) => mkdir.byte_size(), 591 | Rmessage::RenameAt => 0, 592 | Rmessage::UnlinkAt => 0, 593 | Rmessage::Lerror(ref lerror) => lerror.byte_size(), 594 | }; 595 | 596 | // size + type + tag + message size 597 | (mem::size_of::() + mem::size_of::() + mem::size_of::()) as u32 + msg_size 598 | } 599 | 600 | fn encode(&self, writer: &mut W) -> io::Result<()> { 601 | self.byte_size().encode(writer)?; 602 | 603 | let ty = match self.msg { 604 | Rmessage::Version(_) => RVERSION, 605 | Rmessage::Flush => RFLUSH, 606 | Rmessage::Walk(_) => RWALK, 607 | Rmessage::Read(_) => RREAD, 608 | Rmessage::Write(_) => RWRITE, 609 | Rmessage::Clunk => RCLUNK, 610 | Rmessage::Remove => RREMOVE, 611 | Rmessage::Attach(_) => RATTACH, 612 | Rmessage::Auth(_) => RAUTH, 613 | Rmessage::Statfs(_) => RSTATFS, 614 | Rmessage::Lopen(_) => RLOPEN, 615 | Rmessage::Lcreate(_) => RLCREATE, 616 | Rmessage::Symlink(_) => RSYMLINK, 617 | Rmessage::Mknod(_) => RMKNOD, 618 | Rmessage::Rename => RRENAME, 619 | Rmessage::Readlink(_) => RREADLINK, 620 | Rmessage::GetAttr(_) => RGETATTR, 621 | Rmessage::SetAttr => RSETATTR, 622 | Rmessage::XattrWalk(_) => RXATTRWALK, 623 | Rmessage::XattrCreate => RXATTRCREATE, 624 | Rmessage::Readdir(_) => RREADDIR, 625 | Rmessage::Fsync => RFSYNC, 626 | Rmessage::Lock(_) => RLOCK, 627 | Rmessage::GetLock(_) => RGETLOCK, 628 | Rmessage::Link => RLINK, 629 | Rmessage::Mkdir(_) => RMKDIR, 630 | Rmessage::RenameAt => RRENAMEAT, 631 | Rmessage::UnlinkAt => RUNLINKAT, 632 | Rmessage::Lerror(_) => RLERROR, 633 | }; 634 | 635 | ty.encode(writer)?; 636 | self.tag.encode(writer)?; 637 | 638 | match self.msg { 639 | Rmessage::Version(ref version) => version.encode(writer), 640 | Rmessage::Flush => Ok(()), 641 | Rmessage::Walk(ref walk) => walk.encode(writer), 642 | Rmessage::Read(ref read) => read.encode(writer), 643 | Rmessage::Write(ref write) => write.encode(writer), 644 | Rmessage::Clunk => Ok(()), 645 | Rmessage::Remove => Ok(()), 646 | Rmessage::Attach(ref attach) => attach.encode(writer), 647 | Rmessage::Auth(ref auth) => auth.encode(writer), 648 | Rmessage::Statfs(ref statfs) => statfs.encode(writer), 649 | Rmessage::Lopen(ref lopen) => lopen.encode(writer), 650 | Rmessage::Lcreate(ref lcreate) => lcreate.encode(writer), 651 | Rmessage::Symlink(ref symlink) => symlink.encode(writer), 652 | Rmessage::Mknod(ref mknod) => mknod.encode(writer), 653 | Rmessage::Rename => Ok(()), 654 | Rmessage::Readlink(ref readlink) => readlink.encode(writer), 655 | Rmessage::GetAttr(ref getattr) => getattr.encode(writer), 656 | Rmessage::SetAttr => Ok(()), 657 | Rmessage::XattrWalk(ref xattrwalk) => xattrwalk.encode(writer), 658 | Rmessage::XattrCreate => Ok(()), 659 | Rmessage::Readdir(ref readdir) => readdir.encode(writer), 660 | Rmessage::Fsync => Ok(()), 661 | Rmessage::Lock(ref lock) => lock.encode(writer), 662 | Rmessage::GetLock(ref getlock) => getlock.encode(writer), 663 | Rmessage::Link => Ok(()), 664 | Rmessage::Mkdir(ref mkdir) => mkdir.encode(writer), 665 | Rmessage::RenameAt => Ok(()), 666 | Rmessage::UnlinkAt => Ok(()), 667 | Rmessage::Lerror(ref lerror) => lerror.encode(writer), 668 | } 669 | } 670 | 671 | fn decode(reader: &mut R) -> io::Result { 672 | let byte_size: u32 = WireFormat::decode(reader)?; 673 | 674 | // byte_size includes the size of byte_size so remove that from the 675 | // expected length of the message. 676 | let reader = &mut reader.take((byte_size - mem::size_of::() as u32) as u64); 677 | 678 | let mut ty = [0u8]; 679 | reader.read_exact(&mut ty)?; 680 | 681 | let tag: u16 = WireFormat::decode(reader)?; 682 | 683 | let msg = match ty[0] { 684 | RVERSION => Ok(Rmessage::Version(WireFormat::decode(reader)?)), 685 | RFLUSH => Ok(Rmessage::Flush), 686 | RWALK => Ok(Rmessage::Walk(WireFormat::decode(reader)?)), 687 | RREAD => Ok(Rmessage::Read(WireFormat::decode(reader)?)), 688 | RWRITE => Ok(Rmessage::Write(WireFormat::decode(reader)?)), 689 | RCLUNK => Ok(Rmessage::Clunk), 690 | RREMOVE => Ok(Rmessage::Remove), 691 | RATTACH => Ok(Rmessage::Attach(WireFormat::decode(reader)?)), 692 | RAUTH => Ok(Rmessage::Auth(WireFormat::decode(reader)?)), 693 | RSTATFS => Ok(Rmessage::Statfs(WireFormat::decode(reader)?)), 694 | RLOPEN => Ok(Rmessage::Lopen(WireFormat::decode(reader)?)), 695 | RLCREATE => Ok(Rmessage::Lcreate(WireFormat::decode(reader)?)), 696 | RSYMLINK => Ok(Rmessage::Symlink(WireFormat::decode(reader)?)), 697 | RMKNOD => Ok(Rmessage::Mknod(WireFormat::decode(reader)?)), 698 | RRENAME => Ok(Rmessage::Rename), 699 | RREADLINK => Ok(Rmessage::Readlink(WireFormat::decode(reader)?)), 700 | RGETATTR => Ok(Rmessage::GetAttr(WireFormat::decode(reader)?)), 701 | RSETATTR => Ok(Rmessage::SetAttr), 702 | RXATTRWALK => Ok(Rmessage::XattrWalk(WireFormat::decode(reader)?)), 703 | RXATTRCREATE => Ok(Rmessage::XattrCreate), 704 | RREADDIR => Ok(Rmessage::Readdir(WireFormat::decode(reader)?)), 705 | RFSYNC => Ok(Rmessage::Fsync), 706 | RLOCK => Ok(Rmessage::Lock(WireFormat::decode(reader)?)), 707 | RGETLOCK => Ok(Rmessage::GetLock(WireFormat::decode(reader)?)), 708 | RLINK => Ok(Rmessage::Link), 709 | RMKDIR => Ok(Rmessage::Mkdir(WireFormat::decode(reader)?)), 710 | RRENAMEAT => Ok(Rmessage::RenameAt), 711 | RUNLINKAT => Ok(Rmessage::UnlinkAt), 712 | RLERROR => Ok(Rmessage::Lerror(WireFormat::decode(reader)?)), 713 | err => Err(io::Error::new( 714 | ErrorKind::InvalidData, 715 | format!("unknown message type {}", err), 716 | )), 717 | }?; 718 | 719 | Ok(Rframe { tag, msg }) 720 | } 721 | } 722 | 723 | #[derive(Debug, Copy, Clone, P9WireFormat)] 724 | pub struct Qid { 725 | pub ty: u8, 726 | pub version: u32, 727 | pub path: u64, 728 | } 729 | 730 | #[derive(Debug, P9WireFormat)] 731 | pub struct Dirent { 732 | pub qid: Qid, 733 | pub offset: u64, 734 | pub ty: u8, 735 | pub name: P9String, 736 | } 737 | 738 | #[derive(Debug, P9WireFormat)] 739 | pub struct Rversion { 740 | pub msize: u32, 741 | pub version: P9String, 742 | } 743 | 744 | #[derive(Debug, P9WireFormat)] 745 | pub struct Rwalk { 746 | pub wqids: Vec, 747 | } 748 | 749 | #[derive(Debug, P9WireFormat)] 750 | pub struct Rread { 751 | pub data: Data, 752 | } 753 | 754 | #[derive(Debug, P9WireFormat)] 755 | pub struct Rwrite { 756 | pub count: u32, 757 | } 758 | 759 | #[derive(Debug, P9WireFormat)] 760 | pub struct Rauth { 761 | pub aqid: Qid, 762 | } 763 | 764 | #[derive(Debug, P9WireFormat)] 765 | pub struct Rattach { 766 | pub qid: Qid, 767 | } 768 | 769 | #[derive(Debug, P9WireFormat)] 770 | pub struct Rlerror { 771 | pub ecode: u32, 772 | } 773 | 774 | #[derive(Debug, P9WireFormat)] 775 | pub struct Rstatfs { 776 | pub ty: u32, 777 | pub bsize: u32, 778 | pub blocks: u64, 779 | pub bfree: u64, 780 | pub bavail: u64, 781 | pub files: u64, 782 | pub ffree: u64, 783 | pub fsid: u64, 784 | pub namelen: u32, 785 | } 786 | 787 | #[derive(Debug, P9WireFormat)] 788 | pub struct Rlopen { 789 | pub qid: Qid, 790 | pub iounit: u32, 791 | } 792 | 793 | #[derive(Debug, P9WireFormat)] 794 | pub struct Rlcreate { 795 | pub qid: Qid, 796 | pub iounit: u32, 797 | } 798 | 799 | #[derive(Debug, P9WireFormat)] 800 | pub struct Rsymlink { 801 | pub qid: Qid, 802 | } 803 | 804 | #[derive(Debug, P9WireFormat)] 805 | pub struct Rmknod { 806 | pub qid: Qid, 807 | } 808 | 809 | #[derive(Debug, P9WireFormat)] 810 | pub struct Rreadlink { 811 | pub target: P9String, 812 | } 813 | 814 | #[derive(Debug, P9WireFormat)] 815 | pub struct Rgetattr { 816 | pub valid: u64, 817 | pub qid: Qid, 818 | pub mode: u32, 819 | pub uid: u32, 820 | pub gid: u32, 821 | pub nlink: u64, 822 | pub rdev: u64, 823 | pub size: u64, 824 | pub blksize: u64, 825 | pub blocks: u64, 826 | pub atime_sec: u64, 827 | pub atime_nsec: u64, 828 | pub mtime_sec: u64, 829 | pub mtime_nsec: u64, 830 | pub ctime_sec: u64, 831 | pub ctime_nsec: u64, 832 | pub btime_sec: u64, 833 | pub btime_nsec: u64, 834 | pub gen: u64, 835 | pub data_version: u64, 836 | } 837 | 838 | #[derive(Debug, P9WireFormat)] 839 | pub struct Rxattrwalk { 840 | pub size: u64, 841 | } 842 | 843 | #[derive(Debug, P9WireFormat)] 844 | pub struct Rreaddir { 845 | pub data: Data, 846 | } 847 | 848 | #[derive(Debug, P9WireFormat)] 849 | pub struct Rlock { 850 | pub status: u8, 851 | } 852 | 853 | #[derive(Debug, P9WireFormat)] 854 | pub struct Rgetlock { 855 | pub type_: u8, 856 | pub start: u64, 857 | pub length: u64, 858 | pub proc_id: u32, 859 | pub client_id: P9String, 860 | } 861 | 862 | #[derive(Debug, P9WireFormat)] 863 | pub struct Rmkdir { 864 | pub qid: Qid, 865 | } 866 | -------------------------------------------------------------------------------- /src/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The ChromiumOS Authors 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | mod messages; 6 | mod wire_format; 7 | 8 | pub use self::messages::*; 9 | pub use self::wire_format::Data; 10 | pub use self::wire_format::P9String; 11 | pub use self::wire_format::WireFormat; 12 | -------------------------------------------------------------------------------- /src/protocol/wire_format.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The ChromiumOS Authors 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std::ffi::CStr; 6 | use std::ffi::CString; 7 | use std::ffi::OsStr; 8 | use std::fmt; 9 | use std::io; 10 | use std::io::ErrorKind; 11 | use std::io::Read; 12 | use std::io::Write; 13 | use std::mem; 14 | use std::ops::Deref; 15 | use std::ops::DerefMut; 16 | use std::vec::Vec; 17 | 18 | /// A type that can be encoded on the wire using the 9P protocol. 19 | pub trait WireFormat: std::marker::Sized { 20 | /// Returns the number of bytes necessary to fully encode `self`. 21 | fn byte_size(&self) -> u32; 22 | 23 | /// Encodes `self` into `writer`. 24 | fn encode(&self, writer: &mut W) -> io::Result<()>; 25 | 26 | /// Decodes `Self` from `reader`. 27 | fn decode(reader: &mut R) -> io::Result; 28 | } 29 | 30 | // This doesn't really _need_ to be a macro but unfortunately there is no trait bound to 31 | // express "can be casted to another type", which means we can't write `T as u8` in a trait 32 | // based implementation. So instead we have this macro, which is implemented for all the 33 | // stable unsigned types with the added benefit of not being implemented for the signed 34 | // types which are not allowed by the protocol. 35 | macro_rules! uint_wire_format_impl { 36 | ($Ty:ty) => { 37 | impl WireFormat for $Ty { 38 | fn byte_size(&self) -> u32 { 39 | mem::size_of::<$Ty>() as u32 40 | } 41 | 42 | fn encode(&self, writer: &mut W) -> io::Result<()> { 43 | // Encode the bytes into the buffer in little endian order. 44 | writer.write_all(&<$Ty>::to_le_bytes(*self)) 45 | } 46 | 47 | fn decode(reader: &mut R) -> io::Result { 48 | let mut buf = [0u8; mem::size_of::<$Ty>()]; 49 | reader.read_exact(&mut buf)?; 50 | 51 | // Read bytes from the buffer in little endian order. 52 | Ok(<$Ty>::from_le_bytes(buf)) 53 | } 54 | } 55 | }; 56 | } 57 | uint_wire_format_impl!(u8); 58 | uint_wire_format_impl!(u16); 59 | uint_wire_format_impl!(u32); 60 | uint_wire_format_impl!(u64); 61 | 62 | /// A 9P protocol string. 63 | /// 64 | /// The string is always valid UTF-8 and 65535 bytes or less (enforced by `P9String::new()`). 65 | /// 66 | /// It is represented as a C string with a terminating 0 (NUL) character to allow it to be passed 67 | /// directly to libc functions. 68 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] 69 | pub struct P9String { 70 | cstr: CString, 71 | } 72 | 73 | impl P9String { 74 | pub fn new(string_bytes: impl Into>) -> io::Result { 75 | let string_bytes: Vec = string_bytes.into(); 76 | 77 | if string_bytes.len() > u16::MAX as usize { 78 | return Err(io::Error::new( 79 | ErrorKind::InvalidInput, 80 | "string is too long", 81 | )); 82 | } 83 | 84 | // 9p strings must be valid UTF-8. 85 | let _check_utf8 = std::str::from_utf8(&string_bytes) 86 | .map_err(|e| io::Error::new(ErrorKind::InvalidInput, e))?; 87 | 88 | let cstr = 89 | CString::new(string_bytes).map_err(|e| io::Error::new(ErrorKind::InvalidInput, e))?; 90 | 91 | Ok(P9String { cstr }) 92 | } 93 | 94 | pub fn len(&self) -> usize { 95 | self.cstr.as_bytes().len() 96 | } 97 | 98 | pub fn as_c_str(&self) -> &CStr { 99 | self.cstr.as_c_str() 100 | } 101 | 102 | pub fn as_bytes(&self) -> &[u8] { 103 | self.cstr.as_bytes() 104 | } 105 | 106 | /// Returns a raw pointer to the string's storage. 107 | /// 108 | /// The string bytes are always followed by a NUL terminator ('\0'), so the pointer can be 109 | /// passed directly to libc functions that expect a C string. 110 | pub fn as_ptr(&self) -> *const libc::c_char { 111 | self.cstr.as_ptr() 112 | } 113 | } 114 | 115 | impl PartialEq<&str> for P9String { 116 | fn eq(&self, other: &&str) -> bool { 117 | self.cstr.as_bytes() == other.as_bytes() 118 | } 119 | } 120 | 121 | impl TryFrom<&OsStr> for P9String { 122 | type Error = io::Error; 123 | 124 | fn try_from(value: &OsStr) -> io::Result { 125 | let string_bytes = value.as_encoded_bytes(); 126 | Self::new(string_bytes) 127 | } 128 | } 129 | 130 | // The 9P protocol requires that strings are UTF-8 encoded. The wire format is a u16 131 | // count |N|, encoded in little endian, followed by |N| bytes of UTF-8 data. 132 | impl WireFormat for P9String { 133 | fn byte_size(&self) -> u32 { 134 | (mem::size_of::() + self.len()) as u32 135 | } 136 | 137 | fn encode(&self, writer: &mut W) -> io::Result<()> { 138 | (self.len() as u16).encode(writer)?; 139 | writer.write_all(self.cstr.as_bytes()) 140 | } 141 | 142 | fn decode(reader: &mut R) -> io::Result { 143 | let len: u16 = WireFormat::decode(reader)?; 144 | let mut string_bytes = vec![0u8; usize::from(len)]; 145 | reader.read_exact(&mut string_bytes)?; 146 | Self::new(string_bytes) 147 | } 148 | } 149 | 150 | // The wire format for repeated types is similar to that of strings: a little endian 151 | // encoded u16 |N|, followed by |N| instances of the given type. 152 | impl WireFormat for Vec { 153 | fn byte_size(&self) -> u32 { 154 | mem::size_of::() as u32 + self.iter().map(|elem| elem.byte_size()).sum::() 155 | } 156 | 157 | fn encode(&self, writer: &mut W) -> io::Result<()> { 158 | if self.len() > u16::MAX as usize { 159 | return Err(io::Error::new( 160 | ErrorKind::InvalidInput, 161 | "too many elements in vector", 162 | )); 163 | } 164 | 165 | (self.len() as u16).encode(writer)?; 166 | for elem in self { 167 | elem.encode(writer)?; 168 | } 169 | 170 | Ok(()) 171 | } 172 | 173 | fn decode(reader: &mut R) -> io::Result { 174 | let len: u16 = WireFormat::decode(reader)?; 175 | let mut result = Vec::with_capacity(len as usize); 176 | 177 | for _ in 0..len { 178 | result.push(WireFormat::decode(reader)?); 179 | } 180 | 181 | Ok(result) 182 | } 183 | } 184 | 185 | /// A type that encodes an arbitrary number of bytes of data. Typically used for Rread 186 | /// Twrite messages. This differs from a `Vec` in that it encodes the number of bytes 187 | /// using a `u32` instead of a `u16`. 188 | #[derive(PartialEq, Eq)] 189 | pub struct Data(pub Vec); 190 | 191 | // The maximum length of a data buffer that we support. In practice the server's max message 192 | // size should prevent us from reading too much data so this check is mainly to ensure a 193 | // malicious client cannot trick us into allocating massive amounts of memory. 194 | const MAX_DATA_LENGTH: u32 = 32 * 1024 * 1024; 195 | 196 | impl fmt::Debug for Data { 197 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 198 | // There may be a lot of data and we don't want to spew it all out in a trace. Instead 199 | // just print out the number of bytes in the buffer. 200 | write!(f, "Data({} bytes)", self.len()) 201 | } 202 | } 203 | 204 | // Implement Deref and DerefMut so that we don't have to use self.0 everywhere. 205 | impl Deref for Data { 206 | type Target = Vec; 207 | fn deref(&self) -> &Self::Target { 208 | &self.0 209 | } 210 | } 211 | impl DerefMut for Data { 212 | fn deref_mut(&mut self) -> &mut Self::Target { 213 | &mut self.0 214 | } 215 | } 216 | 217 | // Same as Vec except that it encodes the length as a u32 instead of a u16. 218 | impl WireFormat for Data { 219 | fn byte_size(&self) -> u32 { 220 | mem::size_of::() as u32 + self.iter().map(|elem| elem.byte_size()).sum::() 221 | } 222 | 223 | fn encode(&self, writer: &mut W) -> io::Result<()> { 224 | if self.len() > u32::MAX as usize { 225 | return Err(io::Error::new(ErrorKind::InvalidInput, "data is too large")); 226 | } 227 | (self.len() as u32).encode(writer)?; 228 | writer.write_all(self) 229 | } 230 | 231 | fn decode(reader: &mut R) -> io::Result { 232 | let len: u32 = WireFormat::decode(reader)?; 233 | if len > MAX_DATA_LENGTH { 234 | return Err(io::Error::new( 235 | ErrorKind::InvalidData, 236 | format!("data length ({} bytes) is too large", len), 237 | )); 238 | } 239 | 240 | let mut buf = Vec::with_capacity(len as usize); 241 | reader.take(len as u64).read_to_end(&mut buf)?; 242 | 243 | if buf.len() == len as usize { 244 | Ok(Data(buf)) 245 | } else { 246 | Err(io::Error::new( 247 | ErrorKind::UnexpectedEof, 248 | format!( 249 | "unexpected end of data: want: {} bytes, got: {} bytes", 250 | len, 251 | buf.len() 252 | ), 253 | )) 254 | } 255 | } 256 | } 257 | 258 | #[cfg(test)] 259 | mod test { 260 | use std::io::Cursor; 261 | use std::mem; 262 | use std::string::String; 263 | 264 | use p9_wire_format_derive::P9WireFormat; 265 | 266 | use super::*; 267 | 268 | #[test] 269 | fn integer_byte_size() { 270 | assert_eq!(1, 0u8.byte_size()); 271 | assert_eq!(2, 0u16.byte_size()); 272 | assert_eq!(4, 0u32.byte_size()); 273 | assert_eq!(8, 0u64.byte_size()); 274 | } 275 | 276 | #[test] 277 | fn integer_decode() { 278 | let buf: [u8; 8] = [0xef, 0xbe, 0xad, 0xde, 0x0d, 0xf0, 0xad, 0x8b]; 279 | 280 | assert_eq!(0xef_u8, WireFormat::decode(&mut Cursor::new(&buf)).unwrap()); 281 | assert_eq!(0xbeef_u16, u16::decode(&mut Cursor::new(&buf)).unwrap()); 282 | assert_eq!(0xdeadbeef_u32, u32::decode(&mut Cursor::new(&buf)).unwrap()); 283 | assert_eq!( 284 | 0x8bad_f00d_dead_beef_u64, 285 | u64::decode(&mut Cursor::new(&buf)).unwrap() 286 | ); 287 | } 288 | 289 | #[test] 290 | fn integer_encode() { 291 | let value: u64 = 0x8bad_f00d_dead_beef; 292 | let expected: [u8; 8] = [0xef, 0xbe, 0xad, 0xde, 0x0d, 0xf0, 0xad, 0x8b]; 293 | 294 | let mut buf = vec![0; 8]; 295 | 296 | (value as u8).encode(&mut Cursor::new(&mut *buf)).unwrap(); 297 | assert_eq!(expected[0..1], buf[0..1]); 298 | 299 | (value as u16).encode(&mut Cursor::new(&mut *buf)).unwrap(); 300 | assert_eq!(expected[0..2], buf[0..2]); 301 | 302 | (value as u32).encode(&mut Cursor::new(&mut *buf)).unwrap(); 303 | assert_eq!(expected[0..4], buf[0..4]); 304 | 305 | value.encode(&mut Cursor::new(&mut *buf)).unwrap(); 306 | assert_eq!(expected[0..8], buf[0..8]); 307 | } 308 | 309 | #[test] 310 | fn string_byte_size() { 311 | let values = [ 312 | "Google Video", 313 | "网页 图片 资讯更多 »", 314 | "Παγκόσμιος Ιστός", 315 | "Поиск страниц на русском", 316 | "전체서비스", 317 | ]; 318 | 319 | let exp = values 320 | .iter() 321 | .map(|v| (mem::size_of::() + v.len()) as u32); 322 | 323 | for (value, expected) in values.iter().zip(exp) { 324 | let p9str = P9String::new(value.as_bytes()).unwrap(); 325 | assert_eq!(expected, p9str.byte_size()); 326 | } 327 | } 328 | 329 | #[test] 330 | fn zero_length_string() { 331 | let s = P9String::new("").unwrap(); 332 | assert_eq!(s.byte_size(), mem::size_of::() as u32); 333 | 334 | let mut buf = [0xffu8; 4]; 335 | 336 | s.encode(&mut Cursor::new(&mut buf[..])) 337 | .expect("failed to encode empty string"); 338 | assert_eq!(&[0, 0, 0xff, 0xff], &buf); 339 | 340 | let decoded_s = ::decode(&mut Cursor::new(&[0, 0, 0x61, 0x61][..])) 341 | .expect("failed to decode empty string"); 342 | assert_eq!(decoded_s.as_bytes(), b""); 343 | } 344 | 345 | #[test] 346 | fn string_encode() { 347 | let values = [ 348 | "Google Video", 349 | "网页 图片 资讯更多 »", 350 | "Παγκόσμιος Ιστός", 351 | "Поиск страниц на русском", 352 | "전체서비스", 353 | ]; 354 | 355 | let expected = values.iter().map(|v| { 356 | let len = v.as_bytes().len(); 357 | let mut buf = Vec::with_capacity(len + mem::size_of::()); 358 | 359 | buf.push(len as u8); 360 | buf.push((len >> 8) as u8); 361 | 362 | buf.extend_from_slice(v.as_bytes()); 363 | 364 | buf 365 | }); 366 | 367 | for (val, exp) in values.iter().zip(expected) { 368 | let mut buf = vec![0; exp.len()]; 369 | 370 | let p9str = P9String::new(val.as_bytes()).unwrap(); 371 | WireFormat::encode(&p9str, &mut Cursor::new(&mut *buf)).unwrap(); 372 | assert_eq!(exp, buf); 373 | } 374 | } 375 | 376 | #[test] 377 | fn string_decode() { 378 | assert_eq!( 379 | "Google Video".as_bytes(), 380 | ::decode(&mut Cursor::new( 381 | &[ 382 | 0x0c, 0x00, 0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x20, 0x56, 0x69, 0x64, 0x65, 383 | 0x6F, 384 | ][..] 385 | )) 386 | .unwrap() 387 | .as_bytes() 388 | ); 389 | assert_eq!( 390 | "网页 图片 资讯更多 »".as_bytes(), 391 | ::decode(&mut Cursor::new( 392 | &[ 393 | 0x1d, 0x00, 0xE7, 0xBD, 0x91, 0xE9, 0xA1, 0xB5, 0x20, 0xE5, 0x9B, 0xBE, 0xE7, 394 | 0x89, 0x87, 0x20, 0xE8, 0xB5, 0x84, 0xE8, 0xAE, 0xAF, 0xE6, 0x9B, 0xB4, 0xE5, 395 | 0xA4, 0x9A, 0x20, 0xC2, 0xBB, 396 | ][..] 397 | )) 398 | .unwrap() 399 | .as_bytes() 400 | ); 401 | assert_eq!( 402 | "Παγκόσμιος Ιστός".as_bytes(), 403 | ::decode(&mut Cursor::new( 404 | &[ 405 | 0x1f, 0x00, 0xCE, 0xA0, 0xCE, 0xB1, 0xCE, 0xB3, 0xCE, 0xBA, 0xCF, 0x8C, 0xCF, 406 | 0x83, 0xCE, 0xBC, 0xCE, 0xB9, 0xCE, 0xBF, 0xCF, 0x82, 0x20, 0xCE, 0x99, 0xCF, 407 | 0x83, 0xCF, 0x84, 0xCF, 0x8C, 0xCF, 0x82, 408 | ][..] 409 | )) 410 | .unwrap() 411 | .as_bytes() 412 | ); 413 | assert_eq!( 414 | "Поиск страниц на русском".as_bytes(), 415 | ::decode(&mut Cursor::new( 416 | &[ 417 | 0x2d, 0x00, 0xD0, 0x9F, 0xD0, 0xBE, 0xD0, 0xB8, 0xD1, 0x81, 0xD0, 0xBA, 0x20, 418 | 0xD1, 0x81, 0xD1, 0x82, 0xD1, 0x80, 0xD0, 0xB0, 0xD0, 0xBD, 0xD0, 0xB8, 0xD1, 419 | 0x86, 0x20, 0xD0, 0xBD, 0xD0, 0xB0, 0x20, 0xD1, 0x80, 0xD1, 0x83, 0xD1, 0x81, 420 | 0xD1, 0x81, 0xD0, 0xBA, 0xD0, 0xBE, 0xD0, 0xBC, 421 | ][..] 422 | )) 423 | .unwrap() 424 | .as_bytes() 425 | ); 426 | assert_eq!( 427 | "전체서비스".as_bytes(), 428 | ::decode(&mut Cursor::new( 429 | &[ 430 | 0x0f, 0x00, 0xEC, 0xA0, 0x84, 0xEC, 0xB2, 0xB4, 0xEC, 0x84, 0x9C, 0xEB, 0xB9, 431 | 0x84, 0xEC, 0x8A, 0xA4, 432 | ][..] 433 | )) 434 | .unwrap() 435 | .as_bytes() 436 | ); 437 | } 438 | 439 | #[test] 440 | fn invalid_string_decode() { 441 | let _ = ::decode(&mut Cursor::new(&[ 442 | 0x06, 0x00, 0xed, 0xa0, 0x80, 0xed, 0xbf, 0xbf, 443 | ])) 444 | .expect_err("surrogate code point"); 445 | 446 | let _ = ::decode(&mut Cursor::new(&[ 447 | 0x05, 0x00, 0xf8, 0x80, 0x80, 0x80, 0xbf, 448 | ])) 449 | .expect_err("overlong sequence"); 450 | 451 | let _ = ::decode(&mut Cursor::new(&[ 452 | 0x04, 0x00, 0xf4, 0x90, 0x80, 0x80, 453 | ])) 454 | .expect_err("out of range"); 455 | 456 | let _ = ::decode(&mut Cursor::new(&[ 457 | 0x04, 0x00, 0x63, 0x61, 0x66, 0xe9, 458 | ])) 459 | .expect_err("ISO-8859-1"); 460 | 461 | let _ = ::decode(&mut Cursor::new(&[ 462 | 0x04, 0x00, 0xb0, 0xa1, 0xb0, 0xa2, 463 | ])) 464 | .expect_err("EUC-KR"); 465 | } 466 | 467 | #[test] 468 | fn vector_encode() { 469 | let values: Vec = vec![291, 18_916, 2_497, 22, 797_162, 2_119_732, 3_213_929_716]; 470 | let mut expected: Vec = 471 | Vec::with_capacity(values.len() * mem::size_of::() + mem::size_of::()); 472 | expected.push(values.len() as u8); 473 | expected.push((values.len() >> 8) as u8); 474 | 475 | const MASK: u32 = 0xff; 476 | for val in &values { 477 | expected.push((val & MASK) as u8); 478 | expected.push(((val >> 8) & MASK) as u8); 479 | expected.push(((val >> 16) & MASK) as u8); 480 | expected.push(((val >> 24) & MASK) as u8); 481 | } 482 | 483 | let mut actual: Vec = vec![0; expected.len()]; 484 | 485 | WireFormat::encode(&values, &mut Cursor::new(&mut *actual)) 486 | .expect("failed to encode vector"); 487 | assert_eq!(expected, actual); 488 | } 489 | 490 | #[test] 491 | fn vector_decode() { 492 | let expected: Vec = vec![ 493 | 2_498, 494 | 24, 495 | 897, 496 | 4_097_789_579, 497 | 8_498_119, 498 | 684_279, 499 | 961_189_198, 500 | 7, 501 | ]; 502 | let mut input: Vec = 503 | Vec::with_capacity(expected.len() * mem::size_of::() + mem::size_of::()); 504 | input.push(expected.len() as u8); 505 | input.push((expected.len() >> 8) as u8); 506 | 507 | const MASK: u32 = 0xff; 508 | for val in &expected { 509 | input.push((val & MASK) as u8); 510 | input.push(((val >> 8) & MASK) as u8); 511 | input.push(((val >> 16) & MASK) as u8); 512 | input.push(((val >> 24) & MASK) as u8); 513 | } 514 | 515 | assert_eq!( 516 | expected, 517 | as WireFormat>::decode(&mut Cursor::new(&*input)) 518 | .expect("failed to decode vector") 519 | ); 520 | } 521 | 522 | #[test] 523 | fn data_encode() { 524 | let values = Data(vec![169, 155, 79, 67, 182, 199, 25, 73, 129, 200]); 525 | let mut expected: Vec = 526 | Vec::with_capacity(values.len() * mem::size_of::() + mem::size_of::()); 527 | expected.push(values.len() as u8); 528 | expected.push((values.len() >> 8) as u8); 529 | expected.push((values.len() >> 16) as u8); 530 | expected.push((values.len() >> 24) as u8); 531 | expected.extend_from_slice(&values); 532 | 533 | let mut actual: Vec = vec![0; expected.len()]; 534 | 535 | WireFormat::encode(&values, &mut Cursor::new(&mut *actual)) 536 | .expect("failed to encode datar"); 537 | assert_eq!(expected, actual); 538 | } 539 | 540 | #[test] 541 | fn data_decode() { 542 | let expected = Data(vec![219, 15, 8, 155, 194, 129, 79, 91, 46, 53, 173]); 543 | let mut input: Vec = 544 | Vec::with_capacity(expected.len() * mem::size_of::() + mem::size_of::()); 545 | input.push(expected.len() as u8); 546 | input.push((expected.len() >> 8) as u8); 547 | input.push((expected.len() >> 16) as u8); 548 | input.push((expected.len() >> 24) as u8); 549 | input.extend_from_slice(&expected); 550 | 551 | assert_eq!( 552 | expected, 553 | ::decode(&mut Cursor::new(&mut *input)) 554 | .expect("failed to decode data") 555 | ); 556 | } 557 | 558 | #[test] 559 | fn error_cases() { 560 | // string is too long. 561 | let mut long_str = String::with_capacity(std::u16::MAX as usize); 562 | while long_str.len() < std::u16::MAX as usize { 563 | long_str.push_str("long"); 564 | } 565 | long_str.push('!'); 566 | P9String::new(long_str).expect_err("long string"); 567 | 568 | // vector is too long. 569 | let mut long_vec: Vec = Vec::with_capacity(std::u16::MAX as usize); 570 | while long_vec.len() < std::u16::MAX as usize { 571 | long_vec.push(0x8bad_f00d); 572 | } 573 | long_vec.push(0x00ba_b10c); 574 | 575 | let count = long_vec.len() * mem::size_of::(); 576 | let mut buf = vec![0; count]; 577 | 578 | WireFormat::encode(&long_vec, &mut Cursor::new(&mut *buf)).expect_err("long vector"); 579 | } 580 | 581 | #[derive(Debug, PartialEq, P9WireFormat)] 582 | struct Item { 583 | a: u64, 584 | b: P9String, 585 | c: Vec, 586 | buf: Data, 587 | } 588 | 589 | #[test] 590 | fn struct_encode() { 591 | let item = Item { 592 | a: 0xdead_10cc_00ba_b10c, 593 | b: P9String::new("冻住,不许走!").unwrap(), 594 | c: vec![359, 492, 8891], 595 | buf: Data(vec![254, 129, 0, 62, 49, 172]), 596 | }; 597 | 598 | let mut expected: Vec = vec![0x0c, 0xb1, 0xba, 0x00, 0xcc, 0x10, 0xad, 0xde]; 599 | let strlen = item.b.len() as u16; 600 | expected.push(strlen as u8); 601 | expected.push((strlen >> 8) as u8); 602 | expected.extend_from_slice(item.b.as_bytes()); 603 | 604 | let veclen = item.c.len() as u16; 605 | expected.push(veclen as u8); 606 | expected.push((veclen >> 8) as u8); 607 | for val in &item.c { 608 | expected.push(*val as u8); 609 | expected.push((val >> 8) as u8); 610 | } 611 | 612 | let buflen = item.buf.len() as u32; 613 | expected.push(buflen as u8); 614 | expected.push((buflen >> 8) as u8); 615 | expected.push((buflen >> 16) as u8); 616 | expected.push((buflen >> 24) as u8); 617 | expected.extend_from_slice(&item.buf); 618 | 619 | let mut actual = vec![0; expected.len()]; 620 | 621 | WireFormat::encode(&item, &mut Cursor::new(&mut *actual)).expect("failed to encode item"); 622 | 623 | assert_eq!(expected, actual); 624 | } 625 | 626 | #[test] 627 | fn struct_decode() { 628 | let expected = Item { 629 | a: 0xface_b00c_0404_4b1d, 630 | b: P9String::new("Огонь по готовности!").unwrap(), 631 | c: vec![20067, 32449, 549, 4972, 77, 1987], 632 | buf: Data(vec![126, 236, 79, 59, 6, 159]), 633 | }; 634 | 635 | let mut input: Vec = vec![0x1d, 0x4b, 0x04, 0x04, 0x0c, 0xb0, 0xce, 0xfa]; 636 | let strlen = expected.b.len() as u16; 637 | input.push(strlen as u8); 638 | input.push((strlen >> 8) as u8); 639 | input.extend_from_slice(expected.b.as_bytes()); 640 | 641 | let veclen = expected.c.len() as u16; 642 | input.push(veclen as u8); 643 | input.push((veclen >> 8) as u8); 644 | for val in &expected.c { 645 | input.push(*val as u8); 646 | input.push((val >> 8) as u8); 647 | } 648 | 649 | let buflen = expected.buf.len() as u32; 650 | input.push(buflen as u8); 651 | input.push((buflen >> 8) as u8); 652 | input.push((buflen >> 16) as u8); 653 | input.push((buflen >> 24) as u8); 654 | input.extend_from_slice(&expected.buf); 655 | 656 | let actual: Item = 657 | WireFormat::decode(&mut Cursor::new(input)).expect("failed to decode item"); 658 | 659 | assert_eq!(expected, actual); 660 | } 661 | 662 | #[derive(Debug, PartialEq, P9WireFormat)] 663 | struct Nested { 664 | item: Item, 665 | val: Vec, 666 | } 667 | 668 | #[allow(clippy::vec_init_then_push)] 669 | fn build_encoded_buffer(value: &Nested) -> Vec { 670 | let mut result: Vec = Vec::new(); 671 | 672 | // encode a 673 | result.push(value.item.a as u8); 674 | result.push((value.item.a >> 8) as u8); 675 | result.push((value.item.a >> 16) as u8); 676 | result.push((value.item.a >> 24) as u8); 677 | result.push((value.item.a >> 32) as u8); 678 | result.push((value.item.a >> 40) as u8); 679 | result.push((value.item.a >> 48) as u8); 680 | result.push((value.item.a >> 56) as u8); 681 | 682 | // encode b 683 | result.push(value.item.b.len() as u8); 684 | result.push((value.item.b.len() >> 8) as u8); 685 | result.extend_from_slice(value.item.b.as_bytes()); 686 | 687 | // encode c 688 | result.push(value.item.c.len() as u8); 689 | result.push((value.item.c.len() >> 8) as u8); 690 | for val in &value.item.c { 691 | result.push((val & 0xffu16) as u8); 692 | result.push(((val >> 8) & 0xffu16) as u8); 693 | } 694 | 695 | // encode buf 696 | result.push(value.item.buf.len() as u8); 697 | result.push((value.item.buf.len() >> 8) as u8); 698 | result.push((value.item.buf.len() >> 16) as u8); 699 | result.push((value.item.buf.len() >> 24) as u8); 700 | result.extend_from_slice(&value.item.buf); 701 | 702 | // encode val 703 | result.push(value.val.len() as u8); 704 | result.push((value.val.len() >> 8) as u8); 705 | for val in &value.val { 706 | result.push(*val as u8); 707 | result.push((val >> 8) as u8); 708 | result.push((val >> 16) as u8); 709 | result.push((val >> 24) as u8); 710 | result.push((val >> 32) as u8); 711 | result.push((val >> 40) as u8); 712 | result.push((val >> 48) as u8); 713 | result.push((val >> 56) as u8); 714 | } 715 | 716 | result 717 | } 718 | 719 | #[test] 720 | fn nested_encode() { 721 | let value = Nested { 722 | item: Item { 723 | a: 0xcafe_d00d_8bad_f00d, 724 | b: P9String::new("龍が我が敵を喰らう!").unwrap(), 725 | c: vec![2679, 55_919, 44, 38_819, 792], 726 | buf: Data(vec![129, 55, 200, 93, 7, 68]), 727 | }, 728 | val: vec![1954978, 59, 4519, 15679], 729 | }; 730 | 731 | let expected = build_encoded_buffer(&value); 732 | 733 | let mut actual = vec![0; expected.len()]; 734 | 735 | WireFormat::encode(&value, &mut Cursor::new(&mut *actual)).expect("failed to encode value"); 736 | assert_eq!(expected, actual); 737 | } 738 | 739 | #[test] 740 | fn nested_decode() { 741 | let expected = Nested { 742 | item: Item { 743 | a: 0x0ff1ce, 744 | b: P9String::new("龍神の剣を喰らえ!").unwrap(), 745 | c: vec![21687, 159, 55, 9217, 192], 746 | buf: Data(vec![189, 22, 7, 59, 235]), 747 | }, 748 | val: vec![15679, 8619196, 319746, 123957, 77, 0, 492], 749 | }; 750 | 751 | let input = build_encoded_buffer(&expected); 752 | 753 | assert_eq!( 754 | expected, 755 | ::decode(&mut Cursor::new(&*input)) 756 | .expect("failed to decode value") 757 | ); 758 | } 759 | } 760 | -------------------------------------------------------------------------------- /src/server/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The ChromiumOS Authors 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | mod read_dir; 6 | 7 | use std::cmp::min; 8 | use std::collections::btree_map; 9 | use std::collections::BTreeMap; 10 | use std::ffi::CStr; 11 | use std::ffi::CString; 12 | use std::fs::File; 13 | use std::io; 14 | use std::io::Cursor; 15 | use std::io::Read; 16 | use std::io::Write; 17 | use std::mem; 18 | use std::mem::MaybeUninit; 19 | use std::ops::Deref; 20 | use std::os::unix::ffi::OsStrExt; 21 | use std::os::unix::fs::FileExt; 22 | use std::os::unix::io::AsRawFd; 23 | use std::os::unix::io::FromRawFd; 24 | use std::os::unix::io::RawFd; 25 | use std::path::Path; 26 | use std::str::FromStr; 27 | 28 | #[cfg(target_os = "android")] 29 | use libc::__fsid_t as fsid_t; 30 | #[cfg(not(target_os = "android"))] 31 | use libc::fsid_t; 32 | use read_dir::read_dir; 33 | use serde::Deserialize; 34 | use serde::Serialize; 35 | 36 | use crate::protocol::*; 37 | use crate::syscall; 38 | 39 | // Tlopen and Tlcreate flags. Taken from "include/net/9p/9p.h" in the linux tree. 40 | const P9_RDONLY: u32 = 0o00000000; 41 | const P9_WRONLY: u32 = 0o00000001; 42 | const P9_RDWR: u32 = 0o00000002; 43 | const P9_NOACCESS: u32 = 0o00000003; 44 | const P9_CREATE: u32 = 0o00000100; 45 | const P9_EXCL: u32 = 0o00000200; 46 | const P9_NOCTTY: u32 = 0o00000400; 47 | const P9_TRUNC: u32 = 0o00001000; 48 | const P9_APPEND: u32 = 0o00002000; 49 | const P9_NONBLOCK: u32 = 0o00004000; 50 | const P9_DSYNC: u32 = 0o00010000; 51 | const P9_FASYNC: u32 = 0o00020000; 52 | const P9_DIRECT: u32 = 0o00040000; 53 | const P9_LARGEFILE: u32 = 0o00100000; 54 | const P9_DIRECTORY: u32 = 0o00200000; 55 | const P9_NOFOLLOW: u32 = 0o00400000; 56 | const P9_NOATIME: u32 = 0o01000000; 57 | const _P9_CLOEXEC: u32 = 0o02000000; 58 | const P9_SYNC: u32 = 0o04000000; 59 | 60 | // Mapping from 9P flags to libc flags. 61 | const MAPPED_FLAGS: [(u32, i32); 16] = [ 62 | (P9_WRONLY, libc::O_WRONLY), 63 | (P9_RDWR, libc::O_RDWR), 64 | (P9_CREATE, libc::O_CREAT), 65 | (P9_EXCL, libc::O_EXCL), 66 | (P9_NOCTTY, libc::O_NOCTTY), 67 | (P9_TRUNC, libc::O_TRUNC), 68 | (P9_APPEND, libc::O_APPEND), 69 | (P9_NONBLOCK, libc::O_NONBLOCK), 70 | (P9_DSYNC, libc::O_DSYNC), 71 | (P9_FASYNC, 0), // Unsupported 72 | (P9_DIRECT, libc::O_DIRECT), 73 | (P9_LARGEFILE, libc::O_LARGEFILE), 74 | (P9_DIRECTORY, libc::O_DIRECTORY), 75 | (P9_NOFOLLOW, libc::O_NOFOLLOW), 76 | (P9_NOATIME, libc::O_NOATIME), 77 | (P9_SYNC, libc::O_SYNC), 78 | ]; 79 | 80 | // 9P Qid types. Taken from "include/net/9p/9p.h" in the linux tree. 81 | const P9_QTDIR: u8 = 0x80; 82 | const _P9_QTAPPEND: u8 = 0x40; 83 | const _P9_QTEXCL: u8 = 0x20; 84 | const _P9_QTMOUNT: u8 = 0x10; 85 | const _P9_QTAUTH: u8 = 0x08; 86 | const _P9_QTTMP: u8 = 0x04; 87 | const P9_QTSYMLINK: u8 = 0x02; 88 | const _P9_QTLINK: u8 = 0x01; 89 | const P9_QTFILE: u8 = 0x00; 90 | 91 | // Bitmask values for the getattr request. 92 | const _P9_GETATTR_MODE: u64 = 0x00000001; 93 | const _P9_GETATTR_NLINK: u64 = 0x00000002; 94 | const _P9_GETATTR_UID: u64 = 0x00000004; 95 | const _P9_GETATTR_GID: u64 = 0x00000008; 96 | const _P9_GETATTR_RDEV: u64 = 0x00000010; 97 | const _P9_GETATTR_ATIME: u64 = 0x00000020; 98 | const _P9_GETATTR_MTIME: u64 = 0x00000040; 99 | const _P9_GETATTR_CTIME: u64 = 0x00000080; 100 | const _P9_GETATTR_INO: u64 = 0x00000100; 101 | const _P9_GETATTR_SIZE: u64 = 0x00000200; 102 | const _P9_GETATTR_BLOCKS: u64 = 0x00000400; 103 | 104 | const _P9_GETATTR_BTIME: u64 = 0x00000800; 105 | const _P9_GETATTR_GEN: u64 = 0x00001000; 106 | const _P9_GETATTR_DATA_VERSION: u64 = 0x00002000; 107 | 108 | const P9_GETATTR_BASIC: u64 = 0x000007ff; /* Mask for fields up to BLOCKS */ 109 | const _P9_GETATTR_ALL: u64 = 0x00003fff; /* Mask for All fields above */ 110 | 111 | // Bitmask values for the setattr request. 112 | const P9_SETATTR_MODE: u32 = 0x00000001; 113 | const P9_SETATTR_UID: u32 = 0x00000002; 114 | const P9_SETATTR_GID: u32 = 0x00000004; 115 | const P9_SETATTR_SIZE: u32 = 0x00000008; 116 | const P9_SETATTR_ATIME: u32 = 0x00000010; 117 | const P9_SETATTR_MTIME: u32 = 0x00000020; 118 | const P9_SETATTR_CTIME: u32 = 0x00000040; 119 | const P9_SETATTR_ATIME_SET: u32 = 0x00000080; 120 | const P9_SETATTR_MTIME_SET: u32 = 0x00000100; 121 | 122 | // 9p lock constants. Taken from "include/net/9p/9p.h" in the linux kernel. 123 | const _P9_LOCK_TYPE_RDLCK: u8 = 0; 124 | const _P9_LOCK_TYPE_WRLCK: u8 = 1; 125 | const P9_LOCK_TYPE_UNLCK: u8 = 2; 126 | const _P9_LOCK_FLAGS_BLOCK: u8 = 1; 127 | const _P9_LOCK_FLAGS_RECLAIM: u8 = 2; 128 | const P9_LOCK_SUCCESS: u8 = 0; 129 | const _P9_LOCK_BLOCKED: u8 = 1; 130 | const _P9_LOCK_ERROR: u8 = 2; 131 | const _P9_LOCK_GRACE: u8 = 3; 132 | 133 | // Minimum and maximum message size that we'll expect from the client. 134 | const MIN_MESSAGE_SIZE: u32 = 256; 135 | const MAX_MESSAGE_SIZE: u32 = 64 * 1024 + 24; // 64 KiB of payload plus some extra for the header 136 | 137 | #[derive(PartialEq, Eq)] 138 | enum FileType { 139 | Regular, 140 | Directory, 141 | Other, 142 | } 143 | 144 | impl From for FileType { 145 | fn from(mode: libc::mode_t) -> Self { 146 | match mode & libc::S_IFMT { 147 | libc::S_IFREG => FileType::Regular, 148 | libc::S_IFDIR => FileType::Directory, 149 | _ => FileType::Other, 150 | } 151 | } 152 | } 153 | 154 | // Represents state that the server is holding on behalf of a client. Fids are somewhat like file 155 | // descriptors but are not restricted to open files and directories. Fids are identified by a unique 156 | // 32-bit number chosen by the client. Most messages sent by clients include a fid on which to 157 | // operate. The fid in a Tattach message represents the root of the file system tree that the client 158 | // is allowed to access. A client can create more fids by walking the directory tree from that fid. 159 | struct Fid { 160 | path: File, 161 | file: Option, 162 | filetype: FileType, 163 | } 164 | 165 | impl From for Qid { 166 | fn from(st: libc::stat64) -> Qid { 167 | let ty = match st.st_mode & libc::S_IFMT { 168 | libc::S_IFDIR => P9_QTDIR, 169 | libc::S_IFREG => P9_QTFILE, 170 | libc::S_IFLNK => P9_QTSYMLINK, 171 | _ => 0, 172 | }; 173 | 174 | Qid { 175 | ty, 176 | // TODO: deal with the 2038 problem before 2038 177 | version: st.st_mtime as u32, 178 | path: st.st_ino, 179 | } 180 | } 181 | } 182 | 183 | fn statat(d: &File, name: &CStr, flags: libc::c_int) -> io::Result { 184 | let mut st = MaybeUninit::::zeroed(); 185 | 186 | // Safe because the kernel will only write data in `st` and we check the return 187 | // value. 188 | let res = unsafe { 189 | libc::fstatat64( 190 | d.as_raw_fd(), 191 | name.as_ptr(), 192 | st.as_mut_ptr(), 193 | flags | libc::AT_SYMLINK_NOFOLLOW, 194 | ) 195 | }; 196 | if res >= 0 { 197 | // Safe because the kernel guarantees that the struct is now fully initialized. 198 | Ok(unsafe { st.assume_init() }) 199 | } else { 200 | Err(io::Error::last_os_error()) 201 | } 202 | } 203 | 204 | fn stat(f: &File) -> io::Result { 205 | // Safe because this is a constant value and a valid C string. 206 | let pathname = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") }; 207 | 208 | statat(f, pathname, libc::AT_EMPTY_PATH) 209 | } 210 | 211 | fn string_to_cstring(s: String) -> io::Result { 212 | CString::new(s).map_err(|_| io::Error::from_raw_os_error(libc::EINVAL)) 213 | } 214 | 215 | fn error_to_rmessage(err: io::Error) -> Rmessage { 216 | let errno = if let Some(errno) = err.raw_os_error() { 217 | errno 218 | } else { 219 | // Make a best-effort guess based on the kind. 220 | match err.kind() { 221 | io::ErrorKind::NotFound => libc::ENOENT, 222 | io::ErrorKind::PermissionDenied => libc::EPERM, 223 | io::ErrorKind::ConnectionRefused => libc::ECONNREFUSED, 224 | io::ErrorKind::ConnectionReset => libc::ECONNRESET, 225 | io::ErrorKind::ConnectionAborted => libc::ECONNABORTED, 226 | io::ErrorKind::NotConnected => libc::ENOTCONN, 227 | io::ErrorKind::AddrInUse => libc::EADDRINUSE, 228 | io::ErrorKind::AddrNotAvailable => libc::EADDRNOTAVAIL, 229 | io::ErrorKind::BrokenPipe => libc::EPIPE, 230 | io::ErrorKind::AlreadyExists => libc::EEXIST, 231 | io::ErrorKind::WouldBlock => libc::EWOULDBLOCK, 232 | io::ErrorKind::InvalidInput => libc::EINVAL, 233 | io::ErrorKind::InvalidData => libc::EINVAL, 234 | io::ErrorKind::TimedOut => libc::ETIMEDOUT, 235 | io::ErrorKind::WriteZero => libc::EIO, 236 | io::ErrorKind::Interrupted => libc::EINTR, 237 | io::ErrorKind::Other => libc::EIO, 238 | io::ErrorKind::UnexpectedEof => libc::EIO, 239 | _ => libc::EIO, 240 | } 241 | }; 242 | 243 | Rmessage::Lerror(Rlerror { 244 | ecode: errno as u32, 245 | }) 246 | } 247 | 248 | // Sigh.. Cow requires the underlying type to implement Clone. 249 | enum MaybeOwned<'b, T> { 250 | Borrowed(&'b T), 251 | Owned(T), 252 | } 253 | 254 | impl<'a, T> Deref for MaybeOwned<'a, T> { 255 | type Target = T; 256 | fn deref(&self) -> &Self::Target { 257 | use MaybeOwned::*; 258 | match *self { 259 | Borrowed(borrowed) => borrowed, 260 | Owned(ref owned) => owned, 261 | } 262 | } 263 | } 264 | 265 | impl<'a, T> AsRef for MaybeOwned<'a, T> { 266 | fn as_ref(&self) -> &T { 267 | use MaybeOwned::*; 268 | match self { 269 | Borrowed(borrowed) => borrowed, 270 | Owned(ref owned) => owned, 271 | } 272 | } 273 | } 274 | 275 | fn ebadf() -> io::Error { 276 | io::Error::from_raw_os_error(libc::EBADF) 277 | } 278 | 279 | pub type ServerIdMap = BTreeMap; 280 | pub type ServerUidMap = ServerIdMap; 281 | pub type ServerGidMap = ServerIdMap; 282 | 283 | fn map_id_from_host(map: &ServerIdMap, id: T) -> T { 284 | map.get(&id).map_or(id.clone(), |v| v.clone()) 285 | } 286 | 287 | // Performs an ascii case insensitive lookup and returns an O_PATH fd for the entry, if found. 288 | fn ascii_casefold_lookup(proc: &File, parent: &File, name: &[u8]) -> io::Result { 289 | let mut dir = open_fid(proc, parent, P9_DIRECTORY)?; 290 | let mut dirents = read_dir(&mut dir, 0)?; 291 | 292 | while let Some(entry) = dirents.next().transpose()? { 293 | if name.eq_ignore_ascii_case(entry.name.as_bytes()) { 294 | return lookup(parent, entry.name.as_c_str()); 295 | } 296 | } 297 | 298 | Err(io::Error::from_raw_os_error(libc::ENOENT)) 299 | } 300 | 301 | fn lookup(parent: &File, name: &CStr) -> io::Result { 302 | // Safe because this doesn't modify any memory and we check the return value. 303 | let fd = syscall!(unsafe { 304 | libc::openat64( 305 | parent.as_raw_fd(), 306 | name.as_ptr(), 307 | libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC, 308 | ) 309 | })?; 310 | 311 | // Safe because we just opened this fd. 312 | Ok(unsafe { File::from_raw_fd(fd) }) 313 | } 314 | 315 | fn do_walk( 316 | proc: &File, 317 | wnames: Vec, 318 | start: &File, 319 | ascii_casefold: bool, 320 | mds: &mut Vec, 321 | ) -> io::Result { 322 | let mut current = MaybeOwned::Borrowed(start); 323 | 324 | for wname in wnames { 325 | current = MaybeOwned::Owned(lookup(current.as_ref(), wname.as_c_str()).or_else(|e| { 326 | if ascii_casefold { 327 | if let Some(libc::ENOENT) = e.raw_os_error() { 328 | return ascii_casefold_lookup(proc, current.as_ref(), wname.as_bytes()); 329 | } 330 | } 331 | 332 | Err(e) 333 | })?); 334 | mds.push(stat(¤t)?); 335 | } 336 | 337 | match current { 338 | MaybeOwned::Owned(owned) => Ok(owned), 339 | MaybeOwned::Borrowed(borrowed) => borrowed.try_clone(), 340 | } 341 | } 342 | 343 | fn open_fid(proc: &File, path: &File, p9_flags: u32) -> io::Result { 344 | let pathname = string_to_cstring(format!("self/fd/{}", path.as_raw_fd()))?; 345 | 346 | // We always open files with O_CLOEXEC. 347 | let mut flags: i32 = libc::O_CLOEXEC; 348 | for &(p9f, of) in &MAPPED_FLAGS { 349 | if (p9_flags & p9f) != 0 { 350 | flags |= of; 351 | } 352 | } 353 | 354 | if p9_flags & P9_NOACCESS == P9_RDONLY { 355 | flags |= libc::O_RDONLY; 356 | } 357 | 358 | // Safe because this doesn't modify any memory and we check the return value. We need to 359 | // clear the O_NOFOLLOW flag because we want to follow the proc symlink. 360 | let fd = syscall!(unsafe { 361 | libc::openat64( 362 | proc.as_raw_fd(), 363 | pathname.as_ptr(), 364 | flags & !libc::O_NOFOLLOW, 365 | ) 366 | })?; 367 | 368 | // Safe because we just opened this fd and we know it is valid. 369 | Ok(unsafe { File::from_raw_fd(fd) }) 370 | } 371 | 372 | #[derive(Clone, Serialize, Deserialize)] 373 | pub struct Config { 374 | pub root: Box, 375 | pub msize: u32, 376 | 377 | pub uid_map: ServerUidMap, 378 | pub gid_map: ServerGidMap, 379 | 380 | pub ascii_casefold: bool, 381 | } 382 | 383 | impl FromStr for Config { 384 | type Err = &'static str; 385 | 386 | fn from_str(params: &str) -> Result { 387 | let mut cfg = Self::default(); 388 | if params.is_empty() { 389 | return Ok(cfg); 390 | } 391 | for opt in params.split(':') { 392 | let mut o = opt.splitn(2, '='); 393 | let kind = o.next().ok_or("`cfg` options mut not be empty")?; 394 | let value = o 395 | .next() 396 | .ok_or("`cfg` options must be of the form `kind=value`")?; 397 | match kind { 398 | "ascii_casefold" => { 399 | let ascii_casefold = value 400 | .parse() 401 | .map_err(|_| "`ascii_casefold` must be a boolean")?; 402 | cfg.ascii_casefold = ascii_casefold; 403 | } 404 | _ => return Err("unrecognized option for p9 config"), 405 | } 406 | } 407 | Ok(cfg) 408 | } 409 | } 410 | 411 | impl Default for Config { 412 | fn default() -> Config { 413 | Config { 414 | root: Path::new("/").into(), 415 | msize: MAX_MESSAGE_SIZE, 416 | uid_map: Default::default(), 417 | gid_map: Default::default(), 418 | ascii_casefold: false, 419 | } 420 | } 421 | } 422 | pub struct Server { 423 | fids: BTreeMap, 424 | proc: File, 425 | cfg: Config, 426 | } 427 | 428 | impl Server { 429 | pub fn new>>( 430 | root: P, 431 | uid_map: ServerUidMap, 432 | gid_map: ServerGidMap, 433 | ) -> io::Result { 434 | Server::with_config(Config { 435 | root: root.into(), 436 | msize: MAX_MESSAGE_SIZE, 437 | uid_map, 438 | gid_map, 439 | ascii_casefold: false, 440 | }) 441 | } 442 | 443 | pub fn with_config(cfg: Config) -> io::Result { 444 | // Safe because this is a valid c-string. 445 | let proc_cstr = unsafe { CStr::from_bytes_with_nul_unchecked(b"/proc\0") }; 446 | 447 | // Safe because this doesn't modify any memory and we check the return value. 448 | let fd = syscall!(unsafe { 449 | libc::openat64( 450 | libc::AT_FDCWD, 451 | proc_cstr.as_ptr(), 452 | libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC, 453 | ) 454 | })?; 455 | 456 | // Safe because we just opened this fd and we know it is valid. 457 | let proc = unsafe { File::from_raw_fd(fd) }; 458 | Ok(Server { 459 | fids: BTreeMap::new(), 460 | proc, 461 | cfg, 462 | }) 463 | } 464 | 465 | pub fn keep_fds(&self) -> Vec { 466 | vec![self.proc.as_raw_fd()] 467 | } 468 | 469 | pub fn handle_message( 470 | &mut self, 471 | reader: &mut R, 472 | writer: &mut W, 473 | ) -> io::Result<()> { 474 | let Tframe { tag, msg } = WireFormat::decode(&mut reader.take(self.cfg.msize as u64))?; 475 | 476 | let rmsg = match msg { 477 | Ok(Tmessage::Version(ref version)) => self.version(version).map(Rmessage::Version), 478 | Ok(Tmessage::Flush(ref flush)) => self.flush(flush).and(Ok(Rmessage::Flush)), 479 | Ok(Tmessage::Walk(walk)) => self.walk(walk).map(Rmessage::Walk), 480 | Ok(Tmessage::Read(ref read)) => self.read(read).map(Rmessage::Read), 481 | Ok(Tmessage::Write(ref write)) => self.write(write).map(Rmessage::Write), 482 | Ok(Tmessage::Clunk(ref clunk)) => self.clunk(clunk).and(Ok(Rmessage::Clunk)), 483 | Ok(Tmessage::Remove(ref remove)) => self.remove(remove).and(Ok(Rmessage::Remove)), 484 | Ok(Tmessage::Attach(ref attach)) => self.attach(attach).map(Rmessage::Attach), 485 | Ok(Tmessage::Auth(ref auth)) => self.auth(auth).map(Rmessage::Auth), 486 | Ok(Tmessage::Statfs(ref statfs)) => self.statfs(statfs).map(Rmessage::Statfs), 487 | Ok(Tmessage::Lopen(ref lopen)) => self.lopen(lopen).map(Rmessage::Lopen), 488 | Ok(Tmessage::Lcreate(lcreate)) => self.lcreate(lcreate).map(Rmessage::Lcreate), 489 | Ok(Tmessage::Symlink(ref symlink)) => self.symlink(symlink).map(Rmessage::Symlink), 490 | Ok(Tmessage::Mknod(ref mknod)) => self.mknod(mknod).map(Rmessage::Mknod), 491 | Ok(Tmessage::Rename(ref rename)) => self.rename(rename).and(Ok(Rmessage::Rename)), 492 | Ok(Tmessage::Readlink(ref readlink)) => self.readlink(readlink).map(Rmessage::Readlink), 493 | Ok(Tmessage::GetAttr(ref get_attr)) => self.get_attr(get_attr).map(Rmessage::GetAttr), 494 | Ok(Tmessage::SetAttr(ref set_attr)) => { 495 | self.set_attr(set_attr).and(Ok(Rmessage::SetAttr)) 496 | } 497 | Ok(Tmessage::XattrWalk(ref xattr_walk)) => { 498 | self.xattr_walk(xattr_walk).map(Rmessage::XattrWalk) 499 | } 500 | Ok(Tmessage::XattrCreate(ref xattr_create)) => self 501 | .xattr_create(xattr_create) 502 | .and(Ok(Rmessage::XattrCreate)), 503 | Ok(Tmessage::Readdir(ref readdir)) => self.readdir(readdir).map(Rmessage::Readdir), 504 | Ok(Tmessage::Fsync(ref fsync)) => self.fsync(fsync).and(Ok(Rmessage::Fsync)), 505 | Ok(Tmessage::Lock(ref lock)) => self.lock(lock).map(Rmessage::Lock), 506 | Ok(Tmessage::GetLock(ref get_lock)) => self.get_lock(get_lock).map(Rmessage::GetLock), 507 | Ok(Tmessage::Link(link)) => self.link(link).and(Ok(Rmessage::Link)), 508 | Ok(Tmessage::Mkdir(mkdir)) => self.mkdir(mkdir).map(Rmessage::Mkdir), 509 | Ok(Tmessage::RenameAt(rename_at)) => { 510 | self.rename_at(rename_at).and(Ok(Rmessage::RenameAt)) 511 | } 512 | Ok(Tmessage::UnlinkAt(unlink_at)) => { 513 | self.unlink_at(unlink_at).and(Ok(Rmessage::UnlinkAt)) 514 | } 515 | Err(e) => { 516 | // The header was successfully decoded, but the body failed to decode - send an 517 | // error response for this tag. 518 | let error = format!("Tframe message decode failed: {}", e); 519 | Err(io::Error::new(io::ErrorKind::InvalidData, error)) 520 | } 521 | }; 522 | 523 | // Errors while handling requests are never fatal. 524 | let response = Rframe { 525 | tag, 526 | msg: rmsg.unwrap_or_else(error_to_rmessage), 527 | }; 528 | 529 | response.encode(writer)?; 530 | writer.flush() 531 | } 532 | 533 | fn auth(&mut self, _auth: &Tauth) -> io::Result { 534 | // Returning an error for the auth message means that the server does not require 535 | // authentication. 536 | Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) 537 | } 538 | 539 | fn attach(&mut self, attach: &Tattach) -> io::Result { 540 | // TODO: Check attach parameters 541 | match self.fids.entry(attach.fid) { 542 | btree_map::Entry::Vacant(entry) => { 543 | let root = CString::new(self.cfg.root.as_os_str().as_bytes()) 544 | .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; 545 | 546 | // Safe because this doesn't modify any memory and we check the return value. 547 | let fd = syscall!(unsafe { 548 | libc::openat64( 549 | libc::AT_FDCWD, 550 | root.as_ptr(), 551 | libc::O_PATH | libc::O_NOFOLLOW | libc::O_CLOEXEC, 552 | ) 553 | })?; 554 | 555 | let root_path = unsafe { File::from_raw_fd(fd) }; 556 | let st = stat(&root_path)?; 557 | 558 | let fid = Fid { 559 | // Safe because we just opened this fd. 560 | path: root_path, 561 | file: None, 562 | filetype: st.st_mode.into(), 563 | }; 564 | let response = Rattach { qid: st.into() }; 565 | entry.insert(fid); 566 | Ok(response) 567 | } 568 | btree_map::Entry::Occupied(_) => Err(io::Error::from_raw_os_error(libc::EBADF)), 569 | } 570 | } 571 | 572 | fn version(&mut self, version: &Tversion) -> io::Result { 573 | if version.msize < MIN_MESSAGE_SIZE { 574 | return Err(io::Error::from_raw_os_error(libc::EINVAL)); 575 | } 576 | 577 | // A Tversion request clunks all open fids and terminates any pending I/O. 578 | self.fids.clear(); 579 | self.cfg.msize = min(self.cfg.msize, version.msize); 580 | 581 | Ok(Rversion { 582 | msize: self.cfg.msize, 583 | version: if version.version.as_bytes() == b"9P2000.L" { 584 | P9String::new(b"9P2000.L")? 585 | } else { 586 | P9String::new(b"unknown")? 587 | }, 588 | }) 589 | } 590 | 591 | #[allow(clippy::unnecessary_wraps)] 592 | fn flush(&mut self, _flush: &Tflush) -> io::Result<()> { 593 | // TODO: Since everything is synchronous we can't actually flush requests. 594 | Ok(()) 595 | } 596 | 597 | fn walk(&mut self, walk: Twalk) -> io::Result { 598 | // `newfid` must not currently be in use unless it is the same as `fid`. 599 | if walk.fid != walk.newfid && self.fids.contains_key(&walk.newfid) { 600 | return Err(io::Error::from_raw_os_error(libc::EBADF)); 601 | } 602 | 603 | // We need to walk the tree. First get the starting path. 604 | let start = &self.fids.get(&walk.fid).ok_or_else(ebadf)?.path; 605 | 606 | // Now walk the tree and break on the first error, if any. 607 | let expected_len = walk.wnames.len(); 608 | let mut mds = Vec::with_capacity(expected_len); 609 | match do_walk( 610 | &self.proc, 611 | walk.wnames, 612 | start, 613 | self.cfg.ascii_casefold, 614 | &mut mds, 615 | ) { 616 | Ok(end) => { 617 | // Store the new fid if the full walk succeeded. 618 | if mds.len() == expected_len { 619 | let st = mds.last().copied().map(Ok).unwrap_or_else(|| stat(&end))?; 620 | self.fids.insert( 621 | walk.newfid, 622 | Fid { 623 | path: end, 624 | file: None, 625 | filetype: st.st_mode.into(), 626 | }, 627 | ); 628 | } 629 | } 630 | Err(e) => { 631 | // Only return an error if it occurred on the first component. 632 | if mds.is_empty() { 633 | return Err(e); 634 | } 635 | } 636 | } 637 | 638 | Ok(Rwalk { 639 | wqids: mds.into_iter().map(Qid::from).collect(), 640 | }) 641 | } 642 | 643 | fn read(&mut self, read: &Tread) -> io::Result { 644 | // Thankfully, `read` cannot be used to read directories in 9P2000.L. 645 | let file = self 646 | .fids 647 | .get_mut(&read.fid) 648 | .and_then(|fid| fid.file.as_mut()) 649 | .ok_or_else(ebadf)?; 650 | 651 | // Use an empty Rread struct to figure out the overhead of the header. 652 | let header_size = Rframe { 653 | tag: 0, 654 | msg: Rmessage::Read(Rread { 655 | data: Data(Vec::new()), 656 | }), 657 | } 658 | .byte_size(); 659 | 660 | let capacity = min(self.cfg.msize - header_size, read.count); 661 | let mut buf = Data(vec![0u8; capacity as usize]); 662 | 663 | let count = file.read_at(&mut buf, read.offset)?; 664 | buf.truncate(count); 665 | 666 | Ok(Rread { data: buf }) 667 | } 668 | 669 | fn write(&mut self, write: &Twrite) -> io::Result { 670 | let file = self 671 | .fids 672 | .get_mut(&write.fid) 673 | .and_then(|fid| fid.file.as_mut()) 674 | .ok_or_else(ebadf)?; 675 | 676 | let count = file.write_at(&write.data, write.offset)?; 677 | Ok(Rwrite { 678 | count: count as u32, 679 | }) 680 | } 681 | 682 | fn clunk(&mut self, clunk: &Tclunk) -> io::Result<()> { 683 | match self.fids.entry(clunk.fid) { 684 | btree_map::Entry::Vacant(_) => Err(io::Error::from_raw_os_error(libc::EBADF)), 685 | btree_map::Entry::Occupied(entry) => { 686 | entry.remove(); 687 | Ok(()) 688 | } 689 | } 690 | } 691 | 692 | fn remove(&mut self, _remove: &Tremove) -> io::Result<()> { 693 | // Since a file could be linked into multiple locations, there is no way to know exactly 694 | // which path we are supposed to unlink. Linux uses unlink_at anyway, so we can just return 695 | // an error here. 696 | Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) 697 | } 698 | 699 | fn statfs(&mut self, statfs: &Tstatfs) -> io::Result { 700 | let fid = self.fids.get(&statfs.fid).ok_or_else(ebadf)?; 701 | let mut buf = MaybeUninit::zeroed(); 702 | 703 | // Safe because this will only modify `out` and we check the return value. 704 | syscall!(unsafe { libc::fstatfs64(fid.path.as_raw_fd(), buf.as_mut_ptr()) })?; 705 | 706 | // Safe because this only has integer types and any value is valid. 707 | let out = unsafe { buf.assume_init() }; 708 | Ok(Rstatfs { 709 | ty: out.f_type as u32, 710 | bsize: out.f_bsize as u32, 711 | blocks: out.f_blocks, 712 | bfree: out.f_bfree, 713 | bavail: out.f_bavail, 714 | files: out.f_files, 715 | ffree: out.f_ffree, 716 | // Safe because the fsid has only integer fields and the compiler will verify that is 717 | // the same width as the `fsid` field in Rstatfs. 718 | fsid: unsafe { mem::transmute::(out.f_fsid) }, 719 | namelen: out.f_namelen as u32, 720 | }) 721 | } 722 | 723 | fn lopen(&mut self, lopen: &Tlopen) -> io::Result { 724 | let fid = self.fids.get_mut(&lopen.fid).ok_or_else(ebadf)?; 725 | 726 | let file = open_fid(&self.proc, &fid.path, lopen.flags)?; 727 | let st = stat(&file)?; 728 | 729 | fid.file = Some(file); 730 | Ok(Rlopen { 731 | qid: st.into(), 732 | iounit: 0, // Allow the client to send requests up to the negotiated max message size. 733 | }) 734 | } 735 | 736 | fn lcreate(&mut self, lcreate: Tlcreate) -> io::Result { 737 | let fid = self.fids.get_mut(&lcreate.fid).ok_or_else(ebadf)?; 738 | 739 | if fid.filetype != FileType::Directory { 740 | return Err(io::Error::from_raw_os_error(libc::ENOTDIR)); 741 | } 742 | 743 | let mut flags: i32 = libc::O_CLOEXEC | libc::O_CREAT | libc::O_EXCL; 744 | for &(p9f, of) in &MAPPED_FLAGS { 745 | if (lcreate.flags & p9f) != 0 { 746 | flags |= of; 747 | } 748 | } 749 | if lcreate.flags & P9_NOACCESS == P9_RDONLY { 750 | flags |= libc::O_RDONLY; 751 | } 752 | 753 | // Safe because this doesn't modify any memory and we check the return value. 754 | let fd = syscall!(unsafe { 755 | libc::openat64( 756 | fid.path.as_raw_fd(), 757 | lcreate.name.as_ptr(), 758 | flags, 759 | lcreate.mode, 760 | ) 761 | })?; 762 | 763 | // Safe because we just opened this fd and we know it is valid. 764 | let file = unsafe { File::from_raw_fd(fd) }; 765 | let st = stat(&file)?; 766 | 767 | fid.file = Some(file); 768 | fid.filetype = FileType::Regular; 769 | 770 | // This fid now refers to the newly created file so we need to update the O_PATH fd for it 771 | // as well. 772 | fid.path = lookup(&fid.path, lcreate.name.as_c_str())?; 773 | 774 | Ok(Rlcreate { 775 | qid: st.into(), 776 | iounit: 0, // Allow the client to send requests up to the negotiated max message size. 777 | }) 778 | } 779 | 780 | fn symlink(&mut self, _symlink: &Tsymlink) -> io::Result { 781 | // symlinks are not allowed. 782 | Err(io::Error::from_raw_os_error(libc::EACCES)) 783 | } 784 | 785 | fn mknod(&mut self, _mknod: &Tmknod) -> io::Result { 786 | // No nodes either. 787 | Err(io::Error::from_raw_os_error(libc::EACCES)) 788 | } 789 | 790 | fn rename(&mut self, _rename: &Trename) -> io::Result<()> { 791 | // We cannot support this as an inode may be linked into multiple directories but we don't 792 | // know which one the client wants us to rename. Linux uses rename_at anyway, so we don't 793 | // need to worry about this. 794 | Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) 795 | } 796 | 797 | fn readlink(&mut self, readlink: &Treadlink) -> io::Result { 798 | let fid = self.fids.get(&readlink.fid).ok_or_else(ebadf)?; 799 | 800 | let mut link = vec![0; libc::PATH_MAX as usize]; 801 | 802 | // Safe because this will only modify `link` and we check the return value. 803 | let len = syscall!(unsafe { 804 | libc::readlinkat( 805 | fid.path.as_raw_fd(), 806 | [0].as_ptr(), 807 | link.as_mut_ptr() as *mut libc::c_char, 808 | link.len(), 809 | ) 810 | })? as usize; 811 | link.truncate(len); 812 | let target = P9String::new(link)?; 813 | Ok(Rreadlink { target }) 814 | } 815 | 816 | #[allow(clippy::unnecessary_cast)] // nlink_t is u32 on 32-bit platforms 817 | fn get_attr(&mut self, get_attr: &Tgetattr) -> io::Result { 818 | let fid = self.fids.get_mut(&get_attr.fid).ok_or_else(ebadf)?; 819 | 820 | let st = stat(&fid.path)?; 821 | 822 | Ok(Rgetattr { 823 | valid: P9_GETATTR_BASIC, 824 | qid: st.into(), 825 | mode: st.st_mode, 826 | uid: map_id_from_host(&self.cfg.uid_map, st.st_uid), 827 | gid: map_id_from_host(&self.cfg.gid_map, st.st_gid), 828 | nlink: st.st_nlink as u64, 829 | rdev: st.st_rdev, 830 | size: st.st_size as u64, 831 | blksize: st.st_blksize as u64, 832 | blocks: st.st_blocks as u64, 833 | atime_sec: st.st_atime as u64, 834 | atime_nsec: st.st_atime_nsec as u64, 835 | mtime_sec: st.st_mtime as u64, 836 | mtime_nsec: st.st_mtime_nsec as u64, 837 | ctime_sec: st.st_ctime as u64, 838 | ctime_nsec: st.st_ctime_nsec as u64, 839 | btime_sec: 0, 840 | btime_nsec: 0, 841 | gen: 0, 842 | data_version: 0, 843 | }) 844 | } 845 | 846 | fn set_attr(&mut self, set_attr: &Tsetattr) -> io::Result<()> { 847 | let fid = self.fids.get(&set_attr.fid).ok_or_else(ebadf)?; 848 | let path = string_to_cstring(format!("self/fd/{}", fid.path.as_raw_fd()))?; 849 | 850 | if set_attr.valid & P9_SETATTR_MODE != 0 { 851 | // Safe because this doesn't modify any memory and we check the return value. 852 | syscall!(unsafe { 853 | libc::fchmodat(self.proc.as_raw_fd(), path.as_ptr(), set_attr.mode, 0) 854 | })?; 855 | } 856 | 857 | if set_attr.valid & (P9_SETATTR_UID | P9_SETATTR_GID) != 0 { 858 | let uid = if set_attr.valid & P9_SETATTR_UID != 0 { 859 | set_attr.uid 860 | } else { 861 | -1i32 as u32 862 | }; 863 | let gid = if set_attr.valid & P9_SETATTR_GID != 0 { 864 | set_attr.gid 865 | } else { 866 | -1i32 as u32 867 | }; 868 | 869 | // Safe because this doesn't modify any memory and we check the return value. 870 | syscall!(unsafe { libc::fchownat(self.proc.as_raw_fd(), path.as_ptr(), uid, gid, 0) })?; 871 | } 872 | 873 | if set_attr.valid & P9_SETATTR_SIZE != 0 { 874 | let file = if fid.filetype == FileType::Directory { 875 | return Err(io::Error::from_raw_os_error(libc::EISDIR)); 876 | } else if let Some(ref file) = fid.file { 877 | MaybeOwned::Borrowed(file) 878 | } else { 879 | MaybeOwned::Owned(open_fid(&self.proc, &fid.path, P9_NONBLOCK | P9_RDWR)?) 880 | }; 881 | 882 | file.set_len(set_attr.size)?; 883 | } 884 | 885 | if set_attr.valid & (P9_SETATTR_ATIME | P9_SETATTR_MTIME) != 0 { 886 | let times = [ 887 | libc::timespec { 888 | tv_sec: set_attr.atime_sec as _, 889 | tv_nsec: if set_attr.valid & P9_SETATTR_ATIME == 0 { 890 | libc::UTIME_OMIT 891 | } else if set_attr.valid & P9_SETATTR_ATIME_SET == 0 { 892 | libc::UTIME_NOW 893 | } else { 894 | set_attr.atime_nsec as _ 895 | }, 896 | }, 897 | libc::timespec { 898 | tv_sec: set_attr.mtime_sec as _, 899 | tv_nsec: if set_attr.valid & P9_SETATTR_MTIME == 0 { 900 | libc::UTIME_OMIT 901 | } else if set_attr.valid & P9_SETATTR_MTIME_SET == 0 { 902 | libc::UTIME_NOW 903 | } else { 904 | set_attr.mtime_nsec as _ 905 | }, 906 | }, 907 | ]; 908 | 909 | // Safe because file is valid and we have initialized times fully. 910 | let ret = unsafe { 911 | libc::utimensat( 912 | self.proc.as_raw_fd(), 913 | path.as_ptr(), 914 | × as *const libc::timespec, 915 | 0, 916 | ) 917 | }; 918 | if ret < 0 { 919 | return Err(io::Error::last_os_error()); 920 | } 921 | } 922 | 923 | // The ctime would have been updated by any of the above operations so we only 924 | // need to change it if it was the only option given. 925 | if set_attr.valid & P9_SETATTR_CTIME != 0 && set_attr.valid & (!P9_SETATTR_CTIME) == 0 { 926 | // Setting -1 as the uid and gid will not actually change anything but will 927 | // still update the ctime. 928 | let ret = unsafe { 929 | libc::fchownat( 930 | self.proc.as_raw_fd(), 931 | path.as_ptr(), 932 | libc::uid_t::MAX, 933 | libc::gid_t::MAX, 934 | 0, 935 | ) 936 | }; 937 | if ret < 0 { 938 | return Err(io::Error::last_os_error()); 939 | } 940 | } 941 | 942 | Ok(()) 943 | } 944 | 945 | fn xattr_walk(&mut self, _xattr_walk: &Txattrwalk) -> io::Result { 946 | Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) 947 | } 948 | 949 | fn xattr_create(&mut self, _xattr_create: &Txattrcreate) -> io::Result<()> { 950 | Err(io::Error::from_raw_os_error(libc::EOPNOTSUPP)) 951 | } 952 | 953 | fn readdir(&mut self, readdir: &Treaddir) -> io::Result { 954 | let fid = self.fids.get_mut(&readdir.fid).ok_or_else(ebadf)?; 955 | 956 | if fid.filetype != FileType::Directory { 957 | return Err(io::Error::from_raw_os_error(libc::ENOTDIR)); 958 | } 959 | 960 | // Use an empty Rreaddir struct to figure out the maximum number of bytes that 961 | // can be returned. 962 | let header_size = Rframe { 963 | tag: 0, 964 | msg: Rmessage::Readdir(Rreaddir { 965 | data: Data(Vec::new()), 966 | }), 967 | } 968 | .byte_size(); 969 | let count = min(self.cfg.msize - header_size, readdir.count); 970 | let mut cursor = Cursor::new(Vec::with_capacity(count as usize)); 971 | 972 | let dir = fid.file.as_mut().ok_or_else(ebadf)?; 973 | let mut dirents = read_dir(dir, readdir.offset as libc::c_long)?; 974 | while let Some(dirent) = dirents.next().transpose()? { 975 | let st = statat(&fid.path, dirent.name.as_c_str(), 0)?; 976 | 977 | let entry = Dirent { 978 | qid: st.into(), 979 | offset: dirent.offset, 980 | ty: dirent.type_, 981 | name: dirent.name, 982 | }; 983 | 984 | let byte_size = entry.byte_size() as usize; 985 | 986 | if cursor.get_ref().capacity() - cursor.get_ref().len() < byte_size { 987 | // No more room in the buffer. 988 | break; 989 | } 990 | 991 | entry.encode(&mut cursor)?; 992 | } 993 | 994 | Ok(Rreaddir { 995 | data: Data(cursor.into_inner()), 996 | }) 997 | } 998 | 999 | fn fsync(&mut self, fsync: &Tfsync) -> io::Result<()> { 1000 | let file = self 1001 | .fids 1002 | .get(&fsync.fid) 1003 | .and_then(|fid| fid.file.as_ref()) 1004 | .ok_or_else(ebadf)?; 1005 | 1006 | if fsync.datasync == 0 { 1007 | file.sync_all()?; 1008 | } else { 1009 | file.sync_data()?; 1010 | } 1011 | Ok(()) 1012 | } 1013 | 1014 | /// Implement posix byte range locking code. 1015 | /// Our implementation mirrors that of QEMU/9p - that is to say, 1016 | /// we essentially punt on mirroring lock state between client/server 1017 | /// and defer lock semantics to the VFS layer on the client side. Aside 1018 | /// from fd existence check we always return success. QEMU reference: 1019 | /// 1020 | /// 1021 | /// NOTE: this means that files locked on the client may be interefered with 1022 | /// from either the server's side, or from other clients (guests). This 1023 | /// tracks with QEMU implementation, and will be obviated if crosvm decides 1024 | /// to drop 9p in favor of virtio-fs. QEMU only allows for a single client, 1025 | /// and we leave it to users of the crate to provide actual lock handling. 1026 | fn lock(&mut self, lock: &Tlock) -> io::Result { 1027 | // Ensure fd passed in TLOCK request exists and has a mapping. 1028 | let fd = self 1029 | .fids 1030 | .get(&lock.fid) 1031 | .and_then(|fid| fid.file.as_ref()) 1032 | .ok_or_else(ebadf)? 1033 | .as_raw_fd(); 1034 | 1035 | syscall!(unsafe { 1036 | // Safe because zero-filled libc::stat is a valid value, fstat 1037 | // populates the struct fields. 1038 | let mut stbuf: libc::stat64 = std::mem::zeroed(); 1039 | // Safe because this doesn't modify memory and we check the return value. 1040 | libc::fstat64(fd, &mut stbuf) 1041 | })?; 1042 | 1043 | Ok(Rlock { 1044 | status: P9_LOCK_SUCCESS, 1045 | }) 1046 | } 1047 | 1048 | /// 1049 | /// Much like lock(), defer locking semantics to VFS and return success. 1050 | /// 1051 | fn get_lock(&mut self, get_lock: &Tgetlock) -> io::Result { 1052 | // Ensure fd passed in GETTLOCK request exists and has a mapping. 1053 | let fd = self 1054 | .fids 1055 | .get(&get_lock.fid) 1056 | .and_then(|fid| fid.file.as_ref()) 1057 | .ok_or_else(ebadf)? 1058 | .as_raw_fd(); 1059 | 1060 | // Safe because this doesn't modify memory and we check the return value. 1061 | syscall!(unsafe { 1062 | let mut stbuf: libc::stat64 = std::mem::zeroed(); 1063 | libc::fstat64(fd, &mut stbuf) 1064 | })?; 1065 | 1066 | Ok(Rgetlock { 1067 | type_: P9_LOCK_TYPE_UNLCK, 1068 | start: get_lock.start, 1069 | length: get_lock.length, 1070 | proc_id: get_lock.proc_id, 1071 | client_id: get_lock.client_id.clone(), 1072 | }) 1073 | } 1074 | 1075 | fn link(&mut self, link: Tlink) -> io::Result<()> { 1076 | let target = self.fids.get(&link.fid).ok_or_else(ebadf)?; 1077 | let path = string_to_cstring(format!("self/fd/{}", target.path.as_raw_fd()))?; 1078 | 1079 | let dir = self.fids.get(&link.dfid).ok_or_else(ebadf)?; 1080 | 1081 | // Safe because this doesn't modify any memory and we check the return value. 1082 | syscall!(unsafe { 1083 | libc::linkat( 1084 | self.proc.as_raw_fd(), 1085 | path.as_ptr(), 1086 | dir.path.as_raw_fd(), 1087 | link.name.as_ptr(), 1088 | libc::AT_SYMLINK_FOLLOW, 1089 | ) 1090 | })?; 1091 | Ok(()) 1092 | } 1093 | 1094 | fn mkdir(&mut self, mkdir: Tmkdir) -> io::Result { 1095 | let fid = self.fids.get(&mkdir.dfid).ok_or_else(ebadf)?; 1096 | 1097 | // Safe because this doesn't modify any memory and we check the return value. 1098 | syscall!(unsafe { libc::mkdirat(fid.path.as_raw_fd(), mkdir.name.as_ptr(), mkdir.mode) })?; 1099 | Ok(Rmkdir { 1100 | qid: statat(&fid.path, mkdir.name.as_c_str(), 0).map(Qid::from)?, 1101 | }) 1102 | } 1103 | 1104 | fn rename_at(&mut self, rename_at: Trenameat) -> io::Result<()> { 1105 | let olddir = self.fids.get(&rename_at.olddirfid).ok_or_else(ebadf)?; 1106 | let newdir = self.fids.get(&rename_at.newdirfid).ok_or_else(ebadf)?; 1107 | 1108 | // Safe because this doesn't modify any memory and we check the return value. 1109 | syscall!(unsafe { 1110 | libc::renameat( 1111 | olddir.path.as_raw_fd(), 1112 | rename_at.oldname.as_ptr(), 1113 | newdir.path.as_raw_fd(), 1114 | rename_at.newname.as_ptr(), 1115 | ) 1116 | })?; 1117 | 1118 | Ok(()) 1119 | } 1120 | 1121 | fn unlink_at(&mut self, unlink_at: Tunlinkat) -> io::Result<()> { 1122 | let dir = self.fids.get(&unlink_at.dirfd).ok_or_else(ebadf)?; 1123 | 1124 | syscall!(unsafe { 1125 | libc::unlinkat( 1126 | dir.path.as_raw_fd(), 1127 | unlink_at.name.as_ptr(), 1128 | unlink_at.flags as libc::c_int, 1129 | ) 1130 | })?; 1131 | 1132 | Ok(()) 1133 | } 1134 | } 1135 | 1136 | #[cfg(test)] 1137 | mod tests; 1138 | -------------------------------------------------------------------------------- /src/server/read_dir.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The ChromiumOS Authors 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std::io::Result; 6 | use std::os::unix::io::AsRawFd; 7 | 8 | use libc::F_DUPFD_CLOEXEC; 9 | 10 | use crate::protocol::P9String; 11 | 12 | pub struct DirEntry { 13 | pub ino: libc::ino64_t, 14 | pub offset: u64, 15 | pub type_: u8, 16 | pub name: P9String, 17 | } 18 | 19 | pub struct ReadDir { 20 | dir: *mut libc::DIR, 21 | } 22 | 23 | impl Drop for ReadDir { 24 | fn drop(&mut self) { 25 | // SAFETY: We know that `self.dir` is a valid pointer allocated by the C library. 26 | unsafe { libc::closedir(self.dir) }; 27 | } 28 | } 29 | 30 | impl ReadDir { 31 | /// Return the next directory entry. This is implemented as a separate method rather than via 32 | /// the `Iterator` trait because rust doesn't currently support generic associated types. 33 | #[allow(clippy::should_implement_trait)] 34 | pub fn next(&mut self) -> Option> { 35 | let dirent64 = unsafe { libc::readdir64(self.dir) }; 36 | if dirent64.is_null() { 37 | return None; 38 | } 39 | 40 | // SAFETY: `dirent64` is a non-NULL pointer, as checked above. 41 | // We trust the C library to return a correctly-aligned, valid pointer. 42 | let (d_ino, d_off, d_type) = 43 | unsafe { ((*dirent64).d_ino, (*dirent64).d_off, (*dirent64).d_type) }; 44 | 45 | let d_name: &[u8] = unsafe { std::mem::transmute((*dirent64).d_name.as_ref()) }; 46 | let name = match P9String::new(strip_padding(d_name)) { 47 | Ok(name) => name, 48 | Err(e) => return Some(Err(e)), 49 | }; 50 | 51 | let entry = DirEntry { 52 | ino: d_ino, 53 | offset: d_off as u64, 54 | type_: d_type, 55 | name, 56 | }; 57 | 58 | Some(Ok(entry)) 59 | } 60 | } 61 | 62 | pub fn read_dir(dir: &D, offset: libc::c_long) -> Result { 63 | let dup_fd = unsafe { libc::fcntl(dir.as_raw_fd(), F_DUPFD_CLOEXEC, 0) }; 64 | let dir = unsafe { libc::fdopendir(dup_fd) }; 65 | if dir.is_null() { 66 | unsafe { libc::close(dup_fd) }; 67 | return Err(std::io::Error::last_os_error()); 68 | } 69 | 70 | let read_dir = ReadDir { dir }; 71 | 72 | // Safe because this doesn't modify any memory and we check the return value. 73 | unsafe { libc::seekdir(read_dir.dir, offset) }; 74 | 75 | Ok(read_dir) 76 | } 77 | 78 | // Trims any trailing '\0' bytes. Panics if `b` doesn't contain any '\0' bytes. 79 | fn strip_padding(b: &[u8]) -> &[u8] { 80 | // It would be nice if we could use memchr here but that's locked behind an unstable gate. 81 | let pos = b 82 | .iter() 83 | .position(|&c| c == 0) 84 | .expect("`b` doesn't contain any nul bytes"); 85 | 86 | &b[..pos] 87 | } 88 | 89 | #[cfg(test)] 90 | mod test { 91 | use super::*; 92 | 93 | #[test] 94 | fn padded_cstrings() { 95 | assert_eq!(strip_padding(b".\0\0\0\0\0\0\0"), b"."); 96 | assert_eq!(strip_padding(b"..\0\0\0\0\0\0"), b".."); 97 | assert_eq!(strip_padding(b"normal cstring\0"), b"normal cstring"); 98 | assert_eq!(strip_padding(b"\0\0\0\0"), b""); 99 | assert_eq!(strip_padding(b"interior\0nul bytes\0\0\0"), b"interior"); 100 | } 101 | 102 | #[test] 103 | #[should_panic(expected = "`b` doesn't contain any nul bytes")] 104 | fn no_nul_byte() { 105 | strip_padding(b"no nul bytes in string"); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/server/tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The ChromiumOS Authors 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | use std::borrow::Cow; 6 | use std::collections::HashSet; 7 | use std::collections::VecDeque; 8 | use std::env; 9 | use std::ffi::CString; 10 | use std::ffi::OsString; 11 | use std::fs; 12 | use std::fs::File; 13 | use std::io; 14 | use std::io::Cursor; 15 | use std::mem; 16 | use std::ops::Deref; 17 | use std::os::unix::ffi::OsStringExt; 18 | use std::os::unix::fs::symlink; 19 | use std::os::unix::fs::MetadataExt; 20 | use std::path::Component; 21 | use std::path::Path; 22 | use std::path::PathBuf; 23 | use std::u32; 24 | 25 | use super::*; 26 | 27 | // Used to indicate that there is no fid associated with this message. 28 | const P9_NOFID: u32 = u32::MAX; 29 | 30 | // The fid associated with the root directory of the server. 31 | const ROOT_FID: u32 = 1; 32 | 33 | // The pid of the server process, cannot be 1 since that's the kernel init 34 | const SERVER_PID: u32 = 5; 35 | 36 | // How big we want the default buffer to be when running tests. 37 | const DEFAULT_BUFFER_SIZE: u32 = 4096; 38 | 39 | // How big we want to make randomly generated files 40 | const LOCAL_FILE_LEN: u64 = 200; 41 | 42 | // Joins `path` to `buf`. If `path` is '..', removes the last component from `buf` 43 | // only if `buf` != `root` but does nothing if `buf` == `root`. Pushes `path` onto 44 | // `buf` if it is a normal path component. 45 | // 46 | // Returns an error if `path` is absolute, has more than one component, or contains 47 | // a '.' component. 48 | fn join_path, R: AsRef>( 49 | mut buf: PathBuf, 50 | path: P, 51 | root: R, 52 | ) -> io::Result { 53 | let path = path.as_ref(); 54 | let root = root.as_ref(); 55 | debug_assert!(buf.starts_with(root)); 56 | 57 | if path.components().count() > 1 { 58 | return Err(io::Error::from_raw_os_error(libc::EINVAL)); 59 | } 60 | 61 | for component in path.components() { 62 | match component { 63 | // Prefix should only appear on windows systems. 64 | Component::Prefix(_) => return Err(io::Error::from_raw_os_error(libc::EINVAL)), 65 | // Absolute paths are not allowed. 66 | Component::RootDir => return Err(io::Error::from_raw_os_error(libc::EINVAL)), 67 | // '.' elements are not allowed. 68 | Component::CurDir => return Err(io::Error::from_raw_os_error(libc::EINVAL)), 69 | Component::ParentDir => { 70 | // We only remove the parent path if we are not already at the root of the 71 | // file system. 72 | if buf != root { 73 | buf.pop(); 74 | } 75 | } 76 | Component::Normal(element) => buf.push(element), 77 | } 78 | } 79 | 80 | Ok(buf) 81 | } 82 | 83 | // Automatically deletes the path it contains when it goes out of scope. 84 | struct ScopedPath>(P); 85 | 86 | impl> AsRef for ScopedPath

{ 87 | fn as_ref(&self) -> &Path { 88 | self.0.as_ref() 89 | } 90 | } 91 | 92 | impl> Deref for ScopedPath

{ 93 | type Target = Path; 94 | 95 | fn deref(&self) -> &Self::Target { 96 | self.0.as_ref() 97 | } 98 | } 99 | 100 | impl> Drop for ScopedPath

{ 101 | fn drop(&mut self) { 102 | if let Err(e) = fs::remove_dir_all(&**self) { 103 | println!("Failed to remove {}: {}", self.display(), e); 104 | } 105 | } 106 | } 107 | 108 | enum DirEntry<'a> { 109 | File { 110 | name: &'a str, 111 | content: &'a [u8], 112 | }, 113 | Directory { 114 | name: &'a str, 115 | entries: &'a [DirEntry<'a>], 116 | }, 117 | Symlink { 118 | name: &'a str, 119 | target: &'a str, 120 | }, 121 | } 122 | 123 | impl<'a> DirEntry<'a> { 124 | // Creates `self` in the path given by `dir`. 125 | // TODO(b/228627457): clippy is warning about the `Cow` below, but it is necessary 126 | #[allow(clippy::ptr_arg)] 127 | fn create(&self, dir: &mut Cow) { 128 | match *self { 129 | DirEntry::File { name, content } => { 130 | let mut f = File::create(dir.join(name)).expect("failed to create file"); 131 | f.write_all(content).expect("failed to write file content"); 132 | } 133 | DirEntry::Directory { name, entries } => { 134 | dir.to_mut().push(name); 135 | 136 | fs::create_dir_all(&**dir).expect("failed to create directory"); 137 | for e in entries { 138 | e.create(dir); 139 | } 140 | 141 | assert!(dir.to_mut().pop()); 142 | } 143 | DirEntry::Symlink { name, target } => { 144 | symlink(target, dir.join(name)).expect("failed to create symlink"); 145 | } 146 | } 147 | } 148 | } 149 | 150 | // Creates a file with `name` in `dir` and fills it with random 151 | // content. 152 | fn create_local_file>(dir: P, name: &str) -> Vec { 153 | let mut content = Vec::new(); 154 | File::open("/dev/urandom") 155 | .and_then(|f| f.take(LOCAL_FILE_LEN).read_to_end(&mut content)) 156 | .expect("failed to read from /dev/urandom"); 157 | 158 | let f = DirEntry::File { 159 | name, 160 | content: &content, 161 | }; 162 | f.create(&mut Cow::from(dir.as_ref())); 163 | 164 | content 165 | } 166 | 167 | // Create a symlink named `name` that links to `target`. 168 | fn create_local_symlink>(dir: P, name: &str, target: &str) { 169 | let f = DirEntry::Symlink { name, target }; 170 | f.create(&mut Cow::from(dir.as_ref())); 171 | } 172 | 173 | fn check_qid(qid: &Qid, md: &fs::Metadata) { 174 | let ty = if md.is_dir() { 175 | P9_QTDIR 176 | } else if md.is_file() { 177 | P9_QTFILE 178 | } else if md.file_type().is_symlink() { 179 | P9_QTSYMLINK 180 | } else { 181 | panic!("unknown file type: {:?}", md.file_type()); 182 | }; 183 | assert_eq!(qid.ty, ty); 184 | assert_eq!(qid.version, md.mtime() as u32); 185 | assert_eq!(qid.path, md.ino()); 186 | } 187 | 188 | fn check_attr(server: &mut Server, fid: u32, md: &fs::Metadata) { 189 | let tgetattr = Tgetattr { 190 | fid, 191 | request_mask: P9_GETATTR_BASIC, 192 | }; 193 | 194 | let rgetattr = server.get_attr(&tgetattr).expect("failed to call get_attr"); 195 | 196 | let ty = if md.is_dir() { 197 | P9_QTDIR 198 | } else if md.is_file() { 199 | P9_QTFILE 200 | } else if md.file_type().is_symlink() { 201 | P9_QTSYMLINK 202 | } else { 203 | panic!("unknown file type: {:?}", md.file_type()); 204 | }; 205 | assert_eq!(rgetattr.valid, P9_GETATTR_BASIC); 206 | assert_eq!(rgetattr.qid.ty, ty); 207 | assert_eq!(rgetattr.qid.version, md.mtime() as u32); 208 | assert_eq!(rgetattr.qid.path, md.ino()); 209 | assert_eq!(rgetattr.mode, md.mode()); 210 | assert_eq!(rgetattr.uid, md.uid()); 211 | assert_eq!(rgetattr.gid, md.gid()); 212 | assert_eq!(rgetattr.nlink, md.nlink()); 213 | assert_eq!(rgetattr.rdev, md.rdev()); 214 | assert_eq!(rgetattr.size, md.size()); 215 | assert_eq!(rgetattr.atime_sec, md.atime() as u64); 216 | assert_eq!(rgetattr.atime_nsec, md.atime_nsec() as u64); 217 | assert_eq!(rgetattr.mtime_sec, md.mtime() as u64); 218 | assert_eq!(rgetattr.mtime_nsec, md.mtime_nsec() as u64); 219 | assert_eq!(rgetattr.ctime_sec, md.ctime() as u64); 220 | assert_eq!(rgetattr.ctime_nsec, md.ctime_nsec() as u64); 221 | assert_eq!(rgetattr.btime_sec, 0); 222 | assert_eq!(rgetattr.btime_nsec, 0); 223 | assert_eq!(rgetattr.gen, 0); 224 | assert_eq!(rgetattr.data_version, 0); 225 | } 226 | 227 | fn check_content(server: &mut Server, content: &[u8], fid: u32) { 228 | for offset in 0..content.len() { 229 | let tread = Tread { 230 | fid, 231 | offset: offset as u64, 232 | count: DEFAULT_BUFFER_SIZE, 233 | }; 234 | 235 | let rread = server.read(&tread).expect("failed to read file"); 236 | assert_eq!(content[offset..], rread.data[..]); 237 | } 238 | } 239 | 240 | fn walk>( 241 | server: &mut Server, 242 | start: P, 243 | fid: u32, 244 | newfid: u32, 245 | names: Vec, 246 | ) { 247 | let mut mds = Vec::with_capacity(names.len()); 248 | let mut buf = start.into(); 249 | for name in &names { 250 | let name = std::str::from_utf8(name.as_bytes()).unwrap(); 251 | let path = Path::new(&name); 252 | buf.push(path); 253 | mds.push( 254 | buf.symlink_metadata() 255 | .expect("failed to get metadata for path"), 256 | ); 257 | } 258 | 259 | let twalk = Twalk { 260 | fid, 261 | newfid, 262 | wnames: names, 263 | }; 264 | 265 | let rwalk = server.walk(twalk).expect("failed to walk directoy"); 266 | assert_eq!(mds.len(), rwalk.wqids.len()); 267 | for (md, qid) in mds.iter().zip(rwalk.wqids.iter()) { 268 | check_qid(qid, md); 269 | } 270 | } 271 | 272 | fn open>( 273 | server: &mut Server, 274 | dir: P, 275 | dir_fid: u32, 276 | name: &str, 277 | fid: u32, 278 | flags: u32, 279 | ) -> io::Result { 280 | let wnames = if name.is_empty() { 281 | vec![] 282 | } else { 283 | vec![P9String::new(name.as_bytes())?] 284 | }; 285 | walk(server, dir, dir_fid, fid, wnames); 286 | 287 | let tlopen = Tlopen { fid, flags }; 288 | 289 | server.lopen(&tlopen) 290 | } 291 | 292 | fn write>(server: &mut Server, dir: P, name: &str, fid: u32, flags: u32) { 293 | let file_path = dir.as_ref().join(name); 294 | let file_len = if file_path.exists() { 295 | fs::symlink_metadata(&file_path) 296 | .expect("unable to get metadata for file") 297 | .len() as usize 298 | } else { 299 | 0usize 300 | }; 301 | let mut new_content = Vec::new(); 302 | File::open("/dev/urandom") 303 | .and_then(|f| f.take(200).read_to_end(&mut new_content)) 304 | .expect("failed to read from /dev/urandom"); 305 | 306 | let twrite = Twrite { 307 | fid, 308 | offset: 0, 309 | data: Data(new_content), 310 | }; 311 | 312 | let rwrite = server.write(&twrite).expect("failed to write file"); 313 | assert_eq!(rwrite.count, twrite.data.len() as u32); 314 | 315 | let tfsync = Tfsync { fid, datasync: 0 }; 316 | server.fsync(&tfsync).expect("failed to sync file contents"); 317 | 318 | let actual_content = fs::read(file_path).expect("failed to read back content from file"); 319 | 320 | // If the file was opened append-only, then the content should have been 321 | // written to the end even though the offset was 0. 322 | let idx = if flags & P9_APPEND == 0 { 0 } else { file_len }; 323 | assert_eq!(actual_content[idx..], twrite.data[..]); 324 | } 325 | 326 | fn create>( 327 | server: &mut Server, 328 | dir: P, 329 | dir_fid: u32, 330 | fid: u32, 331 | name: &str, 332 | flags: u32, 333 | mode: u32, 334 | ) -> io::Result { 335 | // The `fid` in the lcreate call initially points to the directory 336 | // but is supposed to point to the newly created file after the call 337 | // completes. Duplicate the fid so that we don't end up consuming the 338 | // directory fid. 339 | walk(server, dir, dir_fid, fid, Vec::new()); 340 | 341 | let tlcreate = Tlcreate { 342 | fid, 343 | name: P9String::new(name)?, 344 | flags, 345 | mode, 346 | gid: 0, 347 | }; 348 | 349 | server.lcreate(tlcreate) 350 | } 351 | 352 | struct Readdir<'a> { 353 | server: &'a mut Server, 354 | fid: u32, 355 | offset: u64, 356 | cursor: Cursor>, 357 | } 358 | 359 | impl<'a> Iterator for Readdir<'a> { 360 | type Item = Dirent; 361 | 362 | fn next(&mut self) -> Option { 363 | if self.cursor.position() >= self.cursor.get_ref().len() as u64 { 364 | let treaddir = Treaddir { 365 | fid: self.fid, 366 | offset: self.offset, 367 | count: DEFAULT_BUFFER_SIZE, 368 | }; 369 | 370 | let Rreaddir { data } = self 371 | .server 372 | .readdir(&treaddir) 373 | .expect("failed to read directory"); 374 | if data.is_empty() { 375 | // No more entries. 376 | return None; 377 | } 378 | 379 | mem::drop(mem::replace(&mut self.cursor, Cursor::new(data.0))); 380 | } 381 | 382 | let dirent: Dirent = WireFormat::decode(&mut self.cursor).expect("failed to decode dirent"); 383 | self.offset = dirent.offset; 384 | 385 | Some(dirent) 386 | } 387 | } 388 | 389 | fn readdir(server: &mut Server, fid: u32) -> Readdir { 390 | Readdir { 391 | server, 392 | fid, 393 | offset: 0, 394 | cursor: Cursor::new(Vec::new()), 395 | } 396 | } 397 | 398 | // Sets up the server to start handling messages. Creates a new temporary 399 | // directory to act as the server root and sends an initial Tattach message. 400 | // At the end of setup, fid 1 points to the root of the server. 401 | fn setup>(name: P) -> (ScopedPath, Server) { 402 | let mut test_dir = env::var_os("T") 403 | .map(PathBuf::from) 404 | .unwrap_or_else(env::temp_dir); 405 | test_dir.push(name); 406 | 407 | let mut os_str = OsString::from(test_dir); 408 | os_str.push(".XXXXXX"); 409 | 410 | // Create a c string and release ownership. This seems like the only way 411 | // to get a *mut c_char. 412 | let buf = CString::new(os_str.into_vec()) 413 | .expect("failed to create CString") 414 | .into_raw(); 415 | 416 | // Safe because this will only modify the contents of `buf`. 417 | let ret = unsafe { libc::mkdtemp(buf) }; 418 | 419 | // Take ownership of the buffer back before checking the result. Safe because 420 | // this was created by a call to into_raw() above and mkdtemp will not overwrite 421 | // the trailing '\0'. 422 | let buf = unsafe { CString::from_raw(buf) }; 423 | 424 | assert!(!ret.is_null()); 425 | 426 | let test_dir = ScopedPath(OsString::from_vec(buf.into_bytes())); 427 | 428 | // Create a basic file system hierarchy. 429 | let entries = [ 430 | DirEntry::Directory { 431 | name: "subdir", 432 | entries: &[ 433 | DirEntry::File { 434 | name: "b", 435 | content: b"hello, world!", 436 | }, 437 | DirEntry::Directory { 438 | name: "nested", 439 | entries: &[DirEntry::File { 440 | name: "Огонь по готовности!", 441 | content: &[ 442 | 0xe9u8, 0xbeu8, 0x8du8, 0xe3u8, 0x81u8, 0x8cu8, 0xe6u8, 0x88u8, 0x91u8, 443 | 0xe3u8, 0x81u8, 0x8cu8, 0xe6u8, 0x95u8, 0xb5u8, 0xe3u8, 0x82u8, 0x92u8, 444 | 0xe5u8, 0x96u8, 0xb0u8, 0xe3u8, 0x82u8, 0x89u8, 0xe3u8, 0x81u8, 0x86u8, 445 | 0x21u8, 446 | ], 447 | }], 448 | }, 449 | ], 450 | }, 451 | DirEntry::File { 452 | name: "世界.txt", 453 | content: &[ 454 | 0xe3u8, 0x81u8, 0x93u8, 0xe3u8, 0x82u8, 0x93u8, 0xe3u8, 0x81u8, 0xabu8, 0xe3u8, 455 | 0x81u8, 0xa1u8, 0xe3u8, 0x81u8, 0xafu8, 456 | ], 457 | }, 458 | ]; 459 | 460 | for e in &entries { 461 | e.create(&mut Cow::from(&*test_dir)); 462 | } 463 | 464 | let md = test_dir 465 | .symlink_metadata() 466 | .expect("failed to get metadata for root dir"); 467 | 468 | let mut server = Server::new(&*test_dir, Default::default(), Default::default()) 469 | .expect("Failed to create server"); 470 | 471 | let tversion = Tversion { 472 | msize: DEFAULT_BUFFER_SIZE, 473 | version: P9String::new("9P2000.L").unwrap(), 474 | }; 475 | 476 | let rversion = server 477 | .version(&tversion) 478 | .expect("failed to get version from server"); 479 | assert_eq!(rversion.msize, DEFAULT_BUFFER_SIZE); 480 | assert_eq!(rversion.version, "9P2000.L"); 481 | 482 | let tattach = Tattach { 483 | fid: ROOT_FID, 484 | afid: P9_NOFID, 485 | uname: P9String::new("unittest").unwrap(), 486 | aname: P9String::new("").unwrap(), 487 | n_uname: 1000, 488 | }; 489 | 490 | let rattach = server.attach(&tattach).expect("failed to attach to server"); 491 | check_qid(&rattach.qid, &md); 492 | 493 | (test_dir, server) 494 | } 495 | 496 | #[test] 497 | fn path_joins() { 498 | let root = PathBuf::from("/a/b/c"); 499 | let path = PathBuf::from("/a/b/c/d/e/f"); 500 | 501 | assert_eq!( 502 | &join_path(path.clone(), "nested", &root).expect("normal"), 503 | Path::new("/a/b/c/d/e/f/nested") 504 | ); 505 | 506 | let p1 = join_path(path, "..", &root).expect("parent 1"); 507 | assert_eq!(&p1, Path::new("/a/b/c/d/e/")); 508 | 509 | let p2 = join_path(p1, "..", &root).expect("parent 2"); 510 | assert_eq!(&p2, Path::new("/a/b/c/d/")); 511 | 512 | let p3 = join_path(p2, "..", &root).expect("parent 3"); 513 | assert_eq!(&p3, Path::new("/a/b/c/")); 514 | 515 | let p4 = join_path(p3, "..", &root).expect("parent of root"); 516 | assert_eq!(&p4, Path::new("/a/b/c/")); 517 | } 518 | 519 | #[test] 520 | fn invalid_joins() { 521 | let root = PathBuf::from("/a"); 522 | let path = PathBuf::from("/a/b"); 523 | 524 | join_path(path.clone(), ".", &root).expect_err("current directory"); 525 | join_path(path.clone(), "c/d/e", &root).expect_err("too many components"); 526 | join_path(path, "/c/d/e", &root).expect_err("absolute path"); 527 | } 528 | 529 | #[test] 530 | fn clunk() { 531 | let (_test_dir, mut server) = setup("clunk"); 532 | 533 | let tclunk = Tclunk { fid: ROOT_FID }; 534 | server.clunk(&tclunk).expect("failed to clunk root fid"); 535 | } 536 | 537 | #[test] 538 | fn get_attr() { 539 | let (test_dir, mut server) = setup("get_attr"); 540 | 541 | let md = test_dir 542 | .symlink_metadata() 543 | .expect("failed to get metadata for test dir"); 544 | 545 | check_attr(&mut server, ROOT_FID, &md); 546 | } 547 | 548 | #[test] 549 | fn tree_walk() { 550 | let (test_dir, mut server) = setup("readdir"); 551 | 552 | let mut next_fid = ROOT_FID + 1; 553 | 554 | let mut dirs = VecDeque::new(); 555 | dirs.push_back(test_dir.to_path_buf()); 556 | 557 | while let Some(dir) = dirs.pop_front() { 558 | let dfid = next_fid; 559 | next_fid += 1; 560 | 561 | let wnames: Vec = dir 562 | .strip_prefix(&test_dir) 563 | .expect("test directory is not prefix of subdir") 564 | .components() 565 | .map(|c| P9String::try_from(c.as_os_str()).unwrap()) 566 | .collect(); 567 | walk(&mut server, &*test_dir, ROOT_FID, dfid, wnames); 568 | 569 | let md = dir.symlink_metadata().expect("failed to get metadata"); 570 | 571 | check_attr(&mut server, dfid, &md); 572 | 573 | let fid = next_fid; 574 | next_fid += 1; 575 | open(&mut server, &dir, dfid, "", fid, P9_DIRECTORY).expect("Failed to open directory"); 576 | for dirent in readdir(&mut server, fid) { 577 | if dirent.name == "." || dirent.name == ".." { 578 | continue; 579 | } 580 | 581 | let dir_name = Path::new(std::str::from_utf8(dirent.name.as_bytes()).unwrap()); 582 | 583 | let entry_path = dir.join(dir_name); 584 | assert!( 585 | entry_path.exists(), 586 | "directory entry \"{}\" does not exist", 587 | entry_path.display() 588 | ); 589 | let md = fs::symlink_metadata(&entry_path).expect("failed to get metadata for entry"); 590 | 591 | let ty = if md.is_dir() { 592 | dirs.push_back(dir.join(dir_name)); 593 | libc::DT_DIR 594 | } else if md.is_file() { 595 | libc::DT_REG 596 | } else if md.file_type().is_symlink() { 597 | libc::DT_LNK 598 | } else { 599 | panic!("unknown file type: {:?}", md.file_type()); 600 | }; 601 | 602 | assert_eq!(dirent.ty, ty); 603 | check_qid(&dirent.qid, &md); 604 | } 605 | 606 | let tclunk = Tclunk { fid }; 607 | server.clunk(&tclunk).expect("failed to clunk fid"); 608 | } 609 | } 610 | 611 | #[test] 612 | fn create_existing_file() { 613 | let (test_dir, mut server) = setup("create_existing"); 614 | 615 | let name = "existing"; 616 | create_local_file(&test_dir, name); 617 | 618 | let fid = ROOT_FID + 1; 619 | create( 620 | &mut server, 621 | &*test_dir, 622 | ROOT_FID, 623 | fid, 624 | name, 625 | P9_APPEND, 626 | 0o644, 627 | ) 628 | .expect_err("successfully created existing file"); 629 | } 630 | 631 | enum SetAttrKind { 632 | File, 633 | Directory, 634 | } 635 | 636 | fn set_attr_test(kind: SetAttrKind, set_fields: F) -> io::Result 637 | where 638 | F: FnOnce(&mut Tsetattr), 639 | { 640 | let (test_dir, mut server) = setup("set_attr"); 641 | 642 | let name = "existing"; 643 | match kind { 644 | SetAttrKind::File => { 645 | create_local_file(&test_dir, name); 646 | } 647 | SetAttrKind::Directory => { 648 | let tmkdir = Tmkdir { 649 | dfid: ROOT_FID, 650 | name: P9String::new(name).unwrap(), 651 | mode: 0o755, 652 | gid: 0, 653 | }; 654 | 655 | let rmkdir = server.mkdir(tmkdir).expect("failed to create directory"); 656 | let md = fs::symlink_metadata(test_dir.join(name)) 657 | .expect("failed to get metadata for directory"); 658 | 659 | assert!(md.is_dir()); 660 | check_qid(&rmkdir.qid, &md); 661 | } 662 | }; 663 | 664 | let fid = ROOT_FID + 1; 665 | walk( 666 | &mut server, 667 | &*test_dir, 668 | ROOT_FID, 669 | fid, 670 | vec![P9String::new(name).unwrap()], 671 | ); 672 | 673 | let mut tsetattr = Tsetattr { 674 | fid, 675 | valid: 0, 676 | mode: 0, 677 | uid: 0, 678 | gid: 0, 679 | size: 0, 680 | atime_sec: 0, 681 | atime_nsec: 0, 682 | mtime_sec: 0, 683 | mtime_nsec: 0, 684 | }; 685 | 686 | set_fields(&mut tsetattr); 687 | server.set_attr(&tsetattr)?; 688 | 689 | fs::symlink_metadata(test_dir.join(name)) 690 | } 691 | 692 | #[test] 693 | fn set_len() { 694 | let len = 661; 695 | let md = set_attr_test(SetAttrKind::File, |tsetattr| { 696 | tsetattr.valid = P9_SETATTR_SIZE; 697 | tsetattr.size = len; 698 | }) 699 | .expect("failed to run set length of file"); 700 | 701 | assert_eq!(md.size(), len); 702 | } 703 | 704 | #[test] 705 | fn set_file_mode() { 706 | let mode = 0o640; 707 | let md = set_attr_test(SetAttrKind::File, |tsetattr| { 708 | tsetattr.valid = P9_SETATTR_MODE; 709 | tsetattr.mode = mode; 710 | }) 711 | .expect("failed to set mode"); 712 | 713 | assert_eq!(md.mode() & 0o777, mode); 714 | } 715 | 716 | #[test] 717 | fn set_file_mtime() { 718 | let (secs, nanos) = (1245247825, 524617); 719 | let md = set_attr_test(SetAttrKind::File, |tsetattr| { 720 | tsetattr.valid = P9_SETATTR_MTIME | P9_SETATTR_MTIME_SET; 721 | tsetattr.mtime_sec = secs; 722 | tsetattr.mtime_nsec = nanos; 723 | }) 724 | .expect("failed to set mtime"); 725 | 726 | assert_eq!(md.mtime() as u64, secs); 727 | assert_eq!(md.mtime_nsec() as u64, nanos); 728 | } 729 | 730 | #[test] 731 | fn set_file_atime() { 732 | let (secs, nanos) = (9247605, 4016); 733 | let md = set_attr_test(SetAttrKind::File, |tsetattr| { 734 | tsetattr.valid = P9_SETATTR_ATIME | P9_SETATTR_ATIME_SET; 735 | tsetattr.atime_sec = secs; 736 | tsetattr.atime_nsec = nanos; 737 | }) 738 | .expect("failed to set atime"); 739 | 740 | assert_eq!(md.atime() as u64, secs); 741 | assert_eq!(md.atime_nsec() as u64, nanos); 742 | } 743 | 744 | #[test] 745 | fn set_dir_mode() { 746 | let mode = 0o640; 747 | let md = set_attr_test(SetAttrKind::Directory, |tsetattr| { 748 | tsetattr.valid = P9_SETATTR_MODE; 749 | tsetattr.mode = mode; 750 | }) 751 | .expect("failed to set mode"); 752 | 753 | assert_eq!(md.mode() & 0o777, mode); 754 | } 755 | 756 | #[test] 757 | fn set_dir_mtime() { 758 | let (secs, nanos) = (1245247825, 524617); 759 | let md = set_attr_test(SetAttrKind::Directory, |tsetattr| { 760 | tsetattr.valid = P9_SETATTR_MTIME | P9_SETATTR_MTIME_SET; 761 | tsetattr.mtime_sec = secs; 762 | tsetattr.mtime_nsec = nanos; 763 | }) 764 | .expect("failed to set mtime"); 765 | 766 | assert_eq!(md.mtime() as u64, secs); 767 | assert_eq!(md.mtime_nsec() as u64, nanos); 768 | } 769 | 770 | #[test] 771 | fn set_dir_atime() { 772 | let (secs, nanos) = (9247605, 4016); 773 | let md = set_attr_test(SetAttrKind::Directory, |tsetattr| { 774 | tsetattr.valid = P9_SETATTR_ATIME | P9_SETATTR_ATIME_SET; 775 | tsetattr.atime_sec = secs; 776 | tsetattr.atime_nsec = nanos; 777 | }) 778 | .expect("failed to set atime"); 779 | 780 | assert_eq!(md.atime() as u64, secs); 781 | assert_eq!(md.atime_nsec() as u64, nanos); 782 | } 783 | 784 | #[test] 785 | fn huge_directory() { 786 | let (test_dir, mut server) = setup("huge_directory"); 787 | 788 | let name = "newdir"; 789 | let newdir = test_dir.join(name); 790 | fs::create_dir(&newdir).expect("failed to create directory"); 791 | 792 | let dfid = ROOT_FID + 1; 793 | walk( 794 | &mut server, 795 | &*test_dir, 796 | ROOT_FID, 797 | dfid, 798 | vec![P9String::new(name).unwrap()], 799 | ); 800 | 801 | // Create ~4K files in the directory and then attempt to read them all. 802 | let mut filenames = HashSet::with_capacity(4096); 803 | for i in 0..4096 { 804 | let name = format!("file_{}", i); 805 | create_local_file(&newdir, &name); 806 | assert!(filenames.insert(P9String::new(name).unwrap())); 807 | } 808 | 809 | let fid = dfid + 1; 810 | open(&mut server, &newdir, dfid, "", fid, P9_DIRECTORY).expect("Failed to open directory"); 811 | for f in readdir(&mut server, fid) { 812 | let dir_name = Path::new(std::str::from_utf8(f.name.as_bytes()).unwrap()); 813 | let path = newdir.join(dir_name); 814 | 815 | let md = fs::symlink_metadata(path).expect("failed to get metadata for path"); 816 | check_qid(&f.qid, &md); 817 | 818 | if f.name == "." || f.name == ".." { 819 | assert_eq!(f.ty, libc::DT_DIR); 820 | } else { 821 | assert_eq!(f.ty, libc::DT_REG); 822 | assert!(filenames.remove(&f.name)); 823 | } 824 | } 825 | 826 | assert!(filenames.is_empty()); 827 | } 828 | 829 | #[test] 830 | fn mkdir() { 831 | let (test_dir, mut server) = setup("mkdir"); 832 | 833 | let name = "conan"; 834 | let tmkdir = Tmkdir { 835 | dfid: ROOT_FID, 836 | name: P9String::new(name).unwrap(), 837 | mode: 0o755, 838 | gid: 0, 839 | }; 840 | 841 | let rmkdir = server.mkdir(tmkdir).expect("failed to create directory"); 842 | let md = 843 | fs::symlink_metadata(test_dir.join(name)).expect("failed to get metadata for directory"); 844 | 845 | assert!(md.is_dir()); 846 | check_qid(&rmkdir.qid, &md); 847 | } 848 | 849 | #[test] 850 | fn unlink_all() { 851 | let (test_dir, mut server) = setup("readdir"); 852 | 853 | let mut next_fid = ROOT_FID + 1; 854 | 855 | let mut dirs = VecDeque::new(); 856 | dirs.push_back((ROOT_FID, test_dir.to_path_buf())); 857 | 858 | // First iterate over the whole directory. 859 | let mut unlinks = VecDeque::new(); 860 | while let Some((dfid, dir)) = dirs.pop_front() { 861 | let mut names = VecDeque::new(); 862 | for entry in fs::read_dir(dir).expect("failed to read directory") { 863 | let entry = entry.expect("unable to iterate over directory"); 864 | let ft = entry 865 | .file_type() 866 | .expect("failed to get file type for entry"); 867 | if ft.is_dir() { 868 | let fid = next_fid; 869 | next_fid += 1; 870 | 871 | let wnames: Vec = entry 872 | .path() 873 | .strip_prefix(&test_dir) 874 | .expect("test directory is not prefix of subdir") 875 | .components() 876 | .map(|c| P9String::try_from(c.as_os_str()).unwrap()) 877 | .collect(); 878 | walk(&mut server, &*test_dir, ROOT_FID, fid, wnames); 879 | dirs.push_back((fid, entry.path())); 880 | } 881 | 882 | names.push_back(( 883 | P9String::new(entry.file_name().as_bytes()) 884 | .expect("failed to convert entry name to string"), 885 | if ft.is_dir() { 886 | libc::AT_REMOVEDIR as u32 887 | } else { 888 | 0 889 | }, 890 | )); 891 | } 892 | 893 | unlinks.push_back((dfid, names)); 894 | } 895 | 896 | // Now remove everything in reverse order. 897 | while let Some((dfid, names)) = unlinks.pop_back() { 898 | for (name, flags) in names { 899 | let tunlinkat = Tunlinkat { 900 | dirfd: dfid, 901 | name, 902 | flags, 903 | }; 904 | 905 | server.unlink_at(tunlinkat).expect("failed to unlink path"); 906 | } 907 | } 908 | } 909 | 910 | #[test] 911 | fn rename_at() { 912 | let (test_dir, mut server) = setup("rename"); 913 | 914 | let name = "oldfile"; 915 | let content = create_local_file(&test_dir, name); 916 | 917 | let newname = "newfile"; 918 | let trename = Trenameat { 919 | olddirfid: ROOT_FID, 920 | oldname: P9String::new(name).unwrap(), 921 | newdirfid: ROOT_FID, 922 | newname: P9String::new(newname).unwrap(), 923 | }; 924 | 925 | server.rename_at(trename).expect("failed to rename file"); 926 | 927 | assert!(!test_dir.join(name).exists()); 928 | 929 | let mut newcontent = Vec::with_capacity(content.len()); 930 | let size = File::open(test_dir.join(newname)) 931 | .expect("failed to open file") 932 | .read_to_end(&mut newcontent) 933 | .expect("failed to read new file content"); 934 | assert_eq!(size, content.len()); 935 | assert_eq!(newcontent, content); 936 | } 937 | 938 | fn setlk_tlock(fid: u32, len: u64, start: u64, type_: i32) -> Tlock { 939 | Tlock { 940 | fid, 941 | type_: type_ as u8, 942 | flags: 0, 943 | start, 944 | length: len, 945 | proc_id: SERVER_PID, 946 | client_id: P9String::new("test-server").unwrap(), 947 | } 948 | } 949 | 950 | fn getlk_tgetlock(fid: u32, type_: i32) -> Tgetlock { 951 | Tgetlock { 952 | fid, 953 | type_: type_ as u8, 954 | start: 0, 955 | length: 0, 956 | proc_id: SERVER_PID, 957 | client_id: P9String::new("test-server").unwrap(), 958 | } 959 | } 960 | 961 | fn setup_simple_lock_no_open() -> Server { 962 | let (test_dir, server) = setup("simple lock"); 963 | 964 | let filename = "file"; 965 | create_local_file(&test_dir, filename); 966 | 967 | server 968 | } 969 | 970 | fn setup_simple_lock(flags: u32) -> Server { 971 | let (test_dir, mut server) = setup("simple lock"); 972 | 973 | let filename = "file"; 974 | create_local_file(&test_dir, filename); 975 | 976 | open( 977 | &mut server, 978 | &*test_dir, 979 | ROOT_FID, 980 | filename, 981 | ROOT_FID + 1, 982 | flags, 983 | ) 984 | .expect("failed to open file"); 985 | 986 | server 987 | } 988 | 989 | #[test] 990 | fn lock_rdlck_no_open_file() { 991 | let mut server = setup_simple_lock_no_open(); 992 | 993 | let tlock = setlk_tlock(ROOT_FID + 1, 8, 0, libc::F_RDLCK); 994 | 995 | server.lock(&tlock).expect_err("Bad file descriptor"); 996 | } 997 | 998 | #[test] 999 | fn lock_rdlck() { 1000 | let mut server = setup_simple_lock(P9_RDWR); 1001 | 1002 | let tlock = setlk_tlock(ROOT_FID + 1, 8, 0, libc::F_RDLCK); 1003 | 1004 | server.lock(&tlock).expect("failed to lock file"); 1005 | } 1006 | #[test] 1007 | fn lock_wrlck_no_open_file() { 1008 | let mut server = setup_simple_lock_no_open(); 1009 | 1010 | let tlock = setlk_tlock(ROOT_FID + 1, 8, 0, libc::F_WRLCK); 1011 | 1012 | server.lock(&tlock).expect_err("Bad file descriptor"); 1013 | } 1014 | #[test] 1015 | fn lock_wrlck() { 1016 | let mut server = setup_simple_lock(P9_RDWR); 1017 | 1018 | let tlock = setlk_tlock(ROOT_FID + 1, 8, 0, libc::F_WRLCK); 1019 | 1020 | server.lock(&tlock).expect("failed to lock file"); 1021 | } 1022 | 1023 | #[test] 1024 | fn lock_unlck_no_lock() { 1025 | let mut server = setup_simple_lock(P9_RDWR); 1026 | 1027 | let tlock = setlk_tlock(ROOT_FID + 1, 0, 0, libc::F_UNLCK); 1028 | 1029 | server.lock(&tlock).expect("failed to lock file"); 1030 | } 1031 | 1032 | #[test] 1033 | fn lock_unlck() { 1034 | let mut server = setup_simple_lock(P9_RDWR); 1035 | 1036 | let tlock = setlk_tlock(ROOT_FID + 1, LOCAL_FILE_LEN / 2, 0, libc::F_RDLCK); 1037 | 1038 | server.lock(&tlock).expect("failed to lock file"); 1039 | 1040 | let tlock = setlk_tlock(ROOT_FID + 1, 0, 0, libc::F_UNLCK); 1041 | 1042 | server.lock(&tlock).expect("failed to lock file"); 1043 | } 1044 | 1045 | #[test] 1046 | fn lock_unlck_relock() { 1047 | let mut server = setup_simple_lock(P9_RDWR); 1048 | 1049 | let tlock = setlk_tlock(ROOT_FID + 1, LOCAL_FILE_LEN / 2, 0, libc::F_RDLCK); 1050 | 1051 | server.lock(&tlock).expect("failed to lock file"); 1052 | 1053 | let tlock = setlk_tlock(ROOT_FID + 1, 0, 0, libc::F_UNLCK); 1054 | 1055 | server.lock(&tlock).expect("failed to lock file"); 1056 | 1057 | let tlock = setlk_tlock(ROOT_FID + 1, LOCAL_FILE_LEN / 2, 0, libc::F_RDLCK); 1058 | 1059 | server.lock(&tlock).expect("failed to lock file"); 1060 | } 1061 | 1062 | #[test] 1063 | fn getlock_rdlck_nolock() { 1064 | let mut server = setup_simple_lock(P9_RDWR); 1065 | 1066 | let tgetlock = getlk_tgetlock(ROOT_FID + 1, libc::F_RDLCK); 1067 | 1068 | server 1069 | .get_lock(&tgetlock) 1070 | .expect("failed to get lock on file"); 1071 | } 1072 | 1073 | #[test] 1074 | fn getlock_wrlck() { 1075 | let mut server = setup_simple_lock(P9_RDWR); 1076 | 1077 | let tlock = setlk_tlock(ROOT_FID + 1, LOCAL_FILE_LEN / 2, 0, libc::F_WRLCK); 1078 | 1079 | server.lock(&tlock).expect("failed to lock file"); 1080 | 1081 | let tgetlock = getlk_tgetlock(ROOT_FID + 1, libc::F_WRLCK); 1082 | 1083 | server 1084 | .get_lock(&tgetlock) 1085 | .expect("failed to get lock on file"); 1086 | } 1087 | 1088 | #[test] 1089 | fn getlock_rdlck() { 1090 | let mut server = setup_simple_lock(P9_RDWR); 1091 | 1092 | let tlock = setlk_tlock(ROOT_FID + 1, LOCAL_FILE_LEN / 2, 0, libc::F_RDLCK); 1093 | 1094 | server.lock(&tlock).expect("failed to lock file"); 1095 | 1096 | let tgetlock = getlk_tgetlock(ROOT_FID + 1, libc::F_RDLCK); 1097 | 1098 | server 1099 | .get_lock(&tgetlock) 1100 | .expect("failed to get lock on file"); 1101 | } 1102 | 1103 | macro_rules! open_test { 1104 | ($name:ident, $flags:expr) => { 1105 | #[test] 1106 | fn $name() { 1107 | let (test_dir, mut server) = setup("open"); 1108 | 1109 | let fid = ROOT_FID + 1; 1110 | let name = "test.txt"; 1111 | let content = create_local_file(&test_dir, name); 1112 | 1113 | let rlopen = open(&mut server, &*test_dir, ROOT_FID, name, fid, $flags as u32) 1114 | .expect("failed to open file"); 1115 | 1116 | let md = 1117 | fs::symlink_metadata(test_dir.join(name)).expect("failed to get metadata for file"); 1118 | check_qid(&rlopen.qid, &md); 1119 | assert_eq!(rlopen.iounit, 0); 1120 | 1121 | check_attr(&mut server, fid, &md); 1122 | 1123 | // Check that the file has the proper contents as long as we didn't 1124 | // truncate it first. 1125 | if $flags & P9_TRUNC == 0 && $flags & P9_WRONLY == 0 { 1126 | check_content(&mut server, &content, fid); 1127 | } 1128 | 1129 | // Check that we can write to the file. 1130 | if $flags & P9_RDWR != 0 || $flags & P9_WRONLY != 0 { 1131 | write(&mut server, &test_dir, name, fid, $flags); 1132 | } 1133 | 1134 | let tclunk = Tclunk { fid }; 1135 | server.clunk(&tclunk).expect("Unable to clunk file"); 1136 | } 1137 | }; 1138 | ($name:ident, $flags:expr, $expected_err:expr) => { 1139 | #[test] 1140 | fn $name() { 1141 | let (test_dir, mut server) = setup("open_fail"); 1142 | 1143 | let fid = ROOT_FID + 1; 1144 | let name = "test.txt"; 1145 | create_local_file(&test_dir, name); 1146 | 1147 | let err = open(&mut server, &*test_dir, ROOT_FID, name, fid, $flags as u32) 1148 | .expect_err("successfully opened file"); 1149 | assert_eq!(err.kind(), $expected_err); 1150 | 1151 | let tclunk = Tclunk { fid }; 1152 | server.clunk(&tclunk).expect("Unable to clunk file"); 1153 | } 1154 | }; 1155 | } 1156 | 1157 | open_test!(read_only_file_open, P9_RDONLY); 1158 | open_test!(read_write_file_open, P9_RDWR); 1159 | open_test!(write_only_file_open, P9_WRONLY); 1160 | 1161 | open_test!(create_read_only_file_open, P9_CREATE | P9_RDONLY); 1162 | open_test!(create_read_write_file_open, P9_CREATE | P9_RDWR); 1163 | open_test!(create_write_only_file_open, P9_CREATE | P9_WRONLY); 1164 | 1165 | open_test!(append_read_only_file_open, P9_APPEND | P9_RDONLY); 1166 | open_test!(append_read_write_file_open, P9_APPEND | P9_RDWR); 1167 | open_test!(append_write_only_file_open, P9_APPEND | P9_WRONLY); 1168 | 1169 | open_test!(trunc_read_only_file_open, P9_TRUNC | P9_RDONLY); 1170 | open_test!(trunc_read_write_file_open, P9_TRUNC | P9_RDWR); 1171 | open_test!(trunc_write_only_file_open, P9_TRUNC | P9_WRONLY); 1172 | 1173 | open_test!( 1174 | create_append_read_only_file_open, 1175 | P9_CREATE | P9_APPEND | P9_RDONLY 1176 | ); 1177 | open_test!( 1178 | create_append_read_write_file_open, 1179 | P9_CREATE | P9_APPEND | P9_RDWR 1180 | ); 1181 | open_test!( 1182 | create_append_wronly_file_open, 1183 | P9_CREATE | P9_APPEND | P9_WRONLY 1184 | ); 1185 | 1186 | open_test!( 1187 | create_trunc_read_only_file_open, 1188 | P9_CREATE | P9_TRUNC | P9_RDONLY 1189 | ); 1190 | open_test!( 1191 | create_trunc_read_write_file_open, 1192 | P9_CREATE | P9_TRUNC | P9_RDWR 1193 | ); 1194 | open_test!( 1195 | create_trunc_wronly_file_open, 1196 | P9_CREATE | P9_TRUNC | P9_WRONLY 1197 | ); 1198 | 1199 | open_test!( 1200 | append_trunc_read_only_file_open, 1201 | P9_APPEND | P9_TRUNC | P9_RDONLY 1202 | ); 1203 | open_test!( 1204 | append_trunc_read_write_file_open, 1205 | P9_APPEND | P9_TRUNC | P9_RDWR 1206 | ); 1207 | open_test!( 1208 | append_trunc_wronly_file_open, 1209 | P9_APPEND | P9_TRUNC | P9_WRONLY 1210 | ); 1211 | 1212 | open_test!( 1213 | create_append_trunc_read_only_file_open, 1214 | P9_CREATE | P9_APPEND | P9_TRUNC | P9_RDONLY 1215 | ); 1216 | open_test!( 1217 | create_append_trunc_read_write_file_open, 1218 | P9_CREATE | P9_APPEND | P9_TRUNC | P9_RDWR 1219 | ); 1220 | open_test!( 1221 | create_append_trunc_wronly_file_open, 1222 | P9_CREATE | P9_APPEND | P9_TRUNC | P9_WRONLY 1223 | ); 1224 | 1225 | open_test!( 1226 | create_excl_read_only_file_open, 1227 | P9_CREATE | P9_EXCL | P9_RDONLY, 1228 | io::ErrorKind::AlreadyExists 1229 | ); 1230 | open_test!( 1231 | create_excl_read_write_file_open, 1232 | P9_CREATE | P9_EXCL | P9_RDWR, 1233 | io::ErrorKind::AlreadyExists 1234 | ); 1235 | open_test!( 1236 | create_excl_wronly_file_open, 1237 | P9_CREATE | P9_EXCL | P9_WRONLY, 1238 | io::ErrorKind::AlreadyExists 1239 | ); 1240 | 1241 | macro_rules! create_test { 1242 | ($name:ident, $flags:expr, $mode:expr) => { 1243 | #[test] 1244 | fn $name() { 1245 | let (test_dir, mut server) = setup("create"); 1246 | 1247 | let name = "foo.txt"; 1248 | let fid = ROOT_FID + 1; 1249 | let rlcreate = create(&mut server, &*test_dir, ROOT_FID, fid, name, $flags, $mode) 1250 | .expect("failed to create file"); 1251 | 1252 | let md = 1253 | fs::symlink_metadata(test_dir.join(name)).expect("failed to get metadata for file"); 1254 | assert_eq!(rlcreate.iounit, 0); 1255 | check_qid(&rlcreate.qid, &md); 1256 | check_attr(&mut server, fid, &md); 1257 | 1258 | // Check that we can write to the file. 1259 | if $flags & P9_RDWR != 0 || $flags & P9_WRONLY != 0 { 1260 | write(&mut server, &test_dir, name, fid, $flags); 1261 | } 1262 | 1263 | let tclunk = Tclunk { fid }; 1264 | server.clunk(&tclunk).expect("Unable to clunk file"); 1265 | } 1266 | }; 1267 | ($name:ident, $flags:expr, $mode:expr, $expected_err:expr) => { 1268 | #[test] 1269 | fn $name() { 1270 | let (test_dir, mut server) = setup("create_fail"); 1271 | 1272 | let name = "foo.txt"; 1273 | // The `fid` in the lcreate call initially points to the directory 1274 | // but is supposed to point to the newly created file after the call 1275 | // completes. Duplicate the fid so that we don't end up consuming the 1276 | // root fid. 1277 | let fid = ROOT_FID + 1; 1278 | let err = create(&mut server, &*test_dir, ROOT_FID, fid, name, $flags, $mode) 1279 | .expect_err("successfully created file"); 1280 | assert_eq!(err.kind(), $expected_err); 1281 | } 1282 | }; 1283 | } 1284 | 1285 | create_test!(read_only_file_create, P9_RDONLY, 0o600u32); 1286 | create_test!(read_write_file_create, P9_RDWR, 0o600u32); 1287 | create_test!(write_only_file_create, P9_WRONLY, 0o600u32); 1288 | 1289 | create_test!( 1290 | append_read_only_file_create, 1291 | P9_APPEND | P9_RDONLY, 1292 | 0o600u32 1293 | ); 1294 | create_test!(append_read_write_file_create, P9_APPEND | P9_RDWR, 0o600u32); 1295 | create_test!(append_wronly_file_create, P9_APPEND | P9_WRONLY, 0o600u32); 1296 | 1297 | #[test] 1298 | fn lcreate_set_len() { 1299 | let (test_dir, mut server) = setup("lcreate_set_len"); 1300 | 1301 | let name = "foo.txt"; 1302 | let fid = ROOT_FID + 1; 1303 | create( 1304 | &mut server, 1305 | &*test_dir, 1306 | ROOT_FID, 1307 | fid, 1308 | name, 1309 | P9_RDWR, 1310 | 0o600u32, 1311 | ) 1312 | .expect("failed to create file"); 1313 | 1314 | let tsetattr = Tsetattr { 1315 | fid, 1316 | valid: 0x8, // P9_SETATTR_SIZE 1317 | size: 100, 1318 | // The other fields are not used because the relevant flags aren't set in `valid`. 1319 | mode: 0, 1320 | uid: 0, 1321 | gid: 0, 1322 | atime_sec: 0, 1323 | atime_nsec: 0, 1324 | mtime_sec: 0, 1325 | mtime_nsec: 0, 1326 | }; 1327 | server 1328 | .set_attr(&tsetattr) 1329 | .expect("failed to set file length after lcreate"); 1330 | 1331 | let tclunk = Tclunk { fid }; 1332 | server.clunk(&tclunk).expect("Unable to clunk file"); 1333 | } 1334 | 1335 | #[test] 1336 | fn readlink() { 1337 | let (test_dir, mut server) = setup("readlink"); 1338 | create_local_symlink(&test_dir, "symlink", "target/of/symlink"); 1339 | 1340 | let fid = ROOT_FID + 1; 1341 | walk( 1342 | &mut server, 1343 | &*test_dir, 1344 | ROOT_FID, 1345 | fid, 1346 | vec![P9String::new("symlink").unwrap()], 1347 | ); 1348 | 1349 | let treadlink = Treadlink { fid }; 1350 | 1351 | let rreadlink = server.readlink(&treadlink).expect("failed to readlink"); 1352 | 1353 | assert_eq!(rreadlink.target, "target/of/symlink"); 1354 | } 1355 | --------------------------------------------------------------------------------