ffi-libarchive 0.2.0 → 0.4.1
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 +5 -5
- data/Gemfile +3 -3
- data/LICENSE +201 -674
- data/README.md +69 -4
- data/Rakefile +26 -9
- data/VERSION +1 -0
- data/ffi-libarchive.gemspec +13 -13
- data/lib/ffi-libarchive.rb +10 -11
- data/lib/ffi-libarchive/archive.rb +94 -43
- data/lib/ffi-libarchive/entry.rb +148 -156
- data/lib/ffi-libarchive/reader.rb +31 -34
- data/lib/ffi-libarchive/stat.rb +14 -14
- data/lib/ffi-libarchive/version.rb +1 -1
- data/lib/ffi-libarchive/writer.rb +27 -34
- data/test/sets/ts_read.rb +91 -93
- data/test/sets/ts_write.rb +93 -93
- data/test/test_ffi-libarchive.rb +4 -4
- metadata +22 -18
- data/.gitattributes +0 -6
- data/.gitignore +0 -55
- data/.rubocop.yml +0 -41
- data/.yardopts +0 -5
@@ -1,10 +1,8 @@
|
|
1
1
|
module Archive
|
2
|
-
|
3
2
|
class Reader < BaseArchive
|
4
|
-
|
5
3
|
private_class_method :new
|
6
4
|
|
7
|
-
def self.open_filename
|
5
|
+
def self.open_filename(file_name, command = nil)
|
8
6
|
if block_given?
|
9
7
|
reader = open_filename file_name, command
|
10
8
|
begin
|
@@ -13,11 +11,11 @@ module Archive
|
|
13
11
|
reader.close
|
14
12
|
end
|
15
13
|
else
|
16
|
-
new :
|
14
|
+
new file_name: file_name, command: command
|
17
15
|
end
|
18
16
|
end
|
19
17
|
|
20
|
-
def self.open_memory
|
18
|
+
def self.open_memory(string, command = nil)
|
21
19
|
if block_given?
|
22
20
|
reader = open_memory string, command
|
23
21
|
begin
|
@@ -26,50 +24,50 @@ module Archive
|
|
26
24
|
reader.close
|
27
25
|
end
|
28
26
|
else
|
29
|
-
new :
|
27
|
+
new memory: string, command: command
|
30
28
|
end
|
31
29
|
end
|
32
30
|
|
33
|
-
def initialize
|
34
|
-
super C
|
31
|
+
def initialize(params = {})
|
32
|
+
super C.method(:archive_read_new), C.method(:archive_read_finish)
|
35
33
|
|
36
34
|
if params[:command]
|
37
35
|
cmd = params[:command]
|
38
|
-
raise Error, @archive if C
|
36
|
+
raise Error, @archive if C.archive_read_support_compression_program(archive, cmd) != C::OK
|
39
37
|
else
|
40
|
-
raise Error, @archive if C
|
38
|
+
raise Error, @archive if C.archive_read_support_compression_all(archive) != C::OK
|
41
39
|
end
|
42
40
|
|
43
|
-
raise Error, @archive if C
|
41
|
+
raise Error, @archive if C.archive_read_support_format_all(archive) != C::OK
|
44
42
|
|
45
43
|
if params[:file_name]
|
46
|
-
raise Error, @archive if C
|
44
|
+
raise Error, @archive if C.archive_read_open_filename(archive, params[:file_name], 1024) != C::OK
|
47
45
|
elsif params[:memory]
|
48
46
|
str = params[:memory]
|
49
47
|
@data = FFI::MemoryPointer.new(str.bytesize + 1)
|
50
48
|
@data.write_string str, str.bytesize
|
51
|
-
raise Error, @archive if C
|
49
|
+
raise Error, @archive if C.archive_read_open_memory(archive, @data, str.bytesize) != C::OK
|
52
50
|
end
|
53
51
|
rescue
|
54
52
|
close
|
55
53
|
raise
|
56
54
|
end
|
57
55
|
|
58
|
-
def extract
|
59
|
-
raise ArgumentError, "Expected Archive::Entry as first argument" unless entry.
|
60
|
-
raise ArgumentError, "Expected Integer as second argument" unless flags.
|
56
|
+
def extract(entry, flags = 0)
|
57
|
+
raise ArgumentError, "Expected Archive::Entry as first argument" unless entry.is_a? Entry
|
58
|
+
raise ArgumentError, "Expected Integer as second argument" unless flags.is_a? Integer
|
61
59
|
|
62
60
|
flags &= EXTRACT_FFLAGS
|
63
|
-
raise Error, @archive if C
|
61
|
+
raise Error, @archive if C.archive_read_extract(archive, entry.entry, flags) != C::OK
|
64
62
|
end
|
65
63
|
|
66
64
|
def header_position
|
67
|
-
raise Error, @archive if C
|
65
|
+
raise Error, @archive if C.archive_read_header_position archive
|
68
66
|
end
|
69
67
|
|
70
68
|
def next_header
|
71
69
|
entry_ptr = FFI::MemoryPointer.new(:pointer)
|
72
|
-
case C
|
70
|
+
case C.archive_read_next_header(archive, entry_ptr)
|
73
71
|
when C::OK
|
74
72
|
Entry.from_pointer entry_ptr.read_pointer
|
75
73
|
when C::EOF
|
@@ -81,34 +79,35 @@ module Archive
|
|
81
79
|
end
|
82
80
|
|
83
81
|
def each_entry
|
84
|
-
while entry = next_header
|
82
|
+
while (entry = next_header)
|
85
83
|
yield entry
|
86
84
|
end
|
87
85
|
end
|
88
86
|
|
89
|
-
def each_entry_with_data(
|
90
|
-
while entry = next_header
|
87
|
+
def each_entry_with_data(_size = C::DATA_BUFFER_SIZE)
|
88
|
+
while (entry = next_header)
|
91
89
|
yield entry, read_data
|
92
90
|
end
|
93
91
|
end
|
94
92
|
|
95
|
-
def read_data
|
96
|
-
raise ArgumentError, "Buffer size must be > 0 (was: #{size})" unless size.
|
93
|
+
def read_data(size = C::DATA_BUFFER_SIZE)
|
94
|
+
raise ArgumentError, "Buffer size must be > 0 (was: #{size})" unless size.is_a?(Integer) && size > 0
|
97
95
|
|
98
96
|
data = nil
|
99
|
-
unless block
|
100
|
-
data = ""
|
101
|
-
block = data.method :concat
|
102
|
-
end
|
103
97
|
|
104
98
|
buffer = FFI::MemoryPointer.new(size)
|
105
99
|
len = 0
|
106
|
-
while (n = C
|
100
|
+
while (n = C.archive_read_data(archive, buffer, size)) > 0
|
107
101
|
case n
|
108
102
|
when C::FATAL, C::WARN, C::RETRY
|
109
103
|
raise Error, @archive
|
110
104
|
else
|
111
|
-
|
105
|
+
if block_given?
|
106
|
+
yield buffer.get_bytes(0, n)
|
107
|
+
else
|
108
|
+
data ||= ""
|
109
|
+
data.concat(buffer.get_bytes(0, n))
|
110
|
+
end
|
112
111
|
end
|
113
112
|
len += n
|
114
113
|
end
|
@@ -116,12 +115,10 @@ module Archive
|
|
116
115
|
data || len
|
117
116
|
end
|
118
117
|
|
119
|
-
def save_data
|
118
|
+
def save_data(file_name)
|
120
119
|
IO.sysopen(file_name, "wb") do |fd|
|
121
|
-
raise Error, @archive if C
|
120
|
+
raise Error, @archive if C.archive_read_data_into_fd(archive, fd) != C::OK
|
122
121
|
end
|
123
122
|
end
|
124
|
-
|
125
123
|
end
|
126
|
-
|
127
124
|
end
|
data/lib/ffi-libarchive/stat.rb
CHANGED
@@ -1,38 +1,38 @@
|
|
1
|
-
require
|
1
|
+
require "ffi-inliner"
|
2
2
|
|
3
3
|
module Archive
|
4
4
|
module Stat
|
5
5
|
extend Inliner
|
6
6
|
inline do |builder|
|
7
|
-
builder.include
|
8
|
-
builder.include
|
9
|
-
builder.include
|
10
|
-
builder.include
|
11
|
-
builder.include
|
12
|
-
builder.c
|
7
|
+
builder.include "stdlib.h"
|
8
|
+
builder.include "sys/types.h"
|
9
|
+
builder.include "sys/stat.h"
|
10
|
+
builder.include "string.h"
|
11
|
+
builder.include "errno.h"
|
12
|
+
builder.c '
|
13
13
|
void* ffi_libarchive_create_stat(const char* filename) {
|
14
14
|
struct stat* s = malloc(sizeof(struct stat));
|
15
15
|
if (stat(filename, s) != 0) return NULL;
|
16
16
|
return s;
|
17
17
|
}
|
18
|
-
|
19
|
-
builder.c
|
18
|
+
'
|
19
|
+
builder.c '
|
20
20
|
void* ffi_libarchive_create_lstat(const char* filename) {
|
21
21
|
struct stat* s = malloc(sizeof(struct stat));
|
22
22
|
lstat(filename, s);
|
23
23
|
return s;
|
24
24
|
}
|
25
|
-
|
26
|
-
builder.c
|
25
|
+
'
|
26
|
+
builder.c '
|
27
27
|
void ffi_libarchive_free_stat(void* s) {
|
28
28
|
free((struct stat*)s);
|
29
29
|
}
|
30
|
-
|
31
|
-
builder.c
|
30
|
+
'
|
31
|
+
builder.c '
|
32
32
|
const char* ffi_error() {
|
33
33
|
return strerror(errno);
|
34
34
|
}
|
35
|
-
|
35
|
+
'
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -1,10 +1,8 @@
|
|
1
1
|
module Archive
|
2
|
-
|
3
2
|
class Writer < BaseArchive
|
4
|
-
|
5
3
|
private_class_method :new
|
6
4
|
|
7
|
-
def self.open_filename
|
5
|
+
def self.open_filename(file_name, compression, format)
|
8
6
|
if block_given?
|
9
7
|
writer = open_filename file_name, compression, format
|
10
8
|
begin
|
@@ -13,11 +11,11 @@ module Archive
|
|
13
11
|
writer.close
|
14
12
|
end
|
15
13
|
else
|
16
|
-
new :
|
14
|
+
new file_name: file_name, compression: compression, format: format
|
17
15
|
end
|
18
16
|
end
|
19
17
|
|
20
|
-
def self.open_memory
|
18
|
+
def self.open_memory(string, compression, format)
|
21
19
|
if block_given?
|
22
20
|
writer = open_memory string, compression, format
|
23
21
|
begin
|
@@ -26,57 +24,54 @@ module Archive
|
|
26
24
|
writer.close
|
27
25
|
end
|
28
26
|
else
|
29
|
-
if compression.
|
30
|
-
command = compression
|
27
|
+
if compression.is_a? String
|
31
28
|
compression = -1
|
32
|
-
else
|
33
|
-
command = nil
|
34
29
|
end
|
35
|
-
new :
|
30
|
+
new memory: string, compression: compression, format: format
|
36
31
|
end
|
37
32
|
end
|
38
33
|
|
39
|
-
def initialize
|
40
|
-
super C
|
34
|
+
def initialize(params = {})
|
35
|
+
super C.method(:archive_write_new), C.method(:archive_write_finish)
|
41
36
|
|
42
37
|
compression = params[:compression]
|
43
38
|
case compression
|
44
39
|
when Symbol
|
45
|
-
compression = Archive
|
40
|
+
compression = Archive.const_get("COMPRESSION_#{compression.to_s.upcase}".to_sym)
|
46
41
|
end
|
47
42
|
|
48
43
|
format = params[:format]
|
49
44
|
case format
|
50
45
|
when Symbol
|
51
|
-
format = Archive
|
46
|
+
format = Archive.const_get("FORMAT_#{format.to_s.upcase}".to_sym)
|
52
47
|
end
|
53
48
|
|
54
|
-
raise Error, @archive if C
|
49
|
+
raise Error, @archive if C.archive_write_set_compression(archive, compression) != C::OK
|
55
50
|
|
56
|
-
raise Error, @archive if C
|
51
|
+
raise Error, @archive if C.archive_write_set_format(archive, format) != C::OK
|
57
52
|
|
58
53
|
if params[:file_name]
|
59
|
-
raise Error, @archive if C
|
54
|
+
raise Error, @archive if C.archive_write_open_filename(archive, params[:file_name]) != C::OK
|
60
55
|
elsif params[:memory]
|
61
|
-
if C
|
62
|
-
C
|
56
|
+
if C.archive_write_get_bytes_in_last_block(@archive) == -1
|
57
|
+
C.archive_write_set_bytes_in_last_block(archive, 1)
|
63
58
|
end
|
64
59
|
@data = write_callback params[:memory]
|
65
|
-
raise Error, @archive if C
|
60
|
+
raise Error, @archive if C.archive_write_open(archive, nil,
|
66
61
|
nil,
|
67
62
|
@data,
|
68
63
|
nil) != C::OK
|
69
64
|
end
|
70
|
-
rescue
|
65
|
+
rescue
|
71
66
|
close
|
72
67
|
raise
|
73
68
|
end
|
74
69
|
|
75
|
-
def write_callback
|
76
|
-
|
77
|
-
data.concat buffer.get_bytes(0,length)
|
70
|
+
def write_callback(data)
|
71
|
+
proc do |_ar, _client, buffer, length|
|
72
|
+
data.concat buffer.get_bytes(0, length)
|
78
73
|
length
|
79
|
-
|
74
|
+
end
|
80
75
|
end
|
81
76
|
private :write_callback
|
82
77
|
|
@@ -94,7 +89,7 @@ module Archive
|
|
94
89
|
end
|
95
90
|
end
|
96
91
|
|
97
|
-
def add_entry
|
92
|
+
def add_entry
|
98
93
|
raise ArgumentError, "No block given" unless block_given?
|
99
94
|
|
100
95
|
entry = Entry.new
|
@@ -111,15 +106,15 @@ module Archive
|
|
111
106
|
entry.close
|
112
107
|
end
|
113
108
|
|
114
|
-
def write_data
|
109
|
+
def write_data(*args)
|
115
110
|
if block_given?
|
116
111
|
raise ArgumentError, "wrong number of argument (#{args.size} for 0)" if args.size > 0
|
117
112
|
|
118
113
|
ar = archive
|
119
114
|
len = 0
|
120
|
-
|
115
|
+
loop do
|
121
116
|
str = yield
|
122
|
-
if (
|
117
|
+
if (n = C.archive_write_data(ar, str, str.bytesize)) < 1
|
123
118
|
return len
|
124
119
|
end
|
125
120
|
len += n
|
@@ -127,14 +122,12 @@ module Archive
|
|
127
122
|
else
|
128
123
|
raise ArgumentError, "wrong number of argument (#{args.size}) for 1)" if args.size != 1
|
129
124
|
str = args[0]
|
130
|
-
C
|
125
|
+
C.archive_write_data(archive, str, str.bytesize)
|
131
126
|
end
|
132
127
|
end
|
133
128
|
|
134
|
-
def write_header
|
135
|
-
raise Error, @archive if C
|
129
|
+
def write_header(entry)
|
130
|
+
raise Error, @archive if C.archive_write_header(archive, entry.entry) != C::OK
|
136
131
|
end
|
137
|
-
|
138
132
|
end
|
139
|
-
|
140
133
|
end
|
data/test/sets/ts_read.rb
CHANGED
@@ -1,119 +1,117 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "ffi-libarchive"
|
2
|
+
require "tmpdir"
|
3
|
+
require "test/unit"
|
4
4
|
|
5
5
|
class TS_ReadArchive < Test::Unit::TestCase
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
7
|
+
CONTENT_SPEC =
|
8
|
+
[
|
9
|
+
["test/", "directory", 0755, nil ],
|
10
|
+
["test/b/", "directory", 0755, nil ],
|
11
|
+
["test/b/c/", "directory", 0755, nil ],
|
12
|
+
["test/b/c/c.dat", "file", 0600, "\266\262\v_\266\243\305\3601\204\277\351\354\265\003\036\036\365f\377\210\205\032\222\346\370b\360u\032Y\301".b ],
|
13
|
+
["test/b/c/d/", "directory", 0711, nil ],
|
14
|
+
["test/b/c/d/d.dat", "symbolic_link", 0777, "../c.dat" ],
|
15
|
+
["test/b/b.dat", "file", 0640, "s&\245\354(M\331=\270\000!s\355\240\252\355'N\304\343\bY\317\t\274\210\3128\321\347\234!".b ],
|
16
|
+
["test/a.dat", "file", 0777, "\021\216\231Y\354\236\271\372\336\213\224R\211{D{\277\262\304\211xu\330\\\275@~\035\vSRM".b ]
|
17
|
+
]
|
18
|
+
|
19
|
+
def setup
|
20
|
+
File.open("data/test.tar.gz", "rb") do |f|
|
21
|
+
@archive_content = f.read
|
23
22
|
end
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
verify_content(ar)
|
29
|
-
end
|
25
|
+
def test_read_tar_gz_from_file
|
26
|
+
Archive.read_open_filename("data/test.tar.gz") do |ar|
|
27
|
+
verify_content(ar)
|
30
28
|
end
|
29
|
+
end
|
31
30
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
31
|
+
def test_read_tar_gz_from_file_with_external_gunzip
|
32
|
+
Archive.read_open_filename("data/test.tar.gz", "gunzip") do |ar|
|
33
|
+
verify_content(ar)
|
36
34
|
end
|
35
|
+
end
|
37
36
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
37
|
+
def test_read_tar_gz_from_memory
|
38
|
+
Archive.read_open_memory(@archive_content) do |ar|
|
39
|
+
verify_content(ar)
|
42
40
|
end
|
41
|
+
end
|
43
42
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
43
|
+
def test_read_tar_gz_from_memory_with_external_gunzip
|
44
|
+
Archive.read_open_memory(@archive_content, "gunzip") do |ar|
|
45
|
+
verify_content(ar)
|
48
46
|
end
|
47
|
+
end
|
49
48
|
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
def test_read_entry_bigger_than_internal_buffer
|
50
|
+
alphabet = "abcdefghijklmnopqrstuvwxyz"
|
51
|
+
entry_size = 1024 * 4 - 3
|
53
52
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
53
|
+
srand
|
54
|
+
content = ""
|
55
|
+
1.upto(entry_size) do |i|
|
56
|
+
index = rand(alphabet.size)
|
57
|
+
content += alphabet[index, 1]
|
58
|
+
end
|
60
59
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
Archive.read_open_filename(dir + '/test.tar.gz') do |ar|
|
77
|
-
entry = ar.next_header
|
78
|
-
data = ar.read_data
|
79
|
-
|
80
|
-
assert_equal entry_size, data.size
|
81
|
-
assert_equal content.size, data.size
|
82
|
-
assert_equal content, data
|
83
|
-
end
|
84
|
-
|
85
|
-
Archive.read_open_filename(dir + '/test.tar.gz') do |ar|
|
86
|
-
entry = ar.next_header
|
87
|
-
data = ""
|
88
|
-
ar.read_data(128) { |chunk| data += chunk }
|
89
|
-
|
90
|
-
assert_equal content, data
|
91
|
-
end
|
60
|
+
Dir.mktmpdir do |dir|
|
61
|
+
Archive.write_open_filename(dir + "/test.tar.gz",
|
62
|
+
Archive::COMPRESSION_BZIP2, Archive::FORMAT_TAR) do |ar|
|
63
|
+
ar.new_entry do |entry|
|
64
|
+
entry.pathname = "chubby.dat"
|
65
|
+
entry.mode = 0666
|
66
|
+
entry.filetype = Archive::Entry::FILE
|
67
|
+
entry.atime = Time.now.to_i
|
68
|
+
entry.mtime = Time.now.to_i
|
69
|
+
entry.size = entry_size
|
70
|
+
ar.write_header(entry)
|
71
|
+
ar.write_data(content)
|
92
72
|
end
|
73
|
+
end
|
74
|
+
|
75
|
+
Archive.read_open_filename(dir + "/test.tar.gz") do |ar|
|
76
|
+
ar.next_header
|
77
|
+
data = ar.read_data
|
78
|
+
|
79
|
+
assert_equal entry_size, data.size
|
80
|
+
assert_equal content.size, data.size
|
81
|
+
assert_equal content, data
|
82
|
+
end
|
83
|
+
|
84
|
+
Archive.read_open_filename(dir + "/test.tar.gz") do |ar|
|
85
|
+
ar.next_header
|
86
|
+
data = ""
|
87
|
+
ar.read_data(128) { |chunk| data += chunk }
|
88
|
+
|
89
|
+
assert_equal content, data
|
90
|
+
end
|
93
91
|
end
|
92
|
+
end
|
94
93
|
|
95
|
-
|
94
|
+
private
|
96
95
|
|
97
|
-
|
98
|
-
|
96
|
+
def verify_content(ar)
|
97
|
+
content_spec_idx = 0
|
99
98
|
|
100
|
-
|
101
|
-
|
102
|
-
CONTENT_SPEC[content_spec_idx]
|
99
|
+
while (entry = ar.next_header)
|
100
|
+
expect_pathname, expect_type, expect_mode, expect_content = CONTENT_SPEC[content_spec_idx]
|
103
101
|
|
104
|
-
|
105
|
-
|
106
|
-
|
102
|
+
assert_equal expect_pathname, entry.pathname
|
103
|
+
assert_equal entry.send("#{expect_type}?"), true
|
104
|
+
assert_equal expect_mode, (entry.mode & 07777)
|
107
105
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
106
|
+
if entry.symbolic_link?
|
107
|
+
assert_equal expect_content, entry.symlink
|
108
|
+
elsif entry.file?
|
109
|
+
content = ar.read_data(1024)
|
110
|
+
assert_equal expect_content, content
|
111
|
+
end
|
114
112
|
|
115
|
-
|
116
|
-
end
|
113
|
+
content_spec_idx += 1
|
117
114
|
end
|
115
|
+
end
|
118
116
|
|
119
117
|
end
|