Esempio n. 1
0
        /// <summary>
        /// Reads, decrypts and returns the header of an entity
        /// </summary>
        /// <returns></returns>
        /// <exception cref="InvalidArchiveException"></exception>
        private JpsEntityDescriptionBlockData ReadFileHeader()
        {
            JpsEntityDescriptionBlockHeader preamble   = new JpsEntityDescriptionBlockHeader();
            JpsEntityDescriptionBlockData   fileHeader = new JpsEntityDescriptionBlockData();

            // Read the entity description block header
            preamble.Signature = ReadAsciiString(3);

            if (preamble.Signature != "JPF")
            {
                throw new InvalidArchiveException(
                          String.Format(Language.ResourceManager.GetString("ERR_FORMAT_INVALID_HEADER_AT_POSITION"),
                                        CurrentPartNumber, Progress.FilePosition)
                          );
            }

            preamble.EncryptedSize = ReadUShort();
            preamble.DecryptedSize = ReadUShort();

            // Read the encrypted data
            using (MemoryStream encryptedSteam = ReadIntoStream(preamble.EncryptedSize))
            {
                using (MemoryStream decryptedStream = Decrypt(encryptedSteam))
                {
                    if (decryptedStream.Length != preamble.DecryptedSize)
                    {
                        throw new InvalidArchiveException(
                                  String.Format(
                                      Language.ResourceManager.GetString("ERR_FORMAT_JPS_DECRYPTED_SIZE_DIFFERENCE"),
                                      preamble.DecryptedSize, decryptedStream.Length)
                                  );
                    }

                    fileHeader.PathLength = ReadUShort(decryptedStream);
                    fileHeader.Path       = ReadUtf8String(fileHeader.PathLength, decryptedStream);

                    switch (ReadByte(decryptedStream))
                    {
                    case 0:
                        fileHeader.EntityType = TEntityType.Directory;
                        break;

                    case 1:
                        fileHeader.EntityType = TEntityType.File;
                        break;

                    case 2:
                        fileHeader.EntityType = TEntityType.Symlink;
                        break;

                    default:
                        throw new InvalidArchiveException(Language.ResourceManager.GetString("ERR_FORMAT_INVALID_ENTITY_TYPE"));
                        //break;
                    }

                    switch (ReadByte(decryptedStream))
                    {
                    case 0:
                        fileHeader.CompressionType = TCompressionType.Uncompressed;
                        break;

                    case 1:
                        fileHeader.CompressionType = TCompressionType.GZip;
                        break;

                    case 2:
                        fileHeader.CompressionType = TCompressionType.BZip2;
                        break;

                    default:
                        throw new InvalidArchiveException(Language.ResourceManager.GetString("ERR_FORMAT_INVALID_COMPRESSION_METHOD"));
                        //break;
                    }

                    fileHeader.UncompressedSize     = ReadULong(decryptedStream);
                    fileHeader.Permissions          = ReadULong(decryptedStream);
                    fileHeader.FileModificationTime = ReadULong(decryptedStream);
                }
            }

            // Invoke the OnEntityEvent event. We need to do some work to get there, through...
            // -- Create a new archive information record
            EntityInformation info = new EntityInformation();

            // -- Incorporate bits from the file header
            info.CompressedSize   = 0; // JPS does not report the compressed size. We have to read a chunk at a time.
            info.UncompressedSize = fileHeader.UncompressedSize;
            info.StoredName       = fileHeader.Path;
            // -- Get the absolute path of the file
            info.AbsoluteName = "";

            if (DataWriter != null)
            {
                info.AbsoluteName = DataWriter.GetAbsoluteFilePath(fileHeader.Path);
            }
            // -- Get some bits from the currently open archive file
            info.PartNumber = CurrentPartNumber ?? 1;
            info.PartOffset = InputStream.Position;
            // -- Create the event arguments object
            EntityEventArgs args = new EntityEventArgs(info);

            // -- Finally, invoke the event
            OnEntityEvent(args);

            return(fileHeader);
        }
Esempio n. 2
0
        /// <summary>
        /// Processes a data block in a JPA file located in the current file position
        /// </summary>
        /// <param name="dataBlockHeader">The header of the block being processed</param>
        /// <param name="token">A cancellation token, allowing the called to cancel the processing</param>
        private void ProcessDataBlock(JpsEntityDescriptionBlockData dataBlockHeader, CancellationToken token)
        {
            // Update the archive's progress record
            Progress.FilePosition = (ulong)(SizesOfPartsAlreadyRead + InputStream.Position);
            Progress.Status       = ExtractionStatus.Running;

            // Create the event arguments we'll use when invoking the event
            ProgressEventArgs args = new ProgressEventArgs(Progress);

            switch (dataBlockHeader.EntityType)
            {
            // Just a directory? Create it and return.
            case TEntityType.Directory:
                if (DataWriter != null)
                {
                    DataWriter.MakeDirRecursive(DataWriter.GetAbsoluteFilePath(dataBlockHeader.Path));
                }
                break;

            // Symlink? Read the encrypted target and create the symlink
            case TEntityType.Symlink:
                using (MemoryStream source = ReadAndDecryptNextDataChunkBlock())
                {
                    string symlinkTarget = ReadUtf8String((int)dataBlockHeader.UncompressedSize);

                    if (DataWriter != null)
                    {
                        DataWriter.MakeSymlink(symlinkTarget, DataWriter.GetAbsoluteFilePath(dataBlockHeader.Path));
                    }
                }

                Progress.RunningUncompressed += dataBlockHeader.UncompressedSize;
                break;

            // We have a file. Now it's more complicated. We have to read through and process each chunk until
            // we have reached the decompressed size.
            case TEntityType.File:
                ulong currentRunningDecompressed = 0;

                if (DataWriter != null)
                {
                    DataWriter.StartFile(dataBlockHeader.Path);
                }

                while (currentRunningDecompressed < dataBlockHeader.UncompressedSize)
                {
                    // First chance to cancel: before decompressing data
                    if (token.IsCancellationRequested)
                    {
                        token.ThrowIfCancellationRequested();
                    }

                    using (MemoryStream decryptedStream = ReadAndDecryptNextDataChunkBlock())
                    {
                        // Second chance to cancel: after decompressing data, before decompressing / writing data
                        if (token.IsCancellationRequested)
                        {
                            token.ThrowIfCancellationRequested();
                        }

                        switch (dataBlockHeader.CompressionType)
                        {
                        case TCompressionType.GZip:
                            using (DeflateStream decompressStream = new DeflateStream(decryptedStream, CompressionMode.Decompress))
                            {
                                // We need to decompress the data to get its length
                                using (MemoryStream sourceStream = new MemoryStream())
                                {
                                    decompressStream.CopyTo(sourceStream);
                                    sourceStream.Seek(0, SeekOrigin.Begin);

                                    ulong sourceStreamLength = (ulong)sourceStream.Length;
                                    currentRunningDecompressed += sourceStreamLength;

                                    if (DataWriter != null)
                                    {
                                        DataWriter.WriteData(sourceStream);
                                    }
                                }
                            }
                            break;

                        case TCompressionType.BZip2:
                            using (BZip2InputStream decompressStream = new BZip2InputStream(decryptedStream))
                            {
                                // We need to decompress the data to get its length
                                using (MemoryStream sourceStream = new MemoryStream())
                                {
                                    decompressStream.CopyTo(sourceStream);
                                    sourceStream.Seek(0, SeekOrigin.Begin);

                                    ulong sourceStreamLength = (ulong)sourceStream.Length;
                                    currentRunningDecompressed += sourceStreamLength;

                                    if (DataWriter != null)
                                    {
                                        DataWriter.WriteData(sourceStream);
                                    }
                                }
                            }
                            break;

                        case TCompressionType.Uncompressed:
                            currentRunningDecompressed += (ulong)decryptedStream.Length;

                            if (DataWriter != null)
                            {
                                DataWriter.WriteData(decryptedStream);
                            }
                            break;
                        }
                    }
                }

                Progress.RunningUncompressed += currentRunningDecompressed;
                Progress.FilePosition         = (ulong)(SizesOfPartsAlreadyRead + InputStream.Position);

                if (DataWriter != null)
                {
                    DataWriter.StopFile();
                }

                break;
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Implements the Extract method which extracts a backup archive. A DataWriter must be already assigned and configured or an
        /// exception will be raised.
        /// </summary>
        public override void Extract(CancellationToken token)
        {
            // Initialize
            Close();
            Progress.FilePosition        = 0;
            Progress.RunningCompressed   = 0;
            Progress.RunningUncompressed = 0;
            Progress.Status        = ExtractionStatus.Running;
            Progress.LastException = null;
            ProgressEventArgs args;

            JpsHeaderData archiveHeader;

            if (_legacyKey == null)
            {
                throw new InvalidArchiveException(Language.ResourceManager.GetString("ERR_FORMAT_JPS_EMPTY_PASSWORD"));
            }

            try
            {
                // Read the archive header
                archiveHeader = ReadArchiveHeader();

                // Invoke event at start of extraction
                args = new ProgressEventArgs(Progress);
                OnProgressEvent(args);

                // The end of archive is 18 bytes before the end of the archive due to the End of Archive record
                while ((CurrentPartNumber != null) && (Progress.FilePosition < (archiveHeader.TotalSize - 18)))
                {
                    JpsEntityDescriptionBlockData fileHeader = ReadFileHeader();

                    if (token.IsCancellationRequested)
                    {
                        token.ThrowIfCancellationRequested();
                    }

                    ProcessDataBlock(fileHeader, token);

                    args = new ProgressEventArgs(Progress);
                    OnProgressEvent(args);
                }
            }
            catch (OperationCanceledException cancelledException)
            {
                // The operation was cancelled. Set the state to Idle and reset the extraction.
                Close();
                Progress.FilePosition        = 0;
                Progress.RunningCompressed   = 0;
                Progress.RunningUncompressed = 0;
                Progress.Status        = ExtractionStatus.Idle;
                Progress.LastException = cancelledException;

                // Invoke an event notifying susbcribers about the cancelation.
                args = new ProgressEventArgs(Progress);
                OnProgressEvent(args);

                return;
            }
            catch (Exception errorException)
            {
                // Any other exception. Close the option file and set the status to error.
                Close();
                Progress.Status        = ExtractionStatus.Error;
                Progress.LastException = errorException;

                // Invoke an event notifying of the error state
                args = new ProgressEventArgs(Progress);
                OnProgressEvent(args);

                return;
            }

            // Invoke an event signaling the end of extraction
            Progress.Status = ExtractionStatus.Finished;
            args            = new ProgressEventArgs(Progress);
            OnProgressEvent(args);
        }