diff --git a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs index db6d5cf4..6fe73f78 100644 --- a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs +++ b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs @@ -39,16 +39,17 @@ namespace SharpCompress.Common.Tar.Headers WriteOctalBytes(0, buffer, 116, 8); // group ID //ArchiveEncoding.UTF8.GetBytes("magic").CopyTo(buffer, 257); - if (Name.Length > 100) + var nameByteCount = ArchiveEncoding.GetEncoding().GetByteCount(Name); + if (nameByteCount > 100) { // Set mock filename and filetype to indicate the next block is the actual name of the file WriteStringBytes("././@LongLink", buffer, 0, 100); buffer[156] = (byte)EntryType.LongName; - WriteOctalBytes(Name.Length + 1, buffer, 124, 12); + WriteOctalBytes(nameByteCount + 1, buffer, 124, 12); } else { - WriteStringBytes(Name, buffer, 0, 100); + WriteStringBytes(ArchiveEncoding.Encode(Name), buffer, 0, 100); WriteOctalBytes(Size, buffer, 124, 12); var time = (long)(LastModifiedTime.ToUniversalTime() - EPOCH).TotalSeconds; WriteOctalBytes(time, buffer, 136, 12); @@ -69,10 +70,17 @@ namespace SharpCompress.Common.Tar.Headers output.Write(buffer, 0, buffer.Length); - if (Name.Length > 100) + if (nameByteCount > 100) { WriteLongFilenameHeader(output); - Name = Name.Substring(0, 100); + // update to short name lower than 100 - [max bytes of one character]. + // subtracting bytes is needed because preventing infinite loop(example code is here). + // + // var bytes = Encoding.UTF8.GetBytes(new string(0x3042, 100)); + // var truncated = Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(bytes, 0, 100)); + // + // and then infinite recursion is occured in WriteLongFilenameHeader because truncated.Length is 102. + Name = ArchiveEncoding.Decode(ArchiveEncoding.Encode(Name), 0, 100 - ArchiveEncoding.GetEncoding().GetMaxByteCount(1)); Write(output); } } @@ -184,6 +192,17 @@ namespace SharpCompress.Common.Tar.Headers return buffer; } + private static void WriteStringBytes(byte[] name, byte[] buffer, int offset, int length) + { + int i = Math.Min(length, name.Length); + Buffer.BlockCopy(name, 0, buffer, offset, i); + // if Span.Fill can be used, it is more efficient + for (; i < length; ++i) + { + buffer[offset + i] = 0; + } + } + private static void WriteStringBytes(string name, byte[] buffer, int offset, int length) { int i; diff --git a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs index 84ff7eb3..b67a046b 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs @@ -5,6 +5,10 @@ using SharpCompress.Archives.Tar; using SharpCompress.Common; using SharpCompress.Writers; using Xunit; +using System.Text; +using SharpCompress.Readers; +using SharpCompress.Writers.Tar; +using SharpCompress.Readers.Tar; namespace SharpCompress.Test.Tar { @@ -26,7 +30,7 @@ namespace SharpCompress.Test.Tar { ArchiveFileRead("Tar.tar"); } - + [Fact] public void Tar_FileName_Exactly_100_Characters() { @@ -135,10 +139,17 @@ namespace SharpCompress.Test.Tar string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"); string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar"); + // var aropt = new Ar + using (var archive = TarArchive.Create()) { archive.AddAllFromDirectory(ORIGINAL_FILES_PATH); - archive.SaveTo(scratchPath, CompressionType.None); + var twopt = new TarWriterOptions(CompressionType.None, true); + twopt.ArchiveEncoding = new ArchiveEncoding() + { + Default = Encoding.GetEncoding(866) + }; + archive.SaveTo(scratchPath, twopt); } CompareArchivesByPath(unmodified, scratchPath); } @@ -195,5 +206,40 @@ namespace SharpCompress.Test.Tar Assert.True(archive.Type == ArchiveType.Tar); } } + [Theory] + [InlineData(10)] + [InlineData(128)] + public void Tar_Japanese_Name(int length) + { + using (var mstm = new MemoryStream()) + { + var enc = new ArchiveEncoding() + { + Default = Encoding.UTF8 + }; + var twopt = new TarWriterOptions(CompressionType.None, true); + twopt.ArchiveEncoding = enc; + var fname = new string((char)0x3042, length); + using (var tw = new TarWriter(mstm, twopt)) + using (var input = new MemoryStream(new byte[32])) + { + tw.Write(fname, input, null); + } + using (var inputMemory = new MemoryStream(mstm.ToArray())) + { + var tropt = new ReaderOptions() + { + ArchiveEncoding = enc + }; + using (var tr = TarReader.Open(inputMemory, tropt)) + { + while (tr.MoveToNextEntry()) + { + Assert.Equal(fname, tr.Entry.Key); + } + } + } + } + } } } diff --git a/tests/TestArchives/Archives/Tar.mod.tar b/tests/TestArchives/Archives/Tar.mod.tar index 86267e9a..8b65a692 100644 Binary files a/tests/TestArchives/Archives/Tar.mod.tar and b/tests/TestArchives/Archives/Tar.mod.tar differ diff --git a/tests/TestArchives/Archives/Tar.noEmptyDirs.tar b/tests/TestArchives/Archives/Tar.noEmptyDirs.tar index 6892fcc6..1e23460f 100644 Binary files a/tests/TestArchives/Archives/Tar.noEmptyDirs.tar and b/tests/TestArchives/Archives/Tar.noEmptyDirs.tar differ diff --git a/tests/TestArchives/Archives/Tar.noEmptyDirs.tar.bz2 b/tests/TestArchives/Archives/Tar.noEmptyDirs.tar.bz2 index 831ed029..784e456d 100644 Binary files a/tests/TestArchives/Archives/Tar.noEmptyDirs.tar.bz2 and b/tests/TestArchives/Archives/Tar.noEmptyDirs.tar.bz2 differ diff --git a/tests/TestArchives/Archives/Tar.noEmptyDirs.tar.lz b/tests/TestArchives/Archives/Tar.noEmptyDirs.tar.lz index e6ae5d7c..18d3d976 100644 Binary files a/tests/TestArchives/Archives/Tar.noEmptyDirs.tar.lz and b/tests/TestArchives/Archives/Tar.noEmptyDirs.tar.lz differ