use crate::plugins::s3_common::{
create_s3_client, normalize_prefix, S3MountCredentials, DEFAULT_REGION,
};
use secure_exec_kernel::mount_plugin::{
FileSystemPluginFactory, OpenFileSystemPluginRequest, PluginError,
};
use secure_exec_kernel::mount_table::MountedFileSystem;
use secure_exec_vfs::{S3ObjectBackend, S3ObjectBackendOptions};
use serde::Deserialize;
use vfs::adapter::MountedEngineFileSystem;
use vfs::engine::engines::{ObjectFs, ObjectFsOptions};
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ObjectS3MountConfig {
bucket: String,
prefix: Option<String>,
region: Option<String>,
credentials: Option<S3MountCredentials>,
endpoint: Option<String>,
uid: Option<u32>,
gid: Option<u32>,
file_mode: Option<u32>,
dir_mode: Option<u32>,
}
#[derive(Debug)]
pub(crate) struct ObjectS3MountPlugin;
impl<Context> FileSystemPluginFactory<Context> for ObjectS3MountPlugin {
fn plugin_id(&self) -> &'static str {
"object_s3"
}
fn open(
&self,
request: OpenFileSystemPluginRequest<'_, Context>,
) -> Result<Box<dyn MountedFileSystem>, PluginError> {
let config: ObjectS3MountConfig = serde_json::from_value(request.config.clone())
.map_err(|error| PluginError::invalid_input(error.to_string()))?;
let bucket = config.bucket.trim().to_owned();
if bucket.is_empty() {
return Err(PluginError::invalid_input(
"object_s3 mount requires a non-empty bucket",
));
}
let prefix = normalize_prefix(config.prefix.as_deref());
let client = create_s3_client(
config.region.unwrap_or_else(|| DEFAULT_REGION.to_owned()),
config.endpoint,
config.credentials,
)?;
let backend = S3ObjectBackend::with_options(
client,
bucket,
S3ObjectBackendOptions {
prefix: prefix.clone(),
},
);
let fs = ObjectFs::with_options(
backend,
ObjectFsOptions {
prefix: String::new(),
uid: config.uid.unwrap_or(0),
gid: config.gid.unwrap_or(0),
file_mode: config.file_mode.unwrap_or(0o644),
dir_mode: config.dir_mode.unwrap_or(0o755),
},
);
Ok(Box::new(MountedEngineFileSystem::new(fs)?))
}
}