@@ -636,6 +636,52 @@ impl<'a, W: std::io::Write> OstreeTarWriter<'a, W> {
636636 . append_data ( & mut header, "var/tmp" , std:: io:: empty ( ) ) ?;
637637 Ok ( ( ) )
638638 }
639+
640+ fn write_parents_of (
641+ & mut self ,
642+ path : & Utf8Path ,
643+ cache : & mut HashSet < Utf8PathBuf > ,
644+ ) -> Result < ( ) > {
645+ let Some ( parent) = path. parent ( ) else {
646+ return Ok ( ( ) ) ;
647+ } ;
648+
649+ if parent. components ( ) . count ( ) == 0 {
650+ return Ok ( ( ) ) ;
651+ }
652+
653+ if cache. contains ( parent) {
654+ return Ok ( ( ) ) ;
655+ }
656+
657+ self . write_parents_of ( parent, cache) ?;
658+
659+ let inserted = cache. insert ( parent. to_owned ( ) ) ;
660+ debug_assert ! ( inserted) ;
661+
662+ let root = self
663+ . repo
664+ . read_commit ( & self . commit_checksum , gio:: Cancellable :: NONE ) ?
665+ . 0 ;
666+ let parent_file = root. resolve_relative_path ( unmap_path ( parent) . as_ref ( ) ) ;
667+ let queryattrs = "unix::*" ;
668+ let queryflags = gio:: FileQueryInfoFlags :: NOFOLLOW_SYMLINKS ;
669+ let stat = parent_file. query_info ( & queryattrs, queryflags, gio:: Cancellable :: NONE ) ?;
670+ let uid = stat. attribute_uint32 ( gio:: FILE_ATTRIBUTE_UNIX_UID ) ;
671+ let gid = stat. attribute_uint32 ( gio:: FILE_ATTRIBUTE_UNIX_GID ) ;
672+ let orig_mode = stat. attribute_uint32 ( gio:: FILE_ATTRIBUTE_UNIX_MODE ) ;
673+ let mode = self . filter_mode ( orig_mode) ;
674+
675+ let mut header = tar:: Header :: new_gnu ( ) ;
676+ header. set_entry_type ( tar:: EntryType :: Directory ) ;
677+ header. set_size ( 0 ) ;
678+ header. set_uid ( uid as u64 ) ;
679+ header. set_gid ( gid as u64 ) ;
680+ header. set_mode ( mode) ;
681+ self . out
682+ . append_data ( & mut header, parent, std:: io:: empty ( ) ) ?;
683+ Ok ( ( ) )
684+ }
639685}
640686
641687/// Recursively walk an OSTree commit and generate data into a `[tar::Builder]`
@@ -684,12 +730,17 @@ fn path_for_tar_v1(p: &Utf8Path) -> &Utf8Path {
684730fn write_chunk < W : std:: io:: Write > (
685731 writer : & mut OstreeTarWriter < W > ,
686732 chunk : chunking:: ChunkMapping ,
733+ create_parent_dirs : bool ,
687734) -> Result < ( ) > {
735+ let mut cache = std:: collections:: HashSet :: new ( ) ;
688736 for ( checksum, ( _size, paths) ) in chunk. into_iter ( ) {
689737 let ( objpath, h) = writer. append_content ( checksum. borrow ( ) ) ?;
690738 for path in paths. iter ( ) {
691739 let path = path_for_tar_v1 ( path) ;
692740 let h = h. clone ( ) ;
741+ if create_parent_dirs {
742+ writer. write_parents_of ( & path, & mut cache) ?;
743+ }
693744 writer. append_content_hardlink ( & objpath, h, path) ?;
694745 }
695746 }
@@ -702,13 +753,14 @@ pub(crate) fn export_chunk<W: std::io::Write>(
702753 commit : & str ,
703754 chunk : chunking:: ChunkMapping ,
704755 out : & mut tar:: Builder < W > ,
756+ create_parent_dirs : bool ,
705757) -> Result < ( ) > {
706758 // For chunking, we default to format version 1
707759 #[ allow( clippy:: needless_update) ]
708760 let opts = ExportOptions ;
709761 let writer = & mut OstreeTarWriter :: new ( repo, commit, out, opts) ?;
710762 writer. write_repo_structure ( ) ?;
711- write_chunk ( writer, chunk)
763+ write_chunk ( writer, chunk, create_parent_dirs )
712764}
713765
714766/// Output the last chunk in a chunking.
@@ -718,6 +770,7 @@ pub(crate) fn export_final_chunk<W: std::io::Write>(
718770 commit_checksum : & str ,
719771 remainder : chunking:: Chunk ,
720772 out : & mut tar:: Builder < W > ,
773+ create_parent_dirs : bool ,
721774) -> Result < ( ) > {
722775 let options = ExportOptions ;
723776 let writer = & mut OstreeTarWriter :: new ( repo, commit_checksum, out, options) ?;
@@ -726,7 +779,7 @@ pub(crate) fn export_final_chunk<W: std::io::Write>(
726779 writer. structure_only = true ;
727780 writer. write_commit ( ) ?;
728781 writer. structure_only = false ;
729- write_chunk ( writer, remainder. content )
782+ write_chunk ( writer, remainder. content , create_parent_dirs )
730783}
731784
732785/// Process an exported tar stream, and update the detached metadata.
0 commit comments