arr-pm 0.0.10 → 0.0.12
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/CHANGELOG.md +18 -0
- data/arr-pm.gemspec +4 -2
- data/lib/arr-pm/file/header.rb +2 -12
- data/lib/arr-pm/file/lead.rb +0 -2
- data/lib/arr-pm/file/tag.rb +0 -2
- data/lib/arr-pm/file.rb +28 -48
- data/lib/arr-pm/namespace.rb +6 -0
- data/lib/arr-pm/v2/architecture.rb +32 -0
- data/lib/arr-pm/v2/error.rb +32 -0
- data/lib/arr-pm/v2/format.rb +16 -0
- data/lib/arr-pm/v2/header.rb +35 -0
- data/lib/arr-pm/v2/header_header.rb +36 -0
- data/lib/arr-pm/v2/lead.rb +121 -0
- data/lib/arr-pm/v2/package.rb +18 -0
- data/lib/arr-pm/v2/tag.rb +295 -0
- data/lib/arr-pm/v2/type.rb +15 -0
- data/spec/arr-pm/v2/header_spec.rb +34 -0
- data/spec/arr-pm/v2/lead_spec.rb +125 -0
- data/spec/fixtures/example-1.0-1.x86_64.rpm +0 -0
- data/spec/fixtures/example.json +55 -0
- data/spec/fixtures/pagure-mirror-5.13.2-5.fc35.noarch.rpm +0 -0
- data/spec/rpm/file_spec.rb +57 -0
- metadata +56 -14
- data/.batcave/manifest +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1b18d6ce9e276f133f3456d3f680c9727936e0be921d1f2492f9541aeefcbca9
|
4
|
+
data.tar.gz: d5bca7b579c4ff1f28b411a0d5195a83d34c023a749d563cbc95b9cb0bb5716b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58e476d730ac09d22ea1ee60cf463f3cc26b4cb3cc99f1f8b44d7cd323084d3ca1208a550b069f56afb2414759b9f3e5475716dfee0f2363ae5a10dc0235ae38
|
7
|
+
data.tar.gz: 361d8dfca458258a617e71153ff8e757c4f8963abe5d07d9020a120407f52059a2f3b8bd0a3e2495f9764a2cc02f18d4755d2ba532f670d16c1adfc3eb85a3cb
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
# v0.0.12
|
4
|
+
|
5
|
+
* Security: Fixed a safety problem which would allow for arbitrary shell execution when invoking `RPM::File#extract` or `RPM::File#files`. (Jordan Sissel, @joernchen; #14, #15)
|
6
|
+
* This library now has no external dependencies. (Jordan Sissel, #18)
|
7
|
+
* `RPM::File#extract` now correctly works when the target directory contains spaces or other special characters. (@joernchen, #19)
|
8
|
+
* Listing files (`RPM::File#files`) no longer requires external tools like `cpio` (Jordan Sissel, #17)
|
9
|
+
|
10
|
+
|
11
|
+
# v0.0.11
|
12
|
+
|
13
|
+
* Support Ruby 3.0 (Alexey Morozov, #12)
|
14
|
+
* Fix bug caused when listing config_files on an rpm with no config files. (Daniel Jay Haskin, #7)
|
15
|
+
|
16
|
+
# Older versions
|
17
|
+
|
18
|
+
Changelog not tracked for older versions.
|
data/arr-pm.gemspec
CHANGED
@@ -2,14 +2,13 @@ Gem::Specification.new do |spec|
|
|
2
2
|
files = %x{git ls-files}.split("\n")
|
3
3
|
|
4
4
|
spec.name = "arr-pm"
|
5
|
-
spec.version = "0.0.
|
5
|
+
spec.version = "0.0.12"
|
6
6
|
spec.summary = "RPM reader and writer library"
|
7
7
|
spec.description = "This library allows to you to read and write rpm " \
|
8
8
|
"packages. Written in pure ruby because librpm is not available " \
|
9
9
|
"on all systems"
|
10
10
|
spec.license = "Apache 2"
|
11
11
|
|
12
|
-
spec.add_dependency "cabin", ">0" # for logging. apache 2 license
|
13
12
|
spec.files = files
|
14
13
|
spec.require_paths << "lib"
|
15
14
|
spec.bindir = "bin"
|
@@ -18,6 +17,9 @@ Gem::Specification.new do |spec|
|
|
18
17
|
spec.email = ["jls@semicomplete.com"]
|
19
18
|
|
20
19
|
spec.add_development_dependency "flores", ">0"
|
20
|
+
spec.add_development_dependency "rspec", ">3.0.0"
|
21
|
+
spec.add_development_dependency "stud", ">=0.0.23"
|
22
|
+
spec.add_development_dependency "insist", ">=1.0.0"
|
21
23
|
#spec.homepage = "..."
|
22
24
|
end
|
23
25
|
|
data/lib/arr-pm/file/header.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "..", "namespace"))
|
2
2
|
require File.join(File.dirname(__FILE__), "tag")
|
3
|
-
require "cabin"
|
4
3
|
|
5
4
|
class RPM::File::Header
|
6
|
-
include Cabin::Inspectable
|
7
5
|
attr_reader :tags
|
8
6
|
attr_reader :length
|
9
7
|
|
@@ -12,16 +10,8 @@ class RPM::File::Header
|
|
12
10
|
attr_accessor :data_length # rpmlib calls this field 'dl' unhelpfully
|
13
11
|
|
14
12
|
HEADER_SIGNED_TYPE = 5
|
15
|
-
|
16
|
-
|
17
|
-
# Ruby 2 forces all strings to be UTF-8, so "\x01" becomes "\u0001"
|
18
|
-
# which is two bytes 00 01 which is not what we want. I can't find
|
19
|
-
# a sane way to create a string without this madness in Ruby 2,
|
20
|
-
# so let's just pack two 4-byte integers and go about our day.
|
21
|
-
HEADER_MAGIC = [0x8eade801, 0x00000000].pack("NN")
|
22
|
-
else
|
23
|
-
HEADER_MAGIC = "\x8e\xad\xe8\x01\x00\x00\x00\x00"
|
24
|
-
end
|
13
|
+
HEADER_MAGIC = "\x8e\xad\xe8\x01\x00\x00\x00\x00".force_encoding("BINARY")
|
14
|
+
|
25
15
|
# magic + index_count + data_length
|
26
16
|
HEADER_HEADER_LENGTH = HEADER_MAGIC.length + 4 + 4
|
27
17
|
TAG_ENTRY_SIZE = 16 # tag id, type, offset, count == 16 bytes
|
data/lib/arr-pm/file/lead.rb
CHANGED
data/lib/arr-pm/file/tag.rb
CHANGED
data/lib/arr-pm/file.rb
CHANGED
@@ -3,6 +3,7 @@ require File.join(File.dirname(__FILE__), "file", "header")
|
|
3
3
|
require File.join(File.dirname(__FILE__), "file", "lead")
|
4
4
|
require File.join(File.dirname(__FILE__), "file", "tag")
|
5
5
|
require "fcntl"
|
6
|
+
require "shellwords"
|
6
7
|
|
7
8
|
# Much of the code here is derived from knowledge gained by reading the rpm
|
8
9
|
# source code, but mostly it started making more sense after reading this site:
|
@@ -88,6 +89,14 @@ class RPM::File
|
|
88
89
|
return @payload
|
89
90
|
end # def payload
|
90
91
|
|
92
|
+
def valid_compressor?(name)
|
93
|
+
# I scanned rpm's rpmio.c for payload implementation names and found the following.
|
94
|
+
# sed -rne '/struct FDIO_s \w+ *= *\{/{ n; s/^.*"(\w+)",$/\1/p }' rpmio/rpmio.c
|
95
|
+
# It's possible this misses some supported rpm payload compressors.
|
96
|
+
|
97
|
+
[ "gzip", "bzip2", "xz", "lzma", "zstd" ].include?(name)
|
98
|
+
end
|
99
|
+
|
91
100
|
# Extract this RPM to a target directory.
|
92
101
|
#
|
93
102
|
# This should have roughly the same effect as:
|
@@ -97,8 +106,13 @@ class RPM::File
|
|
97
106
|
if !File.directory?(target)
|
98
107
|
raise Errno::ENOENT.new(target)
|
99
108
|
end
|
109
|
+
|
110
|
+
compressor = tags[:payloadcompressor]
|
111
|
+
if !valid_compressor?(compressor)
|
112
|
+
raise "Cannot decompress. This RPM uses an invalid compressor '#{compressor}'"
|
113
|
+
end
|
100
114
|
|
101
|
-
extractor = IO.popen("#{
|
115
|
+
extractor = IO.popen("#{compressor} -d | (cd #{Shellwords.escape(target)}; cpio -i --quiet --make-directories)", "w")
|
102
116
|
buffer = ""
|
103
117
|
begin
|
104
118
|
buffer.force_encoding("BINARY")
|
@@ -179,10 +193,12 @@ class RPM::File
|
|
179
193
|
results = []
|
180
194
|
# short-circuit if there's no :fileflags tag
|
181
195
|
return results unless tags.include?(:fileflags)
|
182
|
-
tags[:fileflags].
|
183
|
-
|
184
|
-
|
185
|
-
|
196
|
+
if !tags[:fileflags].nil?
|
197
|
+
tags[:fileflags].each_with_index do |flag, i|
|
198
|
+
# The :fileflags (and other :file... tags) are an array, in order of
|
199
|
+
# files in the rpm payload, we want a list of paths of config files.
|
200
|
+
results << files[i] if mask?(flag, FLAG_CONFIG_FILE)
|
201
|
+
end
|
186
202
|
end
|
187
203
|
return results
|
188
204
|
end # def config_files
|
@@ -193,49 +209,13 @@ class RPM::File
|
|
193
209
|
#
|
194
210
|
# % rpm2cpio blah.rpm | cpio -it
|
195
211
|
def files
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
# Do Nothing
|
204
|
-
end
|
205
|
-
payload_fd = payload.clone
|
206
|
-
output = ""
|
207
|
-
loop do
|
208
|
-
data = payload_fd.read(16384, buffer)
|
209
|
-
break if data.nil? # listerextractor.write(data)
|
210
|
-
lister.write(data)
|
211
|
-
|
212
|
-
# Read output from the pipe.
|
213
|
-
begin
|
214
|
-
output << lister.read_nonblock(16384)
|
215
|
-
rescue Errno::EAGAIN
|
216
|
-
# Nothing to read, move on!
|
217
|
-
end
|
218
|
-
end
|
219
|
-
lister.close_write
|
220
|
-
|
221
|
-
# Read remaining output
|
222
|
-
begin
|
223
|
-
output << lister.read
|
224
|
-
rescue Errno::EAGAIN
|
225
|
-
# Because read_nonblock enables NONBLOCK the 'lister' fd,
|
226
|
-
# and we may have invoked a read *before* cpio has started
|
227
|
-
# writing, let's keep retrying this read until we get an EOF
|
228
|
-
retry
|
229
|
-
rescue EOFError
|
230
|
-
# At EOF, hurray! We're done reading.
|
231
|
-
end
|
232
|
-
|
233
|
-
# Split output by newline and strip leading "."
|
234
|
-
@files = output.split("\n").collect { |s| s.gsub(/^\./, "") }
|
235
|
-
return @files
|
236
|
-
ensure
|
237
|
-
lister.close unless lister.nil?
|
238
|
-
payload_fd.close unless payload_fd.nil?
|
212
|
+
# RPM stores the file metadata split across multiple tags.
|
213
|
+
# A single path's filename (with no directories) is stored in the "basename" tag.
|
214
|
+
# The directory a file lives in is stored in the "dirnames" tag
|
215
|
+
# We can find out what directory a file is in using the "dirindexes" tag.
|
216
|
+
#
|
217
|
+
# We can join each entry of dirnames and basenames to make the full filename.
|
218
|
+
return tags[:basenames].zip(tags[:dirindexes]).map { |name, i| File.join(tags[:dirnames][i], name) }
|
239
219
|
end # def files
|
240
220
|
|
241
221
|
def mask?(value, mask)
|
data/lib/arr-pm/namespace.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "arr-pm/namespace"
|
2
|
+
|
3
|
+
module ArrPM::V2::Architecture
|
4
|
+
|
5
|
+
NOARCH = 0
|
6
|
+
I386 = 1
|
7
|
+
ALPHA = 2
|
8
|
+
SPARC = 3
|
9
|
+
MIPS = 4
|
10
|
+
PPC = 5
|
11
|
+
M68K = 6
|
12
|
+
IP = 7
|
13
|
+
RS6000 = 8
|
14
|
+
IA64 = 9
|
15
|
+
SPARC64 = 10
|
16
|
+
MIPSEL = 11
|
17
|
+
ARM = 12
|
18
|
+
MK68KMINT = 13
|
19
|
+
S390 = 14
|
20
|
+
S390X = 15
|
21
|
+
PPC64 = 16
|
22
|
+
SH = 17
|
23
|
+
XTENSA = 18
|
24
|
+
X86_64 = 19
|
25
|
+
|
26
|
+
module_function
|
27
|
+
|
28
|
+
# Is a given rpm architecture value valid?
|
29
|
+
def valid?(value)
|
30
|
+
return value >= 0 && value <= 19
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "arr-pm/namespace"
|
4
|
+
require "arr-pm/v2/format"
|
5
|
+
|
6
|
+
module ArrPM::V2::Error
|
7
|
+
class Base < StandardError; end
|
8
|
+
|
9
|
+
class InvalidMagicValue < Base
|
10
|
+
def initialize(value)
|
11
|
+
super("Got invalid magic value '#{value}'. Expected #{ArrPM::V2::Format::MAGIC}.")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class InvalidHeaderMagicValue < Base
|
16
|
+
def initialize(value)
|
17
|
+
super("Got invalid magic value '#{value}'. Expected #{ArrPM::V2::HeaderHeader::MAGIC}.")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class EmptyFile < Base; end
|
22
|
+
class ShortFile < Base; end
|
23
|
+
class InvalidVersion < Base; end
|
24
|
+
class InvalidType < Base
|
25
|
+
def initialize(value)
|
26
|
+
super("Invalid type: #{value.inspect}")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
class InvalidName < Base; end
|
30
|
+
class InvalidArchitecture < Base; end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "arr-pm/namespace"
|
4
|
+
|
5
|
+
module ArrPM::V2::Format
|
6
|
+
MAGIC = [0x8e, 0xad, 0xe8]
|
7
|
+
MAGIC_LENGTH = MAGIC.count
|
8
|
+
MAGIC_STRING = MAGIC.pack("C#{MAGIC_LENGTH}")
|
9
|
+
|
10
|
+
module_function
|
11
|
+
def valid_magic?(magic)
|
12
|
+
magic = magic.bytes if magic.is_a?(String)
|
13
|
+
|
14
|
+
magic == MAGIC
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "arr-pm/namespace"
|
4
|
+
require "arr-pm/v2/format"
|
5
|
+
require "arr-pm/v2/header_header"
|
6
|
+
require "arr-pm/v2/tag"
|
7
|
+
require "arr-pm/v2/error"
|
8
|
+
|
9
|
+
class ArrPM::V2::Header
|
10
|
+
attr_reader :tags
|
11
|
+
|
12
|
+
def load(io)
|
13
|
+
headerheader = ArrPM::V2::HeaderHeader.new
|
14
|
+
headerheader.load(io)
|
15
|
+
headerdata = io.read(headerheader.entries * 16)
|
16
|
+
tagdata = io.read(headerheader.bytesize)
|
17
|
+
parse(headerdata, headerheader.entries, tagdata)
|
18
|
+
|
19
|
+
# signature headers are padded up to an 8-byte boundar, details here:
|
20
|
+
# http://rpm.org/gitweb?p=rpm.git;a=blob;f=lib/signature.c;h=63e59c00f255a538e48cbc8b0cf3b9bd4a4dbd56;hb=HEAD#l204
|
21
|
+
# Throw away the pad.
|
22
|
+
io.read(tagdata.length % 8)
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse(data, entry_count, tagdata)
|
26
|
+
@tags = entry_count.times.collect do |i|
|
27
|
+
tag_number, type_number, offset, count = data[i * 16, 16].unpack("NNNN")
|
28
|
+
|
29
|
+
tag = ArrPM::V2::Tag.new(tag_number, type_number)
|
30
|
+
tag.parse(tagdata, offset, count)
|
31
|
+
tag
|
32
|
+
end
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "arr-pm/namespace"
|
4
|
+
require "arr-pm/v2/format"
|
5
|
+
require "arr-pm/v2/error"
|
6
|
+
|
7
|
+
# The header of an rpm has ... a header. Funky naming :)
|
8
|
+
class ArrPM::V2::HeaderHeader
|
9
|
+
MAGIC = [ 0x8e, 0xad, 0xe8 ]
|
10
|
+
MAGIC_LENGTH = MAGIC.count
|
11
|
+
|
12
|
+
attr_accessor :version, :entries, :bytesize
|
13
|
+
|
14
|
+
def load(io)
|
15
|
+
data = io.read(16)
|
16
|
+
parse(data)
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse(data)
|
20
|
+
magic, version, reserved, entries, bytesize = data.unpack("a3Ca4NN")
|
21
|
+
self.class.validate_magic(magic.bytes)
|
22
|
+
|
23
|
+
@version = version
|
24
|
+
@entries = entries
|
25
|
+
@bytesize = bytesize
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def dump
|
30
|
+
[magic, 1, 0, @entries, @bytesize].pack("a3Ca4NN")
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.validate_magic(value)
|
34
|
+
raise ArrPM::V2::Error::InvalidHeaderMagicValue, value if value != MAGIC
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "arr-pm/namespace"
|
4
|
+
require "arr-pm/v2/format"
|
5
|
+
require "arr-pm/v2/error"
|
6
|
+
|
7
|
+
class ArrPM::V2::Lead
|
8
|
+
LENGTH = 96
|
9
|
+
MAGIC = [ 0xed, 0xab, 0xee, 0xdb ]
|
10
|
+
MAGIC_LENGTH = MAGIC.count
|
11
|
+
|
12
|
+
SIGNED_TYPE = 5
|
13
|
+
|
14
|
+
attr_accessor :major, :minor, :type, :architecture, :name, :os, :signature_type, :reserved
|
15
|
+
|
16
|
+
def validate
|
17
|
+
self.class.validate_type(type)
|
18
|
+
self.class.validate_architecture(architecture)
|
19
|
+
if name.length > 65
|
20
|
+
raise ArrPM::V2::Error::InvalidName, "Name is longer than 65 chracters. This is invalid."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def dump(io)
|
25
|
+
io.write(serialize)
|
26
|
+
end
|
27
|
+
|
28
|
+
def serialize
|
29
|
+
validate
|
30
|
+
[ *MAGIC, major, minor, type, architecture, name, os, signature_type, *reserved ].pack("C4CCnnZ66nnC16")
|
31
|
+
end
|
32
|
+
|
33
|
+
def load(io)
|
34
|
+
data = io.read(LENGTH)
|
35
|
+
parse(data)
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse(bytestring)
|
39
|
+
raise ArrPM::V2::Error::EmptyFile if bytestring.nil?
|
40
|
+
data = bytestring.bytes
|
41
|
+
|
42
|
+
@magic = self.class.parse_magic(data)
|
43
|
+
@major, @minor = self.class.parse_version(data)
|
44
|
+
@type = self.class.parse_type(data)
|
45
|
+
@architecture = self.class.parse_architecture(data)
|
46
|
+
@name = self.class.parse_name(data)
|
47
|
+
@os = self.class.parse_os(data)
|
48
|
+
@signature_type = self.class.parse_signature_type(data)
|
49
|
+
@reserved = self.class.parse_reserved(data)
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def signature?
|
54
|
+
@signature_type == SIGNED_TYPE
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.valid_version?(version)
|
58
|
+
version == 1
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.parse_magic(data)
|
62
|
+
magic = data[0, MAGIC_LENGTH]
|
63
|
+
validate_magic(magic)
|
64
|
+
magic
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.validate_magic(magic)
|
68
|
+
raise ArrPM::V2::Error::InvalidMagicValue, magic unless magic == MAGIC
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.parse_version(data)
|
72
|
+
offset = MAGIC_LENGTH
|
73
|
+
major, minor = data[offset, 2]
|
74
|
+
return major, minor
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.parse_type(data)
|
78
|
+
offset = MAGIC_LENGTH + 2
|
79
|
+
type = data[offset, 2].pack("CC").unpack("n").first
|
80
|
+
validate_type(type)
|
81
|
+
type
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.validate_type(type)
|
85
|
+
raise ArrPM::V2::Error::InvalidType, type unless ArrPM::V2::Type.valid?(type)
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.parse_architecture(data)
|
89
|
+
offset = MAGIC_LENGTH + 4
|
90
|
+
architecture = data[offset, 2].pack("C*").unpack("n").first
|
91
|
+
validate_architecture(architecture)
|
92
|
+
architecture
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.validate_architecture(architecture)
|
96
|
+
raise ArrPM::V2::Error::InvalidArchitecture unless ArrPM::V2::Architecture.valid?(architecture)
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.parse_name(data)
|
100
|
+
offset = MAGIC_LENGTH + 6
|
101
|
+
name = data[offset, 66]
|
102
|
+
length = name.find_index(0) # find the first null byte
|
103
|
+
raise ArrPM::V2::Error::InvalidName unless length
|
104
|
+
return name[0, length].pack("C*")
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.parse_os(data)
|
108
|
+
offset = MAGIC_LENGTH + 72
|
109
|
+
data[offset, 2].pack("C*").unpack("n").first
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.parse_signature_type(data)
|
113
|
+
offset = MAGIC_LENGTH + 74
|
114
|
+
data[offset, 2].pack("C*").unpack("n").first
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.parse_reserved(data)
|
118
|
+
offset = MAGIC_LENGTH + 76
|
119
|
+
data[offset, 16]
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "arr-pm/namespace"
|
4
|
+
|
5
|
+
class ArrPM::V2::RPM
|
6
|
+
attr_accessor :name
|
7
|
+
attr_accessor :epoch
|
8
|
+
attr_accessor :version
|
9
|
+
attr_accessor :release
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
defaults
|
13
|
+
end
|
14
|
+
|
15
|
+
def defaults
|
16
|
+
@type = Type::BINARY
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,295 @@
|
|
1
|
+
require "arr-pm/namespace"
|
2
|
+
|
3
|
+
class ArrPM::V2::Tag
|
4
|
+
module Type
|
5
|
+
NULL = 0
|
6
|
+
CHAR = 1
|
7
|
+
INT8 = 2
|
8
|
+
INT16 = 3
|
9
|
+
INT32 = 4
|
10
|
+
INT64 = 5
|
11
|
+
STRING = 6
|
12
|
+
BINARY = 7
|
13
|
+
STRING_ARRAY = 8
|
14
|
+
I18NSTRING = 9
|
15
|
+
|
16
|
+
TYPE_MAP = Hash[constants.collect { |c| [const_get(c), c] }]
|
17
|
+
|
18
|
+
def self.parse(data, type, offset, count)
|
19
|
+
case type
|
20
|
+
when NULL
|
21
|
+
nil
|
22
|
+
when CHAR
|
23
|
+
data[offset, count].unpack("A#{count}")
|
24
|
+
when INT8
|
25
|
+
data[offset, count].unpack("C" * count)
|
26
|
+
when INT16
|
27
|
+
data[offset, 2 * count].unpack("n" * count)
|
28
|
+
when INT32
|
29
|
+
data[offset, 4 * count].unpack("N" * count)
|
30
|
+
when INT64
|
31
|
+
a, b = data[offset, 8].unpack("NN")
|
32
|
+
a << 32 + b
|
33
|
+
when STRING, I18NSTRING
|
34
|
+
data[offset..-1][/^[^\0]*/]
|
35
|
+
when BINARY
|
36
|
+
data[offset, count]
|
37
|
+
when STRING_ARRAY
|
38
|
+
data[offset..-1].split("\0")[0...count]
|
39
|
+
else
|
40
|
+
raise ArrPM::V2::Error::InvalidType, type
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end # module Type
|
44
|
+
|
45
|
+
HEADERIMAGE = 61
|
46
|
+
HEADERSIGNATURES = 62
|
47
|
+
HEADERIMMUTABLE = 63
|
48
|
+
HEADERREGIONS = 64
|
49
|
+
HEADERI18NTABLE = 100
|
50
|
+
SIG_BASE = 256
|
51
|
+
|
52
|
+
SIGSIZE = 257
|
53
|
+
SIGLEMD5_1 = 258
|
54
|
+
SIGPGP = 259
|
55
|
+
SIGLEMD5_2 = 260
|
56
|
+
SIGMD5 = 261
|
57
|
+
SIGGPG = 262
|
58
|
+
SIGPGP5 = 263
|
59
|
+
BADSHA1_1 = 264
|
60
|
+
BADSHA1_2 = 265
|
61
|
+
PUBKEYS = 266
|
62
|
+
DSAHEADER = 267
|
63
|
+
RSAHEADER = 268
|
64
|
+
SHA1HEADER = 269
|
65
|
+
LONGSIGSIZE = 270
|
66
|
+
LONGARCHIVESIZE = 271
|
67
|
+
|
68
|
+
NAME = 1000
|
69
|
+
VERSION = 1001
|
70
|
+
RELEASE = 1002
|
71
|
+
EPOCH = 1003
|
72
|
+
SUMMARY = 1004
|
73
|
+
DESCRIPTION = 1005
|
74
|
+
BUILDTIME = 1006
|
75
|
+
BUILDHOST = 1007
|
76
|
+
INSTALLTIME = 1008
|
77
|
+
SIZE = 1009
|
78
|
+
DISTRIBUTION = 1010
|
79
|
+
VENDOR = 1011
|
80
|
+
GIF = 1012
|
81
|
+
XPM = 1013
|
82
|
+
LICENSE = 1014
|
83
|
+
PACKAGER = 1015
|
84
|
+
GROUP = 1016
|
85
|
+
CHANGELOG = 1017
|
86
|
+
SOURCE = 1018
|
87
|
+
PATCH = 1019
|
88
|
+
URL = 1020
|
89
|
+
OS = 1021
|
90
|
+
ARCH = 1022
|
91
|
+
PREIN = 1023
|
92
|
+
POSTIN = 1024
|
93
|
+
PREUN = 1025
|
94
|
+
POSTUN = 1026
|
95
|
+
OLDFILENAMES = 1027
|
96
|
+
FILESIZES = 1028
|
97
|
+
FILESTATES = 1029
|
98
|
+
FILEMODES = 1030
|
99
|
+
FILEUIDS = 1031
|
100
|
+
FILEGIDS = 1032
|
101
|
+
FILERDEVS = 1033
|
102
|
+
FILEMTIMES = 1034
|
103
|
+
FILEDIGESTS = 1035
|
104
|
+
FILELINKTOS = 1036
|
105
|
+
FILEFLAGS = 1037
|
106
|
+
ROOT = 1038
|
107
|
+
FILEUSERNAME = 1039
|
108
|
+
FILEGROUPNAME = 1040
|
109
|
+
EXCLUDE = 1041
|
110
|
+
EXCLUSIVE = 1042
|
111
|
+
ICON = 1043
|
112
|
+
SOURCERPM = 1044
|
113
|
+
FILEVERIFYFLAGS = 1045
|
114
|
+
ARCHIVESIZE = 1046
|
115
|
+
PROVIDENAME = 1047
|
116
|
+
REQUIREFLAGS = 1048
|
117
|
+
REQUIRENAME = 1049
|
118
|
+
REQUIREVERSION = 1050
|
119
|
+
NOSOURCE = 1051
|
120
|
+
NOPATCH = 1052
|
121
|
+
CONFLICTFLAGS = 1053
|
122
|
+
CONFLICTNAME = 1054
|
123
|
+
CONFLICTVERSION = 1055
|
124
|
+
DEFAULTPREFIX = 1056
|
125
|
+
BUILDROOT = 1057
|
126
|
+
INSTALLPREFIX = 1058
|
127
|
+
EXCLUDEARCH = 1059
|
128
|
+
EXCLUDEOS = 1060
|
129
|
+
EXCLUSIVEARCH = 1061
|
130
|
+
EXCLUSIVEOS = 1062
|
131
|
+
AUTOREQPROV = 1063
|
132
|
+
RPMVERSION = 1064
|
133
|
+
TRIGGERSCRIPTS = 1065
|
134
|
+
TRIGGERNAME = 1066
|
135
|
+
TRIGGERVERSION = 1067
|
136
|
+
TRIGGERFLAGS = 1068
|
137
|
+
TRIGGERINDEX = 1069
|
138
|
+
VERIFYSCRIPT = 1079
|
139
|
+
CHANGELOGTIME = 1080
|
140
|
+
CHANGELOGNAME = 1081
|
141
|
+
CHANGELOGTEXT = 1082
|
142
|
+
BROKENMD5 = 1083
|
143
|
+
PREREQ = 1084
|
144
|
+
PREINPROG = 1085
|
145
|
+
POSTINPROG = 1086
|
146
|
+
PREUNPROG = 1087
|
147
|
+
POSTUNPROG = 1088
|
148
|
+
BUILDARCHS = 1089
|
149
|
+
OBSOLETENAME = 1090
|
150
|
+
VERIFYSCRIPTPROG = 1091
|
151
|
+
TRIGGERSCRIPTPROG = 1092
|
152
|
+
DOCDIR = 1093
|
153
|
+
COOKIE = 1094
|
154
|
+
FILEDEVICES = 1095
|
155
|
+
FILEINODES = 1096
|
156
|
+
FILELANGS = 1097
|
157
|
+
PREFIXES = 1098
|
158
|
+
INSTPREFIXES = 1099
|
159
|
+
TRIGGERIN = 1100
|
160
|
+
TRIGGERUN = 1101
|
161
|
+
TRIGGERPOSTUN = 1102
|
162
|
+
AUTOREQ = 1103
|
163
|
+
AUTOPROV = 1104
|
164
|
+
CAPABILITY = 1105
|
165
|
+
SOURCEPACKAGE = 1106
|
166
|
+
OLDORIGFILENAMES = 1107
|
167
|
+
BUILDPREREQ = 1108
|
168
|
+
BUILDREQUIRES = 1109
|
169
|
+
BUILDCONFLICTS = 1110
|
170
|
+
BUILDMACROS = 1111
|
171
|
+
PROVIDEFLAGS = 1112
|
172
|
+
PROVIDEVERSION = 1113
|
173
|
+
OBSOLETEFLAGS = 1114
|
174
|
+
OBSOLETEVERSION = 1115
|
175
|
+
DIRINDEXES = 1116
|
176
|
+
BASENAMES = 1117
|
177
|
+
DIRNAMES = 1118
|
178
|
+
ORIGDIRINDEXES = 1119
|
179
|
+
ORIGBASENAMES = 1120
|
180
|
+
ORIGDIRNAMES = 1121
|
181
|
+
OPTFLAGS = 1122
|
182
|
+
DISTURL = 1123
|
183
|
+
PAYLOADFORMAT = 1124
|
184
|
+
PAYLOADCOMPRESSOR = 1125
|
185
|
+
PAYLOADFLAGS = 1126
|
186
|
+
INSTALLCOLOR = 1127
|
187
|
+
INSTALLTID = 1128
|
188
|
+
REMOVETID = 1129
|
189
|
+
SHA1RHN = 1130
|
190
|
+
RHNPLATFORM = 1131
|
191
|
+
PLATFORM = 1132
|
192
|
+
PATCHESNAME = 1133
|
193
|
+
PATCHESFLAGS = 1134
|
194
|
+
PATCHESVERSION = 1135
|
195
|
+
CACHECTIME = 1136
|
196
|
+
CACHEPKGPATH = 1137
|
197
|
+
CACHEPKGSIZE = 1138
|
198
|
+
CACHEPKGMTIME = 1139
|
199
|
+
FILECOLORS = 1140
|
200
|
+
FILECLASS = 1141
|
201
|
+
CLASSDICT = 1142
|
202
|
+
FILEDEPENDSX = 1143
|
203
|
+
FILEDEPENDSN = 1144
|
204
|
+
DEPENDSDICT = 1145
|
205
|
+
SOURCEPKGID = 1146
|
206
|
+
FILECONTEXTS = 1147
|
207
|
+
FSCONTEXTS = 1148
|
208
|
+
RECONTEXTS = 1149
|
209
|
+
POLICIES = 1150
|
210
|
+
PRETRANS = 1151
|
211
|
+
POSTTRANS = 1152
|
212
|
+
PRETRANSPROG = 1153
|
213
|
+
POSTTRANSPROG = 1154
|
214
|
+
DISTTAG = 1155
|
215
|
+
SUGGESTSNAME = 1156
|
216
|
+
SUGGESTSVERSION = 1157
|
217
|
+
SUGGESTSFLAGS = 1158
|
218
|
+
ENHANCESNAME = 1159
|
219
|
+
ENHANCESVERSION = 1160
|
220
|
+
ENHANCESFLAGS = 1161
|
221
|
+
PRIORITY = 1162
|
222
|
+
CVSID = 1163
|
223
|
+
BLINKPKGID = 1164
|
224
|
+
BLINKHDRID = 1165
|
225
|
+
BLINKNEVRA = 1166
|
226
|
+
FLINKPKGID = 1167
|
227
|
+
FLINKHDRID = 1168
|
228
|
+
FLINKNEVRA = 1169
|
229
|
+
PACKAGEORIGIN = 1170
|
230
|
+
TRIGGERPREIN = 1171
|
231
|
+
BUILDSUGGESTS = 1172
|
232
|
+
BUILDENHANCES = 1173
|
233
|
+
SCRIPTSTATES = 1174
|
234
|
+
SCRIPTMETRICS = 1175
|
235
|
+
BUILDCPUCLOCK = 1176
|
236
|
+
FILEDIGESTALGOS = 1177
|
237
|
+
VARIANTS = 1178
|
238
|
+
XMAJOR = 1179
|
239
|
+
XMINOR = 1180
|
240
|
+
REPOTAG = 1181
|
241
|
+
KEYWORDS = 1182
|
242
|
+
BUILDPLATFORMS = 1183
|
243
|
+
PACKAGECOLOR = 1184
|
244
|
+
PACKAGEPREFCOLOR = 1185
|
245
|
+
XATTRSDICT = 1186
|
246
|
+
FILEXATTRSX = 1187
|
247
|
+
DEPATTRSDICT = 1188
|
248
|
+
CONFLICTATTRSX = 1189
|
249
|
+
OBSOLETEATTRSX = 1190
|
250
|
+
PROVIDEATTRSX = 1191
|
251
|
+
REQUIREATTRSX = 1192
|
252
|
+
BUILDPROVIDES = 1193
|
253
|
+
BUILDOBSOLETES = 1194
|
254
|
+
DBINSTANCE = 1195
|
255
|
+
NVRA = 1196
|
256
|
+
FILENAMES = 5000
|
257
|
+
FILEPROVIDE = 5001
|
258
|
+
FILEREQUIRE = 5002
|
259
|
+
FSNAMES = 5003
|
260
|
+
FSSIZES = 5004
|
261
|
+
TRIGGERCONDS = 5005
|
262
|
+
TRIGGERTYPE = 5006
|
263
|
+
ORIGFILENAMES = 5007
|
264
|
+
LONGFILESIZES = 5008
|
265
|
+
LONGSIZE = 5009
|
266
|
+
FILECAPS = 5010
|
267
|
+
FILEDIGESTALGO = 5011
|
268
|
+
BUGURL = 5012
|
269
|
+
EVR = 5013
|
270
|
+
NVR = 5014
|
271
|
+
NEVR = 5015
|
272
|
+
NEVRA = 5016
|
273
|
+
HEADERCOLOR = 5017
|
274
|
+
VERBOSE = 5018
|
275
|
+
EPOCHNUM = 5019
|
276
|
+
ENCODING = 5062
|
277
|
+
|
278
|
+
TAG_MAP = Hash[constants.collect { |c| [const_get(c), c] }]
|
279
|
+
|
280
|
+
attr_accessor :tag, :type, :value
|
281
|
+
|
282
|
+
def initialize(tag_number, type_number)
|
283
|
+
@tag = self.class::TAG_MAP[tag_number] || tag_number
|
284
|
+
@type = type_number
|
285
|
+
end
|
286
|
+
|
287
|
+
def parse(data, offset, count)
|
288
|
+
@value = Type.parse(data, @type, offset, count)
|
289
|
+
nil
|
290
|
+
end
|
291
|
+
|
292
|
+
def inspect
|
293
|
+
format("<%s#%s> %s/%d value=%s>", self.class.name, self.object_id, @tag, @type, @value.inspect)
|
294
|
+
end
|
295
|
+
end # module ArrPM::V2::Tag
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "arr-pm/namespace"
|
2
|
+
|
3
|
+
module ArrPM::V2::Type
|
4
|
+
BINARY = 0
|
5
|
+
SOURCE = 1
|
6
|
+
|
7
|
+
module_function
|
8
|
+
|
9
|
+
# Is a given rpm type value valid?
|
10
|
+
#
|
11
|
+
# The only valid types are BINARY (0) or SOURCE (1)
|
12
|
+
def valid?(value)
|
13
|
+
return (value == BINARY || value == SOURCE)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "flores/random"
|
4
|
+
|
5
|
+
require "arr-pm/v2/header"
|
6
|
+
require "arr-pm/v2/type"
|
7
|
+
require "arr-pm/v2/architecture"
|
8
|
+
require "json"
|
9
|
+
|
10
|
+
describe ArrPM::V2::Header do
|
11
|
+
context "with a known good rpm" do
|
12
|
+
let(:path) { File.join(File.dirname(__FILE__), "../../fixtures/example-1.0-1.x86_64.rpm") }
|
13
|
+
let(:file) { File.new(path) }
|
14
|
+
|
15
|
+
before do
|
16
|
+
lead = ArrPM::V2::Lead.new
|
17
|
+
lead.load(file)
|
18
|
+
|
19
|
+
# Throw away the signature if we have one
|
20
|
+
described_class.new.load(file) if lead.signature?
|
21
|
+
|
22
|
+
subject.load(file)
|
23
|
+
end
|
24
|
+
|
25
|
+
expectations = JSON.parse(File.read(File.join(File.dirname(__FILE__), "../../fixtures/example.json")))
|
26
|
+
|
27
|
+
expectations.each do |name, expected_value|
|
28
|
+
it "should have expected value for the #{name} tag" do
|
29
|
+
tag = subject.tags.find { |t| t.tag.to_s == name }
|
30
|
+
expect(tag.value).to be == expected_value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "flores/random"
|
4
|
+
|
5
|
+
require "arr-pm/v2/lead"
|
6
|
+
require "arr-pm/v2/type"
|
7
|
+
require "arr-pm/v2/architecture"
|
8
|
+
|
9
|
+
describe ArrPM::V2::Lead do
|
10
|
+
let(:major) { Flores::Random.integer(0..255) }
|
11
|
+
let(:minor) { Flores::Random.integer(0..255) }
|
12
|
+
let(:magic) { described_class::MAGIC }
|
13
|
+
let(:type) { ArrPM::V2::Type::BINARY }
|
14
|
+
let(:architecture) { ArrPM::V2::Architecture::I386 }
|
15
|
+
let(:os) { 0 }
|
16
|
+
let(:os_bytes) { [os].pack("n").unpack("C2") }
|
17
|
+
let(:signature_type) { 0 }
|
18
|
+
let(:signature_type_bytes) { [signature_type].pack("n").unpack("C2") }
|
19
|
+
let(:longname) { "test-1.0-1" }
|
20
|
+
let(:longnamebytes) { longname.bytes + (66-longname.bytesize).times.collect { 0 } }
|
21
|
+
let(:leadbytes) { magic + [major, minor] + [0, type, 0, architecture] + longnamebytes + os_bytes + signature_type_bytes + 16.times.collect { 0 } }
|
22
|
+
let(:lead) { leadbytes.pack("C*") }
|
23
|
+
|
24
|
+
describe ".parse_magic" do
|
25
|
+
context "when given an invalid magic value" do
|
26
|
+
# Generate random bytes for the magic value, should be bad.
|
27
|
+
let(:magic) { Flores::Random.iterations(0..10).collect { Flores::Random.integer(0..255) } }
|
28
|
+
|
29
|
+
it "should fail" do
|
30
|
+
expect { described_class.parse_magic(leadbytes) }.to raise_error(ArrPM::V2::Error::InvalidMagicValue)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when given a valid magic value" do
|
35
|
+
it "should succeed" do
|
36
|
+
expect { described_class.parse_magic(leadbytes) }.not_to raise_error
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe ".parse_version" do
|
42
|
+
context "when given an invalid version value" do
|
43
|
+
let(:data) { magic + [major, minor] }
|
44
|
+
|
45
|
+
it "should return an array of two values " do
|
46
|
+
expect(described_class.parse_version(leadbytes)).to be == [major, minor]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe ".parse_type" do
|
52
|
+
context "when given an invalid type" do
|
53
|
+
let(:type) { Flores::Random.integer(2..1000) }
|
54
|
+
it "should fail" do
|
55
|
+
expect { described_class.parse_type(leadbytes) }.to raise_error(ArrPM::V2::Error::InvalidType)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
context "with a valid type" do
|
59
|
+
it "should return the type" do
|
60
|
+
expect(described_class.parse_type(leadbytes)).to be == type
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe ".parse_name" do
|
66
|
+
context "with a valid name" do
|
67
|
+
it "should return the name" do
|
68
|
+
expect(described_class.parse_name(leadbytes)).to be == longname
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe ".parse_signature_type" do
|
74
|
+
it "should return the signature type" do
|
75
|
+
expect(described_class.parse_signature_type(leadbytes)).to be == signature_type
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe ".parse_reserved" do
|
80
|
+
it "should return exactly 16 bytes" do
|
81
|
+
expect(described_class.parse_reserved(leadbytes).count).to be == 16
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#parse" do
|
86
|
+
before do
|
87
|
+
subject.parse(lead)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should have a correct parsed values" do
|
91
|
+
expect(subject.name).to be == longname
|
92
|
+
expect(subject.major).to be == major
|
93
|
+
expect(subject.minor).to be == minor
|
94
|
+
expect(subject.type).to be == type
|
95
|
+
expect(subject.architecture).to be == architecture
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#dump" do
|
100
|
+
before do
|
101
|
+
subject.parse(lead)
|
102
|
+
end
|
103
|
+
|
104
|
+
let(:blob) { subject.serialize }
|
105
|
+
|
106
|
+
it "should parse successfully" do
|
107
|
+
subject.parse(blob)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "with a known good rpm" do
|
112
|
+
let(:path) { File.join(File.dirname(__FILE__), "../../fixtures/example-1.0-1.x86_64.rpm") }
|
113
|
+
|
114
|
+
before do
|
115
|
+
subject.load(File.new(path))
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should have expected values" do
|
119
|
+
expect(subject.name).to be == "example-1.0-1"
|
120
|
+
expect(subject.major).to be == 3
|
121
|
+
expect(subject.minor).to be == 0
|
122
|
+
expect(subject.architecture).to be == architecture
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
Binary file
|
@@ -0,0 +1,55 @@
|
|
1
|
+
{
|
2
|
+
"NAME": "example",
|
3
|
+
"VERSION": "1.0",
|
4
|
+
"RELEASE": "1",
|
5
|
+
"SUMMARY": "no description given",
|
6
|
+
"DESCRIPTION": "no description given",
|
7
|
+
"BUILDTIME": [
|
8
|
+
1466326707
|
9
|
+
],
|
10
|
+
"BUILDHOST": "localhost",
|
11
|
+
"SIZE": [
|
12
|
+
0
|
13
|
+
],
|
14
|
+
"VENDOR": "none",
|
15
|
+
"LICENSE": "unknown",
|
16
|
+
"PACKAGER": "<jls@localhost.localdomain>",
|
17
|
+
"GROUP": "default",
|
18
|
+
"URL": "http://example.com/no-uri-given",
|
19
|
+
"OS": "linux",
|
20
|
+
"ARCH": "x86_64",
|
21
|
+
"SOURCERPM": "example-1.0-1.src.rpm",
|
22
|
+
"PROVIDENAME": [
|
23
|
+
"example",
|
24
|
+
"example(x86-64)"
|
25
|
+
],
|
26
|
+
"REQUIREFLAGS": [
|
27
|
+
16777226,
|
28
|
+
16777226
|
29
|
+
],
|
30
|
+
"REQUIRENAME": [
|
31
|
+
"rpmlib(CompressedFileNames)",
|
32
|
+
"rpmlib(PayloadFilesHavePrefix)"
|
33
|
+
],
|
34
|
+
"REQUIREVERSION": [
|
35
|
+
"3.0.4-1",
|
36
|
+
"4.0-1"
|
37
|
+
],
|
38
|
+
"RPMVERSION": "4.13.0-rc1",
|
39
|
+
"PREFIXES": [
|
40
|
+
"/"
|
41
|
+
],
|
42
|
+
"PROVIDEFLAGS": [
|
43
|
+
8,
|
44
|
+
8
|
45
|
+
],
|
46
|
+
"PROVIDEVERSION": [
|
47
|
+
"1.0-1",
|
48
|
+
"1.0-1"
|
49
|
+
],
|
50
|
+
"PAYLOADFORMAT": "cpio",
|
51
|
+
"PAYLOADCOMPRESSOR": "gzip",
|
52
|
+
"PAYLOADFLAGS": "9",
|
53
|
+
"PLATFORM": "x86_64-redhat-linux-gnu",
|
54
|
+
"ENCODING": "utf-8"
|
55
|
+
}
|
Binary file
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "arr-pm/file"
|
4
|
+
require "stud/temporary"
|
5
|
+
require "insist"
|
6
|
+
|
7
|
+
describe ::RPM::File do
|
8
|
+
subject { described_class.new(path) }
|
9
|
+
|
10
|
+
context "with a known good rpm" do
|
11
|
+
let(:path) { File.join(File.dirname(__FILE__), "../fixtures/pagure-mirror-5.13.2-5.fc35.noarch.rpm") }
|
12
|
+
|
13
|
+
context "#files" do
|
14
|
+
let(:files) { [
|
15
|
+
"/usr/lib/systemd/system/pagure_mirror.service",
|
16
|
+
"/usr/share/licenses/pagure-mirror",
|
17
|
+
"/usr/share/licenses/pagure-mirror/LICENSE"
|
18
|
+
]}
|
19
|
+
|
20
|
+
it "should have the correct list of files" do
|
21
|
+
expect(subject.files).to eq(files)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "#extract" do
|
27
|
+
# This RPM should be correctly built, but we will modify the tags at runtime to force an error.
|
28
|
+
let(:path) { File.join(File.dirname(__FILE__), "../fixtures/example-1.0-1.x86_64.rpm") }
|
29
|
+
let(:dir) { dir = Stud::Temporary.directory }
|
30
|
+
|
31
|
+
after do
|
32
|
+
FileUtils.rm_rf(dir)
|
33
|
+
end
|
34
|
+
|
35
|
+
context "with an invalid payloadcompressor" do
|
36
|
+
before do
|
37
|
+
subject.tags[:payloadcompressor] = "some invalid | string"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should raise an error" do
|
41
|
+
insist { subject.extract(dir) }.raises(RuntimeError)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
[ "gzip", "bzip2", "xz", "lzma", "zstd" ].each do |name|
|
46
|
+
context "with a '#{name}' payloadcompressor" do
|
47
|
+
before do
|
48
|
+
subject.tags[:payloadcompressor] = name
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should succeed" do
|
52
|
+
reject { subject.extract(dir) }.raises(RuntimeError)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
metadata
CHANGED
@@ -1,23 +1,23 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arr-pm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jordan Sissel
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: flores
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
|
-
type: :
|
20
|
+
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
@@ -25,19 +25,47 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 3.0.0
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 3.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: stud
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.0.23
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.0.23
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: insist
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.0.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.0.0
|
41
69
|
description: This library allows to you to read and write rpm packages. Written in
|
42
70
|
pure ruby because librpm is not available on all systems
|
43
71
|
email:
|
@@ -46,9 +74,9 @@ executables: []
|
|
46
74
|
extensions: []
|
47
75
|
extra_rdoc_files: []
|
48
76
|
files:
|
49
|
-
- ".batcave/manifest"
|
50
77
|
- ".gitignore"
|
51
78
|
- ".rubocop.yml"
|
79
|
+
- CHANGELOG.md
|
52
80
|
- Gemfile
|
53
81
|
- LICENSE
|
54
82
|
- Makefile
|
@@ -63,11 +91,26 @@ files:
|
|
63
91
|
- lib/arr-pm/file/tag.rb
|
64
92
|
- lib/arr-pm/namespace.rb
|
65
93
|
- lib/arr-pm/requires.rb
|
66
|
-
|
94
|
+
- lib/arr-pm/v2/architecture.rb
|
95
|
+
- lib/arr-pm/v2/error.rb
|
96
|
+
- lib/arr-pm/v2/format.rb
|
97
|
+
- lib/arr-pm/v2/header.rb
|
98
|
+
- lib/arr-pm/v2/header_header.rb
|
99
|
+
- lib/arr-pm/v2/lead.rb
|
100
|
+
- lib/arr-pm/v2/package.rb
|
101
|
+
- lib/arr-pm/v2/tag.rb
|
102
|
+
- lib/arr-pm/v2/type.rb
|
103
|
+
- spec/arr-pm/v2/header_spec.rb
|
104
|
+
- spec/arr-pm/v2/lead_spec.rb
|
105
|
+
- spec/fixtures/example-1.0-1.x86_64.rpm
|
106
|
+
- spec/fixtures/example.json
|
107
|
+
- spec/fixtures/pagure-mirror-5.13.2-5.fc35.noarch.rpm
|
108
|
+
- spec/rpm/file_spec.rb
|
109
|
+
homepage:
|
67
110
|
licenses:
|
68
111
|
- Apache 2
|
69
112
|
metadata: {}
|
70
|
-
post_install_message:
|
113
|
+
post_install_message:
|
71
114
|
rdoc_options: []
|
72
115
|
require_paths:
|
73
116
|
- lib
|
@@ -83,9 +126,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
126
|
- !ruby/object:Gem::Version
|
84
127
|
version: '0'
|
85
128
|
requirements: []
|
86
|
-
|
87
|
-
|
88
|
-
signing_key:
|
129
|
+
rubygems_version: 3.3.3
|
130
|
+
signing_key:
|
89
131
|
specification_version: 4
|
90
132
|
summary: RPM reader and writer library
|
91
133
|
test_files: []
|