示例#1
0
        /// <summary>
        /// Determines the offsets for all records in the signature of a package.
        /// </summary>
        /// <param name="package">
        /// The package for which to generate the offsets.
        /// </param>
        public void CalculateSignatureOffsets(RpmPackage package)
        {
            var signature = new RpmSignature(package);

            signature.ImmutableRegionSize = -1 * Marshal.SizeOf <IndexHeader>() * package.Signature.Records.Count;

            this.CalculateSectionOffsets(package.Signature, k => (int)k);
        }
        /// <summary>
        /// Gets a <see cref="MemoryStream"/> wich represents the entire signature.
        /// </summary>
        /// <param name="package">
        /// The package for which to get the header stream.
        /// </param>
        /// <returns>
        /// A <see cref="MemoryStream"/> wich represents the entire signature.
        /// </returns>
        public MemoryStream GetSignatureStream(RpmPackage package)
        {
            MemoryStream stream = new MemoryStream();

            this.WriteSignature(package, stream);
            stream.Position = 0;
            return(stream);
        }
示例#3
0
        /// <summary>
        /// Determines the offsets for all records in the header of a package.
        /// </summary>
        /// <param name="package">
        /// The package for which to generate the offsets.
        /// </param>
        public void CalculateHeaderOffsets(RpmPackage package)
        {
            var metadata = new RpmMetadata(package);

            metadata.ImmutableRegionSize = -1 * Marshal.SizeOf <IndexHeader>() * (package.Header.Records.Count + 1);

            this.CalculateSectionOffsets(package.Header, k => (int)k);
        }
        /// <summary>
        /// Gets a <see cref="MemoryStream"/> wich represents the entire header.
        /// </summary>
        /// <param name="package">
        /// The package for which to get the header stream.
        /// </param>
        /// <returns>
        /// A <see cref="MemoryStream"/> wich represents the entire header.
        /// </returns>
        public MemoryStream GetHeaderStream(RpmPackage package)
        {
            MemoryStream stream = new MemoryStream();

            this.WriteHeader(package, stream);
            stream.Position = 0;
            return(stream);
        }
示例#5
0
 public static void Dump(RpmPackage package, string path)
 {
     using (var file = File.Open(path, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
         using (StreamWriter writer = new StreamWriter(file))
         {
             Dump(package, writer);
         }
 }
示例#6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="RpmSignature"/> class.
        /// </summary>
        /// <param name="package">
        /// The package for which to access the signature.
        /// </param>
        public RpmSignature(RpmPackage package)
        {
            if (package == null)
            {
                throw new ArgumentNullException(nameof(package));
            }

            this.Package = package;
        }
示例#7
0
        public RpmMetadata(RpmPackage package)
        {
            if (package == null)
            {
                throw new ArgumentNullException(nameof(package));
            }

            this.Package = package;
        }
示例#8
0
        /// <summary>
        /// Gets a <see cref="Stream"/> which allows reading the decompressed payload data.
        /// </summary>
        /// <param name="package">
        /// The package for which to read the decompressed payload data.
        /// </param>
        /// <returns>
        /// A <see cref="Stream"/> which allows reading the decompressed payload data.
        /// </returns>
        public static Stream GetDecompressedPayloadStream(RpmPackage package)
        {
            if (package == null)
            {
                throw new ArgumentNullException(nameof(package));
            }

            var compressedPayloadStream = GetCompressedPayloadStream(package);

            return(GetDecompressedPayloadStream(package, compressedPayloadStream));
        }
示例#9
0
        /// <summary>
        /// Gets the compressed payload for a package.
        /// </summary>
        /// <param name="package">
        /// The package for which to get the compressed payload.
        /// </param>
        /// <returns>
        /// A <see cref="Stream"/> which contains the compressed payload.
        /// </returns>
        public static Stream GetCompressedPayloadStream(RpmPackage package)
        {
            if (package == null)
            {
                throw new ArgumentNullException(nameof(package));
            }

            SubStream compressedPayloadStream = new SubStream(
                stream: package.Stream,
                offset: package.PayloadOffset,
                length: package.Stream.Length - package.PayloadOffset,
                leaveParentOpen: true,
                readOnly: true);

            return(compressedPayloadStream);
        }
        /// <summary>
        /// Writes a <see cref="RpmPackage"/> to a <see cref="Stream"/>.
        /// </summary>
        /// <param name="stream">
        /// The <see cref="Stream"/> to which to write the <see cref="RpmPackage"/>.
        /// </param>
        /// <param name="package">
        /// The <see cref="RpmPackage"/> to write to the stream.
        /// </param>
        public static void Write(Stream stream, RpmPackage package)
        {
            if (package == null)
            {
                throw new ArgumentNullException(nameof(package));
            }

            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            stream.WriteStruct(package.Lead);

            WriteSection <SignatureTag>(stream, package.Signature, DefaultOrder.Signature);
            WriteSection <IndexTag>(stream, package.Header, DefaultOrder.Header);
        }
示例#11
0
        public static void Dump(RpmPackage package, StreamWriter writer)
        {
            writer.WriteLine("Lead:");
            writer.WriteLine("  ArchNum        {0}", package.Lead.ArchNum);
            writer.WriteLine("  Magic          {0}", package.Lead.Magic);
            writer.WriteLine("  Major          {0}", package.Lead.Major);
            writer.WriteLine("  Minor          {0}", package.Lead.Minor);
            writer.WriteLine("  Name           {0}", package.Lead.Name);
            writer.WriteLine("  OsNum          {0}", package.Lead.OsNum);
            writer.WriteLine("  SignatureType  {0}", package.Lead.SignatureType);
            writer.WriteLine("  Type           {0}", package.Lead.Type);

            writer.WriteLine();

            writer.WriteLine("Signature:");
            Dump(package.Signature, writer);

            writer.WriteLine("Header:");
            Dump(package.Header, writer);
        }
示例#12
0
 /// <summary>
 /// Calculates the signature for this package.
 /// </summary>
 /// <param name="package">
 /// The package for whcih to calculate the signature.
 /// </param>
 /// <param name="privateKey">
 /// The private key to use when signing packages.
 /// </param>
 /// <param name="compressedPayloadStream">
 /// The compressed payload.
 /// </param>
 public void CalculateSignature(RpmPackage package, PgpPrivateKey privateKey, Stream compressedPayloadStream)
 {
     this.CalculateSignature(package, new PackageSigner(privateKey), compressedPayloadStream);
 }
示例#13
0
 /// <summary>
 /// Writes the signature to a <see cref="Stream"/>.
 /// </summary>
 /// <param name="package">
 /// The package for which to write the signature.
 /// </param>
 /// <param name="targetStream">
 /// The <see cref="Stream"/> tow chih to write the package.
 /// </param>
 public void WriteSignature(RpmPackage package, Stream targetStream)
 {
     RpmPackageWriter.WriteSection(targetStream, package.Signature, DefaultOrder.Signature);
 }
示例#14
0
 /// <summary>
 /// Writes the header to a <see cref="Stream"/>.
 /// </summary>
 /// <param name="package">
 /// The package for which to write the header.
 /// </param>
 /// <param name="targetStream">
 /// The <see cref="Stream"/> to which to write the header.
 /// </param>
 public void WriteHeader(RpmPackage package, Stream targetStream)
 {
     RpmPackageWriter.WriteSection(targetStream, package.Header, DefaultOrder.Header);
 }
示例#15
0
        /// <summary>
        /// Creates a RPM Package.
        /// </summary>
        /// <param name="archiveEntries">
        /// The archive entries which make up the RPM package.
        /// </param>
        /// <param name="payloadStream">
        /// A <see cref="Stream"/> which contains the CPIO archive for the RPM package.
        /// </param>
        /// <param name="name">
        /// The name of the package.
        /// </param>
        /// <param name="version">
        /// The version of the software.
        /// </param>
        /// <param name="arch">
        /// The architecture targetted by the package.
        /// </param>
        /// <param name="release">
        /// The release version.
        /// </param>
        /// <param name="createUser">
        /// <see langword="true"/> to create a user account; otherwise, <see langword="false"/>.
        /// </param>
        /// <param name="userName">
        /// The name of the user account to create.
        /// </param>
        /// <param name="installService">
        /// <see langword="true"/> to install a system service, otherwise, <see langword="false"/>.
        /// </param>
        /// <param name="serviceName">
        /// The name of the system service to create.
        /// </param>
        /// <param name="vendor">
        /// The package vendor.
        /// </param>
        /// <param name="description">
        /// The package description.
        /// </param>
        /// <param name="url">
        /// The package URL.
        /// </param>
        /// <param name="prefix">
        /// A prefix to use.
        /// </param>
        /// <param name="preInstallScript">
        /// Pre-Install script
        /// </param>
        /// <param name="postInstallScript">
        /// Post-Install script
        /// </param>
        /// <param name="preRemoveScript">
        /// Pre-Remove script
        /// </param>
        /// <param name="postRemoveScript">
        /// Post-Remove script
        /// </param>
        /// <param name="additionalDependencies">
        /// Additional dependencies to add to the RPM package.
        /// </param>
        /// <param name="additionalMetadata">
        /// Any additional metadata.
        /// </param>
        /// <param name="signer">
        /// The signer to use when signing the package.
        /// </param>
        /// <param name="targetStream">
        /// The <see cref="Stream"/> to which to write the package.
        /// </param>
        /// <param name="includeVersionInName">
        /// <see langword="true"/> to include the version number and release number
        /// in the <see cref="RpmLead.Name"/>; <see langword="false"/> to only
        /// use the package name.
        /// </param>
        /// <param name="payloadIsCompressed">
        /// <see langword="true"/> if <paramref name="payloadStream"/> is already
        /// compressed. In this case, the <paramref name="payloadStream"/> will be
        /// copied "as is" to the resulting RPM package.
        /// </param>
        public void CreatePackage(
            List <ArchiveEntry> archiveEntries,
            Stream payloadStream,
            string name,
            string version,
            string arch,
            string release,
            bool createUser,
            string userName,
            bool installService,
            string serviceName,
            string vendor,
            string description,
            string url,
            string prefix,
            string preInstallScript,
            string postInstallScript,
            string preRemoveScript,
            string postRemoveScript,
            IEnumerable <PackageDependency> additionalDependencies,
            Action <RpmMetadata> additionalMetadata,
            IPackageSigner signer,
            Stream targetStream,
            bool includeVersionInName = false,
            bool payloadIsCompressed  = false)
        {
            // This routine goes roughly like:
            // 1. Calculate all the metadata, including a signature,
            //    but use an empty compressed payload to calculate
            //    the signature
            // 2. Write out the rpm file, and compress the payload
            // 3. Update the signature
            //
            // This way, we avoid having to compress the payload into a temporary
            // file.

            // Core routine to populate files and dependencies (part of the metadata
            // in the header)
            RpmPackage package  = new RpmPackage();
            var        metadata = new RpmMetadata(package)
            {
                Name    = name,
                Version = version,
                Arch    = arch,
                Release = release,
            };

            this.AddPackageProvides(metadata);
            this.AddLdDependencies(metadata);

            var files = this.CreateFiles(archiveEntries);

            metadata.Files = files;

            this.AddRpmDependencies(metadata, additionalDependencies);

            // Try to define valid defaults for most metadata
            metadata.Locales = new Collection <string> {
                "C"
            };                                                 // Should come before any localizable data.
            metadata.BuildHost         = "dotnet-rpm";
            metadata.BuildTime         = DateTimeOffset.Now;
            metadata.Cookie            = "dotnet-rpm";
            metadata.FileDigetsAlgo    = PgpHashAlgo.PGPHASHALGO_SHA256;
            metadata.Group             = "System Environment/Libraries";
            metadata.OptFlags          = string.Empty;
            metadata.Os                = "linux";
            metadata.PayloadCompressor = "xz";
            metadata.PayloadFlags      = "2";
            metadata.PayloadFormat     = "cpio";
            metadata.Platform          = "x86_64-redhat-linux-gnu";
            metadata.RpmVersion        = "4.11.3";
            metadata.SourcePkgId       = new byte[0x10];
            metadata.SourceRpm         = $"{name}-{version}-{release}.src.rpm";

            // Scripts which run before & after installation and removal.
            var preIn  = preInstallScript ?? string.Empty;
            var postIn = postInstallScript ?? string.Empty;
            var preUn  = preRemoveScript ?? string.Empty;
            var postUn = postRemoveScript ?? string.Empty;

            if (createUser)
            {
                // Add the user and group, under which the service runs.
                // These users are never removed because UIDs are re-used on Linux.
                preIn += $"/usr/sbin/groupadd -r {userName} 2>/dev/null || :\n" +
                         $"/usr/sbin/useradd -g {userName} -s /sbin/nologin -r {userName} 2>/dev/null || :\n";
            }

            if (installService)
            {
                // Install and activate the service.
                postIn +=
                    $"if [ $1 -eq 1 ] ; then \n" +
                    $"    systemctl enable --now {serviceName}.service >/dev/null 2>&1 || : \n" +
                    $"fi\n";

                preUn +=
                    $"if [ $1 -eq 0 ] ; then \n" +
                    $"    # Package removal, not upgrade \n" +
                    $"    systemctl --no-reload disable --now {serviceName}.service > /dev/null 2>&1 || : \n" +
                    $"fi\n";

                postUn +=
                    $"if [ $1 -ge 1 ] ; then \n" +
                    $"    # Package upgrade, not uninstall \n" +
                    $"    systemctl try-restart {serviceName}.service >/dev/null 2>&1 || : \n" +
                    $"fi\n";
            }

            // Remove all directories marked as such (these are usually directories which contain temporary files)
            foreach (var entryToRemove in archiveEntries.Where(e => e.RemoveOnUninstall))
            {
                preUn +=
                    $"if [ $1 -eq 0 ] ; then \n" +
                    $"    # Package removal, not upgrade \n" +
                    $"    /usr/bin/rm -rf {entryToRemove.TargetPath}\n" +
                    $"fi\n";
            }

            if (!string.IsNullOrEmpty(preIn))
            {
                metadata.PreInProg = "/bin/sh";
                metadata.PreIn     = preIn;
            }

            if (!string.IsNullOrEmpty(postIn))
            {
                metadata.PostInProg = "/bin/sh";
                metadata.PostIn     = postIn;
            }

            if (!string.IsNullOrEmpty(preUn))
            {
                metadata.PreUnProg = "/bin/sh";
                metadata.PreUn     = preUn;
            }

            if (!string.IsNullOrEmpty(postUn))
            {
                metadata.PostUnProg = "/bin/sh";
                metadata.PostUn     = postUn;
            }

            // Not providing these (or setting empty values) would cause rpmlint errors
            metadata.Description = string.IsNullOrEmpty(description)
                ? $"{name} version {version}-{release}"
                : description;
            metadata.Summary = $"{name} version {version}-{release}";
            metadata.License = $"{name} License";

            metadata.Distribution = string.Empty;
            metadata.DistUrl      = string.Empty;
            metadata.Url          = url ?? string.Empty;
            metadata.Vendor       = vendor ?? string.Empty;

            metadata.ChangelogEntries = new Collection <ChangelogEntry>()
            {
                new ChangelogEntry(DateTimeOffset.Now, "dotnet-rpm", "Created a RPM package using dotnet-rpm")
            };

            // User-set metadata
            if (additionalMetadata != null)
            {
                additionalMetadata(metadata);
            }

            this.CalculateHeaderOffsets(package);

            using (MemoryStream dummyCompressedPayload = new MemoryStream())
            {
                using (XZOutputStream dummyPayloadCompressor =
                           new XZOutputStream(
                               dummyCompressedPayload,
                               this.CompressionThreads,
                               XZOutputStream.DefaultPreset,
                               leaveOpen: true))
                {
                    dummyPayloadCompressor.Write(new byte[] { 0 }, 0, 1);
                }

                this.CalculateSignature(package, signer, dummyCompressedPayload);
            }

            this.CalculateSignatureOffsets(package);

            // Write out all the data - includes the lead
            byte[] nameBytes  = new byte[66];
            var    nameInLead = includeVersionInName ? $"{name}-{version}-{release}" : name;

            Encoding.UTF8.GetBytes(nameInLead, 0, nameInLead.Length, nameBytes, 0);

            var lead = new RpmLead()
            {
                ArchNum       = 1,
                Magic         = 0xedabeedb,
                Major         = 0x03,
                Minor         = 0x00,
                NameBytes     = nameBytes,
                OsNum         = 0x0001,
                Reserved      = new byte[16],
                SignatureType = 0x0005,
                Type          = 0x0000,
            };

            // Write out the lead, signature and header
            targetStream.Position = 0;
            targetStream.SetLength(0);

            targetStream.WriteStruct(lead);
            this.WriteSignature(package, targetStream);
            this.WriteHeader(package, targetStream);

            // Write out the compressed payload
            int compressedPayloadOffset = (int)targetStream.Position;

            // The user can choose to pass an already-comrpessed
            // payload. In this case, no need to re-compress.
            if (payloadIsCompressed)
            {
                payloadStream.CopyTo(targetStream);
            }
            else
            {
                using (XZOutputStream compressor = new XZOutputStream(
                           targetStream,
                           XZOutputStream.DefaultThreads,
                           XZOutputStream.DefaultPreset,
                           leaveOpen: true))
                {
                    payloadStream.Position = 0;
                    payloadStream.CopyTo(compressor);
                }
            }

            using (SubStream compressedPayloadStream = new SubStream(
                       targetStream,
                       compressedPayloadOffset,
                       targetStream.Length - compressedPayloadOffset,
                       leaveParentOpen: true,
                       readOnly: true))
            {
                this.CalculateSignature(package, signer, compressedPayloadStream);
                this.CalculateSignatureOffsets(package);
            }

            // Update the lead and signature
            targetStream.Position = 0;

            targetStream.WriteStruct(lead);
            this.WriteSignature(package, targetStream);
        }