1use crate::file::FatFile;
5use crate::filesystem::{FatFilesystem, FatFilesystemInner};
6use crate::node::{Closer, FatNode, Node, WeakFatNode};
7use crate::refs::{FatfsDirRef, FatfsFileRef};
8use crate::types::{Dir, DirEntry, File};
9use crate::util::{dos_to_unix_time, fatfs_error_to_status, unix_to_dos_time};
10use fatfs::validate_filename;
11use fidl::endpoints::ServerEnd;
12use fidl_fuchsia_io as fio;
13use fuchsia_sync::RwLock;
14use futures::future::BoxFuture;
15use std::borrow::Borrow;
16use std::cell::UnsafeCell;
17use std::collections::HashMap;
18use std::fmt::Debug;
19use std::hash::{Hash, Hasher};
20use std::pin::Pin;
21use std::sync::Arc;
22use vfs::directory::dirents_sink::{self, AppendResult, Sink};
23use vfs::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
24use vfs::directory::entry_container::{Directory, DirectoryWatcher, MutableDirectory};
25use vfs::directory::mutable::connection::MutableConnection;
26use vfs::directory::traversal_position::TraversalPosition;
27use vfs::directory::watchers::event_producers::{SingleNameEventProducer, StaticVecEventProducer};
28use vfs::directory::watchers::Watchers;
29use vfs::execution_scope::ExecutionScope;
30use vfs::file::FidlIoConnection;
31use vfs::path::Path;
32use vfs::{attributes, ObjectRequestRef, ProtocolsExt as _, ToObjectRequest};
33use zx::Status;
34
35fn check_open_flags_for_existing_entry(flags: fio::OpenFlags) -> Result<(), Status> {
36 if flags.intersects(fio::OpenFlags::CREATE_IF_ABSENT) {
37 return Err(Status::ALREADY_EXISTS);
38 }
39 Ok(())
41}
42
43struct FatDirectoryData {
44 parent: Option<Arc<FatDirectory>>,
47 children: HashMap<InsensitiveString, WeakFatNode>,
51 deleted: bool,
53 watchers: Watchers,
54 name: String,
56}
57
58struct InsensitiveString(String);
64
65impl Hash for InsensitiveString {
66 fn hash<H: Hasher>(&self, hasher: &mut H) {
67 for c in self.0.chars().flat_map(|c| c.to_uppercase()) {
68 hasher.write_u32(c as u32);
69 }
70 }
71}
72
73impl PartialEq for InsensitiveString {
74 fn eq(&self, other: &Self) -> bool {
75 self.0
76 .chars()
77 .flat_map(|c| c.to_uppercase())
78 .eq(other.0.chars().flat_map(|c| c.to_uppercase()))
79 }
80}
81
82impl Eq for InsensitiveString {}
83
84pub(crate) trait InsensitiveStringRef {
86 fn as_str(&self) -> &str;
87}
88
89impl<'a> Borrow<dyn InsensitiveStringRef + 'a> for InsensitiveString {
90 fn borrow(&self) -> &(dyn InsensitiveStringRef + 'a) {
91 self
92 }
93}
94
95impl<'a> Eq for (dyn InsensitiveStringRef + 'a) {}
96
97impl<'a> PartialEq for (dyn InsensitiveStringRef + 'a) {
98 fn eq(&self, other: &dyn InsensitiveStringRef) -> bool {
99 self.as_str()
100 .chars()
101 .flat_map(|c| c.to_uppercase())
102 .eq(other.as_str().chars().flat_map(|c| c.to_uppercase()))
103 }
104}
105
106impl<'a> Hash for (dyn InsensitiveStringRef + 'a) {
107 fn hash<H: Hasher>(&self, hasher: &mut H) {
108 for c in self.as_str().chars().flat_map(|c| c.to_uppercase()) {
109 hasher.write_u32(c as u32);
110 }
111 }
112}
113
114impl InsensitiveStringRef for &str {
115 fn as_str(&self) -> &str {
116 self
117 }
118}
119
120impl InsensitiveStringRef for InsensitiveString {
121 fn as_str(&self) -> &str {
122 &self.0
123 }
124}
125
126pub struct FatDirectory {
128 dir: UnsafeCell<FatfsDirRef>,
130 filesystem: Pin<Arc<FatFilesystem>>,
134 data: RwLock<FatDirectoryData>,
138}
139
140unsafe impl Sync for FatDirectory {}
144unsafe impl Send for FatDirectory {}
145
146enum ExistingRef<'a, 'b> {
147 None,
148 File(&'a mut crate::types::File<'b>),
149 Dir(&'a mut crate::types::Dir<'b>),
150}
151
152impl FatDirectory {
153 pub(crate) fn new(
155 dir: FatfsDirRef,
156 parent: Option<Arc<FatDirectory>>,
157 filesystem: Pin<Arc<FatFilesystem>>,
158 name: String,
159 ) -> Arc<Self> {
160 Arc::new(FatDirectory {
161 dir: UnsafeCell::new(dir),
162 filesystem,
163 data: RwLock::new(FatDirectoryData {
164 parent,
165 children: HashMap::new(),
166 deleted: false,
167 watchers: Watchers::new(),
168 name,
169 }),
170 })
171 }
172
173 pub(crate) fn fs(&self) -> &Pin<Arc<FatFilesystem>> {
174 &self.filesystem
175 }
176
177 pub(crate) fn borrow_dir<'a>(&self, fs: &'a FatFilesystemInner) -> Result<&Dir<'a>, Status> {
179 unsafe { self.dir.get().as_ref() }.unwrap().borrow(fs).ok_or(Status::BAD_HANDLE)
180 }
181
182 pub(crate) fn borrow_dir_mut<'a>(&self, fs: &'a FatFilesystemInner) -> Option<&mut Dir<'a>> {
184 unsafe { self.dir.get().as_mut() }.unwrap().borrow_mut(fs)
185 }
186
187 pub(crate) fn find_child<'a>(
189 &'a self,
190 fs: &'a FatFilesystemInner,
191 name: &str,
192 ) -> Result<Option<DirEntry<'a>>, Status> {
193 if self.data.read().deleted {
194 return Ok(None);
195 }
196 let dir = self.borrow_dir(fs)?;
197 for entry in dir.iter().into_iter() {
198 let entry = entry?;
199 if entry.eq_name(name) {
200 return Ok(Some(entry));
201 }
202 }
203 Ok(None)
204 }
205
206 pub fn remove_child(&self, fs: &FatFilesystemInner, name: &str) -> Option<FatNode> {
210 let node = self.cache_remove(fs, name);
211 if let Some(node) = node {
212 node.detach(fs);
213 Some(node)
214 } else {
215 None
216 }
217 }
218
219 pub fn add_child(
223 self: &Arc<Self>,
224 fs: &FatFilesystemInner,
225 name: String,
226 child: FatNode,
227 ) -> Result<(), Status> {
228 child.attach(self.clone(), &name, fs)?;
229 let mut data = self.data.write();
232 if let Some(node) = data.children.insert(InsensitiveString(name), child.downgrade()) {
234 assert!(node.upgrade().is_none(), "conflicting cache entries with the same name")
235 }
236 Ok(())
237 }
238
239 pub(crate) fn cache_remove(&self, _fs: &FatFilesystemInner, name: &str) -> Option<FatNode> {
242 let mut data = self.data.write();
243 data.children.remove(&name as &dyn InsensitiveStringRef).and_then(|entry| entry.upgrade())
244 }
245
246 pub fn cache_get(&self, name: &str) -> Option<FatNode> {
248 let data = self.data.read();
251 data.children.get(&name as &dyn InsensitiveStringRef).and_then(|entry| entry.upgrade())
252 }
253
254 fn lookup(
255 self: &Arc<Self>,
256 flags: fio::OpenFlags,
257 mut path: Path,
258 closer: &mut Closer<'_>,
259 ) -> Result<FatNode, Status> {
260 let mut cur_entry = FatNode::Dir(self.clone());
261
262 while !path.is_empty() {
263 let child_flags =
264 if path.is_single_component() { flags } else { fio::OpenFlags::DIRECTORY };
265
266 match cur_entry {
267 FatNode::Dir(entry) => {
268 let name = path.next().unwrap();
269 validate_filename(name)?;
270 cur_entry = entry.clone().open_child(name, child_flags, closer)?;
271 }
272 FatNode::File(_) => {
273 return Err(Status::NOT_DIR);
274 }
275 };
276 }
277
278 Ok(cur_entry)
279 }
280
281 fn lookup_with_open3_flags(
282 self: &Arc<Self>,
283 flags: fio::Flags,
284 mut path: Path,
285 closer: &mut Closer<'_>,
286 ) -> Result<FatNode, Status> {
287 let mut current_entry = FatNode::Dir(self.clone());
288
289 while !path.is_empty() {
290 let child_flags =
291 if path.is_single_component() { flags } else { fio::Flags::PROTOCOL_DIRECTORY };
292
293 match current_entry {
294 FatNode::Dir(entry) => {
295 let name = path.next().unwrap();
296 validate_filename(name)?;
297 current_entry = entry.clone().open3_child(name, child_flags, closer)?;
298 }
299 FatNode::File(_) => {
300 return Err(Status::NOT_DIR);
301 }
302 };
303 }
304
305 Ok(current_entry)
306 }
307
308 pub(crate) fn open_child(
315 self: &Arc<Self>,
316 name: &str,
317 flags: fio::OpenFlags,
318 closer: &mut Closer<'_>,
319 ) -> Result<FatNode, Status> {
320 let fs_lock = self.filesystem.lock();
321 if let Some(entry) = self.cache_get(name) {
323 check_open_flags_for_existing_entry(flags)?;
324 entry.open_ref(&fs_lock)?;
325 return Ok(closer.add(entry));
326 };
327
328 let mut created = false;
329 let node = {
330 let entry = self.find_child(&fs_lock, name)?;
332 if let Some(entry) = entry {
333 check_open_flags_for_existing_entry(flags)?;
334 if entry.is_dir() {
335 self.add_directory(entry.to_dir(), name, closer)
336 } else {
337 self.add_file(entry.to_file(), name, closer)
338 }
339 } else if flags.intersects(fio::OpenFlags::CREATE) {
340 created = true;
342 let dir = self.borrow_dir(&fs_lock)?;
343 if flags.intersects(fio::OpenFlags::DIRECTORY) {
344 let dir = dir.create_dir(name).map_err(fatfs_error_to_status)?;
345 self.add_directory(dir, name, closer)
346 } else {
347 let file = dir.create_file(name).map_err(fatfs_error_to_status)?;
348 self.add_file(file, name, closer)
349 }
350 } else {
351 return Err(Status::NOT_FOUND);
353 }
354 };
355
356 let mut data = self.data.write();
357 data.children.insert(InsensitiveString(name.to_owned()), node.downgrade());
358 if created {
359 data.watchers.send_event(&mut SingleNameEventProducer::added(name));
360 self.filesystem.mark_dirty();
361 }
362
363 Ok(node)
364 }
365
366 pub(crate) fn open3_child(
367 self: &Arc<Self>,
368 name: &str,
369 flags: fio::Flags,
370 closer: &mut Closer<'_>,
371 ) -> Result<FatNode, Status> {
372 if flags.create_unnamed_temporary_in_directory_path() {
373 return Err(Status::NOT_SUPPORTED);
374 }
375 let fs_lock = self.filesystem.lock();
376
377 if let Some(entry) = self.cache_get(name) {
379 if flags.creation_mode() == vfs::CreationMode::Always {
380 return Err(Status::ALREADY_EXISTS);
381 }
382 entry.open_ref(&fs_lock)?;
383 return Ok(closer.add(entry));
384 };
385
386 let mut created_entry = false;
387 let node = match self.find_child(&fs_lock, name)? {
388 Some(entry) => {
389 if flags.creation_mode() == vfs::CreationMode::Always {
390 return Err(Status::ALREADY_EXISTS);
391 }
392 if entry.is_dir() {
393 self.add_directory(entry.to_dir(), name, closer)
394 } else {
395 self.add_file(entry.to_file(), name, closer)
396 }
397 }
398 None => {
399 if flags.creation_mode() == vfs::CreationMode::Never {
400 return Err(Status::NOT_FOUND);
401 }
402 created_entry = true;
403 let dir = self.borrow_dir(&fs_lock)?;
404
405 if flags.intersects(fio::Flags::PROTOCOL_DIRECTORY) {
407 let dir = dir.create_dir(name).map_err(fatfs_error_to_status)?;
408 self.add_directory(dir, name, closer)
409 } else {
410 let file = dir.create_file(name).map_err(fatfs_error_to_status)?;
411 self.add_file(file, name, closer)
412 }
413 }
414 };
415
416 let mut data = self.data.write();
417 data.children.insert(InsensitiveString(name.to_owned()), node.downgrade());
418 if created_entry {
419 data.watchers.send_event(&mut SingleNameEventProducer::added(name));
420 self.filesystem.mark_dirty();
421 }
422
423 Ok(node)
424 }
425
426 pub(crate) fn is_deleted(&self) -> bool {
428 self.data.read().deleted
429 }
430
431 pub(crate) fn did_remove(&self, name: &str) {
433 self.data.write().watchers.send_event(&mut SingleNameEventProducer::removed(name));
434 }
435
436 pub(crate) fn did_add(&self, name: &str) {
438 self.data.write().watchers.send_event(&mut SingleNameEventProducer::added(name));
439 }
440
441 fn rename_internal(
445 &self,
446 filesystem: &FatFilesystemInner,
447 src_dir: &Arc<FatDirectory>,
448 src_name: &str,
449 dst_name: &str,
450 existing: ExistingRef<'_, '_>,
451 ) -> Result<(), Status> {
452 let src_fatfs_dir = src_dir.borrow_dir(&filesystem)?;
460 let dst_fatfs_dir = self.borrow_dir(&filesystem)?;
461
462 match existing {
463 ExistingRef::None => {
464 src_fatfs_dir
465 .rename(src_name, &dst_fatfs_dir, dst_name)
466 .map_err(fatfs_error_to_status)?;
467 }
468 ExistingRef::File(file) => {
469 src_fatfs_dir
470 .rename_over_file(src_name, &dst_fatfs_dir, dst_name, file)
471 .map_err(fatfs_error_to_status)?;
472 }
473 ExistingRef::Dir(dir) => {
474 src_fatfs_dir
475 .rename_over_dir(src_name, &dst_fatfs_dir, dst_name, dir)
476 .map_err(fatfs_error_to_status)?;
477 }
478 }
479
480 src_dir.did_remove(src_name);
481 self.did_add(dst_name);
482
483 src_dir.fs().mark_dirty();
484
485 Ok(())
488 }
489
490 fn rename_locked(
492 self: &Arc<Self>,
493 filesystem: &FatFilesystemInner,
494 src_dir: &Arc<FatDirectory>,
495 src_name: &str,
496 dst_name: &str,
497 src_is_dir: bool,
498 closer: &mut Closer<'_>,
499 ) -> Result<(), Status> {
500 if Arc::ptr_eq(&src_dir, self)
503 && (&src_name as &dyn InsensitiveStringRef) == (&dst_name as &dyn InsensitiveStringRef)
504 {
505 if src_name != dst_name {
506 return self.rename_internal(
508 &filesystem,
509 src_dir,
510 src_name,
511 dst_name,
512 ExistingRef::None,
513 );
514 }
515 return Ok(());
516 }
517
518 if let Some(src_node) = src_dir.cache_get(src_name) {
520 if let FatNode::Dir(dir) = &src_node {
521 if Arc::ptr_eq(&dir, self) {
522 return Err(Status::INVALID_ARGS);
523 }
524 let mut dest = self.clone();
526 loop {
527 let next_dir = if let Some(parent) = &dest.data.read().parent {
528 if Arc::ptr_eq(&dir, parent) {
529 return Err(Status::INVALID_ARGS);
530 }
531 parent.clone()
532 } else {
533 break;
534 };
535 dest = next_dir;
536 }
537 }
538 src_node.flush_dir_entry(filesystem)?;
539 }
540
541 let mut dir;
542 let mut file;
543 let mut existing_node = self.cache_get(dst_name);
544 let existing = match existing_node {
545 None => {
546 self.open_ref(filesystem)?;
547 closer.add(FatNode::Dir(self.clone()));
548 match self.find_child(filesystem, dst_name)? {
549 Some(ref dir_entry) => {
550 if dir_entry.is_dir() {
551 dir = Some(dir_entry.to_dir());
552 ExistingRef::Dir(dir.as_mut().unwrap())
553 } else {
554 file = Some(dir_entry.to_file());
555 ExistingRef::File(file.as_mut().unwrap())
556 }
557 }
558 None => ExistingRef::None,
559 }
560 }
561 Some(ref mut node) => {
562 node.open_ref(filesystem)?;
563 closer.add(node.clone());
564 match node {
565 FatNode::Dir(ref mut node_dir) => {
566 ExistingRef::Dir(node_dir.borrow_dir_mut(filesystem).unwrap())
567 }
568 FatNode::File(ref mut node_file) => {
569 ExistingRef::File(node_file.borrow_file_mut(filesystem).unwrap())
570 }
571 }
572 }
573 };
574
575 match existing {
576 ExistingRef::File(_) => {
577 if src_is_dir {
578 return Err(Status::NOT_DIR);
579 }
580 }
581 ExistingRef::Dir(_) => {
582 if !src_is_dir {
583 return Err(Status::NOT_FILE);
584 }
585 }
586 ExistingRef::None => {}
587 }
588
589 self.rename_internal(&filesystem, src_dir, src_name, dst_name, existing)?;
590
591 if let Some(_) = existing_node {
592 self.cache_remove(&filesystem, &dst_name).unwrap().did_delete();
593 }
594
595 if let Some(node) = src_dir.remove_child(&filesystem, &src_name) {
597 self.add_child(&filesystem, dst_name.to_owned(), node)
598 .unwrap_or_else(|e| panic!("Rename failed, but fatfs says it didn't? - {:?}", e));
599 }
600
601 Ok(())
602 }
603
604 fn add_directory(
606 self: &Arc<Self>,
607 dir: Dir<'_>,
608 name: &str,
609 closer: &mut Closer<'_>,
610 ) -> FatNode {
611 let dir_ref = unsafe { FatfsDirRef::from(dir) };
614 closer.add(FatNode::Dir(FatDirectory::new(
615 dir_ref,
616 Some(self.clone()),
617 self.filesystem.clone(),
618 name.to_owned(),
619 )))
620 }
621
622 fn add_file(self: &Arc<Self>, file: File<'_>, name: &str, closer: &mut Closer<'_>) -> FatNode {
624 let file_ref = unsafe { FatfsFileRef::from(file) };
627 closer.add(FatNode::File(FatFile::new(
628 file_ref,
629 self.clone(),
630 self.filesystem.clone(),
631 name.to_owned(),
632 )))
633 }
634}
635
636impl Node for FatDirectory {
637 fn detach(&self, fs: &FatFilesystemInner) {
640 let dir = unsafe { self.dir.get().as_mut() }.unwrap();
642 dir.take(fs);
644 }
645
646 fn attach(
649 &self,
650 new_parent: Arc<FatDirectory>,
651 name: &str,
652 fs: &FatFilesystemInner,
653 ) -> Result<(), Status> {
654 let mut data = self.data.write();
655 data.name = name.to_owned();
656
657 let dir = unsafe { self.dir.get().as_mut().unwrap() };
659 unsafe { dir.maybe_reopen(fs, Some(&new_parent), name)? };
661
662 assert!(data.parent.replace(new_parent).is_some());
663 Ok(())
664 }
665
666 fn did_delete(&self) {
667 let mut data = self.data.write();
668 data.parent.take();
669 data.watchers.send_event(&mut SingleNameEventProducer::deleted());
670 data.deleted = true;
671 }
672
673 fn open_ref(&self, fs: &FatFilesystemInner) -> Result<(), Status> {
674 let data = self.data.read();
675 let dir_ref = unsafe { self.dir.get().as_mut() }.unwrap();
676
677 unsafe { dir_ref.open(&fs, data.parent.as_ref(), &data.name) }
678 }
679
680 fn shut_down(&self, fs: &FatFilesystemInner) -> Result<(), Status> {
681 unsafe { self.dir.get().as_mut() }.unwrap().take(fs);
682 let mut data = self.data.write();
683 for (_, child) in data.children.drain() {
684 if let Some(child) = child.upgrade() {
685 child.shut_down(fs)?;
686 }
687 }
688 Ok(())
689 }
690
691 fn flush_dir_entry(&self, fs: &FatFilesystemInner) -> Result<(), Status> {
692 if let Some(ref mut dir) = self.borrow_dir_mut(fs) {
693 dir.flush_dir_entry().map_err(fatfs_error_to_status)?;
694 }
695 Ok(())
696 }
697
698 fn close_ref(&self, fs: &FatFilesystemInner) {
699 unsafe { self.dir.get().as_mut() }.unwrap().close(fs);
700 }
701}
702
703impl Debug for FatDirectory {
704 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
705 f.debug_struct("FatDirectory").field("parent", &self.data.read().parent).finish()
706 }
707}
708
709impl MutableDirectory for FatDirectory {
710 async fn unlink(self: Arc<Self>, name: &str, must_be_directory: bool) -> Result<(), Status> {
711 let fs_lock = self.filesystem.lock();
712 let parent = self.borrow_dir(&fs_lock)?;
713 let mut existing_node = self.cache_get(name);
714 let mut done = false;
715 match existing_node {
716 Some(FatNode::File(ref mut file)) => {
717 if must_be_directory {
718 return Err(Status::NOT_DIR);
719 }
720 if let Some(file) = file.borrow_file_mut(&fs_lock) {
721 parent.unlink_file(file).map_err(fatfs_error_to_status)?;
722 done = true;
723 }
724 }
725 Some(FatNode::Dir(ref mut dir)) => {
726 if let Some(dir) = dir.borrow_dir_mut(&fs_lock) {
727 parent.unlink_dir(dir).map_err(fatfs_error_to_status)?;
728 done = true;
729 }
730 }
731 None => {
732 if must_be_directory {
733 let entry = self.find_child(&fs_lock, name)?;
734 if !entry.ok_or(Status::NOT_FOUND)?.is_dir() {
735 return Err(Status::NOT_DIR);
736 }
737 }
738 }
739 }
740 if !done {
741 parent.remove(name).map_err(fatfs_error_to_status)?;
742 }
743 if existing_node.is_some() {
744 self.cache_remove(&fs_lock, name);
745 }
746 match existing_node {
747 Some(FatNode::File(ref mut file)) => file.did_delete(),
748 Some(FatNode::Dir(ref mut dir)) => dir.did_delete(),
749 None => {}
750 }
751
752 self.filesystem.mark_dirty();
753 self.data.write().watchers.send_event(&mut SingleNameEventProducer::removed(name));
754 Ok(())
755 }
756
757 async fn update_attributes(
758 &self,
759 attributes: fio::MutableNodeAttributes,
760 ) -> Result<(), Status> {
761 const SUPPORTED_MUTABLE_ATTRIBUTES: fio::NodeAttributesQuery =
762 fio::NodeAttributesQuery::CREATION_TIME
763 .union(fio::NodeAttributesQuery::MODIFICATION_TIME);
764
765 if !SUPPORTED_MUTABLE_ATTRIBUTES
766 .contains(vfs::common::mutable_node_attributes_to_query(&attributes))
767 {
768 return Err(Status::NOT_SUPPORTED);
769 }
770
771 let fs_lock = self.filesystem.lock();
772 let dir = self.borrow_dir_mut(&fs_lock).ok_or(Status::BAD_HANDLE)?;
773 if let Some(creation_time) = attributes.creation_time {
774 dir.set_created(unix_to_dos_time(creation_time));
775 }
776 if let Some(modification_time) = attributes.modification_time {
777 dir.set_modified(unix_to_dos_time(modification_time));
778 }
779
780 self.filesystem.mark_dirty();
781 Ok(())
782 }
783
784 async fn sync(&self) -> Result<(), Status> {
785 Ok(())
787 }
788
789 fn rename(
790 self: Arc<Self>,
791 src_dir: Arc<dyn MutableDirectory>,
792 src_path: Path,
793 dst_path: Path,
794 ) -> BoxFuture<'static, Result<(), Status>> {
795 Box::pin(async move {
796 let src_dir =
797 src_dir.into_any().downcast::<FatDirectory>().map_err(|_| Status::INVALID_ARGS)?;
798 if self.is_deleted() {
799 return Err(Status::NOT_FOUND);
801 }
802
803 let src_name = src_path.peek().unwrap();
804 validate_filename(src_name).map_err(fatfs_error_to_status)?;
805 let dst_name = dst_path.peek().unwrap();
806 validate_filename(dst_name).map_err(fatfs_error_to_status)?;
807
808 let mut closer = Closer::new(&self.filesystem);
809 let filesystem = self.filesystem.lock();
810
811 let entry = src_dir.find_child(&filesystem, &src_name)?;
813 if entry.is_none() {
814 return Err(Status::NOT_FOUND);
817 }
818 let src_is_dir = entry.unwrap().is_dir();
819 if (dst_path.is_dir() || src_path.is_dir()) && !src_is_dir {
820 return Err(Status::NOT_DIR);
823 }
824
825 self.rename_locked(&filesystem, &src_dir, src_name, dst_name, src_is_dir, &mut closer)
826 })
827 }
828}
829
830impl DirectoryEntry for FatDirectory {
831 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
832 request.open_dir(self)
833 }
834}
835
836impl GetEntryInfo for FatDirectory {
837 fn entry_info(&self) -> EntryInfo {
838 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
839 }
840}
841
842impl vfs::node::Node for FatDirectory {
843 async fn get_attributes(
845 &self,
846 requested_attributes: fio::NodeAttributesQuery,
847 ) -> Result<fio::NodeAttributes2, Status> {
848 let fs_lock = self.filesystem.lock();
849 let dir = self.borrow_dir(&fs_lock)?;
850
851 let creation_time = dos_to_unix_time(dir.created());
852 let modification_time = dos_to_unix_time(dir.modified());
853
854 Ok(attributes!(
855 requested_attributes,
856 Mutable { creation_time: creation_time, modification_time: modification_time },
857 Immutable {
858 protocols: fio::NodeProtocolKinds::DIRECTORY,
859 abilities: fio::Operations::GET_ATTRIBUTES
860 | fio::Operations::UPDATE_ATTRIBUTES
861 | fio::Operations::ENUMERATE
862 | fio::Operations::TRAVERSE
863 | fio::Operations::MODIFY_DIRECTORY,
864 link_count: 1, }
866 ))
867 }
868
869 fn close(self: Arc<Self>) {
870 self.close_ref(&self.filesystem.lock());
871 }
872
873 fn query_filesystem(&self) -> Result<fio::FilesystemInfo, Status> {
874 self.filesystem.query_filesystem()
875 }
876
877 fn will_clone(&self) {
878 self.open_ref(&self.filesystem.lock()).unwrap();
879 }
880}
881
882impl Directory for FatDirectory {
883 fn open(
884 self: Arc<Self>,
885 scope: ExecutionScope,
886 flags: fio::OpenFlags,
887 path: Path,
888 server_end: ServerEnd<fio::NodeMarker>,
889 ) {
890 let mut closer = Closer::new(&self.filesystem);
891
892 flags.to_object_request(server_end).handle(|object_request| {
893 match self.lookup(flags, path, &mut closer)? {
894 FatNode::Dir(entry) => {
895 let () = entry
896 .open_ref(&self.filesystem.lock())
897 .expect("entry should already be open");
898 object_request
899 .take()
900 .create_connection_sync::<MutableConnection<_>, _>(scope, entry, flags);
901 Ok(())
902 }
903 FatNode::File(entry) => {
904 let () = entry.open_ref(&self.filesystem.lock())?;
905 object_request
906 .take()
907 .create_connection_sync::<FidlIoConnection<_>, _>(scope, entry, flags);
908 Ok(())
909 }
910 }
911 });
912 }
913
914 fn open3(
915 self: Arc<Self>,
916 scope: ExecutionScope,
917 path: Path,
918 flags: fio::Flags,
919 object_request: ObjectRequestRef<'_>,
920 ) -> Result<(), Status> {
921 let mut closer = Closer::new(&self.filesystem);
922
923 match self.lookup_with_open3_flags(flags, path, &mut closer)? {
924 FatNode::Dir(entry) => {
925 let () = entry.open_ref(&self.filesystem.lock())?;
926 object_request
927 .take()
928 .create_connection_sync::<MutableConnection<_>, _>(scope, entry, flags);
929 Ok(())
930 }
931 FatNode::File(entry) => {
932 let () = entry.open_ref(&self.filesystem.lock())?;
933 object_request
934 .take()
935 .create_connection_sync::<FidlIoConnection<_>, _>(scope, entry, flags);
936 Ok(())
937 }
938 }
939 }
940
941 async fn read_dirents<'a>(
942 &'a self,
943 pos: &'a TraversalPosition,
944 sink: Box<dyn Sink>,
945 ) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), Status> {
946 if self.is_deleted() {
947 return Ok((TraversalPosition::End, sink.seal()));
948 }
949
950 let fs_lock = self.filesystem.lock();
951 let dir = self.borrow_dir(&fs_lock)?;
952
953 if let TraversalPosition::End = pos {
954 return Ok((TraversalPosition::End, sink.seal()));
955 }
956
957 let filter = |name: &str| match pos {
958 TraversalPosition::Start => true,
959 TraversalPosition::Name(next_name) => name >= next_name.as_str(),
960 _ => false,
961 };
962
963 let mut entries: Vec<_> = dir
965 .iter()
966 .filter_map(|maybe_entry| {
967 maybe_entry
968 .map(|entry| {
969 let name = entry.file_name();
970 if &name == ".." || !filter(&name) {
971 None
972 } else {
973 let entry_type = if entry.is_dir() {
974 fio::DirentType::Directory
975 } else {
976 fio::DirentType::File
977 };
978 Some((name, EntryInfo::new(fio::INO_UNKNOWN, entry_type)))
979 }
980 })
981 .transpose()
982 })
983 .collect::<std::io::Result<Vec<_>>>()?;
984
985 if self.data.read().parent.is_none() && filter(".") {
987 entries.push((
988 ".".to_owned(),
989 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory),
990 ));
991 }
992
993 entries.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
995
996 let mut cur_sink = sink;
998 for (name, info) in entries.into_iter() {
999 let result = cur_sink.append(&info, &name.clone());
1000
1001 match result {
1002 AppendResult::Ok(new_sink) => cur_sink = new_sink,
1003 AppendResult::Sealed(sealed) => {
1004 return Ok((TraversalPosition::Name(name), sealed));
1005 }
1006 }
1007 }
1008
1009 return Ok((TraversalPosition::End, cur_sink.seal()));
1010 }
1011
1012 fn register_watcher(
1013 self: Arc<Self>,
1014 scope: ExecutionScope,
1015 mask: fio::WatchMask,
1016 watcher: DirectoryWatcher,
1017 ) -> Result<(), Status> {
1018 let fs_lock = self.filesystem.lock();
1019 let mut data = self.data.write();
1020 let is_deleted = data.deleted;
1021 let is_root = data.parent.is_none();
1022 let controller = data.watchers.add(scope, self.clone(), mask, watcher);
1023 if mask.contains(fio::WatchMask::EXISTING) && !is_deleted {
1024 let entries = {
1025 let dir = self.borrow_dir(&fs_lock)?;
1026 let synthesized_dot = if is_root {
1027 Some(Ok(".".to_owned()))
1029 } else {
1030 None
1031 };
1032 synthesized_dot
1033 .into_iter()
1034 .chain(dir.iter().filter_map(|maybe_entry| {
1035 maybe_entry
1036 .map(|entry| {
1037 let name = entry.file_name();
1038 if &name == ".." {
1039 None
1040 } else {
1041 Some(name)
1042 }
1043 })
1044 .transpose()
1045 }))
1046 .collect::<std::io::Result<Vec<String>>>()
1047 .map_err(fatfs_error_to_status)?
1048 };
1049 controller.send_event(&mut StaticVecEventProducer::existing(entries));
1050 }
1051 controller.send_event(&mut SingleNameEventProducer::idle());
1052 Ok(())
1053 }
1054
1055 fn unregister_watcher(self: Arc<Self>, key: usize) {
1056 self.data.write().watchers.remove(key);
1057 }
1058}
1059
1060#[cfg(test)]
1061mod tests {
1062 use super::*;
1064 use crate::tests::{TestDiskContents, TestFatDisk};
1065 use assert_matches::assert_matches;
1066 use futures::TryStreamExt;
1067 use scopeguard::defer;
1068 use vfs::directory::dirents_sink::Sealed;
1069 use vfs::node::Node as _;
1070 use vfs::ObjectRequest;
1071
1072 const TEST_DISK_SIZE: u64 = 2048 << 10; #[fuchsia::test(allow_stalls = false)]
1075 async fn test_link_fails() {
1076 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1077 let structure = TestDiskContents::dir().add_child("test_file", "test file contents".into());
1078 structure.create(&disk.root_dir());
1079
1080 let fs = disk.into_fatfs();
1081 let dir = fs.get_fatfs_root();
1082 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
1083 defer! { dir.close_ref(&fs.filesystem().lock()) }
1084 assert_eq!(
1085 dir.clone().link("test2".to_owned(), dir.clone(), "test3").await.unwrap_err(),
1086 Status::NOT_SUPPORTED
1087 );
1088 }
1089
1090 #[derive(Clone)]
1091 struct DummySink {
1092 max_size: usize,
1093 entries: Vec<(String, EntryInfo)>,
1094 sealed: bool,
1095 }
1096
1097 impl DummySink {
1098 pub fn new(max_size: usize) -> Self {
1099 DummySink { max_size, entries: Vec::with_capacity(max_size), sealed: false }
1100 }
1101
1102 fn from_sealed(sealed: Box<dyn dirents_sink::Sealed>) -> Box<DummySink> {
1103 sealed.into()
1104 }
1105 }
1106
1107 impl From<Box<dyn dirents_sink::Sealed>> for Box<DummySink> {
1108 fn from(sealed: Box<dyn dirents_sink::Sealed>) -> Self {
1109 sealed.open().downcast::<DummySink>().unwrap()
1110 }
1111 }
1112
1113 impl Sink for DummySink {
1114 fn append(mut self: Box<Self>, entry: &EntryInfo, name: &str) -> AppendResult {
1115 assert!(!self.sealed);
1116 if self.entries.len() == self.max_size {
1117 AppendResult::Sealed(self.seal())
1118 } else {
1119 self.entries.push((name.to_owned(), entry.clone()));
1120 AppendResult::Ok(self)
1121 }
1122 }
1123
1124 fn seal(mut self: Box<Self>) -> Box<dyn Sealed> {
1125 self.sealed = true;
1126 self
1127 }
1128 }
1129
1130 impl Sealed for DummySink {
1131 fn open(self: Box<Self>) -> Box<dyn std::any::Any> {
1132 self
1133 }
1134 }
1135
1136 #[fuchsia::test]
1137 fn test_read_dirents_small_sink() {
1139 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1140 let structure = TestDiskContents::dir()
1141 .add_child("test_file", "test file contents".into())
1142 .add_child("aaa", "this file is first".into())
1143 .add_child("qwerty", "hello".into())
1144 .add_child("directory", TestDiskContents::dir().add_child("a", "test".into()));
1145 structure.create(&disk.root_dir());
1146
1147 let fs = disk.into_fatfs();
1148 let dir = fs.get_fatfs_root();
1149
1150 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
1151 defer! { dir.close_ref(&fs.filesystem().lock()) }
1152
1153 let (pos, sealed) = futures::executor::block_on(
1154 dir.clone().read_dirents(&TraversalPosition::Start, Box::new(DummySink::new(4))),
1155 )
1156 .expect("read_dirents failed");
1157 assert_eq!(
1158 DummySink::from_sealed(sealed).entries,
1159 vec![
1160 (".".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)),
1161 ("aaa".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1162 (
1163 "directory".to_owned(),
1164 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
1165 ),
1166 ("qwerty".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1167 ]
1168 );
1169
1170 let (_, sealed) =
1172 futures::executor::block_on(dir.read_dirents(&pos, Box::new(DummySink::new(4))))
1173 .expect("read_dirents failed");
1174 assert_eq!(
1175 DummySink::from_sealed(sealed).entries,
1176 vec![("test_file".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),]
1177 );
1178 }
1179
1180 #[fuchsia::test]
1181 fn test_read_dirents_big_sink() {
1183 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1184 let structure = TestDiskContents::dir()
1185 .add_child("test_file", "test file contents".into())
1186 .add_child("aaa", "this file is first".into())
1187 .add_child("qwerty", "hello".into())
1188 .add_child("directory", TestDiskContents::dir().add_child("a", "test".into()));
1189 structure.create(&disk.root_dir());
1190
1191 let fs = disk.into_fatfs();
1192 let dir = fs.get_fatfs_root();
1193
1194 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
1195 defer! { dir.close_ref(&fs.filesystem().lock()) }
1196
1197 let (_, sealed) = futures::executor::block_on(
1198 dir.read_dirents(&TraversalPosition::Start, Box::new(DummySink::new(30))),
1199 )
1200 .expect("read_dirents failed");
1201 assert_eq!(
1202 DummySink::from_sealed(sealed).entries,
1203 vec![
1204 (".".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)),
1205 ("aaa".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1206 (
1207 "directory".to_owned(),
1208 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
1209 ),
1210 ("qwerty".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1211 ("test_file".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1212 ]
1213 );
1214 }
1215
1216 #[fuchsia::test]
1217 fn test_read_dirents_with_entry_that_sorts_before_dot() {
1218 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1219 let structure = TestDiskContents::dir().add_child("!", "!".into());
1220 structure.create(&disk.root_dir());
1221
1222 let fs = disk.into_fatfs();
1223 let dir = fs.get_fatfs_root();
1224
1225 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
1226 defer! { dir.close_ref(&fs.filesystem().lock()) }
1227
1228 let (pos, sealed) = futures::executor::block_on(
1229 dir.clone().read_dirents(&TraversalPosition::Start, Box::new(DummySink::new(1))),
1230 )
1231 .expect("read_dirents failed");
1232 assert_eq!(
1233 DummySink::from_sealed(sealed).entries,
1234 vec![("!".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File))]
1235 );
1236
1237 let (_, sealed) =
1238 futures::executor::block_on(dir.read_dirents(&pos, Box::new(DummySink::new(1))))
1239 .expect("read_dirents failed");
1240 assert_eq!(
1241 DummySink::from_sealed(sealed).entries,
1242 vec![(".".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)),]
1243 );
1244 }
1245
1246 #[fuchsia::test(allow_stalls = false)]
1247 async fn test_reopen_root() {
1248 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1249 let structure = TestDiskContents::dir().add_child("test", "Hello".into());
1250 structure.create(&disk.root_dir());
1251
1252 let fs = disk.into_fatfs();
1253 let dir = fs.get_root().expect("get_root OK");
1254
1255 let scope = ExecutionScope::new();
1256 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::NodeMarker>();
1257 dir.clone().open(scope.clone(), fio::OpenFlags::RIGHT_READABLE, Path::dot(), server_end);
1258 let scope_clone = scope.clone();
1259
1260 proxy
1261 .close()
1262 .await
1263 .expect("Send request OK")
1264 .map_err(Status::from_raw)
1265 .expect("First close OK");
1266 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::NodeMarker>();
1267 dir.clone().open(
1268 scope_clone,
1269 fio::OpenFlags::RIGHT_READABLE,
1270 Path::validate_and_split("test").unwrap(),
1271 server_end,
1272 );
1273 proxy
1274 .close()
1275 .await
1276 .expect("Send request OK")
1277 .map_err(Status::from_raw)
1278 .expect("Second close OK");
1279 dir.close();
1280 }
1281
1282 #[fuchsia::test(allow_stalls = false)]
1283 async fn test_reopen3_root() {
1284 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1285 let structure = TestDiskContents::dir().add_child("test", "Hello".into());
1286 structure.create(&disk.root_dir());
1287
1288 let fs = disk.into_fatfs();
1289 let root = fs.get_root().expect("get_root failed");
1290
1291 let scope = ExecutionScope::new();
1292
1293 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::NodeMarker>();
1295 let flags = fio::Flags::PERM_READ;
1296 ObjectRequest::new(flags, &fio::Options::default(), server_end.into())
1297 .handle(|request| root.clone().open3(scope.clone(), Path::dot(), flags, request));
1298 proxy
1299 .close()
1300 .await
1301 .expect("FIDL call failed")
1302 .map_err(Status::from_raw)
1303 .expect("First close failed");
1304
1305 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::NodeMarker>();
1307 ObjectRequest::new(flags, &fio::Options::default(), server_end.into()).handle(|request| {
1308 root.clone().open3(
1309 scope.clone(),
1310 Path::validate_and_split("test").unwrap(),
1311 flags,
1312 request,
1313 )
1314 });
1315 proxy
1316 .close()
1317 .await
1318 .expect("FIDL call failed")
1319 .map_err(Status::from_raw)
1320 .expect("Second close failed");
1321
1322 root.close();
1323 }
1324
1325 #[fuchsia::test(allow_stalls = false)]
1326 async fn test_open3_already_exists() {
1327 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1328 let structure = TestDiskContents::dir().add_child("test", "Hello".into());
1329 structure.create(&disk.root_dir());
1330
1331 let fs = disk.into_fatfs();
1332 let root = fs.get_root().expect("get_root failed");
1333
1334 let scope = ExecutionScope::new();
1335 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::NodeMarker>();
1336 let flags = fio::Flags::PERM_READ
1337 | fio::Flags::FLAG_MUST_CREATE
1338 | fio::Flags::FLAG_SEND_REPRESENTATION;
1339 ObjectRequest::new(flags, &fio::Options::default(), server_end.into()).handle(|request| {
1340 root.clone().open3(
1341 scope.clone(),
1342 Path::validate_and_split("test").unwrap(),
1343 flags,
1344 request,
1345 )
1346 });
1347
1348 let event =
1349 proxy.take_event_stream().try_next().await.expect_err("open2 passed unexpectedly");
1350
1351 assert_matches!(
1352 event,
1353 fidl::Error::ClientChannelClosed { status: Status::ALREADY_EXISTS, .. }
1354 );
1355
1356 root.close();
1357 }
1358
1359 #[fuchsia::test(allow_stalls = false)]
1360 async fn test_update_attributes_directory() {
1361 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1362 let structure = TestDiskContents::dir().add_child("test", "Hello".into());
1363 structure.create(&disk.root_dir());
1364
1365 let fs = disk.into_fatfs();
1366 let root = fs.get_root().expect("get_root failed");
1367
1368 let scope = ExecutionScope::new();
1369 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
1370 let flags = fio::Flags::PERM_READ | fio::Flags::PERM_SET_ATTRIBUTES;
1371 ObjectRequest::new(flags, &fio::Options::default(), server_end.into()).handle(|request| {
1372 root.clone().open3(
1373 scope.clone(),
1374 Path::validate_and_split(".").unwrap(),
1375 flags,
1376 request,
1377 )
1378 });
1379
1380 let mut new_attrs = fio::MutableNodeAttributes {
1381 creation_time: Some(
1382 std::time::SystemTime::now()
1383 .duration_since(std::time::SystemTime::UNIX_EPOCH)
1384 .expect("SystemTime before UNIX EPOCH")
1385 .as_nanos()
1386 .try_into()
1387 .unwrap(),
1388 ),
1389 ..Default::default()
1390 };
1391 proxy
1392 .update_attributes(&new_attrs)
1393 .await
1394 .expect("FIDL call failed")
1395 .map_err(Status::from_raw)
1396 .expect("update attributes failed");
1397
1398 new_attrs.mode = Some(123);
1399 let status = proxy
1400 .update_attributes(&new_attrs)
1401 .await
1402 .expect("FIDL call failed")
1403 .map_err(Status::from_raw)
1404 .expect_err("update unsupported attributes passed unexpectedly");
1405 assert_eq!(status, Status::NOT_SUPPORTED);
1406 root.close();
1407 }
1408
1409 #[fuchsia::test(allow_stalls = false)]
1410 async fn test_update_attributes_file() {
1411 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1412 let structure = TestDiskContents::dir().add_child("test_file", "Hello".into());
1413 structure.create(&disk.root_dir());
1414
1415 let fs = disk.into_fatfs();
1416 let root = fs.get_root().expect("get_root failed");
1417
1418 let scope = ExecutionScope::new();
1419 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::FileMarker>();
1420 let flags =
1421 fio::Flags::PERM_READ | fio::Flags::PERM_SET_ATTRIBUTES | fio::Flags::PROTOCOL_FILE;
1422 ObjectRequest::new(flags, &fio::Options::default(), server_end.into()).handle(|request| {
1423 root.clone().open3(
1424 scope.clone(),
1425 Path::validate_and_split("test_file").unwrap(),
1426 flags,
1427 request,
1428 )
1429 });
1430
1431 let mut new_attrs = fio::MutableNodeAttributes {
1432 creation_time: Some(
1433 std::time::SystemTime::now()
1434 .duration_since(std::time::SystemTime::UNIX_EPOCH)
1435 .expect("SystemTime before UNIX EPOCH")
1436 .as_nanos()
1437 .try_into()
1438 .unwrap(),
1439 ),
1440 ..Default::default()
1441 };
1442 proxy
1443 .update_attributes(&new_attrs)
1444 .await
1445 .expect("FIDL call failed")
1446 .map_err(Status::from_raw)
1447 .expect("update attributes failed");
1448
1449 new_attrs.mode = Some(123);
1450 let status = proxy
1451 .update_attributes(&new_attrs)
1452 .await
1453 .expect("FIDL call failed")
1454 .map_err(Status::from_raw)
1455 .expect_err("update unsupported attributes passed unexpectedly");
1456 assert_eq!(status, Status::NOT_SUPPORTED);
1457 root.close();
1458 }
1459}