class Zip::Entry
Constants
- DEFLATED
- EFS
Language encoding flag (
EFS
) bit- STORED
Attributes
comment[RW]
compressed_size[RW]
compression_method[RW]
crc[RW]
dirty[RW]
external_file_attributes[RW]
extra[RW]
follow_symlinks[RW]
fstype[RW]
gp_flags[RW]
header_signature[RW]
internal_file_attributes[RW]
local_header_offset[RW]
name[RW]
restore_ownership[RW]
restore_permissions[RW]
restore_times[RW]
size[RW]
unix_gid[RW]
unix_perms[RW]
unix_uid[RW]
zipfile[RW]
Public Class Methods
new(*args)
click to toggle source
# File lib/zip/entry.rb, line 55 def initialize(*args) name = args[1] || '' check_name(name) set_default_vars_values @fstype = ::Zip::RUNNING_ON_WINDOWS ? ::Zip::FSTYPE_FAT : ::Zip::FSTYPE_UNIX @zipfile = args[0] || '' @name = name @comment = args[2] || '' @extra = args[3] || '' @compressed_size = args[4] || 0 @crc = args[5] || 0 @compression_method = args[6] || ::Zip::Entry::DEFLATED @size = args[7] || 0 @time = args[8] || ::Zip::DOSTime.now @ftype = name_is_directory? ? :directory : :file @extra = ::Zip::ExtraField.new(@extra.to_s) unless @extra.kind_of?(::Zip::ExtraField) end
read_local_entry(io)
click to toggle source
# File lib/zip/entry.rb, line 221 def read_local_entry(io) entry = new(io) entry.read_local_entry(io) entry rescue Error nil end
Public Instance Methods
<=>(other)
click to toggle source
# File lib/zip/entry.rb, line 512 def <=>(other) to_s <=> other.to_s end
==(other)
click to toggle source
# File lib/zip/entry.rb, line 502 def ==(other) return false unless other.class == self.class # Compares contents of local entry and exposed fields keys_equal = %w[compression_method crc compressed_size size name extra filepath].all? do |k| other.__send__(k.to_sym) == __send__(k.to_sym) end keys_equal && time.dos_equals(other.time) end
check_c_dir_entry_comment_size()
click to toggle source
# File lib/zip/entry.rb, line 376 def check_c_dir_entry_comment_size return if @comment && @comment.bytesize == @comment_length raise ::Zip::Error, 'Truncated cdir zip entry header' end
check_c_dir_entry_signature()
click to toggle source
# File lib/zip/entry.rb, line 370 def check_c_dir_entry_signature return if header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE raise Error, "Zip local header magic not found at location '#{local_header_offset}'" end
check_c_dir_entry_static_header_length(buf)
click to toggle source
# File lib/zip/entry.rb, line 364 def check_c_dir_entry_static_header_length(buf) return if buf.bytesize == ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH raise Error, 'Premature end of file. Not enough data for zip cdir entry header' end
check_name(name)
click to toggle source
# File lib/zip/entry.rb, line 49 def check_name(name) return unless name.start_with?('/') raise ::Zip::EntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /" end
clean_up()
click to toggle source
# File lib/zip/entry.rb, line 603 def clean_up # By default, do nothing end
comment_size()
click to toggle source
# File lib/zip/entry.rb, line 147 def comment_size @comment ? @comment.bytesize : 0 end
encrypted?()
click to toggle source
# File lib/zip/entry.rb, line 76 def encrypted? gp_flags & 1 == 1 end
extra_size()
click to toggle source
# File lib/zip/entry.rb, line 143 def extra_size @extra ? @extra.local_size : 0 end
extract(dest_path = nil, &block)
click to toggle source
Extracts entry to file dest_path (defaults to @name). NB: The caller is responsible for making sure dest_path is safe, if it is passed.
# File lib/zip/entry.rb, line 176 def extract(dest_path = nil, &block) if dest_path.nil? && !name_safe? warn "WARNING: skipped '#{@name}' as unsafe." return self end dest_path ||= @name block ||= proc { ::Zip.on_exists_proc } raise "unknown file type #{inspect}" unless directory? || file? || symlink? __send__("create_#{@ftype}", dest_path, &block) self end
file_type_is?(type)
click to toggle source
# File lib/zip/entry.rb, line 106 def file_type_is?(type) raise InternalError, "current filetype is unknown: #{inspect}" unless @ftype @ftype == type end
get_input_stream() { |NullInputStream| ... }
click to toggle source
Returns an IO like object for the given ZipEntry. Warning: may behave weird with symlinks.
# File lib/zip/entry.rb, line 518 def get_input_stream(&block) if @ftype == :directory yield ::Zip::NullInputStream if block_given? ::Zip::NullInputStream elsif @filepath case @ftype when :file ::File.open(@filepath, 'rb', &block) when :symlink linkpath = ::File.readlink(@filepath) stringio = ::StringIO.new(linkpath) yield(stringio) if block_given? stringio else raise "unknown @file_type #{@ftype}" end else zis = ::Zip::InputStream.new(@zipfile, local_header_offset) zis.instance_variable_set(:@complete_entry, self) zis.get_next_entry if block_given? begin yield(zis) ensure zis.close end else zis end end end
get_raw_input_stream() { |zipfile| ... }
click to toggle source
# File lib/zip/entry.rb, line 595 def get_raw_input_stream(&block) if @zipfile.respond_to?(:seek) && @zipfile.respond_to?(:read) yield @zipfile else ::File.open(@zipfile, 'rb', &block) end end
incomplete?()
click to toggle source
# File lib/zip/entry.rb, line 80 def incomplete? gp_flags & 8 == 8 end
name_safe?()
click to toggle source
Is the name a relative path, free of ‘..` patterns that could lead to path traversal attacks? This does NOT handle symlinks; if the path contains symlinks, this check is NOT enough to guarantee safety.
# File lib/zip/entry.rb, line 126 def name_safe? cleanpath = Pathname.new(@name).cleanpath return false unless cleanpath.relative? root = ::File::SEPARATOR naive_expanded_path = ::File.join(root, cleanpath.to_s) ::File.absolute_path(cleanpath.to_s, root) == naive_expanded_path end
name_size()
click to toggle source
# File lib/zip/entry.rb, line 139 def name_size @name ? @name.bytesize : 0 end
pack_c_dir_entry()
click to toggle source
# File lib/zip/entry.rb, line 447 def pack_c_dir_entry zip64 = @extra['Zip64'] [ @header_signature, @version, # version of encoding software @fstype, # filesystem type @version_needed_to_extract, # @versionNeededToExtract @gp_flags, # @gp_flags @compression_method, @time.to_binary_dos_time, # @last_mod_time @time.to_binary_dos_date, # @last_mod_date @crc, zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size, zip64 && zip64.original_size ? 0xFFFFFFFF : @size, name_size, @extra ? @extra.c_dir_size : 0, comment_size, zip64 && zip64.disk_start_number ? 0xFFFF : 0, # disk number start @internal_file_attributes, # file type (binary=0, text=1) @external_file_attributes, # native filesystem attributes zip64 && zip64.relative_header_offset ? 0xFFFFFFFF : @local_header_offset, @name, @extra, @comment ].pack('VCCvvvvvVVVvvvvvVV') end
pack_local_entry()
click to toggle source
# File lib/zip/entry.rb, line 284 def pack_local_entry zip64 = @extra['Zip64'] [::Zip::LOCAL_ENTRY_SIGNATURE, @version_needed_to_extract, # version needed to extract @gp_flags, # @gp_flags @compression_method, @time.to_binary_dos_time, # @last_mod_time @time.to_binary_dos_date, # @last_mod_date @crc, zip64 && zip64.compressed_size ? 0xFFFFFFFF : @compressed_size, zip64 && zip64.original_size ? 0xFFFFFFFF : @size, name_size, @extra ? @extra.local_size : 0].pack('VvvvvvVVVvv') end
parent_as_string()
click to toggle source
# File lib/zip/entry.rb, line 589 def parent_as_string entry_name = name.chomp('/') slash_index = entry_name.rindex('/') slash_index ? entry_name.slice(0, slash_index + 1) : nil end
read_c_dir_extra_field(io)
click to toggle source
# File lib/zip/entry.rb, line 382 def read_c_dir_extra_field(io) if @extra.kind_of?(::Zip::ExtraField) @extra.merge(io.read(@extra_length)) else @extra = ::Zip::ExtraField.new(io.read(@extra_length)) end end
set_default_vars_values()
click to toggle source
# File lib/zip/entry.rb, line 18 def set_default_vars_values @local_header_offset = 0 @local_header_size = nil # not known until local entry is created or read @internal_file_attributes = 1 @external_file_attributes = 0 @header_signature = ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE @version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT @version = VERSION_MADE_BY @ftype = nil # unspecified or unknown @filepath = nil @gp_flags = 0 if ::Zip.unicode_names @gp_flags |= EFS @version = 63 end @follow_symlinks = false @restore_times = false @restore_permissions = false @restore_ownership = false # BUG: need an extra field to support uid/gid's @unix_uid = nil @unix_gid = nil @unix_perms = nil # @posix_acl = nil # @ntfs_acl = nil @dirty = false end
set_ftype_from_c_dir_entry()
click to toggle source
# File lib/zip/entry.rb, line 335 def set_ftype_from_c_dir_entry @ftype = case @fstype when ::Zip::FSTYPE_UNIX @unix_perms = (@external_file_attributes >> 16) & 0o7777 case (@external_file_attributes >> 28) when ::Zip::FILE_TYPE_DIR :directory when ::Zip::FILE_TYPE_FILE :file when ::Zip::FILE_TYPE_SYMLINK :symlink else # best case guess for whether it is a file or not # Otherwise this would be set to unknown and that entry would never be able to extracted if name_is_directory? :directory else :file end end else if name_is_directory? :directory else :file end end end
set_unix_attributes_on_path(dest_path)
click to toggle source
# File lib/zip/entry.rb, line 425 def set_unix_attributes_on_path(dest_path) # ignore setuid/setgid bits by default. honor if @restore_ownership unix_perms_mask = 0o1777 unix_perms_mask = 0o7777 if @restore_ownership ::FileUtils.chmod(@unix_perms & unix_perms_mask, dest_path) if @restore_permissions && @unix_perms ::FileUtils.chown(@unix_uid, @unix_gid, dest_path) if @restore_ownership && @unix_uid && @unix_gid && ::Process.egid == 0 # Restore the timestamp on a file. This will either have come from the # original source file that was copied into the archive, or from the # creation date of the archive if there was no original source file. ::FileUtils.touch(dest_path, mtime: time) if @restore_times end
time()
click to toggle source
# File lib/zip/entry.rb, line 84 def time if @extra['UniversalTime'] @extra['UniversalTime'].mtime elsif @extra['NTFS'] @extra['NTFS'].mtime else # Standard time field in central directory has local time # under archive creator. Then, we can't get timezone. @time end end
Also aliased as: mtime
time=(value)
click to toggle source
# File lib/zip/entry.rb, line 98 def time=(value) unless @extra.member?('UniversalTime') || @extra.member?('NTFS') @extra.create('UniversalTime') end (@extra['UniversalTime'] || @extra['NTFS']).mtime = value @time = value end
to_s()
click to toggle source
# File lib/zip/entry.rb, line 191 def to_s @name end
unpack_c_dir_entry(buf)
click to toggle source
# File lib/zip/entry.rb, line 311 def unpack_c_dir_entry(buf) @header_signature, @version, # version of encoding software @fstype, # filesystem type @version_needed_to_extract, @gp_flags, @compression_method, @last_mod_time, @last_mod_date, @crc, @compressed_size, @size, @name_length, @extra_length, @comment_length, _, # diskNumberStart @internal_file_attributes, @external_file_attributes, @local_header_offset, @name, @extra, @comment = buf.unpack('VCCvvvvvVVVvvvvvVV') end
unpack_local_entry(buf)
click to toggle source
# File lib/zip/entry.rb, line 230 def unpack_local_entry(buf) @header_signature, @version, @fstype, @gp_flags, @compression_method, @last_mod_time, @last_mod_date, @crc, @compressed_size, @size, @name_length, @extra_length = buf.unpack('VCCvvvvVVVvv') end
verify_local_header_size!()
click to toggle source
check before rewriting an entry (after file sizes are known) that we didn’t change the header size (and thus clobber file data or something)
# File lib/zip/entry.rb, line 157 def verify_local_header_size! return if @local_header_size.nil? new_size = calculate_local_header_size raise Error, "local header size changed (#{@local_header_size} -> #{new_size})" if @local_header_size != new_size end
Private Instance Methods
create_directory(dest_path) { |self, dest_path| ... }
click to toggle source
# File lib/zip/entry.rb, line 642 def create_directory(dest_path) return if ::File.directory?(dest_path) if ::File.exist?(dest_path) if block_given? && yield(self, dest_path) ::FileUtils.rm_f dest_path else raise ::Zip::DestinationFileExistsError, "Cannot create directory '#{dest_path}'. " \ 'A file already exists with that name' end end ::FileUtils.mkdir_p(dest_path) set_extra_attributes_on_path(dest_path) end
create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exists_proc }) { |self, dest_path| ... }
click to toggle source
# File lib/zip/entry.rb, line 615 def create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exists_proc }) if ::File.exist?(dest_path) && !yield(self, dest_path) raise ::Zip::DestinationFileExistsError, "Destination '#{dest_path}' already exists" end ::File.open(dest_path, 'wb') do |os| get_input_stream do |is| bytes_written = 0 warned = false buf = +'' while (buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf)) os << buf bytes_written += buf.bytesize next unless bytes_written > size && !warned message = "entry '#{name}' should be #{size}B, but is larger when inflated." raise ::Zip::EntrySizeError, message if ::Zip.validate_entry_sizes warn "WARNING: #{message}" warned = true end end end set_extra_attributes_on_path(dest_path) end
create_symlink(dest_path)
click to toggle source
BUG: create_symlink
() does not use &block
# File lib/zip/entry.rb, line 659 def create_symlink(dest_path) # TODO: Symlinks pose security challenges. Symlink support temporarily # removed in view of https://github.com/rubyzip/rubyzip/issues/369 . warn "WARNING: skipped symlink '#{dest_path}'." end
data_descriptor_size()
click to toggle source
# File lib/zip/entry.rb, line 677 def data_descriptor_size (@gp_flags & 0x0008) > 0 ? 16 : 0 end
set_time(binary_dos_date, binary_dos_time)
click to toggle source
# File lib/zip/entry.rb, line 609 def set_time(binary_dos_date, binary_dos_time) @time = ::Zip::DOSTime.parse_binary_dos_format(binary_dos_date, binary_dos_time) rescue ArgumentError warn 'WARNING: invalid date/time in zip entry.' if ::Zip.warn_invalid_date end