From 79012be2c8178338cd03066df3fd070096a5ae9f Mon Sep 17 00:00:00 2001 From: Niklas Mollenhauer Date: Fri, 31 Oct 2025 12:32:25 +0100 Subject: [PATCH] Add default storage class to ListObjectsV2 (#765) * Add InvalidRangeSpec error * Add EntityTooSmall to from_u32 * Add InvalidRangeSpec to from_u32 * Map InvalidRangeSpec to correct S3ErrorCode * Return Error::InvalidRangeSpec * Use auto implementation * Add default storage class to ListObjectsV2 Resolves #764 * Add storage_class to response * Make storage class optional so default won't be an empty string --------- Co-authored-by: houseme --- crates/ecstore/src/store_api.rs | 11 +++++++++++ rustfs/src/storage/ecfs.rs | 1 + 2 files changed, 12 insertions(+) diff --git a/crates/ecstore/src/store_api.rs b/crates/ecstore/src/store_api.rs index 7ee08e92..2dd71992 100644 --- a/crates/ecstore/src/store_api.rs +++ b/crates/ecstore/src/store_api.rs @@ -14,6 +14,7 @@ use crate::bucket::metadata_sys::get_versioning_config; use crate::bucket::versioning::VersioningApi as _; +use crate::config::storageclass; use crate::disk::DiskStore; use crate::error::{Error, Result}; use crate::store_utils::clean_metadata; @@ -33,6 +34,7 @@ use rustfs_madmin::heal_commands::HealResultItem; use rustfs_rio::Checksum; use rustfs_rio::{DecompressReader, HashReader, LimitReader, WarpReader}; use rustfs_utils::CompressionAlgorithm; +use rustfs_utils::http::AMZ_STORAGE_CLASS; use rustfs_utils::http::headers::{AMZ_OBJECT_TAGGING, RESERVED_METADATA_PREFIX_LOWER}; use rustfs_utils::path::decode_dir_object; use serde::{Deserialize, Serialize}; @@ -518,6 +520,7 @@ impl From for CompletePart { pub struct ObjectInfo { pub bucket: String, pub name: String, + pub storage_class: Option, pub mod_time: Option, pub size: i64, // Actual size is the real size of the object uploaded by client. @@ -557,6 +560,7 @@ impl Clone for ObjectInfo { Self { bucket: self.bucket.clone(), name: self.name.clone(), + storage_class: self.storage_class.clone(), mod_time: self.mod_time, size: self.size, actual_size: self.actual_size, @@ -689,6 +693,12 @@ impl ObjectInfo { v }; + // Extract storage class from metadata, default to STANDARD if not found + let storage_class = metadata + .get(AMZ_STORAGE_CLASS) + .cloned() + .or_else(|| Some(storageclass::STANDARD.to_string())); + // Convert parts from rustfs_filemeta::ObjectPartInfo to store_api::ObjectPartInfo let parts = fi .parts @@ -727,6 +737,7 @@ impl ObjectInfo { user_defined: metadata, transitioned_object, checksum: fi.checksum.clone(), + storage_class, ..Default::default() } } diff --git a/rustfs/src/storage/ecfs.rs b/rustfs/src/storage/ecfs.rs index d0e43366..413cf6d3 100644 --- a/rustfs/src/storage/ecfs.rs +++ b/rustfs/src/storage/ecfs.rs @@ -2218,6 +2218,7 @@ impl S3 for FS { last_modified: v.mod_time.map(Timestamp::from), size: Some(v.get_actual_size().unwrap_or_default()), e_tag: v.etag.clone().map(|etag| to_s3s_etag(&etag)), + storage_class: v.storage_class.clone().map(ObjectStorageClass::from), ..Default::default() };