mini_tarball 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mini_tarball/header.rb +1 -1
- data/lib/mini_tarball/header_formatter.rb +9 -0
- data/lib/mini_tarball/header_writer.rb +2 -0
- data/lib/mini_tarball/streams/limited_size_stream.rb +23 -0
- data/lib/mini_tarball/streams/placeholder_stream.rb +14 -0
- data/lib/mini_tarball/{write_only_stream.rb → streams/write_only_stream.rb} +1 -1
- data/lib/mini_tarball/version.rb +1 -1
- data/lib/mini_tarball/writer.rb +70 -10
- data/lib/mini_tarball.rb +3 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4f402eb6b43dd9a6a5b62d59d60d4ab949e9ea9c4e03be89354ab71624f5c74
|
4
|
+
data.tar.gz: 96e28996b32be3a777e7e8a1517e746536851e8a5e5cd330854a2e75b763be70
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b941f8cb82d7a0a73782cd59e4e6185c4e28bd4c3e41f5eaaff1056e8503747df7012ba5cbe6a515d2138183bed3502f977b62f5cf6ab1909d9d7efdc3cd956
|
7
|
+
data.tar.gz: 108091d73b67d1cff944b04fc9c0123490b2234fcf1d53a53858bfa341204ab1bb7048b71cc4e596cb979c4ef92d62f0ebce6d914267f0012fe5e387477e7d40
|
data/lib/mini_tarball/header.rb
CHANGED
@@ -11,7 +11,7 @@ module MiniTarball
|
|
11
11
|
# rubocop:disable Layout/HashAlignment
|
12
12
|
FIELDS = {
|
13
13
|
name: { length: 100, type: :chars },
|
14
|
-
mode: { length: 8, type: :
|
14
|
+
mode: { length: 8, type: :mode },
|
15
15
|
uid: { length: 8, type: :number },
|
16
16
|
gid: { length: 8, type: :number },
|
17
17
|
size: { length: 12, type: :number },
|
@@ -4,6 +4,8 @@ module MiniTarball
|
|
4
4
|
ValueTooLargeError = Class.new(StandardError)
|
5
5
|
|
6
6
|
class HeaderFormatter
|
7
|
+
PERMISSION_BITMASK = 0007777
|
8
|
+
|
7
9
|
# @param value [Integer]
|
8
10
|
# @param length [Integer]
|
9
11
|
def self.format_number(value, length)
|
@@ -38,5 +40,12 @@ module MiniTarball
|
|
38
40
|
|
39
41
|
encoded.pack("C#{length}")
|
40
42
|
end
|
43
|
+
|
44
|
+
# Removes file type bitfields and returns file permissions as formatted number
|
45
|
+
# @param value [Integer]
|
46
|
+
# @param length [Integer]
|
47
|
+
def self.format_permissions(value, length)
|
48
|
+
format_number(value & PERMISSION_BITMASK, length)
|
49
|
+
end
|
41
50
|
end
|
42
51
|
end
|
@@ -22,6 +22,8 @@ module MiniTarball
|
|
22
22
|
case field[:type]
|
23
23
|
when :number
|
24
24
|
values_by_field[name] = HeaderFormatter.format_number(value, field[:length])
|
25
|
+
when :mode
|
26
|
+
values_by_field[name] = HeaderFormatter.format_permissions(value, field[:length])
|
25
27
|
when :checksum
|
26
28
|
values_by_field[name] = " " * field[:length]
|
27
29
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniTarball
|
4
|
+
class WriteOutOfRangeError < StandardError; end
|
5
|
+
|
6
|
+
class LimitedSizeStream
|
7
|
+
attr_reader :start_position, :end_position
|
8
|
+
|
9
|
+
def initialize(io, start_position:, max_file_size:)
|
10
|
+
@io = io
|
11
|
+
@start_position = start_position
|
12
|
+
@end_position = start_position + max_file_size
|
13
|
+
end
|
14
|
+
|
15
|
+
def write(data)
|
16
|
+
if @io.pos < start_position || @io.pos + data.bytesize > end_position
|
17
|
+
raise WriteOutOfRangeError.new("Writing outside of limits not allowed")
|
18
|
+
end
|
19
|
+
|
20
|
+
@io.write(data)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MiniTarball
|
4
|
+
class PlaceholderStream < LimitedSizeStream
|
5
|
+
def initialize(io, start_position:, file_size:)
|
6
|
+
super(io, start_position: start_position, max_file_size: file_size)
|
7
|
+
end
|
8
|
+
|
9
|
+
def write(data)
|
10
|
+
super(data)
|
11
|
+
@io.write("\0" * (end_position - @io.pos)) if @io.pos <= end_position
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/mini_tarball/version.rb
CHANGED
data/lib/mini_tarball/writer.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'etc'
|
4
|
+
|
3
5
|
module MiniTarball
|
4
6
|
class Writer
|
5
7
|
END_OF_TAR_BLOCK_SIZE = 1024
|
@@ -27,16 +29,39 @@ module MiniTarball
|
|
27
29
|
end
|
28
30
|
|
29
31
|
def initialize(io)
|
30
|
-
|
32
|
+
ensure_valid_io!(io)
|
31
33
|
|
32
34
|
@io = io
|
33
35
|
@write_only_io = WriteOnlyStream.new(@io)
|
34
36
|
@header_writer = HeaderWriter.new(@write_only_io)
|
35
37
|
@closed = false
|
38
|
+
@placeholders = []
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_file(name:, source_file_path:, mode: nil, uname: nil, gname: nil, uid: nil, gid: nil, mtime: nil)
|
42
|
+
ensure_not_closed!
|
43
|
+
|
44
|
+
file = File.open(source_file_path, "rb")
|
45
|
+
stat = File.stat(source_file_path)
|
46
|
+
|
47
|
+
@header_writer.write(Header.new(
|
48
|
+
name: name,
|
49
|
+
size: stat.size,
|
50
|
+
mode: mode || stat.mode,
|
51
|
+
uid: uid || stat.uid,
|
52
|
+
gid: gid || stat.gid,
|
53
|
+
uname: uname || Etc.getpwuid(stat.uid).name,
|
54
|
+
gname: gname || Etc.getgrgid(stat.gid).name,
|
55
|
+
mtime: mtime || stat.mtime
|
56
|
+
))
|
57
|
+
|
58
|
+
IO.copy_stream(file, @write_only_io)
|
59
|
+
write_padding
|
60
|
+
nil
|
36
61
|
end
|
37
62
|
|
38
|
-
def
|
39
|
-
|
63
|
+
def add_file_from_stream(name:, mode: 0644, uname: "nobody", gname: "nogroup", uid: nil, gid: nil, mtime: nil)
|
64
|
+
ensure_not_closed!
|
40
65
|
|
41
66
|
header_start_position = @io.pos
|
42
67
|
@header_writer.write(Header.new(name: name))
|
@@ -59,6 +84,44 @@ module MiniTarball
|
|
59
84
|
))
|
60
85
|
|
61
86
|
@io.seek(0, IO::SEEK_END)
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_file_placeholder(name:, file_size:)
|
91
|
+
ensure_not_closed!
|
92
|
+
|
93
|
+
placeholder = {}
|
94
|
+
placeholder[:header_start_position] = @io.pos
|
95
|
+
@header_writer.write(Header.new(name: name, size: file_size))
|
96
|
+
|
97
|
+
placeholder[:file_start_position] = @io.pos
|
98
|
+
@io.write("\0" * file_size)
|
99
|
+
placeholder[:file_size] = file_size
|
100
|
+
|
101
|
+
write_padding
|
102
|
+
|
103
|
+
@placeholders << placeholder
|
104
|
+
@placeholders.size - 1
|
105
|
+
end
|
106
|
+
|
107
|
+
def with_placeholder(index)
|
108
|
+
placeholder = @placeholders[index]
|
109
|
+
raise ArgumentError.new("Placeholder not found") if placeholder.nil?
|
110
|
+
|
111
|
+
@io.seek(placeholder[:header_start_position])
|
112
|
+
old_write_only_io = @write_only_io
|
113
|
+
@write_only_io = PlaceholderStream.new(
|
114
|
+
@io,
|
115
|
+
start_position: placeholder[:file_start_position],
|
116
|
+
file_size: placeholder[:file_size]
|
117
|
+
)
|
118
|
+
|
119
|
+
yield self
|
120
|
+
|
121
|
+
@write_only_io = old_write_only_io
|
122
|
+
@io.seek(0, IO::SEEK_END)
|
123
|
+
|
124
|
+
nil
|
62
125
|
end
|
63
126
|
|
64
127
|
def closed?
|
@@ -66,7 +129,7 @@ module MiniTarball
|
|
66
129
|
end
|
67
130
|
|
68
131
|
def close
|
69
|
-
|
132
|
+
ensure_not_closed!
|
70
133
|
|
71
134
|
@io.write("\0" * END_OF_TAR_BLOCK_SIZE)
|
72
135
|
@io.close
|
@@ -75,15 +138,12 @@ module MiniTarball
|
|
75
138
|
|
76
139
|
private
|
77
140
|
|
78
|
-
def
|
141
|
+
def ensure_valid_io!(io)
|
79
142
|
raise "No IO object given" unless io.respond_to?(:pos) &&
|
80
|
-
io.respond_to?(:
|
81
|
-
|
82
|
-
io.seek(0, IO::SEEK_END)
|
83
|
-
raise "Stream must be empty" unless io.pos == 0
|
143
|
+
io.respond_to?(:write) && io.respond_to?(:close)
|
84
144
|
end
|
85
145
|
|
86
|
-
def
|
146
|
+
def ensure_not_closed!
|
87
147
|
raise IOError.new("#{self.class} is closed") if closed?
|
88
148
|
end
|
89
149
|
|
data/lib/mini_tarball.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require 'mini_tarball/header'
|
4
4
|
require 'mini_tarball/header_formatter'
|
5
5
|
require 'mini_tarball/header_writer'
|
6
|
-
require 'mini_tarball/
|
6
|
+
require 'mini_tarball/streams/limited_size_stream'
|
7
|
+
require 'mini_tarball/streams/placeholder_stream'
|
8
|
+
require 'mini_tarball/streams/write_only_stream'
|
7
9
|
require 'mini_tarball/writer'
|
8
10
|
require 'mini_tarball/version'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mini_tarball
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Discourse
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-10-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -105,8 +105,10 @@ files:
|
|
105
105
|
- lib/mini_tarball/header.rb
|
106
106
|
- lib/mini_tarball/header_formatter.rb
|
107
107
|
- lib/mini_tarball/header_writer.rb
|
108
|
+
- lib/mini_tarball/streams/limited_size_stream.rb
|
109
|
+
- lib/mini_tarball/streams/placeholder_stream.rb
|
110
|
+
- lib/mini_tarball/streams/write_only_stream.rb
|
108
111
|
- lib/mini_tarball/version.rb
|
109
|
-
- lib/mini_tarball/write_only_stream.rb
|
110
112
|
- lib/mini_tarball/writer.rb
|
111
113
|
homepage: https://github.com/discourse/mini_tarball
|
112
114
|
licenses:
|
@@ -122,14 +124,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
122
124
|
requirements:
|
123
125
|
- - ">="
|
124
126
|
- !ruby/object:Gem::Version
|
125
|
-
version: 2.
|
127
|
+
version: 2.7.0
|
126
128
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
129
|
requirements:
|
128
130
|
- - ">="
|
129
131
|
- !ruby/object:Gem::Version
|
130
132
|
version: '0'
|
131
133
|
requirements: []
|
132
|
-
rubygems_version: 3.
|
134
|
+
rubygems_version: 3.1.6
|
133
135
|
signing_key:
|
134
136
|
specification_version: 4
|
135
137
|
summary: A minimal implementation of the GNU Tar format.
|