示例#1
0
        /// <summary>
        /// List the contents of the given compressed archive.
        /// </summary>
        /// <param name="compressedArchiveAccess">An instance of <see cref="ICompressedArchiveAccess"/> whose contents are to be listed.</param>
        /// <param name="locationInArchive">A location relative to the root of the archive. The special values <c>null</c>, <c>string.Empty</c>, '\', '/', or '.' indicate the root. Otherwise, must end with a directory separator character.</param>
        /// <param name="includeContainers">If <c>true</c>, include entries that may contain other entries, such as other compressed archives and directories.</param>
        /// <param name="recurse">If <c>true</c>, list all contents from <paramref name="locationInArchive"/> and below, recursively. The contents of nested archives will also be listed.</param>
        /// <returns>The list of entries, which may include entries that could contain more items, depending on the value of <paramref name="includeContainers"/>.
        /// Entry names are always relative to <paramref name="compressedArchiveAccess"/>. Path separators will be normalized to forward slash.</returns>
        /// <remarks>NOTE: Large and /or deeply nested archives may incur performance and disk penalties. Use with care!</remarks>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="compressedArchiveAccess"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">Thrown if <paramref name="locationInArchive"/> is malformed i.e. is not null or empty, or does not end with a directory separator character.</exception>
        /// <exception cref="FileNotFoundException">Thrown if <paramref name="locationInArchive"/> identifies a nested archive that cannot be located.</exception>
        public static IEnumerable <string> ListContents(this ICompressedArchiveAccess compressedArchiveAccess, string locationInArchive, bool includeContainers, bool recurse)
        {
            var entries  = compressedArchiveAccess.ListEntries(locationInArchive, includeContainers, recurse);
            var contents = entries.Select(e => e.Name);

            return(contents);
        }
示例#2
0
        public void ICompressedArchiveAccess_ListWithNullArchive_ThrowsArgumentNullException()
        {
            ICompressedArchiveAccess archive = null;

            Assert.Throws <ArgumentNullException>(() => archive.ListEntries(null, includeContainers: false));
            Assert.Throws <ArgumentNullException>(() => archive.ListContents(null, includeContainers: false));
        }
示例#3
0
            /// <summary>
            /// Creates an instance of <see cref="ICompressedArchiveAccess"/> that can be used to access a nested archive.
            /// </summary>
            /// <param name="parentArchiveAccess">The parent archive, which contains the nested archive indicated by <paramref name="entry"/>.</param>
            /// <param name="entry">The entry within <paramref name="parentArchiveAccess"/> that indicates a nested archive.</param>
            /// <returns>An instance of <see cref="NestedCompressedArchiveAccess"/>, which provides access to the nested archive.</returns>
            /// <remarks>This wrapper type takes care of cleaning up any temporary files that may be required to access the nested archive.
            /// For example, some data streams used to access an archive are not navigable from the parent stream (GZIP).</remarks>
            public static NestedCompressedArchiveAccess Create(ICompressedArchiveAccess parentArchiveAccess, ICompressedArchiveEntry entry)
            {
                NestedCompressedArchiveAccess nestedCompressedArchive = null;
                var entryName            = entry.Name;
                var nestedArchiveFormats = Path.GetExtension(entryName).GetCompressedArchiveFormatsFromFileExtension();
                var nestedArchiveFormat  = nestedArchiveFormats.FirstOrDefault();

                if (nestedArchiveFormat.IsCompressedArchiveFormatSupported())
                {
                    TemporaryDirectory temporaryLocation = null;
                    var entryData = parentArchiveAccess.OpenEntry(entry);
                    if (FormatMustBeExtracted(parentArchiveAccess.Format) || FormatMustBeExtracted(nestedArchiveFormat))
                    {
                        // We can't fully navigate the nested stream, so extract to disk, then proceed.
                        temporaryLocation = new TemporaryDirectory();
                        var temporaryEntryFilePath = Path.Combine(temporaryLocation.Path, entryName);
                        if (Directory.CreateDirectory(Path.GetDirectoryName(temporaryEntryFilePath)).Exists)
                        {
                            System.Diagnostics.Debug.WriteLine("Extracted entry " + entryName + " to " + temporaryLocation.Path);
                            var fileStream = new FileStream(temporaryEntryFilePath, FileMode.Create, FileAccess.ReadWrite);
                            entryData.CopyTo(fileStream);
                            fileStream.Seek(0, SeekOrigin.Begin);
                            entryData.Dispose();
                            entryData = fileStream;
                        }
                    }

                    var compressedArchive = CompressedArchiveAccess.Open(entryData, nestedArchiveFormat, CompressedArchiveAccessMode.Read);
                    nestedCompressedArchive = new NestedCompressedArchiveAccess(parentArchiveAccess, compressedArchive, temporaryLocation);
                    nestedCompressedArchive.NestedArchiveFormats = nestedArchiveFormats;
                }

                return(nestedCompressedArchive);
            }
            /// <summary>
            /// Creates an instance of <see cref="CompressedArchiveFileAccess"/>, depending on <paramref name="mode"/>.
            /// </summary>
            /// <param name="filePath">The absolute path for the compressed archive.</param>
            /// <param name="mode">The access mode to use for operations on the compressed archive.</param>
            /// <param name="implementation">If not <c>null</c>, use a specific implementation if possible. Otherwise, use default, or any.</param>
            /// <returns>An instance of <see cref="CompressedArchiveFileAccess"/> that provides access to the compressed archive located at <paramref name="filePath"/>.</returns>
            /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="filePath"/> is <c>null</c>.</exception>
            /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the file is accessed incorrectly for the given <paramref name="mode"/>.</exception>
            /// <exception cref="System.ArgumentException">Thrown if <paramref name="filePath"/> is empty or white space, contains invalid characters, or is otherwise invalid</exception>
            /// <exception cref="System.NotSupportedException">Thrown if <paramref name="filePath"/> refers to a non-file device, e.g. a COM port, et. al.</exception>
            /// <exception cref="System.IO.FileNotFoundException">Thrown if <paramref name="filePath"/> cannot be found.</exception>
            /// <exception cref="System.IO.IOException">Thrown if an I/O error occurs.</exception>
            /// <exception cref="System.Security.SecurityException">Thrown if the requested action on <paramref name="filePath"/> cannot be performed, e.g. no read / create access is granted, et. al.</exception>
            /// <exception cref="System.IO.DirectoryNotFoundException">Thrown if the directory cannot be found, e.g. an unavailable network drive forms part of the file path.</exception>
            /// <exception cref="System.UnauthorizedAccessException">Thrown if access is denied, e.g. read/write access requested for a read-only file or directory.</exception>
            /// <exception cref="System.IO.PathTooLongException">Thrown if <paramref name="filePath"/> is too long.</exception>
            /// <exception cref="System.InvalidOperationException">Thrown if it is not possible to create an instance of <see cref="CompressedArchiveFileAccess"/> from the file at <paramref name="filePath"/>,
            /// despite the file appearing to be valid.</exception>
            public static CompressedArchiveFileAccess Create(string filePath, CompressedArchiveAccessMode mode, CompressedArchiveAccessImplementation?implementation)
            {
                var fileMode   = CompressedArchiveAccessModeToFileMode(mode);
                var fileAccess = CompressedArchiveAccessModeToFileAccess(mode);
                var fileName   = Path.GetFileName(filePath);

                var successfullyAccessedFormats = new List <CompressedArchiveFormat>();
                var fileStream = new FileStream(filePath, fileMode, fileAccess);
                var formats    = filePath.GetCompressedArchiveFormatsFromFileName();
                ICompressedArchiveAccess compressedArchiveAccess = null;

#if OPEN_NESTED_FORMAT_IMMEDIATELY
                Stream stream = fileStream;
                foreach (var format in formats)
                {
                    compressedArchiveAccess = Utility.CompressedArchiveAccess.Open(stream, format, mode);
                    if (compressedArchiveAccess != null)
                    {
                        successfullyAccessedFormats.Add(format);
                        if (!compressedArchiveAccess.IsArchive)
                        {
                            fileName = Path.GetFileNameWithoutExtension(fileName);
                            var entry = compressedArchiveAccess.FindEntry(fileName);
                            stream = entry == null ? null : compressedArchiveAccess.OpenEntry(entry);
                            if (stream == null)
                            {
                                compressedArchiveAccess = null;
                                break;
                            }
                        }
                    }
                    else
                    {
                        break;
                    }
                }
#else
                var format = formats.FirstOrDefault();
                if (format != CompressedArchiveFormat.None)
                {
                    compressedArchiveAccess = Utility.CompressedArchiveAccess.Open(fileStream, format, mode, implementation);
                }
#endif

                if (compressedArchiveAccess == null)
                {
                    var identifiedFormats          = string.Join(CultureInfo.CurrentCulture.TextInfo.ListSeparator, formats);
                    var successfullyCreatedFormats = string.Join(CultureInfo.CurrentCulture.TextInfo.ListSeparator, successfullyAccessedFormats);
                    var failedFormats = string.Join(CultureInfo.CurrentCulture.TextInfo.ListSeparator, formats.Except(successfullyAccessedFormats));
                    var errorMessage  = string.Format(CultureInfo.CurrentCulture, Resources.Strings.CompressedArchiveAccess_UnableToProcessError_Format, filePath, identifiedFormats, successfullyCreatedFormats, failedFormats);
                    throw new InvalidOperationException(errorMessage);
                }

                var compressedArchiveFileAccess = new CompressedArchiveFileAccess(filePath, compressedArchiveAccess)
                {
                    Stream = fileStream
                };
                return(compressedArchiveFileAccess);
            }
示例#5
0
        /// <summary>
        /// List the contents of the given compressed archive.
        /// </summary>
        /// <param name="compressedArchiveAccess">An instance of <see cref="ICompressedArchiveAccess"/> whose contents are to be listed.</param>
        /// <param name="locationInArchive">A location relative to the root of the archive. The special values <c>null</c>, <c>string.Empty</c>, '\', '/', or '.' indicate the root. Otherwise, must end with a directory separator character.</param>
        /// <param name="includeContainers">If <c>true</c>, include entries that may contain other entries, such as other compressed archives and directories.</param>
        /// <param name="recurse">If <c>true</c>, list all contents from <paramref name="locationInArchive"/> and below, recursively. The contents of nested archives will also be listed.</param>
        /// <returns>The list of entries, which may include entries that could contain more items, depending on the value of <paramref name="includeContainers"/>.
        /// Entry names are always relative to <paramref name="compressedArchiveAccess"/>. Path separators will be normalized to forward slash.</returns>
        /// <remarks>NOTE: Large and / or deeply nested archives may incur performance and disk penalties. Use with care!</remarks>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="compressedArchiveAccess"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">Thrown if <paramref name="locationInArchive"/> is malformed i.e. is not null or empty, or does not end with a directory separator character.</exception>
        /// <exception cref="FileNotFoundException">Thrown if <paramref name="locationInArchive"/> identifies a nested archive that cannot be located.</exception>
        public static IEnumerable <ICompressedArchiveEntry> ListEntries(this ICompressedArchiveAccess compressedArchiveAccess, string locationInArchive, bool includeContainers, bool recurse)
        {
            var entries = new List <ICompressedArchiveEntry>(ListEntriesInCompressedArchive(compressedArchiveAccess, locationInArchive, includeContainers || recurse));

            if (recurse)
            {
                // Uses a simple queue to process nested contents in a breadth-first-ish fashion.
                // Nested archives are kept around as discovered so subsequent listings that may cause actual extraction to temporary
                // locations on disk is somewhat mitigated.
                var nestedCompressedArchives = new Dictionary <string, ICompressedArchiveAccess>();
                var containers = new Queue <ICompressedArchiveEntry>(entries.Where(e => e.IsDirectory || e.Name.IsContainer()));
                while (containers.Any())
                {
                    var container = containers.Dequeue();
                    ICompressedArchiveAccess nestedCompressedArchiveAccess;
                    string nestedArchiveRelativeLocation;
                    var    nestedAchiveLocation = container.Name.GetMostDeeplyNestedContainerLocation(out nestedArchiveRelativeLocation);
                    if (nestedAchiveLocation != null)
                    {
                        if (!nestedCompressedArchives.TryGetValue(nestedAchiveLocation, out nestedCompressedArchiveAccess))
                        {
                            var dontCare = string.Empty;
                            nestedCompressedArchiveAccess = GetNestedCompressedArchive(compressedArchiveAccess, nestedAchiveLocation, ref dontCare);
                            nestedCompressedArchives[nestedAchiveLocation] = nestedCompressedArchiveAccess;
                        }
                    }
                    else
                    {
                        nestedCompressedArchiveAccess = compressedArchiveAccess;
                    }

                    var childEntries = ListEntriesInCompressedArchive(nestedCompressedArchiveAccess, nestedArchiveRelativeLocation, includeContainers: true).Select(e => e.MakeAbsoluteEntry(nestedAchiveLocation));
                    entries.AddRange(childEntries);
                    var childContainers = childEntries.Where(e => e.IsDirectory || e.Name.IsContainer());
                    foreach (var childContainer in childContainers)
                    {
                        containers.Enqueue(childContainer);
                    }
                }
                if (!includeContainers)
                {
                    entries = entries.Where(e => !e.IsDirectory && !e.Name.IsContainer()).ToList();
                }
                foreach (var nestedCompresedArchive in nestedCompressedArchives.Values)
                {
                    nestedCompresedArchive.Dispose();
                }
            }
            return(entries.OrderBy(e => e.Name));
        }
示例#6
0
        private static NestedCompressedArchiveAccess GetNestedCompressedArchive(ICompressedArchiveAccess compressedArchiveAccess, string locationInArchive, ref string nestedArchiveLocation)
        {
            var nestedArchiveLocations = locationInArchive.GetNestedContainerLocations();
            var entryName = nestedArchiveLocations.First();

            if (string.IsNullOrEmpty(nestedArchiveLocation))
            {
                nestedArchiveLocation = entryName + '/';
            }
            else
            {
                nestedArchiveLocation += entryName + '/';
            }
            var nestedArchiveEntry = compressedArchiveAccess.Entries.FirstOrDefault(e => e.Name.NormalizePathSeparators() == entryName);

            NestedCompressedArchiveAccess nestedCompressedArchive = null;

            if (nestedArchiveEntry != null)
            {
                nestedCompressedArchive = NestedCompressedArchiveAccess.Create(compressedArchiveAccess, nestedArchiveEntry);
                if (nestedCompressedArchive != null)
                {
                    // If there is more than one nested archive format on the location, e.g. it's a .tar.gz, or if we did not consume the entire location
                    // in the archive, then we need to recurse into this newly located archive to get to the ultimate nested archive.
                    if ((nestedCompressedArchive.NestedArchiveFormats.Count() > 1) || (nestedArchiveLocations.Length > 1))
                    {
                        locationInArchive = string.Join("/", nestedArchiveLocations, 1, nestedArchiveLocations.Length - 1) + '/';
                        if (locationInArchive.IsInNestedContainer())
                        {
                            nestedCompressedArchive = GetNestedCompressedArchive(nestedCompressedArchive, locationInArchive, ref nestedArchiveLocation);
                        }
                    }
                }
            }

            return(nestedCompressedArchive);
        }
示例#7
0
 /// <summary>
 /// List the contents of the given compressed archive.
 /// </summary>
 /// <param name="compressedArchiveAccess">An instance of <see cref="ICompressedArchiveAccess"/> whose contents are to be listed.</param>
 /// <param name="locationInArchive">A location relative to the root of the archive. The special values <c>null</c>, <c>string.Empty</c>, '\', '/', or '.' indicate the root. Otherwise, must end with a directory separator character.</param>
 /// <param name="includeContainers">If <c>true</c>, include entries that may contain other entries, such as other compressed archives and directories.</param>
 /// <returns>The list of entries, which may include entries that could contain more items, depending on the value of <paramref name="includeContainers"/>.</returns>
 /// <remarks>NOTE: Entry names are always relative to <paramref name="compressedArchiveAccess"/>. Path separators will be normalized to forward slash.</remarks>
 /// <exception cref="ArgumentNullException">Thrown if <paramref name="compressedArchiveAccess"/> is <c>null</c>.</exception>
 /// <exception cref="ArgumentException">Thrown if <paramref name="locationInArchive"/> is malformed i.e. is not null or empty, or does not end with a directory separator character.</exception>
 /// <exception cref="FileNotFoundException">Thrown if <paramref name="locationInArchive"/> identifies a nested archive that cannot be located.</exception>
 public static IEnumerable <ICompressedArchiveEntry> ListEntries(this ICompressedArchiveAccess compressedArchiveAccess, string locationInArchive, bool includeContainers)
 {
     return(compressedArchiveAccess.ListEntries(locationInArchive, includeContainers, recurse: false));
 }
示例#8
0
 private NestedCompressedArchiveAccess(ICompressedArchiveAccess parentArchiveAccess, ICompressedArchiveAccess nestedArchiveAccess, TemporaryDirectory temporaryLocation)
 {
     ParentArchiveAccess = parentArchiveAccess;
     NestedAchiveAccess  = nestedArchiveAccess;
     TemporaryLocation   = temporaryLocation;
 }
示例#9
0
        private static IEnumerable <ICompressedArchiveEntry> ListEntriesInCompressedArchive(ICompressedArchiveAccess compressedArchiveAccess, string locationInArchive, bool includeContainers)
        {
            if (compressedArchiveAccess == null)
            {
                throw new ArgumentNullException("compressedArchiveAccess");
            }
            if (!string.IsNullOrEmpty(locationInArchive) && (locationInArchive != "."))
            {
                if ((locationInArchive.Last() != '\\') && (locationInArchive.Last() != '/'))
                {
                    throw new ArgumentException("locationInArchive");
                }
            }

            // If location is within a nested archive, do what is necessary to get access to the nested archive from the supplied archive.
            var originalArchiveAccess = compressedArchiveAccess;

            locationInArchive = locationInArchive.NormalizePathSeparators();
            var normalizedLocationInArchive = locationInArchive;
            var locationIsInNestedContainer = locationInArchive.IsInNestedContainer();

            if (locationIsInNestedContainer)
            {
                normalizedLocationInArchive = string.Empty;
                compressedArchiveAccess     = GetNestedCompressedArchive(compressedArchiveAccess, locationInArchive, ref normalizedLocationInArchive);
                if (compressedArchiveAccess == null)
                {
                    var message = string.Format(CultureInfo.CurrentCulture, Resources.Strings.CompressedArchiveAccess_NestedArchiveNotFound, locationInArchive);
                    throw new FileNotFoundException(message, locationInArchive);
                }
            }

            var entries = compressedArchiveAccess.Entries.Select(e => new NormalizedCompressedArchiveEntry(e, normalizedLocationInArchive, locationIsInNestedContainer));

            if (!includeContainers)
            {
                entries = entries.Where(e => !e.IsDirectory && !e.Name.IsContainer());
            }

            if (locationInArchive.IsRootLocation())
            {
                var virtualEntriesToAdd = new List <NormalizedCompressedArchiveEntry>();
                if (includeContainers)
                {
                    // When listing, there are cases in which there may no direct entry for a directory. Add one.
                    var entryNames       = entries.Select(e => e.Name).ToList();
                    var nestedEntryNames = entryNames.Select(n => n.GetArchiveRelativePathSegments()).Where(s => s.Length > 1).Select(s => s.First() + '/').Distinct().ToList();
                    nestedEntryNames.RemoveAll(n => entryNames.Contains(n));
                    virtualEntriesToAdd.AddRange(nestedEntryNames.Select(n => new NormalizedCompressedArchiveEntry(n, locationInArchive, locationIsInNestedContainer)));
                }
                entries = entries.Where(e => e.Name.GetArchiveRelativePathSegments().Length < 2);
                entries = entries.Concat(virtualEntriesToAdd);
            }
            else
            {
                entries = entries.Where(e => e.Name.StartsWith(locationInArchive, PathComparer.DefaultPolicy) && (e.Name.Length > locationInArchive.Length));
                entries = entries.Where(e => e.Name.Substring(locationInArchive.Length + 1).GetArchiveRelativePathSegments().Length < 2);
            }

            if (!object.ReferenceEquals(compressedArchiveAccess, originalArchiveAccess))
            {
                compressedArchiveAccess.Dispose();
            }

            return(entries);
        }
 /// <summary>
 /// Initializes a new instance of <see cref="CompressedArchiveFileAccess"/>.
 /// </summary>
 /// <param name="filePath">The absolute path to the compressed archive file.</param>
 /// <param name="compressedArchiveAccess">The compressed archive to wrap.</param>
 private CompressedArchiveFileAccess(string filePath, ICompressedArchiveAccess compressedArchiveAccess)
 {
     RootLocation            = filePath;
     CompressedArchiveAccess = compressedArchiveAccess;
 }
示例#11
0
            public static TestCompressedArchiveAccess GetFromCompressedArchiveFileAccess(ICompressedArchiveAccess archive)
            {
                var fileAccessArchiveType = typeof(INTV.Shared.Utility.CompressedArchiveAccess).Assembly.GetType("INTV.Shared.Utility.CompressedArchiveAccess+CompressedArchiveFileAccess");
                var instanceFlags         = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
                var property    = fileAccessArchiveType.GetProperty("CompressedArchiveAccess", instanceFlags);
                var testArchive = property.GetValue(archive) as TestCompressedArchiveAccess;

                return(testArchive);
            }