1use crate::common::io1_to_io2_attrs;
8use crate::directory::connection::{BaseConnection, ConnectionState};
9use crate::directory::entry_container::MutableDirectory;
10use crate::execution_scope::ExecutionScope;
11use crate::name::validate_name;
12use crate::node::OpenNode;
13use crate::object_request::ConnectionCreator;
14use crate::path::Path;
15use crate::request_handler::{RequestHandler, RequestListener};
16use crate::token_registry::{TokenInterface, TokenRegistry, Tokenizable};
17use crate::{ObjectRequestRef, ProtocolsExt};
18
19use anyhow::Error;
20use fidl::Handle;
21use fidl_fuchsia_io as fio;
22use std::ops::ControlFlow;
23use std::pin::Pin;
24use std::sync::Arc;
25use storage_trace::{self as trace, TraceFutureExt};
26use zx_status::Status;
27
28pub struct MutableConnection<DirectoryType: MutableDirectory> {
29 base: BaseConnection<DirectoryType>,
30}
31
32impl<DirectoryType: MutableDirectory> MutableConnection<DirectoryType> {
33 pub async fn create(
39 scope: ExecutionScope,
40 directory: Arc<DirectoryType>,
41 protocols: impl ProtocolsExt,
42 object_request: ObjectRequestRef<'_>,
43 ) -> Result<(), Status> {
44 let directory = OpenNode::new(directory);
46
47 let connection = MutableConnection {
48 base: BaseConnection::new(scope.clone(), directory, protocols.to_directory_options()?),
49 };
50
51 if let Ok(requests) = object_request.take().into_request_stream(&connection.base).await {
52 scope.spawn(RequestListener::new(requests, Tokenizable::new(connection)));
53 }
54 Ok(())
55 }
56
57 async fn handle_request(
58 this: Pin<&mut Tokenizable<Self>>,
59 request: fio::DirectoryRequest,
60 ) -> Result<ConnectionState, Error> {
61 match request {
62 fio::DirectoryRequest::Unlink { name, options, responder } => {
63 let result = this.handle_unlink(name, options).await;
64 responder.send(result.map_err(Status::into_raw))?;
65 }
66 fio::DirectoryRequest::GetToken { responder } => {
67 let (status, token) = match Self::handle_get_token(this.into_ref()) {
68 Ok(token) => (Status::OK, Some(token)),
69 Err(status) => (status, None),
70 };
71 responder.send(status.into_raw(), token)?;
72 }
73 fio::DirectoryRequest::Rename { src, dst_parent_token, dst, responder } => {
74 let result = this.handle_rename(src, Handle::from(dst_parent_token), dst).await;
75 responder.send(result.map_err(Status::into_raw))?;
76 }
77 #[cfg(fuchsia_api_level_at_least = "28")]
78 fio::DirectoryRequest::DeprecatedSetAttr { flags, attributes, responder } => {
79 let status = match this
80 .handle_update_attributes(io1_to_io2_attrs(flags, attributes))
81 .await
82 {
83 Ok(()) => Status::OK,
84 Err(status) => status,
85 };
86 responder.send(status.into_raw())?;
87 }
88 #[cfg(not(fuchsia_api_level_at_least = "28"))]
89 fio::DirectoryRequest::SetAttr { flags, attributes, responder } => {
90 let status = match this
91 .handle_update_attributes(io1_to_io2_attrs(flags, attributes))
92 .await
93 {
94 Ok(()) => Status::OK,
95 Err(status) => status,
96 };
97 responder.send(status.into_raw())?;
98 }
99 fio::DirectoryRequest::Sync { responder } => {
100 responder.send(this.base.directory.sync().await.map_err(Status::into_raw))?;
101 }
102 fio::DirectoryRequest::CreateSymlink {
103 responder, name, target, connection, ..
104 } => {
105 if !this.base.options.rights.contains(fio::Operations::MODIFY_DIRECTORY) {
106 responder.send(Err(Status::ACCESS_DENIED.into_raw()))?;
107 } else if validate_name(&name).is_err() {
108 responder.send(Err(Status::INVALID_ARGS.into_raw()))?;
109 } else {
110 responder.send(
111 this.base
112 .directory
113 .create_symlink(name, target, connection)
114 .await
115 .map_err(Status::into_raw),
116 )?;
117 }
118 }
119 fio::DirectoryRequest::UpdateAttributes { payload, responder } => {
120 async move {
121 responder.send(
122 this.handle_update_attributes(payload).await.map_err(Status::into_raw),
123 )
124 }
125 .trace(trace::trace_future_args!(c"storage", c"Directory::UpdateAttributes"))
126 .await?;
127 }
128 request => {
129 return this.as_mut().base.handle_request(request).await;
130 }
131 }
132 Ok(ConnectionState::Alive)
133 }
134
135 async fn handle_update_attributes(
136 &self,
137 attributes: fio::MutableNodeAttributes,
138 ) -> Result<(), Status> {
139 if !self.base.options.rights.contains(fio::Operations::UPDATE_ATTRIBUTES) {
140 return Err(Status::BAD_HANDLE);
141 }
142 self.base.directory.update_attributes(attributes).await
145 }
146
147 async fn handle_unlink(&self, name: String, options: fio::UnlinkOptions) -> Result<(), Status> {
148 if !self.base.options.rights.contains(fio::Rights::MODIFY_DIRECTORY) {
149 return Err(Status::BAD_HANDLE);
150 }
151
152 if name.is_empty() || name.contains('/') || name == "." || name == ".." {
153 return Err(Status::INVALID_ARGS);
154 }
155
156 self.base
157 .directory
158 .clone()
159 .unlink(
160 &name,
161 options
162 .flags
163 .map(|f| f.contains(fio::UnlinkFlags::MUST_BE_DIRECTORY))
164 .unwrap_or(false),
165 )
166 .await
167 }
168
169 fn handle_get_token(this: Pin<&Tokenizable<Self>>) -> Result<Handle, Status> {
170 if !this.base.options.rights.contains(fio::Rights::MODIFY_DIRECTORY) {
173 return Err(Status::BAD_HANDLE);
174 }
175 Ok(TokenRegistry::get_token(this)?)
176 }
177
178 async fn handle_rename(
179 &self,
180 src: String,
181 dst_parent_token: Handle,
182 dst: String,
183 ) -> Result<(), Status> {
184 if !self.base.options.rights.contains(fio::Rights::MODIFY_DIRECTORY) {
185 return Err(Status::BAD_HANDLE);
186 }
187
188 let src = Path::validate_and_split(src)?;
189 let dst = Path::validate_and_split(dst)?;
190
191 if !src.is_single_component() || !dst.is_single_component() {
192 return Err(Status::INVALID_ARGS);
193 }
194
195 let dst_parent = match self.base.scope.token_registry().get_owner(dst_parent_token)? {
196 None => return Err(Status::NOT_FOUND),
197 Some(entry) => entry,
198 };
199
200 dst_parent.clone().rename(self.base.directory.clone(), src, dst).await
201 }
202}
203
204impl<DirectoryType: MutableDirectory> ConnectionCreator<DirectoryType>
205 for MutableConnection<DirectoryType>
206{
207 async fn create<'a>(
208 scope: ExecutionScope,
209 node: Arc<DirectoryType>,
210 protocols: impl ProtocolsExt,
211 object_request: ObjectRequestRef<'a>,
212 ) -> Result<(), Status> {
213 Self::create(scope, node, protocols, object_request).await
214 }
215}
216
217impl<DirectoryType: MutableDirectory> RequestHandler
218 for Tokenizable<MutableConnection<DirectoryType>>
219{
220 type Request = Result<fio::DirectoryRequest, fidl::Error>;
221
222 async fn handle_request(self: Pin<&mut Self>, request: Self::Request) -> ControlFlow<()> {
223 if let Some(_guard) = self.base.scope.try_active_guard() {
224 match request {
225 Ok(request) => {
226 match MutableConnection::<DirectoryType>::handle_request(self, request).await {
227 Ok(ConnectionState::Alive) => ControlFlow::Continue(()),
228 Ok(ConnectionState::Closed) | Err(_) => ControlFlow::Break(()),
229 }
230 }
231 Err(_) => ControlFlow::Break(()),
232 }
233 } else {
234 ControlFlow::Break(())
235 }
236 }
237}
238
239impl<DirectoryType: MutableDirectory> TokenInterface for MutableConnection<DirectoryType> {
240 fn get_node(&self) -> Arc<dyn MutableDirectory> {
241 self.base.directory.clone()
242 }
243
244 fn token_registry(&self) -> &TokenRegistry {
245 self.base.scope.token_registry()
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252 use crate::ToObjectRequest;
253 use crate::directory::dirents_sink;
254 use crate::directory::entry::{EntryInfo, GetEntryInfo};
255 use crate::directory::entry_container::{Directory, DirectoryWatcher};
256 use crate::directory::traversal_position::TraversalPosition;
257 use crate::node::Node;
258 use fuchsia_sync::Mutex;
259 use futures::future::BoxFuture;
260 use std::any::Any;
261 use std::future::ready;
262 use std::sync::Weak;
263
264 #[derive(Debug, PartialEq)]
265 enum MutableDirectoryAction {
266 Link { id: u32, path: String },
267 Unlink { id: u32, name: String },
268 Rename { id: u32, src_name: String, dst_dir: u32, dst_name: String },
269 UpdateAttributes { id: u32, attributes: fio::MutableNodeAttributes },
270 Sync,
271 Close,
272 }
273
274 #[derive(Debug)]
275 struct MockDirectory {
276 id: u32,
277 fs: Arc<MockFilesystem>,
278 }
279
280 impl MockDirectory {
281 pub fn new(id: u32, fs: Arc<MockFilesystem>) -> Arc<Self> {
282 Arc::new(MockDirectory { id, fs })
283 }
284 }
285
286 impl PartialEq for MockDirectory {
287 fn eq(&self, other: &Self) -> bool {
288 self.id == other.id
289 }
290 }
291
292 impl GetEntryInfo for MockDirectory {
293 fn entry_info(&self) -> EntryInfo {
294 EntryInfo::new(0, fio::DirentType::Directory)
295 }
296 }
297
298 impl Node for MockDirectory {
299 async fn get_attributes(
300 &self,
301 _query: fio::NodeAttributesQuery,
302 ) -> Result<fio::NodeAttributes2, Status> {
303 unimplemented!("Not implemented");
304 }
305
306 fn close(self: Arc<Self>) {
307 let _ = self.fs.handle_event(MutableDirectoryAction::Close);
308 }
309 }
310
311 impl Directory for MockDirectory {
312 fn open(
313 self: Arc<Self>,
314 _scope: ExecutionScope,
315 _path: Path,
316 _flags: fio::Flags,
317 _object_request: ObjectRequestRef<'_>,
318 ) -> Result<(), Status> {
319 unimplemented!("Not implemented!");
320 }
321
322 async fn read_dirents<'a>(
323 &'a self,
324 _pos: &'a TraversalPosition,
325 _sink: Box<dyn dirents_sink::Sink>,
326 ) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), Status> {
327 unimplemented!("Not implemented");
328 }
329
330 fn register_watcher(
331 self: Arc<Self>,
332 _scope: ExecutionScope,
333 _mask: fio::WatchMask,
334 _watcher: DirectoryWatcher,
335 ) -> Result<(), Status> {
336 unimplemented!("Not implemented");
337 }
338
339 fn unregister_watcher(self: Arc<Self>, _key: usize) {
340 unimplemented!("Not implemented");
341 }
342 }
343
344 impl MutableDirectory for MockDirectory {
345 fn link<'a>(
346 self: Arc<Self>,
347 path: String,
348 _source_dir: Arc<dyn Any + Send + Sync>,
349 _source_name: &'a str,
350 ) -> BoxFuture<'a, Result<(), Status>> {
351 let result = self.fs.handle_event(MutableDirectoryAction::Link { id: self.id, path });
352 Box::pin(ready(result))
353 }
354
355 async fn unlink(
356 self: Arc<Self>,
357 name: &str,
358 _must_be_directory: bool,
359 ) -> Result<(), Status> {
360 self.fs.handle_event(MutableDirectoryAction::Unlink {
361 id: self.id,
362 name: name.to_string(),
363 })
364 }
365
366 async fn update_attributes(
367 &self,
368 attributes: fio::MutableNodeAttributes,
369 ) -> Result<(), Status> {
370 self.fs
371 .handle_event(MutableDirectoryAction::UpdateAttributes { id: self.id, attributes })
372 }
373
374 async fn sync(&self) -> Result<(), Status> {
375 self.fs.handle_event(MutableDirectoryAction::Sync)
376 }
377
378 fn rename(
379 self: Arc<Self>,
380 src_dir: Arc<dyn MutableDirectory>,
381 src_name: Path,
382 dst_name: Path,
383 ) -> BoxFuture<'static, Result<(), Status>> {
384 let src_dir = src_dir.into_any().downcast::<MockDirectory>().unwrap();
385 let result = self.fs.handle_event(MutableDirectoryAction::Rename {
386 id: src_dir.id,
387 src_name: src_name.into_string(),
388 dst_dir: self.id,
389 dst_name: dst_name.into_string(),
390 });
391 Box::pin(ready(result))
392 }
393 }
394
395 struct Events(Mutex<Vec<MutableDirectoryAction>>);
396
397 impl Events {
398 fn new() -> Arc<Self> {
399 Arc::new(Events(Mutex::new(vec![])))
400 }
401 }
402
403 struct MockFilesystem {
404 cur_id: Mutex<u32>,
405 scope: ExecutionScope,
406 events: Weak<Events>,
407 }
408
409 impl MockFilesystem {
410 pub fn new(events: &Arc<Events>) -> Self {
411 let scope = ExecutionScope::new();
412 MockFilesystem { cur_id: Mutex::new(0), scope, events: Arc::downgrade(events) }
413 }
414
415 pub fn handle_event(&self, event: MutableDirectoryAction) -> Result<(), Status> {
416 self.events.upgrade().map(|x| x.0.lock().push(event));
417 Ok(())
418 }
419
420 pub fn make_connection(
421 self: &Arc<Self>,
422 flags: fio::Flags,
423 ) -> (Arc<MockDirectory>, fio::DirectoryProxy) {
424 let mut cur_id = self.cur_id.lock();
425 let dir = MockDirectory::new(*cur_id, self.clone());
426 *cur_id += 1;
427 let (proxy, server_end) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
428 flags.to_object_request(server_end).create_connection_sync::<MutableConnection<_>, _>(
429 self.scope.clone(),
430 dir.clone(),
431 flags,
432 );
433 (dir, proxy)
434 }
435 }
436
437 impl std::fmt::Debug for MockFilesystem {
438 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
439 f.debug_struct("MockFilesystem").field("cur_id", &self.cur_id).finish()
440 }
441 }
442
443 #[fuchsia::test]
444 async fn test_rename() {
445 use fidl::Event;
446
447 let events = Events::new();
448 let fs = Arc::new(MockFilesystem::new(&events));
449
450 let (_dir, proxy) = fs.clone().make_connection(fio::PERM_READABLE | fio::PERM_WRITABLE);
451 let (dir2, proxy2) = fs.clone().make_connection(fio::PERM_READABLE | fio::PERM_WRITABLE);
452
453 let (status, token) = proxy2.get_token().await.unwrap();
454 assert_eq!(Status::from_raw(status), Status::OK);
455
456 let status = proxy.rename("src", Event::from(token.unwrap()), "dest").await.unwrap();
457 assert!(status.is_ok());
458
459 let events = events.0.lock();
460 assert_eq!(
461 *events,
462 vec![MutableDirectoryAction::Rename {
463 id: 0,
464 src_name: "src".to_owned(),
465 dst_dir: dir2.id,
466 dst_name: "dest".to_owned(),
467 },]
468 );
469 }
470
471 #[fuchsia::test]
472 async fn test_update_attributes() {
473 let events = Events::new();
474 let fs = Arc::new(MockFilesystem::new(&events));
475 let (_dir, proxy) = fs.clone().make_connection(fio::PERM_READABLE | fio::PERM_WRITABLE);
476 let attributes = fio::MutableNodeAttributes {
477 creation_time: Some(30),
478 modification_time: Some(100),
479 mode: Some(200),
480 ..Default::default()
481 };
482 proxy
483 .update_attributes(&attributes)
484 .await
485 .expect("FIDL call failed")
486 .map_err(Status::from_raw)
487 .expect("update attributes failed");
488
489 let events = events.0.lock();
490 assert_eq!(*events, vec![MutableDirectoryAction::UpdateAttributes { id: 0, attributes }]);
491 }
492
493 #[fuchsia::test]
494 async fn test_link() {
495 let events = Events::new();
496 let fs = Arc::new(MockFilesystem::new(&events));
497 let (_dir, proxy) = fs.clone().make_connection(fio::PERM_READABLE | fio::PERM_WRITABLE);
498 let (_dir2, proxy2) = fs.clone().make_connection(fio::PERM_READABLE | fio::PERM_WRITABLE);
499
500 let (status, token) = proxy2.get_token().await.unwrap();
501 assert_eq!(Status::from_raw(status), Status::OK);
502
503 let status = proxy.link("src", token.unwrap(), "dest").await.unwrap();
504 assert_eq!(Status::from_raw(status), Status::OK);
505 let events = events.0.lock();
506 assert_eq!(*events, vec![MutableDirectoryAction::Link { id: 1, path: "dest".to_owned() },]);
507 }
508
509 #[fuchsia::test]
510 async fn test_unlink() {
511 let events = Events::new();
512 let fs = Arc::new(MockFilesystem::new(&events));
513 let (_dir, proxy) = fs.clone().make_connection(fio::PERM_READABLE | fio::PERM_WRITABLE);
514 proxy
515 .unlink("test", &fio::UnlinkOptions::default())
516 .await
517 .expect("fidl call failed")
518 .expect("unlink failed");
519 let events = events.0.lock();
520 assert_eq!(
521 *events,
522 vec![MutableDirectoryAction::Unlink { id: 0, name: "test".to_string() },]
523 );
524 }
525
526 #[fuchsia::test]
527 async fn test_sync() {
528 let events = Events::new();
529 let fs = Arc::new(MockFilesystem::new(&events));
530 let (_dir, proxy) = fs.clone().make_connection(fio::PERM_READABLE | fio::PERM_WRITABLE);
531 let () = proxy.sync().await.unwrap().map_err(Status::from_raw).unwrap();
532 let events = events.0.lock();
533 assert_eq!(*events, vec![MutableDirectoryAction::Sync]);
534 }
535
536 #[fuchsia::test]
537 async fn test_close() {
538 let events = Events::new();
539 let fs = Arc::new(MockFilesystem::new(&events));
540 let (_dir, proxy) = fs.clone().make_connection(fio::PERM_READABLE | fio::PERM_WRITABLE);
541 let () = proxy.close().await.unwrap().map_err(Status::from_raw).unwrap();
542 let events = events.0.lock();
543 assert_eq!(*events, vec![MutableDirectoryAction::Close]);
544 }
545
546 #[fuchsia::test]
547 async fn test_implicit_close() {
548 let events = Events::new();
549 let fs = Arc::new(MockFilesystem::new(&events));
550 let (_dir, _proxy) = fs.clone().make_connection(fio::PERM_READABLE | fio::PERM_WRITABLE);
551
552 fs.scope.shutdown();
553 fs.scope.wait().await;
554
555 let events = events.0.lock();
556 assert_eq!(*events, vec![MutableDirectoryAction::Close]);
557 }
558}