Exemple #1
0
        public DirectoryInfo CreateSubdirectory(string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                throw new ArgumentException(SR.Argument_PathEmpty, nameof(path));
            }
            if (Path.IsPathRooted(path))
            {
                throw new ArgumentException(SR.Arg_Path2IsRooted, nameof(path));
            }

            string fullPath = Path.GetFullPath(Path.Combine(FullPath, path));

            if (fullPath.Length < FullPath.Length ||
                (fullPath.Length > FullPath.Length && !PathInternal.IsDirectorySeparator(fullPath[FullPath.Length])) ||
                string.Compare(FullPath, 0, fullPath, 0, FullPath.Length, PathInternal.StringComparison) != 0)
            {
                throw new ArgumentException(SR.Format(SR.Argument_InvalidSubPath, path, FullPath), nameof(path));
            }

            FileSystem.CreateDirectory(fullPath);
            return(new DirectoryInfo(fullPath));
        }
Exemple #2
0
        // Returns the directory path of a file path. This method effectively
        // removes the last element of the given file path, i.e. it returns a
        // string consisting of all characters up to but not including the last
        // backslash ("\") in the file path. The returned value is null if the file
        // path is null or if the file path denotes a root (such as "\", "C:", or
        // "\\server\share").
        public static string GetDirectoryName(string path)
        {
            if (path == null)
            {
                return(null);
            }

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

            PathInternal.CheckInvalidPathChars(path);
            path = PathInternal.NormalizeDirectorySeparators(path);
            int root = PathInternal.GetRootLength(path);

            int i = path.Length;

            if (i > root)
            {
                while (i > root && !PathInternal.IsDirectorySeparator(path[--i]))
                {
                    ;
                }
                return(path.Substring(0, i));
            }

            return(null);
        }
Exemple #3
0
        public DirectoryInfo CreateSubdirectory(string path)
        {
            ArgumentNullException.ThrowIfNull(path);

            if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
            {
                throw new ArgumentException(SR.Argument_PathEmpty, nameof(path));
            }
            if (Path.IsPathRooted(path))
            {
                throw new ArgumentException(SR.Arg_Path2IsRooted, nameof(path));
            }

            string newPath = Path.GetFullPath(Path.Combine(FullPath, path));

            ReadOnlySpan <char> trimmedNewPath     = Path.TrimEndingDirectorySeparator(newPath.AsSpan());
            ReadOnlySpan <char> trimmedCurrentPath = Path.TrimEndingDirectorySeparator(FullPath.AsSpan());

            // We want to make sure the requested directory is actually under the subdirectory.
            if (trimmedNewPath.StartsWith(trimmedCurrentPath, PathInternal.StringComparison)
                // Allow the exact same path, but prevent allowing "..\FooBar" through when the directory is "Foo"
                && ((trimmedNewPath.Length == trimmedCurrentPath.Length) || PathInternal.IsDirectorySeparator(newPath[trimmedCurrentPath.Length])))
            {
                FileSystem.CreateDirectory(newPath);
                return(new DirectoryInfo(newPath));
            }

            // We weren't nested
            throw new ArgumentException(SR.Format(SR.Argument_InvalidSubPath, path, FullPath), nameof(path));
        }
Exemple #4
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));
        }
Exemple #5
0
 /// <summary>
 /// Returns the path root or null if path is empty or null.
 /// </summary>
 public static string?GetPathRoot(string?path)
 {
     if (PathInternal.IsEffectivelyEmpty(path))
     {
         return(null);
     }
     return(IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString : string.Empty);
 }
        public static ReadOnlySpan <char> GetDirectoryName(ReadOnlySpan <char> path)
        {
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                return(ReadOnlySpan <char> .Empty);
            }
            int directoryNameOffset = Path.GetDirectoryNameOffset(path);

            return(directoryNameOffset < 0 ? ReadOnlySpan <char> .Empty : path.Slice(0, directoryNameOffset));
        }
        public static string GetDirectoryName(string path)
        {
            if (path == null || PathInternal.IsEffectivelyEmpty(path.AsSpan()))
            {
                return((string)null);
            }
            int directoryNameOffset = Path.GetDirectoryNameOffset(path.AsSpan());

            return(directoryNameOffset < 0 ? (string)null : PathInternal.NormalizeDirectorySeparators(path.Substring(0, directoryNameOffset)));
        }
Exemple #8
0
        /// <summary>
        /// Returns the directory portion of a file path. This method effectively
        /// removes the last segment of the given file path, i.e. it returns a
        /// string consisting of all characters up to but not including the last
        /// backslash ("\") in the file path. The returned value is null if the
        /// specified path is null, empty, or a root (such as "\", "C:", or
        /// "\\server\share").
        /// </summary>
        /// <remarks>
        /// Directory separators are normalized in the returned string.
        /// </remarks>
        public static string?GetDirectoryName(string?path)
        {
            if (path == null || PathInternal.IsEffectivelyEmpty(path.AsSpan()))
            {
                return(null);
            }

            int end = GetDirectoryNameOffset(path.AsSpan());

            return(end >= 0 ? PathInternal.NormalizeDirectorySeparators(path.Substring(0, end)) : null);
        }
Exemple #9
0
        /// <summary>
        /// Returns the directory portion of a file path. The returned value is empty
        /// if the specified path is null, empty, or a root (such as "\", "C:", or
        /// "\\server\share").
        /// </summary>
        /// <remarks>
        /// Unlike the string overload, this method will not normalize directory separators.
        /// </remarks>
        public static ReadOnlySpan <char> GetDirectoryName(ReadOnlySpan <char> path)
        {
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                return(ReadOnlySpan <char> .Empty);
            }

            int end = GetDirectoryNameOffset(path);

            return(end >= 0 ? path.Slice(0, end) : ReadOnlySpan <char> .Empty);
        }
Exemple #10
0
        /// <remarks>
        /// Unlike the string overload, this method will not normalize directory separators.
        /// </remarks>
        public static ReadOnlySpan <char> GetPathRoot(ReadOnlySpan <char> path)
        {
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                return(ReadOnlySpan <char> .Empty);
            }

            int pathRoot = PathInternal.GetRootLength(path);

            return(pathRoot <= 0 ? ReadOnlySpan <char> .Empty : path.Slice(0, pathRoot));
        }
Exemple #11
0
        // The resulting string is null if path is null. If the path is empty or
        // only contains whitespace characters an ArgumentException gets thrown.
        public static string GetPathRoot(string path)
        {
            if (path == null)
            {
                return(null);
            }
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
            }

            return(IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString : String.Empty);
        }
Exemple #12
0
        // Returns the root portion of the given path. The resulting string
        // consists of those rightmost characters of the path that constitute the
        // root of the path. Possible patterns for the resulting string are: An
        // empty string (a relative path on the current drive), "\" (an absolute
        // path on the current drive), "X:" (a relative path on a given drive,
        // where X is the drive letter), "X:\" (an absolute path on a given drive),
        // and "\\server\share" (a UNC path for a given server and share name).
        // The resulting string is null if path is null. If the path is empty or
        // only contains whitespace characters an ArgumentException gets thrown.
        public static string GetPathRoot(string path)
        {
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                return(null);
            }

            ReadOnlySpan <char> result = GetPathRoot(path.AsSpan());

            if (path.Length == result.Length)
            {
                return(PathInternal.NormalizeDirectorySeparators(path));
            }

            return(PathInternal.NormalizeDirectorySeparators(new string(result)));
        }
Exemple #13
0
        // Returns the directory path of a file path. This method effectively
        // removes the last element of the given file path, i.e. it returns a
        // string consisting of all characters up to but not including the last
        // backslash ("\") in the file path. The returned value is null if the file
        // path is null or if the file path denotes a root (such as "\", "C:", or
        // "\\server\share").
        public static string GetDirectoryName(string path)
        {
            if (path == null)
            {
                return(null);
            }

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

            path = PathInternal.NormalizeDirectorySeparators(path);
            int end = PathInternal.GetDirectoryNameOffset(path);

            return(end >= 0 ? path.Substring(0, end) : null);
        }
Exemple #14
0
        // Returns the root portion of the given path. The resulting string
        // consists of those rightmost characters of the path that constitute the
        // root of the path. Possible patterns for the resulting string are: An
        // empty string (a relative path on the current drive), "\" (an absolute
        // path on the current drive), "X:" (a relative path on a given drive,
        // where X is the drive letter), "X:\" (an absolute path on a given drive),
        // and "\\server\share" (a UNC path for a given server and share name).
        // The resulting string is null if path is null. If the path is empty or
        // only contains whitespace characters an ArgumentException gets thrown.
        public static string GetPathRoot(string path)
        {
            if (path == null)
            {
                return(null);
            }
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                throw new ArgumentException(SR.Arg_PathEmpty, nameof(path));
            }

            // Need to return the normalized directory separator
            path = PathInternal.NormalizeDirectorySeparators(path);

            int pathRoot = PathInternal.GetRootLength(path);

            return(pathRoot <= 0 ? string.Empty : path.Substring(0, pathRoot));
        }
Exemple #15
0
        // Expands the given path to a fully qualified path.
        public static string GetFullPath(string path)
        {
            ArgumentNullException.ThrowIfNull(path);

            // If the path would normalize to string empty, we'll consider it empty
            if (PathInternal.IsEffectivelyEmpty(path.AsSpan()))
            {
                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.Contains('\0'))
            {
                throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
            }

            return(GetFullPathInternal(path));
        }
Exemple #16
0
        // Returns the root portion of the given path. The resulting string
        // consists of those rightmost characters of the path that constitute the
        // root of the path. Possible patterns for the resulting string are: An
        // empty string (a relative path on the current drive), "\" (an absolute
        // path on the current drive), "X:" (a relative path on a given drive,
        // where X is the drive letter), "X:\" (an absolute path on a given drive),
        // and "\\server\share" (a UNC path for a given server and share name).
        // The resulting string is null if path is null. If the path is empty or
        // only contains whitespace characters an ArgumentException gets thrown.
        public static string GetPathRoot(string path)
        {
            if (path == null)
            {
                return(null);
            }

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

            ReadOnlySpan <char> result = GetPathRoot(path.AsReadOnlySpan());

            if (path.Length == result.Length)
            {
                return(PathInternal.NormalizeDirectorySeparators(path));
            }

            return(PathInternal.NormalizeDirectorySeparators(new string(result)));
        }
Exemple #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);
        }
Exemple #18
0
 public static ReadOnlySpan <char> GetPathRoot(ReadOnlySpan <char> path)
 {
     return(PathInternal.IsEffectivelyEmpty(path) && IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString.AsSpan() : ReadOnlySpan <char> .Empty);
 }
Exemple #19
0
        private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType)
        {
            if (string.IsNullOrEmpty(relativeTo))
            {
                throw new ArgumentNullException(nameof(relativeTo));
            }
            if (PathInternal.IsEffectivelyEmpty(path))
            {
                throw new ArgumentNullException(nameof(path));
            }
            Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase);

            relativeTo = GetFullPath(relativeTo);
            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 (PathInternal.EndsInDirectorySeparator(relativeTo))
            {
                relativeToLength--;
            }

            bool pathEndsInSeparator = PathInternal.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(PathInternal.ParentDirectoryPrefix);

                for (int i = commonLength; i < relativeToLength; i++)
                {
                    if (PathInternal.IsDirectorySeparator(relativeTo[i]))
                    {
                        sb.Append(PathInternal.ParentDirectoryPrefix);
                    }
                }
            }
            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 count = pathLength - commonLength;

            if (pathEndsInSeparator)
            {
                count++;
            }

            sb.Append(path, commonLength, count);
            return(StringBuilderCache.GetStringAndRelease(sb));
        }
Exemple #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))
            {
                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).EqualsOrdinal(GetVolumeName(basePath)))
                {
                    // Matching root
                    // "C:Foo" and "C:\Bar" => "C:\Bar\Foo"
                    // "C:Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
                    combinedPath = Join(basePath, 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)
                        ? path.Insert(2, @"\")
                        : length == 2
                            ? JoinInternal(basePath.AsSpan(0, 4), path, @"\")
                            : JoinInternal(basePath.AsSpan(0, 4), path.AsSpan(0, 2), @"\", path.AsSpan(2));
                }
            }
            else
            {
                // "Simple" relative path
                // "Foo" and "C:\Bar" => "C:\Bar\Foo"
                // "Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
                combinedPath = JoinInternal(basePath, path);
            }

            // 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)
                ? PathInternal.RemoveRelativeSegments(combinedPath, PathInternal.GetRootLength(combinedPath))
                : GetFullPath(combinedPath));
        }