mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-08 22:33:22 +00:00
290 lines
9.2 KiB
Rust
290 lines
9.2 KiB
Rust
use super::{
|
|
encryption::BucketSSEConfig, event, lifecycle::lifecycle::Lifecycle, objectlock, policy::bucket_policy::BucketPolicy,
|
|
quota::BucketQuota, replication, tags::Tags, target::BucketTargets, versioning::Versioning,
|
|
};
|
|
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
|
use rmp_serde::Serializer as rmpSerializer;
|
|
use serde::Serializer;
|
|
use serde::{Deserialize, Deserializer, Serialize};
|
|
use std::collections::HashMap;
|
|
use std::fmt::Display;
|
|
use std::str::FromStr;
|
|
use time::OffsetDateTime;
|
|
use tracing::error;
|
|
|
|
use crate::bucket::tags;
|
|
use crate::config::common::{read_config, save_config};
|
|
use crate::config::error::ConfigError;
|
|
use crate::error::{Error, Result};
|
|
|
|
use crate::disk::BUCKET_META_PREFIX;
|
|
use crate::store::ECStore;
|
|
use crate::store_api::StorageAPI;
|
|
|
|
pub const BUCKET_METADATA_FILE: &str = ".metadata.bin";
|
|
pub const BUCKET_METADATA_FORMAT: u16 = 1;
|
|
pub const BUCKET_METADATA_VERSION: u16 = 1;
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
|
#[serde(rename_all = "PascalCase", default)]
|
|
pub struct BucketMetadata {
|
|
pub name: String,
|
|
pub created: OffsetDateTime,
|
|
pub lock_enabled: bool, // 虽然标记为不使用,但可能需要保留
|
|
pub policy_config_json: Vec<u8>,
|
|
pub notification_config_xml: Vec<u8>,
|
|
pub lifecycle_config_xml: Vec<u8>,
|
|
pub object_lock_config_xml: Vec<u8>,
|
|
pub versioning_config_xml: Vec<u8>,
|
|
pub encryption_config_xml: Vec<u8>,
|
|
pub tagging_config_xml: Vec<u8>,
|
|
pub quota_config_json: Vec<u8>,
|
|
pub replication_config_xml: Vec<u8>,
|
|
pub bucket_targets_config_json: Vec<u8>,
|
|
pub bucket_targets_config_meta_json: Vec<u8>,
|
|
|
|
pub policy_config_updated_at: OffsetDateTime,
|
|
pub object_lock_config_updated_at: OffsetDateTime,
|
|
pub encryption_config_updated_at: OffsetDateTime,
|
|
pub tagging_config_updated_at: OffsetDateTime,
|
|
pub quota_config_updated_at: OffsetDateTime,
|
|
pub replication_config_updated_at: OffsetDateTime,
|
|
pub versioning_config_updated_at: OffsetDateTime,
|
|
pub lifecycle_config_updated_at: OffsetDateTime,
|
|
pub notification_config_updated_at: OffsetDateTime,
|
|
pub bucket_targets_config_updated_at: OffsetDateTime,
|
|
pub bucket_targets_config_meta_updated_at: OffsetDateTime,
|
|
|
|
#[serde(skip)]
|
|
pub new_field_updated_at: OffsetDateTime,
|
|
|
|
#[serde(skip)]
|
|
pub policy_config: Option<BucketPolicy>,
|
|
#[serde(skip)]
|
|
pub notification_config: Option<event::Config>,
|
|
#[serde(skip)]
|
|
pub lifecycle_config: Option<Lifecycle>,
|
|
#[serde(skip)]
|
|
pub object_lock_config: Option<objectlock::Config>,
|
|
#[serde(skip)]
|
|
pub versioning_config: Option<Versioning>,
|
|
#[serde(skip)]
|
|
pub sse_config: Option<BucketSSEConfig>,
|
|
#[serde(skip)]
|
|
pub tagging_config: Option<Tags>,
|
|
#[serde(skip)]
|
|
pub quota_config: Option<BucketQuota>,
|
|
#[serde(skip)]
|
|
pub replication_config: Option<replication::Config>,
|
|
#[serde(skip)]
|
|
pub bucket_target_config: Option<BucketTargets>,
|
|
#[serde(skip)]
|
|
pub bucket_target_config_meta: Option<HashMap<String, String>>,
|
|
}
|
|
|
|
impl Default for BucketMetadata {
|
|
fn default() -> Self {
|
|
Self {
|
|
name: Default::default(),
|
|
created: OffsetDateTime::UNIX_EPOCH,
|
|
lock_enabled: Default::default(),
|
|
policy_config_json: Default::default(),
|
|
notification_config_xml: Default::default(),
|
|
lifecycle_config_xml: Default::default(),
|
|
object_lock_config_xml: Default::default(),
|
|
versioning_config_xml: Default::default(),
|
|
encryption_config_xml: Default::default(),
|
|
tagging_config_xml: Default::default(),
|
|
quota_config_json: Default::default(),
|
|
replication_config_xml: Default::default(),
|
|
bucket_targets_config_json: Default::default(),
|
|
bucket_targets_config_meta_json: Default::default(),
|
|
policy_config_updated_at: OffsetDateTime::UNIX_EPOCH,
|
|
object_lock_config_updated_at: OffsetDateTime::UNIX_EPOCH,
|
|
encryption_config_updated_at: OffsetDateTime::UNIX_EPOCH,
|
|
tagging_config_updated_at: OffsetDateTime::UNIX_EPOCH,
|
|
quota_config_updated_at: OffsetDateTime::UNIX_EPOCH,
|
|
replication_config_updated_at: OffsetDateTime::UNIX_EPOCH,
|
|
versioning_config_updated_at: OffsetDateTime::UNIX_EPOCH,
|
|
lifecycle_config_updated_at: OffsetDateTime::UNIX_EPOCH,
|
|
notification_config_updated_at: OffsetDateTime::UNIX_EPOCH,
|
|
bucket_targets_config_updated_at: OffsetDateTime::UNIX_EPOCH,
|
|
bucket_targets_config_meta_updated_at: OffsetDateTime::UNIX_EPOCH,
|
|
new_field_updated_at: OffsetDateTime::UNIX_EPOCH,
|
|
policy_config: Default::default(),
|
|
notification_config: Default::default(),
|
|
lifecycle_config: Default::default(),
|
|
object_lock_config: Default::default(),
|
|
versioning_config: Default::default(),
|
|
sse_config: Default::default(),
|
|
tagging_config: Default::default(),
|
|
quota_config: Default::default(),
|
|
replication_config: Default::default(),
|
|
bucket_target_config: Default::default(),
|
|
bucket_target_config_meta: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl BucketMetadata {
|
|
pub fn new(name: &str) -> Self {
|
|
BucketMetadata {
|
|
name: name.to_string(),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
pub fn save_file_path(&self) -> String {
|
|
format!("{}/{}/{}", BUCKET_META_PREFIX, self.name.as_str(), BUCKET_METADATA_FILE)
|
|
}
|
|
|
|
// fn msg_size(&self) -> usize {
|
|
// unimplemented!()
|
|
// }
|
|
|
|
pub fn marshal_msg(&self) -> Result<Vec<u8>> {
|
|
let mut buf = Vec::new();
|
|
|
|
self.serialize(&mut rmpSerializer::new(&mut buf).with_struct_map())?;
|
|
|
|
Ok(buf)
|
|
}
|
|
|
|
pub fn unmarshal(buf: &[u8]) -> Result<Self> {
|
|
let t: BucketMetadata = rmp_serde::from_slice(buf)?;
|
|
Ok(t)
|
|
}
|
|
|
|
pub fn check_header(buf: &[u8]) -> Result<()> {
|
|
if buf.len() <= 4 {
|
|
return Err(Error::msg("read_bucket_metadata: data invalid"));
|
|
}
|
|
|
|
// TODO: check version
|
|
Ok(())
|
|
}
|
|
|
|
fn default_timestamps(&mut self) {
|
|
if self.tagging_config_updated_at == OffsetDateTime::UNIX_EPOCH {
|
|
self.tagging_config_updated_at = self.created
|
|
}
|
|
}
|
|
|
|
async fn save(&mut self, api: &ECStore) -> Result<()> {
|
|
self.parse_all_configs(api)?;
|
|
|
|
let mut buf: Vec<u8> = vec![0; 4];
|
|
|
|
LittleEndian::write_u16(&mut buf[0..2], BUCKET_METADATA_FORMAT);
|
|
|
|
LittleEndian::write_u16(&mut buf[2..4], BUCKET_METADATA_VERSION);
|
|
|
|
let data = self.marshal_msg()?;
|
|
|
|
buf.extend_from_slice(&data);
|
|
|
|
save_config(api, self.save_file_path().as_str(), &buf).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn parse_all_configs(&mut self, _api: &ECStore) -> Result<()> {
|
|
if !self.tagging_config_xml.is_empty() {
|
|
self.tagging_config = Some(tags::Tags::unmarshal(&self.tagging_config_xml)?);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub async fn load_bucket_metadata(api: &ECStore, bucket: &str) -> Result<BucketMetadata> {
|
|
load_bucket_metadata_parse(api, bucket, true).await
|
|
}
|
|
|
|
async fn load_bucket_metadata_parse(api: &ECStore, bucket: &str, parse: bool) -> Result<BucketMetadata> {
|
|
let mut bm = match read_bucket_metadata(api, bucket).await {
|
|
Ok(res) => res,
|
|
Err(err) => {
|
|
if let Some(e) = err.downcast_ref::<ConfigError>() {
|
|
if !ConfigError::is_not_found(&e) {
|
|
return Err(err);
|
|
}
|
|
}
|
|
|
|
BucketMetadata::new(bucket)
|
|
}
|
|
};
|
|
|
|
bm.default_timestamps();
|
|
|
|
if parse {
|
|
bm.parse_all_configs(api)?;
|
|
}
|
|
|
|
// TODO: parse_all_configs
|
|
|
|
Ok(bm)
|
|
}
|
|
|
|
async fn read_bucket_metadata(api: &ECStore, bucket: &str) -> Result<BucketMetadata> {
|
|
if bucket.is_empty() {
|
|
error!("bucket name empty");
|
|
return Err(Error::msg("invalid argument"));
|
|
}
|
|
|
|
let bm = BucketMetadata::new(&bucket);
|
|
let file_path = bm.save_file_path();
|
|
|
|
let data = read_config(api, &file_path).await?;
|
|
|
|
BucketMetadata::check_header(&data)?;
|
|
|
|
BucketMetadata::unmarshal(&data[4..])
|
|
}
|
|
|
|
fn _deserialize_from_str<'de, S, D>(deserializer: D) -> core::result::Result<S, D::Error>
|
|
where
|
|
S: FromStr,
|
|
S::Err: Display,
|
|
D: Deserializer<'de>,
|
|
{
|
|
// let s: String = Deserialize::deserialize(deserializer)?;
|
|
// S::from_str(&s).map_err(de::Error::custom)
|
|
unimplemented!()
|
|
}
|
|
|
|
fn _write_time<S>(t: &OffsetDateTime, s: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
let mut buf = vec![0x0; 15];
|
|
|
|
let sec = t.unix_timestamp() - 62135596800;
|
|
let nsec = t.nanosecond();
|
|
buf[0] = 0xc7; // mext8
|
|
buf[1] = 0x0c; // 长度
|
|
buf[2] = 0x05; // 时间扩展类型
|
|
BigEndian::write_u64(&mut buf[3..], sec as u64);
|
|
BigEndian::write_u32(&mut buf[11..], nsec as u32);
|
|
s.serialize_bytes(&buf)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
#[tokio::test]
|
|
async fn marshal_msg() {
|
|
// write_time(OffsetDateTime::UNIX_EPOCH).unwrap();
|
|
|
|
let bm = BucketMetadata::new("dada");
|
|
|
|
let buf = bm.marshal_msg().unwrap();
|
|
|
|
let new = BucketMetadata::unmarshal(&buf).unwrap();
|
|
|
|
assert_eq!(bm.name, new.name);
|
|
}
|
|
}
|