diff options
author | Steve Pfister <steveisok@users.noreply.github.com> | 2019-12-09 16:01:47 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-09 16:01:47 +0300 |
commit | f3815a94011e12cbce7ce2e41328f5faa54cad0e (patch) | |
tree | 5e5d6cc9379c762731e6da92fd495a541e71df99 | |
parent | 5951d0ebe97b523b7110d93c250c062c952201d1 (diff) |
Backport FixPathLength 255 PR
Fixes https://github.com/mono/mono/issues/17948
Pulled in upstream change https://github.com/dotnet/corefx/pull/34389
5 files changed, 73 insertions, 10 deletions
diff --git a/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.ReadDir.cs b/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.ReadDir.cs index 9e32f1ccc2..5888c80fbc 100644 --- a/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.ReadDir.cs +++ b/src/Common/src/CoreLib/Interop/Unix/System.Native/Interop.ReadDir.cs @@ -30,21 +30,21 @@ internal static partial class Interop internal byte* Name; internal int NameLength; internal NodeType InodeType; - internal const int NameBufferSize = 256; + internal const int NameBufferSize = 256; // sizeof(dirent->d_name) == NAME_MAX + 1 internal ReadOnlySpan<char> GetName(Span<char> buffer) { - Debug.Assert(buffer.Length >= Encoding.UTF8.GetMaxCharCount(NameBufferSize - 1), "should have enough space for the max file name"); + // -1 for null terminator (buffer will not include one), + // and -1 because GetMaxCharCount pessimistically assumes the buffer may start with a partial surrogate + Debug.Assert(buffer.Length >= Encoding.UTF8.GetMaxCharCount(NameBufferSize - 1 - 1)); Debug.Assert(Name != null, "should not have a null name"); ReadOnlySpan<byte> nameBytes = (NameLength == -1) // In this case the struct was allocated via struct dirent *readdir(DIR *dirp); - ? new ReadOnlySpan<byte>(Name, new ReadOnlySpan<byte>(Name, NameBufferSize - 1).IndexOf<byte>(0)) + ? new ReadOnlySpan<byte>(Name, new ReadOnlySpan<byte>(Name, NameBufferSize).IndexOf<byte>(0)) : new ReadOnlySpan<byte>(Name, NameLength); Debug.Assert(nameBytes.Length > 0, "we shouldn't have gotten a garbage value from the OS"); - if (nameBytes.Length == 0) - return buffer.Slice(0, 0); int charCount = Encoding.UTF8.GetChars(nameBytes, buffer); ReadOnlySpan<char> value = buffer.Slice(0, charCount); diff --git a/src/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Unix.cs b/src/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Unix.cs index 1139d2f3f5..728275bf95 100644 --- a/src/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Unix.cs +++ b/src/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemEntry.Unix.cs @@ -10,14 +10,13 @@ namespace System.IO.Enumeration /// Lower level view of FileSystemInfo used for processing and filtering find results. /// </summary> public unsafe ref partial struct FileSystemEntry - { - private const int FileNameBufferSize = 256; + { internal Interop.Sys.DirectoryEntry _directoryEntry; private FileStatus _status; private Span<char> _pathBuffer; private ReadOnlySpan<char> _fullPath; private ReadOnlySpan<char> _fileName; - private fixed char _fileNameBuffer[FileNameBufferSize]; + private fixed char _fileNameBuffer[Interop.Sys.DirectoryEntry.NameBufferSize]; private FileAttributes _initialAttributes; internal static FileAttributes Initialize( @@ -112,7 +111,7 @@ namespace System.IO.Enumeration { fixed (char* c = _fileNameBuffer) { - Span<char> buffer = new Span<char>(c, FileNameBufferSize); + Span<char> buffer = new Span<char>(c, Interop.Sys.DirectoryEntry.NameBufferSize); _fileName = _directoryEntry.GetName(buffer); } } diff --git a/src/System.IO.FileSystem/tests/Enumeration/ErrorHandlingTests.netcoreapp.cs b/src/System.IO.FileSystem/tests/Enumeration/ErrorHandlingTests.netcoreapp.cs index e4c85d831e..60bc3d9224 100644 --- a/src/System.IO.FileSystem/tests/Enumeration/ErrorHandlingTests.netcoreapp.cs +++ b/src/System.IO.FileSystem/tests/Enumeration/ErrorHandlingTests.netcoreapp.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Collections.Generic; +using System.Linq; using System.IO.Enumeration; using Xunit; @@ -96,5 +99,39 @@ namespace System.IO.Tests Assert.Equal(info.FullName, ie.DirectoryFinished); } } + + [Fact] + public void VariableLengthFileNames_AllCreatableFilesAreEnumerable() + { + DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath()); + var names = new List<string>(); + + for (int length = 1; length < 10_000; length++) // arbitrarily large limit for the test + { + string name = new string('a', length); + try { File.Create(Path.Join(testDirectory.FullName, name)).Dispose(); } + catch { break; } + names.Add(name); + } + Assert.InRange(names.Count, 1, int.MaxValue); + Assert.Equal(names.OrderBy(n => n), Directory.GetFiles(testDirectory.FullName).Select(n => Path.GetFileName(n)).OrderBy(n => n)); + } + + [Fact] + public void VariableLengthDirectoryNames_AllCreatableDirectoriesAreEnumerable() + { + DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath()); + var names = new List<string>(); + + for (int length = 1; length < 10_000; length++) // arbitrarily large limit for the test + { + string name = new string('a', length); + try { Directory.CreateDirectory(Path.Join(testDirectory.FullName, name)); } + catch { break; } + names.Add(name); + } + Assert.InRange(names.Count, 1, int.MaxValue); + Assert.Equal(names.OrderBy(n => n), Directory.GetDirectories(testDirectory.FullName).Select(n => Path.GetFileName(n)).OrderBy(n => n)); + } } } diff --git a/src/System.IO.FileSystem/tests/File/Create.cs b/src/System.IO.FileSystem/tests/File/Create.cs index c910e735b3..be16528e76 100644 --- a/src/System.IO.FileSystem/tests/File/Create.cs +++ b/src/System.IO.FileSystem/tests/File/Create.cs @@ -169,6 +169,33 @@ namespace System.IO.Tests #region PlatformSpecific [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] + public void LongDirectoryName() + { + // 255 = NAME_MAX on Linux and macOS + DirectoryInfo path = Directory.CreateDirectory(Path.Combine(GetTestFilePath(), new string('a', 255))); + + Assert.True(Directory.Exists(path.FullName)); + Directory.Delete(path.FullName); + Assert.False(Directory.Exists(path.FullName)); + } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] + public void LongFileName() + { + // 255 = NAME_MAX on Linux and macOS + var dir = GetTestFilePath(); + Directory.CreateDirectory(dir); + var path = Path.Combine(dir, new string('b', 255)); + File.Create(path).Dispose(); + + Assert.True(File.Exists(path)); + File.Delete(path); + Assert.False(File.Exists(path)); + } + + [Fact] [PlatformSpecific(CaseSensitivePlatforms)] public void CaseSensitive() { diff --git a/src/System.IO.FileSystem/tests/FileStream/LockUnlock.cs b/src/System.IO.FileSystem/tests/FileStream/LockUnlock.cs index 2d7207e8d5..68e7a56336 100644 --- a/src/System.IO.FileSystem/tests/FileStream/LockUnlock.cs +++ b/src/System.IO.FileSystem/tests/FileStream/LockUnlock.cs @@ -153,7 +153,7 @@ namespace System.IO.Tests } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsSubsystemForLinux))] // https://github.com/dotnet/corefx/issues/34397 [InlineData(10, 0, 10, 1, 2)] [InlineData(10, 3, 5, 3, 5)] [InlineData(10, 3, 5, 3, 4)] |