@@ -2126,4 +2126,267 @@ impl S3 for ArunaS3Service {
21262126
21272127 Ok ( resp)
21282128 }
2129+
2130+ #[ tracing:: instrument( err) ]
2131+ #[ allow( clippy:: blocks_in_conditions) ]
2132+ async fn copy_object (
2133+ & self ,
2134+ req : S3Request < CopyObjectInput > ,
2135+ ) -> S3Result < S3Response < CopyObjectOutput > > {
2136+ // Check access for target
2137+ let CheckAccessResult {
2138+ user_state,
2139+ headers,
2140+ objects_state,
2141+ } = req
2142+ . extensions
2143+ . get :: < CheckAccessResult > ( )
2144+ . cloned ( )
2145+ . ok_or_else ( || {
2146+ error ! ( error = "Missing data context" ) ;
2147+ s3_error ! ( UnexpectedContent , "Missing data context" )
2148+ } ) ?;
2149+
2150+ // Query results from check access
2151+ let ( states, _) = objects_state. require_regular ( ) ?;
2152+ let all = states. to_new_or_existing ( ) ?;
2153+ trace ! ( ?all) ;
2154+ let ( _, collection, dataset, new_object, path) = all;
2155+
2156+ // Authorize existing object and get object + location
2157+ let ( existing_object, existing_location) =
2158+ if let CopySource :: Bucket { key, bucket, .. } = & req. input . copy_source {
2159+ let lock = self . cache . auth . read ( ) . await ;
2160+ let auth = match lock. as_ref ( ) {
2161+ Some ( auth) => auth,
2162+ None => {
2163+ return Err ( s3_error ! (
2164+ InternalError ,
2165+ "Could not access authorization handler"
2166+ ) ) ;
2167+ }
2168+ } ;
2169+ let CheckAccessResult { objects_state, .. } = auth
2170+ . handle_object (
2171+ bucket,
2172+ key,
2173+ & http:: Method :: GET ,
2174+ req. credentials . as_ref ( ) ,
2175+ & req. headers ,
2176+ )
2177+ . await ?;
2178+ drop ( lock) ;
2179+
2180+ let ( states, location) = objects_state. require_regular ( ) ?;
2181+ let all = states. to_new_or_existing ( ) ?;
2182+ trace ! ( ?all) ;
2183+ let ( _, _, _, object, _) = all;
2184+
2185+ let NewOrExistingObject :: Existing ( object) = object else {
2186+ return Err ( s3_error ! ( NoSuchKey , "Source object not found" ) ) ;
2187+ } ;
2188+ let Some ( location) = location else {
2189+ return Err ( s3_error ! ( NoSuchKey , "Location of source object not found" ) ) ;
2190+ } ;
2191+ ( object, location)
2192+ } else {
2193+ return Err ( s3_error ! (
2194+ InvalidRequest ,
2195+ "AWS Access points are unsupported"
2196+ ) ) ;
2197+ } ;
2198+
2199+ let md5hash = existing_object. hashes . get ( "MD5" ) . cloned ( ) ;
2200+ let sha256hash = existing_object. hashes . get ( "SHA256" ) . cloned ( ) ;
2201+
2202+ // Create missing objects for copy
2203+ let impersonating_token =
2204+ user_state. sign_impersonating_token ( self . cache . auth . read ( ) . await . as_ref ( ) ) ;
2205+
2206+ let ( mut new_object, exists) = match new_object {
2207+ NewOrExistingObject :: Missing ( mut new_object) => {
2208+ let mut collection_id = None ;
2209+ if let NewOrExistingObject :: Missing ( collection) = collection {
2210+ trace ! ( ?collection) ;
2211+ if let Some ( handler) = self . cache . aruna_client . read ( ) . await . as_ref ( ) {
2212+ if let Some ( token) = & impersonating_token {
2213+ let col = handler. create_collection ( collection, token) . await . map_err (
2214+ |_| {
2215+ error ! ( error = "Unable to create collection" ) ;
2216+ s3_error ! ( InternalError , "Unable to create collection" )
2217+ } ,
2218+ ) ?;
2219+ collection_id = Some ( col. id ) ;
2220+ }
2221+ }
2222+ }
2223+
2224+ let mut dataset_id = None ;
2225+ if let NewOrExistingObject :: Missing ( mut dataset) = dataset {
2226+ trace ! ( ?dataset) ;
2227+ if let Some ( handler) = self . cache . aruna_client . read ( ) . await . as_ref ( ) {
2228+ if let Some ( token) = & impersonating_token {
2229+ if let Some ( collection_id) = collection_id {
2230+ dataset. parents =
2231+ Some ( HashSet :: from_iter ( [ TypedRelation :: Collection (
2232+ collection_id,
2233+ ) ] ) ) ;
2234+ }
2235+ let dataset =
2236+ handler. create_dataset ( dataset, token) . await . map_err ( |_| {
2237+ error ! ( error = "Unable to create dataset" ) ;
2238+ s3_error ! ( InternalError , "Unable to create dataset" )
2239+ } ) ?;
2240+ dataset_id = Some ( dataset. id ) ;
2241+ }
2242+ }
2243+ }
2244+
2245+ if let Some ( dataset_id) = dataset_id {
2246+ new_object. parents =
2247+ Some ( HashSet :: from_iter ( [ TypedRelation :: Dataset ( dataset_id) ] ) ) ;
2248+ } else if let Some ( collection_id) = collection_id {
2249+ new_object. parents = Some ( HashSet :: from_iter ( [ TypedRelation :: Collection (
2250+ collection_id,
2251+ ) ] ) ) ;
2252+ }
2253+ ( new_object, false )
2254+ }
2255+ NewOrExistingObject :: Existing ( mut object) => {
2256+ if object. object_status == Status :: Initializing {
2257+ ( object, true )
2258+ } else {
2259+ if let Some ( handler) = self . cache . aruna_client . read ( ) . await . as_ref ( ) {
2260+ if let Some ( token) = & impersonating_token {
2261+ handler
2262+ . init_object_update ( object. clone ( ) , token, true )
2263+ . await
2264+ . map_err ( |_| {
2265+ error ! ( error = "Object update failed" ) ;
2266+ s3_error ! ( InternalError , "Object update failed" )
2267+ } ) ?
2268+ } else {
2269+ error ! ( "missing impersonating token" ) ;
2270+ return Err ( s3_error ! ( InternalError , "Token creation failed" ) ) ;
2271+ }
2272+ } else {
2273+ error ! ( "ArunaServer client not available" ) ;
2274+ return Err ( s3_error ! ( InternalError , "ArunaServer client not available" ) ) ;
2275+ } ;
2276+ object. hashes = existing_object. hashes . clone ( ) ;
2277+ object. synced = existing_object. synced ;
2278+ object. children = None ; // This should be None anyway, because this is an Object
2279+ object. dynamic = existing_object. dynamic ;
2280+
2281+ ( object, true )
2282+ }
2283+ }
2284+ NewOrExistingObject :: None => {
2285+ return Err ( s3_error ! (
2286+ InvalidObjectState ,
2287+ "Object in invalid state: None"
2288+ ) )
2289+ }
2290+ } ;
2291+
2292+ // Create new location
2293+ let new_location = self
2294+ . backend
2295+ . initialize_location (
2296+ & new_object,
2297+ Some ( existing_location. raw_content_len ) ,
2298+ path,
2299+ false ,
2300+ )
2301+ . await
2302+ . map_err ( |_| {
2303+ error ! ( error = "Unable to create object_location" ) ;
2304+ s3_error ! ( InternalError , "Unable to create object_location" )
2305+ } ) ?;
2306+ let content_len = new_location. raw_content_len ;
2307+
2308+ // Copy data
2309+ self . backend
2310+ . copy_data ( existing_location, new_location. clone ( ) )
2311+ . await
2312+ . map_err ( |e| {
2313+ error ! ( error = ?e, "Unable to create and finish object" ) ;
2314+ s3_error ! ( InternalError , "{}" , e. to_string( ) )
2315+ } ) ?;
2316+
2317+ // Finish object
2318+ if let Some ( handler) = self . cache . aruna_client . read ( ) . await . as_ref ( ) {
2319+ if let Some ( token) = & impersonating_token {
2320+ if exists {
2321+ new_object = handler
2322+ . finish_object (
2323+ new_object. id ,
2324+ content_len,
2325+ existing_object. get_hashes ( ) ,
2326+ token,
2327+ )
2328+ . await
2329+ . map_err ( |_| {
2330+ error ! ( error = "Unable to finish object" ) ;
2331+ s3_error ! ( InternalError , "Unable to finish object" )
2332+ } ) ?;
2333+ } else {
2334+ trace ! ( ?new_object) ;
2335+ new_object = handler
2336+ . create_and_finish ( new_object. clone ( ) , content_len, token)
2337+ . await
2338+ . map_err ( |e| {
2339+ error ! ( error = ?e, "Unable to create and finish object" ) ;
2340+ s3_error ! ( InvalidObjectState , "{}" , e. to_string( ) )
2341+ } ) ?;
2342+ }
2343+ }
2344+ }
2345+
2346+ trace ! ( "Before finish" ) ;
2347+
2348+ // Finish location
2349+ self . cache
2350+ . add_location_with_binding ( new_object. id , new_location)
2351+ . await
2352+ . map_err ( |e| {
2353+ error ! ( error = ?e, msg = "Unable to add location with binding" ) ;
2354+ s3_error ! ( InternalError , "Unable to add location with binding" )
2355+ } ) ?;
2356+
2357+ trace ! ( "After finish" ) ;
2358+
2359+ let response = CopyObjectOutput {
2360+ copy_object_result : Some ( CopyObjectResult {
2361+ e_tag : md5hash,
2362+ checksum_sha256 : sha256hash,
2363+ last_modified : Some (
2364+ time:: OffsetDateTime :: from_unix_timestamp (
2365+ ( new_object. id . timestamp ( ) / 1000 ) as i64 ,
2366+ )
2367+ . map_err ( |_| {
2368+ error ! ( error = "Unable to parse timestamp" ) ;
2369+ s3_error ! ( InternalError , "Unable to parse timestamp" )
2370+ } ) ?
2371+ . into ( ) ,
2372+ ) ,
2373+ ..Default :: default ( )
2374+ } ) ,
2375+ ..Default :: default ( )
2376+ } ;
2377+ let mut resp = S3Response :: new ( response) ;
2378+ if let Some ( headers) = headers {
2379+ for ( k, v) in headers {
2380+ resp. headers . insert (
2381+ HeaderName :: from_bytes ( k. as_bytes ( ) )
2382+ . map_err ( |_| s3_error ! ( InternalError , "Unable to parse header name" ) ) ?,
2383+ HeaderValue :: from_str ( & v)
2384+ . map_err ( |_| s3_error ! ( InternalError , "Unable to parse header value" ) ) ?,
2385+ ) ;
2386+ }
2387+ }
2388+
2389+ trace ! ( "Respond" ) ;
2390+ Ok ( resp)
2391+ }
21292392}
0 commit comments