Support writing tar archives with file names > 99 characters

This commit is contained in:
Frederik Carlier
2017-11-11 14:37:28 +01:00
parent 015c0ce8bf
commit 39057a96c9
5 changed files with 116 additions and 12 deletions

View File

@@ -1,10 +1,11 @@
using System;
using Packaging.Targets.IO;
using Packaging.Targets.IO;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using Xunit;
namespace Packaging.Targets.Tests.IO
@@ -52,6 +53,56 @@ namespace Packaging.Targets.Tests.IO
}
}
[Fact]
public void WriteTarWithLongFilenameTest()
{
using (Stream expected = File.OpenRead(@"IO/largefilename.tar"))
using (Stream actual = new MemoryStream())
using (Stream output = new ValidatingCompositeStream(null, actual, expected))
{
var directories = new string[]
{
"./",
"./usr/",
"./usr/lib/",
"./usr/lib/mono/",
"./usr/lib/mono/gac/",
"./usr/lib/mono/gac/System.Runtime.InteropServices.RuntimeInformation/",
"./usr/lib/mono/gac/System.Runtime.InteropServices.RuntimeInformation/4.0.0.0__b03f5f7f11d50a3a/",
};
foreach (var directory in directories)
{
TarFileCreator.WriteEntry(
output,
new ArchiveEntry()
{
Mode = LinuxFileMode.S_IXOTH | LinuxFileMode.S_IROTH | LinuxFileMode.S_IXGRP | LinuxFileMode.S_IRGRP | LinuxFileMode.S_IXUSR | LinuxFileMode.S_IWUSR | LinuxFileMode.S_IRUSR | LinuxFileMode.S_IFDIR,
TargetPath = directory,
Group = "root",
Owner = "root",
Modified = new DateTimeOffset(2016, 12, 15, 10, 58, 56, TimeSpan.Zero)
},
null);
}
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes("This file has a very long name.")))
{
TarFileCreator.WriteEntry(
output,
new ArchiveEntry()
{
Mode = (LinuxFileMode)0x81FF,
TargetPath = "./usr/lib/mono/gac/System.Runtime.InteropServices.RuntimeInformation/4.0.0.0__b03f5f7f11d50a3a/System.Runtime.InteropServices.RuntimeInformation.txt",
Modified = new DateTimeOffset(2017, 11, 11, 12, 37, 58, TimeSpan.Zero)
},
stream);
}
TarFileCreator.WriteTrailer(output);
}
}
[Fact]
public void WriteTarFileTest()
{

Binary file not shown.

View File

@@ -41,6 +41,9 @@
<None Update="Deb\libplist3_1.12-3.1_amd64.deb">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="IO\largefilename.tar">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="IO\test.cpio">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@@ -103,5 +103,15 @@ namespace Packaging.Targets.IO
/// The file is a Unix socket.
/// </summary>
S_IFSOCK = 0xC000 // 0140000 in octal
/// <summary>
/// A flag to get all permissions applied to this file.
/// </summary>
PermissionsMask = 0x0FFF,
/// <summary>
/// A flag to get the file type.
/// </summary>
FileTypeMask = 0xF000,
}
}

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
namespace Packaging.Targets.IO
{
@@ -55,14 +53,55 @@ namespace Packaging.Targets.IO
targetPath = "." + targetPath;
}
// Handle long file names (> 99 characters). If this is the case, add a "././@LongLink" pseudo-entry
// which contains the full name.
if (targetPath.Length > 99)
{
// Must include a trailing \0
var nameLength = Encoding.UTF8.GetByteCount(targetPath);
byte[] entryName = new byte[nameLength + 1];
Encoding.UTF8.GetBytes(targetPath, 0, targetPath.Length, entryName, 0);
ArchiveEntry nameEntry = new ArchiveEntry()
{
Mode = entry.Mode,
Modified = entry.Modified,
TargetPath = "././@LongLink",
Owner = entry.Owner,
Group = entry.Group
};
using (MemoryStream nameStream = new MemoryStream(entryName))
{
WriteEntry(stream, nameEntry, nameStream);
}
targetPath = targetPath.Substring(0, 99);
}
var isDir = entry.Mode.HasFlag(LinuxFileMode.S_IFDIR);
var isLink = !isDir && !string.IsNullOrWhiteSpace(entry.LinkTo);
var isFile = !isDir && !isLink;
var type = isFile
? TarTypeFlag.RegType
: isDir
? TarTypeFlag.DirType
: TarTypeFlag.LnkType;
TarTypeFlag type;
if (entry.TargetPath == "././@LongLink")
{
type = TarTypeFlag.LongName;
}
else if (isFile)
{
type = TarTypeFlag.RegType;
}
else if (isDir)
{
type = TarTypeFlag.DirType;
}
else
{
type = TarTypeFlag.LnkType;
}
bool dispose = false;
if (data == null)
@@ -82,7 +121,8 @@ namespace Packaging.Targets.IO
{
var hdr = new TarHeader()
{
FileMode = entry.Mode,
// No need to set the file type, the tar header has a special field for that.
FileMode = entry.Mode & LinuxFileMode.PermissionsMask,
DevMajor = null,
DevMinor = null,
FileName = targetPath,