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