Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit a28c280

Browse files
committed
Merge pull request #2850 from stephentoub/temp_feature_files
Consolidate temp files in Unix implementations
2 parents 626649a + 1dca9c4 commit a28c280

File tree

10 files changed

+137
-49
lines changed

10 files changed

+137
-49
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
namespace System.IO
5+
{
6+
internal static partial class PersistedFiles
7+
{
8+
// Temporary data, /tmp/.dotnet/corefx
9+
// User-persisted data, ~/.dotnet/corefx/
10+
// System-persisted data, /etc/dotnet/corefx/
11+
12+
internal const string TopLevelDirectory = "dotnet";
13+
internal const string TopLevelHiddenDirectory = "." + TopLevelDirectory;
14+
internal const string SecondLevelDirectory = "corefx";
15+
}
16+
}

src/Common/src/System/IO/PersistedFiles.Unix.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,28 @@
55

66
namespace System.IO
77
{
8-
internal static class PersistedFiles
8+
internal static partial class PersistedFiles
99
{
10-
// If we ever need system persisted data, /etc/dotnet/corefx/
11-
private const string TopLevelDirectory = "dotnet";
12-
// User persisted data, ~/.dotnet/corefx/
13-
private const string TopLevelUserDirectory = "." + TopLevelDirectory;
14-
private const string SecondLevelDirectory = "corefx";
15-
10+
private static string s_tempProductDirectory;
1611
private static string s_userProductDirectory;
1712

13+
/// <summary>
14+
/// Get the location of where to store temporary files for a particular aspect of the framework,
15+
/// such as "maps".
16+
/// </summary>
17+
/// <param name="featureName">The directory name for the feature</param>
18+
/// <returns>A path within the temp directory for storing temporary files related to the feature.</returns>
19+
internal static string GetTempFeatureDirectory(string featureName)
20+
{
21+
string path = s_tempProductDirectory;
22+
if (path == null)
23+
{
24+
s_tempProductDirectory = path = Path.Combine(Path.GetTempPath(), TopLevelHiddenDirectory, SecondLevelDirectory);
25+
}
26+
27+
return Path.Combine(path, featureName);
28+
}
29+
1830
/// <summary>
1931
/// Get the location of where to persist information for a particular aspect of the framework,
2032
/// such as "cryptography".
@@ -79,7 +91,7 @@ private static void EnsureUserDirectories()
7991

8092
s_userProductDirectory = Path.Combine(
8193
userHomeDirectory,
82-
TopLevelUserDirectory,
94+
TopLevelHiddenDirectory,
8395
SecondLevelDirectory);
8496
}
8597
}

src/System.IO.MemoryMappedFiles/src/Resources/Strings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,7 @@
216216
<data name="PlatformNotSupported_NamedMaps" xml:space="preserve">
217217
<value>Named maps are not supported.</value>
218218
</data>
219+
<data name="PersistedFiles_NoHomeDirectory" xml:space="preserve">
220+
<value>The home directory of the current user could not be determined.</value>
221+
</data>
219222
</root>

src/System.IO.MemoryMappedFiles/src/System.IO.MemoryMappedFiles.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,12 @@
198198
<Compile Include="$(CommonPath)\Interop\OSX\libc\Interop.SysConfNames.cs">
199199
<Link>Common\Interop\OSX\Interop.SysConfNames.cs</Link>
200200
</Compile>
201+
<Compile Include="$(CommonPath)\System\IO\PersistedFiles.Names.Unix.cs">
202+
<Link>Common\System\IO\PersistedFiles.Names.Unix.cs</Link>
203+
</Compile>
204+
<Compile Include="$(CommonPath)\System\IO\PersistedFiles.Unix.cs">
205+
<Link>Common\System\IO\PersistedFiles.Unix.cs</Link>
206+
</Compile>
201207
</ItemGroup>
202208
<!-- Resource files -->
203209
<ItemGroup>

src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.BackingObject_File.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ public partial class MemoryMappedFile
99
{
1010
private static FileStream CreateSharedBackingObject(Interop.libc.MemoryMappedProtections protections, long capacity)
1111
{
12-
string path = TmpPathPrefix + Guid.NewGuid().ToString("N") + ".tmp";
13-
12+
Directory.CreateDirectory(s_tempMapsDirectory);
13+
string path = Path.Combine(s_tempMapsDirectory, Guid.NewGuid().ToString("N"));
14+
1415
FileAccess access =
1516
(protections & (Interop.libc.MemoryMappedProtections.PROT_READ | Interop.libc.MemoryMappedProtections.PROT_WRITE)) != 0 ? FileAccess.ReadWrite :
1617
(protections & (Interop.libc.MemoryMappedProtections.PROT_WRITE)) != 0 ? FileAccess.Write :
@@ -37,6 +38,6 @@ private static FileStream CreateSharedBackingObject(Interop.libc.MemoryMappedPro
3738
// ---- PAL layer ends here ----
3839
// -----------------------------
3940

40-
private static readonly string TmpPathPrefix = Path.Combine(Path.GetTempPath(), MemoryMapObjectFilePrefix);
41+
private static readonly string s_tempMapsDirectory = PersistedFiles.GetTempFeatureDirectory("maps");
4142
}
4243
}

src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.BackingObject_Memory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ private static FileStream CreateSharedBackingObject(
1111
Interop.libc.MemoryMappedProtections protections, long capacity)
1212
{
1313
// The POSIX shared memory object name must begin with '/'. After that we just want something short and unique.
14-
string mapName = "/" + MemoryMapObjectFilePrefix + Guid.NewGuid().ToString("N");
14+
string mapName = "/corefx_map_" + Guid.NewGuid().ToString("N");
1515

1616
// Determine the flags to use when creating the shared memory object
1717
Interop.libc.OpenFlags flags = (protections & Interop.libc.MemoryMappedProtections.PROT_WRITE) != 0 ?

src/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.Unix.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,7 @@ private static Exception CreateNamedMapsNotSupportedException()
122122
{
123123
return new PlatformNotSupportedException(SR.PlatformNotSupported_NamedMaps);
124124
}
125-
126-
private const string MemoryMapObjectFilePrefix = "corefx_mmf_";
127-
125+
128126
private static FileAccess TranslateProtectionsToFileAccess(Interop.libc.MemoryMappedProtections protections)
129127
{
130128
return

src/System.IO.Pipes/src/System.IO.Pipes.csproj

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,6 @@
139139
<Compile Include="System\IO\Pipes\PipeStreamAsyncResult.cs" />
140140
</ItemGroup>
141141
<ItemGroup Condition=" '$(TargetsUnix)' == 'true' ">
142-
<Compile Include="$(CommonPath)\System\NotImplemented.cs">
143-
<!-- TODO: Remove once implemented -->
144-
<Link>Common\System\NotImplemented.cs</Link>
145-
</Compile>
146142
<Compile Include="Microsoft\Win32\SafeHandles\SafePipeHandle.Unix.cs" />
147143
<Compile Include="System\IO\Pipes\AnonymousPipeServerStream.Unix.cs" />
148144
<Compile Include="System\IO\Pipes\NamedPipeClientStream.Unix.cs" />
@@ -199,6 +195,9 @@
199195
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Stat.cs">
200196
<Link>Common\Interop\Unix\Interop.Stat.cs</Link>
201197
</Compile>
198+
<Compile Include="$(CommonPath)\System\IO\PersistedFiles.Names.Unix.cs">
199+
<Link>Common\System\IO\PersistedFiles.Names.Unix.cs</Link>
200+
</Compile>
202201
</ItemGroup>
203202
<!-- Linux -->
204203
<ItemGroup Condition="'$(TargetsLinux)' == 'true'">

src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs

Lines changed: 80 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ public abstract partial class PipeStream : Stream
2020
// Windows, but can't assume a valid handle on Unix.
2121
internal const bool CheckOperationsRequiresSetHandle = false;
2222

23-
private static readonly string PipeDirectoryPath = Path.Combine(Path.GetTempPath(), "corefxnamedpipes");
24-
2523
internal static string GetPipePath(string serverName, string pipeName)
2624
{
2725
if (serverName != "." && serverName != Interop.libc.gethostname())
@@ -38,35 +36,8 @@ internal static string GetPipePath(string serverName, string pipeName)
3836
throw new PlatformNotSupportedException();
3937
}
4038

41-
// Make sure we have the directory in which to put the pipe paths
42-
while (true)
43-
{
44-
int result = Interop.libc.mkdir(PipeDirectoryPath, (int)Interop.libc.Permissions.S_IRWXU);
45-
if (result >= 0)
46-
{
47-
// directory created
48-
break;
49-
}
50-
51-
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
52-
if (errorInfo.Error == Interop.Error.EINTR)
53-
{
54-
// I/O was interrupted, try again
55-
continue;
56-
}
57-
else if (errorInfo.Error == Interop.Error.EEXIST)
58-
{
59-
// directory already exists
60-
break;
61-
}
62-
else
63-
{
64-
throw Interop.GetExceptionForIoErrno(errorInfo, PipeDirectoryPath, isDirectory: true);
65-
}
66-
}
67-
6839
// Return the pipe path
69-
return Path.Combine(PipeDirectoryPath, pipeName);
40+
return Path.Combine(EnsurePipeDirectoryPath(), pipeName);
7041
}
7142

7243
/// <summary>Throws an exception if the supplied handle does not represent a valid pipe.</summary>
@@ -255,6 +226,85 @@ public virtual PipeTransmissionMode ReadMode
255226
// ---- PAL layer ends here ----
256227
// -----------------------------
257228

229+
private static string s_pipeDirectoryPath;
230+
231+
private static string EnsurePipeDirectoryPath()
232+
{
233+
const string PipesFeatureName = "pipes";
234+
235+
// Ideally this would simply use PersistedFiles.GetTempFeatureDirectory(PipesFeatureName) and then
236+
// Directory.CreateDirectory to ensure it exists. But this assembly doesn't reference System.IO.FileSystem.
237+
// As such, we'd be calling GetTempFeatureDirectory, only to then need to parse it in order
238+
// to create each of the individual directories as part of the path. We instead access the named portions
239+
// of the path directly and do the building of the path and directory structure manually.
240+
241+
// First ensure we know what the full path should be, e.g. /tmp/.dotnet/corefx/pipes/
242+
string fullPath = s_pipeDirectoryPath;
243+
string tempPath = null;
244+
if (fullPath == null)
245+
{
246+
tempPath = Path.GetTempPath();
247+
fullPath = Path.Combine(tempPath, PersistedFiles.TopLevelHiddenDirectory, PersistedFiles.SecondLevelDirectory, PipesFeatureName);
248+
s_pipeDirectoryPath = fullPath;
249+
}
250+
251+
// Then create the directory if it doesn't already exist. If we get any error back from stat,
252+
// just proceed to build up the directory, failing in the CreateDirectory calls if there's some
253+
// problem. Similarly, it's possible stat succeeds but the path is a file rather than directory; we'll
254+
// call that success for now and let this fail later when the code tries to create a file in this "directory"
255+
// (we don't want to overwrite/delete whatever that unknown file may be, and this is similar to other cases
256+
// we can't control where the file system is manipulated concurrently with and separately from this code).
257+
Interop.Sys.FileStatus ignored;
258+
bool pathExists = Interop.Sys.Stat(fullPath, out ignored) == 0;
259+
if (!pathExists)
260+
{
261+
// We need to build up the directory manually. Ensure we have the temp directory in which
262+
// we'll create the structure, e.g. /tmp/
263+
if (tempPath == null)
264+
{
265+
tempPath = Path.GetTempPath();
266+
}
267+
Debug.Assert(Interop.Sys.Stat(tempPath, out ignored) == 0, "Path.GetTempPath() directory could not be accessed");
268+
269+
// Create /tmp/.dotnet/ if it doesn't exist.
270+
string partialPath = Path.Combine(tempPath, PersistedFiles.TopLevelHiddenDirectory);
271+
CreateDirectory(partialPath);
272+
273+
// Create /tmp/.dotnet/corefx/ if it doesn't exist
274+
partialPath = Path.Combine(partialPath, PersistedFiles.SecondLevelDirectory);
275+
CreateDirectory(partialPath);
276+
277+
// Create /tmp/.dotnet/corefx/pipes/ if it doesn't exist
278+
CreateDirectory(fullPath);
279+
}
280+
281+
return fullPath;
282+
}
283+
284+
private static void CreateDirectory(string directoryPath)
285+
{
286+
while (true)
287+
{
288+
int result = Interop.libc.mkdir(directoryPath, (int)Interop.libc.Permissions.S_IRWXU);
289+
290+
// If successful created, we're done.
291+
if (result >= 0)
292+
return;
293+
294+
// If the directory already exists, consider it a success.
295+
Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
296+
if (errorInfo.Error == Interop.Error.EEXIST)
297+
return;
298+
299+
// If the I/O was interrupted, try again.
300+
if (errorInfo.Error == Interop.Error.EINTR)
301+
continue;
302+
303+
// Otherwise, fail.
304+
throw Interop.GetExceptionForIoErrno(errorInfo, directoryPath, isDirectory: true);
305+
}
306+
}
307+
258308
internal static Interop.libc.OpenFlags TranslateFlags(PipeDirection direction, PipeOptions options, HandleInheritability inheritability)
259309
{
260310
// Translate direction

src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,9 @@
219219
<Compile Include="$(CommonPath)\System\IO\PersistedFiles.Unix.cs">
220220
<Link>Common\System\IO\PersistedFiles.Unix.cs</Link>
221221
</Compile>
222+
<Compile Include="$(CommonPath)\System\IO\PersistedFiles.Names.Unix.cs">
223+
<Link>Common\System\IO\PersistedFiles.Names.Unix.cs</Link>
224+
</Compile>
222225
</ItemGroup>
223226
<ItemGroup>
224227
<None Include="project.json" />

0 commit comments

Comments
 (0)