arr-pm 0.0.10 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|