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};
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::Watchers;
30use vfs::directory::watchers::event_producers::{SingleNameEventProducer, StaticVecEventProducer};
31use vfs::execution_scope::ExecutionScope;
32use vfs::file::FidlIoConnection;
33use vfs::path::Path;
34use vfs::{ObjectRequestRef, ProtocolsExt as _, ToObjectRequest, attributes};
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() { Err(Status::BAD_HANDLE) } else { Ok(Guard::new(fs, dir)) }
186 }
187
188 pub(crate) fn borrow_dir_mut<'a>(
190 &'a self,
191 fs: &'a FatFilesystemInner,
192 ) -> Option<GuardMut<'a, FatfsDirRef>> {
193 let dir = self.dir.borrow_mut();
194 if dir.get(fs).is_none() { None } else { Some(GuardMut::new(fs, dir)) }
195 }
196
197 pub(crate) fn find_child<'a>(
199 &'a self,
200 fs: &'a FatFilesystemInner,
201 name: &str,
202 ) -> Result<Option<DirEntry<'a>>, Status> {
203 if self.data.read().deleted {
204 return Ok(None);
205 }
206 let dir = self.borrow_dir(fs)?;
207 for entry in dir.iter().into_iter() {
208 let entry = entry.map_err(fatfs_error_to_status)?;
209 if entry.eq_name(name) {
210 return Ok(Some(entry));
211 }
212 }
213 Ok(None)
214 }
215
216 pub fn remove_child(&self, fs: &FatFilesystemInner, name: &str) -> Option<FatNode> {
220 let node = self.cache_remove(fs, name);
221 if let Some(node) = node {
222 node.detach(fs);
223 Some(node)
224 } else {
225 None
226 }
227 }
228
229 pub fn add_child(
233 self: &Arc<Self>,
234 fs: &FatFilesystemInner,
235 name: String,
236 child: FatNode,
237 ) -> Result<(), Status> {
238 child.attach(self.clone(), &name, fs)?;
239 let mut data = self.data.write();
242 if let Some(node) = data.children.insert(InsensitiveString(name), child.downgrade()) {
244 assert!(node.upgrade().is_none(), "conflicting cache entries with the same name")
245 }
246 Ok(())
247 }
248
249 pub(crate) fn cache_remove(&self, _fs: &FatFilesystemInner, name: &str) -> Option<FatNode> {
252 let mut data = self.data.write();
253 data.children.remove(&name as &dyn InsensitiveStringRef).and_then(|entry| entry.upgrade())
254 }
255
256 pub fn cache_get(&self, name: &str) -> Option<FatNode> {
258 let data = self.data.read();
261 data.children.get(&name as &dyn InsensitiveStringRef).and_then(|entry| entry.upgrade())
262 }
263
264 fn lookup(
265 self: &Arc<Self>,
266 flags: fio::OpenFlags,
267 mut path: Path,
268 closer: &mut Closer<'_>,
269 ) -> Result<FatNode, Status> {
270 let mut cur_entry = FatNode::Dir(self.clone());
271
272 while !path.is_empty() {
273 let child_flags =
274 if path.is_single_component() { flags } else { fio::OpenFlags::DIRECTORY };
275
276 match cur_entry {
277 FatNode::Dir(entry) => {
278 let name = path.next().unwrap();
279 validate_filename(name).map_err(fatfs_error_to_status)?;
280 cur_entry = entry.clone().open_child(name, child_flags, closer)?;
281 }
282 FatNode::File(_) => {
283 return Err(Status::NOT_DIR);
284 }
285 };
286 }
287
288 Ok(cur_entry)
289 }
290
291 fn lookup_with_open3_flags(
292 self: &Arc<Self>,
293 flags: fio::Flags,
294 mut path: Path,
295 closer: &mut Closer<'_>,
296 ) -> Result<FatNode, Status> {
297 let mut current_entry = FatNode::Dir(self.clone());
298
299 while !path.is_empty() {
300 let child_flags =
301 if path.is_single_component() { flags } else { fio::Flags::PROTOCOL_DIRECTORY };
302
303 match current_entry {
304 FatNode::Dir(entry) => {
305 let name = path.next().unwrap();
306 validate_filename(name).map_err(fatfs_error_to_status)?;
307 current_entry = entry.clone().open3_child(name, child_flags, closer)?;
308 }
309 FatNode::File(_) => {
310 return Err(Status::NOT_DIR);
311 }
312 };
313 }
314
315 Ok(current_entry)
316 }
317
318 pub(crate) fn open_child(
325 self: &Arc<Self>,
326 name: &str,
327 flags: fio::OpenFlags,
328 closer: &mut Closer<'_>,
329 ) -> Result<FatNode, Status> {
330 let fs_lock = self.filesystem.lock();
331 if let Some(entry) = self.cache_get(name) {
333 check_open_flags_for_existing_entry(flags)?;
334 entry.open_ref(&fs_lock)?;
335 return Ok(closer.add(entry));
336 };
337
338 let mut created = false;
339 let node = {
340 let entry = self.find_child(&fs_lock, name)?;
342 if let Some(entry) = entry {
343 check_open_flags_for_existing_entry(flags)?;
344 if entry.is_dir() {
345 self.add_directory(entry.to_dir(), name, closer)
346 } else {
347 self.add_file(entry.to_file(), name, closer)
348 }
349 } else if flags.intersects(fio::OpenFlags::CREATE) {
350 created = true;
352 let dir = self.borrow_dir(&fs_lock)?;
353 if flags.intersects(fio::OpenFlags::DIRECTORY) {
354 let dir = dir.create_dir(name).map_err(fatfs_error_to_status)?;
355 self.add_directory(dir, name, closer)
356 } else {
357 let file = dir.create_file(name).map_err(fatfs_error_to_status)?;
358 self.add_file(file, name, closer)
359 }
360 } else {
361 return Err(Status::NOT_FOUND);
363 }
364 };
365
366 let mut data = self.data.write();
367 data.children.insert(InsensitiveString(name.to_owned()), node.downgrade());
368 if created {
369 data.watchers.send_event(&mut SingleNameEventProducer::added(name));
370 self.filesystem.mark_dirty();
371 }
372
373 Ok(node)
374 }
375
376 pub(crate) fn open3_child(
377 self: &Arc<Self>,
378 name: &str,
379 flags: fio::Flags,
380 closer: &mut Closer<'_>,
381 ) -> Result<FatNode, Status> {
382 if flags.create_unnamed_temporary_in_directory_path() {
383 return Err(Status::NOT_SUPPORTED);
384 }
385 let fs_lock = self.filesystem.lock();
386
387 if let Some(entry) = self.cache_get(name) {
389 if flags.creation_mode() == vfs::CreationMode::Always {
390 return Err(Status::ALREADY_EXISTS);
391 }
392 entry.open_ref(&fs_lock)?;
393 return Ok(closer.add(entry));
394 };
395
396 let mut created_entry = false;
397 let node = match self.find_child(&fs_lock, name)? {
398 Some(entry) => {
399 if flags.creation_mode() == vfs::CreationMode::Always {
400 return Err(Status::ALREADY_EXISTS);
401 }
402 if entry.is_dir() {
403 self.add_directory(entry.to_dir(), name, closer)
404 } else {
405 self.add_file(entry.to_file(), name, closer)
406 }
407 }
408 None => {
409 if flags.creation_mode() == vfs::CreationMode::Never {
410 return Err(Status::NOT_FOUND);
411 }
412 created_entry = true;
413 let dir = self.borrow_dir(&fs_lock)?;
414
415 if flags.intersects(fio::Flags::PROTOCOL_DIRECTORY) {
417 let dir = dir.create_dir(name).map_err(fatfs_error_to_status)?;
418 self.add_directory(dir, name, closer)
419 } else {
420 let file = dir.create_file(name).map_err(fatfs_error_to_status)?;
421 self.add_file(file, name, closer)
422 }
423 }
424 };
425
426 let mut data = self.data.write();
427 data.children.insert(InsensitiveString(name.to_owned()), node.downgrade());
428 if created_entry {
429 data.watchers.send_event(&mut SingleNameEventProducer::added(name));
430 self.filesystem.mark_dirty();
431 }
432
433 Ok(node)
434 }
435
436 pub(crate) fn is_deleted(&self) -> bool {
438 self.data.read().deleted
439 }
440
441 pub(crate) fn did_remove(&self, name: &str) {
443 self.data.write().watchers.send_event(&mut SingleNameEventProducer::removed(name));
444 }
445
446 pub(crate) fn did_add(&self, name: &str) {
448 self.data.write().watchers.send_event(&mut SingleNameEventProducer::added(name));
449 }
450
451 fn rename_internal(
455 &self,
456 filesystem: &FatFilesystemInner,
457 src_dir: &Arc<FatDirectory>,
458 src_name: &str,
459 dst_name: &str,
460 existing: ExistingRef<'_, '_>,
461 ) -> Result<(), Status> {
462 let src_fatfs_dir = src_dir.borrow_dir(&filesystem)?;
470 let dst_fatfs_dir = self.borrow_dir(&filesystem)?;
471
472 match existing {
473 ExistingRef::None => {
474 src_fatfs_dir
475 .rename(src_name, &dst_fatfs_dir, dst_name)
476 .map_err(fatfs_error_to_status)?;
477 }
478 ExistingRef::File(file) => {
479 src_fatfs_dir
480 .rename_over_file(src_name, &dst_fatfs_dir, dst_name, file)
481 .map_err(fatfs_error_to_status)?;
482 }
483 ExistingRef::Dir(dir) => {
484 src_fatfs_dir
485 .rename_over_dir(src_name, &dst_fatfs_dir, dst_name, dir)
486 .map_err(fatfs_error_to_status)?;
487 }
488 }
489
490 src_dir.did_remove(src_name);
491 self.did_add(dst_name);
492
493 src_dir.fs().mark_dirty();
494
495 Ok(())
498 }
499
500 fn rename_locked(
502 self: &Arc<Self>,
503 filesystem: &FatFilesystemInner,
504 src_dir: &Arc<FatDirectory>,
505 src_name: &str,
506 dst_name: &str,
507 src_is_dir: bool,
508 closer: &mut Closer<'_>,
509 ) -> Result<(), Status> {
510 if Arc::ptr_eq(&src_dir, self)
513 && (&src_name as &dyn InsensitiveStringRef) == (&dst_name as &dyn InsensitiveStringRef)
514 {
515 if src_name != dst_name {
516 return self.rename_internal(
518 &filesystem,
519 src_dir,
520 src_name,
521 dst_name,
522 ExistingRef::None,
523 );
524 }
525 return Ok(());
526 }
527
528 if let Some(src_node) = src_dir.cache_get(src_name) {
530 if let FatNode::Dir(dir) = &src_node {
531 if Arc::ptr_eq(&dir, self) {
532 return Err(Status::INVALID_ARGS);
533 }
534 let mut dest = self.clone();
536 loop {
537 let next_dir = if let Some(parent) = &dest.data.read().parent {
538 if Arc::ptr_eq(&dir, parent) {
539 return Err(Status::INVALID_ARGS);
540 }
541 parent.clone()
542 } else {
543 break;
544 };
545 dest = next_dir;
546 }
547 }
548 src_node.flush_dir_entry(filesystem)?;
549 }
550
551 let mut existing_node = self.cache_get(dst_name);
552 let remove_from_cache = existing_node.is_some();
553 let mut dir;
554 let mut file;
555 let mut borrowed_dir;
556 let mut borrowed_file;
557 let existing = match existing_node {
558 None => {
559 self.open_ref(filesystem)?;
560 closer.add(FatNode::Dir(self.clone()));
561 match self.find_child(filesystem, dst_name)? {
562 Some(ref dir_entry) => {
563 if dir_entry.is_dir() {
564 dir = Some(dir_entry.to_dir());
565 ExistingRef::Dir(dir.as_mut().unwrap())
566 } else {
567 file = Some(dir_entry.to_file());
568 ExistingRef::File(file.as_mut().unwrap())
569 }
570 }
571 None => ExistingRef::None,
572 }
573 }
574 Some(ref mut node) => {
575 node.open_ref(filesystem)?;
576 closer.add(node.clone());
577 match node {
578 FatNode::Dir(node_dir) => {
579 if Arc::ptr_eq(node_dir, src_dir) {
584 return Err(Status::INVALID_ARGS);
585 }
586 borrowed_dir = node_dir.borrow_dir_mut(filesystem).unwrap();
587 ExistingRef::Dir(&mut *borrowed_dir)
588 }
589 FatNode::File(node_file) => {
590 borrowed_file = node_file.borrow_file_mut(filesystem).unwrap();
591 ExistingRef::File(&mut *borrowed_file)
592 }
593 }
594 }
595 };
596
597 match existing {
598 ExistingRef::File(_) => {
599 if src_is_dir {
600 return Err(Status::NOT_DIR);
601 }
602 }
603 ExistingRef::Dir(_) => {
604 if !src_is_dir {
605 return Err(Status::NOT_FILE);
606 }
607 }
608 ExistingRef::None => {}
609 }
610
611 self.rename_internal(&filesystem, src_dir, src_name, dst_name, existing)?;
612
613 if remove_from_cache {
614 self.cache_remove(&filesystem, &dst_name).unwrap().did_delete();
615 }
616
617 if let Some(node) = src_dir.remove_child(&filesystem, &src_name) {
619 self.add_child(&filesystem, dst_name.to_owned(), node)
620 .unwrap_or_else(|e| panic!("Rename failed, but fatfs says it didn't? - {:?}", e));
621 }
622
623 Ok(())
624 }
625
626 fn add_directory(
628 self: &Arc<Self>,
629 dir: Dir<'_>,
630 name: &str,
631 closer: &mut Closer<'_>,
632 ) -> FatNode {
633 let dir_ref = unsafe { FatfsDirRef::from(dir) };
636 closer.add(FatNode::Dir(FatDirectory::new(
637 dir_ref,
638 Some(self.clone()),
639 self.filesystem.clone(),
640 name.to_owned(),
641 )))
642 }
643
644 fn add_file(self: &Arc<Self>, file: File<'_>, name: &str, closer: &mut Closer<'_>) -> FatNode {
646 let file_ref = unsafe { FatfsFileRef::from(file) };
649 closer.add(FatNode::File(FatFile::new(
650 file_ref,
651 self.clone(),
652 self.filesystem.clone(),
653 name.to_owned(),
654 )))
655 }
656}
657
658impl Node for FatDirectory {
659 fn detach(&self, fs: &FatFilesystemInner) {
662 self.dir.borrow_mut().take(fs);
664 }
665
666 fn attach(
669 &self,
670 new_parent: Arc<FatDirectory>,
671 name: &str,
672 fs: &FatFilesystemInner,
673 ) -> Result<(), Status> {
674 let mut data = self.data.write();
675 data.name = name.to_owned();
676
677 unsafe { self.dir.borrow_mut().maybe_reopen(fs, Some(&new_parent), name)? };
679
680 assert!(data.parent.replace(new_parent).is_some());
681 Ok(())
682 }
683
684 fn did_delete(&self) {
685 let mut data = self.data.write();
686 data.parent.take();
687 data.watchers.send_event(&mut SingleNameEventProducer::deleted());
688 data.deleted = true;
689 }
690
691 fn open_ref(&self, fs: &FatFilesystemInner) -> Result<(), Status> {
692 let data = self.data.read();
693 unsafe { self.dir.borrow_mut().open(&fs, data.parent.as_ref(), &data.name) }
694 }
695
696 fn shut_down(&self, fs: &FatFilesystemInner) -> Result<(), Status> {
697 self.dir.borrow_mut().take(fs);
698 let mut data = self.data.write();
699 for (_, child) in data.children.drain() {
700 if let Some(child) = child.upgrade() {
701 child.shut_down(fs)?;
702 }
703 }
704 Ok(())
705 }
706
707 fn flush_dir_entry(&self, fs: &FatFilesystemInner) -> Result<(), Status> {
708 if let Some(ref mut dir) = self.borrow_dir_mut(fs) {
709 dir.flush_dir_entry().map_err(fatfs_error_to_status)?;
710 }
711 Ok(())
712 }
713
714 fn close_ref(&self, fs: &FatFilesystemInner) {
715 self.dir.borrow_mut().close(fs);
716 }
717}
718
719impl Debug for FatDirectory {
720 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
721 f.debug_struct("FatDirectory").field("parent", &self.data.read().parent).finish()
722 }
723}
724
725impl MutableDirectory for FatDirectory {
726 async fn unlink(self: Arc<Self>, name: &str, must_be_directory: bool) -> Result<(), Status> {
727 let fs_lock = self.filesystem.lock();
728 let parent = self.borrow_dir(&fs_lock)?;
729 let mut existing_node = self.cache_get(name);
730 let mut done = false;
731 match existing_node {
732 Some(FatNode::File(ref mut file)) => {
733 if must_be_directory {
734 return Err(Status::NOT_DIR);
735 }
736 if let Some(mut file) = file.borrow_file_mut(&fs_lock) {
737 parent.unlink_file(&mut *file).map_err(fatfs_error_to_status)?;
738 done = true;
739 }
740 }
741 Some(FatNode::Dir(ref mut dir)) => {
742 if let Some(mut dir) = dir.borrow_dir_mut(&fs_lock) {
743 parent.unlink_dir(&mut *dir).map_err(fatfs_error_to_status)?;
744 done = true;
745 }
746 }
747 None => {
748 if must_be_directory {
749 let entry = self.find_child(&fs_lock, name)?;
750 if !entry.ok_or(Status::NOT_FOUND)?.is_dir() {
751 return Err(Status::NOT_DIR);
752 }
753 }
754 }
755 }
756 if !done {
757 parent.remove(name).map_err(fatfs_error_to_status)?;
758 }
759 if existing_node.is_some() {
760 self.cache_remove(&fs_lock, name);
761 }
762 match existing_node {
763 Some(FatNode::File(ref mut file)) => file.did_delete(),
764 Some(FatNode::Dir(ref mut dir)) => dir.did_delete(),
765 None => {}
766 }
767
768 self.filesystem.mark_dirty();
769 self.data.write().watchers.send_event(&mut SingleNameEventProducer::removed(name));
770 Ok(())
771 }
772
773 async fn update_attributes(
774 &self,
775 attributes: fio::MutableNodeAttributes,
776 ) -> Result<(), Status> {
777 const SUPPORTED_MUTABLE_ATTRIBUTES: fio::NodeAttributesQuery =
778 fio::NodeAttributesQuery::CREATION_TIME
779 .union(fio::NodeAttributesQuery::MODIFICATION_TIME);
780
781 if !SUPPORTED_MUTABLE_ATTRIBUTES
782 .contains(vfs::common::mutable_node_attributes_to_query(&attributes))
783 {
784 return Err(Status::NOT_SUPPORTED);
785 }
786
787 let fs_lock = self.filesystem.lock();
788 let mut dir = self.borrow_dir_mut(&fs_lock).ok_or(Status::BAD_HANDLE)?;
789 if let Some(creation_time) = attributes.creation_time {
790 dir.set_created(unix_to_dos_time(creation_time));
791 }
792 if let Some(modification_time) = attributes.modification_time {
793 dir.set_modified(unix_to_dos_time(modification_time));
794 }
795
796 self.filesystem.mark_dirty();
797 Ok(())
798 }
799
800 async fn sync(&self) -> Result<(), Status> {
801 Ok(())
803 }
804
805 fn rename(
806 self: Arc<Self>,
807 src_dir: Arc<dyn MutableDirectory>,
808 src_path: Path,
809 dst_path: Path,
810 ) -> BoxFuture<'static, Result<(), Status>> {
811 Box::pin(async move {
812 let src_dir =
813 src_dir.into_any().downcast::<FatDirectory>().map_err(|_| Status::INVALID_ARGS)?;
814 if self.is_deleted() {
815 return Err(Status::NOT_FOUND);
817 }
818
819 let src_name = src_path.peek().unwrap();
820 validate_filename(src_name).map_err(fatfs_error_to_status)?;
821 let dst_name = dst_path.peek().unwrap();
822 validate_filename(dst_name).map_err(fatfs_error_to_status)?;
823
824 let mut closer = Closer::new(&self.filesystem);
825 let filesystem = self.filesystem.lock();
826
827 let entry = src_dir.find_child(&filesystem, &src_name)?;
829 if entry.is_none() {
830 return Err(Status::NOT_FOUND);
833 }
834 let src_is_dir = entry.unwrap().is_dir();
835 if (dst_path.is_dir() || src_path.is_dir()) && !src_is_dir {
836 return Err(Status::NOT_DIR);
839 }
840
841 self.rename_locked(&filesystem, &src_dir, src_name, dst_name, src_is_dir, &mut closer)
842 })
843 }
844}
845
846impl DirectoryEntry for FatDirectory {
847 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
848 request.open_dir(self)
849 }
850}
851
852impl GetEntryInfo for FatDirectory {
853 fn entry_info(&self) -> EntryInfo {
854 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
855 }
856}
857
858impl vfs::node::Node for FatDirectory {
859 async fn get_attributes(
860 &self,
861 requested_attributes: fio::NodeAttributesQuery,
862 ) -> Result<fio::NodeAttributes2, Status> {
863 let fs_lock = self.filesystem.lock();
864 let dir = self.borrow_dir(&fs_lock)?;
865
866 let creation_time = dos_to_unix_time(dir.created());
867 let modification_time = dos_to_unix_time(dir.modified());
868 let access_time = dos_date_to_unix_time(dir.accessed());
869
870 Ok(attributes!(
871 requested_attributes,
872 Mutable {
873 creation_time: creation_time,
874 modification_time: modification_time,
875 access_time: access_time
876 },
877 Immutable {
878 protocols: fio::NodeProtocolKinds::DIRECTORY,
879 abilities: fio::Operations::GET_ATTRIBUTES
880 | fio::Operations::UPDATE_ATTRIBUTES
881 | fio::Operations::ENUMERATE
882 | fio::Operations::TRAVERSE
883 | fio::Operations::MODIFY_DIRECTORY,
884 link_count: 1, }
886 ))
887 }
888
889 fn close(self: Arc<Self>) {
890 self.close_ref(&self.filesystem.lock());
891 }
892
893 fn query_filesystem(&self) -> Result<fio::FilesystemInfo, Status> {
894 self.filesystem.query_filesystem()
895 }
896
897 fn will_clone(&self) {
898 self.open_ref(&self.filesystem.lock()).unwrap();
899 }
900}
901
902impl Directory for FatDirectory {
903 fn deprecated_open(
904 self: Arc<Self>,
905 scope: ExecutionScope,
906 flags: fio::OpenFlags,
907 path: Path,
908 server_end: ServerEnd<fio::NodeMarker>,
909 ) {
910 let mut closer = Closer::new(&self.filesystem);
911
912 flags.to_object_request(server_end).handle(|object_request| {
913 match self.lookup(flags, path, &mut closer)? {
914 FatNode::Dir(entry) => {
915 let () = entry
916 .open_ref(&self.filesystem.lock())
917 .expect("entry should already be open");
918 object_request
919 .take()
920 .create_connection_sync::<MutableConnection<_>, _>(scope, entry, flags);
921 Ok(())
922 }
923 FatNode::File(entry) => {
924 let () = entry.open_ref(&self.filesystem.lock())?;
925 object_request
926 .take()
927 .create_connection_sync::<FidlIoConnection<_>, _>(scope, entry, flags);
928 Ok(())
929 }
930 }
931 });
932 }
933
934 fn open(
935 self: Arc<Self>,
936 scope: ExecutionScope,
937 path: Path,
938 flags: fio::Flags,
939 object_request: ObjectRequestRef<'_>,
940 ) -> Result<(), Status> {
941 let mut closer = Closer::new(&self.filesystem);
942
943 match self.lookup_with_open3_flags(flags, path, &mut closer)? {
944 FatNode::Dir(entry) => {
945 let () = entry.open_ref(&self.filesystem.lock())?;
946 object_request
947 .take()
948 .create_connection_sync::<MutableConnection<_>, _>(scope, entry, flags);
949 Ok(())
950 }
951 FatNode::File(entry) => {
952 let () = entry.open_ref(&self.filesystem.lock())?;
953 object_request
954 .take()
955 .create_connection_sync::<FidlIoConnection<_>, _>(scope, entry, flags);
956 Ok(())
957 }
958 }
959 }
960
961 async fn read_dirents(
962 &self,
963 pos: &TraversalPosition,
964 sink: Box<dyn dirents_sink::Sink>,
965 ) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), Status> {
966 if self.is_deleted() {
967 return Ok((TraversalPosition::End, sink.seal()));
968 }
969
970 let fs_lock = self.filesystem.lock();
971 let dir = self.borrow_dir(&fs_lock)?;
972
973 if let TraversalPosition::End = pos {
974 return Ok((TraversalPosition::End, sink.seal()));
975 }
976
977 let filter = |name: &str| match pos {
978 TraversalPosition::Start => true,
979 TraversalPosition::Name(next_name) => name >= next_name.as_str(),
980 _ => false,
981 };
982
983 let mut entries: Vec<_> = dir
985 .iter()
986 .filter_map(|maybe_entry| {
987 maybe_entry
988 .map(|entry| {
989 let name = entry.file_name();
990 if &name == ".." || !filter(&name) {
991 None
992 } else {
993 let entry_type = if entry.is_dir() {
994 fio::DirentType::Directory
995 } else {
996 fio::DirentType::File
997 };
998 Some((name, EntryInfo::new(fio::INO_UNKNOWN, entry_type)))
999 }
1000 })
1001 .transpose()
1002 })
1003 .collect::<std::io::Result<Vec<_>>>()
1004 .map_err(fatfs_error_to_status)?;
1005
1006 if self.data.read().parent.is_none() && filter(".") {
1008 entries.push((
1009 ".".to_owned(),
1010 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory),
1011 ));
1012 }
1013
1014 entries.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
1016
1017 let mut cur_sink = sink;
1019 for (name, info) in entries.into_iter() {
1020 let result = cur_sink.append(&info, &name.clone());
1021
1022 match result {
1023 AppendResult::Ok(new_sink) => cur_sink = new_sink,
1024 AppendResult::Sealed(sealed) => {
1025 return Ok((TraversalPosition::Name(name), sealed));
1026 }
1027 }
1028 }
1029
1030 return Ok((TraversalPosition::End, cur_sink.seal()));
1031 }
1032
1033 fn register_watcher(
1034 self: Arc<Self>,
1035 scope: ExecutionScope,
1036 mask: fio::WatchMask,
1037 watcher: DirectoryWatcher,
1038 ) -> Result<(), Status> {
1039 let fs_lock = self.filesystem.lock();
1040 let mut data = self.data.write();
1041 let is_deleted = data.deleted;
1042 let is_root = data.parent.is_none();
1043 let controller = data.watchers.add(scope, self.clone(), mask, watcher);
1044 if mask.contains(fio::WatchMask::EXISTING) && !is_deleted {
1045 let entries = {
1046 let dir = self.borrow_dir(&fs_lock)?;
1047 let synthesized_dot = if is_root {
1048 Some(Ok(".".to_owned()))
1050 } else {
1051 None
1052 };
1053 synthesized_dot
1054 .into_iter()
1055 .chain(dir.iter().filter_map(|maybe_entry| {
1056 maybe_entry
1057 .map(|entry| {
1058 let name = entry.file_name();
1059 if &name == ".." { None } else { Some(name) }
1060 })
1061 .transpose()
1062 }))
1063 .collect::<std::io::Result<Vec<String>>>()
1064 .map_err(fatfs_error_to_status)?
1065 };
1066 controller.send_event(&mut StaticVecEventProducer::existing(entries));
1067 }
1068 controller.send_event(&mut SingleNameEventProducer::idle());
1069 Ok(())
1070 }
1071
1072 fn unregister_watcher(self: Arc<Self>, key: usize) {
1073 self.data.write().watchers.remove(key);
1074 }
1075}
1076
1077#[cfg(test)]
1078mod tests {
1079 use super::*;
1081 use crate::tests::{TestDiskContents, TestFatDisk};
1082 use assert_matches::assert_matches;
1083 use futures::TryStreamExt;
1084 use scopeguard::defer;
1085 use vfs::ObjectRequest;
1086 use vfs::directory::dirents_sink::{Sealed, Sink};
1087 use vfs::node::Node as _;
1088
1089 const TEST_DISK_SIZE: u64 = 2048 << 10; #[fuchsia::test(allow_stalls = false)]
1092 async fn test_link_fails() {
1093 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1094 let structure = TestDiskContents::dir().add_child("test_file", "test file contents".into());
1095 structure.create(&disk.root_dir());
1096
1097 let fs = disk.into_fatfs();
1098 let dir = fs.get_fatfs_root();
1099 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
1100 defer! { dir.close_ref(&fs.filesystem().lock()) }
1101 assert_eq!(
1102 dir.clone().link("test2".to_owned(), dir.clone(), "test3").await.unwrap_err(),
1103 Status::NOT_SUPPORTED
1104 );
1105 }
1106
1107 #[derive(Clone)]
1108 struct DummySink {
1109 max_size: usize,
1110 entries: Vec<(String, EntryInfo)>,
1111 sealed: bool,
1112 }
1113
1114 impl DummySink {
1115 pub fn new(max_size: usize) -> Self {
1116 DummySink { max_size, entries: Vec::with_capacity(max_size), sealed: false }
1117 }
1118
1119 fn from_sealed(sealed: Box<dyn dirents_sink::Sealed>) -> Box<DummySink> {
1120 sealed.into()
1121 }
1122 }
1123
1124 impl From<Box<dyn dirents_sink::Sealed>> for Box<DummySink> {
1125 fn from(sealed: Box<dyn dirents_sink::Sealed>) -> Self {
1126 sealed.open().downcast::<DummySink>().unwrap()
1127 }
1128 }
1129
1130 impl Sink for DummySink {
1131 fn append(mut self: Box<Self>, entry: &EntryInfo, name: &str) -> AppendResult {
1132 assert!(!self.sealed);
1133 if self.entries.len() == self.max_size {
1134 AppendResult::Sealed(self.seal())
1135 } else {
1136 self.entries.push((name.to_owned(), entry.clone()));
1137 AppendResult::Ok(self)
1138 }
1139 }
1140
1141 fn seal(mut self: Box<Self>) -> Box<dyn Sealed> {
1142 self.sealed = true;
1143 self
1144 }
1145 }
1146
1147 impl Sealed for DummySink {
1148 fn open(self: Box<Self>) -> Box<dyn std::any::Any> {
1149 self
1150 }
1151 }
1152
1153 #[fuchsia::test]
1154 fn test_read_dirents_small_sink() {
1156 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1157 let structure = TestDiskContents::dir()
1158 .add_child("test_file", "test file contents".into())
1159 .add_child("aaa", "this file is first".into())
1160 .add_child("qwerty", "hello".into())
1161 .add_child("directory", TestDiskContents::dir().add_child("a", "test".into()));
1162 structure.create(&disk.root_dir());
1163
1164 let fs = disk.into_fatfs();
1165 let dir = fs.get_fatfs_root();
1166
1167 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
1168 defer! { dir.close_ref(&fs.filesystem().lock()) }
1169
1170 let (pos, sealed) = futures::executor::block_on(
1171 dir.clone().read_dirents(&TraversalPosition::Start, Box::new(DummySink::new(4))),
1172 )
1173 .expect("read_dirents failed");
1174 assert_eq!(
1175 DummySink::from_sealed(sealed).entries,
1176 vec![
1177 (".".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)),
1178 ("aaa".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1179 (
1180 "directory".to_owned(),
1181 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
1182 ),
1183 ("qwerty".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1184 ]
1185 );
1186
1187 let (_, sealed) =
1189 futures::executor::block_on(dir.read_dirents(&pos, Box::new(DummySink::new(4))))
1190 .expect("read_dirents failed");
1191 assert_eq!(
1192 DummySink::from_sealed(sealed).entries,
1193 vec![("test_file".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),]
1194 );
1195 }
1196
1197 #[fuchsia::test]
1198 fn test_read_dirents_big_sink() {
1200 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1201 let structure = TestDiskContents::dir()
1202 .add_child("test_file", "test file contents".into())
1203 .add_child("aaa", "this file is first".into())
1204 .add_child("qwerty", "hello".into())
1205 .add_child("directory", TestDiskContents::dir().add_child("a", "test".into()));
1206 structure.create(&disk.root_dir());
1207
1208 let fs = disk.into_fatfs();
1209 let dir = fs.get_fatfs_root();
1210
1211 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
1212 defer! { dir.close_ref(&fs.filesystem().lock()) }
1213
1214 let (_, sealed) = futures::executor::block_on(
1215 dir.read_dirents(&TraversalPosition::Start, Box::new(DummySink::new(30))),
1216 )
1217 .expect("read_dirents failed");
1218 assert_eq!(
1219 DummySink::from_sealed(sealed).entries,
1220 vec![
1221 (".".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)),
1222 ("aaa".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1223 (
1224 "directory".to_owned(),
1225 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
1226 ),
1227 ("qwerty".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1228 ("test_file".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1229 ]
1230 );
1231 }
1232
1233 #[fuchsia::test]
1234 fn test_read_dirents_with_entry_that_sorts_before_dot() {
1235 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1236 let structure = TestDiskContents::dir().add_child("!", "!".into());
1237 structure.create(&disk.root_dir());
1238
1239 let fs = disk.into_fatfs();
1240 let dir = fs.get_fatfs_root();
1241
1242 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
1243 defer! { dir.close_ref(&fs.filesystem().lock()) }
1244
1245 let (pos, sealed) = futures::executor::block_on(
1246 dir.clone().read_dirents(&TraversalPosition::Start, Box::new(DummySink::new(1))),
1247 )
1248 .expect("read_dirents failed");
1249 assert_eq!(
1250 DummySink::from_sealed(sealed).entries,
1251 vec![("!".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File))]
1252 );
1253
1254 let (_, sealed) =
1255 futures::executor::block_on(dir.read_dirents(&pos, Box::new(DummySink::new(1))))
1256 .expect("read_dirents failed");
1257 assert_eq!(
1258 DummySink::from_sealed(sealed).entries,
1259 vec![(".".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)),]
1260 );
1261 }
1262
1263 #[fuchsia::test(allow_stalls = false)]
1264 async fn test_deprecated_reopen_root() {
1265 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1266 let structure = TestDiskContents::dir().add_child("test", "Hello".into());
1267 structure.create(&disk.root_dir());
1268
1269 let fs = disk.into_fatfs();
1270 let dir = fs.get_root().expect("get_root OK");
1271
1272 let proxy = vfs::directory::serve_read_only(dir.clone(), ExecutionScope::new());
1273 proxy
1274 .close()
1275 .await
1276 .expect("Send request OK")
1277 .map_err(Status::from_raw)
1278 .expect("First close OK");
1279
1280 let proxy = vfs::directory::serve_read_only(dir.clone(), ExecutionScope::new());
1281 proxy
1282 .close()
1283 .await
1284 .expect("Send request OK")
1285 .map_err(Status::from_raw)
1286 .expect("Second close OK");
1287 dir.close();
1288 }
1289
1290 #[fuchsia::test(allow_stalls = false)]
1291 async fn test_reopen_root() {
1292 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1293 let structure = TestDiskContents::dir().add_child("test", "Hello".into());
1294 structure.create(&disk.root_dir());
1295
1296 let fs = disk.into_fatfs();
1297 let root = fs.get_root().expect("get_root failed");
1298
1299 let proxy = vfs::directory::serve_read_only(root.clone(), ExecutionScope::new());
1301 proxy
1302 .close()
1303 .await
1304 .expect("FIDL call failed")
1305 .map_err(Status::from_raw)
1306 .expect("First close failed");
1307
1308 let proxy = vfs::serve_directory(
1310 root.clone(),
1311 Path::validate_and_split("test").unwrap(),
1312 ExecutionScope::new(),
1313 fio::PERM_READABLE,
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_open_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::PERM_READABLE
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().open(
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("open 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 let proxy = vfs::directory::serve(
1368 root.clone(),
1369 ExecutionScope::new(),
1370 fio::PERM_READABLE | fio::PERM_WRITABLE,
1371 );
1372
1373 let mut new_attrs = fio::MutableNodeAttributes {
1374 creation_time: Some(
1375 std::time::SystemTime::now()
1376 .duration_since(std::time::SystemTime::UNIX_EPOCH)
1377 .expect("SystemTime before UNIX EPOCH")
1378 .as_nanos()
1379 .try_into()
1380 .unwrap(),
1381 ),
1382 ..Default::default()
1383 };
1384 proxy
1385 .update_attributes(&new_attrs)
1386 .await
1387 .expect("FIDL call failed")
1388 .map_err(Status::from_raw)
1389 .expect("update attributes failed");
1390
1391 new_attrs.mode = Some(123);
1392 let status = proxy
1393 .update_attributes(&new_attrs)
1394 .await
1395 .expect("FIDL call failed")
1396 .map_err(Status::from_raw)
1397 .expect_err("update unsupported attributes passed unexpectedly");
1398 assert_eq!(status, Status::NOT_SUPPORTED);
1399 root.close();
1400 }
1401
1402 #[fuchsia::test(allow_stalls = false)]
1403 async fn test_update_attributes_file() {
1404 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1405 let structure = TestDiskContents::dir().add_child("test_file", "Hello".into());
1406 structure.create(&disk.root_dir());
1407
1408 let fs = disk.into_fatfs();
1409 let root = fs.get_root().expect("get_root failed");
1410 let proxy = vfs::serve_file(
1411 root.clone(),
1412 Path::validate_and_split("test_file").unwrap(),
1413 ExecutionScope::new(),
1414 fio::PERM_WRITABLE,
1415 );
1416
1417 let mut new_attrs = fio::MutableNodeAttributes {
1418 creation_time: Some(
1419 std::time::SystemTime::now()
1420 .duration_since(std::time::SystemTime::UNIX_EPOCH)
1421 .expect("SystemTime before UNIX EPOCH")
1422 .as_nanos()
1423 .try_into()
1424 .unwrap(),
1425 ),
1426 ..Default::default()
1427 };
1428 proxy
1429 .update_attributes(&new_attrs)
1430 .await
1431 .expect("FIDL call failed")
1432 .map_err(Status::from_raw)
1433 .expect("update attributes failed");
1434
1435 new_attrs.mode = Some(123);
1436 let status = proxy
1437 .update_attributes(&new_attrs)
1438 .await
1439 .expect("FIDL call failed")
1440 .map_err(Status::from_raw)
1441 .expect_err("update unsupported attributes passed unexpectedly");
1442 assert_eq!(status, Status::NOT_SUPPORTED);
1443 root.close();
1444 }
1445}