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