|
4 | 4 |
|
5 | 5 | using System; |
6 | 6 | using System.Runtime.InteropServices; |
| 7 | +using System.Threading; |
7 | 8 | using Microsoft.Win32.SafeHandles; |
8 | 9 |
|
9 | 10 | internal static partial class Interop |
10 | 11 | { |
11 | 12 | internal static partial class Sys |
12 | 13 | { |
13 | | - private static readonly int s_direntSize = GetDirentSize(); |
| 14 | + private static readonly int s_readBufferSize = GetReadDirRBufferSize(); |
14 | 15 |
|
15 | 16 | internal enum NodeType : int |
16 | 17 | { |
@@ -42,29 +43,51 @@ internal struct DirectoryEntry |
42 | 43 | [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_OpenDir", SetLastError = true)] |
43 | 44 | internal static extern Microsoft.Win32.SafeHandles.SafeDirectoryHandle OpenDir(string path); |
44 | 45 |
|
45 | | - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetDirentSize", SetLastError = false)] |
46 | | - internal static extern int GetDirentSize(); |
| 46 | + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetReadDirRBufferSize", SetLastError = false)] |
| 47 | + internal static extern int GetReadDirRBufferSize(); |
47 | 48 |
|
48 | 49 | [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ReadDirR", SetLastError = false)] |
49 | | - private static extern unsafe int ReadDirR(SafeDirectoryHandle dir, byte* buffer, int bufferSize, out InternalDirectoryEntry outputEntry); |
| 50 | + private static extern unsafe int ReadDirR(IntPtr dir, byte* buffer, int bufferSize, out InternalDirectoryEntry outputEntry); |
50 | 51 |
|
51 | 52 | [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_CloseDir", SetLastError = true)] |
52 | 53 | internal static extern int CloseDir(IntPtr dir); |
53 | 54 |
|
54 | 55 | // The calling pattern for ReadDir is described in src/Native/System.Native/pal_readdir.cpp |
55 | 56 | internal static int ReadDir(SafeDirectoryHandle dir, out DirectoryEntry outputEntry) |
56 | 57 | { |
57 | | - unsafe |
| 58 | + bool addedRef = false; |
| 59 | + try |
58 | 60 | { |
59 | | - // To reduce strcpys, alloc a buffer here and get the result from OS, then copy it over for the caller. |
60 | | - byte* buffer = stackalloc byte[s_direntSize]; |
61 | | - InternalDirectoryEntry temp; |
62 | | - int ret = ReadDirR(dir, buffer, s_direntSize, out temp); |
63 | | - outputEntry = ret == 0 ? |
64 | | - new DirectoryEntry() { InodeName = GetDirectoryEntryName(temp), InodeType = temp.InodeType } : |
65 | | - default(DirectoryEntry); |
| 61 | + // We avoid a native string copy into InternalDirectoryEntry. |
| 62 | + // - If the platform suppors reading into a buffer, the data is read directly into the buffer. The |
| 63 | + // data can be read as long as the buffer is valid. |
| 64 | + // - If the platform does not support reading into a buffer, the information returned in |
| 65 | + // InternalDirectoryEntry points to native memory owned by the SafeDirectoryHandle. The data is only |
| 66 | + // valid until the next call to CloseDir/ReadDir. We extend the reference until we have copied all data |
| 67 | + // to ensure it does not become invalid by a CloseDir; and we copy the data so our caller does not |
| 68 | + // use the native memory held by the SafeDirectoryHandle. |
| 69 | + dir.DangerousAddRef(ref addedRef); |
66 | 70 |
|
67 | | - return ret; |
| 71 | + unsafe |
| 72 | + { |
| 73 | + // s_readBufferSize is zero when the native implementation does not support reading into a buffer. |
| 74 | + byte* buffer = stackalloc byte[s_readBufferSize]; |
| 75 | + InternalDirectoryEntry temp; |
| 76 | + int ret = ReadDirR(dir.DangerousGetHandle(), buffer, s_readBufferSize, out temp); |
| 77 | + // We copy data into DirectoryEntry to ensure there are no dangling references. |
| 78 | + outputEntry = ret == 0 ? |
| 79 | + new DirectoryEntry() { InodeName = GetDirectoryEntryName(temp), InodeType = temp.InodeType } : |
| 80 | + default(DirectoryEntry); |
| 81 | + |
| 82 | + return ret; |
| 83 | + } |
| 84 | + } |
| 85 | + finally |
| 86 | + { |
| 87 | + if (addedRef) |
| 88 | + { |
| 89 | + dir.DangerousRelease(); |
| 90 | + } |
68 | 91 | } |
69 | 92 | } |
70 | 93 |
|
|
0 commit comments