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