/// <summary> /// Writes the remaining contents of the archive to the directory at the specified <paramref name="targetPath" />. /// </summary> /// <param name="targetPath">The path to the directory to which to write the remaining contents of the archive.</param> /// <param name="skipTopLevelDirectory">if set to <c>true</c>, the top level directory in the archive is skipped.</param> /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="targetPath" /> is null.</exception> /// <exception cref="TarException"> /// Thrown when an entry attempts to write outside of the target path by using parent path /// (../) operators. /// </exception> /// <exception cref="NotSupportedException">Thrown when an entry contains unsupported flags.</exception> public void WriteToDirectory(string targetPath, bool skipTopLevelDirectory) { if (targetPath == null) { throw new ArgumentNullException(nameof(targetPath)); } AssertNotDisposed(); // Collapse target directory. targetPath = PathHelper.Normalize(targetPath); targetPath = PathHelper.AppendDirectorySeparator(targetPath); // Read all files in the archive. TarEntryStream stream; while ((stream = Next()) != null) { // Convert all directory separators to the local machine's prefered form. var fileName = PathHelper.PartialNormalize(stream.Header.Name); // Trim leading directory separators. fileName = PathHelper.TrimLeadingDirectorySeparators(fileName); // Skip the top level directory. if (skipTopLevelDirectory) { fileName = PathHelper.RemoveTopLevelFolderFromRelativePath(fileName); // Skip if it was the top level. if (fileName == null) { continue; } } var filePath = PathHelper.Normalize(Path.Combine(targetPath, fileName)); if (!filePath.StartsWith(targetPath)) { throw new TarException($"{Path.Combine(targetPath, fileName)} is outside of the specified target directory."); } // If it's a directory entry. if (fileName.EndsWith(Path.DirectorySeparatorChar.ToString()) || stream.Header.Flag == TarHeaderFlag.Directory) { Directory.CreateDirectory(filePath); // TODO: FIXME None of the below set the "last modified" date on windows. Directory.SetCreationTimeUtc(filePath, stream.Header.LastModification.Value); Directory.SetLastWriteTimeUtc(filePath, stream.Header.LastModification.Value); Directory.SetLastAccessTimeUtc(filePath, stream.Header.LastModification.Value); } // If it's a file entry. else if (stream.Header.Flag == TarHeaderFlag.NormalFile || stream.Header.Flag == TarHeaderFlag.NormalFileAlt) { using (var fileStream = File.Open(filePath, FileMode.Create, FileAccess.Write)) { stream.CopyTo(fileStream); } File.SetCreationTimeUtc(filePath, stream.Header.LastModification.Value); File.SetLastWriteTimeUtc(filePath, stream.Header.LastModification.Value); } else { throw new NotSupportedException( $"File entries of type '{stream.Header.Flag}' are not supported by WriteToDirectory at this time."); } } }