Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Address feedback
  • Loading branch information
jozkee committed Sep 28, 2022
commit a629811734261089d2794402495651a19d4f2d57
Original file line number Diff line number Diff line change
Expand Up @@ -378,45 +378,45 @@ private int WriteName(Span<byte> buffer)
}

// 'https://www.freebsd.org/cgi/man.cgi?tar(5)'
// If the pathname is too long to fit in the 100 bytes provided by the standard format,
// If the path name is too long to fit in the 100 bytes provided by the standard format,
// it can be split at any / character with the first portion going into the prefix field.
private int WriteUstarName(Span<byte> buffer)
{
// We can have a pathname as big as 256, prefix + '/' + name,
// We can have a path name as big as 256, prefix + '/' + name,
// the separator in between can be neglected as the reader will append it when it joins both fields.
const int MaxPathname = FieldLengths.Prefix + FieldLengths.Name + 1;
const int MaxPathName = FieldLengths.Prefix + 1 + FieldLengths.Name;

if (GetUtf8TextLength(_name) > MaxPathname)
if (GetUtf8TextLength(_name) > MaxPathName)
{
throw new ArgumentException(SR.TarEntryFieldExceedsMaxLength, ArgNameEntry);
throw new ArgumentException(SR.Format(SR.TarEntryFieldExceedsMaxLength, nameof(TarEntry.Name)), ArgNameEntry);
}

Span<byte> encodingBuffer = stackalloc byte[MaxPathname];
Span<byte> encodingBuffer = stackalloc byte[MaxPathName];
int encoded = Encoding.UTF8.GetBytes(_name, encodingBuffer);
ReadOnlySpan<byte> pathnameBytes = encodingBuffer.Slice(0, encoded);
ReadOnlySpan<byte> pathNameBytes = encodingBuffer.Slice(0, encoded);

// If the pathname is able to fit in Name, we can write it down there and avoid calculating Prefix.
if (pathnameBytes.Length <= FieldLengths.Name)
if (pathNameBytes.Length <= FieldLengths.Name)
{
return WriteLeftAlignedBytesAndGetChecksum(pathnameBytes, buffer.Slice(FieldLocations.Name, FieldLengths.Name));
return WriteLeftAlignedBytesAndGetChecksum(pathNameBytes, buffer.Slice(FieldLocations.Name, FieldLengths.Name));
}

int lastIdx = pathnameBytes.LastIndexOfAny(PathInternal.Utf8DirectorySeparators);
int lastIdx = pathNameBytes.LastIndexOfAny(PathInternal.Utf8DirectorySeparators);
scoped ReadOnlySpan<byte> name;
scoped ReadOnlySpan<byte> prefix;

if (lastIdx < 1) // splitting at the root is not allowed.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// splitting at the root is not allowed.

I copied this behavior from GNU Tar, it does not store one single separtor in the prefix, I assume we should do the same.

No big deal to go the other way but "absolute path" is already very odd scenario for tar and I don't have a reason to not do the same.

On top of that, I think we should instruct users to be aware about/avoid using absolute paths with Tar APIs.

{
name = pathnameBytes;
name = pathNameBytes;
prefix = default;
}
else
{
name = pathnameBytes.Slice(lastIdx + 1);
prefix = pathnameBytes.Slice(0, lastIdx);
name = pathNameBytes.Slice(lastIdx + 1);
prefix = pathNameBytes.Slice(0, lastIdx);
}

// At this point pathnameBytes.Length > 100.
// At this point path name is > 100.
// Attempt to split it in a way it can use prefix.
while (prefix.Length - name.Length > FieldLengths.Prefix)
{
Expand All @@ -426,8 +426,8 @@ private int WriteUstarName(Span<byte> buffer)
break;
}

name = pathnameBytes.Slice(lastIdx + 1);
prefix = pathnameBytes.Slice(0, lastIdx);
name = pathNameBytes.Slice(lastIdx + 1);
prefix = pathNameBytes.Slice(0, lastIdx);
}

if (prefix.Length <= FieldLengths.Prefix && name.Length <= FieldLengths.Name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ public async Task NameRoundtripsAsync(TarEntryFormat entryFormat, TarEntryType e
MemoryStream ms = new();
Stream s = unseekableStream ? new WrappedStream(ms, ms.CanRead, ms.CanWrite, canSeek: false) : ms;

using (TarWriter writer = new(s, leaveOpen: true))
await using (TarWriter writer = new(s, leaveOpen: true))
{
await writer.WriteEntryAsync(entry);
}

ms.Position = 0;
using TarReader reader = new(s);
await using TarReader reader = new(s);

entry = await reader.GetNextEntryAsync();
Assert.Null(await reader.GetNextEntryAsync());
Expand All @@ -41,7 +41,7 @@ public static IEnumerable<object[]> LinkNameRoundtripsAsyncTheoryData()

[Theory]
[MemberData(nameof(LinkNameRoundtripsAsyncTheoryData))]
public async Task LinkNameRoundtrips(TarEntryFormat entryFormat, TarEntryType entryType, bool unseekableStream, string linkName)
public async Task LinkNameRoundtripsAsync(TarEntryFormat entryFormat, TarEntryType entryType, bool unseekableStream, string linkName)
{
string name = "foo";
TarEntry entry = InvokeTarEntryCreationConstructor(entryFormat, entryType, name);
Expand All @@ -50,13 +50,13 @@ public async Task LinkNameRoundtrips(TarEntryFormat entryFormat, TarEntryType en
MemoryStream ms = new();
Stream s = unseekableStream ? new WrappedStream(ms, ms.CanRead, ms.CanWrite, canSeek: false) : ms;

using (TarWriter writer = new(s, leaveOpen: true))
await using (TarWriter writer = new(s, leaveOpen: true))
{
await writer.WriteEntryAsync(entry);
}

ms.Position = 0;
using TarReader reader = new(s);
await using TarReader reader = new(s);

entry = await reader.GetNextEntryAsync();
Assert.Null(await reader.GetNextEntryAsync());
Expand All @@ -69,7 +69,7 @@ public static IEnumerable<object[]> UserNameGroupNameRoundtripsAsyncTheoryData()

[Theory]
[MemberData(nameof(UserNameGroupNameRoundtripsAsyncTheoryData))]
public async Task UserNameGroupNameRoundtrips(TarEntryFormat entryFormat, bool unseekableStream, string userGroupName)
public async Task UserNameGroupNameRoundtripsAsync(TarEntryFormat entryFormat, bool unseekableStream, string userGroupName)
{
string name = "foo";
TarEntry entry = InvokeTarEntryCreationConstructor(entryFormat, TarEntryType.RegularFile, name);
Expand All @@ -80,13 +80,13 @@ public async Task UserNameGroupNameRoundtrips(TarEntryFormat entryFormat, bool u
MemoryStream ms = new();
Stream s = unseekableStream ? new WrappedStream(ms, ms.CanRead, ms.CanWrite, canSeek: false) : ms;

using (TarWriter writer = new(s, leaveOpen: true))
await using (TarWriter writer = new(s, leaveOpen: true))
{
await writer.WriteEntryAsync(posixEntry);
}

ms.Position = 0;
using TarReader reader = new(s);
await using TarReader reader = new(s);

entry = await reader.GetNextEntryAsync();
posixEntry = Assert.IsAssignableFrom<PosixTarEntry>(entry);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ public static IEnumerable<object[]> WriteEntry_TooLongName_Throws_Async_TheoryDa
[MemberData(nameof(WriteEntry_TooLongName_Throws_Async_TheoryData))]
public async Task WriteEntry_TooLongName_Throws_Async(TarEntryFormat entryFormat, TarEntryType entryType, string name)
{
using TarWriter writer = new(new MemoryStream());
await using TarWriter writer = new(new MemoryStream());

TarEntry entry = InvokeTarEntryCreationConstructor(entryFormat, entryType, name);
await Assert.ThrowsAsync<ArgumentException>("entry", () => writer.WriteEntryAsync(entry));
Expand All @@ -343,7 +343,7 @@ public static IEnumerable<object[]> WriteEntry_TooLongLinkName_Throws_Async_Theo
[MemberData(nameof(WriteEntry_TooLongLinkName_Throws_Async_TheoryData))]
public async Task WriteEntry_TooLongLinkName_Throws_Async(TarEntryFormat entryFormat, TarEntryType entryType, string linkName)
{
using TarWriter writer = new(new MemoryStream());
await using TarWriter writer = new(new MemoryStream());

TarEntry entry = InvokeTarEntryCreationConstructor(entryFormat, entryType, "foo");
entry.LinkName = linkName;
Expand All @@ -356,9 +356,9 @@ public static IEnumerable<object[]> WriteEntry_TooLongUserGroupName_Throws_Async

[Theory]
[MemberData(nameof(WriteEntry_TooLongUserGroupName_Throws_Async_TheoryData))]
public async Task WriteEntry_TooLongUserName_Throws(TarEntryFormat entryFormat, string userName)
public async Task WriteEntry_TooLongUserName_Throws_Async(TarEntryFormat entryFormat, string userName)
{
using TarWriter writer = new(new MemoryStream());
await using TarWriter writer = new(new MemoryStream());

TarEntry entry = InvokeTarEntryCreationConstructor(entryFormat, TarEntryType.RegularFile, "foo");
PosixTarEntry posixEntry = Assert.IsAssignableFrom<PosixTarEntry>(entry);
Expand All @@ -369,9 +369,9 @@ public async Task WriteEntry_TooLongUserName_Throws(TarEntryFormat entryFormat,

[Theory]
[MemberData(nameof(WriteEntry_TooLongUserGroupName_Throws_Async_TheoryData))]
public async Task WriteEntry_TooLongGroupName_Throws(TarEntryFormat entryFormat, string groupName)
public async Task WriteEntry_TooLongGroupName_Throws_Async(TarEntryFormat entryFormat, string groupName)
{
using TarWriter writer = new(new MemoryStream());
await using TarWriter writer = new(new MemoryStream());

TarEntry entry = InvokeTarEntryCreationConstructor(entryFormat, TarEntryType.RegularFile, "foo");
PosixTarEntry posixEntry = Assert.IsAssignableFrom<PosixTarEntry>(entry);
Expand Down