Class: Omnizip::Formats::Zip::EndOfCentralDirectory

Inherits:
Object
  • Object
show all
Includes:
Constants
Defined in:
lib/omnizip/formats/zip/end_of_central_directory.rb

Overview

ZIP End of Central Directory Record

Constant Summary

Constants included from Constants

Constants::ATTR_ARCHIVE, Constants::ATTR_DIRECTORY, Constants::CENTRAL_DIRECTORY_SIGNATURE, Constants::COMPRESSION_BZIP2, Constants::COMPRESSION_DEFLATE, Constants::COMPRESSION_DEFLATE64, Constants::COMPRESSION_IMPLODED, Constants::COMPRESSION_LZMA, Constants::COMPRESSION_PPMD, Constants::COMPRESSION_REDUCED_1, Constants::COMPRESSION_REDUCED_2, Constants::COMPRESSION_REDUCED_3, Constants::COMPRESSION_REDUCED_4, Constants::COMPRESSION_SHRUNK, Constants::COMPRESSION_STORE, Constants::COMPRESSION_ZSTANDARD, Constants::DATA_DESCRIPTOR_SIGNATURE, Constants::END_OF_CENTRAL_DIRECTORY_SIGNATURE, Constants::FLAG_DATA_DESCRIPTOR, Constants::FLAG_ENCRYPTED, Constants::FLAG_STRONG_ENCRYPTION, Constants::FLAG_UTF8, Constants::LOCAL_FILE_HEADER_SIGNATURE, Constants::MAX_COMMENT_LENGTH, Constants::UNIX_DIR_PERMISSIONS, Constants::UNIX_EXTRA_FIELD_TAG, Constants::UNIX_FILE_PERMISSIONS, Constants::UNIX_SYMLINK_PERMISSIONS, Constants::VERSION_BZIP2, Constants::VERSION_DEFAULT, Constants::VERSION_DEFLATE, Constants::VERSION_LZMA, Constants::VERSION_MADE_BY_UNIX, Constants::VERSION_MADE_BY_WINDOWS, Constants::VERSION_ZIP64, Constants::ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIGNATURE, Constants::ZIP64_END_OF_CENTRAL_DIRECTORY_SIGNATURE, Constants::ZIP64_EXTRA_FIELD_TAG, Constants::ZIP64_LIMIT

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(signature: END_OF_CENTRAL_DIRECTORY_SIGNATURE, disk_number: 0, disk_number_with_cd: 0, total_entries_this_disk: 0, total_entries: 0, central_directory_size: 0, central_directory_offset: 0, comment_length: 0, comment: "") ⇒ EndOfCentralDirectory

Returns a new instance of EndOfCentralDirectory.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 15

def initialize(
  signature: END_OF_CENTRAL_DIRECTORY_SIGNATURE,
  disk_number: 0,
  disk_number_with_cd: 0,
  total_entries_this_disk: 0,
  total_entries: 0,
  central_directory_size: 0,
  central_directory_offset: 0,
  comment_length: 0,
  comment: ""
)
  @signature = signature
  @disk_number = disk_number
  @disk_number_with_cd = disk_number_with_cd
  @total_entries_this_disk = total_entries_this_disk
  @total_entries = total_entries
  @central_directory_size = central_directory_size
  @central_directory_offset = central_directory_offset
  @comment_length = comment_length
  @comment = comment
end

Instance Attribute Details

#central_directory_offsetObject

Returns the value of attribute central_directory_offset.



10
11
12
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 10

def central_directory_offset
  @central_directory_offset
end

#central_directory_sizeObject

Returns the value of attribute central_directory_size.



10
11
12
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 10

def central_directory_size
  @central_directory_size
end

#commentObject

Returns the value of attribute comment.



10
11
12
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 10

def comment
  @comment
end

#comment_lengthObject

Returns the value of attribute comment_length.



10
11
12
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 10

def comment_length
  @comment_length
end

#disk_numberObject

Returns the value of attribute disk_number.



10
11
12
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 10

def disk_number
  @disk_number
end

#disk_number_with_cdObject

Returns the value of attribute disk_number_with_cd.



10
11
12
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 10

def disk_number_with_cd
  @disk_number_with_cd
end

#signatureObject

Returns the value of attribute signature.



10
11
12
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 10

def signature
  @signature
end

#total_entriesObject

Returns the value of attribute total_entries.



10
11
12
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 10

def total_entries
  @total_entries
end

#total_entries_this_diskObject

Returns the value of attribute total_entries_this_disk.



10
11
12
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 10

def total_entries_this_disk
  @total_entries_this_disk
end

Class Method Details

.find_in_file(io) ⇒ Object

Find EOCD record by scanning backwards from end of file



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 96

def self.find_in_file(io)
  # Start from the end and work backwards
  # EOCD is at least 22 bytes, can be up to 22 + MAX_COMMENT_LENGTH
  io.seek(0, ::IO::SEEK_END)
  file_size = io.pos

  # Start searching from the end
  max_comment_size = [file_size - 22, MAX_COMMENT_LENGTH].min
  search_start = [file_size - 22 - max_comment_size, 0].max

  io.seek(search_start, ::IO::SEEK_SET)
  buffer = io.read(file_size - search_start)

  # Search for EOCD signature from the end
  signature_bytes = [END_OF_CENTRAL_DIRECTORY_SIGNATURE].pack("V")

  (buffer.size - 22).downto(0) do |i|
    if buffer[i, 4] == signature_bytes
      # Found potential EOCD
      eocd_data = buffer[i..]
      comment_length = eocd_data[20, 2].unpack1("v")

      # Verify this is the actual EOCD by checking if comment length is reasonable
      # Some ZIP tools add trailing data, so we check if comment fits within remaining buffer
      if i + 22 + comment_length <= buffer.size
        return from_binary(eocd_data[0, 22 + comment_length])
      end
    end
  end

  raise Omnizip::FormatError,
        "Could not find End of Central Directory record"
end

.from_binary(data) ⇒ Object

Parse from binary data



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 64

def self.from_binary(data)
  signature, disk_number, disk_number_with_cd,
  total_entries_this_disk, total_entries,
  central_directory_size, central_directory_offset,
  comment_length = data.unpack("VvvvvVVv")

  unless signature == END_OF_CENTRAL_DIRECTORY_SIGNATURE
    raise Omnizip::FormatError,
          "Invalid EOCD signature"
  end

  comment = data[22, comment_length].to_s.force_encoding("UTF-8")

  new(
    signature: signature,
    disk_number: disk_number,
    disk_number_with_cd: disk_number_with_cd,
    total_entries_this_disk: total_entries_this_disk,
    total_entries: total_entries,
    central_directory_size: central_directory_size,
    central_directory_offset: central_directory_offset,
    comment_length: comment_length,
    comment: comment,
  )
end

Instance Method Details

#record_sizeObject

Size of the record in bytes



91
92
93
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 91

def record_size
  22 + comment_length
end

#to_binaryObject

Serialize to binary format



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 47

def to_binary
  @comment_length = comment.bytesize

  [
    signature,
    disk_number,
    disk_number_with_cd,
    total_entries_this_disk,
    total_entries,
    central_directory_size,
    central_directory_offset,
    comment_length,
  ].pack("VvvvvVVv") +
    comment.b
end

#zip64?Boolean

Check if ZIP64 format is needed

Returns:

  • (Boolean)


38
39
40
41
42
43
44
# File 'lib/omnizip/formats/zip/end_of_central_directory.rb', line 38

def zip64?
  total_entries == 0xFFFF ||
    central_directory_size == ZIP64_LIMIT ||
    central_directory_offset == ZIP64_LIMIT ||
    disk_number == 0xFFFF ||
    disk_number_with_cd == 0xFFFF
end