• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
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 super::*;
6 
7 use std::borrow::Cow;
8 use std::collections::{HashSet, VecDeque};
9 use std::env;
10 use std::ffi::{CString, OsString};
11 use std::fs::{self, File};
12 use std::io::{self, Cursor};
13 use std::mem;
14 use std::ops::Deref;
15 use std::os::unix::ffi::OsStringExt;
16 use std::os::unix::fs::MetadataExt;
17 use std::path::{Component, Path, PathBuf};
18 use std::u32;
19 
20 // Used to indicate that there is no fid associated with this message.
21 const P9_NOFID: u32 = u32::MAX;
22 
23 // The fid associated with the root directory of the server.
24 const ROOT_FID: u32 = 1;
25 
26 // How big we want the default buffer to be when running tests.
27 const DEFAULT_BUFFER_SIZE: u32 = 4096;
28 
29 // Joins `path` to `buf`.  If `path` is '..', removes the last component from `buf`
30 // only if `buf` != `root` but does nothing if `buf` == `root`.  Pushes `path` onto
31 // `buf` if it is a normal path component.
32 //
33 // Returns an error if `path` is absolute, has more than one component, or contains
34 // a '.' component.
join_path<P: AsRef<Path>, R: AsRef<Path>>( mut buf: PathBuf, path: P, root: R, ) -> io::Result<PathBuf>35 fn join_path<P: AsRef<Path>, R: AsRef<Path>>(
36     mut buf: PathBuf,
37     path: P,
38     root: R,
39 ) -> io::Result<PathBuf> {
40     let path = path.as_ref();
41     let root = root.as_ref();
42     debug_assert!(buf.starts_with(root));
43 
44     if path.components().count() > 1 {
45         return Err(io::Error::from_raw_os_error(libc::EINVAL));
46     }
47 
48     for component in path.components() {
49         match component {
50             // Prefix should only appear on windows systems.
51             Component::Prefix(_) => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
52             // Absolute paths are not allowed.
53             Component::RootDir => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
54             // '.' elements are not allowed.
55             Component::CurDir => return Err(io::Error::from_raw_os_error(libc::EINVAL)),
56             Component::ParentDir => {
57                 // We only remove the parent path if we are not already at the root of the
58                 // file system.
59                 if buf != root {
60                     buf.pop();
61                 }
62             }
63             Component::Normal(element) => buf.push(element),
64         }
65     }
66 
67     Ok(buf)
68 }
69 
70 // Automatically deletes the path it contains when it goes out of scope.
71 struct ScopedPath<P: AsRef<Path>>(P);
72 
73 impl<P: AsRef<Path>> AsRef<Path> for ScopedPath<P> {
as_ref(&self) -> &Path74     fn as_ref(&self) -> &Path {
75         self.0.as_ref()
76     }
77 }
78 
79 impl<P: AsRef<Path>> Deref for ScopedPath<P> {
80     type Target = Path;
81 
deref(&self) -> &Self::Target82     fn deref(&self) -> &Self::Target {
83         self.0.as_ref()
84     }
85 }
86 
87 impl<P: AsRef<Path>> Drop for ScopedPath<P> {
drop(&mut self)88     fn drop(&mut self) {
89         if let Err(e) = fs::remove_dir_all(&**self) {
90             println!("Failed to remove {}: {}", self.display(), e);
91         }
92     }
93 }
94 
95 enum DirEntry<'a> {
96     File {
97         name: &'a str,
98         content: &'a [u8],
99     },
100     Directory {
101         name: &'a str,
102         entries: &'a [DirEntry<'a>],
103     },
104 }
105 
106 impl<'a> DirEntry<'a> {
107     // Creates `self` in the path given by `dir`.
create(&self, dir: &mut Cow<Path>)108     fn create(&self, dir: &mut Cow<Path>) {
109         match *self {
110             DirEntry::File { name, content } => {
111                 let mut f = File::create(dir.join(name)).expect("failed to create file");
112                 f.write_all(content).expect("failed to write file content");
113             }
114             DirEntry::Directory { name, entries } => {
115                 dir.to_mut().push(name);
116 
117                 fs::create_dir_all(&**dir).expect("failed to create directory");
118                 for e in entries {
119                     e.create(dir);
120                 }
121 
122                 assert!(dir.to_mut().pop());
123             }
124         }
125     }
126 }
127 
128 // Creates a file with `name` in `dir` and fills it with random
129 // content.
create_local_file<P: AsRef<Path>>(dir: P, name: &str) -> Vec<u8>130 fn create_local_file<P: AsRef<Path>>(dir: P, name: &str) -> Vec<u8> {
131     let mut content = Vec::new();
132     File::open("/dev/urandom")
133         .and_then(|f| f.take(200).read_to_end(&mut content))
134         .expect("failed to read from /dev/urandom");
135 
136     let f = DirEntry::File {
137         name,
138         content: &content,
139     };
140     f.create(&mut Cow::from(dir.as_ref()));
141 
142     content
143 }
144 
check_qid(qid: &Qid, md: &fs::Metadata)145 fn check_qid(qid: &Qid, md: &fs::Metadata) {
146     let ty = if md.is_dir() {
147         P9_QTDIR
148     } else if md.is_file() {
149         P9_QTFILE
150     } else if md.file_type().is_symlink() {
151         P9_QTSYMLINK
152     } else {
153         panic!("unknown file type: {:?}", md.file_type());
154     };
155     assert_eq!(qid.ty, ty);
156     assert_eq!(qid.version, md.mtime() as u32);
157     assert_eq!(qid.path, md.ino());
158 }
159 
check_attr(server: &mut Server, fid: u32, md: &fs::Metadata)160 fn check_attr(server: &mut Server, fid: u32, md: &fs::Metadata) {
161     let tgetattr = Tgetattr {
162         fid,
163         request_mask: P9_GETATTR_BASIC,
164     };
165 
166     let rgetattr = server.get_attr(&tgetattr).expect("failed to call get_attr");
167 
168     let ty = if md.is_dir() {
169         P9_QTDIR
170     } else if md.is_file() {
171         P9_QTFILE
172     } else if md.file_type().is_symlink() {
173         P9_QTSYMLINK
174     } else {
175         panic!("unknown file type: {:?}", md.file_type());
176     };
177     assert_eq!(rgetattr.valid, P9_GETATTR_BASIC);
178     assert_eq!(rgetattr.qid.ty, ty);
179     assert_eq!(rgetattr.qid.version, md.mtime() as u32);
180     assert_eq!(rgetattr.qid.path, md.ino());
181     assert_eq!(rgetattr.mode, md.mode());
182     assert_eq!(rgetattr.uid, md.uid());
183     assert_eq!(rgetattr.gid, md.gid());
184     assert_eq!(rgetattr.nlink, md.nlink());
185     assert_eq!(rgetattr.rdev, md.rdev());
186     assert_eq!(rgetattr.size, md.size());
187     assert_eq!(rgetattr.atime_sec, md.atime() as u64);
188     assert_eq!(rgetattr.atime_nsec, md.atime_nsec() as u64);
189     assert_eq!(rgetattr.mtime_sec, md.mtime() as u64);
190     assert_eq!(rgetattr.mtime_nsec, md.mtime_nsec() as u64);
191     assert_eq!(rgetattr.ctime_sec, md.ctime() as u64);
192     assert_eq!(rgetattr.ctime_nsec, md.ctime_nsec() as u64);
193     assert_eq!(rgetattr.btime_sec, 0);
194     assert_eq!(rgetattr.btime_nsec, 0);
195     assert_eq!(rgetattr.gen, 0);
196     assert_eq!(rgetattr.data_version, 0);
197 }
198 
check_content(server: &mut Server, content: &[u8], fid: u32)199 fn check_content(server: &mut Server, content: &[u8], fid: u32) {
200     for offset in 0..content.len() {
201         let tread = Tread {
202             fid,
203             offset: offset as u64,
204             count: DEFAULT_BUFFER_SIZE,
205         };
206 
207         let rread = server.read(&tread).expect("failed to read file");
208         assert_eq!(content[offset..], rread.data[..]);
209     }
210 }
211 
walk<P: Into<PathBuf>>( server: &mut Server, start: P, fid: u32, newfid: u32, names: Vec<String>, )212 fn walk<P: Into<PathBuf>>(
213     server: &mut Server,
214     start: P,
215     fid: u32,
216     newfid: u32,
217     names: Vec<String>,
218 ) {
219     let mut mds = Vec::with_capacity(names.len());
220     let mut buf = start.into();
221     for name in &names {
222         buf.push(name);
223         mds.push(
224             buf.symlink_metadata()
225                 .expect("failed to get metadata for path"),
226         );
227     }
228 
229     let twalk = Twalk {
230         fid,
231         newfid,
232         wnames: names,
233     };
234 
235     let rwalk = server.walk(twalk).expect("failed to walk directoy");
236     assert_eq!(mds.len(), rwalk.wqids.len());
237     for (md, qid) in mds.iter().zip(rwalk.wqids.iter()) {
238         check_qid(qid, md);
239     }
240 }
241 
open<P: Into<PathBuf>>( server: &mut Server, dir: P, dir_fid: u32, name: &str, fid: u32, flags: u32, ) -> io::Result<Rlopen>242 fn open<P: Into<PathBuf>>(
243     server: &mut Server,
244     dir: P,
245     dir_fid: u32,
246     name: &str,
247     fid: u32,
248     flags: u32,
249 ) -> io::Result<Rlopen> {
250     let wnames = if name.is_empty() {
251         vec![]
252     } else {
253         vec![String::from(name)]
254     };
255     walk(server, dir, dir_fid, fid, wnames);
256 
257     let tlopen = Tlopen { fid, flags };
258 
259     server.lopen(&tlopen)
260 }
261 
write<P: AsRef<Path>>(server: &mut Server, dir: P, name: &str, fid: u32, flags: u32)262 fn write<P: AsRef<Path>>(server: &mut Server, dir: P, name: &str, fid: u32, flags: u32) {
263     let file_path = dir.as_ref().join(name);
264     let file_len = if file_path.exists() {
265         fs::symlink_metadata(&file_path)
266             .expect("unable to get metadata for file")
267             .len() as usize
268     } else {
269         0usize
270     };
271     let mut new_content = Vec::new();
272     File::open("/dev/urandom")
273         .and_then(|f| f.take(200).read_to_end(&mut new_content))
274         .expect("failed to read from /dev/urandom");
275 
276     let twrite = Twrite {
277         fid,
278         offset: 0,
279         data: Data(new_content),
280     };
281 
282     let rwrite = server.write(&twrite).expect("failed to write file");
283     assert_eq!(rwrite.count, twrite.data.len() as u32);
284 
285     let tfsync = Tfsync { fid, datasync: 0 };
286     server.fsync(&tfsync).expect("failed to sync file contents");
287 
288     let actual_content = fs::read(file_path).expect("failed to read back content from file");
289 
290     // If the file was opened append-only, then the content should have been
291     // written to the end even though the offset was 0.
292     let idx = if flags & P9_APPEND == 0 { 0 } else { file_len };
293     assert_eq!(actual_content[idx..], twrite.data[..]);
294 }
295 
create<P: Into<PathBuf>>( server: &mut Server, dir: P, dir_fid: u32, fid: u32, name: &str, flags: u32, mode: u32, ) -> io::Result<Rlcreate>296 fn create<P: Into<PathBuf>>(
297     server: &mut Server,
298     dir: P,
299     dir_fid: u32,
300     fid: u32,
301     name: &str,
302     flags: u32,
303     mode: u32,
304 ) -> io::Result<Rlcreate> {
305     // The `fid` in the lcreate call initially points to the directory
306     // but is supposed to point to the newly created file after the call
307     // completes.  Duplicate the fid so that we don't end up consuming the
308     // directory fid.
309     walk(server, dir, dir_fid, fid, Vec::new());
310 
311     let tlcreate = Tlcreate {
312         fid,
313         name: String::from(name),
314         flags,
315         mode,
316         gid: 0,
317     };
318 
319     server.lcreate(tlcreate)
320 }
321 
322 struct Readdir<'a> {
323     server: &'a mut Server,
324     fid: u32,
325     offset: u64,
326     cursor: Cursor<Vec<u8>>,
327 }
328 
329 impl<'a> Iterator for Readdir<'a> {
330     type Item = Dirent;
331 
next(&mut self) -> Option<Self::Item>332     fn next(&mut self) -> Option<Self::Item> {
333         if self.cursor.position() >= self.cursor.get_ref().len() as u64 {
334             let treaddir = Treaddir {
335                 fid: self.fid,
336                 offset: self.offset,
337                 count: DEFAULT_BUFFER_SIZE,
338             };
339 
340             let Rreaddir { data } = self
341                 .server
342                 .readdir(&treaddir)
343                 .expect("failed to read directory");
344             if data.is_empty() {
345                 // No more entries.
346                 return None;
347             }
348 
349             mem::drop(mem::replace(&mut self.cursor, Cursor::new(data.0)));
350         }
351 
352         let dirent: Dirent = WireFormat::decode(&mut self.cursor).expect("failed to decode dirent");
353         self.offset = dirent.offset;
354 
355         Some(dirent)
356     }
357 }
358 
readdir(server: &mut Server, fid: u32) -> Readdir359 fn readdir(server: &mut Server, fid: u32) -> Readdir {
360     Readdir {
361         server,
362         fid,
363         offset: 0,
364         cursor: Cursor::new(Vec::new()),
365     }
366 }
367 
368 // Sets up the server to start handling messages.  Creates a new temporary
369 // directory to act as the server root and sends an initial Tattach message.
370 // At the end of setup, fid 1 points to the root of the server.
setup<P: AsRef<Path>>(name: P) -> (ScopedPath<OsString>, Server)371 fn setup<P: AsRef<Path>>(name: P) -> (ScopedPath<OsString>, Server) {
372     let mut test_dir = env::var_os("T")
373         .map(PathBuf::from)
374         .unwrap_or_else(env::temp_dir);
375     test_dir.push(name);
376 
377     let mut os_str = OsString::from(test_dir);
378     os_str.push(".XXXXXX");
379 
380     // Create a c string and release ownership.  This seems like the only way
381     // to get a *mut c_char.
382     let buf = CString::new(os_str.into_vec())
383         .expect("failed to create CString")
384         .into_raw();
385 
386     // Safe because this will only modify the contents of `buf`.
387     let ret = unsafe { libc::mkdtemp(buf) };
388 
389     // Take ownership of the buffer back before checking the result.  Safe because
390     // this was created by a call to into_raw() above and mkdtemp will not overwrite
391     // the trailing '\0'.
392     let buf = unsafe { CString::from_raw(buf) };
393 
394     assert!(!ret.is_null());
395 
396     let test_dir = ScopedPath(OsString::from_vec(buf.into_bytes()));
397 
398     // Create a basic file system hierarchy.
399     let entries = [
400         DirEntry::Directory {
401             name: "subdir",
402             entries: &[
403                 DirEntry::File {
404                     name: "b",
405                     content: b"hello, world!",
406                 },
407                 DirEntry::Directory {
408                     name: "nested",
409                     entries: &[DirEntry::File {
410                         name: "Огонь по готовности!",
411                         content: &[
412                             0xe9u8, 0xbeu8, 0x8du8, 0xe3u8, 0x81u8, 0x8cu8, 0xe6u8, 0x88u8, 0x91u8,
413                             0xe3u8, 0x81u8, 0x8cu8, 0xe6u8, 0x95u8, 0xb5u8, 0xe3u8, 0x82u8, 0x92u8,
414                             0xe5u8, 0x96u8, 0xb0u8, 0xe3u8, 0x82u8, 0x89u8, 0xe3u8, 0x81u8, 0x86u8,
415                             0x21u8,
416                         ],
417                     }],
418                 },
419             ],
420         },
421         DirEntry::File {
422             name: "世界.txt",
423             content: &[
424                 0xe3u8, 0x81u8, 0x93u8, 0xe3u8, 0x82u8, 0x93u8, 0xe3u8, 0x81u8, 0xabu8, 0xe3u8,
425                 0x81u8, 0xa1u8, 0xe3u8, 0x81u8, 0xafu8,
426             ],
427         },
428     ];
429 
430     for e in &entries {
431         e.create(&mut Cow::from(&*test_dir));
432     }
433 
434     let md = test_dir
435         .symlink_metadata()
436         .expect("failed to get metadata for root dir");
437 
438     let mut server = Server::new(&*test_dir, Default::default(), Default::default())
439         .expect("Failed to create server");
440 
441     let tversion = Tversion {
442         msize: DEFAULT_BUFFER_SIZE,
443         version: String::from("9P2000.L"),
444     };
445 
446     let rversion = server
447         .version(&tversion)
448         .expect("failed to get version from server");
449     assert_eq!(rversion.msize, DEFAULT_BUFFER_SIZE);
450     assert_eq!(rversion.version, "9P2000.L");
451 
452     let tattach = Tattach {
453         fid: ROOT_FID,
454         afid: P9_NOFID,
455         uname: String::from("unittest"),
456         aname: String::from(""),
457         n_uname: 1000,
458     };
459 
460     let rattach = server.attach(&tattach).expect("failed to attach to server");
461     check_qid(&rattach.qid, &md);
462 
463     (test_dir, server)
464 }
465 
466 #[test]
path_joins()467 fn path_joins() {
468     let root = PathBuf::from("/a/b/c");
469     let path = PathBuf::from("/a/b/c/d/e/f");
470 
471     assert_eq!(
472         &join_path(path.clone(), "nested", &root).expect("normal"),
473         Path::new("/a/b/c/d/e/f/nested")
474     );
475 
476     let p1 = join_path(path, "..", &root).expect("parent 1");
477     assert_eq!(&p1, Path::new("/a/b/c/d/e/"));
478 
479     let p2 = join_path(p1, "..", &root).expect("parent 2");
480     assert_eq!(&p2, Path::new("/a/b/c/d/"));
481 
482     let p3 = join_path(p2, "..", &root).expect("parent 3");
483     assert_eq!(&p3, Path::new("/a/b/c/"));
484 
485     let p4 = join_path(p3, "..", &root).expect("parent of root");
486     assert_eq!(&p4, Path::new("/a/b/c/"));
487 }
488 
489 #[test]
invalid_joins()490 fn invalid_joins() {
491     let root = PathBuf::from("/a");
492     let path = PathBuf::from("/a/b");
493 
494     join_path(path.clone(), ".", &root).expect_err("current directory");
495     join_path(path.clone(), "c/d/e", &root).expect_err("too many components");
496     join_path(path, "/c/d/e", &root).expect_err("absolute path");
497 }
498 
499 #[test]
clunk()500 fn clunk() {
501     let (_test_dir, mut server) = setup("clunk");
502 
503     let tclunk = Tclunk { fid: ROOT_FID };
504     server.clunk(&tclunk).expect("failed to clunk root fid");
505 }
506 
507 #[test]
get_attr()508 fn get_attr() {
509     let (test_dir, mut server) = setup("get_attr");
510 
511     let md = test_dir
512         .symlink_metadata()
513         .expect("failed to get metadata for test dir");
514 
515     check_attr(&mut server, ROOT_FID, &md);
516 }
517 
518 #[test]
tree_walk()519 fn tree_walk() {
520     let (test_dir, mut server) = setup("readdir");
521 
522     let mut next_fid = ROOT_FID + 1;
523 
524     let mut dirs = VecDeque::new();
525     dirs.push_back(test_dir.to_path_buf());
526 
527     while let Some(dir) = dirs.pop_front() {
528         let dfid = next_fid;
529         next_fid += 1;
530 
531         let wnames: Vec<String> = dir
532             .strip_prefix(&test_dir)
533             .expect("test directory is not prefix of subdir")
534             .components()
535             .map(|c| Path::new(&c).to_string_lossy().to_string())
536             .collect();
537         walk(&mut server, &*test_dir, ROOT_FID, dfid, wnames);
538 
539         let md = dir.symlink_metadata().expect("failed to get metadata");
540 
541         check_attr(&mut server, dfid, &md);
542 
543         let fid = next_fid;
544         next_fid += 1;
545         open(&mut server, &dir, dfid, "", fid, P9_DIRECTORY).expect("Failed to open directory");
546         for dirent in readdir(&mut server, fid) {
547             if dirent.name == "." || dirent.name == ".." {
548                 continue;
549             }
550 
551             let entry_path = dir.join(&dirent.name);
552             assert!(
553                 entry_path.exists(),
554                 "directory entry \"{}\" does not exist",
555                 entry_path.display()
556             );
557             let md = fs::symlink_metadata(&entry_path).expect("failed to get metadata for entry");
558 
559             let ty = if md.is_dir() {
560                 dirs.push_back(dir.join(dirent.name));
561                 libc::DT_DIR
562             } else if md.is_file() {
563                 libc::DT_REG
564             } else if md.file_type().is_symlink() {
565                 libc::DT_LNK
566             } else {
567                 panic!("unknown file type: {:?}", md.file_type());
568             };
569 
570             assert_eq!(dirent.ty, ty);
571             check_qid(&dirent.qid, &md);
572         }
573 
574         let tclunk = Tclunk { fid };
575         server.clunk(&tclunk).expect("failed to clunk fid");
576     }
577 }
578 
579 #[test]
create_existing_file()580 fn create_existing_file() {
581     let (test_dir, mut server) = setup("create_existing");
582 
583     let name = "existing";
584     create_local_file(&test_dir, name);
585 
586     let fid = ROOT_FID + 1;
587     create(
588         &mut server,
589         &*test_dir,
590         ROOT_FID,
591         fid,
592         name,
593         P9_APPEND,
594         0o644,
595     )
596     .expect_err("successfully created existing file");
597 }
598 
599 enum SetAttrKind {
600     File,
601     Directory,
602 }
603 
set_attr_test<F>(kind: SetAttrKind, set_fields: F) -> io::Result<fs::Metadata> where F: FnOnce(&mut Tsetattr),604 fn set_attr_test<F>(kind: SetAttrKind, set_fields: F) -> io::Result<fs::Metadata>
605 where
606     F: FnOnce(&mut Tsetattr),
607 {
608     let (test_dir, mut server) = setup("set_attr");
609 
610     let name = "existing";
611     match kind {
612         SetAttrKind::File => {
613             create_local_file(&test_dir, name);
614         }
615         SetAttrKind::Directory => {
616             let tmkdir = Tmkdir {
617                 dfid: ROOT_FID,
618                 name: String::from(name),
619                 mode: 0o755,
620                 gid: 0,
621             };
622 
623             let rmkdir = server.mkdir(tmkdir).expect("failed to create directory");
624             let md = fs::symlink_metadata(test_dir.join(name))
625                 .expect("failed to get metadata for directory");
626 
627             assert!(md.is_dir());
628             check_qid(&rmkdir.qid, &md);
629         }
630     };
631 
632     let fid = ROOT_FID + 1;
633     walk(
634         &mut server,
635         &*test_dir,
636         ROOT_FID,
637         fid,
638         vec![String::from(name)],
639     );
640 
641     let mut tsetattr = Tsetattr {
642         fid,
643         valid: 0,
644         mode: 0,
645         uid: 0,
646         gid: 0,
647         size: 0,
648         atime_sec: 0,
649         atime_nsec: 0,
650         mtime_sec: 0,
651         mtime_nsec: 0,
652     };
653 
654     set_fields(&mut tsetattr);
655     server.set_attr(&tsetattr)?;
656 
657     fs::symlink_metadata(test_dir.join(name))
658 }
659 
660 #[test]
set_len()661 fn set_len() {
662     let len = 661;
663     let md = set_attr_test(SetAttrKind::File, |tsetattr| {
664         tsetattr.valid = P9_SETATTR_SIZE;
665         tsetattr.size = len;
666     })
667     .expect("failed to run set length of file");
668 
669     assert_eq!(md.size(), len);
670 }
671 
672 #[test]
set_file_mode()673 fn set_file_mode() {
674     let mode = 0o640;
675     let err = set_attr_test(SetAttrKind::File, |tsetattr| {
676         tsetattr.valid = P9_SETATTR_MODE;
677         tsetattr.mode = mode;
678     })
679     .expect_err("successfully set mode");
680 
681     assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
682 }
683 
684 #[test]
set_file_uid()685 fn set_file_uid() {
686     let uid = 294;
687     let err = set_attr_test(SetAttrKind::File, |tsetattr| {
688         tsetattr.valid = P9_SETATTR_UID;
689         tsetattr.uid = uid;
690     })
691     .expect_err("successfully set uid");
692 
693     assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
694 }
695 
696 #[test]
set_file_gid()697 fn set_file_gid() {
698     let gid = 9024;
699     let err = set_attr_test(SetAttrKind::File, |tsetattr| {
700         tsetattr.valid = P9_SETATTR_GID;
701         tsetattr.gid = gid;
702     })
703     .expect_err("successfully set gid");
704 
705     assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
706 }
707 
708 #[test]
set_file_mtime()709 fn set_file_mtime() {
710     let (secs, nanos) = (1245247825, 524617);
711     let md = set_attr_test(SetAttrKind::File, |tsetattr| {
712         tsetattr.valid = P9_SETATTR_MTIME | P9_SETATTR_MTIME_SET;
713         tsetattr.mtime_sec = secs;
714         tsetattr.mtime_nsec = nanos;
715     })
716     .expect("failed to set mtime");
717 
718     assert_eq!(md.mtime() as u64, secs);
719     assert_eq!(md.mtime_nsec() as u64, nanos);
720 }
721 
722 #[test]
set_file_atime()723 fn set_file_atime() {
724     let (secs, nanos) = (9247605, 4016);
725     let md = set_attr_test(SetAttrKind::File, |tsetattr| {
726         tsetattr.valid = P9_SETATTR_ATIME | P9_SETATTR_ATIME_SET;
727         tsetattr.atime_sec = secs;
728         tsetattr.atime_nsec = nanos;
729     })
730     .expect("failed to set atime");
731 
732     assert_eq!(md.atime() as u64, secs);
733     assert_eq!(md.atime_nsec() as u64, nanos);
734 }
735 
736 #[test]
set_dir_mode()737 fn set_dir_mode() {
738     let mode = 0o640;
739     let err = set_attr_test(SetAttrKind::Directory, |tsetattr| {
740         tsetattr.valid = P9_SETATTR_MODE;
741         tsetattr.mode = mode;
742     })
743     .expect_err("successfully set mode");
744 
745     assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
746 }
747 
748 #[test]
set_dir_uid()749 fn set_dir_uid() {
750     let uid = 294;
751     let err = set_attr_test(SetAttrKind::Directory, |tsetattr| {
752         tsetattr.valid = P9_SETATTR_UID;
753         tsetattr.uid = uid;
754     })
755     .expect_err("successfully set uid");
756 
757     assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
758 }
759 
760 #[test]
set_dir_gid()761 fn set_dir_gid() {
762     let gid = 9024;
763     let err = set_attr_test(SetAttrKind::Directory, |tsetattr| {
764         tsetattr.valid = P9_SETATTR_GID;
765         tsetattr.gid = gid;
766     })
767     .expect_err("successfully set gid");
768 
769     assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
770 }
771 
772 #[test]
set_dir_mtime()773 fn set_dir_mtime() {
774     let (secs, nanos) = (1245247825, 524617);
775     let md = set_attr_test(SetAttrKind::Directory, |tsetattr| {
776         tsetattr.valid = P9_SETATTR_MTIME | P9_SETATTR_MTIME_SET;
777         tsetattr.mtime_sec = secs;
778         tsetattr.mtime_nsec = nanos;
779     })
780     .expect("failed to set mtime");
781 
782     assert_eq!(md.mtime() as u64, secs);
783     assert_eq!(md.mtime_nsec() as u64, nanos);
784 }
785 
786 #[test]
set_dir_atime()787 fn set_dir_atime() {
788     let (secs, nanos) = (9247605, 4016);
789     let md = set_attr_test(SetAttrKind::Directory, |tsetattr| {
790         tsetattr.valid = P9_SETATTR_ATIME | P9_SETATTR_ATIME_SET;
791         tsetattr.atime_sec = secs;
792         tsetattr.atime_nsec = nanos;
793     })
794     .expect("failed to set atime");
795 
796     assert_eq!(md.atime() as u64, secs);
797     assert_eq!(md.atime_nsec() as u64, nanos);
798 }
799 
800 #[test]
huge_directory()801 fn huge_directory() {
802     let (test_dir, mut server) = setup("huge_directory");
803 
804     let name = "newdir";
805     let newdir = test_dir.join(name);
806     fs::create_dir(&newdir).expect("failed to create directory");
807 
808     let dfid = ROOT_FID + 1;
809     walk(
810         &mut server,
811         &*test_dir,
812         ROOT_FID,
813         dfid,
814         vec![String::from(name)],
815     );
816 
817     // Create ~4K files in the directory and then attempt to read them all.
818     let mut filenames = HashSet::with_capacity(4096);
819     for i in 0..4096 {
820         let name = format!("file_{}", i);
821         create_local_file(&newdir, &name);
822         assert!(filenames.insert(name));
823     }
824 
825     let fid = dfid + 1;
826     open(&mut server, &newdir, dfid, "", fid, P9_DIRECTORY).expect("Failed to open directory");
827     for f in readdir(&mut server, fid) {
828         let path = newdir.join(&f.name);
829 
830         let md = fs::symlink_metadata(path).expect("failed to get metadata for path");
831         check_qid(&f.qid, &md);
832 
833         if f.name == "." || f.name == ".." {
834             assert_eq!(f.ty, libc::DT_DIR);
835         } else {
836             assert_eq!(f.ty, libc::DT_REG);
837             assert!(filenames.remove(&f.name));
838         }
839     }
840 
841     assert!(filenames.is_empty());
842 }
843 
844 #[test]
mkdir()845 fn mkdir() {
846     let (test_dir, mut server) = setup("mkdir");
847 
848     let name = "conan";
849     let tmkdir = Tmkdir {
850         dfid: ROOT_FID,
851         name: String::from(name),
852         mode: 0o755,
853         gid: 0,
854     };
855 
856     let rmkdir = server.mkdir(tmkdir).expect("failed to create directory");
857     let md =
858         fs::symlink_metadata(test_dir.join(name)).expect("failed to get metadata for directory");
859 
860     assert!(md.is_dir());
861     check_qid(&rmkdir.qid, &md);
862 }
863 
864 #[test]
unlink_all()865 fn unlink_all() {
866     let (test_dir, mut server) = setup("readdir");
867 
868     let mut next_fid = ROOT_FID + 1;
869 
870     let mut dirs = VecDeque::new();
871     dirs.push_back((ROOT_FID, test_dir.to_path_buf()));
872 
873     // First iterate over the whole directory.
874     let mut unlinks = VecDeque::new();
875     while let Some((dfid, dir)) = dirs.pop_front() {
876         let mut names = VecDeque::new();
877         for entry in fs::read_dir(dir).expect("failed to read directory") {
878             let entry = entry.expect("unable to iterate over directory");
879             let ft = entry
880                 .file_type()
881                 .expect("failed to get file type for entry");
882             if ft.is_dir() {
883                 let fid = next_fid;
884                 next_fid += 1;
885 
886                 let wnames: Vec<String> = entry
887                     .path()
888                     .strip_prefix(&test_dir)
889                     .expect("test directory is not prefix of subdir")
890                     .components()
891                     .map(|c| Path::new(&c).to_string_lossy().to_string())
892                     .collect();
893                 walk(&mut server, &*test_dir, ROOT_FID, fid, wnames);
894                 dirs.push_back((fid, entry.path()));
895             }
896 
897             names.push_back((
898                 entry
899                     .file_name()
900                     .into_string()
901                     .expect("failed to convert entry name to string"),
902                 if ft.is_dir() {
903                     libc::AT_REMOVEDIR as u32
904                 } else {
905                     0
906                 },
907             ));
908         }
909 
910         unlinks.push_back((dfid, names));
911     }
912 
913     // Now remove everything in reverse order.
914     while let Some((dfid, names)) = unlinks.pop_back() {
915         for (name, flags) in names {
916             let tunlinkat = Tunlinkat {
917                 dirfd: dfid,
918                 name,
919                 flags,
920             };
921 
922             server.unlink_at(tunlinkat).expect("failed to unlink path");
923         }
924     }
925 }
926 
927 #[test]
rename_at()928 fn rename_at() {
929     let (test_dir, mut server) = setup("rename");
930 
931     let name = "oldfile";
932     let content = create_local_file(&test_dir, name);
933 
934     let newname = "newfile";
935     let trename = Trenameat {
936         olddirfid: ROOT_FID,
937         oldname: String::from(name),
938         newdirfid: ROOT_FID,
939         newname: String::from(newname),
940     };
941 
942     server.rename_at(trename).expect("failed to rename file");
943 
944     assert!(!test_dir.join(name).exists());
945 
946     let mut newcontent = Vec::with_capacity(content.len());
947     let size = File::open(test_dir.join(newname))
948         .expect("failed to open file")
949         .read_to_end(&mut newcontent)
950         .expect("failed to read new file content");
951     assert_eq!(size, content.len());
952     assert_eq!(newcontent, content);
953 }
954 
955 macro_rules! open_test {
956     ($name:ident, $flags:expr) => {
957         #[test]
958         fn $name() {
959             let (test_dir, mut server) = setup("open");
960 
961             let fid = ROOT_FID + 1;
962             let name = "test.txt";
963             let content = create_local_file(&test_dir, name);
964 
965             let rlopen = open(&mut server, &*test_dir, ROOT_FID, name, fid, $flags as u32)
966                 .expect("failed to open file");
967 
968             let md =
969                 fs::symlink_metadata(test_dir.join(name)).expect("failed to get metadata for file");
970             check_qid(&rlopen.qid, &md);
971             assert_eq!(rlopen.iounit, md.blksize() as u32);
972 
973             check_attr(&mut server, fid, &md);
974 
975             // Check that the file has the proper contents as long as we didn't
976             // truncate it first.
977             if $flags & P9_TRUNC == 0 && $flags & P9_WRONLY == 0 {
978                 check_content(&mut server, &content, fid);
979             }
980 
981             // Check that we can write to the file.
982             if $flags & P9_RDWR != 0 || $flags & P9_WRONLY != 0 {
983                 write(&mut server, &test_dir, name, fid, $flags);
984             }
985 
986             let tclunk = Tclunk { fid };
987             server.clunk(&tclunk).expect("Unable to clunk file");
988         }
989     };
990     ($name:ident, $flags:expr, $expected_err:expr) => {
991         #[test]
992         fn $name() {
993             let (test_dir, mut server) = setup("open_fail");
994 
995             let fid = ROOT_FID + 1;
996             let name = "test.txt";
997             create_local_file(&test_dir, name);
998 
999             let err = open(&mut server, &*test_dir, ROOT_FID, name, fid, $flags as u32)
1000                 .expect_err("successfully opened file");
1001             assert_eq!(err.kind(), $expected_err);
1002 
1003             let tclunk = Tclunk { fid };
1004             server.clunk(&tclunk).expect("Unable to clunk file");
1005         }
1006     };
1007 }
1008 
1009 open_test!(read_only_file_open, P9_RDONLY);
1010 open_test!(read_write_file_open, P9_RDWR);
1011 open_test!(write_only_file_open, P9_WRONLY);
1012 
1013 open_test!(create_read_only_file_open, P9_CREATE | P9_RDONLY);
1014 open_test!(create_read_write_file_open, P9_CREATE | P9_RDWR);
1015 open_test!(create_write_only_file_open, P9_CREATE | P9_WRONLY);
1016 
1017 open_test!(append_read_only_file_open, P9_APPEND | P9_RDONLY);
1018 open_test!(append_read_write_file_open, P9_APPEND | P9_RDWR);
1019 open_test!(append_write_only_file_open, P9_APPEND | P9_WRONLY);
1020 
1021 open_test!(trunc_read_only_file_open, P9_TRUNC | P9_RDONLY);
1022 open_test!(trunc_read_write_file_open, P9_TRUNC | P9_RDWR);
1023 open_test!(trunc_write_only_file_open, P9_TRUNC | P9_WRONLY);
1024 
1025 open_test!(
1026     create_append_read_only_file_open,
1027     P9_CREATE | P9_APPEND | P9_RDONLY
1028 );
1029 open_test!(
1030     create_append_read_write_file_open,
1031     P9_CREATE | P9_APPEND | P9_RDWR
1032 );
1033 open_test!(
1034     create_append_wronly_file_open,
1035     P9_CREATE | P9_APPEND | P9_WRONLY
1036 );
1037 
1038 open_test!(
1039     create_trunc_read_only_file_open,
1040     P9_CREATE | P9_TRUNC | P9_RDONLY
1041 );
1042 open_test!(
1043     create_trunc_read_write_file_open,
1044     P9_CREATE | P9_TRUNC | P9_RDWR
1045 );
1046 open_test!(
1047     create_trunc_wronly_file_open,
1048     P9_CREATE | P9_TRUNC | P9_WRONLY
1049 );
1050 
1051 open_test!(
1052     append_trunc_read_only_file_open,
1053     P9_APPEND | P9_TRUNC | P9_RDONLY
1054 );
1055 open_test!(
1056     append_trunc_read_write_file_open,
1057     P9_APPEND | P9_TRUNC | P9_RDWR
1058 );
1059 open_test!(
1060     append_trunc_wronly_file_open,
1061     P9_APPEND | P9_TRUNC | P9_WRONLY
1062 );
1063 
1064 open_test!(
1065     create_append_trunc_read_only_file_open,
1066     P9_CREATE | P9_APPEND | P9_TRUNC | P9_RDONLY
1067 );
1068 open_test!(
1069     create_append_trunc_read_write_file_open,
1070     P9_CREATE | P9_APPEND | P9_TRUNC | P9_RDWR
1071 );
1072 open_test!(
1073     create_append_trunc_wronly_file_open,
1074     P9_CREATE | P9_APPEND | P9_TRUNC | P9_WRONLY
1075 );
1076 
1077 open_test!(
1078     create_excl_read_only_file_open,
1079     P9_CREATE | P9_EXCL | P9_RDONLY,
1080     io::ErrorKind::AlreadyExists
1081 );
1082 open_test!(
1083     create_excl_read_write_file_open,
1084     P9_CREATE | P9_EXCL | P9_RDWR,
1085     io::ErrorKind::AlreadyExists
1086 );
1087 open_test!(
1088     create_excl_wronly_file_open,
1089     P9_CREATE | P9_EXCL | P9_WRONLY,
1090     io::ErrorKind::AlreadyExists
1091 );
1092 
1093 macro_rules! create_test {
1094     ($name:ident, $flags:expr, $mode:expr) => {
1095         #[test]
1096         fn $name() {
1097             let (test_dir, mut server) = setup("create");
1098 
1099             let name = "foo.txt";
1100             let fid = ROOT_FID + 1;
1101             let rlcreate = create(&mut server, &*test_dir, ROOT_FID, fid, name, $flags, $mode)
1102                 .expect("failed to create file");
1103 
1104             let md =
1105                 fs::symlink_metadata(test_dir.join(name)).expect("failed to get metadata for file");
1106             assert_eq!(rlcreate.iounit, md.blksize() as u32);
1107             check_qid(&rlcreate.qid, &md);
1108             check_attr(&mut server, fid, &md);
1109 
1110             // Check that we can write to the file.
1111             if $flags & P9_RDWR != 0 || $flags & P9_WRONLY != 0 {
1112                 write(&mut server, &test_dir, name, fid, $flags);
1113             }
1114 
1115             let tclunk = Tclunk { fid };
1116             server.clunk(&tclunk).expect("Unable to clunk file");
1117         }
1118     };
1119     ($name:ident, $flags:expr, $mode:expr, $expected_err:expr) => {
1120         #[test]
1121         fn $name() {
1122             let (test_dir, mut server) = setup("create_fail");
1123 
1124             let name = "foo.txt";
1125             // The `fid` in the lcreate call initially points to the directory
1126             // but is supposed to point to the newly created file after the call
1127             // completes.  Duplicate the fid so that we don't end up consuming the
1128             // root fid.
1129             let fid = ROOT_FID + 1;
1130             let err = create(&mut server, &*test_dir, ROOT_FID, fid, name, $flags, $mode)
1131                 .expect_err("successfully created file");
1132             assert_eq!(err.kind(), $expected_err);
1133         }
1134     };
1135 }
1136 
1137 create_test!(read_only_file_create, P9_RDONLY, 0o600u32);
1138 create_test!(read_write_file_create, P9_RDWR, 0o600u32);
1139 create_test!(write_only_file_create, P9_WRONLY, 0o600u32);
1140 
1141 create_test!(
1142     append_read_only_file_create,
1143     P9_APPEND | P9_RDONLY,
1144     0o600u32
1145 );
1146 create_test!(append_read_write_file_create, P9_APPEND | P9_RDWR, 0o600u32);
1147 create_test!(append_wronly_file_create, P9_APPEND | P9_WRONLY, 0o600u32);
1148