Method: ZipKit::ZipWriter#write_central_directory_file_header

Defined in:
lib/zip_kit/zip_writer.rb

#write_central_directory_file_header(io:, local_file_header_location:, gp_flags:, storage_mode:, compressed_size:, uncompressed_size:, mtime:, crc32:, filename:, unix_permissions: nil) ⇒ void

This method returns an undefined value.

Writes the file header for the central directory, for a particular file in the archive. When writing out this data, ensure that the CRC32 and both sizes (compressed/uncompressed) are correct for the entry in question.

Parameters:

  • io (#<<)

    the buffer to write the local file header to

  • filename (String)

    the name of the file in the archive

  • compressed_size (Integer)

    The size of the compressed (or stored) data - how much space it uses in the ZIP

  • uncompressed_size (Integer)

    The size of the file once extracted

  • crc32 (Integer)

    The CRC32 checksum of the file

  • mtime (Time)

    the modification time to be recorded in the ZIP

  • gp_flags (Integer)

    bit-packed general purpose flags

  • unix_permissions (Integer) (defaults to: nil)

    the permissions for the file, or nil for the default to be used



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/zip_kit/zip_writer.rb', line 146

def write_central_directory_file_header(io:,
  local_file_header_location:,
  gp_flags:,
  storage_mode:,
  compressed_size:,
  uncompressed_size:,
  mtime:,
  crc32:,
  filename:,
  unix_permissions: nil)
  # At this point if the header begins somewhere beyound 0xFFFFFFFF we _have_ to record the offset
  # of the local file header as a zip64 extra field, so we give up, give in, you loose, love will always win...
  add_zip64 = (local_file_header_location > FOUR_BYTE_MAX_UINT) ||
    (compressed_size > FOUR_BYTE_MAX_UINT) || (uncompressed_size > FOUR_BYTE_MAX_UINT)

  # central file header signature   4 bytes  (0x02014b50)
  io << [0x02014b50].pack(C_UINT4)
  # version made by                 2 bytes
  io << MADE_BY_SIGNATURE

  # version needed to extract       2 bytes
  io << if add_zip64
    [VERSION_NEEDED_TO_EXTRACT_ZIP64].pack(C_UINT2)
  else
    [VERSION_NEEDED_TO_EXTRACT].pack(C_UINT2)
  end

  # general purpose bit flag        2 bytes
  io << [gp_flags].pack(C_UINT2)
  # compression method              2 bytes
  io << [storage_mode].pack(C_UINT2)
  # last mod file time              2 bytes
  io << [to_binary_dos_time(mtime)].pack(C_UINT2)
  # last mod file date              2 bytes
  io << [to_binary_dos_date(mtime)].pack(C_UINT2)
  # crc-32                          4 bytes
  io << [crc32].pack(C_UINT4)

  # compressed size              4 bytes
  # uncompressed size            4 bytes
  if add_zip64
    io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4)
    io << [FOUR_BYTE_MAX_UINT].pack(C_UINT4)
  else
    io << [compressed_size].pack(C_UINT4)
    io << [uncompressed_size].pack(C_UINT4)
  end

  # Filename should not be longer than 0xFFFF otherwise this wont fit here
  # file name length                2 bytes
  io << [filename.bytesize].pack(C_UINT2)

  extra_fields = StringIO.new
  if add_zip64
    extra_fields << zip_64_extra_for_central_directory_file_header(local_file_header_location: local_file_header_location,
      compressed_size: compressed_size,
      uncompressed_size: uncompressed_size)
  end
  extra_fields << timestamp_extra_for_central_directory_entry(mtime)

  # extra field length              2 bytes
  io << [extra_fields.size].pack(C_UINT2)
  # file comment length             2 bytes
  io << [0].pack(C_UINT2)

  # For The Unarchiver < 3.11.1 this field has to be set to the overflow value if zip64 is used
  # because otherwise it does not properly advance the pointer when reading the Zip64 extra field
  # https://bitbucket.org/WAHa_06x36/theunarchiver/pull-requests/2/bug-fix-for-zip64-extra-field-parser/diff
  # disk number start               2 bytes
  io << if add_zip64
    [TWO_BYTE_MAX_UINT].pack(C_UINT2)
  else
    [0].pack(C_UINT2)
  end
  # internal file attributes        2 bytes
  io << [0].pack(C_UINT2)

  # Because the add_empty_directory method will create a directory with a trailing "/",
  # this check can be used to assign proper permissions to the created directory.
  external_attrs = if filename.end_with?("/")
    unix_permissions ||= DEFAULT_DIRECTORY_UNIX_PERMISSIONS
    generate_external_attrs(unix_permissions, FILE_TYPE_DIRECTORY)
  else
    unix_permissions ||= DEFAULT_FILE_UNIX_PERMISSIONS
    generate_external_attrs(unix_permissions, FILE_TYPE_FILE)
  end

  # external file attributes        4 bytes
  io << [external_attrs].pack(C_UINT4)

  # relative offset of local header 4 bytes
  io << if add_zip64
    [FOUR_BYTE_MAX_UINT].pack(C_UINT4)
  else
    [local_file_header_location].pack(C_UINT4)
  end

  # file name (variable size)
  io << filename
  # extra field (variable size)
  io << extra_fields.string
  # file comment (variable size)
  # (empty)
end