Skip to content
This repository was archived by the owner on Aug 8, 2024. It is now read-only.

Commit f3815a9

Browse files
authored
Backport FixPathLength 255 PR
Fixes mono/mono#17948 Pulled in upstream change dotnet#34389
1 parent 5951d0e commit f3815a9

File tree

5 files changed

+73
-10
lines changed

5 files changed

+73
-10
lines changed

src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.ReadDir.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,21 @@ internal unsafe struct DirectoryEntry
3030
internal byte* Name;
3131
internal int NameLength;
3232
internal NodeType InodeType;
33-
internal const int NameBufferSize = 256;
33+
internal const int NameBufferSize = 256; // sizeof(dirent->d_name) == NAME_MAX + 1
3434

3535
internal ReadOnlySpan<char> GetName(Span<char> buffer)
3636
{
37-
Debug.Assert(buffer.Length >= Encoding.UTF8.GetMaxCharCount(NameBufferSize - 1), "should have enough space for the max file name");
37+
// -1 for null terminator (buffer will not include one),
38+
// and -1 because GetMaxCharCount pessimistically assumes the buffer may start with a partial surrogate
39+
Debug.Assert(buffer.Length >= Encoding.UTF8.GetMaxCharCount(NameBufferSize - 1 - 1));
3840
Debug.Assert(Name != null, "should not have a null name");
3941

4042
ReadOnlySpan<byte> nameBytes = (NameLength == -1)
4143
// In this case the struct was allocated via struct dirent *readdir(DIR *dirp);
42-
? new ReadOnlySpan<byte>(Name, new ReadOnlySpan<byte>(Name, NameBufferSize - 1).IndexOf<byte>(0))
44+
? new ReadOnlySpan<byte>(Name, new ReadOnlySpan<byte>(Name, NameBufferSize).IndexOf<byte>(0))
4345
: new ReadOnlySpan<byte>(Name, NameLength);
4446

4547
Debug.Assert(nameBytes.Length > 0, "we shouldn't have gotten a garbage value from the OS");
46-
if (nameBytes.Length == 0)
47-
return buffer.Slice(0, 0);
4848

4949
int charCount = Encoding.UTF8.GetChars(nameBytes, buffer);
5050
ReadOnlySpan<char> value = buffer.Slice(0, charCount);

src/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Unix.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,13 @@ namespace System.IO.Enumeration
1010
/// Lower level view of FileSystemInfo used for processing and filtering find results.
1111
/// </summary>
1212
public unsafe ref partial struct FileSystemEntry
13-
{
14-
private const int FileNameBufferSize = 256;
13+
{
1514
internal Interop.Sys.DirectoryEntry _directoryEntry;
1615
private FileStatus _status;
1716
private Span<char> _pathBuffer;
1817
private ReadOnlySpan<char> _fullPath;
1918
private ReadOnlySpan<char> _fileName;
20-
private fixed char _fileNameBuffer[FileNameBufferSize];
19+
private fixed char _fileNameBuffer[Interop.Sys.DirectoryEntry.NameBufferSize];
2120
private FileAttributes _initialAttributes;
2221

2322
internal static FileAttributes Initialize(
@@ -112,7 +111,7 @@ public ReadOnlySpan<char> FileName
112111
{
113112
fixed (char* c = _fileNameBuffer)
114113
{
115-
Span<char> buffer = new Span<char>(c, FileNameBufferSize);
114+
Span<char> buffer = new Span<char>(c, Interop.Sys.DirectoryEntry.NameBufferSize);
116115
_fileName = _directoryEntry.GetName(buffer);
117116
}
118117
}

src/System.IO.FileSystem/tests/Enumeration/ErrorHandlingTests.netcoreapp.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
58
using System.IO.Enumeration;
69
using Xunit;
710

@@ -96,5 +99,39 @@ public void DeleteDirectoryAfterOpening()
9699
Assert.Equal(info.FullName, ie.DirectoryFinished);
97100
}
98101
}
102+
103+
[Fact]
104+
public void VariableLengthFileNames_AllCreatableFilesAreEnumerable()
105+
{
106+
DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
107+
var names = new List<string>();
108+
109+
for (int length = 1; length < 10_000; length++) // arbitrarily large limit for the test
110+
{
111+
string name = new string('a', length);
112+
try { File.Create(Path.Join(testDirectory.FullName, name)).Dispose(); }
113+
catch { break; }
114+
names.Add(name);
115+
}
116+
Assert.InRange(names.Count, 1, int.MaxValue);
117+
Assert.Equal(names.OrderBy(n => n), Directory.GetFiles(testDirectory.FullName).Select(n => Path.GetFileName(n)).OrderBy(n => n));
118+
}
119+
120+
[Fact]
121+
public void VariableLengthDirectoryNames_AllCreatableDirectoriesAreEnumerable()
122+
{
123+
DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
124+
var names = new List<string>();
125+
126+
for (int length = 1; length < 10_000; length++) // arbitrarily large limit for the test
127+
{
128+
string name = new string('a', length);
129+
try { Directory.CreateDirectory(Path.Join(testDirectory.FullName, name)); }
130+
catch { break; }
131+
names.Add(name);
132+
}
133+
Assert.InRange(names.Count, 1, int.MaxValue);
134+
Assert.Equal(names.OrderBy(n => n), Directory.GetDirectories(testDirectory.FullName).Select(n => Path.GetFileName(n)).OrderBy(n => n));
135+
}
99136
}
100137
}

src/System.IO.FileSystem/tests/File/Create.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,33 @@ public void LongPathSegment()
168168

169169
#region PlatformSpecific
170170

171+
[Fact]
172+
[PlatformSpecific(TestPlatforms.AnyUnix)]
173+
public void LongDirectoryName()
174+
{
175+
// 255 = NAME_MAX on Linux and macOS
176+
DirectoryInfo path = Directory.CreateDirectory(Path.Combine(GetTestFilePath(), new string('a', 255)));
177+
178+
Assert.True(Directory.Exists(path.FullName));
179+
Directory.Delete(path.FullName);
180+
Assert.False(Directory.Exists(path.FullName));
181+
}
182+
183+
[Fact]
184+
[PlatformSpecific(TestPlatforms.AnyUnix)]
185+
public void LongFileName()
186+
{
187+
// 255 = NAME_MAX on Linux and macOS
188+
var dir = GetTestFilePath();
189+
Directory.CreateDirectory(dir);
190+
var path = Path.Combine(dir, new string('b', 255));
191+
File.Create(path).Dispose();
192+
193+
Assert.True(File.Exists(path));
194+
File.Delete(path);
195+
Assert.False(File.Exists(path));
196+
}
197+
171198
[Fact]
172199
[PlatformSpecific(CaseSensitivePlatforms)]
173200
public void CaseSensitive()

src/System.IO.FileSystem/tests/FileStream/LockUnlock.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ public void OverlappingRegionsFromSameProcess_AllowedOnUnix(long fileLength, lon
153153
}
154154
}
155155

156-
[Theory]
156+
[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/34397
157157
[InlineData(10, 0, 10, 1, 2)]
158158
[InlineData(10, 3, 5, 3, 5)]
159159
[InlineData(10, 3, 5, 3, 4)]

0 commit comments

Comments
 (0)