1use crate::file::ErofsFile;
6use crate::volume::ErofsVolume;
7use erofs::{DirectoryNode, FileType, Node};
8use fidl_fuchsia_io as fio;
9use fuchsia_sync::Mutex;
10use std::sync::Arc;
11use vfs::directory::dirents_sink::{self, AppendResult};
12use vfs::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
13use vfs::directory::entry_container::{Directory, DirectoryWatcher};
14use vfs::directory::immutable::connection::ImmutableConnection;
15use vfs::directory::traversal_position::TraversalPosition;
16use vfs::directory::watchers::Watchers;
17use vfs::directory::watchers::event_producers::{SingleNameEventProducer, StaticVecEventProducer};
18use vfs::execution_scope::ExecutionScope;
19use vfs::path::Path;
20use vfs::{CreationMode, ObjectRequestRef, ProtocolsExt};
21
22pub struct ErofsDirectory {
24 volume: Arc<ErofsVolume>,
25 node: DirectoryNode,
26 watchers: Mutex<Watchers>,
27}
28
29fn check_open_flags(flags: fio::Flags, exists: bool) -> Result<(), zx::Status> {
30 match flags.creation_mode() {
31 CreationMode::Never => Ok(()),
32 CreationMode::Always => {
33 if exists {
34 Err(zx::Status::ALREADY_EXISTS)
35 } else {
36 Err(zx::Status::NOT_SUPPORTED)
37 }
38 }
39 CreationMode::AllowExisting => {
40 if exists {
41 Ok(())
42 } else {
43 Err(zx::Status::NOT_SUPPORTED)
44 }
45 }
46 CreationMode::UnnamedTemporary | CreationMode::UnlinkableUnnamedTemporary => {
47 Err(zx::Status::NOT_SUPPORTED)
48 }
49 }
50}
51
52impl ErofsDirectory {
53 pub fn new(volume: Arc<ErofsVolume>, node: DirectoryNode) -> Self {
54 Self { volume, node, watchers: Mutex::new(Watchers::new()) }
55 }
56}
57
58impl DirectoryEntry for ErofsDirectory {
59 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), zx::Status> {
60 request.open_dir(self)
61 }
62}
63
64impl GetEntryInfo for ErofsDirectory {
65 fn entry_info(&self) -> EntryInfo {
66 EntryInfo::new(self.node.ino() as u64, fio::DirentType::Directory)
67 }
68}
69
70impl vfs::node::Node for ErofsDirectory {
71 async fn get_attributes(
72 &self,
73 requested_attributes: fio::NodeAttributesQuery,
74 ) -> Result<fio::NodeAttributes2, zx::Status> {
75 Ok(vfs::immutable_attributes!(
76 requested_attributes,
77 Immutable {
78 protocols: fio::NodeProtocolKinds::DIRECTORY,
79 abilities: fio::Operations::GET_ATTRIBUTES
80 | fio::Operations::ENUMERATE
81 | fio::Operations::TRAVERSE,
82 id: self.node.ino() as u64,
83 }
84 ))
85 }
86}
87
88impl Directory for ErofsDirectory {
89 fn open(
90 self: Arc<Self>,
91 scope: ExecutionScope,
92 mut path: Path,
93 flags: fio::Flags,
94 object_request: ObjectRequestRef<'_>,
95 ) -> Result<(), zx::Status> {
96 let (name, _) = match path.next_with_ref() {
97 (path_ref, Some(name)) => (name, path_ref),
98 (_, None) => {
99 check_open_flags(flags, true)?;
101 object_request
102 .take()
103 .create_connection_sync::<ImmutableConnection<_>, _>(scope, self, flags);
104 return Ok(());
105 }
106 };
107
108 let child_node_opt = self.volume.fs().lookup(&self.node, name).map_err(|e| {
110 log::error!("Lookup failed for '{}': {:?}", name, e);
111 e.to_status()
112 })?;
113
114 let child_node = match child_node_opt {
115 Some(node) => node,
116 None => {
117 if path.is_empty() {
118 check_open_flags(flags, false)?;
119 }
120 return Err(zx::Status::NOT_FOUND);
121 }
122 };
123
124 if path.is_empty() {
125 check_open_flags(flags, true)?;
126 }
127
128 match child_node {
130 Node::Directory(dir_node) => {
131 let child_dir = Arc::new(ErofsDirectory::new(self.volume.clone(), dir_node));
132 child_dir.open(scope, path, flags, object_request)
133 }
134 Node::File(file_node) => {
135 if !path.is_empty() {
136 return Err(zx::Status::NOT_DIR);
137 }
138 let child_file = ErofsFile::new(self.volume.clone(), file_node)?;
139 vfs::file::serve(child_file, scope, &flags, object_request)
140 }
141 }
142 }
143
144 async fn read_dirents(
145 &self,
146 pos: &TraversalPosition,
147 sink: Box<dyn dirents_sink::Sink>,
148 ) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), zx::Status> {
149 let mut entry_offset = match pos {
150 TraversalPosition::Start => 0,
151 TraversalPosition::Index(index) => *index,
152 TraversalPosition::End => return Ok((TraversalPosition::End, sink.seal())),
153 _ => return Err(zx::Status::NOT_SUPPORTED),
154 };
155
156 let mut sink = sink;
157 let mut buffer = vec![erofs::DirectoryEntry::default(); 16];
158
159 loop {
160 let filled = self
161 .volume
162 .fs()
163 .read_directory(&self.node, entry_offset as usize, &mut buffer)
164 .map_err(|e| {
165 log::error!("Read directory failed at offset {}: {:?}", entry_offset, e);
166 e.to_status()
167 })?;
168
169 for i in 0..filled {
170 let entry = &buffer[i];
171 if entry.name == ".." {
172 entry_offset += 1;
173 continue;
174 }
175 let dirent_type = match entry.file_type {
176 FileType::RegFile => fio::DirentType::File,
177 FileType::Dir => fio::DirentType::Directory,
178 FileType::Symlink => fio::DirentType::Symlink,
179 _ => fio::DirentType::Unknown,
180 };
181
182 let child = self.volume.fs().node(entry.nid).map_err(|e| {
184 log::error!("Failed to lookup child node {} for ino: {:?}", entry.nid, e);
185 e.to_status()
186 })?;
187 let ino = child.ino();
188
189 let entry_info = EntryInfo::new(ino as u64, dirent_type);
190 match sink.append(&entry_info, &entry.name) {
191 AppendResult::Ok(new_sink) => {
192 sink = new_sink;
193 entry_offset += 1;
194 }
195 AppendResult::Sealed(sealed) => {
196 return Ok((TraversalPosition::Index(entry_offset), sealed));
197 }
198 }
199 }
200
201 if filled < buffer.len() {
202 break;
203 }
204 }
205
206 Ok((TraversalPosition::End, sink.seal()))
207 }
208
209 fn register_watcher(
210 self: Arc<Self>,
211 scope: ExecutionScope,
212 mask: fio::WatchMask,
213 watcher: DirectoryWatcher,
214 ) -> Result<(), zx::Status> {
215 let mut entry_names = vec![];
216 let mut entry_offset = 0;
217 let mut buffer = vec![erofs::DirectoryEntry::default(); 16];
218 loop {
219 let filled =
220 self.volume.fs().read_directory(&self.node, entry_offset, &mut buffer).map_err(
221 |e| {
222 log::error!(
223 "Watcher read directory failed at offset {}: {:?}",
224 entry_offset,
225 e
226 );
227 e.to_status()
228 },
229 )?;
230 if filled == 0 {
231 break;
232 }
233 for i in 0..filled {
234 let name = &buffer[i].name;
235 if name != ".." {
236 entry_names.push(name.clone());
237 }
238 }
239 entry_offset += filled;
240 }
241
242 let mut names = StaticVecEventProducer::existing(entry_names);
243
244 let mut watchers = self.watchers.lock();
245 let controller = watchers.add(scope, self.clone(), mask, watcher);
246 controller.send_event(&mut names);
247 controller.send_event(&mut SingleNameEventProducer::idle());
248
249 Ok(())
250 }
251
252 fn unregister_watcher(self: Arc<Self>, key: usize) {
253 self.watchers.lock().remove(key);
254 }
255}