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