internal static void ValidateFileTypeForNonExtendedPaths(SafeFileHandle handle, string originalPath)
        {
            if (!PathInternal.IsExtended(originalPath))
            {
                // To help avoid stumbling into opening COM/LPT ports by accident, we will block on non file handles unless
                // we were explicitly passed a path that has \\?\. GetFullPath() will turn paths like C:\foo\con.txt into
                // \\.\CON, so we'll only allow the \\?\ syntax.

                int fileType = Interop.Kernel32.GetFileType(handle);
                if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK)
                {
                    int errorCode = fileType == Interop.Kernel32.FileTypes.FILE_TYPE_UNKNOWN
                        ? Marshal.GetLastWin32Error()
                        : Interop.Errors.ERROR_SUCCESS;

                    handle.Dispose();

                    if (errorCode != Interop.Errors.ERROR_SUCCESS)
                    {
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode);
                    }
                    throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles);
                }
            }
        }
示例#2
0
        private static void GetFullPathName(ReadOnlySpan <char> path, ref ValueStringBuilder builder)
        {
            // If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as
            // it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here.
            Debug.Assert(PathInternal.IsPartiallyQualified(path) || !PathInternal.IsExtended(path));

            uint result;

            while ((result = Interop.Kernel32.GetFullPathNameW(ref MemoryMarshal.GetReference(path), (uint)builder.Capacity, ref builder.GetPinnableReference(), IntPtr.Zero)) > builder.Capacity)
            {
                // Reported size is greater than the buffer size. Increase the capacity.
                builder.EnsureCapacity(checked ((int)result));
            }

            if (result == 0)
            {
                // Failure, get the error and throw
                int errorCode = Marshal.GetLastWin32Error();
                if (errorCode == 0)
                {
                    errorCode = Interop.Errors.ERROR_BAD_PATHNAME;
                }
                throw Win32Marshal.GetExceptionForWin32Error(errorCode, path.ToString());
            }

            builder.Length = (int)result;
        }
示例#3
0
        unsafe private static void GetFullPathName(string path, StringBuffer fullPath)
        {
            // If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as
            // it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here.
            Contract.Assert(PathInternal.IsPartiallyQualified(path) || !PathInternal.IsExtended(path));

            // Historically we would skip leading spaces *only* if the path started with a drive " C:" or a UNC " \\"
            int startIndex = PathInternal.PathStartSkip(path);

            fixed(char *pathStart = path)
            {
                uint result = 0;

                while ((result = Win32Native.GetFullPathNameW(pathStart + startIndex, fullPath.CharCapacity, fullPath.GetHandle(), IntPtr.Zero)) > fullPath.CharCapacity)
                {
                    // Reported size (which does not include the null) is greater than the buffer size. Increase the capacity.
                    fullPath.EnsureCharCapacity(result);
                }

                if (result == 0)
                {
                    // Failure, get the error and throw
                    int errorCode = Marshal.GetLastWin32Error();
                    if (errorCode == 0)
                    {
                        errorCode = Win32Native.ERROR_BAD_PATHNAME;
                    }
                    __Error.WinIOError(errorCode, path);
                }

                fullPath.Length = result;
            }
        }
示例#4
0
        private static unsafe void GetFullPathName(string path, ref StringBuffer fullPath)
        {
            // If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as
            // it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here.
            Debug.Assert(PathInternal.IsPartiallyQualified(path) || !PathInternal.IsExtended(path));

            // Historically we would skip leading spaces *only* if the path started with a drive " C:" or a UNC " \\"
            int startIndex = PathInternal.PathStartSkip(path);

            fixed(char *pathStart = path)
            {
                uint result = 0;

                while ((result = Interop.Kernel32.GetFullPathNameW(pathStart + startIndex, (uint)fullPath.Capacity, fullPath.UnderlyingArray, IntPtr.Zero)) > fullPath.Capacity)
                {
                    // Reported size is greater than the buffer size. Increase the capacity.
                    fullPath.EnsureCapacity(checked ((int)result));
                }

                if (result == 0)
                {
                    // Failure, get the error and throw
                    int errorCode = Marshal.GetLastWin32Error();
                    if (errorCode == 0)
                    {
                        errorCode = Interop.Errors.ERROR_BAD_PATHNAME;
                    }
                    throw Win32Marshal.GetExceptionForWin32Error(errorCode, path);
                }

                fullPath.Length = checked ((int)result);
            }
        }
示例#5
0
        // Expands the given path to a fully qualified path.
        public static string GetFullPath(string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            // If the path would normalize to string empty, we'll consider it empty
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
            }

            // Embedded null characters are the only invalid character case we trully care about.
            // This is because the nulls will signal the end of the string to Win32 and therefore have
            // unpredictable results.
            if (path.IndexOf('\0') != -1)
            {
                throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
            }

            if (PathInternal.IsExtended(path))
            {
                // \\?\ paths are considered normalized by definition. Windows doesn't normalize \\?\
                // paths and neither should we. Even if we wanted to GetFullPathName does not work
                // properly with device paths. If one wants to pass a \\?\ path through normalization
                // one can chop off the prefix, pass it to GetFullPath and add it again.
                return(path);
            }

            return(PathHelper.Normalize(path));
        }
 internal static bool IsDevice(ReadOnlySpan <char> path)
 {
     if (PathInternal.IsExtended(path))
     {
         return(true);
     }
     return(path.Length >= 4 && PathInternal.IsDirectorySeparator(path[0]) && PathInternal.IsDirectorySeparator(path[1]) && (path[2] == '.' || path[2] == '?') && PathInternal.IsDirectorySeparator(path[3]));
 }
示例#7
0
        /// <summary>
        /// Returns a value indicating if the given path contains invalid characters (", &lt;, &gt;, |
        /// NUL, or any ASCII char whose integer representation is in the range of 1 through 31),
        /// optionally checking for ? and *.
        /// </summary>
        internal static bool HasIllegalCharacters(string path, bool checkAdditional = false)
        {
            Debug.Assert(path != null);

            // See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
            // Question mark is a normal part of extended path syntax (\\?\)
            int startIndex = PathInternal.IsExtended(path) ? ExtendedPathPrefix.Length : 0;

            return(path.IndexOfAny(checkAdditional ? InvalidPathCharsWithAdditionalChecks : InvalidPathChars, startIndex) >= 0);
        }
示例#8
0
 // Token: 0x06001948 RID: 6472 RVA: 0x00054082 File Offset: 0x00052282
 internal static string RemoveExtendedPrefix(string path)
 {
     if (!PathInternal.IsExtended(path))
     {
         return(path);
     }
     if (PathInternal.IsExtendedUnc(path))
     {
         return(path.Remove(2, 6));
     }
     return(path.Substring(4));
 }
示例#9
0
 // Token: 0x06001949 RID: 6473 RVA: 0x000540A6 File Offset: 0x000522A6
 internal static StringBuilder RemoveExtendedPrefix(StringBuilder path)
 {
     if (!PathInternal.IsExtended(path))
     {
         return(path);
     }
     if (PathInternal.IsExtendedUnc(path))
     {
         return(path.Remove(2, 6));
     }
     return(path.Remove(0, 4));
 }
示例#10
0
        private static string NormalizePath(string path, bool fullCheck = true, bool expandShortPaths = true)
        {
            Debug.Assert(path != null, "path can't be null");

            bool isExtended = PathInternal.IsExtended(path);

            if (fullCheck)
            {
                // Embedded null characters are the only invalid character case we want to check up front.
                // This is because the nulls will signal the end of the string to Win32 and therefore have
                // unpredictable results. Other invalid characters we give a chance to be normalized out.
                if (path.IndexOf('\0') != -1)
                {
                    throw new ArgumentException(SR.Argument_InvalidPathChars, "path");
                }

                // Toss out paths with colons that aren't a valid drive specifier.
                // Cannot start with a colon and can only be of the form "C:" or "\\?\C:".
                // (Note that we used to explicitly check "http:" and "file:"- these are caught by this check now.)
                int startIndex = PathInternal.PathStartSkip(path) + 2;
                if (isExtended)
                {
                    startIndex += PathInternal.ExtendedPathPrefix.Length;
                }

                if ((path.Length > 0 && path[0] == VolumeSeparatorChar) ||
                    (path.Length >= startIndex && path[startIndex - 1] == VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[startIndex - 2])) ||
                    (path.Length > startIndex && path.IndexOf(VolumeSeparatorChar, startIndex) != -1))
                {
                    throw new NotSupportedException(SR.Argument_PathFormatNotSupported);
                }
            }

            if (isExtended)
            {
                return(NormalizeExtendedPath(path, fullCheck));
            }
            else
            {
                // Technically this doesn't matter but we used to throw for this case
                if (String.IsNullOrWhiteSpace(path))
                {
                    throw new ArgumentException(SR.Arg_PathIllegal);
                }

                return(PathHelper.Normalize(path, fullCheck, expandShortPaths));
            }
        }
示例#11
0
        public static string GetFullPath(string path)
        {
            string fullPath = GetFullPathInternal(path);

            // Emulate FileIOPermissions checks, retained for compatibility
            PathInternal.CheckInvalidPathChars(fullPath, true);

            int startIndex = PathInternal.IsExtended(fullPath) ? PathInternal.ExtendedPathPrefix.Length + 2 : 2;

            if (fullPath.Length > startIndex && fullPath.IndexOf(':', startIndex) != -1)
            {
                throw new NotSupportedException(SR.Argument_PathFormatNotSupported);
            }

            return(fullPath);
        }
示例#12
0
        internal unsafe static bool HasAdditionalIllegalCharacters(string path)
        {
            int startIndex = PathInternal.IsExtended(path) ? ExtendedPathPrefix.Length : 0;

            char currentChar;

            for (int i = startIndex; i < path.Length; i++)
            {
                currentChar = path[i];
                if (currentChar == '*' || currentChar == '?')
                {
                    return(true);
                }
            }
            return(false);
        }
示例#13
0
        // Gets the full path without argument validation
        private static string GetFullPathInternal(string path)
        {
            Debug.Assert(!string.IsNullOrEmpty(path));
            Debug.Assert(!path.Contains('\0'));

            if (PathInternal.IsExtended(path.AsSpan()))
            {
                // \\?\ paths are considered normalized by definition. Windows doesn't normalize \\?\
                // paths and neither should we. Even if we wanted to GetFullPathName does not work
                // properly with device paths. If one wants to pass a \\?\ path through normalization
                // one can chop off the prefix, pass it to GetFullPath and add it again.
                return(path);
            }

            return(PathHelper.Normalize(path));
        }
示例#14
0
        internal unsafe static bool HasWildCardCharacters(string path)
        {
            // Question mark is part of extended syntax so we have to skip if we're extended
            int startIndex = PathInternal.IsExtended(path) ? ExtendedPathPrefix.Length : 0;

            char currentChar;

            for (int i = startIndex; i < path.Length; i++)
            {
                currentChar = path[i];
                if (currentChar == '*' || currentChar == '?')
                {
                    return(true);
                }
            }
            return(false);
        }
示例#15
0
        private StringBuilder GetFullPathName(string path)
        {
            // Historically we would skip leading spaces *only* if the path started with a drive " C:" or a UNC " \\"
            int startIndex = PathInternal.PathStartSkip(path);
            int capacity   = path.Length;

            if (PathInternal.IsRelative(path))
            {
                // If the initial path is relative the final path will likely be no more than the current directory length (which can only
                // be MaxPath) so we'll pick that as a reasonable start.
                capacity += PathInternal.MaxShortPath;
            }
            else
            {
                // If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as
                // it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here.
                Debug.Assert(!PathInternal.IsExtended(path));
            }

            StringBuilder outputBuffer = this.GetOutputBuffer(capacity);

            fixed(char *pathStart = path)
            {
                int result = 0;

                while ((result = Interop.mincore.GetFullPathNameW(pathStart + startIndex, outputBuffer.Capacity + 1, outputBuffer, IntPtr.Zero)) > outputBuffer.Capacity)
                {
                    // Reported size (which does not include the null) is greater than the buffer size. Increase the capacity.
                    outputBuffer.Capacity = result;
                }

                if (result == 0)
                {
                    // Failure, get the error and throw
                    int errorCode = Marshal.GetLastWin32Error();
                    if (errorCode == 0)
                    {
                        errorCode = Interop.mincore.Errors.ERROR_BAD_PATHNAME;
                    }
                    throw Win32Marshal.GetExceptionForWin32Error(errorCode, path);
                }
            }

            return(outputBuffer);
        }
示例#16
0
            Guid.NewGuid().ToString("N").Substring(0, 8));     // randomness to avoid collisions between derived test classes using same base method concurrently

        // Some Windows versions like Windows Nano Server have the %TEMP% environment variable set to "C:\TEMP" but the
        // actual folder name is "C:\Temp", which prevents asserting path values using Assert.Equal due to case sensitiveness.
        // So instead of using TestDirectory directly, we retrieve the real path with proper casing of the initial folder path.
        private unsafe string GetTestDirectoryActualCasing()
        {
            if (!PlatformDetection.IsWindows)
            {
                return(TestDirectory);
            }

            try
            {
                using SafeFileHandle handle = Interop.Kernel32.CreateFile(
                          TestDirectory,
                          dwDesiredAccess: 0,
                          dwShareMode: FileShare.ReadWrite | FileShare.Delete,
                          dwCreationDisposition: FileMode.Open,
                          dwFlagsAndAttributes:
                          Interop.Kernel32.FileOperations.OPEN_EXISTING |
                          Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS       // Necessary to obtain a handle to a directory
                          );

                if (!handle.IsInvalid)
                {
                    const int InitialBufferSize = 4096;
                    char[]? buffer = ArrayPool <char> .Shared.Rent(InitialBufferSize);

                    uint result = GetFinalPathNameByHandle(handle, buffer);

                    // Remove extended prefix
                    int skip = PathInternal.IsExtended(buffer) ? 4 : 0;

                    return(new string(
                               buffer,
                               skip,
                               (int)result - skip));
                }
            }
            catch { }

            return(TestDirectory);
        }
示例#17
0
        // Expands the given path to a fully qualified path.
        public static string GetFullPath(string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            // Embedded null characters are the only invalid character case we want to check up front.
            // This is because the nulls will signal the end of the string to Win32 and therefore have
            // unpredictable results. Other invalid characters we give a chance to be normalized out.
            if (path.IndexOf('\0') != -1)
            {
                throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
            }

            if (PathInternal.IsExtended(path))
            {
                // We can't really know what is valid for all cases of extended paths.
                //
                //  - object names can include other characters as well (':', '/', etc.)
                //  - even file objects have different rules (pipe names can contain most characters)
                //
                // As such we will do no further analysis of extended paths to avoid blocking known and unknown
                // scenarios as well as minimizing compat breaks should we block now and need to unblock later.
                return(path);
            }

            bool isDevice = PathInternal.IsDevice(path);

            if (!isDevice)
            {
                // Toss out paths with colons that aren't a valid drive specifier.
                // Cannot start with a colon and can only be of the form "C:".
                // (Note that we used to explicitly check "http:" and "file:"- these are caught by this check now.)
                int startIndex = PathInternal.PathStartSkip(path);

                // Move past the colon
                startIndex += 2;

                if ((path.Length > 0 && path[0] == PathInternal.VolumeSeparatorChar) ||
                    (path.Length >= startIndex && path[startIndex - 1] == PathInternal.VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[startIndex - 2])) ||
                    (path.Length > startIndex && path.IndexOf(PathInternal.VolumeSeparatorChar, startIndex) != -1))
                {
                    throw new NotSupportedException(SR.Format(SR.Argument_PathFormatNotSupported_Path, path));
                }
            }

            // Technically this doesn't matter but we used to throw for this case
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
            }

            // We don't want to check invalid characters for device format- see comments for extended above
            string fullPath = PathHelper.Normalize(path, checkInvalidCharacters: !isDevice, expandShortPaths: true);

            if (!isDevice)
            {
                // Emulate FileIOPermissions checks, retained for compatibility (normal invalid characters have already been checked)
                if (PathInternal.HasWildCardCharacters(fullPath))
                {
                    throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
                }
            }

            return(fullPath);
        }
示例#18
0
        [System.Security.SecurityCritical]  // auto-generated
        private unsafe static string NormalizePath(string path, bool fullCheck, int maxPathLength, bool expandShortPaths)
        {
            Contract.Requires(path != null, "path can't be null");

            // If the path is in extended syntax, we don't need to normalize, but we still do some basic validity checks
            if (PathInternal.IsExtended(path))
            {
                if (!ValidateExtendedPath(path, fullCheck))
                {
                    throw new ArgumentException(SR.Arg_PathIllegal);
                }

                // \\?\GLOBALROOT gives access to devices out of the scope of the current user, we
                // don't want to allow this for security reasons.
                // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#nt_namespaces
                if (path.StartsWith(@"\\?\globalroot", StringComparison.OrdinalIgnoreCase))
                {
                    throw new ArgumentException(SR.Arg_PathGlobalRoot);
                }

                return(path);
            }

            // If we're doing a full path check, trim whitespace and look for
            // illegal path characters.
            if (fullCheck)
            {
                // Trim whitespace off the end of the string.
                // Win32 normalization trims only U+0020.
                path = path.TrimEnd(TrimEndChars);

                // Look for illegal path characters.
                PathInternal.CheckInvalidPathChars(path);
            }

            int index = 0;

            // We prefer to allocate on the stack for workingset/perf gain. If the
            // starting path is less than MaxPath then we can stackalloc; otherwise we'll
            // use a StringBuilder (PathHelper does this under the hood). The latter may
            // happen in 2 cases:
            // 1. Starting path is greater than MaxPath but it normalizes down to MaxPath.
            // This is relevant for paths containing escape sequences. In this case, we
            // attempt to normalize down to MaxPath, but the caller pays a perf penalty
            // since StringBuilder is used.
            // 2. IsolatedStorage, which supports paths longer than MaxPath (value given
            // by maxPathLength.
            PathHelper newBuffer = null;

            if (path.Length + 1 <= MaxPath)
            {
                char *m_arrayPtr = stackalloc char[MaxPath];
                newBuffer = new PathHelper(m_arrayPtr, MaxPath);
            }
            else
            {
                newBuffer = new PathHelper(path.Length + MaxPath, maxPathLength);
            }

            uint numSpaces = 0;
            uint numDots   = 0;
            bool fixupDirectorySeparator = false;
            // Number of significant chars other than potentially suppressible
            // dots and spaces since the last directory or volume separator char
            uint numSigChars = 0;
            int  lastSigChar = -1; // Index of last significant character.
            // Whether this segment of the path (not the complete path) started
            // with a volume separator char.  Reject "c:...".
            bool startedWithVolumeSeparator = false;
            bool firstSegment = true;
            int  lastDirectorySeparatorPos = 0;

            bool mightBeShortFileName = false;

            // LEGACY: This code is here for backwards compatibility reasons. It
            // ensures that \\foo.cs\bar.cs stays \\foo.cs\bar.cs instead of being
            // turned into \foo.cs\bar.cs.
            if (path.Length > 0 && PathInternal.IsDirectorySeparator(path[0]))
            {
                newBuffer.Append('\\');
                index++;
                lastSigChar = 0;
            }

            // Normalize the string, stripping out redundant dots, spaces, and
            // slashes.
            while (index < path.Length)
            {
                char currentChar = path[index];

                // We handle both directory separators and dots specially.  For
                // directory separators, we consume consecutive appearances.
                // For dots, we consume all dots beyond the second in
                // succession.  All other characters are added as is.  In
                // addition we consume all spaces after the last other char
                // in a directory name up until the directory separator.

                if (PathInternal.IsDirectorySeparator(currentChar))
                {
                    // If we have a path like "123.../foo", remove the trailing dots.
                    // However, if we found "c:\temp\..\bar" or "c:\temp\...\bar", don't.
                    // Also remove trailing spaces from both files & directory names.
                    // This was agreed on with the OS team to fix undeletable directory
                    // names ending in spaces.

                    // If we saw a '\' as the previous last significant character and
                    // are simply going to write out dots, suppress them.
                    // If we only contain dots and slashes though, only allow
                    // a string like [dot]+ [space]*.  Ignore everything else.
                    // Legal: "\.. \", "\...\", "\. \"
                    // Illegal: "\.. .\", "\. .\", "\ .\"
                    if (numSigChars == 0)
                    {
                        // Dot and space handling
                        if (numDots > 0)
                        {
                            // Look for ".[space]*" or "..[space]*"
                            int start = lastSigChar + 1;
                            if (path[start] != '.')
                            {
                                throw new ArgumentException(SR.Arg_PathIllegal);
                            }

                            // Only allow "[dot]+[space]*", and normalize the
                            // legal ones to "." or ".."
                            if (numDots >= 2)
                            {
                                // Reject "C:..."
                                if (startedWithVolumeSeparator && numDots > 2)
                                {
                                    throw new ArgumentException(SR.Arg_PathIllegal);
                                }

                                if (path[start + 1] == '.')
                                {
                                    // Search for a space in the middle of the
                                    // dots and throw
                                    for (int i = start + 2; i < start + numDots; i++)
                                    {
                                        if (path[i] != '.')
                                        {
                                            throw new ArgumentException(SR.Arg_PathIllegal);
                                        }
                                    }

                                    numDots = 2;
                                }
                                else
                                {
                                    if (numDots > 1)
                                    {
                                        throw new ArgumentException(SR.Arg_PathIllegal);
                                    }
                                    numDots = 1;
                                }
                            }

                            if (numDots == 2)
                            {
                                newBuffer.Append('.');
                            }

                            newBuffer.Append('.');
                            fixupDirectorySeparator = false;
                            // Continue in this case, potentially writing out '\'.
                        }

                        if (numSpaces > 0 && firstSegment)
                        {
                            // Handle strings like " \\server\share".
                            if (index + 1 < path.Length && PathInternal.IsDirectorySeparator(path[index + 1]))
                            {
                                newBuffer.Append(DirectorySeparatorChar);
                            }
                        }
                    }
                    numDots   = 0;
                    numSpaces = 0;  // Suppress trailing spaces

                    if (!fixupDirectorySeparator)
                    {
                        fixupDirectorySeparator = true;
                        newBuffer.Append(DirectorySeparatorChar);
                    }
                    numSigChars = 0;
                    lastSigChar = index;
                    startedWithVolumeSeparator = false;
                    firstSegment = false;

                    // For short file names, we must try to expand each of them as
                    // soon as possible.  We need to allow people to specify a file
                    // name that doesn't exist using a path with short file names
                    // in it, such as this for a temp file we're trying to create:
                    // C:\DOCUME~1\USERNA~1.RED\LOCALS~1\Temp\bg3ylpzp
                    // We could try doing this afterwards piece by piece, but it's
                    // probably a lot simpler to do it here.
                    if (mightBeShortFileName)
                    {
                        newBuffer.TryExpandShortFileName();
                        mightBeShortFileName = false;
                    }

                    int thisPos = newBuffer.Length - 1;
                    if (thisPos - lastDirectorySeparatorPos > MaxComponentLength)
                    {
                        throw new PathTooLongException(SR.IO_PathTooLong);
                    }
                    lastDirectorySeparatorPos = thisPos;
                } // if (Found directory separator)
                else if (currentChar == '.')
                {
                    // Reduce only multiple .'s only after slash to 2 dots. For
                    // instance a...b is a valid file name.
                    numDots++;
                    // Don't flush out non-terminal spaces here, because they may in
                    // the end not be significant.  Turn "c:\ . .\foo" -> "c:\foo"
                    // which is the conclusion of removing trailing dots & spaces,
                    // as well as folding multiple '\' characters.
                }
                else if (currentChar == ' ')
                {
                    numSpaces++;
                }
                else
                {  // Normal character logic
                    if (currentChar == '~' && expandShortPaths)
                    {
                        mightBeShortFileName = true;
                    }

                    fixupDirectorySeparator = false;

                    // To reject strings like "C:...\foo" and "C  :\foo"
                    if (firstSegment && currentChar == VolumeSeparatorChar)
                    {
                        // Only accept "C:", not "c :" or ":"
                        // Get a drive letter or ' ' if index is 0.
                        char driveLetter = (index > 0) ? path[index - 1] : ' ';
                        bool validPath   = ((numDots == 0) && (numSigChars >= 1) && (driveLetter != ' '));
                        if (!validPath)
                        {
                            throw new ArgumentException(SR.Arg_PathIllegal);
                        }

                        startedWithVolumeSeparator = true;
                        // We need special logic to make " c:" work, we should not fix paths like "  foo::$DATA"
                        if (numSigChars > 1)
                        {                       // Common case, simply do nothing
                            int spaceCount = 0; // How many spaces did we write out, numSpaces has already been reset.
                            while ((spaceCount < newBuffer.Length) && newBuffer[spaceCount] == ' ')
                            {
                                spaceCount++;
                            }
                            if (numSigChars - spaceCount == 1)
                            {
                                //Safe to update stack ptr directly
                                newBuffer.Length = 0;
                                newBuffer.Append(driveLetter); // Overwrite spaces, we need a special case to not break "  foo" as a relative path.
                            }
                        }
                        numSigChars = 0;
                    }
                    else
                    {
                        numSigChars += 1 + numDots + numSpaces;
                    }

                    // Copy any spaces & dots since the last significant character
                    // to here.  Note we only counted the number of dots & spaces,
                    // and don't know what order they're in.  Hence the copy.
                    if (numDots > 0 || numSpaces > 0)
                    {
                        int numCharsToCopy = (lastSigChar >= 0) ? index - lastSigChar - 1 : index;
                        if (numCharsToCopy > 0)
                        {
                            for (int i = 0; i < numCharsToCopy; i++)
                            {
                                newBuffer.Append(path[lastSigChar + 1 + i]);
                            }
                        }
                        numDots   = 0;
                        numSpaces = 0;
                    }

                    newBuffer.Append(currentChar);
                    lastSigChar = index;
                }

                index++;
            } // end while

            if (newBuffer.Length - 1 - lastDirectorySeparatorPos > MaxComponentLength)
            {
                throw new PathTooLongException(SR.IO_PathTooLong);
            }

            // Drop any trailing dots and spaces from file & directory names, EXCEPT
            // we MUST make sure that "C:\foo\.." is correctly handled.
            // Also handle "C:\foo\." -> "C:\foo", while "C:\." -> "C:\"
            if (numSigChars == 0)
            {
                if (numDots > 0)
                {
                    // Look for ".[space]*" or "..[space]*"
                    int start = lastSigChar + 1;
                    if (path[start] != '.')
                    {
                        throw new ArgumentException(SR.Arg_PathIllegal);
                    }

                    // Only allow "[dot]+[space]*", and normalize the
                    // legal ones to "." or ".."
                    if (numDots >= 2)
                    {
                        // Reject "C:..."
                        if (startedWithVolumeSeparator && numDots > 2)
                        {
                            throw new ArgumentException(SR.Arg_PathIllegal);
                        }

                        if (path[start + 1] == '.')
                        {
                            // Search for a space in the middle of the
                            // dots and throw
                            for (int i = start + 2; i < start + numDots; i++)
                            {
                                if (path[i] != '.')
                                {
                                    throw new ArgumentException(SR.Arg_PathIllegal);
                                }
                            }

                            numDots = 2;
                        }
                        else
                        {
                            if (numDots > 1)
                            {
                                throw new ArgumentException(SR.Arg_PathIllegal);
                            }
                            numDots = 1;
                        }
                    }

                    if (numDots == 2)
                    {
                        newBuffer.Append('.');
                    }

                    newBuffer.Append('.');
                }
            } // if (numSigChars == 0)

            // If we ended up eating all the characters, bail out.
            if (newBuffer.Length == 0)
            {
                throw new ArgumentException(SR.Arg_PathIllegal);
            }

            // Disallow URL's here.  Some of our other Win32 API calls will reject
            // them later, so we might be better off rejecting them here.
            // Note we've probably turned them into "file:\D:\foo.tmp" by now.
            // But for compatibility, ensure that callers that aren't doing a
            // full check aren't rejected here.
            if (fullCheck)
            {
                if (newBuffer.OrdinalStartsWith("http:", false) ||
                    newBuffer.OrdinalStartsWith("file:", false))
                {
                    throw new ArgumentException(SR.Argument_PathUriFormatNotSupported);
                }
            }

            // If the last part of the path (file or directory name) had a tilde,
            // expand that too.
            if (mightBeShortFileName)
            {
                newBuffer.TryExpandShortFileName();
            }

            // Call the Win32 API to do the final canonicalization step.
            int result = 1;

            if (fullCheck)
            {
                // NOTE: Win32 GetFullPathName requires the input buffer to be big enough to fit the initial
                // path which is a concat of CWD and the relative path, this can be of an arbitrary
                // size and could be > MaxPath (which becomes an artificial limit at this point),
                // even though the final normalized path after fixing up the relative path syntax
                // might be well within the MaxPath restriction. For ex,
                // "c:\SomeReallyLongDirName(thinkGreaterThan_MAXPATH)\..\foo.txt" which actually requires a
                // buffer well with in the MaxPath as the normalized path is just "c:\foo.txt"
                // This buffer requirement seems wrong, it could be a bug or a perf optimization
                // like returning required buffer length quickly or avoid stratch buffer etc.
                // Either way we need to workaround it here...

                // Ideally we would get the required buffer length first by calling GetFullPathName
                // once without the buffer and use that in the later call but this doesn't always work
                // due to Win32 GetFullPathName bug. For instance, in Win2k, when the path we are trying to
                // fully qualify is a single letter name (such as "a", "1", ",") GetFullPathName
                // fails to return the right buffer size (i.e, resulting in insufficient buffer).
                // To workaround this bug we will start with MaxPath buffer and grow it once if the
                // return value is > MaxPath.

                result = newBuffer.GetFullPathName();

                // If we called GetFullPathName with something like "foo" and our
                // command window was in short file name mode (ie, by running edlin or
                // DOS versions of grep, etc), we might have gotten back a short file
                // name.  So, check to see if we need to expand it.
                mightBeShortFileName = false;
                for (int i = 0; i < newBuffer.Length && !mightBeShortFileName; i++)
                {
                    if (newBuffer[i] == '~' && expandShortPaths)
                    {
                        mightBeShortFileName = true;
                    }
                }

                if (mightBeShortFileName)
                {
                    bool r = newBuffer.TryExpandShortFileName();
                    // Consider how the path "Doesn'tExist" would expand.  If
                    // we add in the current directory, it too will need to be
                    // fully expanded, which doesn't happen if we use a file
                    // name that doesn't exist.
                    if (!r)
                    {
                        int lastSlash = -1;

                        for (int i = newBuffer.Length - 1; i >= 0; i--)
                        {
                            if (newBuffer[i] == DirectorySeparatorChar)
                            {
                                lastSlash = i;
                                break;
                            }
                        }

                        if (lastSlash >= 0)
                        {
                            // This bounds check is for safe memcpy but we should never get this far
                            if (newBuffer.Length >= maxPathLength)
                            {
                                throw new PathTooLongException(SR.IO_PathTooLong);
                            }

                            int lenSavedName = newBuffer.Length - lastSlash - 1;
                            Debug.Assert(lastSlash < newBuffer.Length, "path unexpectedly ended in a '\'");

                            newBuffer.Fixup(lenSavedName, lastSlash);
                        }
                    }
                }
            }

            if (result != 0)
            {
                /* Throw an ArgumentException for paths like \\, \\server, \\server\
                 * This check can only be properly done after normalizing, so
                 \\foo\.. will be properly rejected. */
                if (newBuffer.Length > 1 && newBuffer[0] == '\\' && newBuffer[1] == '\\')
                {
                    int startIndex = 2;
                    while (startIndex < result)
                    {
                        if (newBuffer[startIndex] == '\\')
                        {
                            startIndex++;
                            break;
                        }
                        else
                        {
                            startIndex++;
                        }
                    }
                    if (startIndex == result)
                    {
                        throw new ArgumentException(SR.Arg_PathIllegalUNC);
                    }
                }
            }

            // Check our result and form the managed string as necessary.
            if (newBuffer.Length >= maxPathLength)
            {
                throw new PathTooLongException(SR.IO_PathTooLong);
            }

            if (result == 0)
            {
                int errorCode = Marshal.GetLastWin32Error();
                if (errorCode == 0)
                {
                    errorCode = Interop.mincore.Errors.ERROR_BAD_PATHNAME;
                }
                throw Win32Marshal.GetExceptionForWin32Error(errorCode, path);
            }

            string returnVal = newBuffer.ToString();

            if (string.Equals(returnVal, path, StringComparison.Ordinal))
            {
                returnVal = path;
            }
            return(returnVal);
        }
示例#19
0
        private static string TryExpandShortFileName(StringBuffer outputBuffer, string originalPath)
        {
            // We guarantee we'll expand short names for paths that only partially exist. As such, we need to find the part of the path that actually does exist. To
            // avoid allocating like crazy we'll create only one input array and modify the contents with embedded nulls.

            Debug.Assert(!PathInternal.IsRelative(outputBuffer), "should have resolved by now");
            Debug.Assert(!PathInternal.IsExtended(outputBuffer), "expanding short names expects normal paths");

            // Add the extended prefix before expanding to allow growth over MAX_PATH
            StringBuffer inputBuffer = null;
            ulong        rootLength  = PathInternal.GetRootLength(outputBuffer);
            bool         isUnc       = IsUnc(outputBuffer);

            ulong rootDifference = GetInputBuffer(outputBuffer, isUnc, out inputBuffer);

            rootLength += rootDifference;
            ulong inputLength = inputBuffer.Length;

            bool  success    = false;
            ulong foundIndex = inputBuffer.Length - 1;

            while (!success)
            {
                uint result = Interop.mincore.GetLongPathNameW(inputBuffer.GetHandle(), outputBuffer.GetHandle(), (uint)outputBuffer.CharCapacity);

                // Replace any temporary null we added
                if (inputBuffer[foundIndex] == '\0')
                {
                    inputBuffer[foundIndex] = '\\';
                }

                if (result == 0)
                {
                    // Look to see if we couldn't find the file
                    int error = Marshal.GetLastWin32Error();
                    if (error != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND && error != Interop.mincore.Errors.ERROR_PATH_NOT_FOUND)
                    {
                        // Some other failure, give up
                        break;
                    }

                    // We couldn't find the path at the given index, start looking further back in the string.
                    foundIndex--;

                    for (; foundIndex > rootLength && inputBuffer[foundIndex] != '\\'; foundIndex--)
                    {
                        ;
                    }
                    if (foundIndex == rootLength)
                    {
                        // Can't trim the path back any further
                        break;
                    }
                    else
                    {
                        // Temporarily set a null in the string to get Windows to look further up the path
                        inputBuffer[foundIndex] = '\0';
                    }
                }
                else if (result > outputBuffer.CharCapacity)
                {
                    // Not enough space. The result count for this API does not include the null terminator.
                    outputBuffer.EnsureCharCapacity(result);
                    result = Interop.mincore.GetLongPathNameW(inputBuffer.GetHandle(), outputBuffer.GetHandle(), (uint)outputBuffer.CharCapacity);
                }
                else
                {
                    // Found the path
                    success             = true;
                    outputBuffer.Length = result;
                    if (foundIndex < inputLength - 1)
                    {
                        // It was a partial find, put the non-existant part of the path back
                        outputBuffer.Append(inputBuffer, foundIndex, inputBuffer.Length - foundIndex);
                    }
                }
            }

            // Strip out the prefix and return the string
            StringBuffer bufferToUse = success ? outputBuffer : inputBuffer;
            string       returnValue = null;

            int newLength = (int)(bufferToUse.Length - rootDifference);

            if (isUnc)
            {
                // Need to go from \\?\UNC\ to \\?\UN\\
                bufferToUse[(ulong)PathInternal.UncExtendedPathPrefix.Length - 1] = '\\';
            }

            if (bufferToUse.SubstringEquals(originalPath, rootDifference, newLength))
            {
                // Use the original path to avoid allocating
                returnValue = originalPath;
            }
            else
            {
                returnValue = bufferToUse.Substring(rootDifference, newLength);
            }

            inputBuffer.Dispose();
            return(returnValue);
        }
示例#20
0
 // Token: 0x06001946 RID: 6470 RVA: 0x00054019 File Offset: 0x00052219
 internal static bool IsDirectoryTooLong(string fullPath)
 {
     if (AppContextSwitches.BlockLongPaths && (AppContextSwitches.UseLegacyPathHandling || !PathInternal.IsExtended(fullPath)))
     {
         return(fullPath.Length >= 248);
     }
     return(PathInternal.IsPathTooLong(fullPath));
 }
示例#21
0
 // Token: 0x06001950 RID: 6480 RVA: 0x000542E0 File Offset: 0x000524E0
 internal static bool IsExtendedUnc(StringBuilder path)
 {
     return(path.Length >= "\\\\?\\UNC\\".Length && PathInternal.IsExtended(path) && char.ToUpper(path[4]) == 'U' && char.ToUpper(path[5]) == 'N' && char.ToUpper(path[6]) == 'C' && path[7] == '\\');
 }
示例#22
0
 // Token: 0x0600194B RID: 6475 RVA: 0x0005412C File Offset: 0x0005232C
 internal static bool IsDevice(StringBuffer path)
 {
     return(PathInternal.IsExtended(path) || (path.Length >= 4U && PathInternal.IsDirectorySeparator(path[0U]) && PathInternal.IsDirectorySeparator(path[1U]) && (path[2U] == '.' || path[2U] == '?') && PathInternal.IsDirectorySeparator(path[3U])));
 }
示例#23
0
 // Token: 0x0600194A RID: 6474 RVA: 0x000540CC File Offset: 0x000522CC
 internal static bool IsDevice(string path)
 {
     return(PathInternal.IsExtended(path) || (path.Length >= 4 && PathInternal.IsDirectorySeparator(path[0]) && PathInternal.IsDirectorySeparator(path[1]) && (path[2] == '.' || path[2] == '?') && PathInternal.IsDirectorySeparator(path[3])));
 }
示例#24
0
        // Token: 0x06001941 RID: 6465 RVA: 0x00053E6C File Offset: 0x0005206C
        internal static bool HasInvalidVolumeSeparator(string path)
        {
            int num = (!AppContextSwitches.UseLegacyPathHandling && PathInternal.IsExtended(path)) ? "\\\\?\\".Length : PathInternal.PathStartSkip(path);

            return((path.Length > num && path[num] == Path.VolumeSeparatorChar) || (path.Length >= num + 2 && path[num + 1] == Path.VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[num])) || (path.Length > num + 2 && path.IndexOf(Path.VolumeSeparatorChar, num + 2) != -1));
        }
示例#25
0
        /// <summary>
        /// Only check for ? and *.
        /// </summary>
        internal static bool HasAdditionalIllegalCharacters(string path)
        {
            int startIndex = PathInternal.IsExtended(path) ? ExtendedPathPrefix.Length : 0;

            return(path.IndexOfAny(AdditionalInvalidPathChars, startIndex) >= 0);
        }