public async Task CreateEntryAsync(TarEntry entry) { ValidateWroteAll(); var paxAttributes = new Dictionary <string, string>(); // Clear padding. for (int i = 0; i < TarCommon.BlockSize; i++) { _buffer[i] = 0; } var state = new TarHeaderView(_buffer, TarCommon.BlockSize, paxAttributes); var padding = _remainingPadding; _remainingPadding = 0; GenerateHeader(ref state, entry, default(ArraySegment <byte>)); if (paxAttributes.Count > 0) { padding = await WritePaxEntry(entry, paxAttributes, padding); } await _stream.WriteAsync(_buffer, TarCommon.BlockSize - padding, TarCommon.BlockSize + padding); _remaining = entry.Length; _remainingPadding = TarCommon.Padding(_remaining); }
private async Task <int> WritePaxEntry(TarEntry entry, Dictionary <string, string> paxAttributes, int padding) { var paxStream = new MemoryStream(); // Skip the tar header, then go back and write it later. var paxDataOffset = padding + TarCommon.BlockSize; paxStream.SetLength(paxDataOffset); paxStream.Position = paxDataOffset; foreach (var attribute in paxAttributes) { WritePaxAttribute(paxStream, attribute.Key, attribute.Value); } var paxEntry = new TarEntry { Type = TarCommon.PaxHeaderType, Length = paxStream.Length - paxDataOffset, }; ArraySegment <byte> buffer; paxStream.TryGetBuffer(out buffer); var paxHeader = new TarHeaderView(buffer.Array, buffer.Offset + padding, null); GenerateHeader(ref paxHeader, paxEntry, GetPaxName(entry.Name)); paxStream.Position = 0; await paxStream.CopyToAsync(_stream); return(TarCommon.Padding(paxEntry.Length)); }
public bool TryPutString(string str, TarHeader.HeaderField field) { if (str == null) { PutNul(field); return(true); } if (TarCommon.IsASCII(str)) { if (str.Length <= field.Length) { for (int i = 0; i < str.Length; i++) { this[field.Offset + i] = (byte)str[i]; } PutNul(field.Offset + str.Length, field.Length - str.Length); return(true); } } if (field.PaxAttribute != null && PaxAttributes != null) { PaxAttributes[field.PaxAttribute] = str; } PutNul(field); return(false); }
private async Task <TarEntry> ReadHeader(Dictionary <string, string> paxAttributes) { if (_remaining > 0) { await Skip(_remaining); _remaining = 0; } var read = await _stream.ReadAsync(_buffer, 0, TarCommon.BlockSize + _remainingPadding); var padding = _remainingPadding; _remainingPadding = 0; if (read <= _remainingPadding) { // No more entries. return(null); } else if (read - _remainingPadding < TarCommon.BlockSize) { throw new EndOfStreamException(); } if (IsArrayZero(padding, TarCommon.BlockSize)) { // Verify that the next block is also zero. read = await _stream.ReadAsync(_buffer, 0, TarCommon.BlockSize); if (read == 0) { return(null); } else if (read < TarCommon.BlockSize) { throw new EndOfStreamException(); } else if (IsArrayZero(0, TarCommon.BlockSize)) { return(null); } else { throw new TarParseException("non-zero header after zero header"); } } var header = new TarHeaderView(_buffer, padding, paxAttributes); var entry = ParseHeader(ref header); _remaining = entry.Length; _remainingPadding = TarCommon.Padding(_remaining); return(entry); }
// Tries to split a path at a '/' character so that the two pieces will fit into // the prefix and name fields. private static bool TrySplitPath(string path, out int splitIndex) { splitIndex = -1; if (!TarCommon.IsASCII(path)) { return(false); } for (int i = 0; i < path.Length; i++) { if (path[i] == '/') { if (i < TarHeader.Prefix.Length && path.Length - i - 1 < TarHeader.Name.Length) { splitIndex = i; return(true); } } } return(false); }
private static TarEntry ParseHeader(ref TarHeaderView header) { var entry = new TarEntry { Name = header.GetString(TarHeader.Name), Mode = header.GetOctal(TarHeader.Mode), UserID = header.GetOctal(TarHeader.UserID), GroupID = header.GetOctal(TarHeader.GroupID), Length = header.GetOctalLong(TarHeader.Length), ModifiedTime = header.GetTime(TarHeader.ModifiedTime) }; var checksum = header.GetOctal(TarHeader.Checksum); int signedChecksum; var unsignedChecksum = TarCommon.Checksum(header.Field(TarHeader.FullHeader), out signedChecksum); if (checksum != signedChecksum && checksum != unsignedChecksum) { throw new TarParseException("invalid tar checksum"); } var typeFlag = header[TarHeader.TypeFlag.Offset]; if (typeFlag == 0) { typeFlag = (byte)'0'; } entry.Type = (TarEntryType)typeFlag; entry.LinkTarget = header.GetString(TarHeader.LinkTarget); var magic = header.GetString(TarHeader.FullMagic); if (magic == TarCommon.PosixMagic || magic == TarCommon.GnuMagic) { entry.UserName = header.GetString(TarHeader.UserName); entry.GroupName = header.GetString(TarHeader.GroupName); entry.DeviceMajor = header.GetOctal(TarHeader.DeviceMajor); entry.DeviceMinor = header.GetOctal(TarHeader.DeviceMinor); if (magic == TarCommon.PosixMagic) { if (header.PaxAttributes == null || !header.PaxAttributes.ContainsKey(TarHeader.Name.PaxAttribute)) { var prefix = header.GetString(TarHeader.Prefix); if (prefix.Length > 0) { entry.Name = prefix + "/" + entry.Name; } } string atime = header.GetPaxValue(TarCommon.PaxAtime); if (atime != null) { entry.AccessTime = TarTime.FromPaxTime(atime); } string ctime = header.GetPaxValue(TarCommon.PaxCtime); if (ctime != null) { entry.ChangeTime = TarTime.FromPaxTime(ctime); } string creationtime = header.GetPaxValue(TarCommon.PaxCreationTime); if (creationtime != null) { entry.CreationTime = TarTime.FromPaxTime(creationtime); } string fileAttributes = header.GetPaxValue(TarCommon.PaxWindowsFileAttributes); if (fileAttributes != null) { entry.FileAttributes = (FileAttributes)Convert.ToUInt32(fileAttributes); } entry.SecurityDescriptor = header.GetPaxValue(TarCommon.PaxWindowsSecurityDescriptor); if (header.GetPaxValue(TarCommon.PaxWindowsMountPoint) != null) { entry.IsMountPoint = true; } } } return(entry); }
private static void GenerateHeader(ref TarHeaderView header, TarEntry entry, ArraySegment <byte> name) { var needsPath = false; header.PutNul(TarHeader.FullHeader); // Try to write the name, but don't write the PAX attribute yet in case we can // just write a split name later. if (name.Count > 0) { header.PutBytes(name, TarHeader.Name); } else if (!header.TryPutString(entry.Name, TarHeader.Name.WithoutPax)) { needsPath = true; } header.TryPutOctal(entry.Mode, TarHeader.Mode); header.TryPutOctal(entry.UserID, TarHeader.UserID); header.TryPutOctal(entry.GroupID, TarHeader.GroupID); header.TryPutOctal(entry.Length, TarHeader.Length); header.TryPutTime(entry.ModifiedTime, TarHeader.ModifiedTime); header[TarHeader.ChecksumSpace.Offset] = (byte)' '; header[TarHeader.TypeFlag.Offset] = (byte)entry.Type; header.TryPutString(entry.LinkTarget, TarHeader.LinkTarget); header.TryPutString(TarCommon.PosixMagic, TarHeader.Magic); header[TarHeader.Version.Offset] = (byte)'0'; header[TarHeader.Version.Offset + 1] = (byte)'0'; header.TryPutString(entry.UserName, TarHeader.UserName); header.TryPutString(entry.GroupName, TarHeader.GroupName); header.TryPutOctal(entry.DeviceMajor, TarHeader.DeviceMajor); header.TryPutOctal(entry.DeviceMinor, TarHeader.DeviceMinor); if (header.PaxAttributes != null) { if (entry.AccessTime.HasValue) { header.PaxAttributes[TarCommon.PaxAtime] = TarTime.ToPaxTime(entry.AccessTime.Value); } if (entry.ChangeTime.HasValue) { header.PaxAttributes[TarCommon.PaxCtime] = TarTime.ToPaxTime(entry.ChangeTime.Value); } if (entry.CreationTime.HasValue) { header.PaxAttributes[TarCommon.PaxCreationTime] = TarTime.ToPaxTime(entry.CreationTime.Value); } if (entry.FileAttributes.HasValue) { header.PaxAttributes[TarCommon.PaxWindowsFileAttributes] = Convert.ToString((uint)entry.FileAttributes.Value); } if (entry.IsMountPoint) { header.PaxAttributes[TarCommon.PaxWindowsMountPoint] = "1"; } if (entry.SecurityDescriptor != null) { header.PaxAttributes[TarCommon.PaxWindowsSecurityDescriptor] = entry.SecurityDescriptor; } if (needsPath) { int splitIndex; if (header.PaxAttributes.Count == 0 && TrySplitPath(entry.Name, out splitIndex)) { header.TryPutString(entry.Name.Substring(0, splitIndex), TarHeader.Prefix); header.TryPutString(entry.Name.Substring(splitIndex + 1), TarHeader.Name); } else { header.PaxAttributes[TarHeader.Name.PaxAttribute] = entry.Name; } } } int signedChecksum; var checksum = TarCommon.Checksum(header.Field(TarHeader.FullHeader), out signedChecksum); header.TryPutOctal(checksum, TarHeader.Checksum); }