| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- // Licensed to the .NET Foundation under one or more agreements.
- // 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.Diagnostics;
- using System.Runtime.InteropServices;
- namespace System.IO
- {
- internal static partial class PersistedFiles
- {
- private static string s_userProductDirectory;
- /// <summary>
- /// Get the location of where to persist information for a particular aspect of the framework,
- /// such as "cryptography".
- /// </summary>
- /// <param name="featureName">The directory name for the feature</param>
- /// <returns>A path within the user's home directory for persisting data for the feature</returns>
- internal static string GetUserFeatureDirectory(string featureName)
- {
- if (s_userProductDirectory == null)
- {
- EnsureUserDirectories();
- }
- return Path.Combine(s_userProductDirectory, featureName);
- }
- /// <summary>
- /// Get the location of where to persist information for a particular aspect of a feature of
- /// the framework, such as "x509stores" within "cryptography".
- /// </summary>
- /// <param name="featureName">The directory name for the feature</param>
- /// <param name="subFeatureName">The directory name for the sub-feature</param>
- /// <returns>A path within the user's home directory for persisting data for the sub-feature</returns>
- internal static string GetUserFeatureDirectory(string featureName, string subFeatureName)
- {
- if (s_userProductDirectory == null)
- {
- EnsureUserDirectories();
- }
- return Path.Combine(s_userProductDirectory, featureName, subFeatureName);
- }
- /// <summary>
- /// Get the location of where to persist information for a particular aspect of the framework,
- /// with a lot of hierarchy, such as ["cryptography", "x509stores", "my"]
- /// </summary>
- /// <param name="featurePathParts">A non-empty set of directories to use for the storage hierarchy</param>
- /// <returns>A path within the user's home directory for persisting data for the feature</returns>
- internal static string GetUserFeatureDirectory(params string[] featurePathParts)
- {
- Debug.Assert(featurePathParts != null);
- Debug.Assert(featurePathParts.Length > 0);
- if (s_userProductDirectory == null)
- {
- EnsureUserDirectories();
- }
- return Path.Combine(s_userProductDirectory, Path.Combine(featurePathParts));
- }
- private static void EnsureUserDirectories()
- {
- string userHomeDirectory = GetHomeDirectory();
- if (string.IsNullOrEmpty(userHomeDirectory))
- {
- throw new InvalidOperationException(SR.PersistedFiles_NoHomeDirectory);
- }
- s_userProductDirectory = Path.Combine(
- userHomeDirectory,
- TopLevelHiddenDirectory,
- SecondLevelDirectory);
- }
- /// <summary>Gets the current user's home directory.</summary>
- /// <returns>The path to the home directory, or null if it could not be determined.</returns>
- internal static string GetHomeDirectory()
- {
- // First try to get the user's home directory from the HOME environment variable.
- // This should work in most cases.
- string userHomeDirectory = Environment.GetEnvironmentVariable("HOME");
- if (!string.IsNullOrEmpty(userHomeDirectory))
- return userHomeDirectory;
- // In initialization conditions, however, the "HOME" environment variable may
- // not yet be set. For such cases, consult with the password entry.
- unsafe
- {
- // First try with a buffer that should suffice for 99% of cases.
- // Note that, theoretically, userHomeDirectory may be null in the success case
- // if we simply couldn't find a home directory for the current user.
- // In that case, we pass back the null value and let the caller decide
- // what to do.
- const int BufLen = Interop.Sys.Passwd.InitialBufferSize;
- byte* stackBuf = stackalloc byte[BufLen];
- if (TryGetHomeDirectoryFromPasswd(stackBuf, BufLen, out userHomeDirectory))
- return userHomeDirectory;
- // Fallback to heap allocations if necessary, growing the buffer until
- // we succeed. TryGetHomeDirectory will throw if there's an unexpected error.
- int lastBufLen = BufLen;
- while (true)
- {
- lastBufLen *= 2;
- byte[] heapBuf = new byte[lastBufLen];
- fixed (byte* buf = &heapBuf[0])
- {
- if (TryGetHomeDirectoryFromPasswd(buf, heapBuf.Length, out userHomeDirectory))
- return userHomeDirectory;
- }
- }
- }
- }
- /// <summary>Wrapper for getpwuid_r.</summary>
- /// <param name="buf">The scratch buffer to pass into getpwuid_r.</param>
- /// <param name="bufLen">The length of <paramref name="buf"/>.</param>
- /// <param name="path">The resulting path; null if the user didn't have an entry.</param>
- /// <returns>true if the call was successful (path may still be null); false is a larger buffer is needed.</returns>
- private static unsafe bool TryGetHomeDirectoryFromPasswd(byte* buf, int bufLen, out string path)
- {
- // Call getpwuid_r to get the passwd struct
- Interop.Sys.Passwd passwd;
- int error = Interop.Sys.GetPwUidR(Interop.Sys.GetEUid(), out passwd, buf, bufLen);
- // If the call succeeds, give back the home directory path retrieved
- if (error == 0)
- {
- Debug.Assert(passwd.HomeDirectory != null);
- path = Marshal.PtrToStringAnsi((IntPtr)passwd.HomeDirectory);
- return true;
- }
- // If the current user's entry could not be found, give back null
- // path, but still return true as false indicates the buffer was
- // too small.
- if (error == -1)
- {
- path = null;
- return true;
- }
- var errorInfo = new Interop.ErrorInfo(error);
- // If the call failed because the buffer was too small, return false to
- // indicate the caller should try again with a larger buffer.
- if (errorInfo.Error == Interop.Error.ERANGE)
- {
- path = null;
- return false;
- }
- // Otherwise, fail.
- throw new IOException(errorInfo.GetErrorMessage(), errorInfo.RawErrno);
- }
- }
- }
|