public static FileAttributes GetAttributes(string fullPath) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true); if (errorCode != 0) { throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } return((FileAttributes)data.dwFileAttributes); }
public static DateTimeOffset GetCreationTime(string fullPath) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: false); if (errorCode != 0) { throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } return(data.ftCreationTime.ToDateTimeOffset()); }
private void EnsureDataInitialized() { if (_dataInitialized == -1) { _data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); Refresh(); } if (_dataInitialized != 0) // Refresh was unable to initialize the data { throw Win32Marshal.GetExceptionForWin32Error(_dataInitialized, FullPath); } }
private void EnsureDataInitialized() { if (_dataInitialized == -1) { _data = default; Refresh(); } if (_dataInitialized != 0) // Refresh was unable to initialize the data { throw Win32Marshal.GetExceptionForWin32Error(_dataInitialized, FullPath); } }
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 = path.TrimEnd(PathHelpers.DirectorySeparatorChars); using (new DisableMediaInsertionPrompt()) { 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.fileAttributes = -1; return(Interop.Errors.ERROR_SUCCESS); } } return(errorCode); }
public override DateTimeOffset GetLastWriteTime(string fullPath) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: false); if (errorCode != 0) { throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } long dt = ((long)data.ftLastWriteTimeHigh << 32) | ((long)data.ftLastWriteTimeLow); return(DateTimeOffset.FromFileTime(dt)); }
internal static void CreateSymbolicLink(string path, string pathToTarget, bool isDirectory) { string pathToTargetFullPath = PathInternal.GetLinkTargetFullPath(path, pathToTarget); Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = default; int errorCode = FillAttributeInfo(pathToTargetFullPath, ref data, returnErrorOnNotFound: true); if (errorCode == Interop.Errors.ERROR_SUCCESS && data.dwFileAttributes != -1 && isDirectory != ((data.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0)) { throw new IOException(SR.Format(SR.IO_InconsistentLinkType, path)); } Interop.Kernel32.CreateSymbolicLink(path, pathToTarget, isDirectory); }
private static bool IsReparsePoint(string fullPath) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, returnErrorOnNotFound: true); if (errorCode != Interop.Errors.ERROR_SUCCESS) { // 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); } return(((FileAttributes)data.dwFileAttributes & FileAttributes.ReparsePoint) != 0); }
private static void PopulateAttributeData(ref Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data, in Interop.Kernel32.BY_HANDLE_FILE_INFORMATION fileInformationData)
// Returns 0 on success, otherwise a Win32 error code. Note that // classes should use -1 as the uninitialized state for dataInitialized. internal static int FillAttributeInfo(String path, ref Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data, bool tryagain, bool returnErrorOnNotFound) { int errorCode = 0; if (tryagain) // someone has a handle to the file open, or other error { Interop.Kernel32.WIN32_FIND_DATA findData; findData = new Interop.Kernel32.WIN32_FIND_DATA(); // Remove trailing slash since this can cause grief to FindFirstFile. You will get an invalid argument error String tempPath = path.TrimEnd(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); // For floppy drives, normally the OS will pop up a dialog saying // there is no disk in drive A:, please insert one. We don't want that. // SetThreadErrorMode will let us disable this, but we should set the error // mode back, since this may have wide-ranging effects. uint oldMode; bool setThreadErrorModeSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode); try { bool error = false; SafeFindHandle handle = Interop.Kernel32.FindFirstFile(tempPath, ref findData); try { if (handle.IsInvalid) { error = true; errorCode = Marshal.GetLastWin32Error(); if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND || errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND || errorCode == Interop.Errors.ERROR_NOT_READY) // floppy device not ready { if (!returnErrorOnNotFound) { // Return default value for backward compatibility errorCode = 0; data.fileAttributes = -1; } } return(errorCode); } } finally { // Close the Win32 handle try { handle.Dispose(); } catch { // if we're already returning an error, don't throw another one. if (!error) { throw Win32Marshal.GetExceptionForLastWin32Error(); } } } } finally { if (setThreadErrorModeSuccess) { Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode); } } // Copy the information to data data.PopulateFrom(ref findData); } else { // For floppy drives, normally the OS will pop up a dialog saying // there is no disk in drive A:, please insert one. We don't want that. // SetThreadErrorMode will let us disable this, but we should set the error // mode back, since this may have wide-ranging effects. bool success = false; uint oldMode; bool setThreadErrorModeSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode); try { success = Interop.Kernel32.GetFileAttributesEx(path, Interop.Kernel32.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data); } finally { if (setThreadErrorModeSuccess) { Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode); } } if (!success) { errorCode = Marshal.GetLastWin32Error(); if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND && errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND && errorCode != Interop.Errors.ERROR_NOT_READY) // floppy device not ready { // In case someone latched onto the file. Take the perf hit only for failure return(FillAttributeInfo(path, ref data, true, returnErrorOnNotFound)); } else { if (!returnErrorOnNotFound) { // Return default value for backward compatibility errorCode = 0; data.fileAttributes = -1; } } } } return(errorCode); }
public override bool FileExists(System.String fullPath) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, false, true); return (errorCode == 0) && (data.fileAttributes != -1) && ((data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0); }
private void EnsureDataInitialized() { if (_dataInitialized == -1) { _data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); Refresh(); } if (_dataInitialized != 0) // Refresh was unable to initialize the data throw Win32Marshal.GetExceptionForWin32Error(_dataInitialized, FullPath); }
public override void RemoveDirectory(string fullPath, bool recursive) { // Do not recursively delete through reparse points. Perhaps in a // future version we will add a new flag to control this behavior, // but for now we're much safer if we err on the conservative side. // This applies to symbolic links and mount points. Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, false, true); if (errorCode != 0) { // Ensure we throw a DirectoryNotFoundException. if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND) errorCode = Interop.Errors.ERROR_PATH_NOT_FOUND; throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); } if (((FileAttributes)data.fileAttributes & FileAttributes.ReparsePoint) != 0) recursive = false; // We want extended syntax so we can delete "extended" subdirectories and files // (most notably ones with trailing whitespace or periods) RemoveDirectoryHelper(PathInternal.EnsureExtendedPrefix(fullPath), recursive, true); }
public override DateTimeOffset GetLastWriteTime(string fullPath) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, false, false); if (errorCode != 0) throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); long dt = ((long)data.ftLastWriteTimeHigh << 32) | ((long)data.ftLastWriteTimeLow); return DateTimeOffset.FromFileTime(dt); }
public override FileAttributes GetAttributes(string fullPath) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); int errorCode = FillAttributeInfo(fullPath, ref data, false, true); if (errorCode != 0) throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath); return (FileAttributes)data.fileAttributes; }
private static bool ShouldUseWinRT(string fullPath, bool isCreate) { // The purpose of this method is to determine if we can access a path // via Win32 or if we need to fallback to WinRT. // We prefer Win32 since it is faster, WinRT's APIs eventually just // call into Win32 after all, but it doesn't provide access to, // brokered paths (like Pictures or Documents) nor does it handle // placeholder files. So we'd like to fall back to WinRT whenever // we can't access a path, or if it known to be a placeholder file. bool useWinRt = false; do { // first use GetFileAttributesEx as it is faster than FindFirstFile and requires minimum permissions Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); if (Interop.Kernel32.GetFileAttributesEx(fullPath, Interop.Kernel32.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data)) { // got the attributes if ((data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0 || (data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT) == 0) { // we have a directory or a file that is not a reparse point // useWinRt = false; break; } else { // we need to get the find data to determine if it is a placeholder file Interop.Kernel32.WIN32_FIND_DATA findData = new Interop.Kernel32.WIN32_FIND_DATA(); using (SafeFindHandle handle = Interop.Kernel32.FindFirstFile(fullPath, ref findData)) { if (!handle.IsInvalid) { // got the find data, use WinRT for placeholder files Debug.Assert((findData.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0); Debug.Assert((findData.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT) != 0); useWinRt = findData.dwReserved0 == Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_FILE_PLACEHOLDER; break; } } } } int error = Marshal.GetLastWin32Error(); Debug.Assert(error != Interop.Errors.ERROR_SUCCESS); if (error == Interop.Errors.ERROR_ACCESS_DENIED) { // The path was not accessible with Win32, so try WinRT useWinRt = true; break; } else if (error != Interop.Errors.ERROR_PATH_NOT_FOUND && error != Interop.Errors.ERROR_FILE_NOT_FOUND) { // We hit some error other than ACCESS_DENIED or NOT_FOUND, // Default to Win32 to provide most accurate error behavior break; } // error was ERROR_PATH_NOT_FOUND or ERROR_FILE_NOT_FOUND // if we are creating a file/directory we cannot assume that Win32 will have access to // the parent directory, so we walk up the path. fullPath = PathHelpers.GetDirectoryNameInternal(fullPath); // only walk up the path if we are creating a file/directory and not at the root } while (isCreate && !String.IsNullOrEmpty(fullPath)); return useWinRt; }
private static bool ShouldUseWinRT(string fullPath, bool isCreate) { // The purpose of this method is to determine if we can access a path // via Win32 or if we need to fallback to WinRT. // We prefer Win32 since it is faster, WinRT's APIs eventually just // call into Win32 after all, but it doesn't provide access to, // brokered paths (like Pictures or Documents) nor does it handle // placeholder files. So we'd like to fall back to WinRT whenever // we can't access a path, or if it known to be a placeholder file. bool useWinRt = false; do { // first use GetFileAttributesEx as it is faster than FindFirstFile and requires minimum permissions Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); if (Interop.Kernel32.GetFileAttributesEx(fullPath, Interop.Kernel32.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data)) { // got the attributes if ((data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0 || (data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT) == 0) { // we have a directory or a file that is not a reparse point // useWinRt = false; break; } else { // we need to get the find data to determine if it is a placeholder file Interop.Kernel32.WIN32_FIND_DATA findData = new Interop.Kernel32.WIN32_FIND_DATA(); using (SafeFindHandle handle = Interop.Kernel32.FindFirstFile(fullPath, ref findData)) { if (!handle.IsInvalid) { // got the find data, use WinRT for placeholder files Debug.Assert((findData.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0); Debug.Assert((findData.dwFileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT) != 0); useWinRt = findData.dwReserved0 == Interop.Kernel32.IOReparseOptions.IO_REPARSE_TAG_FILE_PLACEHOLDER; break; } } } } int error = Marshal.GetLastWin32Error(); Debug.Assert(error != Interop.Errors.ERROR_SUCCESS); if (error == Interop.Errors.ERROR_ACCESS_DENIED) { // The path was not accessible with Win32, so try WinRT useWinRt = true; break; } else if (error != Interop.Errors.ERROR_PATH_NOT_FOUND && error != Interop.Errors.ERROR_FILE_NOT_FOUND) { // We hit some error other than ACCESS_DENIED or NOT_FOUND, // Default to Win32 to provide most accurate error behavior break; } // error was ERROR_PATH_NOT_FOUND or ERROR_FILE_NOT_FOUND // if we are creating a file/directory we cannot assume that Win32 will have access to // the parent directory, so we walk up the path. fullPath = PathHelpers.GetDirectoryNameInternal(fullPath); // only walk up the path if we are creating a file/directory and not at the root } while (isCreate && !String.IsNullOrEmpty(fullPath)); return(useWinRt); }
/// <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); }
private bool DirectoryExists(String path, out int lastError) { Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); lastError = FillAttributeInfo(path, ref data, false, true); return (lastError == 0) && (data.fileAttributes != -1) && ((data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0); }