rubyzip 1.3.0 → 3.0.0.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changelog.md +368 -0
- data/README.md +123 -46
- data/Rakefile +13 -6
- data/lib/zip/central_directory.rb +166 -116
- data/lib/zip/compressor.rb +3 -1
- data/lib/zip/constants.rb +77 -21
- data/lib/zip/crypto/decrypted_io.rb +42 -0
- data/lib/zip/crypto/encryption.rb +4 -2
- data/lib/zip/crypto/null_encryption.rb +5 -3
- data/lib/zip/crypto/traditional_encryption.rb +14 -12
- data/lib/zip/decompressor.rb +21 -2
- data/lib/zip/deflater.rb +10 -8
- data/lib/zip/dirtyable.rb +32 -0
- data/lib/zip/dos_time.rb +53 -12
- data/lib/zip/entry.rb +306 -184
- data/lib/zip/entry_set.rb +11 -7
- data/lib/zip/errors.rb +115 -15
- data/lib/zip/extra_field/generic.rb +11 -17
- data/lib/zip/extra_field/ntfs.rb +8 -2
- data/lib/zip/extra_field/old_unix.rb +6 -2
- data/lib/zip/extra_field/universal_time.rb +45 -13
- data/lib/zip/extra_field/unix.rb +7 -3
- data/lib/zip/extra_field/unknown.rb +33 -0
- data/lib/zip/extra_field/zip64.rb +16 -7
- data/lib/zip/extra_field.rb +22 -26
- data/lib/zip/file.rb +196 -240
- data/lib/zip/file_split.rb +97 -0
- data/lib/zip/filesystem/dir.rb +86 -0
- data/lib/zip/filesystem/directory_iterator.rb +48 -0
- data/lib/zip/filesystem/file.rb +262 -0
- data/lib/zip/filesystem/file_stat.rb +110 -0
- data/lib/zip/filesystem/zip_file_name_mapper.rb +81 -0
- data/lib/zip/filesystem.rb +31 -584
- data/lib/zip/inflater.rb +27 -37
- data/lib/zip/input_stream.rb +67 -42
- data/lib/zip/ioextras/abstract_input_stream.rb +32 -16
- data/lib/zip/ioextras/abstract_output_stream.rb +5 -3
- data/lib/zip/ioextras.rb +7 -7
- data/lib/zip/null_compressor.rb +3 -1
- data/lib/zip/null_decompressor.rb +4 -10
- data/lib/zip/null_input_stream.rb +3 -1
- data/lib/zip/output_stream.rb +58 -43
- data/lib/zip/pass_thru_compressor.rb +5 -3
- data/lib/zip/pass_thru_decompressor.rb +16 -23
- data/lib/zip/streamable_directory.rb +6 -4
- data/lib/zip/streamable_stream.rb +9 -10
- data/lib/zip/version.rb +3 -1
- data/lib/zip.rb +19 -4
- data/rubyzip.gemspec +38 -0
- data/samples/example.rb +9 -4
- data/samples/example_filesystem.rb +3 -2
- data/samples/example_recursive.rb +3 -1
- data/samples/gtk_ruby_zip.rb +22 -20
- data/samples/qtzip.rb +12 -11
- data/samples/write_simple.rb +3 -4
- data/samples/zipfind.rb +24 -22
- metadata +86 -179
- data/TODO +0 -15
- data/lib/zip/extra_field/zip64_placeholder.rb +0 -15
- data/test/basic_zip_file_test.rb +0 -60
- data/test/case_sensitivity_test.rb +0 -69
- data/test/central_directory_entry_test.rb +0 -69
- data/test/central_directory_test.rb +0 -100
- data/test/crypto/null_encryption_test.rb +0 -57
- data/test/crypto/traditional_encryption_test.rb +0 -80
- data/test/data/WarnInvalidDate.zip +0 -0
- data/test/data/file1.txt +0 -46
- data/test/data/file1.txt.deflatedData +0 -0
- data/test/data/file2.txt +0 -1504
- data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
- data/test/data/globTest/foo.txt +0 -0
- data/test/data/globTest/food.txt +0 -0
- data/test/data/globTest.zip +0 -0
- data/test/data/gpbit3stored.zip +0 -0
- data/test/data/mimetype +0 -1
- data/test/data/notzippedruby.rb +0 -7
- data/test/data/ntfs.zip +0 -0
- data/test/data/oddExtraField.zip +0 -0
- data/test/data/path_traversal/Makefile +0 -10
- data/test/data/path_traversal/jwilk/README.md +0 -5
- data/test/data/path_traversal/jwilk/absolute1.zip +0 -0
- data/test/data/path_traversal/jwilk/absolute2.zip +0 -0
- data/test/data/path_traversal/jwilk/dirsymlink.zip +0 -0
- data/test/data/path_traversal/jwilk/dirsymlink2a.zip +0 -0
- data/test/data/path_traversal/jwilk/dirsymlink2b.zip +0 -0
- data/test/data/path_traversal/jwilk/relative0.zip +0 -0
- data/test/data/path_traversal/jwilk/relative2.zip +0 -0
- data/test/data/path_traversal/jwilk/symlink.zip +0 -0
- data/test/data/path_traversal/relative1.zip +0 -0
- data/test/data/path_traversal/tilde.zip +0 -0
- data/test/data/path_traversal/tuzovakaoff/README.md +0 -3
- data/test/data/path_traversal/tuzovakaoff/absolutepath.zip +0 -0
- data/test/data/path_traversal/tuzovakaoff/symlink.zip +0 -0
- data/test/data/rubycode.zip +0 -0
- data/test/data/rubycode2.zip +0 -0
- data/test/data/test.xls +0 -0
- data/test/data/testDirectory.bin +0 -0
- data/test/data/zip64-sample.zip +0 -0
- data/test/data/zipWithDirs.zip +0 -0
- data/test/data/zipWithEncryption.zip +0 -0
- data/test/deflater_test.rb +0 -65
- data/test/encryption_test.rb +0 -42
- data/test/entry_set_test.rb +0 -163
- data/test/entry_test.rb +0 -154
- data/test/errors_test.rb +0 -35
- data/test/extra_field_test.rb +0 -76
- data/test/file_extract_directory_test.rb +0 -54
- data/test/file_extract_test.rb +0 -145
- data/test/file_permissions_test.rb +0 -65
- data/test/file_split_test.rb +0 -57
- data/test/file_test.rb +0 -666
- data/test/filesystem/dir_iterator_test.rb +0 -58
- data/test/filesystem/directory_test.rb +0 -139
- data/test/filesystem/file_mutating_test.rb +0 -87
- data/test/filesystem/file_nonmutating_test.rb +0 -508
- data/test/filesystem/file_stat_test.rb +0 -64
- data/test/gentestfiles.rb +0 -126
- data/test/inflater_test.rb +0 -14
- data/test/input_stream_test.rb +0 -182
- data/test/ioextras/abstract_input_stream_test.rb +0 -102
- data/test/ioextras/abstract_output_stream_test.rb +0 -106
- data/test/ioextras/fake_io_test.rb +0 -18
- data/test/local_entry_test.rb +0 -154
- data/test/output_stream_test.rb +0 -128
- data/test/pass_thru_compressor_test.rb +0 -30
- data/test/pass_thru_decompressor_test.rb +0 -14
- data/test/path_traversal_test.rb +0 -141
- data/test/samples/example_recursive_test.rb +0 -37
- data/test/settings_test.rb +0 -95
- data/test/test_helper.rb +0 -234
- data/test/unicode_file_names_and_comments_test.rb +0 -62
- data/test/zip64_full_test.rb +0 -51
- data/test/zip64_support_test.rb +0 -14
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zip
|
4
|
+
module FileSplit # :nodoc:
|
5
|
+
MAX_SEGMENT_SIZE = 3_221_225_472
|
6
|
+
MIN_SEGMENT_SIZE = 65_536
|
7
|
+
DATA_BUFFER_SIZE = 8192
|
8
|
+
|
9
|
+
def get_segment_size_for_split(segment_size)
|
10
|
+
if MIN_SEGMENT_SIZE > segment_size
|
11
|
+
MIN_SEGMENT_SIZE
|
12
|
+
elsif MAX_SEGMENT_SIZE < segment_size
|
13
|
+
MAX_SEGMENT_SIZE
|
14
|
+
else
|
15
|
+
segment_size
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
|
20
|
+
unless partial_zip_file_name.nil?
|
21
|
+
partial_zip_file_name = zip_file_name.sub(
|
22
|
+
/#{::File.basename(zip_file_name)}\z/,
|
23
|
+
partial_zip_file_name + ::File.extname(zip_file_name)
|
24
|
+
)
|
25
|
+
end
|
26
|
+
partial_zip_file_name ||= zip_file_name
|
27
|
+
partial_zip_file_name
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_segment_count_for_split(zip_file_size, segment_size)
|
31
|
+
(zip_file_size / segment_size).to_i +
|
32
|
+
((zip_file_size % segment_size).zero? ? 0 : 1)
|
33
|
+
end
|
34
|
+
|
35
|
+
def put_split_signature(szip_file, segment_size)
|
36
|
+
signature_packed = [SPLIT_FILE_SIGNATURE].pack('V')
|
37
|
+
szip_file << signature_packed
|
38
|
+
segment_size - signature_packed.size
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# TODO: Make the code more understandable
|
43
|
+
#
|
44
|
+
def save_splited_part(
|
45
|
+
zip_file, partial_zip_file_name, zip_file_size,
|
46
|
+
szip_file_index, segment_size, segment_count
|
47
|
+
)
|
48
|
+
ssegment_size = zip_file_size - zip_file.pos
|
49
|
+
ssegment_size = segment_size if ssegment_size > segment_size
|
50
|
+
szip_file_name = "#{partial_zip_file_name}.#{format('%03d', szip_file_index)}"
|
51
|
+
::File.open(szip_file_name, 'wb') do |szip_file|
|
52
|
+
if szip_file_index == 1
|
53
|
+
ssegment_size = put_split_signature(szip_file, segment_size)
|
54
|
+
end
|
55
|
+
chunk_bytes = 0
|
56
|
+
until ssegment_size == chunk_bytes || zip_file.eof?
|
57
|
+
segment_bytes_left = ssegment_size - chunk_bytes
|
58
|
+
buffer_size = [segment_bytes_left, DATA_BUFFER_SIZE].min
|
59
|
+
chunk = zip_file.read(buffer_size)
|
60
|
+
chunk_bytes += buffer_size
|
61
|
+
szip_file << chunk
|
62
|
+
# Info for track splitting
|
63
|
+
yield segment_count, szip_file_index, chunk_bytes, ssegment_size if block_given?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Splits an archive into parts with segment size
|
69
|
+
def split(
|
70
|
+
zip_file_name, segment_size: MAX_SEGMENT_SIZE,
|
71
|
+
delete_original: true, partial_zip_file_name: nil
|
72
|
+
)
|
73
|
+
raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name)
|
74
|
+
raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name)
|
75
|
+
|
76
|
+
zip_file_size = ::File.size(zip_file_name)
|
77
|
+
segment_size = get_segment_size_for_split(segment_size)
|
78
|
+
return if zip_file_size <= segment_size
|
79
|
+
|
80
|
+
segment_count = get_segment_count_for_split(zip_file_size, segment_size)
|
81
|
+
::Zip::File.open(zip_file_name) {} # Check for correct zip structure.
|
82
|
+
partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
|
83
|
+
szip_file_index = 0
|
84
|
+
::File.open(zip_file_name, 'rb') do |zip_file|
|
85
|
+
until zip_file.eof?
|
86
|
+
szip_file_index += 1
|
87
|
+
save_splited_part(
|
88
|
+
zip_file, partial_zip_file_name, zip_file_size,
|
89
|
+
szip_file_index, segment_size, segment_count
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
::File.delete(zip_file_name) if delete_original
|
94
|
+
szip_file_index
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zip
|
4
|
+
module FileSystem
|
5
|
+
class Dir # :nodoc:all
|
6
|
+
def initialize(mapped_zip)
|
7
|
+
@mapped_zip = mapped_zip
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_writer :file
|
11
|
+
|
12
|
+
def new(directory_name)
|
13
|
+
DirectoryIterator.new(entries(directory_name))
|
14
|
+
end
|
15
|
+
|
16
|
+
def open(directory_name)
|
17
|
+
dir_iter = new(directory_name)
|
18
|
+
if block_given?
|
19
|
+
begin
|
20
|
+
yield(dir_iter)
|
21
|
+
return nil
|
22
|
+
ensure
|
23
|
+
dir_iter.close
|
24
|
+
end
|
25
|
+
end
|
26
|
+
dir_iter
|
27
|
+
end
|
28
|
+
|
29
|
+
def pwd
|
30
|
+
@mapped_zip.pwd
|
31
|
+
end
|
32
|
+
alias getwd pwd
|
33
|
+
|
34
|
+
def chdir(directory_name)
|
35
|
+
unless @file.stat(directory_name).directory?
|
36
|
+
raise Errno::EINVAL, "Invalid argument - #{directory_name}"
|
37
|
+
end
|
38
|
+
|
39
|
+
@mapped_zip.pwd = @file.expand_path(directory_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def entries(directory_name)
|
43
|
+
entries = []
|
44
|
+
foreach(directory_name) { |e| entries << e }
|
45
|
+
entries
|
46
|
+
end
|
47
|
+
|
48
|
+
def glob(*args, &block)
|
49
|
+
@mapped_zip.glob(*args, &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def foreach(directory_name)
|
53
|
+
unless @file.stat(directory_name).directory?
|
54
|
+
raise Errno::ENOTDIR, directory_name
|
55
|
+
end
|
56
|
+
|
57
|
+
path = @file.expand_path(directory_name)
|
58
|
+
path << '/' unless path.end_with?('/')
|
59
|
+
path = Regexp.escape(path)
|
60
|
+
subdir_entry_regex = Regexp.new("^#{path}([^/]+)$")
|
61
|
+
@mapped_zip.each do |filename|
|
62
|
+
match = subdir_entry_regex.match(filename)
|
63
|
+
yield(match[1]) unless match.nil?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete(entry_name)
|
68
|
+
unless @file.stat(entry_name).directory?
|
69
|
+
raise Errno::EINVAL, "Invalid argument - #{entry_name}"
|
70
|
+
end
|
71
|
+
|
72
|
+
@mapped_zip.remove(entry_name)
|
73
|
+
end
|
74
|
+
alias rmdir delete
|
75
|
+
alias unlink delete
|
76
|
+
|
77
|
+
def mkdir(entry_name, permissions = 0o755)
|
78
|
+
@mapped_zip.mkdir(entry_name, permissions)
|
79
|
+
end
|
80
|
+
|
81
|
+
def chroot(*_args)
|
82
|
+
raise NotImplementedError, 'The chroot() function is not implemented'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zip
|
4
|
+
module FileSystem
|
5
|
+
class DirectoryIterator # :nodoc:all
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize(filenames)
|
9
|
+
@filenames = filenames
|
10
|
+
@index = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def close
|
14
|
+
@filenames = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def each(&a_proc)
|
18
|
+
raise IOError, 'closed directory' if @filenames.nil?
|
19
|
+
|
20
|
+
@filenames.each(&a_proc)
|
21
|
+
end
|
22
|
+
|
23
|
+
def read
|
24
|
+
raise IOError, 'closed directory' if @filenames.nil?
|
25
|
+
|
26
|
+
@filenames[(@index += 1) - 1]
|
27
|
+
end
|
28
|
+
|
29
|
+
def rewind
|
30
|
+
raise IOError, 'closed directory' if @filenames.nil?
|
31
|
+
|
32
|
+
@index = 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def seek(position)
|
36
|
+
raise IOError, 'closed directory' if @filenames.nil?
|
37
|
+
|
38
|
+
@index = position
|
39
|
+
end
|
40
|
+
|
41
|
+
def tell
|
42
|
+
raise IOError, 'closed directory' if @filenames.nil?
|
43
|
+
|
44
|
+
@index
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,262 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'file_stat'
|
4
|
+
|
5
|
+
module Zip
|
6
|
+
module FileSystem
|
7
|
+
# Instances of this class are normally accessed via the accessor
|
8
|
+
# Zip::File::file. An instance of File behaves like ruby's
|
9
|
+
# builtin File (class) object, except it works on Zip::File entries.
|
10
|
+
#
|
11
|
+
# The individual methods are not documented due to their
|
12
|
+
# similarity with the methods in File
|
13
|
+
class File # :nodoc:all
|
14
|
+
attr_writer :dir
|
15
|
+
|
16
|
+
def initialize(mapped_zip)
|
17
|
+
@mapped_zip = mapped_zip
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_entry(filename)
|
21
|
+
unless exists?(filename)
|
22
|
+
raise Errno::ENOENT, "No such file or directory - #{filename}"
|
23
|
+
end
|
24
|
+
|
25
|
+
@mapped_zip.find_entry(filename)
|
26
|
+
end
|
27
|
+
|
28
|
+
def unix_mode_cmp(filename, mode)
|
29
|
+
e = find_entry(filename)
|
30
|
+
e.fstype == FSTYPE_UNIX && ((e.external_file_attributes >> 16) & mode) != 0
|
31
|
+
rescue Errno::ENOENT
|
32
|
+
false
|
33
|
+
end
|
34
|
+
private :unix_mode_cmp
|
35
|
+
|
36
|
+
def exists?(filename)
|
37
|
+
expand_path(filename) == '/' || !@mapped_zip.find_entry(filename).nil?
|
38
|
+
end
|
39
|
+
alias exist? exists?
|
40
|
+
|
41
|
+
# Permissions not implemented, so if the file exists it is accessible
|
42
|
+
alias owned? exists?
|
43
|
+
alias grpowned? exists?
|
44
|
+
|
45
|
+
def readable?(filename)
|
46
|
+
unix_mode_cmp(filename, 0o444)
|
47
|
+
end
|
48
|
+
alias readable_real? readable?
|
49
|
+
|
50
|
+
def writable?(filename)
|
51
|
+
unix_mode_cmp(filename, 0o222)
|
52
|
+
end
|
53
|
+
alias writable_real? writable?
|
54
|
+
|
55
|
+
def executable?(filename)
|
56
|
+
unix_mode_cmp(filename, 0o111)
|
57
|
+
end
|
58
|
+
alias executable_real? executable?
|
59
|
+
|
60
|
+
def setuid?(filename)
|
61
|
+
unix_mode_cmp(filename, 0o4000)
|
62
|
+
end
|
63
|
+
|
64
|
+
def setgid?(filename)
|
65
|
+
unix_mode_cmp(filename, 0o2000)
|
66
|
+
end
|
67
|
+
|
68
|
+
def sticky?(filename)
|
69
|
+
unix_mode_cmp(filename, 0o1000)
|
70
|
+
end
|
71
|
+
|
72
|
+
def umask(*args)
|
73
|
+
::File.umask(*args)
|
74
|
+
end
|
75
|
+
|
76
|
+
def truncate(_filename, _len)
|
77
|
+
raise StandardError, 'truncate not supported'
|
78
|
+
end
|
79
|
+
|
80
|
+
def directory?(filename)
|
81
|
+
entry = @mapped_zip.find_entry(filename)
|
82
|
+
expand_path(filename) == '/' || (!entry.nil? && entry.directory?)
|
83
|
+
end
|
84
|
+
|
85
|
+
def open(filename, mode = 'r', permissions = 0o644, &block)
|
86
|
+
mode = mode.tr('b', '') # ignore b option
|
87
|
+
case mode
|
88
|
+
when 'r'
|
89
|
+
@mapped_zip.get_input_stream(filename, &block)
|
90
|
+
when 'w'
|
91
|
+
@mapped_zip.get_output_stream(filename, permissions, &block)
|
92
|
+
else
|
93
|
+
raise StandardError, "openmode '#{mode} not supported" unless mode == 'r'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def new(filename, mode = 'r')
|
98
|
+
self.open(filename, mode)
|
99
|
+
end
|
100
|
+
|
101
|
+
def size(filename)
|
102
|
+
@mapped_zip.get_entry(filename).size
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns nil for not found and nil for directories
|
106
|
+
def size?(filename)
|
107
|
+
entry = @mapped_zip.find_entry(filename)
|
108
|
+
entry.nil? || entry.directory? ? nil : entry.size
|
109
|
+
end
|
110
|
+
|
111
|
+
def chown(owner, group, *filenames)
|
112
|
+
filenames.each do |filename|
|
113
|
+
e = find_entry(filename)
|
114
|
+
e.extra.create('IUnix') unless e.extra.member?('IUnix')
|
115
|
+
e.extra['IUnix'].uid = owner
|
116
|
+
e.extra['IUnix'].gid = group
|
117
|
+
end
|
118
|
+
filenames.size
|
119
|
+
end
|
120
|
+
|
121
|
+
def chmod(mode, *filenames)
|
122
|
+
filenames.each do |filename|
|
123
|
+
e = find_entry(filename)
|
124
|
+
e.fstype = FSTYPE_UNIX # Force conversion filesystem type to unix.
|
125
|
+
e.unix_perms = mode
|
126
|
+
e.external_file_attributes = mode << 16
|
127
|
+
end
|
128
|
+
filenames.size
|
129
|
+
end
|
130
|
+
|
131
|
+
def zero?(filename)
|
132
|
+
sz = size(filename)
|
133
|
+
sz.nil? || sz == 0
|
134
|
+
rescue Errno::ENOENT
|
135
|
+
false
|
136
|
+
end
|
137
|
+
|
138
|
+
def file?(filename)
|
139
|
+
entry = @mapped_zip.find_entry(filename)
|
140
|
+
!entry.nil? && entry.file?
|
141
|
+
end
|
142
|
+
|
143
|
+
def dirname(filename)
|
144
|
+
::File.dirname(filename)
|
145
|
+
end
|
146
|
+
|
147
|
+
def basename(filename)
|
148
|
+
::File.basename(filename)
|
149
|
+
end
|
150
|
+
|
151
|
+
def split(filename)
|
152
|
+
::File.split(filename)
|
153
|
+
end
|
154
|
+
|
155
|
+
def join(*fragments)
|
156
|
+
::File.join(*fragments)
|
157
|
+
end
|
158
|
+
|
159
|
+
def utime(modified_time, *filenames)
|
160
|
+
filenames.each do |filename|
|
161
|
+
find_entry(filename).time = modified_time
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def mtime(filename)
|
166
|
+
@mapped_zip.get_entry(filename).mtime
|
167
|
+
end
|
168
|
+
|
169
|
+
def atime(filename)
|
170
|
+
@mapped_zip.get_entry(filename).atime
|
171
|
+
end
|
172
|
+
|
173
|
+
def ctime(filename)
|
174
|
+
@mapped_zip.get_entry(filename).ctime
|
175
|
+
end
|
176
|
+
|
177
|
+
def pipe?(_filename)
|
178
|
+
false
|
179
|
+
end
|
180
|
+
|
181
|
+
def blockdev?(_filename)
|
182
|
+
false
|
183
|
+
end
|
184
|
+
|
185
|
+
def chardev?(_filename)
|
186
|
+
false
|
187
|
+
end
|
188
|
+
|
189
|
+
def symlink?(filename)
|
190
|
+
@mapped_zip.get_entry(filename).symlink?
|
191
|
+
end
|
192
|
+
|
193
|
+
def socket?(_filename)
|
194
|
+
false
|
195
|
+
end
|
196
|
+
|
197
|
+
def ftype(filename)
|
198
|
+
@mapped_zip.get_entry(filename).directory? ? 'directory' : 'file'
|
199
|
+
end
|
200
|
+
|
201
|
+
def readlink(_filename)
|
202
|
+
raise NotImplementedError, 'The readlink() function is not implemented'
|
203
|
+
end
|
204
|
+
|
205
|
+
def symlink(_filename, _symlink_name)
|
206
|
+
raise NotImplementedError, 'The symlink() function is not implemented'
|
207
|
+
end
|
208
|
+
|
209
|
+
def link(_filename, _symlink_name)
|
210
|
+
raise NotImplementedError, 'The link() function is not implemented'
|
211
|
+
end
|
212
|
+
|
213
|
+
def pipe
|
214
|
+
raise NotImplementedError, 'The pipe() function is not implemented'
|
215
|
+
end
|
216
|
+
|
217
|
+
def stat(filename)
|
218
|
+
raise Errno::ENOENT, filename unless exists?(filename)
|
219
|
+
|
220
|
+
Stat.new(self, filename)
|
221
|
+
end
|
222
|
+
|
223
|
+
alias lstat stat
|
224
|
+
|
225
|
+
def readlines(filename)
|
226
|
+
self.open(filename, &:readlines)
|
227
|
+
end
|
228
|
+
|
229
|
+
def read(filename)
|
230
|
+
@mapped_zip.read(filename)
|
231
|
+
end
|
232
|
+
|
233
|
+
def popen(*args, &a_proc)
|
234
|
+
::File.popen(*args, &a_proc)
|
235
|
+
end
|
236
|
+
|
237
|
+
def foreach(filename, sep = $INPUT_RECORD_SEPARATOR, &a_proc)
|
238
|
+
self.open(filename) { |is| is.each_line(sep, &a_proc) }
|
239
|
+
end
|
240
|
+
|
241
|
+
def delete(*args)
|
242
|
+
args.each do |filename|
|
243
|
+
if directory?(filename)
|
244
|
+
raise Errno::EISDIR, "Is a directory - \"#{filename}\""
|
245
|
+
end
|
246
|
+
|
247
|
+
@mapped_zip.remove(filename)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def rename(file_to_rename, new_name)
|
252
|
+
@mapped_zip.rename(file_to_rename, new_name) { true }
|
253
|
+
end
|
254
|
+
|
255
|
+
alias unlink delete
|
256
|
+
|
257
|
+
def expand_path(path)
|
258
|
+
@mapped_zip.expand_path(path)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zip
|
4
|
+
module FileSystem
|
5
|
+
class File # :nodoc:all
|
6
|
+
class Stat # :nodoc:all
|
7
|
+
class << self
|
8
|
+
def delegate_to_fs_file(*methods)
|
9
|
+
methods.each do |method|
|
10
|
+
class_exec do
|
11
|
+
define_method(method) do
|
12
|
+
@zip_fs_file.__send__(method, @entry_name)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(zip_fs_file, entry_name)
|
20
|
+
@zip_fs_file = zip_fs_file
|
21
|
+
@entry_name = entry_name
|
22
|
+
end
|
23
|
+
|
24
|
+
def kind_of?(type)
|
25
|
+
super || type == ::File::Stat
|
26
|
+
end
|
27
|
+
|
28
|
+
delegate_to_fs_file :file?, :directory?, :pipe?, :chardev?, :symlink?,
|
29
|
+
:socket?, :blockdev?, :readable?, :readable_real?, :writable?, :ctime,
|
30
|
+
:writable_real?, :executable?, :executable_real?, :sticky?, :owned?,
|
31
|
+
:grpowned?, :setuid?, :setgid?, :zero?, :size, :size?, :mtime, :atime
|
32
|
+
|
33
|
+
def blocks
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def gid
|
38
|
+
e = find_entry
|
39
|
+
if e.extra.member? 'IUnix'
|
40
|
+
e.extra['IUnix'].gid || 0
|
41
|
+
else
|
42
|
+
0
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def uid
|
47
|
+
e = find_entry
|
48
|
+
if e.extra.member? 'IUnix'
|
49
|
+
e.extra['IUnix'].uid || 0
|
50
|
+
else
|
51
|
+
0
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def ino
|
56
|
+
0
|
57
|
+
end
|
58
|
+
|
59
|
+
def dev
|
60
|
+
0
|
61
|
+
end
|
62
|
+
|
63
|
+
def rdev
|
64
|
+
0
|
65
|
+
end
|
66
|
+
|
67
|
+
def rdev_major
|
68
|
+
0
|
69
|
+
end
|
70
|
+
|
71
|
+
def rdev_minor
|
72
|
+
0
|
73
|
+
end
|
74
|
+
|
75
|
+
def ftype
|
76
|
+
if file?
|
77
|
+
'file'
|
78
|
+
elsif directory?
|
79
|
+
'directory'
|
80
|
+
else
|
81
|
+
raise StandardError, 'Unknown file type'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def nlink
|
86
|
+
1
|
87
|
+
end
|
88
|
+
|
89
|
+
def blksize
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
|
93
|
+
def mode
|
94
|
+
e = find_entry
|
95
|
+
if e.fstype == FSTYPE_UNIX
|
96
|
+
e.external_file_attributes >> 16
|
97
|
+
else
|
98
|
+
0o100_666 # Equivalent to -rw-rw-rw-.
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def find_entry
|
105
|
+
@zip_fs_file.find_entry(@entry_name)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zip
|
4
|
+
module FileSystem
|
5
|
+
# All access to Zip::File from FileSystem::File and FileSystem::Dir
|
6
|
+
# goes through a ZipFileNameMapper, which has one responsibility: ensure
|
7
|
+
class ZipFileNameMapper # :nodoc:all
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
def initialize(zip_file)
|
11
|
+
@zip_file = zip_file
|
12
|
+
@pwd = '/'
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :pwd
|
16
|
+
|
17
|
+
def find_entry(filename)
|
18
|
+
@zip_file.find_entry(expand_to_entry(filename))
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_entry(filename)
|
22
|
+
@zip_file.get_entry(expand_to_entry(filename))
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_input_stream(filename, &a_proc)
|
26
|
+
@zip_file.get_input_stream(expand_to_entry(filename), &a_proc)
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_output_stream(filename, permissions = nil, &a_proc)
|
30
|
+
@zip_file.get_output_stream(
|
31
|
+
expand_to_entry(filename), permissions: permissions, &a_proc
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def glob(pattern, *flags, &block)
|
36
|
+
@zip_file.glob(expand_to_entry(pattern), *flags, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def read(filename)
|
40
|
+
@zip_file.read(expand_to_entry(filename))
|
41
|
+
end
|
42
|
+
|
43
|
+
def remove(filename)
|
44
|
+
@zip_file.remove(expand_to_entry(filename))
|
45
|
+
end
|
46
|
+
|
47
|
+
def rename(filename, new_name, &continue_on_exists_proc)
|
48
|
+
@zip_file.rename(
|
49
|
+
expand_to_entry(filename),
|
50
|
+
expand_to_entry(new_name),
|
51
|
+
&continue_on_exists_proc
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def mkdir(filename, permissions = 0o755)
|
56
|
+
@zip_file.mkdir(expand_to_entry(filename), permissions)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Turns entries into strings and adds leading /
|
60
|
+
# and removes trailing slash on directories
|
61
|
+
def each
|
62
|
+
@zip_file.each do |e|
|
63
|
+
yield("/#{e.to_s.chomp('/')}")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def expand_path(path)
|
68
|
+
expanded = path.start_with?('/') ? path.dup : ::File.join(@pwd, path)
|
69
|
+
expanded.gsub!(/\/\.(\/|$)/, '')
|
70
|
+
expanded.gsub!(/[^\/]+\/\.\.(\/|$)/, '')
|
71
|
+
expanded.empty? ? '/' : expanded
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def expand_to_entry(path)
|
77
|
+
expand_path(path)[1..-1]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|