Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/hakchi2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2017-03-29 01:25:16 +0300
committerAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2017-03-29 01:25:16 +0300
commit24145e9db04513d15b7cb60f033a9e9d863f5de7 (patch)
treeb7c6f4edd6b12f7439d65c6a3cbbc8bfc67bc212 /TarStream.cs
parent99ad7ec16c71750a577b7604206864ed4b23da76 (diff)
On-the-fly tar archiving, it's using not so much memory now. And faster.
Diffstat (limited to 'TarStream.cs')
-rw-r--r--TarStream.cs274
1 files changed, 274 insertions, 0 deletions
diff --git a/TarStream.cs b/TarStream.cs
new file mode 100644
index 00000000..e701b163
--- /dev/null
+++ b/TarStream.cs
@@ -0,0 +1,274 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace com.clusterrr.util
+{
+ public class TarStream : Stream
+ {
+ List<string> entries = new List<string>();
+ readonly string rootDirectory;
+ long totalSize = 0;
+ long position = 0;
+ int currentEntry = 0;
+ long currentEntryPosition = 0;
+ long currentEntryLength = 0;
+ FileStream currentFile = null;
+ byte[] currentHeader;
+
+ public delegate void OnProgressDelegate(long Position, long Length);
+ public event OnProgressDelegate OnReadProgress = delegate { };
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct TarHeader
+ {
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
+ public string FileName;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
+ public string FileMode;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
+ public string OwnerID;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
+ public string GroupID;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
+ public string FileSize;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
+ public string LastModificationTime;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
+ public byte[] Checksum;
+ [MarshalAs(UnmanagedType.U1)]
+ public char FileType;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
+ public string LinkedName;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
+ public string UstarMagic;
+ //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
+ //public char[] UstarVersion;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string OwnerUserName;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string OwnerGroupName;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
+ public string DeviceMajorNumber;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
+ public string DeviceMinorNumber;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 155)]
+ public string FileNamePrefix;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
+ string padding;
+
+ public void CalcChecksum()
+ {
+ Checksum = new byte[] { 32, 32, 32, 32, 32, 32, 32, 32 };
+ var bytes = getBytes();
+ uint summ = 0;
+ foreach (var b in bytes)
+ summ += b;
+ Array.Copy(Encoding.ASCII.GetBytes(Convert.ToString(summ, 8).PadLeft(6, '0')), 0, Checksum, 0, 6);
+ Checksum[6] = 0;
+ Checksum[7] = 32;
+ }
+
+ public byte[] getBytes()
+ {
+ int size = Marshal.SizeOf(this);
+ byte[] arr = new byte[size];
+
+ IntPtr ptr = Marshal.AllocHGlobal(size);
+ Marshal.StructureToPtr(this, ptr, true);
+ Marshal.Copy(ptr, arr, 0, size);
+ Marshal.FreeHGlobal(ptr);
+ return arr;
+ }
+ }
+
+ public TarStream(string directory, string rootDirectory = null)
+ {
+ if (rootDirectory == null) rootDirectory = directory;
+ if (!Directory.Exists(directory))
+ throw new Exception("Directory not exists");
+ directory = Path.GetFullPath(directory);
+ if (!Directory.Exists(rootDirectory))
+ throw new Exception("Root directory not exists");
+ rootDirectory = Path.GetFullPath(rootDirectory); // Full path
+ if (!directory.StartsWith(rootDirectory))
+ throw new Exception("Invarid root directory");
+
+ LoadDirectory(directory);
+ if (totalSize % 10240 != 0)
+ totalSize += (10240 - (totalSize % 10240));
+ this.rootDirectory = rootDirectory;
+ }
+
+ private void LoadDirectory(string directory)
+ {
+ if (!Directory.Exists(directory)) return;
+ var directories = Directory.GetDirectories(directory);
+ foreach (var d in directories)
+ {
+ var dname = d;
+ if (!dname.EndsWith(@"\"))
+ dname += @"\";
+ entries.Add(dname);
+ totalSize += 512;
+ LoadDirectory(d);
+ }
+ var files = Directory.GetFiles(directory);
+ foreach (var f in files)
+ {
+ entries.Add(f);
+ var size = new FileInfo(f).Length;
+ if (size % 512 != 0)
+ size += 512 - (size % 512);
+ totalSize += 512 + size;
+ }
+ }
+
+ public override bool CanRead
+ {
+ get { return true; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return false; }
+ }
+
+ public override long Length
+ {
+ get { return totalSize; }
+ }
+
+ public override long Position
+ {
+ get
+ {
+ return position;
+ }
+ set
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ int origCount = count;
+
+ while (count > 0)
+ {
+ if (currentEntryLength > 0 && currentEntryPosition >= currentEntryLength) // Next entry
+ {
+ currentEntry++;
+ currentEntryPosition = 0;
+ }
+
+ if (currentEntry >= entries.Count) // end of archive, write zeros
+ {
+ if (currentFile != null)
+ {
+ currentFile.Dispose();
+ currentFile = null;
+ }
+ long l = Math.Min(count, totalSize - position);
+ var dummy = new byte[l];
+ Array.Copy(dummy, 0, buffer, offset, l);
+ count -= (int)l;
+ position += l;
+ currentEntryPosition += l;
+ offset += (int)l;
+ break;
+ }
+
+ if (currentEntryPosition == 0) // New entry
+ {
+ currentEntryLength = 512;
+ var header = new TarHeader();
+ header.FileName = entries[currentEntry].Substring(rootDirectory.Length + 1).Replace(@"\", "/");
+ // close previous file
+ if (currentFile != null)
+ {
+ currentFile.Dispose();
+ currentFile = null;
+ }
+ if (!header.FileName.EndsWith("/")) // It's a file!
+ {
+ currentFile = new FileStream(entries[currentEntry], FileMode.Open);
+ header.FileMode = "0100644";
+ currentEntryLength += currentFile.Length;
+ if (currentFile.Length % 512 != 0)
+ currentEntryLength += 512 - (currentFile.Length % 512);
+ header.FileSize = Convert.ToString(currentFile.Length, 8).PadLeft(11, '0');
+ header.LastModificationTime = Convert.ToString(
+ (long)new FileInfo(entries[currentEntry]).LastWriteTimeUtc.Subtract(new DateTime(1970, 1, 1)).TotalSeconds
+ , 8).PadLeft(11, '0');
+ header.FileType = '0';
+ }
+ else // It's a directory...
+ {
+ header.FileMode = "0040755";
+ header.FileSize = "".PadLeft(11, '0');
+ header.LastModificationTime = Convert.ToString(
+ (long)new DirectoryInfo(entries[currentEntry]).LastWriteTimeUtc.Subtract(new DateTime(1970, 1, 1)).TotalSeconds
+ , 8).PadLeft(11, '0');
+ header.FileType = '5';
+ }
+ header.OwnerID = "0000000";
+ header.GroupID = "0000000";
+ header.UstarMagic = "ustar ";
+ //header.UstarVersion = new char[] {'0', '0'};
+ header.CalcChecksum();
+ currentHeader = header.getBytes();
+ }
+
+ if (currentEntryPosition < 512) // Header
+ {
+ long l = Math.Min(count, 512 - currentEntryPosition);
+ Array.Copy(currentHeader, currentEntryPosition, buffer, offset, l);
+ count -= (int)l;
+ position += l;
+ currentEntryPosition += l;
+ offset += (int)l;
+ }
+ else // Data
+ {
+ long l = Math.Min(count, currentEntryLength - currentEntryPosition);
+ currentFile.Read(buffer, offset, (int)l);
+ count -= (int)l;
+ position += l;
+ currentEntryPosition += l;
+ offset += (int)l;
+ }
+ }
+ OnReadProgress(Position, Length);
+ return origCount - count;
+ }
+
+ public override void Flush()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}