@@ -68,14 +68,15 @@ create(Metadata, Files, Config) ->
6868 tarball_max_size := TarballMaxSize ,
6969 tarball_max_uncompressed_size := TarballMaxUncompressedSize
7070 } = Config ,
71+ FilesRoot = maps :get (tarball_files_root , Config , undefined ),
7172
7273 MetadataBinary = encode_metadata (Metadata ),
7374
7475 case valid_size (MetadataBinary , ? MAX_METADATA_SIZE ) of
7576 false ->
7677 {error , {tarball , {file_too_big , " metadata.config" }}};
7778 true ->
78- case validate_create_files (Files ) of
79+ case validate_create_files (Files , FilesRoot ) of
7980 ok ->
8081 ContentsTarball = create_memory_tarball (Files ),
8182 ContentsTarballCompressed = gzip (ContentsTarball ),
@@ -139,8 +140,9 @@ create_docs(Files, Config) ->
139140 docs_tarball_max_size := TarballMaxSize ,
140141 docs_tarball_max_uncompressed_size := TarballMaxUncompressedSize
141142 } = Config ,
143+ FilesRoot = maps :get (tarball_files_root , Config , undefined ),
142144
143- case validate_create_files (Files ) of
145+ case validate_create_files (Files , FilesRoot ) of
144146 ok ->
145147 UncompressedTarball = create_memory_tarball (Files ),
146148
@@ -648,23 +650,23 @@ guess_build_tools(Metadata) ->
648650% %====================================================================
649651
650652% % @private
651- validate_create_files (Files ) when is_list (Files ) ->
652- validate_create_files (Files , ok ).
653+ validate_create_files (Files , FilesRoot ) when is_list (Files ) ->
654+ validate_create_files (Files , FilesRoot , ok ).
653655
654- validate_create_files (_Files , {error , _ } = Error ) ->
656+ validate_create_files (_Files , _FilesRoot , {error , _ } = Error ) ->
655657 Error ;
656- validate_create_files ([], ok ) ->
658+ validate_create_files ([], _FilesRoot , ok ) ->
657659 ok ;
658- validate_create_files ([File | Rest ], ok ) ->
659- validate_create_files (Rest , validate_create_file (File )).
660+ validate_create_files ([File | Rest ], FilesRoot , ok ) ->
661+ validate_create_files (Rest , FilesRoot , validate_create_file (File , FilesRoot )).
660662
661- validate_create_file ({Filename , Contents }) when is_list (Filename ), is_binary (Contents ) ->
663+ validate_create_file ({Filename , Contents }, _FilesRoot ) when is_list (Filename ), is_binary (Contents ) ->
662664 validate_archive_path (Filename );
663- validate_create_file (Filename ) when is_list (Filename ) ->
664- validate_create_file ({Filename , Filename });
665- validate_create_file ({Filename , AbsFilename }) when is_list (Filename ), is_list (AbsFilename ) ->
665+ validate_create_file (Filename , FilesRoot ) when is_list (Filename ) ->
666+ validate_create_file ({Filename , Filename }, FilesRoot );
667+ validate_create_file ({Filename , AbsFilename }, FilesRoot ) when is_list (Filename ), is_list (AbsFilename ) ->
666668 case validate_archive_path (Filename ) of
667- ok -> validate_source_file (Filename , AbsFilename );
669+ ok -> validate_source_file (Filename , AbsFilename , FilesRoot );
668670 {error , _ } = Error -> Error
669671 end .
670672
@@ -674,13 +676,13 @@ validate_archive_path(Filename) ->
674676 true -> ok
675677 end .
676678
677- validate_source_file (ArchiveName , SourcePath ) ->
679+ validate_source_file (ArchiveName , SourcePath , FilesRoot ) ->
678680 case file :read_link_info (SourcePath , []) of
679681 {ok , # file_info {type = Type }} when Type =:= regular ; Type =:= directory ->
680- ok ;
682+ validate_source_root ( ArchiveName , SourcePath , FilesRoot ) ;
681683 {ok , # file_info {type = symlink }} ->
682684 {ok , LinkTarget } = file :read_link (SourcePath ),
683- ResolvedTarget = filename : join ( filename : dirname (ArchiveName ), LinkTarget ),
685+ ResolvedTarget = archive_join ( archive_dirname (ArchiveName ), LinkTarget ),
684686 case safe_relative_archive_path (ResolvedTarget ) of
685687 false -> {error , {tarball , {unsafe_symlink , ArchiveName , LinkTarget }}};
686688 true -> ok
@@ -691,10 +693,18 @@ validate_source_file(ArchiveName, SourcePath) ->
691693 ok
692694 end .
693695
696+ validate_source_root (_ArchiveName , _SourcePath , undefined ) ->
697+ ok ;
698+ validate_source_root (ArchiveName , SourcePath , FilesRoot ) ->
699+ case filelib :safe_relative_path (SourcePath , filename :absname (FilesRoot )) of
700+ unsafe -> {error , {tarball , {unsafe_path , ArchiveName }}};
701+ _ -> ok
702+ end .
703+
694704safe_relative_archive_path (Path ) ->
695- case filename : pathtype (Path ) of
696- relative -> safe_relative_archive_path ( filename : split ( Path ), []) ;
697- _ -> false
705+ case archive_path_absolute ( Path ) orelse archive_path_drive (Path ) of
706+ true -> false ;
707+ false -> safe_relative_archive_path ( archive_path_split ( Path ), [])
698708 end .
699709
700710safe_relative_archive_path ([], _Acc ) ->
@@ -708,6 +718,47 @@ safe_relative_archive_path([".." | Rest], [_ | Acc]) ->
708718safe_relative_archive_path ([_Part | Rest ], Acc ) ->
709719 safe_relative_archive_path (Rest , [ok | Acc ]).
710720
721+ archive_path_absolute ([$/ | _Rest ]) ->
722+ true ;
723+ archive_path_absolute ([$\\ | _Rest ]) ->
724+ true ;
725+ archive_path_absolute (_Path ) ->
726+ false .
727+
728+ archive_path_drive ([Drive , $: | _Rest ]) when
729+ Drive >= $a , Drive =< $z ;
730+ Drive >= $A , Drive =< $Z
731+ ->
732+ true ;
733+ archive_path_drive (_Path ) ->
734+ false .
735+
736+ archive_path_split (Path ) ->
737+ string :tokens (Path , " /\\ " ).
738+
739+ archive_dirname (Path ) ->
740+ case archive_path_split (Path ) of
741+ [] -> " ." ;
742+ [_Name ] -> " ." ;
743+ Parts -> string :join (lists :droplast (Parts ), " /" )
744+ end .
745+
746+ archive_join (_Dir , Path ) when Path =:= [] ->
747+ Path ;
748+ archive_join (_Dir , Path = [$/ | _Rest ]) ->
749+ Path ;
750+ archive_join (_Dir , Path = [$\\ | _Rest ]) ->
751+ Path ;
752+ archive_join (_Dir , Path = [Drive , $: | _Rest ]) when
753+ Drive >= $a , Drive =< $z ;
754+ Drive >= $A , Drive =< $Z
755+ ->
756+ Path ;
757+ archive_join (" ." , Path ) ->
758+ Path ;
759+ archive_join (Dir , Path ) ->
760+ Dir ++ " /" ++ Path .
761+
711762% % @private
712763unpack_tarball (Source , memory , MaxSize ) ->
713764 case mix_hex_erl_tar :extract (Source , [memory , compressed , {max_size , MaxSize }]) of
0 commit comments