diff --git a/LibGit2Sharp/Core/DataWriter.cs b/LibGit2Sharp/Core/DataWriter.cs
new file mode 100644
index 000000000..e972b66d9
--- /dev/null
+++ b/LibGit2Sharp/Core/DataWriter.cs
@@ -0,0 +1,63 @@
+/*
+ * 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
new file mode 100644
index 000000000..ed52d01c1
--- /dev/null
+++ b/LibGit2Sharp/Core/IArchiveDataWriter.cs
@@ -0,0 +1,29 @@
+/*
+ * 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
+ {
+ ///
+ /// Write `length` bytes of data from `buffer` to corresponding archive.
+ ///
+ /// data storage
+ /// how many bytes to be written to the corresponding archive
+ 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
new file mode 100644
index 000000000..bdcc1341a
--- /dev/null
+++ b/LibGit2Sharp/Core/ITarHeader.cs
@@ -0,0 +1,32 @@
+/*
+ * 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
new file mode 100644
index 000000000..e03a42d36
--- /dev/null
+++ b/LibGit2Sharp/Core/LegacyTarWriter.cs
@@ -0,0 +1,163 @@
+/*
+ * 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;
+
+ ///
+ /// Writes tar (see GNU tar) archive to a stream
+ ///
+ /// stream to write archive to
+ 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
new file mode 100644
index 000000000..ea1d484b7
--- /dev/null
+++ b/LibGit2Sharp/Core/TarException.cs
@@ -0,0 +1,26 @@
+/*
+ * 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
new file mode 100644
index 000000000..0a4c285a9
--- /dev/null
+++ b/LibGit2Sharp/Core/TarHeader.cs
@@ -0,0 +1,223 @@
+/*
+ * 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
new file mode 100644
index 000000000..3709bafab
--- /dev/null
+++ b/LibGit2Sharp/Core/TarReader.cs
@@ -0,0 +1,210 @@
+/*
+ * 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
+{
+ ///
+ /// Extract contents of a tar file represented by a stream for the TarReader constructor
+ ///
+ public class TarReader
+ {
+ private readonly byte[] dataBuffer = new byte[512];
+ private readonly UsTarHeader header;
+ private readonly Stream inStream;
+ private long remainingBytesInFile;
+
+ ///
+ /// Constructs TarReader object to read data from `tarredData` stream
+ ///
+ /// A stream to read tar archive from
+ public TarReader(Stream tarredData)
+ {
+ inStream = tarredData;
+ header = new UsTarHeader();
+ }
+
+ public ITarHeader FileInfo
+ {
+ get { return header; }
+ }
+
+ ///
+ /// Read all files from an archive to a directory. It creates some child directories to
+ /// reproduce a file structure from the archive.
+ ///
+ /// The out directory.
+ ///
+ /// 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);
+ }
+ }
+ }
+
+ ///
+ /// Read data from a current file to a Stream.
+ ///
+ /// A stream to read data to
+ ///
+ ///
+ 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;
+ }
+
+ ///
+ /// Check if all bytes in buffer are zeroes
+ ///
+ /// buffer to check
+ /// true if all bytes are zeroes, otherwise false
+ private static bool IsEmpty(IEnumerable buffer)
+ {
+ foreach(byte b in buffer)
+ {
+ if (b != 0) return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Move internal pointer to a next file in archive.
+ ///
+ /// Should be true if you want to read a header only, otherwise false
+ /// false on End Of File otherwise true
+ ///
+ /// Example:
+ /// while(MoveNext())
+ /// {
+ /// Read(dataDestStream);
+ /// }
+ ///
+ 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
new file mode 100644
index 000000000..94a6ae70a
--- /dev/null
+++ b/LibGit2Sharp/Core/TarWriter.cs
@@ -0,0 +1,83 @@
+/*
+ * Source: http://code.google.com/p/tar-cs/
+ *
+ * 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;
+
+namespace tar_cs
+{
+ public class TarWriter : LegacyTarWriter
+ {
+
+ public TarWriter(Stream writeStream) : base(writeStream)
+ {
+ }
+
+ protected override void WriteHeader(string name, DateTime lastModificationTime, long count, int userId, int groupId, int mode)
+ {
+ 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);
+ }
+
+ protected virtual void WriteHeader(string name, DateTime lastModificationTime, long count, string userName, string groupName, int mode)
+ {
+ var tarHeader = new UsTarHeader()
+ {
+ 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);
+ }
+
+
+ public virtual void Write(string name, long dataSizeInBytes, string userName, string groupName, int mode, DateTime lastModificationTime, WriteDataDelegate writeDelegate)
+ {
+ var writer = new DataWriter(OutStream,dataSizeInBytes);
+ WriteHeader(name, lastModificationTime, dataSizeInBytes, userName, groupName, mode);
+ while(writer.CanWrite)
+ {
+ writeDelegate(writer);
+ }
+ AlignTo512(dataSizeInBytes, false);
+ }
+
+
+ public void Write(Stream data, long dataSizeInBytes, string fileName, string userId, string groupId, int mode,
+ DateTime lastModificationTime)
+ {
+ WriteHeader(fileName,lastModificationTime,dataSizeInBytes,userId, groupId, mode);
+ WriteContent(dataSizeInBytes,data);
+ AlignTo512(dataSizeInBytes,false);
+ }
+ }
+}
diff --git a/LibGit2Sharp/Core/UsTarHeader.cs b/LibGit2Sharp/Core/UsTarHeader.cs
new file mode 100644
index 000000000..a4f2a9a89
--- /dev/null
+++ b/LibGit2Sharp/Core/UsTarHeader.cs
@@ -0,0 +1,142 @@
+/*
+ * 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
+{
+ ///
+ /// UsTar header implementation.
+ ///
+ 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 b71344b34..f865814db 100644
--- a/LibGit2Sharp/LibGit2Sharp.csproj
+++ b/LibGit2Sharp/LibGit2Sharp.csproj
@@ -153,6 +153,7 @@
+
@@ -160,11 +161,19 @@
+
+
+
+
+
+
+
+
diff --git a/acknowledgments.md b/acknowledgments.md
new file mode 100644
index 000000000..a04ce2847
--- /dev/null
+++ b/acknowledgments.md
@@ -0,0 +1,2 @@
+LibGit2Sharp is making use of the following OSS projects:
+- [tar-cs](http://code.google.com/p/tar-cs/) project by Vladimir Vasiltsov and is used under the [BSD license](http://code.google.com/p/tar-cs/source/browse/trunk/COPYING)