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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 186593f6df9a85bfd1247d7ac48d2e2516d21d3dc7cd92e97774667ecc0768be
4
- data.tar.gz: 7b4c0465dddd0c67822fca7f7d44c5e134dea5dae4e839f307975d42aacf5728
3
+ metadata.gz: e4f402eb6b43dd9a6a5b62d59d60d4ab949e9ea9c4e03be89354ab71624f5c74
4
+ data.tar.gz: 96e28996b32be3a777e7e8a1517e746536851e8a5e5cd330854a2e75b763be70
5
5
  SHA512:
6
- metadata.gz: 76239123207c12d071fa25ac28a7ee9e1d1796e3a68f15a5d124eeaf695590900130dff61cfe3e8af8c92f6b48ffa41e9be6e311ab0e93c42da5b0fb7efa1da8
7
- data.tar.gz: e8bfcc562c8569b8f9168757406dfe162f31417a5bf7fb3b658b99a76de6896fa9b8dd3b5d73f8e1c33b434499dadc332a9c5dd6659185359a0d278d41b872c5
6
+ metadata.gz: 0b941f8cb82d7a0a73782cd59e4e6185c4e28bd4c3e41f5eaaff1056e8503747df7012ba5cbe6a515d2138183bed3502f977b62f5cf6ab1909d9d7efdc3cd956
7
+ data.tar.gz: 108091d73b67d1cff944b04fc9c0123490b2234fcf1d53a53858bfa341204ab1bb7048b71cc4e596cb979c4ef92d62f0ebce6d914267f0012fe5e387477e7d40
@@ -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: :number },
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
@@ -7,7 +7,7 @@ module MiniTarball
7
7
  end
8
8
 
9
9
  def write(data)
10
- @io.write (data)
10
+ @io.write(data)
11
11
  end
12
12
  end
13
13
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniTarball
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -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
- check_io!(io)
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 add_file(name:, mode: 0644, uname: "nobody", gname: "nogroup", uid: nil, gid: nil, mtime: nil)
39
- check_closed!
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
- check_closed!
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 check_io!(io)
141
+ def ensure_valid_io!(io)
79
142
  raise "No IO object given" unless io.respond_to?(:pos) &&
80
- io.respond_to?(:seek) && io.respond_to?(:write) && io.respond_to?(:close)
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 check_closed!
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/write_only_stream'
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.1.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: 2021-02-22 00:00:00.000000000 Z
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.6.0
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.0.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.