mirror of
https://github.com/quamotion/dotnet-packaging.git
synced 2026-02-15 13:46:36 +00:00
1020 lines
37 KiB
C#
1020 lines
37 KiB
C#
using Packaging.Targets.IO;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
namespace Packaging.Targets.Rpm
|
|
{
|
|
/// <summary>
|
|
/// Provides access to the metadata stored in the RPM package.
|
|
/// </summary>
|
|
internal class RpmMetadata
|
|
{
|
|
public RpmMetadata(RpmPackage package)
|
|
{
|
|
if (package == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(package));
|
|
}
|
|
|
|
this.Package = package;
|
|
}
|
|
|
|
public RpmPackage Package
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the length of the immutable region, starting from the position of the <see cref="IndexTag.RPMTAG_HEADERIMMUTABLE"/>
|
|
/// record. This record should be the last record in the signature block, and the value is negative, indicating the previous
|
|
/// blocks are considered immutable.
|
|
/// </summary>
|
|
public int ImmutableRegionSize
|
|
{
|
|
get
|
|
{
|
|
// For more information about immutable regions, see:
|
|
// http://ftp.rpm.org/api/4.4.2.2/hregions.html
|
|
// https://dentrassi.de/2016/04/15/writing-rpm-files-in-plain-java/
|
|
// https://blog.bethselamin.de/posts/argh-pm.html
|
|
// For now, we're always assuming the entire header and the entire signature is immutable
|
|
var immutableSignatureRegion = this.GetByteArray(IndexTag.RPMTAG_HEADERIMMUTABLE);
|
|
|
|
using (MemoryStream s = new MemoryStream(immutableSignatureRegion))
|
|
{
|
|
var h = s.ReadStruct<IndexHeader>();
|
|
return h.Offset;
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
IndexHeader header = default(IndexHeader);
|
|
header.Offset = value;
|
|
header.Count = 0x10;
|
|
header.Type = IndexType.RPM_BIN_TYPE;
|
|
header.Tag = (uint)IndexTag.RPMTAG_HEADERIMMUTABLE;
|
|
|
|
byte[] data;
|
|
|
|
using (MemoryStream s = new MemoryStream())
|
|
{
|
|
s.WriteStruct(header);
|
|
data = s.ToArray();
|
|
}
|
|
|
|
this.SetByteArray(IndexTag.RPMTAG_HEADERIMMUTABLE, data);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a list of all locales supported by this package. The default, invariant locale is marked as the <c>C</c> locale.
|
|
/// </summary>
|
|
public Collection<string> Locales
|
|
{
|
|
get { return this.GetStringArray(IndexTag.RPMTAG_HEADERI18NTABLE); }
|
|
set { this.SetStringArray(IndexTag.RPMTAG_HEADERI18NTABLE, value.ToArray()); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the package.
|
|
/// </summary>
|
|
public string Name
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_NAME); }
|
|
set { this.SetString(IndexTag.RPMTAG_NAME, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the package version.
|
|
/// </summary>
|
|
public string Version
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_VERSION); }
|
|
set { this.SetString(IndexTag.RPMTAG_VERSION, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the release number of the package.
|
|
/// </summary>
|
|
public string Release
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_RELEASE); }
|
|
set { this.SetString(IndexTag.RPMTAG_RELEASE, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the a summary (one line) description of the package.
|
|
/// </summary>
|
|
public string Summary
|
|
{
|
|
get { return this.GetLocalizedString(IndexTag.RPMTAG_SUMMARY); }
|
|
set { this.SetLocalizedString(IndexTag.RPMTAG_SUMMARY, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the full description of the package.
|
|
/// </summary>
|
|
public string Description
|
|
{
|
|
get { return this.GetLocalizedString(IndexTag.RPMTAG_DESCRIPTION); }
|
|
set { this.SetLocalizedString(IndexTag.RPMTAG_DESCRIPTION, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the date and time at which the package was built.
|
|
/// </summary>
|
|
public DateTimeOffset BuildTime
|
|
{
|
|
get { return DateTimeOffset.FromUnixTimeSeconds(this.GetInt(IndexTag.RPMTAG_BUILDTIME)); }
|
|
set { this.SetInt(IndexTag.RPMTAG_BUILDTIME, (int)value.ToUnixTimeSeconds()); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the host on which the package was built.
|
|
/// </summary>
|
|
public string BuildHost
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_BUILDHOST); }
|
|
set { this.SetString(IndexTag.RPMTAG_BUILDHOST, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the sum of the sizes of the regular files in the package.
|
|
/// </summary>
|
|
public int Size
|
|
{
|
|
get { return this.GetInt(IndexTag.RPMTAG_SIZE); }
|
|
set { this.SetInt(IndexTag.RPMTAG_SIZE, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the distribution for which the package was built.
|
|
/// </summary>
|
|
public string Distribution
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_DISTRIBUTION); }
|
|
set { this.SetString(IndexTag.RPMTAG_DISTRIBUTION, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the organization which produced the package.
|
|
/// </summary>
|
|
public string Vendor
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_VENDOR); }
|
|
set { this.SetString(IndexTag.RPMTAG_VENDOR, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the license which applies to this package.
|
|
/// </summary>
|
|
public string License
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_LICENSE); }
|
|
set { this.SetString(IndexTag.RPMTAG_LICENSE, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the administrative group to which this package belongs.
|
|
/// </summary>
|
|
public string Group
|
|
{
|
|
get { return this.GetLocalizedString(IndexTag.RPMTAG_GROUP); }
|
|
set { this.SetLocalizedString(IndexTag.RPMTAG_GROUP, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the generic package information URL.
|
|
/// </summary>
|
|
public string Url
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_URL); }
|
|
set { this.SetString(IndexTag.RPMTAG_URL, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the generic name of the OS for which this package was built. Should be <c>linux</c>.
|
|
/// </summary>
|
|
public string Os
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_OS); }
|
|
set { this.SetString(IndexTag.RPMTAG_OS, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the architecture for which the package was built, as defined in the architecture-specific
|
|
/// LSB specifications.
|
|
/// </summary>
|
|
public string Arch
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_ARCH); }
|
|
set { this.SetString(IndexTag.RPMTAG_ARCH, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the source RPM.
|
|
/// </summary>
|
|
public string SourceRpm
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_SOURCERPM); }
|
|
set { this.SetString(IndexTag.RPMTAG_SOURCERPM, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the version of the RPM tool used to build this package.
|
|
/// </summary>
|
|
public string RpmVersion
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_RPMVERSION); }
|
|
set { this.SetString(IndexTag.RPMTAG_RPMVERSION, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the scipt to run before installation of this package.
|
|
/// </summary>
|
|
public string PreIn
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_PREIN); }
|
|
set { this.SetString(IndexTag.RPMTAG_PREIN, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the scipt to run after installation of this package.
|
|
/// </summary>
|
|
public string PostIn
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_POSTIN); }
|
|
set { this.SetString(IndexTag.RPMTAG_POSTIN, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the scipt to run before removal of this package.
|
|
/// </summary>
|
|
public string PreUn
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_PREUN); }
|
|
set { this.SetString(IndexTag.RPMTAG_PREUN, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the scipt to run after removal of this package.
|
|
/// </summary>
|
|
public string PostUn
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_POSTUN); }
|
|
set { this.SetString(IndexTag.RPMTAG_POSTUN, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the program to run before installation of this package.
|
|
/// </summary>
|
|
public string PreInProg
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_PREINPROG); }
|
|
set { this.SetString(IndexTag.RPMTAG_PREINPROG, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the program to run after installation of this package.
|
|
/// </summary>
|
|
public string PostInProg
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_POSTINPROG); }
|
|
set { this.SetString(IndexTag.RPMTAG_POSTINPROG, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the program to run before removal of this package.
|
|
/// </summary>
|
|
public string PreUnProg
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_PREUNPROG); }
|
|
set { this.SetString(IndexTag.RPMTAG_PREUNPROG, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the program to run after removal of this package.
|
|
/// </summary>
|
|
public string PostUnProg
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_POSTUNPROG); }
|
|
set { this.SetString(IndexTag.RPMTAG_POSTUNPROG, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets an opaque string whose contents are undefined.
|
|
/// </summary>
|
|
public string Cookie
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_COOKIE); }
|
|
set { this.SetString(IndexTag.RPMTAG_COOKIE, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets additional flags which may have been passed to the compiler when building this package.
|
|
/// </summary>
|
|
public string OptFlags
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_OPTFLAGS); }
|
|
set { this.SetString(IndexTag.RPMTAG_OPTFLAGS, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the URL for the package.
|
|
/// </summary>
|
|
public string DistUrl
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_DISTURL); }
|
|
set { this.SetString(IndexTag.RPMTAG_DISTURL, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the format of the payload. Should be <c>cpio.</c>
|
|
/// </summary>
|
|
public string PayloadFormat
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_PAYLOADFORMAT); }
|
|
set { this.SetString(IndexTag.RPMTAG_PAYLOADFORMAT, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the compressor used to compress the payload.
|
|
/// </summary>
|
|
public string PayloadCompressor
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_PAYLOADCOMPRESSOR); }
|
|
set { this.SetString(IndexTag.RPMTAG_PAYLOADCOMPRESSOR, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the compression level used for the payload.
|
|
/// </summary>
|
|
public string PayloadFlags
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_PAYLOADFLAGS); }
|
|
set { this.SetString(IndexTag.RPMTAG_PAYLOADFLAGS, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets an opaque string whose value is undefined.
|
|
/// </summary>
|
|
public string Platform
|
|
{
|
|
get { return this.GetString(IndexTag.RPMTAG_PLATFORM); }
|
|
set { this.SetString(IndexTag.RPMTAG_PLATFORM, value); }
|
|
}
|
|
|
|
public byte[] SourcePkgId
|
|
{
|
|
get { return this.GetByteArray(IndexTag.RPMTAG_SOURCEPKGID); }
|
|
set { this.SetByteArray(IndexTag.RPMTAG_SOURCEPKGID, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the hashing algorithm used to calculate the hash of files embedded
|
|
/// in this RPM archive.
|
|
/// </summary>
|
|
public PgpHashAlgo FileDigetsAlgo
|
|
{
|
|
get { return (PgpHashAlgo)this.GetInt(IndexTag.RPMTAG_FILEDIGESTALGO); }
|
|
set { this.SetInt(IndexTag.RPMTAG_FILEDIGESTALGO, (int)value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets all change log entries.
|
|
/// </summary>
|
|
public IEnumerable<ChangelogEntry> ChangelogEntries
|
|
{
|
|
get
|
|
{
|
|
var times = this.GetIntArray(IndexTag.RPMTAG_CHANGELOGTIME);
|
|
var names = this.GetStringArray(IndexTag.RPMTAG_CHANGELOGNAME);
|
|
var text = this.GetStringArray(IndexTag.RPMTAG_CHANGELOGTEXT);
|
|
|
|
int count = Math.Min(times.Count, Math.Min(names.Count, text.Count));
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
yield return new ChangelogEntry()
|
|
{
|
|
Date = DateTimeOffset.FromUnixTimeSeconds(times[i]),
|
|
Name = names[i],
|
|
Text = text[i]
|
|
};
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
var entries = value.ToArray();
|
|
|
|
var times = new int[entries.Length];
|
|
var names = new string[entries.Length];
|
|
var text = new string[entries.Length];
|
|
|
|
for (int i = 0; i < entries.Length; i++)
|
|
{
|
|
times[i] = (int)entries[i].Date.ToUnixTimeSeconds();
|
|
names[i] = entries[i].Name;
|
|
text[i] = entries[i].Text;
|
|
}
|
|
|
|
this.SetIntArray(IndexTag.RPMTAG_CHANGELOGTIME, times);
|
|
this.SetStringArray(IndexTag.RPMTAG_CHANGELOGNAME, names);
|
|
this.SetStringArray(IndexTag.RPMTAG_CHANGELOGTEXT, text);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a list of all files embedded in this package.
|
|
/// </summary>
|
|
public IEnumerable<RpmFile> Files
|
|
{
|
|
get
|
|
{
|
|
var sizes = this.GetIntArray(IndexTag.RPMTAG_FILESIZES);
|
|
var modes = this.GetShortArray(IndexTag.RPMTAG_FILEMODES);
|
|
var rdevs = this.GetShortArray(IndexTag.RPMTAG_FILERDEVS);
|
|
var mtimes = this.GetIntArray(IndexTag.RPMTAG_FILEMTIMES);
|
|
var md5s = this.GetStringArray(IndexTag.RPMTAG_FILEDIGESTS);
|
|
var linkTos = this.GetStringArray(IndexTag.RPMTAG_FILELINKTOS);
|
|
var flags = this.GetIntArray(IndexTag.RPMTAG_FILEFLAGS);
|
|
var usernames = this.GetStringArray(IndexTag.RPMTAG_FILEUSERNAME);
|
|
var groupnames = this.GetStringArray(IndexTag.RPMTAG_FILEGROUPNAME);
|
|
var verifyFlags = this.GetIntArray(IndexTag.RPMTAG_FILEVERIFYFLAGS);
|
|
var devices = this.GetIntArray(IndexTag.RPMTAG_FILEDEVICES);
|
|
var inodes = this.GetIntArray(IndexTag.RPMTAG_FILEINODES);
|
|
var langs = this.GetStringArray(IndexTag.RPMTAG_FILELANGS);
|
|
var colors = this.GetIntArray(IndexTag.RPMTAG_FILECOLORS);
|
|
var classes = this.GetIntArray(IndexTag.RPMTAG_FILECLASS);
|
|
var dependsX = this.GetIntArray(IndexTag.RPMTAG_FILEDEPENDSX);
|
|
var dependsN = this.GetIntArray(IndexTag.RPMTAG_FILEDEPENDSN);
|
|
|
|
var baseNames = this.GetStringArray(IndexTag.RPMTAG_BASENAMES);
|
|
var dirIndexes = this.GetIntArray(IndexTag.RPMTAG_DIRINDEXES);
|
|
var dirNames = this.GetStringArray(IndexTag.RPMTAG_DIRNAMES);
|
|
|
|
var classDict = this.GetStringArray(IndexTag.RPMTAG_CLASSDICT);
|
|
var dependsDict = this.GetIntArray(IndexTag.RPMTAG_DEPENDSDICT);
|
|
|
|
for (int i = 0; i < sizes.Count; i++)
|
|
{
|
|
Collection<PackageDependency> requires = new Collection<PackageDependency>();
|
|
Collection<PackageDependency> provides = new Collection<PackageDependency>();
|
|
|
|
for (int j = dependsX[i]; j < dependsX[i] + dependsN[i]; j++)
|
|
{
|
|
// https://github.com/rpm-software-management/rpm/blob/8f509d669b9ae79c86dd510c5a4bc5109f60d733/build/rpmfc.c#L734
|
|
var value = dependsDict[j];
|
|
|
|
var dependencyType = this.GetDependencyTag((char)((value >> 24) & 0xFF));
|
|
var index = value & 0x00ffffff;
|
|
|
|
var values = this.GetStringArray(dependencyType);
|
|
Collection<string> versions;
|
|
RpmSense[] types;
|
|
|
|
switch (dependencyType)
|
|
{
|
|
case IndexTag.RPMTAG_REQUIRENAME:
|
|
versions = this.GetStringArray(IndexTag.RPMTAG_REQUIREVERSION);
|
|
types = this.GetIntArray(IndexTag.RPMTAG_REQUIREFLAGS).Select(e => (RpmSense)e).ToArray();
|
|
break;
|
|
|
|
case IndexTag.RPMTAG_PROVIDENAME:
|
|
versions = this.GetStringArray(IndexTag.RPMTAG_PROVIDEVERSION);
|
|
types = this.GetIntArray(IndexTag.RPMTAG_PROVIDEFLAGS).Select(e => (RpmSense)e).ToArray();
|
|
break;
|
|
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(dependencyType));
|
|
}
|
|
|
|
var dependencyName = values[index];
|
|
var dependencyVersion = versions[index];
|
|
var dependencyFlags = types[index];
|
|
|
|
var dependency = new PackageDependency()
|
|
{
|
|
Flags = dependencyFlags,
|
|
Name = dependencyName,
|
|
Version = dependencyVersion
|
|
};
|
|
|
|
switch (dependencyType)
|
|
{
|
|
case IndexTag.RPMTAG_REQUIRENAME:
|
|
requires.Add(dependency);
|
|
break;
|
|
|
|
case IndexTag.RPMTAG_PROVIDENAME:
|
|
provides.Add(dependency);
|
|
break;
|
|
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(dependencyType));
|
|
}
|
|
}
|
|
|
|
var directoryName = dirNames[dirIndexes[i]];
|
|
var name = $"{directoryName}{baseNames[i]}";
|
|
|
|
yield return new RpmFile()
|
|
{
|
|
Size = sizes[i],
|
|
Mode = (LinuxFileMode)modes[i],
|
|
Rdev = rdevs[i],
|
|
ModifiedTime = DateTimeOffset.FromUnixTimeSeconds(mtimes[i]),
|
|
MD5Hash = RpmSignature.StringToByteArray(md5s[i]),
|
|
LinkTo = linkTos[i],
|
|
Flags = (RpmFileFlags)flags[i],
|
|
UserName = usernames[i],
|
|
GroupName = groupnames[i],
|
|
VerifyFlags = (RpmVerifyFlags)verifyFlags[i],
|
|
Device = devices[i],
|
|
Inode = inodes[i],
|
|
Lang = langs[i],
|
|
Color = (RpmFileColor)colors[i],
|
|
Class = classDict[classes[i]],
|
|
Requires = requires,
|
|
Provides = provides,
|
|
Name = name
|
|
};
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(value));
|
|
}
|
|
|
|
var files = value.ToArray();
|
|
|
|
var sizes = new int[files.Length];
|
|
var modes = new short[files.Length];
|
|
var rdevs = new short[files.Length];
|
|
var mtimes = new int[files.Length];
|
|
var md5s = new string[files.Length];
|
|
var linkTos = new string[files.Length];
|
|
var flags = new int[files.Length];
|
|
var usernames = new string[files.Length];
|
|
var groupnames = new string[files.Length];
|
|
var verifyFlags = new int[files.Length];
|
|
var devices = new int[files.Length];
|
|
var inodes = new int[files.Length];
|
|
var langs = new string[files.Length];
|
|
var colors = new int[files.Length];
|
|
var classes = new int[files.Length];
|
|
|
|
var baseNames = new string[files.Length];
|
|
var dirIndexes = new int[files.Length];
|
|
var dirNames = new Collection<string>();
|
|
|
|
var dependsX = new int[files.Length];
|
|
var dependsN = new int[files.Length];
|
|
var classDict = new Collection<string>();
|
|
var dependsDict = new Collection<int>();
|
|
|
|
var currentDependencies = this.Dependencies.ToList();
|
|
var currentProvides = this.Provides.ToList();
|
|
|
|
// Prepopulate the new dependencies in sorted order
|
|
var allDependencies = files
|
|
.SelectMany(f => f.Requires)
|
|
.Distinct()
|
|
.Where(d => !currentDependencies.Contains(d))
|
|
.OrderBy(f => f.Name)
|
|
.ToList();
|
|
|
|
var allProvides = files
|
|
.SelectMany(f => f.Provides)
|
|
.Distinct()
|
|
.Where(d => !currentProvides.Contains(d))
|
|
.ToList();
|
|
|
|
currentDependencies.AddRange(allDependencies);
|
|
this.Dependencies = currentDependencies;
|
|
|
|
currentProvides.AddRange(allProvides);
|
|
this.Provides = currentProvides;
|
|
|
|
var requireNames = this.GetStringArray(IndexTag.RPMTAG_REQUIRENAME);
|
|
var provideNames = this.GetStringArray(IndexTag.RPMTAG_PROVIDENAME);
|
|
|
|
for (int i = 0; i < files.Length; i++)
|
|
{
|
|
var file = files[i];
|
|
sizes[i] = file.Size;
|
|
modes[i] = (short)file.Mode;
|
|
rdevs[i] = file.Rdev;
|
|
mtimes[i] = (int)file.ModifiedTime.ToUnixTimeSeconds();
|
|
md5s[i] = file.MD5Hash == null ? string.Empty : BitConverter.ToString(file.MD5Hash).Replace("-", string.Empty).ToLowerInvariant();
|
|
linkTos[i] = file.LinkTo;
|
|
usernames[i] = file.UserName;
|
|
groupnames[i] = file.GroupName;
|
|
verifyFlags[i] = (int)file.VerifyFlags;
|
|
devices[i] = file.Device;
|
|
inodes[i] = file.Inode;
|
|
langs[i] = file.Lang;
|
|
colors[i] = (int)file.Color;
|
|
flags[i] = (int)file.Flags;
|
|
|
|
if (!classDict.Contains(file.Class))
|
|
{
|
|
classDict.Add(file.Class);
|
|
}
|
|
|
|
classes[i] = classDict.IndexOf(file.Class);
|
|
|
|
var dirName = Path.GetDirectoryName(file.Name).Replace('\\', '/') + '/';
|
|
var fileName = Path.GetFileName(file.Name);
|
|
|
|
if (!dirNames.Contains(dirName))
|
|
{
|
|
dirNames.Add(dirName);
|
|
}
|
|
|
|
dirIndexes[i] = dirNames.IndexOf(dirName);
|
|
baseNames[i] = fileName;
|
|
|
|
int dependX = dependsDict.Count;
|
|
dependsN[i] = file.Requires.Count + file.Provides.Count;
|
|
dependsX[i] = dependsN[i] == 0 ? 0 : dependX;
|
|
|
|
foreach (var provide in file.Provides)
|
|
{
|
|
byte type = (byte)this.GetDependencyType(IndexTag.RPMTAG_PROVIDENAME);
|
|
|
|
// Incomplete - we should check for not only name but also flags & version
|
|
var index = provideNames.IndexOf(provide.Name);
|
|
index |= type << 24;
|
|
|
|
dependsDict.Add(index);
|
|
}
|
|
|
|
foreach (var dependency in file.Requires)
|
|
{
|
|
byte type = (byte)this.GetDependencyType(IndexTag.RPMTAG_REQUIRENAME);
|
|
|
|
// Incomplete - we should check for not only name but also flags & version
|
|
var index = requireNames.IndexOf(dependency.Name);
|
|
index |= type << 24;
|
|
|
|
dependsDict.Add(index);
|
|
}
|
|
}
|
|
|
|
this.SetIntArray(IndexTag.RPMTAG_FILESIZES, sizes);
|
|
this.SetShortArray(IndexTag.RPMTAG_FILEMODES, modes);
|
|
this.SetShortArray(IndexTag.RPMTAG_FILERDEVS, rdevs);
|
|
this.SetIntArray(IndexTag.RPMTAG_FILEMTIMES, mtimes);
|
|
this.SetStringArray(IndexTag.RPMTAG_FILEDIGESTS, md5s);
|
|
this.SetStringArray(IndexTag.RPMTAG_FILELINKTOS, linkTos);
|
|
this.SetIntArray(IndexTag.RPMTAG_FILEFLAGS, flags);
|
|
this.SetStringArray(IndexTag.RPMTAG_FILEUSERNAME, usernames);
|
|
this.SetStringArray(IndexTag.RPMTAG_FILEGROUPNAME, groupnames);
|
|
this.SetIntArray(IndexTag.RPMTAG_FILEVERIFYFLAGS, verifyFlags);
|
|
this.SetIntArray(IndexTag.RPMTAG_FILEDEVICES, devices);
|
|
this.SetIntArray(IndexTag.RPMTAG_FILEINODES, inodes);
|
|
this.SetStringArray(IndexTag.RPMTAG_FILELANGS, langs);
|
|
this.SetIntArray(IndexTag.RPMTAG_FILECOLORS, colors);
|
|
this.SetIntArray(IndexTag.RPMTAG_FILECLASS, classes);
|
|
this.SetIntArray(IndexTag.RPMTAG_FILEDEPENDSX, dependsX);
|
|
this.SetIntArray(IndexTag.RPMTAG_FILEDEPENDSN, dependsN);
|
|
|
|
this.SetStringArray(IndexTag.RPMTAG_BASENAMES, baseNames);
|
|
this.SetIntArray(IndexTag.RPMTAG_DIRINDEXES, dirIndexes);
|
|
this.SetStringArray(IndexTag.RPMTAG_DIRNAMES, dirNames.ToArray());
|
|
|
|
this.SetStringArray(IndexTag.RPMTAG_CLASSDICT, classDict.ToArray());
|
|
this.SetIntArray(IndexTag.RPMTAG_DEPENDSDICT, dependsDict.ToArray());
|
|
|
|
this.Size = files.Where(f => f.Mode.HasFlag(LinuxFileMode.S_IFREG)).Sum(f => f.Size) - 0x24;
|
|
}
|
|
}
|
|
|
|
public IEnumerable<PackageDependency> Dependencies
|
|
{
|
|
get
|
|
{
|
|
var flags = this.GetIntArray(IndexTag.RPMTAG_REQUIREFLAGS);
|
|
var names = this.GetStringArray(IndexTag.RPMTAG_REQUIRENAME);
|
|
var vers = this.GetStringArray(IndexTag.RPMTAG_REQUIREVERSION);
|
|
|
|
for (int i = 0; i < flags.Count; i++)
|
|
{
|
|
yield return new PackageDependency()
|
|
{
|
|
Flags = (RpmSense)flags[i],
|
|
Name = names[i],
|
|
Version = vers[i]
|
|
};
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
var dependencies = value.ToArray();
|
|
|
|
var flags = new int[dependencies.Length];
|
|
var names = new string[dependencies.Length];
|
|
var vers = new string[dependencies.Length];
|
|
|
|
for (int i = 0; i < dependencies.Length; i++)
|
|
{
|
|
flags[i] = (int)dependencies[i].Flags;
|
|
names[i] = dependencies[i].Name;
|
|
vers[i] = dependencies[i].Version;
|
|
}
|
|
|
|
this.SetIntArray(IndexTag.RPMTAG_REQUIREFLAGS, flags);
|
|
this.SetStringArray(IndexTag.RPMTAG_REQUIRENAME, names);
|
|
this.SetStringArray(IndexTag.RPMTAG_REQUIREVERSION, vers);
|
|
}
|
|
}
|
|
|
|
public IEnumerable<PackageDependency> Provides
|
|
{
|
|
get
|
|
{
|
|
var flags = this.GetIntArray(IndexTag.RPMTAG_PROVIDEFLAGS);
|
|
var names = this.GetStringArray(IndexTag.RPMTAG_PROVIDENAME);
|
|
var vers = this.GetStringArray(IndexTag.RPMTAG_PROVIDEVERSION);
|
|
|
|
for (int i = 0; i < flags.Count; i++)
|
|
{
|
|
yield return new PackageDependency()
|
|
{
|
|
Flags = (RpmSense)flags[i],
|
|
Name = names[i],
|
|
Version = vers[i]
|
|
};
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
var provides = value.ToArray();
|
|
|
|
var flags = new int[provides.Length];
|
|
var names = new string[provides.Length];
|
|
var vers = new string[provides.Length];
|
|
|
|
for (int i = 0; i < provides.Length; i++)
|
|
{
|
|
flags[i] = (int)provides[i].Flags;
|
|
names[i] = provides[i].Name;
|
|
vers[i] = provides[i].Version;
|
|
}
|
|
|
|
this.SetIntArray(IndexTag.RPMTAG_PROVIDEFLAGS, flags);
|
|
this.SetStringArray(IndexTag.RPMTAG_PROVIDENAME, names);
|
|
this.SetStringArray(IndexTag.RPMTAG_PROVIDEVERSION, vers);
|
|
}
|
|
}
|
|
|
|
protected char GetDependencyType(IndexTag tag)
|
|
{
|
|
switch (tag)
|
|
{
|
|
case IndexTag.RPMTAG_PROVIDENAME:
|
|
return 'P';
|
|
case IndexTag.RPMTAG_REQUIRENAME:
|
|
return 'R';
|
|
case IndexTag.RPMTAG_RECOMMENDNAME:
|
|
return 'r';
|
|
case IndexTag.RPMTAG_SUGGESTNAME:
|
|
return 'S';
|
|
case IndexTag.RPMTAG_SUPPLEMENTNAME:
|
|
return 'S';
|
|
case IndexTag.RPMTAG_ENHANCENAME:
|
|
return 'e';
|
|
case IndexTag.RPMTAG_CONFLICTNAME:
|
|
return 'C';
|
|
case IndexTag.RPMTAG_OBSOLETENAME:
|
|
return 'O';
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(tag));
|
|
}
|
|
}
|
|
|
|
protected IndexTag GetDependencyTag(char deptype)
|
|
{
|
|
switch (deptype)
|
|
{
|
|
case 'P':
|
|
return IndexTag.RPMTAG_PROVIDENAME;
|
|
case 'R':
|
|
return IndexTag.RPMTAG_REQUIRENAME;
|
|
case 'r':
|
|
return IndexTag.RPMTAG_RECOMMENDNAME;
|
|
case 's':
|
|
return IndexTag.RPMTAG_SUGGESTNAME;
|
|
case 'S':
|
|
return IndexTag.RPMTAG_SUPPLEMENTNAME;
|
|
case 'e':
|
|
return IndexTag.RPMTAG_ENHANCENAME;
|
|
case 'C':
|
|
return IndexTag.RPMTAG_CONFLICTNAME;
|
|
case 'O':
|
|
return IndexTag.RPMTAG_OBSOLETENAME;
|
|
default:
|
|
return IndexTag.RPMTAG_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
protected Collection<string> GetStringArray(IndexTag tag)
|
|
{
|
|
var value = this.GetValue<Collection<string>>(tag, IndexType.RPM_STRING_ARRAY_TYPE);
|
|
|
|
if (value == null)
|
|
{
|
|
return new Collection<string>();
|
|
}
|
|
else
|
|
{
|
|
return value;
|
|
}
|
|
}
|
|
|
|
protected void SetStringArray(IndexTag tag, string[] value)
|
|
{
|
|
this.SetValue(tag, IndexType.RPM_STRING_ARRAY_TYPE, value);
|
|
}
|
|
|
|
protected string GetLocalizedString(IndexTag tag)
|
|
{
|
|
var localizedValues = this.GetValue<Collection<string>>(tag, IndexType.RPM_I18NSTRING_TYPE);
|
|
|
|
return localizedValues.First();
|
|
}
|
|
|
|
protected void SetLocalizedString(IndexTag tag, string value)
|
|
{
|
|
if (this.Locales.Count == 0)
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
var values = new string[this.Locales.Count];
|
|
|
|
for (int i = 0; i < values.Length; i++)
|
|
{
|
|
values[i] = value;
|
|
}
|
|
|
|
this.SetValue<string>(tag, IndexType.RPM_I18NSTRING_TYPE, values);
|
|
}
|
|
|
|
protected string GetString(IndexTag tag)
|
|
{
|
|
return this.GetValue<string>(tag, IndexType.RPM_STRING_TYPE);
|
|
}
|
|
|
|
protected void SetString(IndexTag tag, string value)
|
|
{
|
|
this.SetSingleValue(tag, IndexType.RPM_STRING_TYPE, value);
|
|
}
|
|
|
|
protected int GetInt(IndexTag tag)
|
|
{
|
|
return this.GetValue<int>(tag, IndexType.RPM_INT32_TYPE);
|
|
}
|
|
|
|
protected void SetInt(IndexTag tag, int value)
|
|
{
|
|
this.SetSingleValue(tag, IndexType.RPM_INT32_TYPE, value);
|
|
}
|
|
|
|
protected Collection<int> GetIntArray(IndexTag tag)
|
|
{
|
|
var value = this.GetValue<Collection<int>>(tag, IndexType.RPM_INT32_TYPE);
|
|
|
|
if (value == null)
|
|
{
|
|
return new Collection<int>();
|
|
}
|
|
else
|
|
{
|
|
return value;
|
|
}
|
|
}
|
|
|
|
protected void SetIntArray(IndexTag tag, int[] value)
|
|
{
|
|
this.SetValue(tag, IndexType.RPM_INT32_TYPE, value);
|
|
}
|
|
|
|
protected Collection<short> GetShortArray(IndexTag tag)
|
|
{
|
|
return this.GetValue<Collection<short>>(tag, IndexType.RPM_INT16_TYPE);
|
|
}
|
|
|
|
protected void SetShortArray(IndexTag tag, short[] value)
|
|
{
|
|
this.SetValue(tag, IndexType.RPM_INT16_TYPE, value);
|
|
}
|
|
|
|
protected byte[] GetByteArray(IndexTag tag)
|
|
{
|
|
return this.GetValue<byte[]>(tag, IndexType.RPM_BIN_TYPE);
|
|
}
|
|
|
|
protected void SetByteArray(IndexTag tag, byte[] value)
|
|
{
|
|
this.SetValue(tag, IndexType.RPM_BIN_TYPE, value);
|
|
}
|
|
|
|
protected T GetValue<T>(IndexTag tag, IndexType type)
|
|
{
|
|
if (!this.Package.Header.Records.ContainsKey(tag))
|
|
{
|
|
return default(T);
|
|
}
|
|
|
|
var record = this.Package.Header.Records[tag];
|
|
|
|
if (record.Header.Type != type)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(tag));
|
|
}
|
|
|
|
return (T)record.Value;
|
|
}
|
|
|
|
protected void SetValue<T>(IndexTag tag, IndexType type, T[] value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(value));
|
|
}
|
|
|
|
// We won't accept empty arrays; rather, we remove the key alltogether.
|
|
// This brings compatibility with newer versions of RPM
|
|
if (value.Length > 0)
|
|
{
|
|
var collection = new Collection<T>(value);
|
|
|
|
IndexRecord record = new IndexRecord()
|
|
{
|
|
Header = new IndexHeader()
|
|
{
|
|
Count = collection.Count,
|
|
Offset = 0,
|
|
Tag = (uint)tag,
|
|
Type = type
|
|
},
|
|
Value = collection
|
|
};
|
|
|
|
if (!this.Package.Header.Records.ContainsKey(tag))
|
|
{
|
|
this.Package.Header.Records.Add(tag, record);
|
|
}
|
|
else
|
|
{
|
|
this.Package.Header.Records[tag] = record;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this.Package.Header.Records.ContainsKey(tag))
|
|
{
|
|
this.Package.Header.Records.Remove(tag);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void SetSingleValue<T>(IndexTag tag, IndexType type, T value)
|
|
{
|
|
IndexRecord record = new IndexRecord()
|
|
{
|
|
Header = new IndexHeader()
|
|
{
|
|
Count = 1,
|
|
Offset = 0,
|
|
Tag = (uint)tag,
|
|
Type = type
|
|
},
|
|
Value = value
|
|
};
|
|
|
|
if (!this.Package.Header.Records.ContainsKey(tag))
|
|
{
|
|
this.Package.Header.Records.Add(tag, record);
|
|
}
|
|
else
|
|
{
|
|
this.Package.Header.Records[tag] = record;
|
|
}
|
|
}
|
|
}
|
|
}
|