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