public void PathStartSkipTest(string path, int expected)
 {
     Assert.Equal(expected, PathInternal.PathStartSkip(path));
 }
Exemplo n.º 2
0
        /// <summary>
        /// Returns 0 on success, otherwise a Win32 error code.  Note that
        /// classes should use -1 as the uninitialized state for dataInitialized.
        /// </summary>
        /// <param name="path">The file path from which the file attribute information will be filled.</param>
        /// <param name="data">A struct that will contain the attribute information.</param>
        /// <param name="returnErrorOnNotFound">Return the error code for not found errors?</param>
        internal static int FillAttributeInfo(string path, ref Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data, bool returnErrorOnNotFound)
        {
            int errorCode = Interop.Errors.ERROR_SUCCESS;

            // Neither GetFileAttributes or FindFirstFile like trailing separators
            path = PathInternal.TrimEndingDirectorySeparator(path);

            using (DisableMediaInsertionPrompt.Create())
            {
                if (!Interop.Kernel32.GetFileAttributesEx(path, Interop.Kernel32.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data))
                {
                    errorCode = Marshal.GetLastWin32Error();

                    if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND &&
                        errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND &&
                        errorCode != Interop.Errors.ERROR_NOT_READY &&
                        errorCode != Interop.Errors.ERROR_INVALID_NAME &&
                        errorCode != Interop.Errors.ERROR_BAD_PATHNAME &&
                        errorCode != Interop.Errors.ERROR_BAD_NETPATH &&
                        errorCode != Interop.Errors.ERROR_BAD_NET_NAME &&
                        errorCode != Interop.Errors.ERROR_INVALID_PARAMETER &&
                        errorCode != Interop.Errors.ERROR_NETWORK_UNREACHABLE &&
                        errorCode != Interop.Errors.ERROR_NETWORK_ACCESS_DENIED &&
                        errorCode != Interop.Errors.ERROR_INVALID_HANDLE &&         // eg from \\.\CON
                        errorCode != Interop.Errors.ERROR_FILENAME_EXCED_RANGE      // Path is too long
                        )
                    {
                        // Assert so we can track down other cases (if any) to add to our test suite
                        Debug.Assert(errorCode == Interop.Errors.ERROR_ACCESS_DENIED || errorCode == Interop.Errors.ERROR_SHARING_VIOLATION,
                                     $"Unexpected error code getting attributes {errorCode} from path {path}");

                        // Files that are marked for deletion will not let you GetFileAttributes,
                        // ERROR_ACCESS_DENIED is given back without filling out the data struct.
                        // FindFirstFile, however, will. Historically we always gave back attributes
                        // for marked-for-deletion files.
                        //
                        // Another case where enumeration works is with special system files such as
                        // pagefile.sys that give back ERROR_SHARING_VIOLATION on GetAttributes.
                        //
                        // Ideally we'd only try again for known cases due to the potential performance
                        // hit. The last attempt to do so baked for nearly a year before we found the
                        // pagefile.sys case. As such we're probably stuck filtering out specific
                        // cases that we know we don't want to retry on.

                        var findData = new Interop.Kernel32.WIN32_FIND_DATA();
                        using (SafeFindHandle handle = Interop.Kernel32.FindFirstFile(path, ref findData))
                        {
                            if (handle.IsInvalid)
                            {
                                errorCode = Marshal.GetLastWin32Error();
                            }
                            else
                            {
                                errorCode = Interop.Errors.ERROR_SUCCESS;
                                data.PopulateFrom(ref findData);
                            }
                        }
                    }
                }
            }

            if (errorCode != Interop.Errors.ERROR_SUCCESS && !returnErrorOnNotFound)
            {
                switch (errorCode)
                {
                case Interop.Errors.ERROR_FILE_NOT_FOUND:
                case Interop.Errors.ERROR_PATH_NOT_FOUND:
                case Interop.Errors.ERROR_NOT_READY:     // Removable media not ready
                    // Return default value for backward compatibility
                    data.dwFileAttributes = -1;
                    return(Interop.Errors.ERROR_SUCCESS);
                }
            }

            return(errorCode);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Gets reparse point information associated to <paramref name="linkPath"/>.
        /// </summary>
        /// <returns>The immediate link target, absolute or relative or null if the file is not a supported link.</returns>
        internal static unsafe string?GetImmediateLinkTarget(string linkPath, bool isDirectory, bool throwOnError, bool returnFullPath)
        {
            using SafeFileHandle handle = OpenSafeFileHandle(linkPath,
                                                             Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS |
                                                             Interop.Kernel32.FileOperations.FILE_FLAG_OPEN_REPARSE_POINT);

            if (handle.IsInvalid)
            {
                if (!throwOnError)
                {
                    return(null);
                }

                int error = Marshal.GetLastWin32Error();
                // File not found doesn't make much sense coming from a directory.
                if (isDirectory && error == Interop.Errors.ERROR_FILE_NOT_FOUND)
                {
                    error = Interop.Errors.ERROR_PATH_NOT_FOUND;
                }

                throw Win32Marshal.GetExceptionForWin32Error(error, linkPath);
            }

            byte[] buffer = ArrayPool <byte> .Shared.Rent(Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE);

            try
            {
                bool success = Interop.Kernel32.DeviceIoControl(
                    handle,
                    dwIoControlCode: Interop.Kernel32.FSCTL_GET_REPARSE_POINT,
                    lpInBuffer: IntPtr.Zero,
                    nInBufferSize: 0,
                    lpOutBuffer: buffer,
                    nOutBufferSize: Interop.Kernel32.MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
                    out _,
                    IntPtr.Zero);

                if (!success)
                {
                    if (!throwOnError)
                    {
                        return(null);
                    }

                    int error = Marshal.GetLastWin32Error();
                    // The file or directory is not a reparse point.
                    if (error == Interop.Errors.ERROR_NOT_A_REPARSE_POINT)
                    {
                        return(null);
                    }

                    throw Win32Marshal.GetExceptionForWin32Error(error, linkPath);
                }

                Span <byte> bufferSpan = new(buffer);
                success = MemoryMarshal.TryRead(bufferSpan, out Interop.Kernel32.REPARSE_DATA_BUFFER rdb);
                Debug.Assert(success);

                // Only symbolic links are supported at the moment.
                if ((rdb.ReparseTag & Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_SYMLINK) == 0)
                {
                    return(null);
                }

                // We use PrintName instead of SubstitutneName given that we don't want to return a NT path when the link wasn't created with such NT path.
                // Unlike SubstituteName and GetFinalPathNameByHandle(), PrintName doesn't start with a prefix.
                // Another nuance is that SubstituteName does not contain redundant path segments while PrintName does.
                // PrintName can ONLY return a NT path if the link was created explicitly targeting a file/folder in such way. e.g: mklink /D linkName \??\C:\path\to\target.
                int printNameNameOffset = sizeof(Interop.Kernel32.REPARSE_DATA_BUFFER) + rdb.ReparseBufferSymbolicLink.PrintNameOffset;
                int printNameNameLength = rdb.ReparseBufferSymbolicLink.PrintNameLength;

                Span <char> targetPath = MemoryMarshal.Cast <byte, char>(bufferSpan.Slice(printNameNameOffset, printNameNameLength));
                Debug.Assert((rdb.ReparseBufferSymbolicLink.Flags & Interop.Kernel32.SYMLINK_FLAG_RELATIVE) == 0 || !PathInternal.IsExtended(targetPath));

                if (returnFullPath && (rdb.ReparseBufferSymbolicLink.Flags & Interop.Kernel32.SYMLINK_FLAG_RELATIVE) != 0)
                {
                    // Target path is relative and is for ResolveLinkTarget(), we need to append the link directory.
                    return(Path.Join(Path.GetDirectoryName(linkPath.AsSpan()), targetPath));
                }

                return(targetPath.ToString());
            }
            finally
            {
                ArrayPool <byte> .Shared.Return(buffer);
            }
        }
Exemplo n.º 4
0
 public void EnsureExtendedPrefixTest(string path, string expected)
 {
     Assert.Equal(expected, PathInternal.EnsureExtendedPrefix(path));
 }
Exemplo n.º 5
0
 public void IsDeviceTest(string path, bool expected)
 {
     Assert.Equal(expected, PathInternal.IsDevice(path));
 }
Exemplo n.º 6
0
 internal static bool RemoveDirectory(string path)
 {
     path = PathInternal.EnsureExtendedPrefixOverMaxPath(path);
     return(RemoveDirectoryPrivate(path));
 }
Exemplo n.º 7
0
        } // PrepareDataForSetup

        private static Object Setup(Object arg)
        {
            Contract.Requires(arg != null && arg is Object[]);
            Contract.Requires(((Object[])arg).Length >= 8);

            Object[]       args         = (Object[])arg;
            String         friendlyName = (String)args[0];
            AppDomainSetup setup        = (AppDomainSetup)args[1];

            string[] propertyNames  = (string[])args[2]; // can contain null elements
            string[] propertyValues = (string[])args[3]; // can contain null elements

            AppDomain      ad       = AppDomain.CurrentDomain;
            AppDomainSetup newSetup = new AppDomainSetup(setup, false);

            if (propertyNames != null && propertyValues != null)
            {
                for (int i = 0; i < propertyNames.Length; i++)
                {
                    // We want to set native dll probing directories before any P/Invokes have a
                    // chance to fire. The Path class, for one, has P/Invokes.
                    if (propertyNames[i] == "NATIVE_DLL_SEARCH_DIRECTORIES")
                    {
                        if (propertyValues[i] == null)
                        {
                            throw new ArgumentNullException("NATIVE_DLL_SEARCH_DIRECTORIES");
                        }

                        string paths = propertyValues[i];
                        if (paths.Length == 0)
                        {
                            break;
                        }

                        nSetNativeDllSearchDirectories(paths);
                    }
                }

                for (int i = 0; i < propertyNames.Length; i++)
                {
                    if (propertyNames[i] == "APPBASE") // make sure in sync with Fusion
                    {
                        if (propertyValues[i] == null)
                        {
                            throw new ArgumentNullException("APPBASE");
                        }

                        if (PathInternal.IsPartiallyQualified(propertyValues[i]))
                        {
                            throw new ArgumentException(SR.Argument_AbsolutePathRequired);
                        }

                        newSetup.ApplicationBase = NormalizePath(propertyValues[i], fullCheck: true);
                    }
                    else if (propertyNames[i] == "TRUSTED_PLATFORM_ASSEMBLIES" ||
                             propertyNames[i] == "PLATFORM_RESOURCE_ROOTS" ||
                             propertyNames[i] == "APP_PATHS" ||
                             propertyNames[i] == "APP_NI_PATHS")
                    {
                        string values = propertyValues[i];
                        if (values == null)
                        {
                            throw new ArgumentNullException(propertyNames[i]);
                        }

                        ad.SetData(propertyNames[i], NormalizeAppPaths(values));
                    }
                    else if (propertyNames[i] != null)
                    {
                        ad.SetData(propertyNames[i], propertyValues[i]);     // just propagate
                    }
                }
            }

            ad.SetupFusionStore(newSetup, null); // makes FusionStore a ref to newSetup

            // technically, we don't need this, newSetup refers to the same object as FusionStore
            // but it's confusing since it isn't immediately obvious whether we have a ref or a copy
            AppDomainSetup adSetup = ad.FusionStore;

            // set up the friendly name
            ad.nSetupFriendlyName(friendlyName);

            ad.CreateAppDomainManager(); // could modify FusionStore's object

            return(null);
        }
        public Path Run <TMod>(LogLevel pathfindingLogLevel, Vector3 fromPoint, Vector3 toPoint, Constraint constraint, Graph graph, TMod pathModifier, int threadIndex = 0,
                               bool burstEnabled = true, bool cacheEnabled = false) where TMod : struct, IPathModifier
        {
            var path         = new Path();
            var pathResult   = new PathInternal();
            var navMeshGraph = (NavMeshGraph)graph;

            var areas = -1;

            if (constraint.checkArea == true)
            {
                areas = (int)constraint.areaMask;
            }

            System.Diagnostics.Stopwatch swPath = null;
            if ((pathfindingLogLevel & LogLevel.Path) != 0)
            {
                swPath = System.Diagnostics.Stopwatch.StartNew();
            }

            var statLength  = 0;
            var statVisited = 0;

            var query = new UnityEngine.Experimental.AI.NavMeshQuery(UnityEngine.Experimental.AI.NavMeshWorld.GetDefaultWorld(), Unity.Collections.Allocator.TempJob, PathfindingNavMeshProcessor.POOL_SIZE);

            if (burstEnabled == true)
            {
                var results     = new Unity.Collections.NativeArray <UnityEngine.Experimental.AI.NavMeshLocation>(PathfindingNavMeshProcessor.MAX_PATH_SIZE, Unity.Collections.Allocator.TempJob);
                var pathResults = new Unity.Collections.NativeArray <int>(2, Unity.Collections.Allocator.TempJob);
                var job         = new BuildPathJob()
                {
                    query       = query,
                    fromPoint   = fromPoint,
                    toPoint     = toPoint,
                    agentTypeId = navMeshGraph.agentTypeId,
                    areas       = areas,
                    pathResults = pathResults,
                    results     = results,
                };
                job.Schedule().Complete();
                var pathStatus  = (UnityEngine.Experimental.AI.PathQueryStatus)pathResults[0];
                var cornerCount = pathResults[1];
                pathResults.Dispose();

                if ((pathStatus & UnityEngine.Experimental.AI.PathQueryStatus.Success) != 0)
                {
                    if (cornerCount >= 2)
                    {
                        path.navMeshPoints = PoolListCopyable <Vector3> .Spawn(cornerCount);

                        for (var i = 0; i < cornerCount; ++i)
                        {
                            path.navMeshPoints.Add(results[i].position);
                        }

                        if ((pathfindingLogLevel & LogLevel.Path) != 0)
                        {
                            var hash = 0;
                            for (var i = 0; i < cornerCount; ++i)
                            {
                                hash ^= (int)(results[i].position.x * 1000000f);
                            }

                            UnityEngine.Debug.Log("Path hash X: " + hash);

                            hash = 0;
                            for (var i = 0; i < cornerCount; ++i)
                            {
                                hash ^= (int)(results[i].position.y * 1000000f);
                            }

                            UnityEngine.Debug.Log("Path hash Y: " + hash);

                            hash = 0;
                            for (var i = 0; i < cornerCount; ++i)
                            {
                                hash ^= (int)(results[i].position.z * 1000000f);
                            }

                            UnityEngine.Debug.Log("Path hash Z: " + hash);
                        }

                        if ((pathStatus & UnityEngine.Experimental.AI.PathQueryStatus.PartialResult) != 0)
                        {
                            path.result = PathCompleteState.CompletePartial;
                        }
                        else
                        {
                            path.result = PathCompleteState.Complete;
                        }
                    }
                    else
                    {
                        path.result = PathCompleteState.NotExist;
                    }
                }
                results.Dispose();
                query.Dispose();
                return(path);
            }

            UnityEngine.AI.NavMesh.SamplePosition(fromPoint, out var hitFrom, 1000f, new UnityEngine.AI.NavMeshQueryFilter()
            {
                agentTypeID = navMeshGraph.agentTypeId,
                areaMask    = areas,
            });
            fromPoint = hitFrom.position;
            var from = query.MapLocation(fromPoint, Vector3.one * 10f, navMeshGraph.agentTypeId, areas);

            if (from.polygon.IsNull() == true)
            {
                return(path);
            }

            UnityEngine.AI.NavMesh.SamplePosition(toPoint, out var hitTo, 1000f, new UnityEngine.AI.NavMeshQueryFilter()
            {
                agentTypeID = navMeshGraph.agentTypeId,
                areaMask    = areas,
            });
            toPoint = hitTo.position;
            var to = query.MapLocation(toPoint, Vector3.one * 10f, navMeshGraph.agentTypeId, areas);

            if (to.polygon.IsNull() == true)
            {
                return(path);
            }

            var marker = new Unity.Profiling.ProfilerMarker("PathfindingNavMeshProcessor::Query::BuildPath");

            marker.Begin();
            query.BeginFindPath(from, to, areas);
            query.UpdateFindPath(PathfindingNavMeshProcessor.MAX_ITERATIONS, out var performed);
            marker.End();
            statVisited = performed;

            var result = query.EndFindPath(out var pathSize);

            if ((result & UnityEngine.Experimental.AI.PathQueryStatus.Success) != 0)
            {
                var pathInternal = new Unity.Collections.NativeArray <UnityEngine.Experimental.AI.PolygonId>(pathSize, Unity.Collections.Allocator.Persistent);
                query.GetPathResult(pathInternal);

                var markerFindStraight = new Unity.Profiling.ProfilerMarker("PathfindingNavMeshProcessor::Query::FindStraightPath");
                markerFindStraight.Begin();

                var straightPathFlags = new Unity.Collections.NativeArray <StraightPathFlags>(PathfindingNavMeshProcessor.MAX_PATH_SIZE, Unity.Collections.Allocator.Persistent);
                var vertexSide        = new Unity.Collections.NativeArray <float>(PathfindingNavMeshProcessor.MAX_PATH_SIZE, Unity.Collections.Allocator.Persistent);
                var results           = new Unity.Collections.NativeArray <UnityEngine.Experimental.AI.NavMeshLocation>(PathfindingNavMeshProcessor.MAX_PATH_SIZE, Unity.Collections.Allocator.Persistent);
                var resultStatus      = new Unity.Collections.NativeArray <int>(2, Unity.Collections.Allocator.TempJob);
                var job = new FindStraightPathJob()
                {
                    query             = query,
                    from              = from,
                    to                = to,
                    pathInternal      = pathInternal,
                    pathSize          = pathSize,
                    results           = results,
                    straightPathFlags = straightPathFlags,
                    vertexSide        = vertexSide,
                    resultStatus      = resultStatus,
                };
                job.Schedule().Complete();

                var pathStatus  = (UnityEngine.Experimental.AI.PathQueryStatus)job.resultStatus[0];
                var cornerCount = job.resultStatus[1];
                resultStatus.Dispose();

                statLength = cornerCount;

                markerFindStraight.End();

                if (pathStatus == UnityEngine.Experimental.AI.PathQueryStatus.Success)
                {
                    /*for (int i = 1; i < cornerCount; ++i) {
                     *
                     *  Gizmos.color = Color.green;
                     *  Gizmos.DrawLine(results[i].position, results[i - 1].position);
                     *
                     * }*/
                    pathResult.pathStatus = pathStatus;
                    pathResult.results    = results;
                    pathResult.corners    = cornerCount;

                    if (cornerCount >= 2)
                    {
                        path.navMeshPoints = PoolListCopyable <Vector3> .Spawn(cornerCount);

                        for (var i = 0; i < cornerCount; ++i)
                        {
                            path.navMeshPoints.Add(results[i].position);
                        }

                        if ((pathfindingLogLevel & LogLevel.Path) != 0)
                        {
                            var hash = 0;
                            for (var i = 0; i < cornerCount; ++i)
                            {
                                hash ^= (int)(results[i].position.x * 1000000f);
                            }

                            UnityEngine.Debug.Log("Path hash X: " + hash);

                            hash = 0;
                            for (var i = 0; i < cornerCount; ++i)
                            {
                                hash ^= (int)(results[i].position.y * 1000000f);
                            }

                            UnityEngine.Debug.Log("Path hash Y: " + hash);

                            hash = 0;
                            for (var i = 0; i < cornerCount; ++i)
                            {
                                hash ^= (int)(results[i].position.z * 1000000f);
                            }

                            UnityEngine.Debug.Log("Path hash Z: " + hash);
                        }

                        path.result = PathCompleteState.Complete;
                    }
                    else
                    {
                        path.result = PathCompleteState.NotExist;
                    }

                    pathResult.Dispose();
                }
                else
                {
                    path.result = PathCompleteState.NotExist;
                    results.Dispose();
                }

                vertexSide.Dispose();
                straightPathFlags.Dispose();
                pathInternal.Dispose();
            }
            else
            {
                path.result = PathCompleteState.NotExist;
                //Debug.LogWarning("Path result: " + result + ", performed: " + performed);
            }

            System.Diagnostics.Stopwatch swModifier = null;
            if ((pathfindingLogLevel & LogLevel.PathMods) != 0)
            {
                swModifier = System.Diagnostics.Stopwatch.StartNew();
            }

            if ((path.result & PathCompleteState.Complete) != 0)
            {
                path = pathModifier.Run(path, constraint);
            }

            if ((pathfindingLogLevel & LogLevel.Path) != 0)
            {
                Logger.Log(
                    $"Path result {path.result}, built in {(swPath.ElapsedTicks / (double)System.TimeSpan.TicksPerMillisecond).ToString("0.##")}ms. Path length: {statLength} (Visited: {statVisited})\nThread Index: {threadIndex}");
            }

            if ((pathfindingLogLevel & LogLevel.PathMods) != 0)
            {
                Logger.Log($"Path Mods: {swModifier.ElapsedMilliseconds}ms");
            }

            query.Dispose();

            return(path);
        }
Exemplo n.º 9
0
        public static string Combine(params string[] paths)
        {
            if (paths == null)
            {
                throw new ArgumentNullException(nameof(paths));
            }

            int maxSize        = 0;
            int firstComponent = 0;

            // We have two passes, the first calculates how large a buffer to allocate and does some precondition
            // checks on the paths passed in.  The second actually does the combination.

            for (int i = 0; i < paths.Length; i++)
            {
                if (paths[i] == null)
                {
                    throw new ArgumentNullException(nameof(paths));
                }

                if (paths[i].Length == 0)
                {
                    continue;
                }

                if (IsPathRooted(paths[i]))
                {
                    firstComponent = i;
                    maxSize        = paths[i].Length;
                }
                else
                {
                    maxSize += paths[i].Length;
                }

                char ch = paths[i][paths[i].Length - 1];
                if (!PathInternal.IsDirectorySeparator(ch))
                {
                    maxSize++;
                }
            }

            var builder = new ValueStringBuilder(stackalloc char[260]); // MaxShortPath on Windows

            builder.EnsureCapacity(maxSize);

            for (int i = firstComponent; i < paths.Length; i++)
            {
                if (paths[i].Length == 0)
                {
                    continue;
                }

                if (builder.Length == 0)
                {
                    builder.Append(paths[i]);
                }
                else
                {
                    char ch = builder[builder.Length - 1];
                    if (!PathInternal.IsDirectorySeparator(ch))
                    {
                        builder.Append(PathInternal.DirectorySeparatorChar);
                    }

                    builder.Append(paths[i]);
                }
            }

            return(builder.ToString());
        }
Exemplo n.º 10
0
 internal static bool DeleteVolumeMountPoint(string mountPoint)
 {
     mountPoint = PathInternal.EnsureExtendedPrefixOverMaxPath(mountPoint);
     return(DeleteVolumeMountPointPrivate(mountPoint));
 }
Exemplo n.º 11
0
        /// <summary>
        /// Creates and Resolves a chain of links.
        /// link1 -> link2 -> file
        /// </summary>
        private void ResolveLinkTarget_ReturnFinalTarget(string link1Path, string link1Target, string link2Path, string link2Target, string filePath)
        {
            Assert.True(Path.IsPathFullyQualified(link1Path));
            Assert.True(Path.IsPathFullyQualified(link2Path));
            Assert.True(Path.IsPathFullyQualified(filePath));

            CreateFileOrDirectory(filePath);

            // link2 to file
            FileSystemInfo link2Info = CreateSymbolicLink(link2Path, link2Target);

            Assert.True(link2Info.Exists);
            Assert.True(link2Info.Attributes.HasFlag(FileAttributes.ReparsePoint));
            AssertIsCorrectTypeAndDirectoryAttribute(link2Info);
            AssertPathEquals_RelativeSegments(link2Target, link2Info.LinkTarget);

            // link1 to link2
            FileSystemInfo link1Info = CreateSymbolicLink(link1Path, link1Target);

            Assert.True(link1Info.Exists);
            Assert.True(link1Info.Attributes.HasFlag(FileAttributes.ReparsePoint));
            AssertIsCorrectTypeAndDirectoryAttribute(link1Info);
            AssertPathEquals_RelativeSegments(link1Target, link1Info.LinkTarget);

            // link1: do not follow symlinks
            FileSystemInfo link1TargetInfo = ResolveLinkTarget(link1Path, returnFinalTarget: false);

            Assert.True(link1TargetInfo.Exists);
            AssertIsCorrectTypeAndDirectoryAttribute(link1TargetInfo);
            Assert.True(link1TargetInfo.Attributes.HasFlag(FileAttributes.ReparsePoint));
            Assert.Equal(link2Path, link1TargetInfo.FullName);
            AssertPathEquals_RelativeSegments(link2Target, link1TargetInfo.LinkTarget);

            // link2: do not follow symlinks
            FileSystemInfo link2TargetInfo = ResolveLinkTarget(link2Path, returnFinalTarget: false);

            Assert.True(link2TargetInfo.Exists);
            AssertIsCorrectTypeAndDirectoryAttribute(link2TargetInfo);
            Assert.False(link2TargetInfo.Attributes.HasFlag(FileAttributes.ReparsePoint));
            Assert.Equal(filePath, link2TargetInfo.FullName);
            Assert.Null(link2TargetInfo.LinkTarget);

            // link1: follow symlinks
            FileSystemInfo finalTarget = ResolveLinkTarget(link1Path, returnFinalTarget: true);

            Assert.True(finalTarget.Exists);
            AssertIsCorrectTypeAndDirectoryAttribute(finalTarget);
            Assert.False(finalTarget.Attributes.HasFlag(FileAttributes.ReparsePoint));
            Assert.Equal(filePath, finalTarget.FullName);

            void AssertPathEquals_RelativeSegments(string expected, string actual)
            {
#if WINDOWS
                // DeviceIoControl canonicalizes the target path i.e: removes redundant segments.
                int rootLength = PathInternal.GetRootLength(expected);
                if (rootLength > 0)
                {
                    expected = PathInternal.RemoveRelativeSegments(expected, rootLength);
                }
#endif
                Assert.Equal(expected, actual);
            }
        }
Exemplo n.º 12
0
        [System.Security.SecurityCritical]  // auto-generated
        public void AddExpressions(String str)
        {
            if (str == null)
            {
                throw new ArgumentNullException(nameof(str));
            }
            Contract.EndContractBlock();
            if (str.Length == 0)
            {
                return;
            }

            str = ProcessWholeString(str);

            if (m_expressions == null)
            {
                m_expressions = str;
            }
            else
            {
                m_expressions = m_expressions + m_separators[0] + str;
            }

            m_expressionsArray = null;

            // We have to parse the string and compute the list here.
            // The logic in this class tries to delay this parsing but
            // since operations like IsSubsetOf are called during
            // demand evaluation, it is not safe to delay this step
            // as that would cause concurring threads to update the object
            // at the same time. The CheckList operation should ideally be
            // removed from this class, but for the sake of keeping the
            // changes to a minimum here, we simply make sure m_list
            // cannot be null by parsing m_expressions eagerly.

            String[] arystr = Split(str);

            if (m_list == null)
            {
                m_list = new ArrayList();
            }

            for (int index = 0; index < arystr.Length; ++index)
            {
                if (arystr[index] != null && !arystr[index].Equals(""))
                {
                    String temp        = ProcessSingleString(arystr[index]);
                    int    indexOfNull = temp.IndexOf('\0');

                    if (indexOfNull != -1)
                    {
                        temp = temp.Substring(0, indexOfNull);
                    }

                    if (temp != null && !temp.Equals(""))
                    {
                        if (m_throwOnRelative)
                        {
                            if (PathInternal.IsPartiallyQualified(temp))
                            {
                                throw new ArgumentException(Environment.GetResourceString("Argument_AbsolutePathRequired"));
                            }

                            temp = CanonicalizePath(temp);
                        }

                        m_list.Add(temp);
                    }
                }
            }

            Reduce();
        }
        private static SafeFileHandle ValidateFileHandle(SafeFileHandle fileHandle, string path, bool useAsyncIO)
        {
            if (fileHandle.IsInvalid)
            {
                // Return a meaningful exception with the full path.

                // NT5 oddity - when trying to open "C:\" as a Win32FileStream,
                // we usually get ERROR_PATH_NOT_FOUND from the OS.  We should
                // probably be consistent w/ every other directory.
                int errorCode = Marshal.GetLastPInvokeError();

                if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && path !.Length == PathInternal.GetRootLength(path))
                {
                    errorCode = Interop.Errors.ERROR_ACCESS_DENIED;
                }

                throw Win32Marshal.GetExceptionForWin32Error(errorCode, path);
            }

            fileHandle.IsAsync = useAsyncIO;
            return(fileHandle);
        }
Exemplo n.º 14
0
 internal static bool DeleteFile(string path)
 {
     path = PathInternal.EnsureExtendedPrefixOverMaxPath(path);
     return(DeleteFilePrivate(path));
 }
Exemplo n.º 15
0
 /// <summary>
 /// Returns true if the path ends in a directory separator.
 /// </summary>
 public static bool EndsInDirectorySeparator(string path)
 => path != null && path.Length > 0 && PathInternal.IsDirectorySeparator(path[path.Length - 1]);
Exemplo n.º 16
0
        public static string Join(params string?[] paths)
        {
            if (paths == null)
            {
                throw new ArgumentNullException(nameof(paths));
            }

            if (paths.Length == 0)
            {
                return(string.Empty);
            }

            int maxSize = 0;

            foreach (string?path in paths)
            {
                maxSize += path?.Length ?? 0;
            }
            maxSize += paths.Length - 1;

            var builder = new ValueStringBuilder(stackalloc char[260]); // MaxShortPath on Windows

            builder.EnsureCapacity(maxSize);

            for (int i = 0; i < paths.Length; i++)
            {
                string?path = paths[i];
                if (string.IsNullOrEmpty(path))
                {
                    continue;
                }

                if (builder.Length == 0)
                {
                    builder.Append(path);
                }
                else
                {
                    if (!PathInternal.IsDirectorySeparator(builder[builder.Length - 1]) && !PathInternal.IsDirectorySeparator(path[0]))
                    {
                        builder.Append(PathInternal.DirectorySeparatorChar);
                    }

                    builder.Append(path);
                }
            }

            return(builder.ToString());
        }
Exemplo n.º 17
0
        private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType)
        {
            if (relativeTo == null)
            {
                throw new ArgumentNullException(nameof(relativeTo));
            }

            if (PathInternal.IsEffectivelyEmpty(relativeTo))
            {
                throw new ArgumentException("SR.Arg_PathEmpty", nameof(relativeTo));
            }

            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            if (PathInternal.IsEffectivelyEmpty(path))
            {
                throw new ArgumentException("SR.Arg_PathEmpty", nameof(path));
            }

            //Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase);

            relativeTo = Path.GetFullPath(relativeTo);
            path       = Path.GetFullPath(path);

            // Need to check if the roots are different- if they are we need to return the "to" path.
            if (!PathInternal.AreRootsEqual(relativeTo, path, comparisonType))
            {
                return(path);
            }

            int commonLength = PathInternal.GetCommonPathLength(relativeTo, path, ignoreCase: comparisonType == StringComparison.OrdinalIgnoreCase);

            // If there is nothing in common they can't share the same root, return the "to" path as is.
            if (commonLength == 0)
            {
                return(path);
            }

            // Trailing separators aren't significant for comparison
            int relativeToLength = relativeTo.Length;

            if (EndsInDirectorySeparator(relativeTo))
            {
                relativeToLength--;
            }

            bool pathEndsInSeparator = EndsInDirectorySeparator(path);
            int  pathLength          = path.Length;

            if (pathEndsInSeparator)
            {
                pathLength--;
            }

            // If we have effectively the same path, return "."
            if (relativeToLength == pathLength && commonLength >= relativeToLength)
            {
                return(".");
            }

            // We have the same root, we need to calculate the difference now using the
            // common Length and Segment count past the length.
            //
            // Some examples:
            //
            //  C:\Foo C:\Bar L3, S1 -> ..\Bar
            //  C:\Foo C:\Foo\Bar L6, S0 -> Bar
            //  C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar
            //  C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar

            StringBuilder sb = StringBuilderCache.Acquire(Math.Max(relativeTo.Length, path.Length));

            // Add parent segments for segments past the common on the "from" path
            if (commonLength < relativeToLength)
            {
                sb.Append("..");

                for (int i = commonLength + 1; i < relativeToLength; i++)
                {
                    if (PathInternal.IsDirectorySeparator(relativeTo[i]))
                    {
                        sb.Append(PathInternal.DirectorySeparatorChar);
                        sb.Append("..");
                    }
                }
            }
            else if (PathInternal.IsDirectorySeparator(path[commonLength]))
            {
                // No parent segments and we need to eat the initial separator
                //  (C:\Foo C:\Foo\Bar case)
                commonLength++;
            }

            // Now add the rest of the "to" path, adding back the trailing separator
            int differenceLength = pathLength - commonLength;

            if (pathEndsInSeparator)
            {
                differenceLength++;
            }

            if (differenceLength > 0)
            {
                if (sb.Length > 0)
                {
                    sb.Append(PathInternal.DirectorySeparatorChar);
                }

                sb.Append(path, commonLength, differenceLength);
            }

            return(StringBuilderCache.GetStringAndRelease(sb));
        }
Exemplo n.º 18
0
 internal static ReadOnlySpan <char> TrimEndingDirectorySeparator(ReadOnlySpan <char> path) =>
 PathInternal.EndsInDirectorySeparator(path) ?
 path.Slice(0, path.Length - 1) :
 path;
Exemplo n.º 19
0
 internal static bool CreateDirectory(string path, ref SECURITY_ATTRIBUTES lpSecurityAttributes)
 {
     // We always want to add for CreateDirectory to get around the legacy 248 character limitation
     path = PathInternal.EnsureExtendedPrefix(path);
     return(CreateDirectoryPrivate(path, ref lpSecurityAttributes));
 }
Exemplo n.º 20
0
        public static string GetFullPath(string path, string basePath)
        {
            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            if (basePath == null)
            {
                throw new ArgumentNullException(nameof(basePath));
            }

            if (!IsPathFullyQualified(basePath))
            {
                throw new ArgumentException(SR.Arg_BasePathNotFullyQualified, nameof(basePath));
            }

            if (basePath.Contains('\0') || path.Contains('\0'))
            {
                throw new ArgumentException(SR.Argument_InvalidPathChars);
            }

            if (IsPathFullyQualified(path))
            {
                return(GetFullPath(path));
            }

            if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
            {
                return(basePath);
            }

            int    length       = path.Length;
            string?combinedPath = null;

            if ((length >= 1 && PathInternal.IsDirectorySeparator(path[0])))
            {
                // Path is current drive rooted i.e. starts with \:
                // "\Foo" and "C:\Bar" => "C:\Foo"
                // "\Foo" and "\\?\C:\Bar" => "\\?\C:\Foo"
                combinedPath = Join(GetPathRoot(basePath.AsSpan()), path.AsSpan(1)); // Cut the separator to ensure we don't end up with two separators when joining with the root.
            }
            else if (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar)
            {
                // Drive relative paths
                Debug.Assert(length == 2 || !PathInternal.IsDirectorySeparator(path[2]));

                if (GetVolumeName(path.AsSpan()).EqualsOrdinal(GetVolumeName(basePath.AsSpan())))
                {
                    // Matching root
                    // "C:Foo" and "C:\Bar" => "C:\Bar\Foo"
                    // "C:Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
                    combinedPath = Join(basePath.AsSpan(), path.AsSpan(2));
                }
                else
                {
                    // No matching root, root to specified drive
                    // "D:Foo" and "C:\Bar" => "D:Foo"
                    // "D:Foo" and "\\?\C:\Bar" => "\\?\D:\Foo"
                    combinedPath = !PathInternal.IsDevice(basePath.AsSpan())
                        ? path.Insert(2, @"\")
                        : length == 2
                            ? JoinInternal(basePath.AsSpan(0, 4), path.AsSpan(), @"\".AsSpan())
                            : JoinInternal(basePath.AsSpan(0, 4), path.AsSpan(0, 2), @"\".AsSpan(), path.AsSpan(2));
                }
            }
            else
            {
                // "Simple" relative path
                // "Foo" and "C:\Bar" => "C:\Bar\Foo"
                // "Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
                combinedPath = JoinInternal(basePath.AsSpan(), path.AsSpan());
            }

            // Device paths are normalized by definition, so passing something of this format (i.e. \\?\C:\.\tmp, \\.\C:\foo)
            // to Windows APIs won't do anything by design. Additionally, GetFullPathName() in Windows doesn't root
            // them properly. As such we need to manually remove segments and not use GetFullPath().

            return(PathInternal.IsDevice(combinedPath.AsSpan())
                ? PathInternal.RemoveRelativeSegments(combinedPath)
                : GetFullPath(combinedPath));
        }
Exemplo n.º 21
0
 public void AreRootsEqual(string first, string second, StringComparison comparisonType, bool expected)
 {
     Assert.Equal(expected, PathInternal.AreRootsEqual(first, second, comparisonType));
 }
 internal static bool GetFileAttributesEx(string name, GET_FILEEX_INFO_LEVELS fileInfoLevel, ref WIN32_FILE_ATTRIBUTE_DATA lpFileInformation)
 {
     name = PathInternal.EnsureExtendedPrefixOverMaxPath(name);
     return(GetFileAttributesExPrivate(name, fileInfoLevel, ref lpFileInformation));
 }
Exemplo n.º 23
0
 public void IsExtendedTest(string path, bool expected)
 {
     Assert.Equal(expected, PathInternal.IsExtended(path));
 }
Exemplo n.º 24
0
        } // PrepareDataForSetup

        private static Object Setup(Object arg)
        {
            Contract.Requires(arg != null && arg is Object[]);
            Contract.Requires(((Object[])arg).Length >= 8);

            Object[]       args                     = (Object[])arg;
            String         friendlyName             = (String)args[0];
            AppDomainSetup setup                    = (AppDomainSetup)args[1];
            IntPtr         parentSecurityDescriptor = (IntPtr)args[2];
            bool           generateDefaultEvidence  = (bool)args[3];

            byte[] serializedEvidence = (byte[])args[4];
            AppDomainInitializerInfo initializerInfo = (AppDomainInitializerInfo)args[5];
            string sandboxName = (string)args[6];

            string[] propertyNames  = (string[])args[7]; // can contain null elements
            string[] propertyValues = (string[])args[8]; // can contain null elements
            // extract evidence
            Evidence providedSecurityInfo = null;
            Evidence creatorsSecurityInfo = null;

            AppDomain      ad       = AppDomain.CurrentDomain;
            AppDomainSetup newSetup = new AppDomainSetup(setup, false);

            if (propertyNames != null && propertyValues != null)
            {
                for (int i = 0; i < propertyNames.Length; i++)
                {
                    // We want to set native dll probing directories before any P/Invokes have a
                    // chance to fire. The Path class, for one, has P/Invokes.
                    if (propertyNames[i] == "NATIVE_DLL_SEARCH_DIRECTORIES")
                    {
                        if (propertyValues[i] == null)
                        {
                            throw new ArgumentNullException("NATIVE_DLL_SEARCH_DIRECTORIES");
                        }

                        string paths = propertyValues[i];
                        if (paths.Length == 0)
                        {
                            break;
                        }

                        nSetNativeDllSearchDirectories(paths);
                    }
                }

                for (int i = 0; i < propertyNames.Length; i++)
                {
                    if (propertyNames[i] == "APPBASE") // make sure in sync with Fusion
                    {
                        if (propertyValues[i] == null)
                        {
                            throw new ArgumentNullException("APPBASE");
                        }

                        if (PathInternal.IsPartiallyQualified(propertyValues[i]))
                        {
                            throw new ArgumentException(SR.Argument_AbsolutePathRequired);
                        }

                        newSetup.ApplicationBase = NormalizePath(propertyValues[i], fullCheck: true);
                    }
                    else if (propertyNames[i] == "LOADER_OPTIMIZATION")
                    {
                        if (propertyValues[i] == null)
                        {
                            throw new ArgumentNullException("LOADER_OPTIMIZATION");
                        }

                        switch (propertyValues[i])
                        {
                        case "SingleDomain": newSetup.LoaderOptimization = LoaderOptimization.SingleDomain; break;

                        case "MultiDomain": newSetup.LoaderOptimization = LoaderOptimization.MultiDomain; break;

                        case "MultiDomainHost": newSetup.LoaderOptimization = LoaderOptimization.MultiDomainHost; break;

                        case "NotSpecified": newSetup.LoaderOptimization = LoaderOptimization.NotSpecified; break;

                        default: throw new ArgumentException(SR.Argument_UnrecognizedLoaderOptimization, "LOADER_OPTIMIZATION");
                        }
                    }
                    else if (propertyNames[i] == "TRUSTED_PLATFORM_ASSEMBLIES" ||
                             propertyNames[i] == "PLATFORM_RESOURCE_ROOTS" ||
                             propertyNames[i] == "APP_PATHS" ||
                             propertyNames[i] == "APP_NI_PATHS")
                    {
                        string values = propertyValues[i];
                        if (values == null)
                        {
                            throw new ArgumentNullException(propertyNames[i]);
                        }

                        ad.SetData(propertyNames[i], NormalizeAppPaths(values));
                    }
                    else if (propertyNames[i] != null)
                    {
                        ad.SetData(propertyNames[i], propertyValues[i]);     // just propagate
                    }
                }
            }

            ad.SetupFusionStore(newSetup, null); // makes FusionStore a ref to newSetup

            // technically, we don't need this, newSetup refers to the same object as FusionStore
            // but it's confusing since it isn't immediately obvious whether we have a ref or a copy
            AppDomainSetup adSetup = ad.FusionStore;

            adSetup.InternalSetApplicationTrust(sandboxName);

            // set up the friendly name
            ad.nSetupFriendlyName(friendlyName);

#if FEATURE_COMINTEROP
            if (setup != null && setup.SandboxInterop)
            {
                ad.nSetDisableInterfaceCache();
            }
#endif // FEATURE_COMINTEROP

            ad.CreateAppDomainManager(); // could modify FusionStore's object
            ad.InitializeDomainSecurity(providedSecurityInfo,
                                        creatorsSecurityInfo,
                                        generateDefaultEvidence,
                                        parentSecurityDescriptor,
                                        true);

            // can load user code now
            if (initializerInfo != null)
            {
                adSetup.AppDomainInitializer = initializerInfo.Unwrap();
            }
            RunInitializer(adSetup);

            return(null);
        }
Exemplo n.º 25
0
 public void IsPartiallyQualifiedTest(string path, bool expected)
 {
     Assert.Equal(expected, PathInternal.IsPartiallyQualified(path));
 }
 internal static bool SetFileAttributes(string name, int attr)
 {
     name = PathInternal.EnsureExtendedPrefixIfNeeded(name);
     return(SetFileAttributesPrivate(name, attr));
 }
Exemplo n.º 27
0
 internal static bool DecryptFile(string path)
 {
     path = PathInternal.EnsureExtendedPrefixIfNeeded(path);
     return(DecryptFileFilePrivate(path, 0));
 }
Exemplo n.º 28
0
 public static bool IsPathFullyQualified(ReadOnlySpan <char> path)
 {
     return(!PathInternal.IsPartiallyQualified(path));
 }
Exemplo n.º 29
0
        private static unsafe string?GetFinalLinkTarget(string linkPath, bool isDirectory)
        {
            Interop.Kernel32.WIN32_FIND_DATA data = default;
            GetFindData(linkPath, isDirectory, ref data);

            // The file or directory is not a reparse point.
            if ((data.dwFileAttributes & (uint)FileAttributes.ReparsePoint) == 0 ||
                // Only symbolic links are supported at the moment.
                (data.dwReserved0 & Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_SYMLINK) == 0)
            {
                return(null);
            }

            // We try to open the final file since they asked for the final target.
            using SafeFileHandle handle = OpenSafeFileHandle(linkPath,
                                                             Interop.Kernel32.FileOperations.OPEN_EXISTING |
                                                             Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS);

            if (handle.IsInvalid)
            {
                // If the handle fails because it is unreachable, is because the link was broken.
                // We need to fallback to manually traverse the links and return the target of the last resolved link.
                int error = Marshal.GetLastWin32Error();
                if (IsPathUnreachableError(error))
                {
                    return(GetFinalLinkTargetSlow(linkPath));
                }

                throw Win32Marshal.GetExceptionForWin32Error(error, linkPath);
            }

            const int InitialBufferSize = 4096;

            char[] buffer = ArrayPool <char> .Shared.Rent(InitialBufferSize);

            try
            {
                uint result = GetFinalPathNameByHandle(handle, buffer);

                // If the function fails because lpszFilePath is too small to hold the string plus the terminating null character,
                // the return value is the required buffer size, in TCHARs. This value includes the size of the terminating null character.
                if (result > buffer.Length)
                {
                    char[] toReturn = buffer;
                    buffer = ArrayPool <char> .Shared.Rent((int)result);

                    ArrayPool <char> .Shared.Return(toReturn);

                    result = GetFinalPathNameByHandle(handle, buffer);
                }

                // If the function fails for any other reason, the return value is zero.
                if (result == 0)
                {
                    throw Win32Marshal.GetExceptionForLastWin32Error(linkPath);
                }

                Debug.Assert(PathInternal.IsExtended(new string(buffer, 0, (int)result).AsSpan()));
                // GetFinalPathNameByHandle always returns with extended DOS prefix even if the link target was created without one.
                // While this does not interfere with correct behavior, it might be unexpected.
                // Hence we trim it if the passed-in path to the link wasn't extended.
                int start = PathInternal.IsExtended(linkPath.AsSpan()) ? 0 : 4;
                return(new string(buffer, start, (int)result - start));
            }
            finally
            {
                ArrayPool <char> .Shared.Return(buffer);
            }

            uint GetFinalPathNameByHandle(SafeFileHandle handle, char[] buffer)
            {
                fixed(char *bufPtr = buffer)
                {
                    return(Interop.Kernel32.GetFinalPathNameByHandle(handle, bufPtr, (uint)buffer.Length, Interop.Kernel32.FILE_NAME_NORMALIZED));
                }
            }

            string?GetFinalLinkTargetSlow(string linkPath)
            {
                // Since all these paths will be passed to CreateFile, which takes a string anyway, it is pointless to use span.
                // I am not sure if it's possible to change CreateFile's param to ROS<char> and avoid all these allocations.

                // We don't throw on error since we already did all the proper validations before.
                string?current = GetImmediateLinkTarget(linkPath, isDirectory, throwOnError: false, returnFullPath: true);
                string?prev    = null;

                while (current != null)
                {
                    prev    = current;
                    current = GetImmediateLinkTarget(current, isDirectory, throwOnError: false, returnFullPath: true);
                }

                return(prev);
            }
        }
Exemplo n.º 30
0
        public static void CreateDirectory(string fullPath)
        {
            // We can save a bunch of work if the directory we want to create already exists.  This also
            // saves us in the case where sub paths are inaccessible (due to ERROR_ACCESS_DENIED) but the
            // final path is accessible and the directory already exists.  For example, consider trying
            // to create c:\Foo\Bar\Baz, where everything already exists but ACLS prevent access to c:\Foo
            // and c:\Foo\Bar.  In that case, this code will think it needs to create c:\Foo, and c:\Foo\Bar
            // and fail to due so, causing an exception to be thrown.  This is not what we want.
            if (DirectoryExists(fullPath))
            {
                return;
            }

            List <string> stackDir = new List <string>();

            // Attempt to figure out which directories don't exist, and only
            // create the ones we need.  Note that FileExists may fail due
            // to Win32 ACL's preventing us from seeing a directory, and this
            // isn't threadsafe.

            bool somepathexists = false;

            int length = fullPath.Length;

            // We need to trim the trailing slash or the code will try to create 2 directories of the same name.
            if (length >= 2 && Path.EndsInDirectorySeparator(fullPath.AsSpan()))
            {
                length--;
            }

            int lengthRoot = PathInternal.GetRootLength(fullPath.AsSpan());

            if (length > lengthRoot)
            {
                // Special case root (fullpath = X:\\)
                int i = length - 1;
                while (i >= lengthRoot && !somepathexists)
                {
                    string dir = fullPath.Substring(0, i + 1);

                    if (!DirectoryExists(dir)) // Create only the ones missing
                    {
                        stackDir.Add(dir);
                    }
                    else
                    {
                        somepathexists = true;
                    }

                    while (i > lengthRoot && !PathInternal.IsDirectorySeparator(fullPath[i]))
                    {
                        i--;
                    }
                    i--;
                }
            }

            int count = stackDir.Count;

            // If we were passed a DirectorySecurity, convert it to a security
            // descriptor and set it in he call to CreateDirectory.
            Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default;

            bool   r           = true;
            int    firstError  = 0;
            string errorString = fullPath;

            // If all the security checks succeeded create all the directories
            while (stackDir.Count > 0)
            {
                string name = stackDir[stackDir.Count - 1];
                stackDir.RemoveAt(stackDir.Count - 1);

                r = Interop.Kernel32.CreateDirectory(name, ref secAttrs);
                if (!r && (firstError == 0))
                {
                    int currentError = Marshal.GetLastWin32Error();
                    // While we tried to avoid creating directories that don't
                    // exist above, there are at least two cases that will
                    // cause us to see ERROR_ALREADY_EXISTS here.  FileExists
                    // can fail because we didn't have permission to the
                    // directory.  Secondly, another thread or process could
                    // create the directory between the time we check and the
                    // time we try using the directory.  Thirdly, it could
                    // fail because the target does exist, but is a file.
                    if (currentError != Interop.Errors.ERROR_ALREADY_EXISTS)
                    {
                        firstError = currentError;
                    }
                    else
                    {
                        // If there's a file in this directory's place, or if we have ERROR_ACCESS_DENIED when checking if the directory already exists throw.
                        if (FileExists(name) || (!DirectoryExists(name, out currentError) && currentError == Interop.Errors.ERROR_ACCESS_DENIED))
                        {
                            firstError  = currentError;
                            errorString = name;
                        }
                    }
                }
            }

            // We need this check to mask OS differences
            // Handle CreateDirectory("X:\\") when X: doesn't exist. Similarly for n/w paths.
            if ((count == 0) && !somepathexists)
            {
                string root = Directory.InternalGetDirectoryRoot(fullPath);
                if (!DirectoryExists(root))
                {
                    throw Win32Marshal.GetExceptionForWin32Error(Interop.Errors.ERROR_PATH_NOT_FOUND, root);
                }
                return;
            }

            // Only throw an exception if creating the exact directory we
            // wanted failed to work correctly.
            if (!r && (firstError != 0))
            {
                throw Win32Marshal.GetExceptionForWin32Error(firstError, errorString);
            }
        }