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