[System.Security.SecurityCritical] // auto-generated private static void DeleteHelper(String fullPath, String userPath, bool recursive, bool throwOnTopLevelDirectoryNotFound) { bool r; int hr; Exception ex = null; // 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. // Note the logic to check whether fullPath is a reparse point is // in Delete(String, String, bool), and will set "recursive" to false. // Note that Win32's DeleteFile and RemoveDirectory will just delete // the reparse point itself. if (recursive) { Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); // Open a Find handle using (SafeFindHandle hnd = Win32Native.FindFirstFile(fullPath+Path.DirectorySeparatorCharAsString+"*", data)) { if (hnd.IsInvalid) { hr = Marshal.GetLastWin32Error(); __Error.WinIOError(hr, fullPath); } do { bool isDir = (0!=(data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)); if (isDir) { // Skip ".", "..". if (data.cFileName.Equals(".") || data.cFileName.Equals("..")) continue; // Recurse for all directories, unless they are // reparse points. Do not follow mount points nor // symbolic links, but do delete the reparse point // itself. bool shouldRecurse = (0 == (data.dwFileAttributes & (int) FileAttributes.ReparsePoint)); if (shouldRecurse) { String newFullPath = Path.InternalCombine(fullPath, data.cFileName); String newUserPath = Path.InternalCombine(userPath, data.cFileName); try { DeleteHelper(newFullPath, newUserPath, recursive, false); } catch(Exception e) { if (ex == null) { ex = e; } } } else { // Check to see if this is a mount point, and // unmount it. if (data.dwReserved0 == Win32Native.IO_REPARSE_TAG_MOUNT_POINT) { // Use full path plus a trailing '\' String mountPoint = Path.InternalCombine(fullPath, data.cFileName + Path.DirectorySeparatorChar); r = Win32Native.DeleteVolumeMountPoint(mountPoint); if (!r) { hr = Marshal.GetLastWin32Error(); if (hr != Win32Native.ERROR_PATH_NOT_FOUND) { try { __Error.WinIOError(hr, data.cFileName); } catch(Exception e) { if (ex == null) { ex = e; } } } } } // RemoveDirectory on a symbolic link will // remove the link itself. String reparsePoint = Path.InternalCombine(fullPath, data.cFileName); r = Win32Native.RemoveDirectory(reparsePoint); if (!r) { hr = Marshal.GetLastWin32Error(); if (hr != Win32Native.ERROR_PATH_NOT_FOUND) { try { __Error.WinIOError(hr, data.cFileName); } catch(Exception e) { if (ex == null) { ex = e; } } } } } } else { String fileName = Path.InternalCombine(fullPath, data.cFileName); r = Win32Native.DeleteFile(fileName); if (!r) { hr = Marshal.GetLastWin32Error(); if (hr != Win32Native.ERROR_FILE_NOT_FOUND) { try { __Error.WinIOError(hr, data.cFileName); } catch (Exception e) { if (ex == null) { ex = e; } } } } } } while (Win32Native.FindNextFile(hnd, data)); // Make sure we quit with a sensible error. hr = Marshal.GetLastWin32Error(); } if (ex != null) throw ex; if (hr!=0 && hr!=Win32Native.ERROR_NO_MORE_FILES) __Error.WinIOError(hr, userPath); } r = Win32Native.RemoveDirectory(fullPath); if (!r) { hr = Marshal.GetLastWin32Error(); if (hr == Win32Native.ERROR_FILE_NOT_FOUND) // A dubious error code. hr = Win32Native.ERROR_PATH_NOT_FOUND; // This check was originally put in for Win9x (unfortunately without special casing it to be for Win9x only). We can't change the NT codepath now for backcomp reasons. if (hr == Win32Native.ERROR_ACCESS_DENIED) throw new IOException(Environment.GetResourceString("UnauthorizedAccess_IODenied_Path", userPath)); // don't throw the DirectoryNotFoundException since this is a subdir and there could be a race condition // between two Directory.Delete callers if (hr == Win32Native.ERROR_PATH_NOT_FOUND && !throwOnTopLevelDirectoryNotFound) return; __Error.WinIOError(hr, fullPath); } }
internal static extern SafeFindHandle FindFirstFile(String fileName, [In, Out] Win32Native.WIN32_FIND_DATA data);
/// <include file='doc\Directory.uex' path='docs/doc[@for="Directory.Delete1"]/*' /> public static void Delete(String path, bool recursive) { String fullPath = Path.GetFullPathInternal(path); String demandPath; if (!recursive) { // Do normal check only on this directory if (fullPath.EndsWith( Path.DirectorySeparatorChar )) demandPath = fullPath + "."; else demandPath = fullPath + Path.DirectorySeparatorChar + "."; } else { // Check for deny anywhere below and fail if (fullPath.EndsWith( Path.DirectorySeparatorChar )) demandPath = fullPath.Substring(0,fullPath.Length - 1); else demandPath = fullPath; } // Make sure we have write permission to this directory new FileIOPermission(FileIOPermissionAccess.Write, new String[] { demandPath }, false, false ).Demand(); bool r; int hr; Exception ex = null; if (recursive) { Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); // Open a Find handle (Win32 is weird) IntPtr hnd = Win32Native.FindFirstFile(fullPath+Path.DirectorySeparatorChar+"*", data); if (hnd==Win32Native.INVALID_HANDLE_VALUE) { hr = Marshal.GetLastWin32Error(); __Error.WinIOError(hr, path); } do { bool isDir = (0!=(data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)); if (isDir) { if (data.cFileName.Equals(".") || data.cFileName.Equals("..")) continue; // recurse DirectoryInfo d = new DirectoryInfo(Path.InternalCombine(fullPath, data.cFileName), false); try { d.Delete(true); } catch(Exception e) { if (ex == null) { ex = e; } } } else { String fileName = path + Path.DirectorySeparatorChar + data.cFileName; r = Win32Native.DeleteFile(fileName); if (!r) { hr = Marshal.GetLastWin32Error(); try { __Error.WinIOError(hr, data.cFileName); } catch(Exception e) { if (ex == null) { ex = e; } } } } } while (Win32Native.FindNextFile(hnd, data)); // Make sure we quit with a sensible error. hr = Marshal.GetLastWin32Error(); Win32Native.FindClose(hnd); // Close Find handle in all cases. if (ex != null) throw ex; if (hr!=0 && hr!=Win32Native.ERROR_NO_MORE_FILES) __Error.WinIOError(hr, path); } r = Win32Native.RemoveDirectory(fullPath); hr = Marshal.GetLastWin32Error(); if (hr == Win32Native.ERROR_FILE_NOT_FOUND) // Win32 is weird, it gives back a file not found hr = Win32Native.ERROR_PATH_NOT_FOUND; if (hr == Win32Native.ERROR_ACCESS_DENIED) // WinNT throws IOException. Win9x hack to do the same. throw new IOException(String.Format(Environment.GetResourceString("UnauthorizedAccess_IODenied_Path"), path)); if (!r) __Error.WinIOError(hr, path); }
// From IO.Directory class (make that internal if possible) private static String[] GetFileDirectoryNames(String path, String msg, bool file) { int hr; if (path==null) throw new ArgumentNullException("path", Environment.GetResourceString("ArgumentNull_Path")); bool fEndsWithDirectory = false; char lastChar = path[path.Length-1]; if (lastChar == Path.DirectorySeparatorChar || lastChar == Path.AltDirectorySeparatorChar || lastChar == '.') fEndsWithDirectory = true; // Get an absolute path and do a security check String fullPath = Path.GetFullPathInternal(path); // GetFullPath() removes '\', "\." etc from path, we will restore // it here. If path ends in a trailing slash (\), append a * // or we'll get a "Cannot find the file specified" exception if ((fEndsWithDirectory) && (fullPath[fullPath.Length - 1] != lastChar)) fullPath += "\\*"; // Check for read permission to the directory, not to the contents. String dir = Path.GetDirectoryName(fullPath); if (dir != null) dir += "\\"; #if _DEBUG if (s_fDebug) { Console.WriteLine("path = " + path); Console.WriteLine("fullPath = " + fullPath); Console.WriteLine("dir = " + dir); } #endif new FileIOPermission(FileIOPermissionAccess.Read, dir == null ? fullPath : dir).Demand(); String[] list = new String[10]; int listSize = 0; Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); // Open a Find handle (Win32 is weird) IntPtr hnd = Win32Native.FindFirstFile(fullPath, data); if (hnd==Win32Native.INVALID_HANDLE_VALUE) { // Calls to GetLastWin32Error clobber HResult. Store HResult. hr = Marshal.GetLastWin32Error(); if (hr==Win32Native.ERROR_FILE_NOT_FOUND) return new String[0]; __Error.WinIOError(hr, msg); } // Keep asking for more matching files, adding file names to list int numEntries = 0; // Number of directory entities we see. do { bool includeThis; // Should this file/directory be included in the output? if (file) includeThis = (0==(data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)); else { includeThis = (0!=(data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)); // Don't add "." nor ".." if (includeThis && (data.cFileName.Equals(".") || data.cFileName.Equals(".."))) includeThis = false; } if (includeThis) { numEntries++; if (listSize==list.Length) { String[] newList = new String[list.Length*2]; Array.Copy(list, 0, newList, 0, listSize); list = newList; } list[listSize++] = data.cFileName; } } while (Win32Native.FindNextFile(hnd, data)); // Make sure we quit with a sensible error. hr = Marshal.GetLastWin32Error(); Win32Native.FindClose(hnd); // Close Find handle in all cases. if (hr!=0 && hr!=Win32Native.ERROR_NO_MORE_FILES) __Error.WinIOError(hr, msg); // Check for a string such as "C:\tmp", in which case we return // just the directory name. FindNextFile fails first time, and // data still contains a directory. if (!file && numEntries==1 && (0!=(data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY))) { String[] sa = new String[1]; sa[0] = data.cFileName; return sa; } // Return list of files/directories as an array of strings if (listSize == list.Length) return list; String[] items = new String[listSize]; Array.Copy(list, 0, items, 0, listSize); return items; }
[System.Security.SecurityCritical] // auto-generated #endif private static String[] GetFileDirectoryNames(String path, String msg, bool file, IsolatedStorageFile isf) { int hr; if(path == null) throw new ArgumentNullException("path", Environment.GetResourceString("ArgumentNull_Path")); Contract.EndContractBlock(); bool fEndsWithDirectory = false; char lastChar = path[path.Length - 1]; if(lastChar == Path.DirectorySeparatorChar || lastChar == Path.AltDirectorySeparatorChar || lastChar == '.') fEndsWithDirectory = true; // Get an absolute path and do a security check String fullPath = Path.GetFullPathInternal(path); // GetFullPath() removes '\', "\." etc from path, we will restore // it here. If path ends in a trailing slash (\), append a * // or we'll get a "Cannot find the file specified" exception if((fEndsWithDirectory) && (fullPath[fullPath.Length - 1] != lastChar)) fullPath += "\\*"; // Check for read permission to the directory, not to the contents. String dir = Path.GetDirectoryName(fullPath); if(dir != null) dir += "\\"; if(isf != null) { try { isf.Demand(dir == null ? fullPath : dir); } catch (Exception e) { throw GetIsolatedStorageException("IsolatedStorage_Operation", e); } } if(CompatibilitySwitches.IsAppEarlierThanWindowsPhoneMango) { // Pre Mango Windows Phone had very odd behavior for this function. It would take the parent directory of the search pattern and do a * // in there. That means something like GetDirectories("Dir1") would be treated as GetDirectories("*") and GetDirectories("Dir2\Dir3") would be // treated as GetDirectories("Dir2\*"). // This also means that GetDirectories("") returned "IsolatedStorage" since it was looking at the directory above the root of Isolated Storage. fullPath = Path.Combine(Path.GetDirectoryName(fullPath), "*"); } String[] list = new String[10]; int listSize = 0; Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); // Open a Find handle SafeFindHandle hnd = Win32Native.FindFirstFile(fullPath, data); if(hnd.IsInvalid) { // Calls to GetLastWin32Error overwrites HResult. Store HResult. hr = Marshal.GetLastWin32Error(); if(hr == Win32Native.ERROR_FILE_NOT_FOUND) return new String[0]; // Mango would throw DirectoryNotFoundException if we got ERROR_PATH_NOT_FOUND instead of IsolatedStorageException if(CompatibilitySwitches.IsAppEarlierThanWindowsPhone8 && hr == Win32Native.ERROR_PATH_NOT_FOUND) __Error.WinIOError(hr, msg); #if FEATURE_ISOSTORE_LIGHT throw GetIsolatedStorageException("IsolatedStorage_Operation", Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error(), new IntPtr(-1))); #else __Error.WinIOError(hr, msg); #endif } // Keep asking for more matching files, adding file names to list int numEntries = 0; // Number of directory entities we see. do { bool includeThis; // Should this file/directory be included in the output? if(file) includeThis = (0 == (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)); else { includeThis = (0 != (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)); // Don't add "." nor ".." if(includeThis && (data.cFileName.Equals(".") || data.cFileName.Equals(".."))) includeThis = false; } if(includeThis) { numEntries++; if(listSize == list.Length) { String[] newList = new String[list.Length * 2]; Array.Copy(list, 0, newList, 0, listSize); list = newList; } list[listSize++] = data.cFileName; } } while(Win32Native.FindNextFile(hnd, data)); // Make sure we quit with a sensible error. hr = Marshal.GetLastWin32Error(); hnd.Close(); // Close Find handle in all cases. if(hr != 0 && hr != Win32Native.ERROR_NO_MORE_FILES) __Error.WinIOError(hr, msg); // Check for a string such as "C:\tmp", in which case we return // just the directory name. FindNextFile fails first time, and // data still contains a directory. if(!file && numEntries == 1 && (0 != (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY))) { String[] sa = new String[1]; sa[0] = data.cFileName; return sa; } // Return list of files/directories as an array of strings if(listSize == list.Length) return list; String[] items = new String[listSize]; Array.Copy(list, 0, items, 0, listSize); return items; }
// Private helper function that does not do any security checks internal static String[] InternalGetFileDirectoryNames(String fullPath, bool file) { int hr; // If path ends in a trailing slash (\), append a * or we'll // get a "Cannot find the file specified" exception char lastChar = fullPath[fullPath.Length-1]; if (lastChar == Path.DirectorySeparatorChar || lastChar == Path.AltDirectorySeparatorChar || lastChar == Path.VolumeSeparatorChar) fullPath = fullPath + '*'; String[] list = new String[10]; int listSize = 0; Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); // Open a Find handle (Win32 is weird) IntPtr hnd = Win32Native.FindFirstFile(fullPath, data); if (hnd==Win32Native.INVALID_HANDLE_VALUE) { // Calls to GetLastWin32Error clobber HResult. Store HResult. hr = Marshal.GetLastWin32Error(); if (hr==Win32Native.ERROR_FILE_NOT_FOUND) return new String[0]; __Error.WinIOError(hr, Path.GetDirectoryName(fullPath)); } // Keep asking for more matching files, adding file names to list int numEntries = 0; // Number of DirectoryInfo entities we see. do { bool includeThis; // Should this file/DirectoryInfo be included in the output? if (file) includeThis = (0==(data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)); else { includeThis = (0!=(data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)); // Don't add "." nor ".." if (includeThis && (data.cFileName.Equals(".") || data.cFileName.Equals(".."))) includeThis = false; } if (includeThis) { numEntries++; if (listSize==list.Length) { String[] newList = new String[list.Length*2]; Array.Copy(list, 0, newList, 0, listSize); list = newList; } list[listSize++] = data.cFileName; } } while (Win32Native.FindNextFile(hnd, data)); // Make sure we quit with a sensible error. hr = Marshal.GetLastWin32Error(); Win32Native.FindClose(hnd); // Close Find handle in all cases. if (hr!=0 && hr!=Win32Native.ERROR_NO_MORE_FILES) __Error.WinIOError(hr, Path.GetDirectoryName(fullPath)); // Check for a string such as "C:\tmp", in which case we return // just the DirectoryInfo name. FindNextFile fails first time, and // data still contains a directory. if (!file && numEntries==1 && (0!=(data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY))) { String[] sa = new String[1]; sa[0] = data.cFileName; return sa; } // Return list of files/directories as an array of strings if (listSize == list.Length) return list; String[] items = new String[listSize]; Array.Copy(list, 0, items, 0, listSize); return items; }
private static void DeleteHelper(string fullPath, string userPath, bool recursive) { int num; Exception exception = null; if (recursive) { Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); using (SafeFindHandle handle = Win32Native.FindFirstFile(fullPath + Path.DirectorySeparatorChar + "*", data)) { if (handle.IsInvalid) { num = Marshal.GetLastWin32Error(); __Error.WinIOError(num, fullPath); } do { if (0 != (data.dwFileAttributes & 0x10)) { if (!data.cFileName.Equals(".") && !data.cFileName.Equals("..")) { if (0 == (data.dwFileAttributes & 0x400)) { string str = Path.InternalCombine(fullPath, data.cFileName); string str2 = Path.InternalCombine(userPath, data.cFileName); try { DeleteHelper(str, str2, recursive); } catch (Exception exception2) { if (exception == null) { exception = exception2; } } } else { if ((data.dwReserved0 == -1610612733) && !Win32Native.DeleteVolumeMountPoint(Path.InternalCombine(fullPath, data.cFileName + Path.DirectorySeparatorChar))) { num = Marshal.GetLastWin32Error(); try { __Error.WinIOError(num, data.cFileName); } catch (Exception exception3) { if (exception == null) { exception = exception3; } } } if (!Win32Native.RemoveDirectory(Path.InternalCombine(fullPath, data.cFileName))) { num = Marshal.GetLastWin32Error(); try { __Error.WinIOError(num, data.cFileName); } catch (Exception exception4) { if (exception == null) { exception = exception4; } } } } } } else if (!Win32Native.DeleteFile(Path.InternalCombine(fullPath, data.cFileName))) { num = Marshal.GetLastWin32Error(); if (num != 2) { try { __Error.WinIOError(num, data.cFileName); } catch (Exception exception5) { if (exception == null) { exception = exception5; } } } } } while (Win32Native.FindNextFile(handle, data)); num = Marshal.GetLastWin32Error(); } if (exception != null) { throw exception; } if ((num != 0) && (num != 0x12)) { __Error.WinIOError(num, userPath); } } if (!Win32Native.RemoveDirectory(fullPath)) { num = Marshal.GetLastWin32Error(); switch (num) { case 2: num = 3; break; case 5: throw new IOException(Environment.GetResourceString("UnauthorizedAccess_IODenied_Path", new object[] { userPath })); } __Error.WinIOError(num, fullPath); } }
internal static string[] GetFileDirectoryNames(string path, string userSearchPattern, bool file) { int num; if (path == null) { throw new ArgumentNullException("path", Environment.GetResourceString("ArgumentNull_Path")); } userSearchPattern = NormalizeSearchPattern(userSearchPattern); if (userSearchPattern.Length == 0) { return new string[0]; } bool flag = false; char ch = path[path.Length - 1]; if (((ch == Path.DirectorySeparatorChar) || (ch == Path.AltDirectorySeparatorChar)) || (ch == '.')) { flag = true; } string str = LongPath.NormalizePath(path); if (flag && (str[str.Length - 1] != ch)) { str = str + @"\*"; } string directoryName = LongPath.GetDirectoryName(str); if (directoryName != null) { directoryName = directoryName + @"\"; } try { string[] pathList = new string[] { (directoryName == null) ? str : directoryName }; new FileIOPermission(FileIOPermissionAccess.Read, pathList, false, false).Demand(); } catch { throw new IsolatedStorageException(Environment.GetResourceString("IsolatedStorage_Operation")); } string[] sourceArray = new string[10]; int length = 0; Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); SafeFindHandle hndFindFile = Win32Native.FindFirstFile(Path.AddLongPathPrefix(str), data); if (hndFindFile.IsInvalid) { num = Marshal.GetLastWin32Error(); if (num == 2) { return new string[0]; } __Error.WinIOError(num, userSearchPattern); } int num3 = 0; do { bool flag2; if (file) { flag2 = 0 == (data.dwFileAttributes & 0x10); } else { flag2 = 0 != (data.dwFileAttributes & 0x10); if (flag2 && (data.cFileName.Equals(".") || data.cFileName.Equals(".."))) { flag2 = false; } } if (flag2) { num3++; if (length == sourceArray.Length) { string[] strArray3 = new string[sourceArray.Length * 2]; Array.Copy(sourceArray, 0, strArray3, 0, length); sourceArray = strArray3; } sourceArray[length++] = data.cFileName; } } while (Win32Native.FindNextFile(hndFindFile, data)); num = Marshal.GetLastWin32Error(); hndFindFile.Close(); if ((num != 0) && (num != 0x12)) { __Error.WinIOError(num, userSearchPattern); } if ((!file && (num3 == 1)) && ((data.dwFileAttributes & 0x10) != 0)) { return new string[] { data.cFileName }; } if (length == sourceArray.Length) { return sourceArray; } string[] destinationArray = new string[length]; Array.Copy(sourceArray, 0, destinationArray, 0, length); return destinationArray; }
internal SearchResult(string fullPath, string userPath, Win32Native.WIN32_FIND_DATA findData) { this.fullPath = fullPath; this.userPath = userPath; this.findData = findData; }
internal static int FillAttributeInfo(string path, ref Win32Native.WIN32_FILE_ATTRIBUTE_DATA data, bool tryagain, bool returnErrorOnNotFound) { int num = 0; if (tryagain) { Win32Native.WIN32_FIND_DATA win_find_data = new Win32Native.WIN32_FIND_DATA(); string fileName = path.TrimEnd(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); int num2 = Win32Native.SetErrorMode(1); try { bool flag = false; SafeFindHandle handle = Win32Native.FindFirstFile(fileName, win_find_data); try { if (handle.IsInvalid) { flag = true; num = Marshal.GetLastWin32Error(); if ((((num == 2) || (num == 3)) || (num == 0x15)) && !returnErrorOnNotFound) { num = 0; data.fileAttributes = -1; } return num; } } finally { try { handle.Close(); } catch { if (!flag) { __Error.WinIOError(); } } } } finally { Win32Native.SetErrorMode(num2); } data.PopulateFrom(win_find_data); return num; } bool flag2 = false; int newMode = Win32Native.SetErrorMode(1); try { flag2 = Win32Native.GetFileAttributesEx(path, 0, ref data); } finally { Win32Native.SetErrorMode(newMode); } if (!flag2) { num = Marshal.GetLastWin32Error(); if (((num != 2) && (num != 3)) && (num != 0x15)) { return FillAttributeInfo(path, ref data, true, returnErrorOnNotFound); } if (!returnErrorOnNotFound) { num = 0; data.fileAttributes = -1; } } return num; }
internal static String[] InternalGetFileDirectoryNames(String path, String userPathOriginal, String searchPattern, bool includeFiles, bool includeDirs, SearchOption searchOption) { int hr = 0; if ((searchOption != SearchOption.TopDirectoryOnly) && (searchOption != SearchOption.AllDirectories)) throw new ArgumentOutOfRangeException("searchOption", Environment.GetResourceString("ArgumentOutOfRange_Enum")); searchPattern = searchPattern.TrimEnd(); if (searchPattern.Length == 0) return new String[0]; Path.CheckSearchPattern(searchPattern); // Build various paths and query strings // Must fully qualify the path for the security check String fullPath = Path.GetFullPathInternal(path); // Any illegal chars such as *, ? will be caught by FileIOPermission.HasIllegalCharacters String[] demandPaths = new String[] {GetDemandDir(fullPath, true)}; new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false).Demand(); String userPath = userPathOriginal; String tempPath = Path.GetDirectoryName(searchPattern); if (tempPath != null && tempPath.Length != 0) { // For filters like foo\*.cs we need to verify if the directory foo is not denied access. // Do a demand on the combined path so that we can fail early in case of deny demandPaths = new String[] {GetDemandDir(Path.InternalCombine(fullPath, tempPath), true)}; new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false).Demand(); userPath = Path.Combine(userPath, tempPath); // Need to add the searchPath to return correct path and for right security checks } String tempStr = Path.InternalCombine(fullPath, searchPattern); // If path ends in a trailing slash (\), append a * or we'll // get a "Cannot find the file specified" exception char lastChar = tempStr[tempStr.Length-1]; if (lastChar == Path.DirectorySeparatorChar || lastChar == Path.AltDirectorySeparatorChar || lastChar == Path.VolumeSeparatorChar) tempStr = tempStr + '*'; fullPath = Path.GetDirectoryName(tempStr); BCLDebug.Assert((fullPath != null),"fullpath can't be null!"); String searchCriteria; bool trailingSlash = false; bool trailingSlashUserPath = false; lastChar = fullPath[fullPath.Length-1]; trailingSlash = (lastChar == Path.DirectorySeparatorChar) || (lastChar == Path.AltDirectorySeparatorChar); if (trailingSlash) { // Can happen if the path is C:\temp, in which case GetDirectoryName would return C:\ searchCriteria = tempStr.Substring(fullPath.Length); } else searchCriteria = tempStr.Substring(fullPath.Length + 1); Win32Native.WIN32_FIND_DATA data = new Win32Native.WIN32_FIND_DATA(); SafeFindHandle hnd = null; // Heap based search stack SearchData searchData = new SearchData(fullPath, userPath, searchOption); List<SearchData> searchStack = new List<SearchData>(); searchStack.Add(searchData); List<string> demandPathList = new List<string>(); int numEntries = 0; // Number of directory entities we see. int listSize = 0; String[] list = new String[10]; String searchPath; int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS); try { // Traverse directory structure. We need to get '*' while(searchStack.Count > 0) { searchData = searchStack[searchStack.Count - 1]; searchStack.RemoveAt(searchStack.Count - 1); BCLDebug.Assert((searchData.fullPath != null),"fullpath can't be null!"); lastChar = searchData.fullPath[searchData.fullPath.Length -1]; trailingSlash = (lastChar == Path.DirectorySeparatorChar) || (lastChar == Path.AltDirectorySeparatorChar); if (searchData.userPath.Length > 0) { lastChar = searchData.userPath[searchData.userPath.Length -1]; trailingSlashUserPath = (lastChar == Path.DirectorySeparatorChar) || (lastChar == Path.AltDirectorySeparatorChar); } // Traverse the subdirs if specified if (searchData.searchOption != SearchOption.TopDirectoryOnly) { try { if (trailingSlash) searchPath = searchData.fullPath + "*"; else searchPath = searchData.fullPath + Path.DirectorySeparatorChar + "*"; // Get all files and dirs hnd = Win32Native.FindFirstFile(searchPath, data); if (hnd.IsInvalid) { hr = Marshal.GetLastWin32Error(); // This could happen if the dir doesn't contain any files. // Continue with the recursive search though, eventually // searchStack will become empty if (hr==Win32Native.ERROR_FILE_NOT_FOUND) continue; __Error.WinIOError(hr, searchData.fullPath); } // Add subdirs to searchStack. Exempt ReparsePoints as appropriate do { if ((0 != (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)) && !data.cFileName.Equals(".") && !data.cFileName.Equals("..")) { // Setup search data for the sub directory and push it into the stack SearchData searchDataSubDir = new SearchData(); // FullPath StringBuilder strBldr = new StringBuilder(searchData.fullPath); if (!trailingSlash) strBldr.Append(Path.DirectorySeparatorChar); strBldr.Append(data.cFileName); searchDataSubDir.fullPath = strBldr.ToString(); // UserPath strBldr.Length = 0; strBldr.Append(searchData.userPath); if (!trailingSlashUserPath) strBldr.Append(Path.DirectorySeparatorChar); strBldr.Append(data.cFileName); searchDataSubDir.userPath = strBldr.ToString(); // SearchOption searchDataSubDir.searchOption = searchData.searchOption; searchStack.Add(searchDataSubDir); } } while (Win32Native.FindNextFile(hnd, data)); // We don't care about any error here } finally { if (hnd != null) hnd.Dispose(); } } // Execute searchCriteria against the current directory try{ if (trailingSlash) searchPath = searchData.fullPath + searchCriteria; else searchPath = searchData.fullPath + Path.DirectorySeparatorChar + searchCriteria; // Open a Find handle hnd = Win32Native.FindFirstFile(searchPath, data); if (hnd.IsInvalid) { hr = Marshal.GetLastWin32Error(); if (hr == Win32Native.ERROR_FILE_NOT_FOUND) continue; __Error.WinIOError(hr, searchData.fullPath); } numEntries = 0; // Keep asking for more matching files/dirs, add it to the list do { bool includeThis = false; // Should this file/directory be included in the output? if (includeFiles) includeThis = (0 == (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)); if (includeDirs) { // Don't add "." nor ".." if ((0 != (data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY)) && !data.cFileName.Equals(".") && !data.cFileName.Equals("..")) { BCLDebug.Assert(!includeThis, data.cFileName + ": current item can't be both file and dir!"); includeThis = true; } } if (includeThis) { numEntries++; if (listSize==list.Length) { String[] newList = new String[list.Length*2]; Array.Copy(list, 0, newList, 0, listSize); list = newList; } list[listSize++] = Path.InternalCombine(searchData.userPath, data.cFileName); } } while (Win32Native.FindNextFile(hnd, data)); // Make sure we quit with a sensible error. hr = Marshal.GetLastWin32Error(); // Demand pathdiscovery for all the parent dirs that are returned // I.e, if C:\temp\foo\bar is returned, demand C:\temp\foo if (numEntries > 0) { demandPathList.Add(GetDemandDir(searchData.fullPath, true)); } } finally { if (hnd != null) hnd.Dispose(); } } // End while } finally { Win32Native.SetErrorMode(oldMode); } // ERROR_FILE_NOT_FOUND is valid here because if the top level // dir doen't contain any subdirs and matching files then // we will get here with this errorcode from the searchStack walk if ((hr != 0) && (hr != Win32Native.ERROR_NO_MORE_FILES) && (hr != Win32Native.ERROR_FILE_NOT_FOUND)) { __Error.WinIOError(hr, searchData.fullPath); } // Demand pathdiscovery for all the parent dirs that are returned // I.e, if C:\temp\foo\bar is returned, demand C:\temp\foo if (demandPathList.Count > 0) { demandPaths = new String[demandPathList.Count]; demandPathList.CopyTo(demandPaths, 0); // No need to check for dupls as the demandPathList entries should be unique new FileIOPermission(FileIOPermissionAccess.PathDiscovery, demandPaths, false, false ).Demand(); } // Check for a string such as "C:\tmp", in which case we return // just the DirectoryInfo name. FindNextFile fails first time, and // data still contains a directory. /* if (includeDirs && numEntries==1 && (0!=(data.dwFileAttributes & Win32Native.FILE_ATTRIBUTE_DIRECTORY))) { String[] sa = new String[1]; sa[0] = Path.InternalCombine(searchData.userPath, data.cFileName); return sa; } */ // Return list of files/directories as an array of strings if (listSize == list.Length) return list; String[] items = new String[listSize]; Array.Copy(list, 0, items, 0, listSize); return items; }