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::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?;
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)?;
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)?;
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(ref mut 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(ref mut 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<'a>(
962 &'a self,
963 pos: &'a TraversalPosition,
964 sink: Box<dyn 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
1005 if self.data.read().parent.is_none() && filter(".") {
1007 entries.push((
1008 ".".to_owned(),
1009 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory),
1010 ));
1011 }
1012
1013 entries.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
1015
1016 let mut cur_sink = sink;
1018 for (name, info) in entries.into_iter() {
1019 let result = cur_sink.append(&info, &name.clone());
1020
1021 match result {
1022 AppendResult::Ok(new_sink) => cur_sink = new_sink,
1023 AppendResult::Sealed(sealed) => {
1024 return Ok((TraversalPosition::Name(name), sealed));
1025 }
1026 }
1027 }
1028
1029 return Ok((TraversalPosition::End, cur_sink.seal()));
1030 }
1031
1032 fn register_watcher(
1033 self: Arc<Self>,
1034 scope: ExecutionScope,
1035 mask: fio::WatchMask,
1036 watcher: DirectoryWatcher,
1037 ) -> Result<(), Status> {
1038 let fs_lock = self.filesystem.lock();
1039 let mut data = self.data.write();
1040 let is_deleted = data.deleted;
1041 let is_root = data.parent.is_none();
1042 let controller = data.watchers.add(scope, self.clone(), mask, watcher);
1043 if mask.contains(fio::WatchMask::EXISTING) && !is_deleted {
1044 let entries = {
1045 let dir = self.borrow_dir(&fs_lock)?;
1046 let synthesized_dot = if is_root {
1047 Some(Ok(".".to_owned()))
1049 } else {
1050 None
1051 };
1052 synthesized_dot
1053 .into_iter()
1054 .chain(dir.iter().filter_map(|maybe_entry| {
1055 maybe_entry
1056 .map(|entry| {
1057 let name = entry.file_name();
1058 if &name == ".." { None } else { Some(name) }
1059 })
1060 .transpose()
1061 }))
1062 .collect::<std::io::Result<Vec<String>>>()
1063 .map_err(fatfs_error_to_status)?
1064 };
1065 controller.send_event(&mut StaticVecEventProducer::existing(entries));
1066 }
1067 controller.send_event(&mut SingleNameEventProducer::idle());
1068 Ok(())
1069 }
1070
1071 fn unregister_watcher(self: Arc<Self>, key: usize) {
1072 self.data.write().watchers.remove(key);
1073 }
1074}
1075
1076#[cfg(test)]
1077mod tests {
1078 use super::*;
1080 use crate::tests::{TestDiskContents, TestFatDisk};
1081 use assert_matches::assert_matches;
1082 use futures::TryStreamExt;
1083 use scopeguard::defer;
1084 use vfs::ObjectRequest;
1085 use vfs::directory::dirents_sink::Sealed;
1086 use vfs::node::Node as _;
1087
1088 const TEST_DISK_SIZE: u64 = 2048 << 10; #[fuchsia::test(allow_stalls = false)]
1091 async fn test_link_fails() {
1092 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1093 let structure = TestDiskContents::dir().add_child("test_file", "test file contents".into());
1094 structure.create(&disk.root_dir());
1095
1096 let fs = disk.into_fatfs();
1097 let dir = fs.get_fatfs_root();
1098 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
1099 defer! { dir.close_ref(&fs.filesystem().lock()) }
1100 assert_eq!(
1101 dir.clone().link("test2".to_owned(), dir.clone(), "test3").await.unwrap_err(),
1102 Status::NOT_SUPPORTED
1103 );
1104 }
1105
1106 #[derive(Clone)]
1107 struct DummySink {
1108 max_size: usize,
1109 entries: Vec<(String, EntryInfo)>,
1110 sealed: bool,
1111 }
1112
1113 impl DummySink {
1114 pub fn new(max_size: usize) -> Self {
1115 DummySink { max_size, entries: Vec::with_capacity(max_size), sealed: false }
1116 }
1117
1118 fn from_sealed(sealed: Box<dyn dirents_sink::Sealed>) -> Box<DummySink> {
1119 sealed.into()
1120 }
1121 }
1122
1123 impl From<Box<dyn dirents_sink::Sealed>> for Box<DummySink> {
1124 fn from(sealed: Box<dyn dirents_sink::Sealed>) -> Self {
1125 sealed.open().downcast::<DummySink>().unwrap()
1126 }
1127 }
1128
1129 impl Sink for DummySink {
1130 fn append(mut self: Box<Self>, entry: &EntryInfo, name: &str) -> AppendResult {
1131 assert!(!self.sealed);
1132 if self.entries.len() == self.max_size {
1133 AppendResult::Sealed(self.seal())
1134 } else {
1135 self.entries.push((name.to_owned(), entry.clone()));
1136 AppendResult::Ok(self)
1137 }
1138 }
1139
1140 fn seal(mut self: Box<Self>) -> Box<dyn Sealed> {
1141 self.sealed = true;
1142 self
1143 }
1144 }
1145
1146 impl Sealed for DummySink {
1147 fn open(self: Box<Self>) -> Box<dyn std::any::Any> {
1148 self
1149 }
1150 }
1151
1152 #[fuchsia::test]
1153 fn test_read_dirents_small_sink() {
1155 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1156 let structure = TestDiskContents::dir()
1157 .add_child("test_file", "test file contents".into())
1158 .add_child("aaa", "this file is first".into())
1159 .add_child("qwerty", "hello".into())
1160 .add_child("directory", TestDiskContents::dir().add_child("a", "test".into()));
1161 structure.create(&disk.root_dir());
1162
1163 let fs = disk.into_fatfs();
1164 let dir = fs.get_fatfs_root();
1165
1166 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
1167 defer! { dir.close_ref(&fs.filesystem().lock()) }
1168
1169 let (pos, sealed) = futures::executor::block_on(
1170 dir.clone().read_dirents(&TraversalPosition::Start, Box::new(DummySink::new(4))),
1171 )
1172 .expect("read_dirents failed");
1173 assert_eq!(
1174 DummySink::from_sealed(sealed).entries,
1175 vec![
1176 (".".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)),
1177 ("aaa".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1178 (
1179 "directory".to_owned(),
1180 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
1181 ),
1182 ("qwerty".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1183 ]
1184 );
1185
1186 let (_, sealed) =
1188 futures::executor::block_on(dir.read_dirents(&pos, Box::new(DummySink::new(4))))
1189 .expect("read_dirents failed");
1190 assert_eq!(
1191 DummySink::from_sealed(sealed).entries,
1192 vec![("test_file".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),]
1193 );
1194 }
1195
1196 #[fuchsia::test]
1197 fn test_read_dirents_big_sink() {
1199 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1200 let structure = TestDiskContents::dir()
1201 .add_child("test_file", "test file contents".into())
1202 .add_child("aaa", "this file is first".into())
1203 .add_child("qwerty", "hello".into())
1204 .add_child("directory", TestDiskContents::dir().add_child("a", "test".into()));
1205 structure.create(&disk.root_dir());
1206
1207 let fs = disk.into_fatfs();
1208 let dir = fs.get_fatfs_root();
1209
1210 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
1211 defer! { dir.close_ref(&fs.filesystem().lock()) }
1212
1213 let (_, sealed) = futures::executor::block_on(
1214 dir.read_dirents(&TraversalPosition::Start, Box::new(DummySink::new(30))),
1215 )
1216 .expect("read_dirents failed");
1217 assert_eq!(
1218 DummySink::from_sealed(sealed).entries,
1219 vec![
1220 (".".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)),
1221 ("aaa".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1222 (
1223 "directory".to_owned(),
1224 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
1225 ),
1226 ("qwerty".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1227 ("test_file".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)),
1228 ]
1229 );
1230 }
1231
1232 #[fuchsia::test]
1233 fn test_read_dirents_with_entry_that_sorts_before_dot() {
1234 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1235 let structure = TestDiskContents::dir().add_child("!", "!".into());
1236 structure.create(&disk.root_dir());
1237
1238 let fs = disk.into_fatfs();
1239 let dir = fs.get_fatfs_root();
1240
1241 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
1242 defer! { dir.close_ref(&fs.filesystem().lock()) }
1243
1244 let (pos, sealed) = futures::executor::block_on(
1245 dir.clone().read_dirents(&TraversalPosition::Start, Box::new(DummySink::new(1))),
1246 )
1247 .expect("read_dirents failed");
1248 assert_eq!(
1249 DummySink::from_sealed(sealed).entries,
1250 vec![("!".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File))]
1251 );
1252
1253 let (_, sealed) =
1254 futures::executor::block_on(dir.read_dirents(&pos, Box::new(DummySink::new(1))))
1255 .expect("read_dirents failed");
1256 assert_eq!(
1257 DummySink::from_sealed(sealed).entries,
1258 vec![(".".to_owned(), EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)),]
1259 );
1260 }
1261
1262 #[fuchsia::test(allow_stalls = false)]
1263 async fn test_deprecated_reopen_root() {
1264 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1265 let structure = TestDiskContents::dir().add_child("test", "Hello".into());
1266 structure.create(&disk.root_dir());
1267
1268 let fs = disk.into_fatfs();
1269 let dir = fs.get_root().expect("get_root OK");
1270
1271 let proxy = vfs::directory::serve_read_only(dir.clone());
1272 proxy
1273 .close()
1274 .await
1275 .expect("Send request OK")
1276 .map_err(Status::from_raw)
1277 .expect("First close OK");
1278
1279 let proxy = vfs::directory::serve_read_only(dir.clone());
1280 proxy
1281 .close()
1282 .await
1283 .expect("Send request OK")
1284 .map_err(Status::from_raw)
1285 .expect("Second close OK");
1286 dir.close();
1287 }
1288
1289 #[fuchsia::test(allow_stalls = false)]
1290 async fn test_reopen_root() {
1291 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1292 let structure = TestDiskContents::dir().add_child("test", "Hello".into());
1293 structure.create(&disk.root_dir());
1294
1295 let fs = disk.into_fatfs();
1296 let root = fs.get_root().expect("get_root failed");
1297
1298 let proxy = vfs::directory::serve_read_only(root.clone());
1300 proxy
1301 .close()
1302 .await
1303 .expect("FIDL call failed")
1304 .map_err(Status::from_raw)
1305 .expect("First close failed");
1306
1307 let proxy = vfs::serve_directory(
1309 root.clone(),
1310 Path::validate_and_split("test").unwrap(),
1311 fio::PERM_READABLE,
1312 );
1313 proxy
1314 .close()
1315 .await
1316 .expect("FIDL call failed")
1317 .map_err(Status::from_raw)
1318 .expect("Second close failed");
1319
1320 root.close();
1321 }
1322
1323 #[fuchsia::test(allow_stalls = false)]
1324 async fn test_open_already_exists() {
1325 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1326 let structure = TestDiskContents::dir().add_child("test", "Hello".into());
1327 structure.create(&disk.root_dir());
1328
1329 let fs = disk.into_fatfs();
1330 let root = fs.get_root().expect("get_root failed");
1331
1332 let scope = ExecutionScope::new();
1333 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::NodeMarker>();
1334 let flags = fio::PERM_READABLE
1335 | fio::Flags::FLAG_MUST_CREATE
1336 | fio::Flags::FLAG_SEND_REPRESENTATION;
1337 ObjectRequest::new(flags, &fio::Options::default(), server_end.into()).handle(|request| {
1338 root.clone().open(
1339 scope.clone(),
1340 Path::validate_and_split("test").unwrap(),
1341 flags,
1342 request,
1343 )
1344 });
1345
1346 let event =
1347 proxy.take_event_stream().try_next().await.expect_err("open passed unexpectedly");
1348
1349 assert_matches!(
1350 event,
1351 fidl::Error::ClientChannelClosed { status: Status::ALREADY_EXISTS, .. }
1352 );
1353
1354 root.close();
1355 }
1356
1357 #[fuchsia::test(allow_stalls = false)]
1358 async fn test_update_attributes_directory() {
1359 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1360 let structure = TestDiskContents::dir().add_child("test", "Hello".into());
1361 structure.create(&disk.root_dir());
1362
1363 let fs = disk.into_fatfs();
1364 let root = fs.get_root().expect("get_root failed");
1365 let proxy = vfs::directory::serve(root.clone(), fio::PERM_READABLE | fio::PERM_WRITABLE);
1366
1367 let mut new_attrs = fio::MutableNodeAttributes {
1368 creation_time: Some(
1369 std::time::SystemTime::now()
1370 .duration_since(std::time::SystemTime::UNIX_EPOCH)
1371 .expect("SystemTime before UNIX EPOCH")
1372 .as_nanos()
1373 .try_into()
1374 .unwrap(),
1375 ),
1376 ..Default::default()
1377 };
1378 proxy
1379 .update_attributes(&new_attrs)
1380 .await
1381 .expect("FIDL call failed")
1382 .map_err(Status::from_raw)
1383 .expect("update attributes failed");
1384
1385 new_attrs.mode = Some(123);
1386 let status = proxy
1387 .update_attributes(&new_attrs)
1388 .await
1389 .expect("FIDL call failed")
1390 .map_err(Status::from_raw)
1391 .expect_err("update unsupported attributes passed unexpectedly");
1392 assert_eq!(status, Status::NOT_SUPPORTED);
1393 root.close();
1394 }
1395
1396 #[fuchsia::test(allow_stalls = false)]
1397 async fn test_update_attributes_file() {
1398 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
1399 let structure = TestDiskContents::dir().add_child("test_file", "Hello".into());
1400 structure.create(&disk.root_dir());
1401
1402 let fs = disk.into_fatfs();
1403 let root = fs.get_root().expect("get_root failed");
1404 let proxy = vfs::serve_file(
1405 root.clone(),
1406 Path::validate_and_split("test_file").unwrap(),
1407 fio::PERM_WRITABLE,
1408 );
1409
1410 let mut new_attrs = fio::MutableNodeAttributes {
1411 creation_time: Some(
1412 std::time::SystemTime::now()
1413 .duration_since(std::time::SystemTime::UNIX_EPOCH)
1414 .expect("SystemTime before UNIX EPOCH")
1415 .as_nanos()
1416 .try_into()
1417 .unwrap(),
1418 ),
1419 ..Default::default()
1420 };
1421 proxy
1422 .update_attributes(&new_attrs)
1423 .await
1424 .expect("FIDL call failed")
1425 .map_err(Status::from_raw)
1426 .expect("update attributes failed");
1427
1428 new_attrs.mode = Some(123);
1429 let status = proxy
1430 .update_attributes(&new_attrs)
1431 .await
1432 .expect("FIDL call failed")
1433 .map_err(Status::from_raw)
1434 .expect_err("update unsupported attributes passed unexpectedly");
1435 assert_eq!(status, Status::NOT_SUPPORTED);
1436 root.close();
1437 }
1438}