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.nid(), 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 let mtime = self.node.mtime_ns();
76 Ok(vfs::attributes!(
77 requested_attributes,
78 Mutable {
79 mode: self.node.mode() as u32,
80 uid: self.node.uid(),
81 gid: self.node.gid(),
82 creation_time: mtime,
83 modification_time: mtime,
84 access_time: mtime,
85 },
86 Immutable {
87 protocols: fio::NodeProtocolKinds::DIRECTORY,
88 abilities: fio::Operations::GET_ATTRIBUTES
89 | fio::Operations::ENUMERATE
90 | fio::Operations::TRAVERSE,
91 id: self.node.nid(),
92 link_count: self.node.link_count() as u64,
93 change_time: mtime,
94 }
95 ))
96 }
97
98 fn query_filesystem(&self) -> Result<fio::FilesystemInfo, zx::Status> {
99 self.volume.query_filesystem()
100 }
101
102 async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, zx::Status> {
103 self.volume.fs().list_xattrs(&self.node).map_err(|e| e.to_status())
104 }
105
106 async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, zx::Status> {
107 self.volume
108 .fs()
109 .get_xattr(&self.node, &name)
110 .map_err(|e| e.to_status())?
111 .ok_or(zx::Status::NOT_FOUND)
112 }
113}
114
115impl Directory for ErofsDirectory {
116 fn open(
117 self: Arc<Self>,
118 scope: ExecutionScope,
119 mut path: Path,
120 flags: fio::Flags,
121 object_request: ObjectRequestRef<'_>,
122 ) -> Result<(), zx::Status> {
123 let (name, _) = match path.next_with_ref() {
124 (path_ref, Some(name)) => (name, path_ref),
125 (_, None) => {
126 check_open_flags(flags, true)?;
128 object_request
129 .take()
130 .create_connection_sync::<ImmutableConnection<_>, _>(scope, self, flags);
131 return Ok(());
132 }
133 };
134
135 let child_node_opt = self.volume.fs().lookup(&self.node, name).map_err(|e| {
137 log::error!("Lookup failed for '{}': {:?}", name, e);
138 e.to_status()
139 })?;
140
141 let child_node = match child_node_opt {
142 Some(node) => node,
143 None => {
144 if path.is_empty() {
145 check_open_flags(flags, false)?;
146 }
147 return Err(zx::Status::NOT_FOUND);
148 }
149 };
150
151 if path.is_empty() {
152 check_open_flags(flags, true)?;
153 }
154
155 match child_node {
157 Node::Directory(dir_node) => {
158 let child_dir = Arc::new(ErofsDirectory::new(self.volume.clone(), dir_node));
159 child_dir.open(scope, path, flags, object_request)
160 }
161 Node::File(file_node) => {
162 if !path.is_empty() {
163 return Err(zx::Status::NOT_DIR);
164 }
165 let child_file = ErofsFile::new(self.volume.clone(), file_node)?;
166 vfs::file::serve(child_file, scope, &flags, object_request)
167 }
168 }
169 }
170
171 async fn read_dirents(
172 &self,
173 pos: &TraversalPosition,
174 sink: Box<dyn dirents_sink::Sink>,
175 ) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), zx::Status> {
176 let mut entry_offset = match pos {
177 TraversalPosition::Start => 0,
178 TraversalPosition::Index(index) => *index,
179 TraversalPosition::End => return Ok((TraversalPosition::End, sink.seal())),
180 _ => return Err(zx::Status::NOT_SUPPORTED),
181 };
182
183 let mut sink = sink;
184 let mut buffer = vec![erofs::DirectoryEntry::default(); 16];
185
186 loop {
187 let filled = self
188 .volume
189 .fs()
190 .read_directory(&self.node, entry_offset as usize, &mut buffer)
191 .map_err(|e| {
192 log::error!("Read directory failed at offset {}: {:?}", entry_offset, e);
193 e.to_status()
194 })?;
195
196 for i in 0..filled {
197 let entry = &buffer[i];
198 if entry.name == ".." {
199 entry_offset += 1;
200 continue;
201 }
202 let dirent_type = match entry.file_type {
203 FileType::RegFile => fio::DirentType::File,
204 FileType::Dir => fio::DirentType::Directory,
205 FileType::Symlink => fio::DirentType::Symlink,
206 _ => fio::DirentType::Unknown,
207 };
208
209 let entry_info = EntryInfo::new(entry.nid, dirent_type);
210 match sink.append(&entry_info, &entry.name) {
211 AppendResult::Ok(new_sink) => {
212 sink = new_sink;
213 entry_offset += 1;
214 }
215 AppendResult::Sealed(sealed) => {
216 return Ok((TraversalPosition::Index(entry_offset), sealed));
217 }
218 }
219 }
220
221 if filled < buffer.len() {
222 break;
223 }
224 }
225
226 Ok((TraversalPosition::End, sink.seal()))
227 }
228
229 fn register_watcher(
230 self: Arc<Self>,
231 scope: ExecutionScope,
232 mask: fio::WatchMask,
233 watcher: DirectoryWatcher,
234 ) -> Result<(), zx::Status> {
235 let mut entry_names = vec![];
236 let mut entry_offset = 0;
237 let mut buffer = vec![erofs::DirectoryEntry::default(); 16];
238 loop {
239 let filled =
240 self.volume.fs().read_directory(&self.node, entry_offset, &mut buffer).map_err(
241 |e| {
242 log::error!(
243 "Watcher read directory failed at offset {}: {:?}",
244 entry_offset,
245 e
246 );
247 e.to_status()
248 },
249 )?;
250 if filled == 0 {
251 break;
252 }
253 for i in 0..filled {
254 let name = &buffer[i].name;
255 if name != ".." {
256 entry_names.push(name.clone());
257 }
258 }
259 entry_offset += filled;
260 }
261
262 let mut names = StaticVecEventProducer::existing(entry_names);
263
264 let mut watchers = self.watchers.lock();
265 let controller = watchers.add(scope, self.clone(), mask, watcher);
266 controller.send_event(&mut names);
267 controller.send_event(&mut SingleNameEventProducer::idle());
268
269 Ok(())
270 }
271
272 fn unregister_watcher(self: Arc<Self>, key: usize) {
273 self.watchers.lock().remove(key);
274 }
275}