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