NTFS Extra Field (0x000a) support
This commit is contained in:
parent
8ede42436e
commit
a7a11e33bd
|
@ -74,6 +74,8 @@ module Zip
|
|||
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.
|
||||
|
@ -84,10 +86,10 @@ module Zip
|
|||
alias :mtime :time
|
||||
|
||||
def time=(value)
|
||||
unless @extra.member?('UniversalTime')
|
||||
unless @extra.member?('UniversalTime') || @extra.member?('NTFS')
|
||||
@extra.create('UniversalTime')
|
||||
end
|
||||
@extra['UniversalTime'].mtime = value
|
||||
(@extra['UniversalTime'] || @extra['NTFS']).mtime = value
|
||||
@time = value
|
||||
end
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ require 'zip/extra_field/old_unix'
|
|||
require 'zip/extra_field/unix'
|
||||
require 'zip/extra_field/zip64'
|
||||
require 'zip/extra_field/zip64_placeholder'
|
||||
require 'zip/extra_field/ntfs'
|
||||
|
||||
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
||||
# rubyzip is free software; you can redistribute it and/or
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
module Zip
|
||||
# PKWARE NTFS Extra Field (0x000a)
|
||||
# Only Tag 0x0001 is supported
|
||||
class ExtraField::NTFS < ExtraField::Generic
|
||||
HEADER_ID = [0x000A].pack('v')
|
||||
register_map
|
||||
|
||||
WINDOWS_TICK = 10000000.0
|
||||
SEC_TO_UNIX_EPOCH = 11644473600
|
||||
|
||||
def initialize(binstr = nil)
|
||||
@ctime = nil
|
||||
@mtime = nil
|
||||
@atime = nil
|
||||
binstr and merge(binstr)
|
||||
end
|
||||
|
||||
attr_accessor :atime, :ctime, :mtime
|
||||
|
||||
def merge(binstr)
|
||||
return if binstr.empty?
|
||||
size, content = initial_parse(binstr)
|
||||
(size && content) or return
|
||||
|
||||
content = content[4..-1]
|
||||
tags = parse_tags(content)
|
||||
|
||||
tag1 = tags[1]
|
||||
if tag1
|
||||
ntfs_mtime, ntfs_atime, ntfs_ctime = tag1.unpack("Q<Q<Q<")
|
||||
ntfs_mtime and @mtime ||= from_ntfs_time(ntfs_mtime)
|
||||
ntfs_atime and @atime ||= from_ntfs_time(ntfs_atime)
|
||||
ntfs_ctime and @ctime ||= from_ntfs_time(ntfs_ctime)
|
||||
end
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
@mtime == other.mtime &&
|
||||
@atime == other.atime &&
|
||||
@ctime == other.ctime
|
||||
end
|
||||
|
||||
# Info-ZIP note states this extra field is stored at local header
|
||||
def pack_for_local
|
||||
pack_for_c_dir
|
||||
end
|
||||
|
||||
# But 7-zip for Windows only stores at central dir
|
||||
def pack_for_c_dir
|
||||
# reserved 0 and tag 1
|
||||
s = [0, 1].pack("Vv")
|
||||
|
||||
tag1 = ''.force_encoding(Encoding::BINARY)
|
||||
if @mtime
|
||||
tag1 << [to_ntfs_time(@mtime)].pack('Q<')
|
||||
if @atime
|
||||
tag1 << [to_ntfs_time(@atime)].pack('Q<')
|
||||
if @ctime
|
||||
tag1 << [to_ntfs_time(@ctime)].pack('Q<')
|
||||
end
|
||||
end
|
||||
end
|
||||
s << [tag1.bytesize].pack('v') << tag1
|
||||
s
|
||||
end
|
||||
|
||||
private
|
||||
def parse_tags(content)
|
||||
return {} if content.nil?
|
||||
tags = {}
|
||||
i = 0
|
||||
while i < content.bytesize do
|
||||
tag, size = content[i, 4].unpack('vv')
|
||||
i += 4
|
||||
break unless tag && size
|
||||
value = content[i, size]
|
||||
i += size
|
||||
tags[tag] = value
|
||||
end
|
||||
|
||||
tags
|
||||
end
|
||||
|
||||
def from_ntfs_time(ntfs_time)
|
||||
::Zip::DOSTime.at(ntfs_time / WINDOWS_TICK - SEC_TO_UNIX_EPOCH)
|
||||
end
|
||||
|
||||
def to_ntfs_time(time)
|
||||
((time.to_f + SEC_TO_UNIX_EPOCH) * WINDOWS_TICK).to_i
|
||||
end
|
||||
end
|
||||
end
|
|
@ -320,6 +320,8 @@ module Zip
|
|||
e = get_entry(fileName)
|
||||
if e.extra.member? "UniversalTime"
|
||||
e.extra["UniversalTime"].atime
|
||||
elsif e.extra.member? "NTFS"
|
||||
e.extra["NTFS"].atime
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
@ -329,6 +331,8 @@ module Zip
|
|||
e = get_entry(fileName)
|
||||
if e.extra.member? "UniversalTime"
|
||||
e.extra["UniversalTime"].ctime
|
||||
elsif e.extra.member? "NTFS"
|
||||
e.extra["NTFS"].ctime
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
|
Binary file not shown.
|
@ -17,6 +17,15 @@ class ZipExtraFieldTest < MiniTest::Test
|
|||
assert_equal(extra.to_s, "fooabarbaz")
|
||||
end
|
||||
|
||||
def test_ntfs
|
||||
str = "\x0A\x00 \x00\x00\x00\x00\x00\x01\x00\x18\x00\xC0\x81\x17\xE8B\xCE\xCF\x01\xC0\x81\x17\xE8B\xCE\xCF\x01\xC0\x81\x17\xE8B\xCE\xCF\x01"
|
||||
extra = ::Zip::ExtraField.new(str)
|
||||
assert(extra.member?("NTFS"))
|
||||
t = ::Zip::DOSTime.at(1410496497.405178)
|
||||
assert_equal(t, extra['NTFS'].mtime)
|
||||
assert_equal(t, extra['NTFS'].atime)
|
||||
assert_equal(t, extra['NTFS'].ctime)
|
||||
end
|
||||
|
||||
def test_merge
|
||||
str = "UT\x5\0\x3\250$\r@Ux\0\0"
|
||||
|
|
|
@ -307,6 +307,15 @@ class ZipFsFileNonmutatingTest < MiniTest::Test
|
|||
assert_nil(@zip_file.file.stat("file1").atime)
|
||||
end
|
||||
|
||||
def test_ntfs_time
|
||||
::Zip::File.open("test/data/ntfs.zip") do |zf|
|
||||
t = ::Zip::DOSTime.at(1410496497.405178)
|
||||
assert_equal(zf.file.mtime("data.txt"), t)
|
||||
assert_equal(zf.file.atime("data.txt"), t)
|
||||
assert_equal(zf.file.ctime("data.txt"), t)
|
||||
end
|
||||
end
|
||||
|
||||
def test_readable?
|
||||
assert(! @zip_file.file.readable?("noSuchFile"))
|
||||
assert(@zip_file.file.readable?("file1"))
|
||||
|
|
Loading…
Reference in New Issue