public static void MoveDirectory(string sourceFullPath, string destFullPath)
        {
            // Windows doesn't care if you try and copy a file via "MoveDirectory"...
            if (FileExists(sourceFullPath))
            {
                // ... but it doesn't like the source to have a trailing slash ...

                // On Windows we end up with ERROR_INVALID_NAME, which is
                // "The filename, directory name, or volume label syntax is incorrect."
                //
                // This surfaces as a IOException, if we let it go beyond here it would
                // give DirectoryNotFound.

                if (PathInternal.EndsInDirectorySeparator(sourceFullPath))
                {
                    throw new IOException(SR.Format(SR.IO_PathNotFound_Path, sourceFullPath));
                }

                // ... but it doesn't care if the destination has a trailing separator.
                destFullPath = PathInternal.TrimEndingDirectorySeparator(destFullPath);
            }

            if (Interop.Sys.Rename(sourceFullPath, destFullPath) < 0)
            {
                Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
                switch (errorInfo.Error)
                {
                case Interop.Error.EACCES:     // match Win32 exception
                    throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, sourceFullPath), errorInfo.RawErrno);

                default:
                    throw Interop.GetExceptionForIoErrno(errorInfo, sourceFullPath, isDirectory: true);
                }
            }
        }
Exemple #2
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 newPath = Path.GetFullPath(Path.Combine(FullPath, path));

            ReadOnlySpan <char> trimmedNewPath     = PathInternal.TrimEndingDirectorySeparator(newPath.AsSpan());
            ReadOnlySpan <char> trimmedCurrentPath = PathInternal.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 #3
0
        public static bool FileExists(string fullPath)
        {
            Interop.ErrorInfo ignored;

            // Input allows trailing separators in order to match Windows behavior
            // Unix does not accept trailing separators, so must be trimmed
            return(FileExists(PathInternal.TrimEndingDirectorySeparator(fullPath), Interop.Sys.FileTypes.S_IFREG, out ignored));
        }
 public static bool FileExists(ReadOnlySpan <char> fullPath)
 {
     Interop.ErrorInfo ignored;
     // File.Exists() explicitly checks for a trailing separator and returns false if found. FileInfo.Exists and all other
     // internal usages do not check for the trailing separator. Historically we've always removed the trailing separator
     // when getting attributes as trailing separators are generally not accepted by Windows APIs. Unix will take
     // trailing separators, but it infers that the path must be a directory (it effectively appends "."). To align with
     // our historical behavior (outside of File.Exists()), we need to trim.
     //
     // See http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11 for details.
     return(FileExists(PathInternal.TrimEndingDirectorySeparator(fullPath), Interop.Sys.FileTypes.S_IFREG, out ignored));
 }
        /// <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="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_ACCESS_DENIED)
                    {
                        // 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.

                        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);
        }
Exemple #6
0
        private void Init(string originalPath, string fullPath = null, string fileName = null, bool isNormalized = false)
        {
            // Want to throw the original argument name
            OriginalPath = originalPath ?? throw new ArgumentNullException("path");

            fullPath = fullPath ?? originalPath;
            fullPath = isNormalized ? fullPath : Path.GetFullPath(fullPath);

            _name = fileName ?? (PathInternal.IsRoot(fullPath) ?
                                 fullPath :
                                 Path.GetFileName(PathInternal.TrimEndingDirectorySeparator(fullPath.AsSpan()))).ToString();

            FullPath = fullPath;
        }
Exemple #7
0
 private static void GetFindData(string fullPath, ref Interop.Kernel32.WIN32_FIND_DATA findData)
 {
     using (SafeFindHandle handle = Interop.Kernel32.FindFirstFile(PathInternal.TrimEndingDirectorySeparator(fullPath), ref findData))
     {
         if (handle.IsInvalid)
         {
             int errorCode = Marshal.GetLastWin32Error();
             // File not found doesn't make much sense coming from a directory delete.
             if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND)
             {
                 errorCode = Interop.Errors.ERROR_PATH_NOT_FOUND;
             }
             throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
         }
     }
 }
Exemple #8
0
        public void Refresh(ReadOnlySpan <char> path)
        {
            // This should not throw, instead we store the result so that we can throw it
            // when someone actually accesses a property.

            // Use lstat to get the details on the object, without following symlinks.
            // If it is a symlink, then subsequently get details on the target of the symlink,
            // storing those results separately.  We only report failure if the initial
            // lstat fails, as a broken symlink should still report info on exists, attributes, etc.
            _isDirectory = false;
            path         = PathInternal.TrimEndingDirectorySeparator(path);
            int result = Interop.Sys.LStat(path, out _fileStatus);

            if (result < 0)
            {
                Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();

                // This should never set the error if the file can't be found.
                // (see the Windows refresh passing returnErrorOnNotFound: false).
                if (errorInfo.Error == Interop.Error.ENOENT ||
                    errorInfo.Error == Interop.Error.ENOTDIR)
                {
                    _fileStatusInitialized = 0;
                    _exists = false;
                }
                else
                {
                    _fileStatusInitialized = errorInfo.RawErrno;
                }
                return;
            }

            _exists = true;

            // IMPORTANT: Is directory logic must match the logic in FileSystemEntry
            _isDirectory = (_fileStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR;

            // If we're a symlink, attempt to check the target to see if it is a directory
            if ((_fileStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFLNK &&
                Interop.Sys.Stat(path, out Interop.Sys.FileStatus targetStatus) >= 0)
            {
                _isDirectory = (targetStatus.Mode & Interop.Sys.FileTypes.S_IFMT) == Interop.Sys.FileTypes.S_IFDIR;
            }

            _fileStatusInitialized = 0;
        }
        public static void DeleteFile(string fullPath)
        {
            if (Interop.Sys.Unlink(fullPath) < 0)
            {
                Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
                switch (errorInfo.Error)
                {
                case Interop.Error.ENOENT:
                    // In order to match Windows behavior
                    string directoryName = Path.GetDirectoryName(fullPath);
                    if (directoryName.Length > 0 && !Directory.Exists(directoryName))
                    {
                        throw Interop.GetExceptionForIoErrno(errorInfo, fullPath, true);
                    }
                    return;

                case Interop.Error.EROFS:
                    // EROFS means the file system is read-only
                    // Need to manually check file existence
                    // github.com/dotnet/corefx/issues/21273
                    Interop.ErrorInfo fileExistsError;

                    // Input allows trailing separators in order to match Windows behavior
                    // Unix does not accept trailing separators, so must be trimmed
                    if (!FileExists(PathInternal.TrimEndingDirectorySeparator(fullPath),
                                    Interop.Sys.FileTypes.S_IFREG, out fileExistsError) &&
                        fileExistsError.Error == Interop.Error.ENOENT)
                    {
                        return;
                    }
                    goto default;

                case Interop.Error.EISDIR:
                    errorInfo = Interop.Error.EACCES.Info();
                    goto default;

                default:
                    throw Interop.GetExceptionForIoErrno(errorInfo, fullPath);
                }
            }
        }
Exemple #10
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="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)
                    {
                        // 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}");

                        // 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);
        }