use crate::common::{inherit_rights_for_clone, IntoAny};
use crate::directory::entry::GetEntryInfo;
use crate::directory::entry_container::MutableDirectory;
use crate::execution_scope::ExecutionScope;
use crate::name::Name;
use crate::object_request::Representation;
use crate::protocols::ToNodeOptions;
use crate::{node, ObjectRequestRef, ToObjectRequest};
use anyhow::Error;
use fidl::endpoints::ServerEnd;
use fidl_fuchsia_io as fio;
use futures::stream::StreamExt;
use libc::{S_IRUSR, S_IWUSR};
use std::future::{ready, Future};
use std::sync::Arc;
use zx_status::Status;
#[cfg(not(target_os = "macos"))]
pub const POSIX_READ_WRITE_PROTECTION_ATTRIBUTES: u32 = S_IRUSR | S_IWUSR;
#[cfg(target_os = "macos")]
pub const POSIX_READ_WRITE_PROTECTION_ATTRIBUTES: u16 = S_IRUSR | S_IWUSR;
#[derive(Clone, Copy)]
pub struct NodeOptions {
pub rights: fio::Operations,
}
pub trait Node: GetEntryInfo + IntoAny + Send + Sync + 'static {
fn get_attributes(
&self,
requested_attributes: fio::NodeAttributesQuery,
) -> impl Future<Output = Result<fio::NodeAttributes2, Status>> + Send
where
Self: Sized;
fn will_open_as_node(&self) -> Result<(), Status> {
self.will_clone();
Ok(())
}
fn will_clone(&self) {}
fn close(self: Arc<Self>) {}
fn link_into(
self: Arc<Self>,
_destination_dir: Arc<dyn MutableDirectory>,
_name: Name,
) -> impl Future<Output = Result<(), Status>> + Send
where
Self: Sized,
{
ready(Err(Status::NOT_SUPPORTED))
}
fn query_filesystem(&self) -> Result<fio::FilesystemInfo, Status> {
Err(Status::NOT_SUPPORTED)
}
fn open_as_node(
self: Arc<Self>,
scope: ExecutionScope,
options: NodeOptions,
object_request: ObjectRequestRef<'_>,
) -> Result<(), Status>
where
Self: Sized,
{
self.will_open_as_node()?;
scope.spawn(node::Connection::create(scope.clone(), self, options, object_request)?);
Ok(())
}
}
pub struct Connection<N: Node> {
scope: ExecutionScope,
node: OpenNode<N>,
options: NodeOptions,
}
enum ConnectionState {
Alive,
Closed,
}
impl<N: Node> Connection<N> {
pub fn create(
scope: ExecutionScope,
node: Arc<N>,
options: impl ToNodeOptions,
object_request: ObjectRequestRef<'_>,
) -> Result<impl Future<Output = ()>, Status> {
let node = OpenNode::new(node);
let options = options.to_node_options(node.entry_info().type_())?;
let object_request = object_request.take();
Ok(async move {
let connection = Connection { scope: scope.clone(), node, options };
if let Ok(requests) = object_request.into_request_stream(&connection).await {
connection.handle_requests(requests).await
}
})
}
async fn handle_requests(mut self, mut requests: fio::NodeRequestStream) {
while let Some(request_or_err) = requests.next().await {
match request_or_err {
Err(_) => {
break;
}
Ok(request) => {
match self.handle_request(request).await {
Ok(ConnectionState::Alive) => (),
Ok(ConnectionState::Closed) | Err(_) => {
break;
}
}
}
}
}
}
async fn handle_request(&mut self, req: fio::NodeRequest) -> Result<ConnectionState, Error> {
match req {
fio::NodeRequest::Clone { flags, object, control_handle: _ } => {
self.handle_clone(flags, object);
}
fio::NodeRequest::Clone2 { request, control_handle: _ } => {
self.handle_reopen(ServerEnd::new(request.into_channel()));
}
fio::NodeRequest::Close { responder } => {
responder.send(Ok(()))?;
return Ok(ConnectionState::Closed);
}
fio::NodeRequest::GetConnectionInfo { responder } => {
responder.send(fio::ConnectionInfo {
rights: Some(self.options.rights),
..Default::default()
})?;
}
fio::NodeRequest::Sync { responder } => {
responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
}
fio::NodeRequest::GetAttr { responder } => {
let (status, attrs) =
crate::common::io2_to_io1_attrs(self.node.as_ref(), self.options.rights).await;
responder.send(status.into_raw(), &attrs)?;
}
fio::NodeRequest::SetAttr { flags: _, attributes: _, responder } => {
responder.send(Status::BAD_HANDLE.into_raw())?;
}
fio::NodeRequest::GetAttributes { query, responder } => {
let result = self.node.get_attributes(query).await;
responder.send(
result
.as_ref()
.map(|attrs| (&attrs.mutable_attributes, &attrs.immutable_attributes))
.map_err(|status| status.into_raw()),
)?;
}
fio::NodeRequest::UpdateAttributes { payload: _, responder } => {
responder.send(Err(Status::BAD_HANDLE.into_raw()))?;
}
fio::NodeRequest::ListExtendedAttributes { iterator, .. } => {
iterator.close_with_epitaph(Status::NOT_SUPPORTED)?;
}
fio::NodeRequest::GetExtendedAttribute { responder, .. } => {
responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
}
fio::NodeRequest::SetExtendedAttribute { responder, .. } => {
responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
}
fio::NodeRequest::RemoveExtendedAttribute { responder, .. } => {
responder.send(Err(Status::NOT_SUPPORTED.into_raw()))?;
}
fio::NodeRequest::GetFlags { responder } => {
responder.send(Status::OK.into_raw(), fio::OpenFlags::NODE_REFERENCE)?;
}
fio::NodeRequest::SetFlags { flags: _, responder } => {
responder.send(Status::BAD_HANDLE.into_raw())?;
}
fio::NodeRequest::Query { responder } => {
responder.send(fio::NODE_PROTOCOL_NAME.as_bytes())?;
}
fio::NodeRequest::QueryFilesystem { responder } => {
responder.send(Status::NOT_SUPPORTED.into_raw(), None)?;
}
#[cfg(fuchsia_api_level_at_least = "24")]
fio::NodeRequest::_UnknownMethod { .. } => (),
}
Ok(ConnectionState::Alive)
}
fn handle_clone(&mut self, flags: fio::OpenFlags, server_end: ServerEnd<fio::NodeMarker>) {
flags.to_object_request(server_end).handle(|object_request| {
let options = inherit_rights_for_clone(fio::OpenFlags::NODE_REFERENCE, flags)?
.to_node_options(self.node.entry_info().type_())?;
self.node.will_clone();
let connection =
Self { scope: self.scope.clone(), node: OpenNode::new(self.node.clone()), options };
object_request.take().spawn(&self.scope, |object_request| {
Box::pin(async {
let requests = object_request.take().into_request_stream(&connection).await?;
Ok(connection.handle_requests(requests))
})
});
Ok(())
});
}
fn handle_reopen(&mut self, server_end: ServerEnd<fio::NodeMarker>) {
self.node.will_clone();
let connection = Self {
scope: self.scope.clone(),
node: OpenNode::new(self.node.clone()),
options: self.options,
};
self.scope.spawn(async move {
if let Ok(requests) = server_end.into_stream() {
connection.handle_requests(requests).await;
}
});
}
}
impl<N: Node> Representation for Connection<N> {
type Protocol = fio::NodeMarker;
async fn get_representation(
&self,
requested_attributes: fio::NodeAttributesQuery,
) -> Result<fio::Representation, Status> {
Ok(fio::Representation::Connector(fio::ConnectorInfo {
attributes: if requested_attributes.is_empty() {
None
} else {
Some(self.node.get_attributes(requested_attributes).await?)
},
..Default::default()
}))
}
async fn node_info(&self) -> Result<fio::NodeInfoDeprecated, Status> {
Ok(fio::NodeInfoDeprecated::Service(fio::Service))
}
}
pub struct OpenNode<T: Node> {
node: Arc<T>,
}
impl<T: Node> OpenNode<T> {
pub fn new(node: Arc<T>) -> Self {
Self { node }
}
}
impl<T: Node> Drop for OpenNode<T> {
fn drop(&mut self) {
self.node.clone().close();
}
}
impl<T: Node> std::ops::Deref for OpenNode<T> {
type Target = Arc<T>;
fn deref(&self) -> &Self::Target {
&self.node
}
}