diff options
author | yorah <yoram.harmelin@gmail.com> | 2013-01-08 20:42:39 +0400 |
---|---|---|
committer | yorah <yoram.harmelin@gmail.com> | 2013-12-13 17:26:40 +0400 |
commit | f2d5b19c35d703738d406d48e6c63b679246513d (patch) | |
tree | 3c40ff46fd2951ff336e5c87f418ee414d0cc5c9 | |
parent | 5701f42e9aba2b40e09882c35de9ede09dbfd3d5 (diff) |
Trim down tar-cs to keep only the tarring capabilities needed for git-archive
The resulting `TarWriter` file is a low-level abstraction that can be used to write tar files.
Included:
- only keep a unique `TarWriter` file
- merging `UsTarHeader` and `TarHeader`, and adding them as private classes of `TarWriter`
- removal of unneeded `Write` overloads
Some changes to the tar format has been included to better comply with the one described here: http://en.wikipedia.org/wiki/Tar_%28computing%29#Format_details
-rw-r--r-- | LibGit2Sharp/Core/DataWriter.cs | 63 | ||||
-rw-r--r-- | LibGit2Sharp/Core/IArchiveDataWriter.cs | 29 | ||||
-rw-r--r-- | LibGit2Sharp/Core/ITarHeader.cs | 32 | ||||
-rw-r--r-- | LibGit2Sharp/Core/LegacyTarWriter.cs | 163 | ||||
-rw-r--r-- | LibGit2Sharp/Core/TarException.cs | 26 | ||||
-rw-r--r-- | LibGit2Sharp/Core/TarHeader.cs | 223 | ||||
-rw-r--r-- | LibGit2Sharp/Core/TarReader.cs | 210 | ||||
-rw-r--r-- | LibGit2Sharp/Core/TarWriter.cs | 300 | ||||
-rw-r--r-- | LibGit2Sharp/Core/UsTarHeader.cs | 142 | ||||
-rw-r--r-- | LibGit2Sharp/LibGit2Sharp.csproj | 8 |
10 files changed, 261 insertions, 935 deletions
diff --git a/LibGit2Sharp/Core/DataWriter.cs b/LibGit2Sharp/Core/DataWriter.cs deleted file mode 100644 index e972b66d..00000000 --- a/LibGit2Sharp/Core/DataWriter.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* - * BSD License - * - * Copyright (c) 2009, Vladimir Vasiltsov - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * * Names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System.IO; - -namespace tar_cs -{ - internal class DataWriter : IArchiveDataWriter - { - private readonly long size; - private long remainingBytes; - private bool canWrite = true; - private readonly Stream stream; - - public DataWriter(Stream data, long dataSizeInBytes) - { - size = dataSizeInBytes; - remainingBytes = size; - stream = data; - } - - public int Write(byte[] buffer, int count) - { - if(remainingBytes == 0) - { - canWrite = false; - return -1; - } - int bytesToWrite; - if(remainingBytes - count < 0) - { - bytesToWrite = (int)remainingBytes; - } - else - { - bytesToWrite = count; - } - stream.Write(buffer,0,bytesToWrite); - remainingBytes -= bytesToWrite; - return bytesToWrite; - } - - public bool CanWrite - { - get - { - return canWrite; - } - } - } -}
\ No newline at end of file diff --git a/LibGit2Sharp/Core/IArchiveDataWriter.cs b/LibGit2Sharp/Core/IArchiveDataWriter.cs deleted file mode 100644 index ed52d01c..00000000 --- a/LibGit2Sharp/Core/IArchiveDataWriter.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * BSD License - * - * Copyright (c) 2009, Vladimir Vasiltsov - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * * Names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -namespace tar_cs -{ - public interface IArchiveDataWriter - { - /// <summary> - /// Write `length` bytes of data from `buffer` to corresponding archive. - /// </summary> - /// <param name="buffer">data storage</param> - /// <param name="count">how many bytes to be written to the corresponding archive</param> - int Write(byte[] buffer, int count); - bool CanWrite { get; } - } - public delegate void WriteDataDelegate(IArchiveDataWriter writer); -} diff --git a/LibGit2Sharp/Core/ITarHeader.cs b/LibGit2Sharp/Core/ITarHeader.cs deleted file mode 100644 index bdcc1341..00000000 --- a/LibGit2Sharp/Core/ITarHeader.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * BSD License - * - * Copyright (c) 2009, Vladimir Vasiltsov - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * * Names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; - -namespace tar_cs -{ - public interface ITarHeader - { - string FileName { get; set; } - int Mode { get; set; } - int UserId { get; set; } - string UserName { get; set; } - int GroupId { get; set; } - string GroupName { get; set; } - long SizeInBytes { get; set; } - DateTime LastModification { get; set; } - int HeaderSize { get; } - } -}
\ No newline at end of file diff --git a/LibGit2Sharp/Core/LegacyTarWriter.cs b/LibGit2Sharp/Core/LegacyTarWriter.cs deleted file mode 100644 index e03a42d3..00000000 --- a/LibGit2Sharp/Core/LegacyTarWriter.cs +++ /dev/null @@ -1,163 +0,0 @@ -/* - * BSD License - * - * Copyright (c) 2009, Vladimir Vasiltsov - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * * Names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.IO; -using System.Threading; -using tar_cs; - -namespace tar_cs -{ - public class LegacyTarWriter : IDisposable - { - private readonly Stream outStream; - protected byte[] buffer = new byte[1024]; - private bool isClosed; - public bool ReadOnZero = true; - - /// <summary> - /// Writes tar (see GNU tar) archive to a stream - /// </summary> - /// <param name="writeStream">stream to write archive to</param> - public LegacyTarWriter(Stream writeStream) - { - outStream = writeStream; - } - - protected virtual Stream OutStream - { - get { return outStream; } - } - - #region IDisposable Members - - public void Dispose() - { - Close(); - } - - #endregion - - public void Write(string fileName) - { - using (FileStream file = File.OpenRead(fileName)) - { - Write(file, file.Length, fileName, 61, 61, 511, File.GetLastWriteTime(file.Name)); - } - } - - public void Write(FileStream file) - { - string path = Path.GetFullPath(file.Name).Replace(Path.GetPathRoot(file.Name),string.Empty); - path = path.Replace(Path.DirectorySeparatorChar, '/'); - Write(file, file.Length, path, 61, 61, 511, File.GetLastWriteTime(file.Name)); - } - - public void Write(Stream data, long dataSizeInBytes, string name) - { - Write(data, dataSizeInBytes, name, 61, 61, 511, DateTime.Now); - } - - public virtual void Write(string name, long dataSizeInBytes, int userId, int groupId, int mode, DateTime lastModificationTime, WriteDataDelegate writeDelegate) - { - IArchiveDataWriter writer = new DataWriter(OutStream, dataSizeInBytes); - WriteHeader(name, lastModificationTime, dataSizeInBytes, userId, groupId, mode); - while(writer.CanWrite) - { - writeDelegate(writer); - } - AlignTo512(dataSizeInBytes, false); - } - - public virtual void Write(Stream data, long dataSizeInBytes, string name, int userId, int groupId, int mode, - DateTime lastModificationTime) - { - if(isClosed) - throw new TarException("Can not write to the closed writer"); - WriteHeader(name, lastModificationTime, dataSizeInBytes, userId, groupId, mode); - WriteContent(dataSizeInBytes, data); - AlignTo512(dataSizeInBytes,false); - } - - protected void WriteContent(long count, Stream data) - { - while (count > 0 && count > buffer.Length) - { - int bytesRead = data.Read(buffer, 0, buffer.Length); - if (bytesRead < 0) - throw new IOException("LegacyTarWriter unable to read from provided stream"); - if (bytesRead == 0) - { - if (ReadOnZero) - Thread.Sleep(100); - else - break; - } - OutStream.Write(buffer, 0, bytesRead); - count -= bytesRead; - } - if (count > 0) - { - int bytesRead = data.Read(buffer, 0, (int) count); - if (bytesRead < 0) - throw new IOException("LegacyTarWriter unable to read from provided stream"); - if (bytesRead == 0) - { - while (count > 0) - { - OutStream.WriteByte(0); - --count; - } - } - else - OutStream.Write(buffer, 0, bytesRead); - } - } - - protected virtual void WriteHeader(string name, DateTime lastModificationTime, long count, int userId, int groupId, int mode) - { - var header = new TarHeader - { - FileName = name, - LastModification = lastModificationTime, - SizeInBytes = count, - UserId = userId, - GroupId = groupId, - Mode = mode - }; - OutStream.Write(header.GetHeaderValue(), 0, header.HeaderSize); - } - - - public void AlignTo512(long size,bool acceptZero) - { - size = size%512; - if (size == 0 && !acceptZero) return; - while (size < 512) - { - OutStream.WriteByte(0); - size++; - } - } - - public virtual void Close() - { - if (isClosed) return; - AlignTo512(0,true); - AlignTo512(0,true); - isClosed = true; - } - } -}
\ No newline at end of file diff --git a/LibGit2Sharp/Core/TarException.cs b/LibGit2Sharp/Core/TarException.cs deleted file mode 100644 index ea1d484b..00000000 --- a/LibGit2Sharp/Core/TarException.cs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * BSD License - * - * Copyright (c) 2009, Vladimir Vasiltsov - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * * Names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; - -namespace tar_cs -{ - public class TarException : Exception - { - public TarException(string message) : base(message) - { - } - } -}
\ No newline at end of file diff --git a/LibGit2Sharp/Core/TarHeader.cs b/LibGit2Sharp/Core/TarHeader.cs deleted file mode 100644 index 0a4c285a..00000000 --- a/LibGit2Sharp/Core/TarHeader.cs +++ /dev/null @@ -1,223 +0,0 @@ -/* - * BSD License - * - * Copyright (c) 2009, Vladimir Vasiltsov - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * * Names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Diagnostics; -using System.Net; -using System.Text; -using tar_cs; - -namespace tar_cs -{ - internal class TarHeader : ITarHeader - { - private readonly byte[] buffer = new byte[512]; - private long headerChecksum; - - public TarHeader() - { - // Default values - Mode = 511; // 0777 dec - UserId = 61; // 101 dec - GroupId = 61; // 101 dec - } - - private string fileName; - protected readonly DateTime TheEpoch = new DateTime(1970, 1, 1, 0, 0, 0); - - public virtual string FileName - { - get - { - return fileName.Replace("\0",string.Empty); - } - set - { - if(value.Length > 100) - { - throw new TarException("A file name can not be more than 100 chars long"); - } - fileName = value; - } - } - public int Mode { get; set; } - - public string ModeString - { - get { return AddChars(Convert.ToString(Mode, 8), 7, '0', true); } - } - - public int UserId { get; set; } - public virtual string UserName - { - get { return UserId.ToString(); } - set { UserId = Int32.Parse(value); } - } - - public string UserIdString - { - get { return AddChars(Convert.ToString(UserId, 8), 7, '0', true); } - } - - public int GroupId { get; set; } - public virtual string GroupName - { - get { return GroupId.ToString(); } - set { GroupId = Int32.Parse(value); } - } - - public string GroupIdString - { - get { return AddChars(Convert.ToString(GroupId, 8), 7, '0', true); } - } - - public long SizeInBytes { get; set; } - - public string SizeString - { - get { return AddChars(Convert.ToString(SizeInBytes, 8), 11, '0', true); } - } - - public DateTime LastModification { get; set; } - - public string LastModificationString - { - get - { - return AddChars( - ((long) (LastModification - TheEpoch).TotalSeconds).ToString(), 11, '0', - true); - } - } - - public string HeaderChecksumString - { - get { return AddChars(Convert.ToString(headerChecksum, 8), 6, '0', true); } - } - - - public virtual int HeaderSize - { - get { return 512; } - } - - private static string AddChars(string str, int num, char ch, bool isLeading) - { - int neededZeroes = num - str.Length; - while (neededZeroes > 0) - { - if (isLeading) - str = ch + str; - else - str = str + ch; - --neededZeroes; - } - return str; - } - - public byte[] GetBytes() - { - return buffer; - } - - public virtual bool UpdateHeaderFromBytes() - { - FileName = Encoding.ASCII.GetString(buffer, 0, 100); - Mode = Convert.ToInt32(Encoding.ASCII.GetString(buffer, 100, 7), 8); - UserId = Convert.ToInt32(Encoding.ASCII.GetString(buffer, 108, 7), 8); - GroupId = Convert.ToInt32(Encoding.ASCII.GetString(buffer, 116, 7), 8); - if((buffer[124] & 0x80) == 0x80) // if size in binary - { - long sizeBigEndian = BitConverter.ToInt64(buffer,0x80); - SizeInBytes = IPAddress.NetworkToHostOrder(sizeBigEndian); - } - else - { - SizeInBytes = Convert.ToInt64(Encoding.ASCII.GetString(buffer, 124, 11), 8); - } - long unixTimeStamp = Convert.ToInt64(Encoding.ASCII.GetString(buffer,136,11)); - LastModification = TheEpoch.AddSeconds(unixTimeStamp); - - var storedChecksum = Convert.ToInt32(Encoding.ASCII.GetString(buffer,148,6)); - RecalculateChecksum(buffer); - if (storedChecksum == headerChecksum) - { - return true; - } - - RecalculateAltChecksum(buffer); - return storedChecksum == headerChecksum; - } - - private void RecalculateAltChecksum(byte[] buf) - { - Encoding.ASCII.GetBytes(" ").CopyTo(buf, 148); - headerChecksum = 0; - foreach(byte b in buf) - { - if((b & 0x80) == 0x80) - { - headerChecksum -= b ^ 0x80; - } - else - { - headerChecksum += b; - } - } - } - - public virtual byte[] GetHeaderValue() - { - // Clean old values - int i = 0; - while (i < 512) - { - buffer[i] = 0; - ++i; - } - - if (string.IsNullOrEmpty(FileName)) throw new TarException("FileName can not be empty."); - if (FileName.Length >= 100) throw new TarException("FileName is too long. It must be less than 100 bytes."); - - // Fill header - Encoding.ASCII.GetBytes(AddChars(FileName, 100, '\0', false)).CopyTo(buffer, 0); - Encoding.ASCII.GetBytes(ModeString).CopyTo(buffer, 100); - Encoding.ASCII.GetBytes(UserIdString).CopyTo(buffer, 108); - Encoding.ASCII.GetBytes(GroupIdString).CopyTo(buffer, 116); - Encoding.ASCII.GetBytes(SizeString).CopyTo(buffer, 124); - Encoding.ASCII.GetBytes(LastModificationString).CopyTo(buffer, 136); - - RecalculateChecksum(buffer); - - // Write checksum - Encoding.ASCII.GetBytes(HeaderChecksumString).CopyTo(buffer, 148); - - return buffer; - } - - protected virtual void RecalculateChecksum(byte[] buf) - { -// Set default value for checksum. That is 8 spaces. - Encoding.ASCII.GetBytes(" ").CopyTo(buf, 148); - - // Calculate checksum - headerChecksum = 0; - foreach (byte b in buf) - { - headerChecksum += b; - } - } - } -}
\ No newline at end of file diff --git a/LibGit2Sharp/Core/TarReader.cs b/LibGit2Sharp/Core/TarReader.cs deleted file mode 100644 index 3709bafa..00000000 --- a/LibGit2Sharp/Core/TarReader.cs +++ /dev/null @@ -1,210 +0,0 @@ -/* - * BSD License - * - * Copyright (c) 2009, Vladimir Vasiltsov - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * * Names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; - -namespace tar_cs -{ - /// <summary> - /// Extract contents of a tar file represented by a stream for the TarReader constructor - /// </summary> - public class TarReader - { - private readonly byte[] dataBuffer = new byte[512]; - private readonly UsTarHeader header; - private readonly Stream inStream; - private long remainingBytesInFile; - - /// <summary> - /// Constructs TarReader object to read data from `tarredData` stream - /// </summary> - /// <param name="tarredData">A stream to read tar archive from</param> - public TarReader(Stream tarredData) - { - inStream = tarredData; - header = new UsTarHeader(); - } - - public ITarHeader FileInfo - { - get { return header; } - } - - /// <summary> - /// Read all files from an archive to a directory. It creates some child directories to - /// reproduce a file structure from the archive. - /// </summary> - /// <param name="destDirectory">The out directory.</param> - /// - /// CAUTION! This method is not safe. It's not tar-bomb proof. - /// {see http://en.wikipedia.org/wiki/Tar_(file_format) } - /// If you are not sure about the source of an archive you extracting, - /// then use MoveNext and Read and handle paths like ".." and "../.." according - /// to your business logic. - public void ReadToEnd(string destDirectory) - { - while (MoveNext(false)) - { - string totalPath = destDirectory + Path.DirectorySeparatorChar + FileInfo.FileName; - string fileName = Path.GetFileName(totalPath); - string directory = totalPath.Remove(totalPath.Length - fileName.Length); - Directory.CreateDirectory(directory); - using (FileStream file = File.Create(totalPath)) - { - Read(file); - } - } - } - - /// <summary> - /// Read data from a current file to a Stream. - /// </summary> - /// <param name="dataDestanation">A stream to read data to</param> - /// - /// <seealso cref="MoveNext"/> - public void Read(Stream dataDestanation) - { - Debug.WriteLine("tar stream position Read in: " + inStream.Position); - int readBytes; - byte[] read; - while ((readBytes = Read(out read)) != -1) - { - Debug.WriteLine("tar stream position Read while(...) : " + inStream.Position); - dataDestanation.Write(read, 0, readBytes); - } - Debug.WriteLine("tar stream position Read out: " + inStream.Position); - } - - protected int Read(out byte[] buffer) - { - if(remainingBytesInFile == 0) - { - buffer = null; - return -1; - } - int align512 = -1; - long toRead = remainingBytesInFile - 512; - - if (toRead > 0) - toRead = 512; - else - { - align512 = 512 - (int)remainingBytesInFile; - toRead = remainingBytesInFile; - } - - - int bytesRead = inStream.Read(dataBuffer, 0, (int)toRead); - remainingBytesInFile -= bytesRead; - - if(inStream.CanSeek && align512 > 0) - { - inStream.Seek(align512, SeekOrigin.Current); - } - else - while(align512 > 0) - { - inStream.ReadByte(); - --align512; - } - - buffer = dataBuffer; - return bytesRead; - } - - /// <summary> - /// Check if all bytes in buffer are zeroes - /// </summary> - /// <param name="buffer">buffer to check</param> - /// <returns>true if all bytes are zeroes, otherwise false</returns> - private static bool IsEmpty(IEnumerable<byte> buffer) - { - foreach(byte b in buffer) - { - if (b != 0) return false; - } - return true; - } - - /// <summary> - /// Move internal pointer to a next file in archive. - /// </summary> - /// <param name="skipData">Should be true if you want to read a header only, otherwise false</param> - /// <returns>false on End Of File otherwise true</returns> - /// - /// Example: - /// while(MoveNext()) - /// { - /// Read(dataDestStream); - /// } - /// <seealso cref="Read(Stream)"/> - public bool MoveNext(bool skipData) - { - Debug.WriteLine("tar stream position MoveNext in: " + inStream.Position); - if (remainingBytesInFile > 0) - { - if (!skipData) - { - throw new TarException( - "You are trying to change file while not all the data from the previous one was read. If you do want to skip files use skipData parameter set to true."); - } - // Skip to the end of file. - if (inStream.CanSeek) - { - long remainer = (remainingBytesInFile%512); - inStream.Seek(remainingBytesInFile + (512 - (remainer == 0 ? 512 : remainer) ), SeekOrigin.Current); - } - else - { - byte[] buffer; - while (Read(out buffer) != -1) - { - } - } - } - - byte[] bytes = header.GetBytes(); - - int headerRead = inStream.Read(bytes, 0, header.HeaderSize); - if (headerRead < 512) - { - throw new TarException("Can not read header"); - } - - if(IsEmpty(bytes)) - { - headerRead = inStream.Read(bytes, 0, header.HeaderSize); - if(headerRead == 512 && IsEmpty(bytes)) - { - Debug.WriteLine("tar stream position MoveNext out(false): " + inStream.Position); - return false; - } - throw new TarException("Broken archive"); - } - - if (header.UpdateHeaderFromBytes()) - { - throw new TarException("Checksum check failed"); - } - - remainingBytesInFile = header.SizeInBytes; - - Debug.WriteLine("tar stream position MoveNext out(true): " + inStream.Position); - return true; - } - } -}
\ No newline at end of file diff --git a/LibGit2Sharp/Core/TarWriter.cs b/LibGit2Sharp/Core/TarWriter.cs index 94a6ae70..fca58b87 100644 --- a/LibGit2Sharp/Core/TarWriter.cs +++ b/LibGit2Sharp/Core/TarWriter.cs @@ -17,67 +17,289 @@ using System; using System.IO; +using System.Linq; +using System.Net; +using System.Text; -namespace tar_cs +namespace LibGit2Sharp.Core { - public class TarWriter : LegacyTarWriter + internal class TarWriter : IDisposable { + private readonly Stream outStream; - public TarWriter(Stream writeStream) : base(writeStream) + /// <summary> + /// Writes tar (see GNU tar) archive to a stream + /// </summary> + /// <param name="writeStream">stream to write archive to</param> + public TarWriter(Stream writeStream) { + outStream = writeStream; } - protected override void WriteHeader(string name, DateTime lastModificationTime, long count, int userId, int groupId, int mode) + protected Stream OutStream { - var tarHeader = new UsTarHeader() - { - FileName = name, - LastModification = lastModificationTime, - SizeInBytes = count, - UserId = userId, - UserName = Convert.ToString(userId,8), - GroupId = groupId, - GroupName = Convert.ToString(groupId,8), - Mode = mode - }; - OutStream.Write(tarHeader.GetHeaderValue(), 0, tarHeader.HeaderSize); + get { return outStream; } } - protected virtual void WriteHeader(string name, DateTime lastModificationTime, long count, string userName, string groupName, int mode) + #region IDisposable Members + + public void Dispose() + { + AlignTo512(0, true); + AlignTo512(0, true); + + GC.SuppressFinalize(this); + } + + #endregion + + public void Write( + string fileName, + Stream data, + DateTimeOffset modificationTime, + int mode, + string userId, + string groupId, + char typeflag, + string userName, + string groupName, + string deviceMajorNumber, + string deviceMinorNumber) { - var tarHeader = new UsTarHeader() + WriteHeader(fileName, modificationTime, (data != null)?data.Length:0, mode, userId, groupId, typeflag, userName, groupName, deviceMajorNumber, deviceMinorNumber); + // folders have no data + if (data != null) { - FileName = name, - LastModification = lastModificationTime, - SizeInBytes = count, - UserId = userName.GetHashCode(), - UserName = userName, - GroupId = groupName.GetHashCode(), - GroupName = groupName, - Mode = mode - }; - OutStream.Write(tarHeader.GetHeaderValue(), 0, tarHeader.HeaderSize); + WriteContent(data.Length, data); + } + AlignTo512(data.Length, false); } + protected void WriteContent(long count, Stream data) + { + var buffer = new byte[1024]; + + while (count > 0 && count > buffer.Length) + { + int bytesRead = data.Read(buffer, 0, buffer.Length); + if (bytesRead < 0) + throw new IOException("TarWriter unable to read from provided stream"); - public virtual void Write(string name, long dataSizeInBytes, string userName, string groupName, int mode, DateTime lastModificationTime, WriteDataDelegate writeDelegate) + OutStream.Write(buffer, 0, bytesRead); + count -= bytesRead; + } + if (count > 0) + { + int bytesRead = data.Read(buffer, 0, (int)count); + if (bytesRead < 0) + throw new IOException("TarWriter unable to read from provided stream"); + if (bytesRead == 0) + { + while (count > 0) + { + OutStream.WriteByte(0); + --count; + } + } + else + OutStream.Write(buffer, 0, bytesRead); + } + } + + protected void AlignTo512(long size, bool acceptZero) { - var writer = new DataWriter(OutStream,dataSizeInBytes); - WriteHeader(name, lastModificationTime, dataSizeInBytes, userName, groupName, mode); - while(writer.CanWrite) + size = size % 512; + if (size == 0 && !acceptZero) return; + while (size < 512) { - writeDelegate(writer); + OutStream.WriteByte(0); + size++; } - AlignTo512(dataSizeInBytes, false); } + protected void WriteHeader( + string name, + DateTimeOffset lastModificationTime, + long count, + int mode, + string userId, + string groupId, + char typeflag, + string userName, + string groupName, + string deviceMajorNumber, + string deviceMinorNumber) + { + var tarHeader = new UsTarHeader(name, lastModificationTime, count, mode, userId, groupId, typeflag, userName, groupName, deviceMajorNumber, deviceMinorNumber); + var header = tarHeader.GetHeaderValue(); + OutStream.Write(header, 0, header.Length); + } - public void Write(Stream data, long dataSizeInBytes, string fileName, string userId, string groupId, int mode, - DateTime lastModificationTime) + /// <summary> + /// UsTar header implementation. + /// </summary> + private class UsTarHeader { - WriteHeader(fileName,lastModificationTime,dataSizeInBytes,userId, groupId, mode); - WriteContent(dataSizeInBytes,data); - AlignTo512(dataSizeInBytes,false); + private readonly string mode; + private readonly long size; + private readonly string unixTime; + private const string magic = "ustar"; + private const string version = "00"; + private readonly string userName; + private readonly string groupName; + private readonly string userId; + private readonly string groupId; + private readonly char typeflag; + private readonly string deviceMajorNumber; + private readonly string deviceMinorNumber; + private string namePrefix; + private string fileName; + + + public UsTarHeader( + string filePath, + DateTimeOffset lastModificationTime, + long size, + int mode, + string userId, + string groupId, + char typeflag, + string userName, + string groupName, + string deviceMajorNumber, + string deviceMinorNumber) + { + #region Length validations + + if (userName.Length > 32) + { + throw new ArgumentException("ustar userName cannot be longer than 32 characters.", "userName"); + } + if (groupName.Length > 32) + { + throw new ArgumentException("ustar groupName cannot be longer than 32 characters.", "groupName"); + } + if (userId.Length > 7) + { + throw new ArgumentException("ustar userId cannot be longer than 7 characters.", "userId"); + } + if (groupId.Length > 7) + { + throw new ArgumentException("ustar groupId cannot be longer than 7 characters.", "groupId"); + } + if (deviceMajorNumber.Length > 7) + { + throw new ArgumentException("ustar deviceMajorNumber cannot be longer than 7 characters.", "deviceMajorNumber"); + } + if (deviceMinorNumber.Length > 7) + { + throw new ArgumentException("ustar deviceMinorNumber cannot be longer than 7 characters.", "deviceMinorNumber"); + } + + #endregion + + this.mode = Convert.ToString(mode, 8).PadLeft(7, '0'); + this.size = size; + unixTime = Convert.ToString(lastModificationTime.ToSecondsSinceEpoch(), 8).PadLeft(11, '0'); + this.userId = userId.PadLeft(7, '0'); + this.groupId = userId.PadLeft(7, '0'); + this.userName = userName; + this.groupName = groupName; + this.typeflag = typeflag; + this.deviceMajorNumber = deviceMajorNumber.PadLeft(7, '0'); + this.deviceMinorNumber = deviceMinorNumber.PadLeft(7, '0'); + + ParseFileName(filePath); + } + + private void ParseFileName(string fullFilePath) + { + var posixPath = fullFilePath.Replace('/', Path.DirectorySeparatorChar); + + if (posixPath.Length > 100) + { + if (posixPath.Length > 255) + { + throw new ArgumentException(string.Format("ustar filePath ({0}) cannot be longer than 255 chars", posixPath)); + } + int position = posixPath.Length - 100; + + // Find first path separator in the remaining 100 chars of the file name + while (!Equals('/', posixPath[position])) + { + ++position; + if (position == posixPath.Length) + { + break; + } + } + if (position == posixPath.Length) + position = posixPath.Length - 100; + namePrefix = posixPath.Substring(0, position); + fileName = posixPath.Substring(position, posixPath.Length - position); + } + else + { + namePrefix = string.Empty; + fileName = posixPath; + } + } + + public byte[] GetHeaderValue() + { + var buffer = new byte[512]; + + // Fill header + Encoding.ASCII.GetBytes(fileName.PadRight(100, '\0')).CopyTo(buffer, 0); + Encoding.ASCII.GetBytes(mode).CopyTo(buffer, 100); + Encoding.ASCII.GetBytes(userId).CopyTo(buffer, 108); + Encoding.ASCII.GetBytes(groupId).CopyTo(buffer, 116); + Encoding.ASCII.GetBytes(Convert.ToString(size, 8).PadLeft(11, '0')).CopyTo(buffer, 124); + Encoding.ASCII.GetBytes(unixTime).CopyTo(buffer, 136); + buffer[156] = Convert.ToByte(typeflag); + + Encoding.ASCII.GetBytes(magic).CopyTo(buffer, 257); // Mark header as ustar + Encoding.ASCII.GetBytes(version).CopyTo(buffer, 263); + Encoding.ASCII.GetBytes(userName).CopyTo(buffer, 265); + Encoding.ASCII.GetBytes(groupName).CopyTo(buffer, 297); + Encoding.ASCII.GetBytes(deviceMajorNumber).CopyTo(buffer, 329); + Encoding.ASCII.GetBytes(deviceMinorNumber).CopyTo(buffer, 337); + Encoding.ASCII.GetBytes(namePrefix).CopyTo(buffer, 345); + + if (size >= 0x1FFFFFFFF) + { + byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(size)); + SetMarker(AlignTo12(bytes)).CopyTo(buffer, 124); + } + + string checksum = CalculateChecksum(buffer); + Encoding.ASCII.GetBytes(checksum).CopyTo(buffer, 148); + Encoding.ASCII.GetBytes("\0").CopyTo(buffer, 155); + + return buffer; + } + + private string CalculateChecksum(byte[] buf) + { + Encoding.ASCII.GetBytes(new string(' ', 8)).CopyTo(buf, 148); + + long headerChecksum = buf.Aggregate<byte, long>(0, (current, b) => current + b); + + return Convert.ToString(headerChecksum, 8).PadLeft(7, '0'); + } + + private static byte[] SetMarker(byte[] bytes) + { + bytes[0] |= 0x80; + return bytes; + } + + private static byte[] AlignTo12(byte[] bytes) + { + var retVal = new byte[12]; + bytes.CopyTo(retVal, 12 - bytes.Length); + return retVal; + } } } } diff --git a/LibGit2Sharp/Core/UsTarHeader.cs b/LibGit2Sharp/Core/UsTarHeader.cs deleted file mode 100644 index a4f2a9a8..00000000 --- a/LibGit2Sharp/Core/UsTarHeader.cs +++ /dev/null @@ -1,142 +0,0 @@ -/* - * BSD License - * - * Copyright (c) 2009, Vladimir Vasiltsov - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * * Names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Net; -using System.Text; - -namespace tar_cs -{ - /// <summary> - /// UsTar header implementation. - /// </summary> - internal class UsTarHeader : TarHeader - { - private const string magic = "ustar"; - private const string version = " "; - private string groupName; - - private string namePrefix = string.Empty; - private string userName; - - public override string UserName - { - get { return userName.Replace("\0",string.Empty); } - set - { - if (value.Length > 32) - { - throw new TarException("user name can not be longer than 32 chars"); - } - userName = value; - } - } - - public override string GroupName - { - get { return groupName.Replace("\0",string.Empty); } - set - { - if (value.Length > 32) - { - throw new TarException("group name can not be longer than 32 chars"); - } - groupName = value; - } - } - - public override string FileName - { - get { return namePrefix.Replace("\0", string.Empty) + base.FileName.Replace("\0", string.Empty); } - set - { - if (value.Length > 100) - { - if (value.Length > 255) - { - throw new TarException("UsTar fileName can not be longer thatn 255 chars"); - } - int position = value.Length - 100; - - // Find first path separator in the remaining 100 chars of the file name - while (!IsPathSeparator(value[position])) - { - ++position; - if (position == value.Length) - { - break; - } - } - if (position == value.Length) - position = value.Length - 100; - namePrefix = value.Substring(0, position); - base.FileName = value.Substring(position, value.Length - position); - } - else - { - base.FileName = value; - } - } - } - - public override bool UpdateHeaderFromBytes() - { - byte[] bytes = GetBytes(); - UserName = Encoding.ASCII.GetString(bytes, 0x109, 32); - GroupName = Encoding.ASCII.GetString(bytes, 0x129, 32); - namePrefix = Encoding.ASCII.GetString(bytes, 347, 157); - return base.UpdateHeaderFromBytes(); - } - - private static bool IsPathSeparator(char ch) - { - return (ch == '\\' || ch == '/' || ch == '|'); // All the path separators I ever met. - } - - public override byte[] GetHeaderValue() - { - byte[] header = base.GetHeaderValue(); - - Encoding.ASCII.GetBytes(magic).CopyTo(header, 0x101); // Mark header as ustar - Encoding.ASCII.GetBytes(version).CopyTo(header, 0x106); - Encoding.ASCII.GetBytes(UserName).CopyTo(header, 0x109); - Encoding.ASCII.GetBytes(GroupName).CopyTo(header, 0x129); - Encoding.ASCII.GetBytes(namePrefix).CopyTo(header, 347); - - if (SizeInBytes >= 0x1FFFFFFFF) - { - byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(SizeInBytes)); - SetMarker(AlignTo12(bytes)).CopyTo(header, 124); - } - - RecalculateChecksum(header); - Encoding.ASCII.GetBytes(HeaderChecksumString).CopyTo(header, 148); - return header; - } - - private static byte[] SetMarker(byte[] bytes) - { - bytes[0] |= 0x80; - return bytes; - } - - private static byte[] AlignTo12(byte[] bytes) - { - var retVal = new byte[12]; - bytes.CopyTo(retVal, 12 - bytes.Length); - return retVal; - } - } -}
\ No newline at end of file diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj index f865814d..c8209359 100644 --- a/LibGit2Sharp/LibGit2Sharp.csproj +++ b/LibGit2Sharp/LibGit2Sharp.csproj @@ -153,7 +153,6 @@ <Compile Include="ContentChanges.cs" /> <Compile Include="InvalidSpecificationException.cs" /> <Compile Include="Core\Compat\EnumExtensions.cs" /> - <Compile Include="Core\DataWriter.cs" /> <Compile Include="Core\GitCheckoutOpts.cs" /> <Compile Include="Core\GitCloneOptions.cs" /> <Compile Include="Core\GitConfigEntry.cs" /> @@ -161,19 +160,12 @@ <Compile Include="Core\GitTransferProgress.cs" /> <Compile Include="Core\Handles\NullGitObjectSafeHandle.cs" /> <Compile Include="Core\Handles\NullIndexSafeHandle.cs" /> - <Compile Include="Core\IArchiveDataWriter.cs" /> <Compile Include="Core\ILazy.cs" /> - <Compile Include="Core\ITarHeader.cs" /> <Compile Include="Core\LazyGroup.cs" /> <Compile Include="Core\Handles\GitConfigEntryHandle.cs" /> - <Compile Include="Core\LegacyTarWriter.cs" /> <Compile Include="Core\Proxy.cs" /> <Compile Include="Credentials.cs" /> - <Compile Include="Core\TarException.cs" /> - <Compile Include="Core\TarHeader.cs" /> - <Compile Include="Core\TarReader.cs" /> <Compile Include="Core\TarWriter.cs" /> - <Compile Include="Core\UsTarHeader.cs" /> <Compile Include="DiffTargets.cs" /> <Compile Include="FetchHead.cs" /> <Compile Include="Core\Handles\PushSafeHandle.cs" /> |