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