mini_tarball 0.1.0 → 0.3.0

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 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.