PageRenderTime 27ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/System.IO.FileSystem/src/System/IO/Win32FileSystem.cs

https://gitlab.com/0072016/0072016-corefx-
C# | 733 lines | 555 code | 86 blank | 92 comment | 160 complexity | 39d76b05235e394018878aff048dc402 MD5 | raw file
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using Microsoft.Win32.SafeHandles;
  5. using System.Collections.Generic;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. namespace System.IO
  9. {
  10. internal sealed partial class Win32FileSystem : FileSystem
  11. {
  12. public override int MaxPath { get { return Interop.mincore.MAX_PATH; } }
  13. public override int MaxDirectoryPath { get { return Interop.mincore.MAX_DIRECTORY_PATH; } }
  14. public override void CopyFile(string sourceFullPath, string destFullPath, bool overwrite)
  15. {
  16. Interop.mincore.SECURITY_ATTRIBUTES secAttrs = default(Interop.mincore.SECURITY_ATTRIBUTES);
  17. int errorCode = Interop.mincore.CopyFile(sourceFullPath, destFullPath, !overwrite);
  18. if (errorCode != Interop.mincore.Errors.ERROR_SUCCESS)
  19. {
  20. String fileName = destFullPath;
  21. if (errorCode != Interop.mincore.Errors.ERROR_FILE_EXISTS)
  22. {
  23. // For a number of error codes (sharing violation, path
  24. // not found, etc) we don't know if the problem was with
  25. // the source or dest file. Try reading the source file.
  26. using (SafeFileHandle handle = Interop.mincore.UnsafeCreateFile(sourceFullPath, Win32FileStream.GENERIC_READ, FileShare.Read, ref secAttrs, FileMode.Open, 0, IntPtr.Zero))
  27. {
  28. if (handle.IsInvalid)
  29. fileName = sourceFullPath;
  30. }
  31. if (errorCode == Interop.mincore.Errors.ERROR_ACCESS_DENIED)
  32. {
  33. if (DirectoryExists(destFullPath))
  34. throw new IOException(SR.Format(SR.Arg_FileIsDirectory_Name, destFullPath), Interop.mincore.Errors.ERROR_ACCESS_DENIED);
  35. }
  36. }
  37. throw Win32Marshal.GetExceptionForWin32Error(errorCode, fileName);
  38. }
  39. }
  40. [System.Security.SecuritySafeCritical]
  41. public override void CreateDirectory(string fullPath)
  42. {
  43. if (PathInternal.IsDirectoryTooLong(fullPath))
  44. throw new PathTooLongException(SR.IO_PathTooLong);
  45. // We can save a bunch of work if the directory we want to create already exists. This also
  46. // saves us in the case where sub paths are inaccessible (due to ERROR_ACCESS_DENIED) but the
  47. // final path is accessible and the directory already exists. For example, consider trying
  48. // to create c:\Foo\Bar\Baz, where everything already exists but ACLS prevent access to c:\Foo
  49. // and c:\Foo\Bar. In that case, this code will think it needs to create c:\Foo, and c:\Foo\Bar
  50. // and fail to due so, causing an exception to be thrown. This is not what we want.
  51. if (DirectoryExists(fullPath))
  52. return;
  53. List<string> stackDir = new List<string>();
  54. // Attempt to figure out which directories don't exist, and only
  55. // create the ones we need. Note that InternalExists may fail due
  56. // to Win32 ACL's preventing us from seeing a directory, and this
  57. // isn't threadsafe.
  58. bool somepathexists = false;
  59. int length = fullPath.Length;
  60. // We need to trim the trailing slash or the code will try to create 2 directories of the same name.
  61. if (length >= 2 && PathHelpers.EndsInDirectorySeparator(fullPath))
  62. length--;
  63. int lengthRoot = PathInternal.GetRootLength(fullPath);
  64. if (length > lengthRoot)
  65. {
  66. // Special case root (fullpath = X:\\)
  67. int i = length - 1;
  68. while (i >= lengthRoot && !somepathexists)
  69. {
  70. String dir = fullPath.Substring(0, i + 1);
  71. if (!DirectoryExists(dir)) // Create only the ones missing
  72. stackDir.Add(dir);
  73. else
  74. somepathexists = true;
  75. while (i > lengthRoot && !PathInternal.IsDirectorySeparator(fullPath[i])) i--;
  76. i--;
  77. }
  78. }
  79. int count = stackDir.Count;
  80. // If we were passed a DirectorySecurity, convert it to a security
  81. // descriptor and set it in he call to CreateDirectory.
  82. Interop.mincore.SECURITY_ATTRIBUTES secAttrs = default(Interop.mincore.SECURITY_ATTRIBUTES);
  83. bool r = true;
  84. int firstError = 0;
  85. String errorString = fullPath;
  86. // If all the security checks succeeded create all the directories
  87. while (stackDir.Count > 0)
  88. {
  89. String name = stackDir[stackDir.Count - 1];
  90. stackDir.RemoveAt(stackDir.Count - 1);
  91. r = Interop.mincore.CreateDirectory(name, ref secAttrs);
  92. if (!r && (firstError == 0))
  93. {
  94. int currentError = Marshal.GetLastWin32Error();
  95. // While we tried to avoid creating directories that don't
  96. // exist above, there are at least two cases that will
  97. // cause us to see ERROR_ALREADY_EXISTS here. InternalExists
  98. // can fail because we didn't have permission to the
  99. // directory. Secondly, another thread or process could
  100. // create the directory between the time we check and the
  101. // time we try using the directory. Thirdly, it could
  102. // fail because the target does exist, but is a file.
  103. if (currentError != Interop.mincore.Errors.ERROR_ALREADY_EXISTS)
  104. firstError = currentError;
  105. else
  106. {
  107. // If there's a file in this directory's place, or if we have ERROR_ACCESS_DENIED when checking if the directory already exists throw.
  108. if (File.InternalExists(name) || (!DirectoryExists(name, out currentError) && currentError == Interop.mincore.Errors.ERROR_ACCESS_DENIED))
  109. {
  110. firstError = currentError;
  111. errorString = name;
  112. }
  113. }
  114. }
  115. }
  116. // We need this check to mask OS differences
  117. // Handle CreateDirectory("X:\\") when X: doesn't exist. Similarly for n/w paths.
  118. if ((count == 0) && !somepathexists)
  119. {
  120. String root = Directory.InternalGetDirectoryRoot(fullPath);
  121. if (!DirectoryExists(root))
  122. throw Win32Marshal.GetExceptionForWin32Error(Interop.mincore.Errors.ERROR_PATH_NOT_FOUND, root);
  123. return;
  124. }
  125. // Only throw an exception if creating the exact directory we
  126. // wanted failed to work correctly.
  127. if (!r && (firstError != 0))
  128. throw Win32Marshal.GetExceptionForWin32Error(firstError, errorString);
  129. }
  130. public override void DeleteFile(System.String fullPath)
  131. {
  132. bool r = Interop.mincore.DeleteFile(fullPath);
  133. if (!r)
  134. {
  135. int errorCode = Marshal.GetLastWin32Error();
  136. if (errorCode == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND)
  137. return;
  138. else
  139. throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
  140. }
  141. }
  142. public override bool DirectoryExists(string fullPath)
  143. {
  144. int lastError = Interop.mincore.Errors.ERROR_SUCCESS;
  145. return DirectoryExists(fullPath, out lastError);
  146. }
  147. private bool DirectoryExists(String path, out int lastError)
  148. {
  149. Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA();
  150. lastError = FillAttributeInfo(path, ref data, false, true);
  151. return (lastError == 0) && (data.fileAttributes != -1)
  152. && ((data.fileAttributes & Interop.mincore.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) != 0);
  153. }
  154. public override IEnumerable<string> EnumeratePaths(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget)
  155. {
  156. return Win32FileSystemEnumerableFactory.CreateFileNameIterator(fullPath, fullPath, searchPattern,
  157. (searchTarget & SearchTarget.Files) == SearchTarget.Files,
  158. (searchTarget & SearchTarget.Directories) == SearchTarget.Directories,
  159. searchOption);
  160. }
  161. public override IEnumerable<FileSystemInfo> EnumerateFileSystemInfos(string fullPath, string searchPattern, SearchOption searchOption, SearchTarget searchTarget)
  162. {
  163. switch (searchTarget)
  164. {
  165. case SearchTarget.Directories:
  166. return Win32FileSystemEnumerableFactory.CreateDirectoryInfoIterator(fullPath, fullPath, searchPattern, searchOption);
  167. case SearchTarget.Files:
  168. return Win32FileSystemEnumerableFactory.CreateFileInfoIterator(fullPath, fullPath, searchPattern, searchOption);
  169. case SearchTarget.Both:
  170. return Win32FileSystemEnumerableFactory.CreateFileSystemInfoIterator(fullPath, fullPath, searchPattern, searchOption);
  171. default:
  172. throw new ArgumentException(SR.ArgumentOutOfRange_Enum, nameof(searchTarget));
  173. }
  174. }
  175. // Returns 0 on success, otherwise a Win32 error code. Note that
  176. // classes should use -1 as the uninitialized state for dataInitialized.
  177. [System.Security.SecurityCritical] // auto-generated
  178. internal static int FillAttributeInfo(String path, ref Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA data, bool tryagain, bool returnErrorOnNotFound)
  179. {
  180. int errorCode = 0;
  181. if (tryagain) // someone has a handle to the file open, or other error
  182. {
  183. Interop.mincore.WIN32_FIND_DATA findData;
  184. findData = new Interop.mincore.WIN32_FIND_DATA();
  185. // Remove trailing slash since this can cause grief to FindFirstFile. You will get an invalid argument error
  186. String tempPath = path.TrimEnd(PathHelpers.DirectorySeparatorChars);
  187. // For floppy drives, normally the OS will pop up a dialog saying
  188. // there is no disk in drive A:, please insert one. We don't want that.
  189. // SetErrorMode will let us disable this, but we should set the error
  190. // mode back, since this may have wide-ranging effects.
  191. uint oldMode = Interop.mincore.SetErrorMode(Interop.mincore.SEM_FAILCRITICALERRORS);
  192. try
  193. {
  194. bool error = false;
  195. SafeFindHandle handle = Interop.mincore.FindFirstFile(tempPath, ref findData);
  196. try
  197. {
  198. if (handle.IsInvalid)
  199. {
  200. error = true;
  201. errorCode = Marshal.GetLastWin32Error();
  202. if (errorCode == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND ||
  203. errorCode == Interop.mincore.Errors.ERROR_PATH_NOT_FOUND ||
  204. errorCode == Interop.mincore.Errors.ERROR_NOT_READY) // floppy device not ready
  205. {
  206. if (!returnErrorOnNotFound)
  207. {
  208. // Return default value for backward compatibility
  209. errorCode = 0;
  210. data.fileAttributes = -1;
  211. }
  212. }
  213. return errorCode;
  214. }
  215. }
  216. finally
  217. {
  218. // Close the Win32 handle
  219. try
  220. {
  221. handle.Dispose();
  222. }
  223. catch
  224. {
  225. // if we're already returning an error, don't throw another one.
  226. if (!error)
  227. {
  228. throw Win32Marshal.GetExceptionForLastWin32Error();
  229. }
  230. }
  231. }
  232. }
  233. finally
  234. {
  235. Interop.mincore.SetErrorMode(oldMode);
  236. }
  237. // Copy the information to data
  238. data.PopulateFrom(ref findData);
  239. }
  240. else
  241. {
  242. // For floppy drives, normally the OS will pop up a dialog saying
  243. // there is no disk in drive A:, please insert one. We don't want that.
  244. // SetErrorMode will let us disable this, but we should set the error
  245. // mode back, since this may have wide-ranging effects.
  246. bool success = false;
  247. uint oldMode = Interop.mincore.SetErrorMode(Interop.mincore.SEM_FAILCRITICALERRORS);
  248. try
  249. {
  250. success = Interop.mincore.GetFileAttributesEx(path, Interop.mincore.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data);
  251. }
  252. finally
  253. {
  254. Interop.mincore.SetErrorMode(oldMode);
  255. }
  256. if (!success)
  257. {
  258. errorCode = Marshal.GetLastWin32Error();
  259. if (errorCode != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND &&
  260. errorCode != Interop.mincore.Errors.ERROR_PATH_NOT_FOUND &&
  261. errorCode != Interop.mincore.Errors.ERROR_NOT_READY) // floppy device not ready
  262. {
  263. // In case someone latched onto the file. Take the perf hit only for failure
  264. return FillAttributeInfo(path, ref data, true, returnErrorOnNotFound);
  265. }
  266. else
  267. {
  268. if (!returnErrorOnNotFound)
  269. {
  270. // Return default value for backward compatibility
  271. errorCode = 0;
  272. data.fileAttributes = -1;
  273. }
  274. }
  275. }
  276. }
  277. return errorCode;
  278. }
  279. public override bool FileExists(System.String fullPath)
  280. {
  281. Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA();
  282. int errorCode = FillAttributeInfo(fullPath, ref data, false, true);
  283. return (errorCode == 0) && (data.fileAttributes != -1)
  284. && ((data.fileAttributes & Interop.mincore.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0);
  285. }
  286. public override FileAttributes GetAttributes(string fullPath)
  287. {
  288. Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA();
  289. int errorCode = FillAttributeInfo(fullPath, ref data, false, true);
  290. if (errorCode != 0)
  291. throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
  292. return (FileAttributes)data.fileAttributes;
  293. }
  294. public override string GetCurrentDirectory()
  295. {
  296. StringBuilder sb = StringBuilderCache.Acquire(Interop.mincore.MAX_PATH + 1);
  297. if (Interop.mincore.GetCurrentDirectory(sb.Capacity, sb) == 0)
  298. throw Win32Marshal.GetExceptionForLastWin32Error();
  299. String currentDirectory = sb.ToString();
  300. // Note that if we have somehow put our command prompt into short
  301. // file name mode (i.e. by running edlin or a DOS grep, etc), then
  302. // this will return a short file name.
  303. if (currentDirectory.IndexOf('~') >= 0)
  304. {
  305. int r = Interop.mincore.GetLongPathName(currentDirectory, sb, sb.Capacity);
  306. if (r == 0 || r >= Interop.mincore.MAX_PATH)
  307. {
  308. int errorCode = Marshal.GetLastWin32Error();
  309. if (r >= Interop.mincore.MAX_PATH)
  310. errorCode = Interop.mincore.Errors.ERROR_FILENAME_EXCED_RANGE;
  311. if (errorCode != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND &&
  312. errorCode != Interop.mincore.Errors.ERROR_PATH_NOT_FOUND &&
  313. errorCode != Interop.mincore.Errors.ERROR_INVALID_FUNCTION && // by design - enough said.
  314. errorCode != Interop.mincore.Errors.ERROR_ACCESS_DENIED)
  315. throw Win32Marshal.GetExceptionForWin32Error(errorCode);
  316. }
  317. currentDirectory = sb.ToString();
  318. }
  319. StringBuilderCache.Release(sb);
  320. return currentDirectory;
  321. }
  322. public override DateTimeOffset GetCreationTime(string fullPath)
  323. {
  324. Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA();
  325. int errorCode = FillAttributeInfo(fullPath, ref data, false, false);
  326. if (errorCode != 0)
  327. throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
  328. long dt = ((long)(data.ftCreationTimeHigh) << 32) | ((long)data.ftCreationTimeLow);
  329. return DateTimeOffset.FromFileTime(dt);
  330. }
  331. public override IFileSystemObject GetFileSystemInfo(string fullPath, bool asDirectory)
  332. {
  333. return asDirectory ?
  334. (IFileSystemObject)new DirectoryInfo(fullPath, null) :
  335. (IFileSystemObject)new FileInfo(fullPath, null);
  336. }
  337. public override DateTimeOffset GetLastAccessTime(string fullPath)
  338. {
  339. Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA();
  340. int errorCode = FillAttributeInfo(fullPath, ref data, false, false);
  341. if (errorCode != 0)
  342. throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
  343. long dt = ((long)(data.ftLastAccessTimeHigh) << 32) | ((long)data.ftLastAccessTimeLow);
  344. return DateTimeOffset.FromFileTime(dt);
  345. }
  346. public override DateTimeOffset GetLastWriteTime(string fullPath)
  347. {
  348. Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA();
  349. int errorCode = FillAttributeInfo(fullPath, ref data, false, false);
  350. if (errorCode != 0)
  351. throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
  352. long dt = ((long)data.ftLastWriteTimeHigh << 32) | ((long)data.ftLastWriteTimeLow);
  353. return DateTimeOffset.FromFileTime(dt);
  354. }
  355. public override void MoveDirectory(string sourceFullPath, string destFullPath)
  356. {
  357. if (!Interop.mincore.MoveFile(sourceFullPath, destFullPath))
  358. {
  359. int errorCode = Marshal.GetLastWin32Error();
  360. if (errorCode == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND)
  361. throw Win32Marshal.GetExceptionForWin32Error(Interop.mincore.Errors.ERROR_PATH_NOT_FOUND, sourceFullPath);
  362. // 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.
  363. if (errorCode == Interop.mincore.Errors.ERROR_ACCESS_DENIED) // WinNT throws IOException. This check is for Win9x. We can't change it for backcomp.
  364. throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, sourceFullPath), Win32Marshal.MakeHRFromErrorCode(errorCode));
  365. throw Win32Marshal.GetExceptionForWin32Error(errorCode);
  366. }
  367. }
  368. public override void MoveFile(string sourceFullPath, string destFullPath)
  369. {
  370. if (!Interop.mincore.MoveFile(sourceFullPath, destFullPath))
  371. {
  372. throw Win32Marshal.GetExceptionForLastWin32Error();
  373. }
  374. }
  375. public override FileStreamBase Open(string fullPath, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, FileStream parent)
  376. {
  377. return new Win32FileStream(fullPath, mode, access, share, bufferSize, options, parent);
  378. }
  379. [System.Security.SecurityCritical]
  380. private static SafeFileHandle OpenHandle(string fullPath, bool asDirectory)
  381. {
  382. String root = fullPath.Substring(0, PathInternal.GetRootLength(fullPath));
  383. if (root == fullPath && root[1] == Path.VolumeSeparatorChar)
  384. {
  385. // intentionally not fullpath, most upstack public APIs expose this as path.
  386. throw new ArgumentException(SR.Arg_PathIsVolume, "path");
  387. }
  388. Interop.mincore.SECURITY_ATTRIBUTES secAttrs = default(Interop.mincore.SECURITY_ATTRIBUTES);
  389. SafeFileHandle handle = Interop.mincore.SafeCreateFile(
  390. fullPath,
  391. (int)Interop.mincore.GenericOperations.GENERIC_WRITE,
  392. FileShare.ReadWrite | FileShare.Delete,
  393. ref secAttrs,
  394. FileMode.Open,
  395. asDirectory ? (int)Interop.mincore.FileOperations.FILE_FLAG_BACKUP_SEMANTICS : (int)FileOptions.None,
  396. IntPtr.Zero
  397. );
  398. if (handle.IsInvalid)
  399. {
  400. int errorCode = Marshal.GetLastWin32Error();
  401. // NT5 oddity - when trying to open "C:\" as a File,
  402. // we usually get ERROR_PATH_NOT_FOUND from the OS. We should
  403. // probably be consistent w/ every other directory.
  404. if (!asDirectory && errorCode == Interop.mincore.Errors.ERROR_PATH_NOT_FOUND && fullPath.Equals(Directory.GetDirectoryRoot(fullPath)))
  405. errorCode = Interop.mincore.Errors.ERROR_ACCESS_DENIED;
  406. throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
  407. }
  408. return handle;
  409. }
  410. public override void RemoveDirectory(string fullPath, bool recursive)
  411. {
  412. // Do not recursively delete through reparse points. Perhaps in a
  413. // future version we will add a new flag to control this behavior,
  414. // but for now we're much safer if we err on the conservative side.
  415. // This applies to symbolic links and mount points.
  416. Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.mincore.WIN32_FILE_ATTRIBUTE_DATA();
  417. int errorCode = FillAttributeInfo(fullPath, ref data, false, true);
  418. if (errorCode != 0)
  419. {
  420. // Ensure we throw a DirectoryNotFoundException.
  421. if (errorCode == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND)
  422. errorCode = Interop.mincore.Errors.ERROR_PATH_NOT_FOUND;
  423. throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
  424. }
  425. if (((FileAttributes)data.fileAttributes & FileAttributes.ReparsePoint) != 0)
  426. recursive = false;
  427. // We want extended syntax so we can delete "extended" subdirectories and files
  428. // (most notably ones with trailing whitespace or periods)
  429. RemoveDirectoryHelper(PathInternal.EnsureExtendedPrefix(fullPath), recursive, true);
  430. }
  431. [System.Security.SecurityCritical] // auto-generated
  432. private static void RemoveDirectoryHelper(string fullPath, bool recursive, bool throwOnTopLevelDirectoryNotFound)
  433. {
  434. bool r;
  435. int errorCode;
  436. Exception ex = null;
  437. // Do not recursively delete through reparse points. Perhaps in a
  438. // future version we will add a new flag to control this behavior,
  439. // but for now we're much safer if we err on the conservative side.
  440. // This applies to symbolic links and mount points.
  441. // Note the logic to check whether fullPath is a reparse point is
  442. // in Delete(String, String, bool), and will set "recursive" to false.
  443. // Note that Win32's DeleteFile and RemoveDirectory will just delete
  444. // the reparse point itself.
  445. if (recursive)
  446. {
  447. Interop.mincore.WIN32_FIND_DATA data = new Interop.mincore.WIN32_FIND_DATA();
  448. // Open a Find handle
  449. using (SafeFindHandle hnd = Interop.mincore.FindFirstFile(Directory.EnsureTrailingDirectorySeparator(fullPath) + "*", ref data))
  450. {
  451. if (hnd.IsInvalid)
  452. throw Win32Marshal.GetExceptionForLastWin32Error(fullPath);
  453. do
  454. {
  455. bool isDir = (0 != (data.dwFileAttributes & Interop.mincore.FileAttributes.FILE_ATTRIBUTE_DIRECTORY));
  456. if (isDir)
  457. {
  458. // Skip ".", "..".
  459. if (data.cFileName.Equals(".") || data.cFileName.Equals(".."))
  460. continue;
  461. // Recurse for all directories, unless they are
  462. // reparse points. Do not follow mount points nor
  463. // symbolic links, but do delete the reparse point
  464. // itself.
  465. bool shouldRecurse = (0 == (data.dwFileAttributes & (int)FileAttributes.ReparsePoint));
  466. if (shouldRecurse)
  467. {
  468. string newFullPath = Path.Combine(fullPath, data.cFileName);
  469. try
  470. {
  471. RemoveDirectoryHelper(newFullPath, recursive, false);
  472. }
  473. catch (Exception e)
  474. {
  475. if (ex == null)
  476. ex = e;
  477. }
  478. }
  479. else
  480. {
  481. // Check to see if this is a mount point, and
  482. // unmount it.
  483. if (data.dwReserved0 == Interop.mincore.IOReparseOptions.IO_REPARSE_TAG_MOUNT_POINT)
  484. {
  485. // Use full path plus a trailing '\'
  486. String mountPoint = Path.Combine(fullPath, data.cFileName + PathHelpers.DirectorySeparatorCharAsString);
  487. if (!Interop.mincore.DeleteVolumeMountPoint(mountPoint))
  488. {
  489. errorCode = Marshal.GetLastWin32Error();
  490. if (errorCode != Interop.mincore.Errors.ERROR_SUCCESS &&
  491. errorCode != Interop.mincore.Errors.ERROR_PATH_NOT_FOUND)
  492. {
  493. try
  494. {
  495. throw Win32Marshal.GetExceptionForWin32Error(errorCode, data.cFileName);
  496. }
  497. catch (Exception e)
  498. {
  499. if (ex == null)
  500. ex = e;
  501. }
  502. }
  503. }
  504. }
  505. // RemoveDirectory on a symbolic link will
  506. // remove the link itself.
  507. String reparsePoint = Path.Combine(fullPath, data.cFileName);
  508. r = Interop.mincore.RemoveDirectory(reparsePoint);
  509. if (!r)
  510. {
  511. errorCode = Marshal.GetLastWin32Error();
  512. if (errorCode != Interop.mincore.Errors.ERROR_PATH_NOT_FOUND)
  513. {
  514. try
  515. {
  516. throw Win32Marshal.GetExceptionForWin32Error(errorCode, data.cFileName);
  517. }
  518. catch (Exception e)
  519. {
  520. if (ex == null)
  521. ex = e;
  522. }
  523. }
  524. }
  525. }
  526. }
  527. else
  528. {
  529. String fileName = Path.Combine(fullPath, data.cFileName);
  530. r = Interop.mincore.DeleteFile(fileName);
  531. if (!r)
  532. {
  533. errorCode = Marshal.GetLastWin32Error();
  534. if (errorCode != Interop.mincore.Errors.ERROR_FILE_NOT_FOUND)
  535. {
  536. try
  537. {
  538. throw Win32Marshal.GetExceptionForWin32Error(errorCode, data.cFileName);
  539. }
  540. catch (Exception e)
  541. {
  542. if (ex == null)
  543. ex = e;
  544. }
  545. }
  546. }
  547. }
  548. } while (Interop.mincore.FindNextFile(hnd, ref data));
  549. // Make sure we quit with a sensible error.
  550. errorCode = Marshal.GetLastWin32Error();
  551. }
  552. if (ex != null)
  553. throw ex;
  554. if (errorCode != 0 && errorCode != Interop.mincore.Errors.ERROR_NO_MORE_FILES)
  555. throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
  556. }
  557. r = Interop.mincore.RemoveDirectory(fullPath);
  558. if (!r)
  559. {
  560. errorCode = Marshal.GetLastWin32Error();
  561. if (errorCode == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND) // A dubious error code.
  562. errorCode = Interop.mincore.Errors.ERROR_PATH_NOT_FOUND;
  563. // 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.
  564. if (errorCode == Interop.mincore.Errors.ERROR_ACCESS_DENIED)
  565. throw new IOException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, fullPath));
  566. // don't throw the DirectoryNotFoundException since this is a subdir and
  567. // there could be a race condition between two Directory.Delete callers
  568. if (errorCode == Interop.mincore.Errors.ERROR_PATH_NOT_FOUND && !throwOnTopLevelDirectoryNotFound)
  569. return;
  570. throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
  571. }
  572. }
  573. public override void SetAttributes(string fullPath, FileAttributes attributes)
  574. {
  575. SetAttributesInternal(fullPath, attributes);
  576. }
  577. private static void SetAttributesInternal(string fullPath, FileAttributes attributes)
  578. {
  579. bool r = Interop.mincore.SetFileAttributes(fullPath, (int)attributes);
  580. if (!r)
  581. {
  582. int errorCode = Marshal.GetLastWin32Error();
  583. if (errorCode == Interop.mincore.Errors.ERROR_INVALID_PARAMETER)
  584. throw new ArgumentException(SR.Arg_InvalidFileAttrs, nameof(attributes));
  585. throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
  586. }
  587. }
  588. public override void SetCreationTime(string fullPath, DateTimeOffset time, bool asDirectory)
  589. {
  590. SetCreationTimeInternal(fullPath, time, asDirectory);
  591. }
  592. private static void SetCreationTimeInternal(string fullPath, DateTimeOffset time, bool asDirectory)
  593. {
  594. using (SafeFileHandle handle = OpenHandle(fullPath, asDirectory))
  595. {
  596. bool r = Interop.mincore.SetFileTime(handle, creationTime: time.ToFileTime());
  597. if (!r)
  598. {
  599. throw Win32Marshal.GetExceptionForLastWin32Error(fullPath);
  600. }
  601. }
  602. }
  603. public override void SetCurrentDirectory(string fullPath)
  604. {
  605. if (!Interop.mincore.SetCurrentDirectory(fullPath))
  606. {
  607. // If path doesn't exist, this sets last error to 2 (File
  608. // not Found). LEGACY: This may potentially have worked correctly
  609. // on Win9x, maybe.
  610. int errorCode = Marshal.GetLastWin32Error();
  611. if (errorCode == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND)
  612. errorCode = Interop.mincore.Errors.ERROR_PATH_NOT_FOUND;
  613. throw Win32Marshal.GetExceptionForWin32Error(errorCode, fullPath);
  614. }
  615. }
  616. public override void SetLastAccessTime(string fullPath, DateTimeOffset time, bool asDirectory)
  617. {
  618. SetLastAccessTimeInternal(fullPath, time, asDirectory);
  619. }
  620. private static void SetLastAccessTimeInternal(string fullPath, DateTimeOffset time, bool asDirectory)
  621. {
  622. using (SafeFileHandle handle = OpenHandle(fullPath, asDirectory))
  623. {
  624. bool r = Interop.mincore.SetFileTime(handle, lastAccessTime: time.ToFileTime());
  625. if (!r)
  626. {
  627. throw Win32Marshal.GetExceptionForLastWin32Error(fullPath);
  628. }
  629. }
  630. }
  631. public override void SetLastWriteTime(string fullPath, DateTimeOffset time, bool asDirectory)
  632. {
  633. SetLastWriteTimeInternal(fullPath, time, asDirectory);
  634. }
  635. private static void SetLastWriteTimeInternal(string fullPath, DateTimeOffset time, bool asDirectory)
  636. {
  637. using (SafeFileHandle handle = OpenHandle(fullPath, asDirectory))
  638. {
  639. bool r = Interop.mincore.SetFileTime(handle, lastWriteTime: time.ToFileTime());
  640. if (!r)
  641. {
  642. throw Win32Marshal.GetExceptionForLastWin32Error(fullPath);
  643. }
  644. }
  645. }
  646. }
  647. }