@@ -341,30 +341,81 @@ pub struct ConvertedLayerInfo {
341341 pub media_type : oci_spec:: image:: MediaType ,
342342}
343343
344- // Consumes an iterable and tries to convert it to a fixed-size array. Returns Ok([T; N]) if the
345- // number of items in the iterable was correct, else an error describing the mismatch.
346- fn fixed_from_iterable < T , const N : usize > (
347- iterable : impl IntoIterator < IntoIter : FusedIterator , Item = T > ,
348- ) -> Result < [ T ; N ] > {
349- let mut iter = iterable. into_iter ( ) ;
350- // We make use of the fact that [_; N].map() returns [_; N]. That makes this a bit more
351- // awkward than it would otherwise be, but it's not too bad.
352- let collected = [ ( ) ; N ] . map ( |_| iter. next ( ) ) ;
353- // Count the Some() in `collected` plus leftovers in the iter.
354- let actual = collected. iter ( ) . flatten ( ) . count ( ) + iter. count ( ) ;
355- if actual == N {
356- // SAFETY: This is a fused iter, so all N items are in our array
357- Ok ( collected. map ( Option :: unwrap) )
358- } else {
359- let type_name = std:: any:: type_name :: < T > ( ) ;
360- let basename = type_name
361- . rsplit_once ( "::" )
362- . map ( |( _path, name) | name)
363- . unwrap_or ( type_name) ;
364-
365- Err ( Error :: Other (
366- format ! ( "Expected {N} {basename} but got {actual}" ) . into ( ) ,
367- ) )
344+ /// A single fd; requires invoking FinishPipe
345+ #[ derive( Debug ) ]
346+ struct FinishPipe {
347+ pipeid : PipeId ,
348+ datafd : OwnedFd ,
349+ }
350+
351+ /// There is a data FD and an error FD. The error FD will be JSON.
352+ #[ derive( Debug ) ]
353+ struct DualFds {
354+ datafd : OwnedFd ,
355+ errfd : OwnedFd ,
356+ }
357+
358+ /// Helper trait for parsing the pipeid and/or file descriptors of a reply
359+ trait FromReplyFds : Send + ' static
360+ where
361+ Self : Sized ,
362+ {
363+ fn from_reply (
364+ iterable : impl IntoIterator < IntoIter : FusedIterator , Item = OwnedFd > ,
365+ pipeid : u32 ,
366+ ) -> Result < Self > ;
367+ }
368+
369+ /// No file descriptors or pipeid expected
370+ impl FromReplyFds for ( ) {
371+ fn from_reply ( fds : impl IntoIterator < Item = OwnedFd > , pipeid : u32 ) -> Result < Self > {
372+ if fds. into_iter ( ) . next ( ) . is_some ( ) {
373+ return Err ( Error :: Other ( "expected no fds" . into ( ) ) ) ;
374+ }
375+ if pipeid != 0 {
376+ return Err ( Error :: Other ( "unexpected pipeid" . into ( ) ) ) ;
377+ }
378+ Ok ( ( ) )
379+ }
380+ }
381+
382+ /// A FinishPipe instance
383+ impl FromReplyFds for FinishPipe {
384+ fn from_reply ( fds : impl IntoIterator < Item = OwnedFd > , pipeid : u32 ) -> Result < Self > {
385+ let mut fds = fds. into_iter ( ) ;
386+ let Some ( first_fd) = fds. next ( ) else {
387+ return Err ( Error :: Other ( "Expected fd for FinishPipe" . into ( ) ) ) ;
388+ } ;
389+ if fds. next ( ) . is_some ( ) {
390+ return Err ( Error :: Other ( "More than one fd for FinishPipe" . into ( ) ) ) ;
391+ }
392+ let Some ( pipeid) = PipeId :: try_new ( pipeid) else {
393+ return Err ( Error :: Other ( "Expected pipeid for FinishPipe" . into ( ) ) ) ;
394+ } ;
395+ Ok ( Self {
396+ pipeid,
397+ datafd : first_fd,
398+ } )
399+ }
400+ }
401+
402+ /// A DualFds instance
403+ impl FromReplyFds for DualFds {
404+ fn from_reply ( fds : impl IntoIterator < Item = OwnedFd > , pipeid : u32 ) -> Result < Self > {
405+ let mut fds = fds. into_iter ( ) ;
406+ let Some ( datafd) = fds. next ( ) else {
407+ return Err ( Error :: Other ( "Expected data fd for DualFds" . into ( ) ) ) ;
408+ } ;
409+ let Some ( errfd) = fds. next ( ) else {
410+ return Err ( Error :: Other ( "Expected err fd for DualFds" . into ( ) ) ) ;
411+ } ;
412+ if fds. next ( ) . is_some ( ) {
413+ return Err ( Error :: Other ( "More than two fds for DualFds" . into ( ) ) ) ;
414+ }
415+ if pipeid != 0 {
416+ return Err ( Error :: Other ( "Unexpected pipeid with DualFds" . into ( ) ) ) ;
417+ }
418+ Ok ( Self { datafd, errfd } )
368419 }
369420}
370421
@@ -404,7 +455,7 @@ impl ImageProxy {
404455 } ;
405456
406457 // Verify semantic version
407- let ( protover, [ ] , [ ] ) : ( String , _ , _ ) = r. impl_request ( "Initialize" , [ ( ) ; 0 ] ) . await ?;
458+ let ( protover, _ ) : ( String , ( ) ) = r. impl_request ( "Initialize" , [ ( ) ; 0 ] ) . await ?;
408459 tracing:: debug!( "Remote protocol version: {protover}" ) ;
409460 let protover = semver:: Version :: parse ( protover. as_str ( ) ) ?;
410461 // Previously we had a feature to opt-in to requiring newer versions using `if cfg!()`.
@@ -420,14 +471,10 @@ impl ImageProxy {
420471 Ok ( r)
421472 }
422473
423- async fn impl_request_raw <
424- T : serde:: de:: DeserializeOwned + Send + ' static ,
425- const N : usize ,
426- const M : usize ,
427- > (
474+ async fn impl_request_raw < T : serde:: de:: DeserializeOwned + Send + ' static , F : FromReplyFds > (
428475 sockfd : Arc < Mutex < OwnedFd > > ,
429476 req : Request ,
430- ) -> Result < ( T , [ OwnedFd ; N ] , [ PipeId ; M ] ) > {
477+ ) -> Result < ( T , F ) > {
431478 tracing:: trace!( "sending request {}" , req. method. as_str( ) ) ;
432479 // TODO: Investigate https://crates.io/crates/uds for SOCK_SEQPACKET tokio
433480 let r = tokio:: task:: spawn_blocking ( move || {
@@ -463,11 +510,8 @@ impl ImageProxy {
463510 error : reply. error . into ( ) ,
464511 } ) ;
465512 }
466- Ok ( (
467- serde_json:: from_value ( reply. value ) ?,
468- fixed_from_iterable ( fdret) ?,
469- fixed_from_iterable ( PipeId :: try_new ( reply. pipeid ) ) ?,
470- ) )
513+ let fds = FromReplyFds :: from_reply ( fdret, reply. pipeid ) ?;
514+ Ok ( ( serde_json:: from_value ( reply. value ) ?, fds) )
471515 } )
472516 . await
473517 . map_err ( |e| Error :: Other ( e. to_string ( ) . into ( ) ) ) ??;
@@ -476,15 +520,11 @@ impl ImageProxy {
476520 }
477521
478522 #[ instrument( skip( args) ) ]
479- async fn impl_request <
480- T : serde:: de:: DeserializeOwned + Send + ' static ,
481- const N : usize ,
482- const M : usize ,
483- > (
523+ async fn impl_request < T : serde:: de:: DeserializeOwned + Send + ' static , F : FromReplyFds > (
484524 & self ,
485525 method : & str ,
486526 args : impl IntoIterator < Item = impl Into < serde_json:: Value > > ,
487- ) -> Result < ( T , [ OwnedFd ; N ] , [ PipeId ; M ] ) > {
527+ ) -> Result < ( T , F ) > {
488528 let req = Self :: impl_request_raw ( Arc :: clone ( & self . sockfd ) , Request :: new ( method, args) ) ;
489529 let mut childwait = self . childwait . lock ( ) . await ;
490530 tokio:: select! {
@@ -500,21 +540,21 @@ impl ImageProxy {
500540 #[ instrument]
501541 async fn finish_pipe ( & self , pipeid : PipeId ) -> Result < ( ) > {
502542 tracing:: debug!( "closing pipe" ) ;
503- let ( r, [ ] , [ ] ) = self . impl_request ( "FinishPipe" , [ pipeid. 0 . get ( ) ] ) . await ?;
543+ let ( r, ( ) ) = self . impl_request ( "FinishPipe" , [ pipeid. 0 . get ( ) ] ) . await ?;
504544 Ok ( r)
505545 }
506546
507547 #[ instrument]
508548 pub async fn open_image ( & self , imgref : & str ) -> Result < OpenedImage > {
509549 tracing:: debug!( "opening image" ) ;
510- let ( imgid, [ ] , [ ] ) = self . impl_request ( "OpenImage" , [ imgref] ) . await ?;
550+ let ( imgid, ( ) ) = self . impl_request ( "OpenImage" , [ imgref] ) . await ?;
511551 Ok ( OpenedImage ( imgid) )
512552 }
513553
514554 #[ instrument]
515555 pub async fn open_image_optional ( & self , imgref : & str ) -> Result < Option < OpenedImage > > {
516556 tracing:: debug!( "opening image" ) ;
517- let ( imgid, [ ] , [ ] ) = self . impl_request ( "OpenImageOptional" , [ imgref] ) . await ?;
557+ let ( imgid, ( ) ) = self . impl_request ( "OpenImageOptional" , [ imgref] ) . await ?;
518558 if imgid == 0 {
519559 Ok ( None )
520560 } else {
@@ -525,16 +565,16 @@ impl ImageProxy {
525565 #[ instrument]
526566 pub async fn close_image ( & self , img : & OpenedImage ) -> Result < ( ) > {
527567 tracing:: debug!( "closing image" ) ;
528- let ( r, [ ] , [ ] ) = self . impl_request ( "CloseImage" , [ img. 0 ] ) . await ?;
568+ let ( r, ( ) ) = self . impl_request ( "CloseImage" , [ img. 0 ] ) . await ?;
529569 Ok ( r)
530570 }
531571
532- async fn read_all_fd ( & self , datafd : OwnedFd , pipeid : PipeId ) -> Result < Vec < u8 > > {
533- let fd = tokio:: fs:: File :: from_std ( std:: fs:: File :: from ( datafd) ) ;
572+ async fn read_finish_pipe ( & self , pipe : FinishPipe ) -> Result < Vec < u8 > > {
573+ let fd = tokio:: fs:: File :: from_std ( std:: fs:: File :: from ( pipe . datafd ) ) ;
534574 let mut fd = tokio:: io:: BufReader :: new ( fd) ;
535575 let mut r = Vec :: new ( ) ;
536576 let reader = fd. read_to_end ( & mut r) ;
537- let ( nbytes, finish) = tokio:: join!( reader, self . finish_pipe( pipeid) ) ;
577+ let ( nbytes, finish) = tokio:: join!( reader, self . finish_pipe( pipe . pipeid) ) ;
538578 finish?;
539579 assert_eq ! ( nbytes?, r. len( ) ) ;
540580 Ok ( r)
@@ -544,8 +584,8 @@ impl ImageProxy {
544584 /// The original digest of the unconverted manifest is also returned.
545585 /// For more information on OCI manifests, see <https://github.com/opencontainers/image-spec/blob/main/manifest.md>
546586 pub async fn fetch_manifest_raw_oci ( & self , img : & OpenedImage ) -> Result < ( String , Vec < u8 > ) > {
547- let ( digest, [ datafd ] , [ pipeid ] ) = self . impl_request ( "GetManifest" , [ img. 0 ] ) . await ?;
548- Ok ( ( digest, self . read_all_fd ( datafd , pipeid ) . await ?) )
587+ let ( digest, pipefd ) = self . impl_request ( "GetManifest" , [ img. 0 ] ) . await ?;
588+ Ok ( ( digest, self . read_finish_pipe ( pipefd ) . await ?) )
549589 }
550590
551591 /// Fetch the manifest.
@@ -562,8 +602,8 @@ impl ImageProxy {
562602 /// Fetch the config.
563603 /// For more information on OCI config, see <https://github.com/opencontainers/image-spec/blob/main/config.md>
564604 pub async fn fetch_config_raw ( & self , img : & OpenedImage ) -> Result < Vec < u8 > > {
565- let ( ( ) , [ datafd ] , [ pipeid ] ) = self . impl_request ( "GetFullConfig" , [ img. 0 ] ) . await ?;
566- self . read_all_fd ( datafd , pipeid ) . await
605+ let ( ( ) , pipe ) = self . impl_request ( "GetFullConfig" , [ img. 0 ] ) . await ?;
606+ self . read_finish_pipe ( pipe ) . await
567607 }
568608
569609 /// Fetch the config.
@@ -600,11 +640,11 @@ impl ImageProxy {
600640 tracing:: debug!( "fetching blob" ) ;
601641 let args: Vec < serde_json:: Value > =
602642 vec ! [ img. 0 . into( ) , digest. to_string( ) . into( ) , size. into( ) ] ;
603- let ( bloblen, [ datafd ] , [ pipeid ] ) = self . impl_request ( "GetBlob" , args) . await ?;
643+ let ( bloblen, pipe ) : ( u64 , FinishPipe ) = self . impl_request ( "GetBlob" , args) . await ?;
604644 let _: u64 = bloblen;
605- let fd = tokio:: fs:: File :: from_std ( std:: fs:: File :: from ( datafd) ) ;
645+ let fd = tokio:: fs:: File :: from_std ( std:: fs:: File :: from ( pipe . datafd ) ) ;
606646 let fd = tokio:: io:: BufReader :: new ( fd) ;
607- let finish = Box :: pin ( self . finish_pipe ( pipeid) ) ;
647+ let finish = Box :: pin ( self . finish_pipe ( pipe . pipeid ) ) ;
608648 Ok ( ( fd, finish) )
609649 }
610650
@@ -649,9 +689,9 @@ impl ImageProxy {
649689 ) > {
650690 tracing:: debug!( "fetching blob" ) ;
651691 let args: Vec < serde_json:: Value > = vec ! [ img. 0 . into( ) , digest. to_string( ) . into( ) ] ;
652- let ( bloblen, [ datafd , errfd ] , [ ] ) = self . impl_request ( "GetRawBlob" , args) . await ?;
653- let fd = tokio:: fs:: File :: from_std ( std:: fs:: File :: from ( datafd) ) ;
654- let err = Self :: read_blob_error ( errfd) . boxed ( ) ;
692+ let ( bloblen, fds ) : ( u64 , DualFds ) = self . impl_request ( "GetRawBlob" , args) . await ?;
693+ let fd = tokio:: fs:: File :: from_std ( std:: fs:: File :: from ( fds . datafd ) ) ;
694+ let err = Self :: read_blob_error ( fds . errfd ) . boxed ( ) ;
655695 Ok ( ( bloblen, fd, err) )
656696 }
657697
@@ -677,14 +717,14 @@ impl ImageProxy {
677717 ) -> Result < Option < Vec < ConvertedLayerInfo > > > {
678718 tracing:: debug!( "Getting layer info" ) ;
679719 if layer_info_piped_proto_version ( ) . matches ( & self . protover ) {
680- let ( ( ) , [ datafd ] , [ pipeid ] ) = self . impl_request ( "GetLayerInfoPiped" , [ img. 0 ] ) . await ?;
681- let buf = self . read_all_fd ( datafd , pipeid ) . await ?;
720+ let ( ( ) , pipe ) = self . impl_request ( "GetLayerInfoPiped" , [ img. 0 ] ) . await ?;
721+ let buf = self . read_finish_pipe ( pipe ) . await ?;
682722 return Ok ( Some ( serde_json:: from_slice ( & buf) ?) ) ;
683723 }
684724 if !layer_info_proto_version ( ) . matches ( & self . protover ) {
685725 return Ok ( None ) ;
686726 }
687- let ( layers, [ ] , [ ] ) = self . impl_request ( "GetLayerInfo" , [ img. 0 ] ) . await ?;
727+ let ( layers, ( ) ) = self . impl_request ( "GetLayerInfo" , [ img. 0 ] ) . await ?;
688728 Ok ( Some ( layers) )
689729 }
690730
@@ -892,31 +932,15 @@ mod tests {
892932 memfd_create ( c"test-fd" , MemfdFlags :: CLOEXEC ) . unwrap ( )
893933 }
894934
895- fn fds_and_pipeid < const N : usize , const M : usize > (
896- fds : impl IntoIterator < IntoIter : FusedIterator , Item = OwnedFd > ,
897- pipeid : u32 ,
898- ) -> Result < ( [ OwnedFd ; N ] , [ PipeId ; M ] ) > {
899- Ok ( (
900- fixed_from_iterable ( fds) ?,
901- fixed_from_iterable ( PipeId :: try_new ( pipeid) ) ?,
902- ) )
903- }
904-
905- #[ test]
906- fn test_new_from_raw_values_no_fds_no_pipeid ( ) {
907- let ( [ ] , [ ] ) = fds_and_pipeid ( [ ] , 0 ) . unwrap ( ) ;
908- }
909-
910935 #[ test]
911936 fn test_new_from_raw_values_finish_pipe ( ) {
912937 let datafd = create_dummy_fd ( ) ;
913938 // Keep a raw fd to compare later, as fds_and_pipeid consumes datafd
914939 let raw_datafd_val = datafd. as_raw_fd ( ) ;
915940 let fds = vec ! [ datafd] ;
916- let pipeid = PipeId :: try_new ( 1 ) . unwrap ( ) ;
917- let ( [ res_datafd] , [ res_pipeid] ) = fds_and_pipeid ( fds, pipeid. 0 . get ( ) ) . unwrap ( ) ;
918- assert_eq ! ( res_pipeid, pipeid) ;
919- assert_eq ! ( res_datafd. as_raw_fd( ) , raw_datafd_val) ;
941+ let v = FinishPipe :: from_reply ( fds, 1 ) . unwrap ( ) ;
942+ assert_eq ! ( v. pipeid. 0 . get( ) , 1 ) ;
943+ assert_eq ! ( v. datafd. as_raw_fd( ) , raw_datafd_val) ;
920944 }
921945
922946 #[ test]
@@ -926,18 +950,18 @@ mod tests {
926950 let raw_datafd_val = datafd. as_raw_fd ( ) ;
927951 let raw_errfd_val = errfd. as_raw_fd ( ) ;
928952 let fds = vec ! [ datafd, errfd] ;
929- let ( [ res_datafd , res_errfd ] , [ ] ) = fds_and_pipeid ( fds, 0 ) . unwrap ( ) ;
930- assert_eq ! ( res_datafd . as_raw_fd( ) , raw_datafd_val) ;
931- assert_eq ! ( res_errfd . as_raw_fd( ) , raw_errfd_val) ;
953+ let v = DualFds :: from_reply ( fds, 0 ) . unwrap ( ) ;
954+ assert_eq ! ( v . datafd . as_raw_fd( ) , raw_datafd_val) ;
955+ assert_eq ! ( v . errfd . as_raw_fd( ) , raw_errfd_val) ;
932956 }
933957
934958 #[ test]
935959 fn test_new_from_raw_values_error_too_many_fds ( ) {
936960 let fds = vec ! [ create_dummy_fd( ) , create_dummy_fd( ) , create_dummy_fd( ) ] ;
937- match fds_and_pipeid ( fds, 0 ) {
938- Ok ( ( [ datafd , errfd ] , [ ] ) ) => unreachable ! ( "{datafd:?} {errfd :?}" ) ,
961+ match DualFds :: from_reply ( fds, 0 ) {
962+ Ok ( v ) => unreachable ! ( "{v :?}" ) ,
939963 Err ( Error :: Other ( msg) ) => {
940- assert_eq ! ( msg. as_ref( ) , "Expected 2 OwnedFd but got 3 " )
964+ assert_eq ! ( msg. as_ref( ) , "More than two fds for DualFds " )
941965 }
942966 Err ( other) => unreachable ! ( "{other}" ) ,
943967 }
@@ -946,10 +970,10 @@ mod tests {
946970 #[ test]
947971 fn test_new_from_raw_values_error_fd_with_zero_pipeid ( ) {
948972 let fds = vec ! [ create_dummy_fd( ) ] ;
949- match fds_and_pipeid ( fds, 0 ) {
950- Ok ( ( [ datafd ] , [ pipeid ] ) ) => unreachable ! ( "{datafd:?} {pipeid :?}" ) ,
973+ match FinishPipe :: from_reply ( fds, 0 ) {
974+ Ok ( v ) => unreachable ! ( "{v :?}" ) ,
951975 Err ( Error :: Other ( msg) ) => {
952- assert_eq ! ( msg. as_ref( ) , "Expected 1 PipeId but got 0 " )
976+ assert_eq ! ( msg. as_ref( ) , "Expected pipeid for FinishPipe " )
953977 }
954978 Err ( other) => unreachable ! ( "{other}" ) ,
955979 }
@@ -958,10 +982,10 @@ mod tests {
958982 #[ test]
959983 fn test_new_from_raw_values_error_pipeid_with_both_fds ( ) {
960984 let fds = vec ! [ create_dummy_fd( ) , create_dummy_fd( ) ] ;
961- match fds_and_pipeid ( fds, 1 ) {
962- Ok ( ( [ datafd , errfd ] , [ ] ) ) => unreachable ! ( "{datafd:?} {errfd :?}" ) ,
985+ match DualFds :: from_reply ( fds, 1 ) {
986+ Ok ( v ) => unreachable ! ( "{v :?}" ) ,
963987 Err ( Error :: Other ( msg) ) => {
964- assert_eq ! ( msg. as_ref( ) , "Expected 0 PipeId but got 1 " )
988+ assert_eq ! ( msg. as_ref( ) , "Unexpected pipeid with DualFds " )
965989 }
966990 Err ( other) => unreachable ! ( "{other}" ) ,
967991 }
@@ -970,10 +994,10 @@ mod tests {
970994 #[ test]
971995 fn test_new_from_raw_values_error_no_fd_with_pipeid ( ) {
972996 let fds: Vec < OwnedFd > = vec ! [ ] ;
973- match fds_and_pipeid ( fds, 1 ) {
974- Ok ( ( [ datafd ] , [ pipeid ] ) ) => unreachable ! ( "{datafd:?} {pipeid :?}" ) ,
997+ match FinishPipe :: from_reply ( fds, 1 ) {
998+ Ok ( v ) => unreachable ! ( "{v :?}" ) ,
975999 Err ( Error :: Other ( msg) ) => {
976- assert_eq ! ( msg. as_ref( ) , "Expected 1 OwnedFd but got 0 " )
1000+ assert_eq ! ( msg. as_ref( ) , "Expected fd for FinishPipe " )
9771001 }
9781002 Err ( other) => unreachable ! ( "{other}" ) ,
9791003 }
0 commit comments