require 'zlib' require 'minitar' module PuppetForge class Tar class Mini SYMLINK_FLAGS = [2] VALID_TAR_FLAGS = (0..7) # @return [Hash{:symbol => Array}] a hash with file-category keys pointing to lists of filenames. def unpack(sourcefile, destdir) # directories need to be changed outside of the Minitar::unpack because directories don't have a :file_done action dirlist = [] file_lists = {} Zlib::GzipReader.open(sourcefile) do |reader| file_lists = validate_files(reader) Minitar.unpack(reader, destdir, file_lists[:valid]) do |action, name, stats| case action when :file_done FileUtils.chmod('u+rw,g+r,a-st', "#{destdir}/#{name}") when :file_start validate_entry(destdir, name) when :dir validate_entry(destdir, name) dirlist << "#{destdir}/#{name}" end end end dirlist.each {|d| File.chmod(0755, d)} file_lists end def pack(sourcedir, destfile) Zlib::GzipWriter.open(destfile) do |writer| Minitar.pack(sourcedir, writer) end end private # Categorize all the files in tarfile as :valid, :invalid, or :symlink. # # :invalid files include 'x' and 'g' flags from the PAX standard but and any other non-standard tar flags. # tar format info: http://pic.dhe.ibm.com/infocenter/zos/v1r13/index.jsp?topic=%2Fcom.ibm.zos.r13.bpxa500%2Ftaf.htm # pax format info: http://pic.dhe.ibm.com/infocenter/zos/v1r13/index.jsp?topic=%2Fcom.ibm.zos.r13.bpxa500%2Fpxarchfm.htm # :symlinks are not supported in Puppet modules # :valid files are any of those that can be used in modules # @param tarfile name of the tarfile # @return [Hash{:symbol => Array}] a hash with file-category keys pointing to lists of filenames. def validate_files(tarfile) file_lists = {:valid => [], :invalid => [], :symlinks => []} Minitar.open(tarfile).each do |entry| flag = entry.typeflag if flag.nil? || flag =~ /[[:digit:]]/ && SYMLINK_FLAGS.include?(flag.to_i) file_lists[:symlinks] << entry.full_name elsif flag.nil? || flag =~ /[[:digit:]]/ && VALID_TAR_FLAGS.include?(flag.to_i) file_lists[:valid] << entry.full_name else file_lists[:invalid] << entry.full_name end end file_lists end def validate_entry(destdir, path) if Pathname.new(path).absolute? raise PuppetForge::InvalidPathInPackageError, :entry_path => path, :directory => destdir end path = File.expand_path File.join(destdir, path) if path !~ /\A#{Regexp.escape destdir}/ raise PuppetForge::InvalidPathInPackageError, :entry_path => path, :directory => destdir end end end end end