/// <summary> /// Writes a directory and its contents to the archive. This method is recursive. /// </summary> /// <param name="path">The path to the directory.</param> /// <param name="basePath">The base path which to remove from the written path names.</param> /// <param name="skipDirectory">if set to <c>true</c> skip the directory itself.</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="path" /> or <paramref name="basePath" /> is null.</exception> /// <exception cref="DirectoryNotFoundException">The specified directory could not be found.</exception> private void WriteDirectoryInternal(string path, string basePath, bool skipDirectory) { if (path == null) { throw new ArgumentNullException(nameof(path)); } if (basePath == null) { throw new ArgumentNullException(nameof(basePath)); } AssertNotDisposed(); // Make sure the directory exists. path = PathHelper.Normalize(path); if (!Directory.Exists(path)) { throw new DirectoryNotFoundException("The specified directory could not be found."); } // Write the directory to the archive. if (!skipDirectory) { var dirInfo = new DirectoryInfo(path); WriteDirectoryEntry(path.Substring(basePath.Length), dirInfo.LastWriteTimeUtc); } // Write the files in the directory to the archive. foreach (var filePath in Directory.GetFiles(path)) { var fileInfo = new FileInfo(filePath); WriteFileEntry(filePath.Substring(basePath.Length), fileInfo.Length, fileInfo.LastWriteTimeUtc, null, null, new FileModeGroup("777")); WriteStream(fileInfo.OpenRead()); } // Write directories in the directory to the archive. foreach (var directoryPath in Directory.GetDirectories(path)) { WriteDirectoryInternal(directoryPath, basePath, false); } }
/// <summary> /// Writes the directory with the specified <paramref name="path" /> and its contents to the archive. /// </summary> /// <param name="path">The path.</param> /// <param name="skipTopLevelDirectory"> /// If set to <c>true</c>, only the contents of the directory with the specified /// <paramref name="path" /> are written to the archive. /// </param> /// <exception cref="System.ArgumentNullException">Thrown if <paramref name="path" /> is null.</exception> /// <exception cref="System.IO.DirectoryNotFoundException">The specified directory could not be found.</exception> public void WriteDirectory(string path, bool skipTopLevelDirectory) { if (path == null) { throw new ArgumentNullException(nameof(path)); } AssertNotDisposed(); // Make sure the directory exists. path = PathHelper.Normalize(path); if (!Directory.Exists(path)) { throw new DirectoryNotFoundException("The specified directory could not be found."); } // Compute the base path. var basePath = skipTopLevelDirectory ? path : Directory.GetParent(path).FullName; basePath = PathHelper.AppendDirectorySeparator(basePath); WriteDirectoryInternal(path, basePath, skipTopLevelDirectory); }
/// <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."); } } }