[Trait("Category", "WindowsOSOnly")] // need to investigate if equivalent on Unix
        public void DeleteDirectoryContentsHandleOpen()
        {
            string directory = Path.Combine(TemporaryDirectory, "directoryToDelete");

            Directory.CreateDirectory(directory);
            string openFile = Path.Combine(directory, "openfileDelDirContents.txt");

            using (Stream s = new FileStream(openFile, FileMode.Create))
            {
                Exception exception = null;
                s.Write(new byte[] { 1, 2, 3 }, 0, 3);
                try
                {
                    FileUtilities.DeleteDirectoryContents(directory);
                }
                catch (BuildXLException ex)
                {
                    exception = ex;
                }

                XAssert.IsNotNull(exception, "Expected failure since a handle to a contained file was still open");
                XAssert.IsTrue(exception.Message.Contains(FileUtilitiesMessages.FileDeleteFailed + NormalizeDirectoryPath(openFile)), exception.Message);
                XAssert.IsTrue(exception.Message.Contains("Handle was used by"), exception.Message);
            }

            // Try again with the handle closed. There should be no crash.
            FileUtilities.DeleteDirectoryContents(directory);

            // Clean up files so we don't leave undeletable files on disk
            FileUtilities.DeleteDirectoryContents(directory, deleteRootDirectory: true);
        }
        public void DeleteDirectoryContents()
        {
            string rootDir = GetFullPath("Directory");

            Directory.CreateDirectory(rootDir);
            Directory.CreateDirectory(Path.Combine(rootDir, "subdir1"));
            Directory.CreateDirectory(Path.Combine(rootDir, "subdir2"));

            // Create a readonly file
            string nestedFile = Path.Combine(rootDir, "subdir1", "File.txt");

            File.WriteAllText(nestedFile, "asdf");
            File.SetAttributes(nestedFile, FileAttributes.ReadOnly);

            // And a normal file
            File.WriteAllText(Path.Combine(rootDir, "File.txt"), "asdf");

            string exeLink = Path.Combine(rootDir, "hardlink");

            XAssert.IsTrue(CreateHardLinkIfSupported(link: exeLink, linkTarget: DummyWaiter.GetDummyWaiterExeLocation()));

            // Add a hardlink
            using (var waiter = DummyWaiter.RunAndWait())
            {
                FileUtilities.DeleteDirectoryContents(rootDir);
            }

            XAssert.AreEqual(0, Directory.GetFileSystemEntries(rootDir).Length);
        }
        public void RetryEmptyDirectoryDelete()
        {
            // Create an empty directory
            string dir = Path.Combine(TemporaryDirectory, "dir");

            Directory.CreateDirectory(dir);

            SafeFileHandle childHandle = null;

            FileUtilities.TryCreateOrOpenFile(
                dir,
                FileDesiredAccess.GenericRead,
                FileShare.Read,
                FileMode.Open,
                FileFlagsAndAttributes.FileFlagBackupSemantics,
                out childHandle);

            using (childHandle)
            {
                Exception exception = null;
                try
                {
                    // Fails because of handle open to /dir
                    FileUtilities.DeleteDirectoryContents(dir, deleteRootDirectory: true);
                }
                catch (Exception e)
                {
                    exception = e;
                }
                XAssert.IsTrue(exception != null);
                XAssert.IsTrue(FileUtilities.Exists(dir));
            }
        }
        public void PosixDeleteDirectoryStressTest()
        {
            FileUtilities.SkipPosixDelete = false;
            string target = Path.Combine(TemporaryDirectory, "loop");
            string nested = Path.Combine(target, "nested");

            for (int i = 0; i < 100000; ++i)
            {
                Directory.CreateDirectory(target);
                Directory.CreateDirectory(nested);
                FileUtilities.DeleteDirectoryContents(target, deleteRootDirectory: true);
            }
        }
Esempio n. 5
0
 public void MoveDeleteDirectoryStressTest()
 {
     try
     {
         FileUtilities.PosixDeleteMode = PosixDeleteMode.NoRun;
         string target = Path.Combine(TemporaryDirectory, "loop");
         string nested = Path.Combine(target, "nested");
         for (int i = 0; i < 100000; ++i)
         {
             Directory.CreateDirectory(target);
             Directory.CreateDirectory(nested);
             FileUtilities.DeleteDirectoryContents(target, deleteRootDirectory: true, tempDirectoryCleaner: MoveDeleteCleaner);
         }
     }
     finally
     {
         FileUtilities.PosixDeleteMode = PosixDeleteMode.RunFirst;
     }
 }
        public void TestDeleteDirectoryContentsWithExclusions()
        {
            // \topDir
            string topDir = Path.Combine(TemporaryDirectory, "top");

            Directory.CreateDirectory(topDir);

            // \topDir\nestedDir
            string nestedDir = Path.Combine(topDir, "nestedDir");

            Directory.CreateDirectory(nestedDir);

            // \topDir\nestedFile
            string nestedFile = Path.Combine(topDir, "nestedFile");

            File.WriteAllText(nestedFile, "asdf");

            // \topDir\nestedDir\doubleNestedFile
            string doubleNestedFile = Path.Combine(nestedDir, "doubleNestedFile");

            File.WriteAllText(doubleNestedFile, "hjkl");

            FileUtilities.DeleteDirectoryContents(
                path: topDir,
                deleteRootDirectory: true,
                shouldDelete: (path) =>
            {
                // exclude \nestedDir\*
                return(!path.Contains(nestedDir));
            });

            // Even though deleteRootDirectory was marked as true,
            // \topDir should still exist because \topDir\nestedDir was excluded
            XAssert.IsTrue(Directory.Exists(topDir));
            // Successfully delete non-excluded file
            XAssert.IsFalse(File.Exists(nestedFile));

            // Excluded entries stay
            XAssert.IsTrue(Directory.Exists(nestedDir));
            XAssert.IsTrue(File.Exists(doubleNestedFile));
        }
Esempio n. 7
0
        public void LongPathAccessControlTest()
        {
            var longPath = Enumerable.Range(0, NativeIOConstants.MaxDirectoryPath).Aggregate(TemporaryDirectory, (path, _) => Path.Combine(path, "dir"));
            var file     = Path.Combine(longPath, "fileWithWriteAccess.txt");

            FileUtilities.CreateDirectory(longPath);
            SafeFileHandle fileHandle;
            var            result = FileUtilities.TryCreateOrOpenFile(
                file,
                FileDesiredAccess.GenericWrite,
                FileShare.Delete,
                FileMode.Create,
                FileFlagsAndAttributes.FileAttributeNormal,
                out fileHandle);

            XAssert.IsTrue(result.Succeeded);

            FileUtilities.SetFileAccessControl(file, FileSystemRights.WriteAttributes, true);
            XAssert.IsTrue(FileUtilities.HasWritableAccessControl(file));

            //Delete the created directory
            fileHandle.Close();
            FileUtilities.DeleteDirectoryContents(longPath, deleteRootDirectory: true);
        }
        public void DeleteDirectoryContentsLongPath()
        {
            string originalRoot = GetFullPath("testRoot");

            // Create a directory with a path that's too long to normally delete
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < 100; i++)
            {
                sb.Append("a");
            }

            string rootDir = @"\\?\" + originalRoot;

            XAssert.IsTrue(CreateDirectoryW(rootDir, IntPtr.Zero));
            rootDir = rootDir + "\\" + sb.ToString();
            XAssert.IsTrue(CreateDirectoryW(rootDir, IntPtr.Zero));
            rootDir = rootDir + "\\" + sb.ToString();
            XAssert.IsTrue(CreateDirectoryW(rootDir, IntPtr.Zero));

            // Write some files in the directory. Set their attributes as readonly to exercise the related logic in DeleteFile
            string shorterPath = Path.Combine(originalRoot, "myFile.txt");

            File.WriteAllText(shorterPath, "foo");
            File.SetAttributes(shorterPath, FileAttributes.ReadOnly);

            // And a file with a filename longer than maxpath
            string         longerPath = rootDir + @"\myFile.txt";
            SafeFileHandle fileHandle;
            var            result = FileUtilities.TryCreateOrOpenFile(
                longerPath,
                FileDesiredAccess.GenericWrite,
                FileShare.Delete,
                FileMode.Create,
                FileFlagsAndAttributes.FileAttributeNormal,
                out fileHandle);

            XAssert.IsTrue(result.Succeeded);
            using (FileStream stream = new FileStream(fileHandle, FileAccess.Write))
            {
                stream.WriteByte(255);
            }

            FileUtilities.SetFileAttributes(longerPath, FileAttributes.ReadOnly);

            string exeLink = Path.Combine(rootDir, "hardlink");

            XAssert.IsTrue(CreateHardLinkIfSupported(link: exeLink, linkTarget: DummyWaiter.GetDummyWaiterExeLocation()));

            // Add a hardlink. Perform deletion attempts while the hardlink target is in use
            using (var waiter = DummyWaiter.RunAndWait())
            {
                // Attempt to delete with the managed API. This should fail because it contains nested paths that are too long
                bool isPathTooLong = false;
                try
                {
                    Directory.Delete(rootDir, recursive: true);
                }
                catch (PathTooLongException)
                {
                    isPathTooLong = true;
                }

                XAssert.IsTrue(isPathTooLong, "Expected to encounter a PathTooLongException. If no exception is thrown by the System.IO method, this test isn't validating anything");

                // Now use the native API. This should succeed
                FileUtilities.DeleteDirectoryContents(originalRoot);
            }

            XAssert.IsTrue(Directory.Exists(originalRoot));
            XAssert.AreEqual(0, Directory.GetFileSystemEntries(originalRoot).Length);
        }
        public void RetryDeleteDirectoryContentsIfContentsPendingDelete()
        {
            try
            {
                // Need to disable POSIX delete to reproduce the Windows pending deletion state,
                // which does not exist in POSIX
                FileUtilities.SkipPosixDelete = true;

                string dir = Path.Combine(TemporaryDirectory, "dir");
                Directory.CreateDirectory(dir);

                string nestedFile = Path.Combine(dir, "nestedFile");
                File.WriteAllText(nestedFile, "asdf");

                SafeFileHandle nestedFileHandle;
                FileUtilities.TryCreateOrOpenFile(
                    nestedFile,
                    FileDesiredAccess.GenericWrite,
                    FileShare.ReadWrite | FileShare.Delete,
                    FileMode.Open,
                    FileFlagsAndAttributes.None,
                    out nestedFileHandle);

                // Hold open a handle to \file, but allow all file sharing permissions
                using (nestedFileHandle)
                {
                    // Sanity check that pending delete doesn't always return true
                    XAssert.IsFalse(FileUtilities.IsPendingDelete(nestedFileHandle));

                    Exception exception = null;
                    try
                    {
                        // Fails because of the open file that cannot be deleted
                        FileUtilities.DeleteDirectoryContents(dir, true);
                    }
                    catch (BuildXLException e)
                    {
                        exception = e;
                        XAssert.IsTrue(e.Message.StartsWith(FileUtilitiesMessages.DeleteDirectoryContentsFailed + NormalizeDirectoryPath(dir)));

                        // Rebuild the exception message using StringBuilder to handle breaklines
                        StringBuilder builder = new StringBuilder();
                        builder.AppendLine(NormalizeDirectoryPath(nestedFile));
                        builder.AppendLine(FileUtilitiesMessages.NoProcessesUsingHandle);
                        builder.AppendLine(FileUtilitiesMessages.PathMayBePendingDeletion);
                        XAssert.IsTrue(e.Message.Contains(builder.ToString()));
                    }

                    XAssert.IsTrue(exception != null);

                    // Check the open handle forced the file to be placed on the Windows pending deletion queue
                    XAssert.IsTrue(FileUtilities.IsPendingDelete(nestedFileHandle));
                }

                // After the file handle is closed, the delete goes through
                XAssert.IsFalse(File.Exists(nestedFile));
            }
            finally
            {
                // Re-enable POSIX delete for the remainder of tests
                FileUtilities.SkipPosixDelete = false;
            }
        }
Esempio n. 10
0
        public void RetryDeleteDirectoryContentsIfContentsPendingDelete()
        {
            try
            {
                // Need to disable POSIX delete to reproduce the Windows pending deletion state,
                // which does not exist in POSIX
                FileUtilities.PosixDeleteMode = PosixDeleteMode.NoRun;

                string dir = Path.Combine(TemporaryDirectory, "dir");
                Directory.CreateDirectory(dir);

                string nestedFile = Path.Combine(dir, "nestedFile");
                File.WriteAllText(nestedFile, "asdf");

                SafeFileHandle nestedFileHandle;
                FileUtilities.TryCreateOrOpenFile(
                    nestedFile,
                    FileDesiredAccess.GenericWrite,
                    FileShare.ReadWrite | FileShare.Delete,
                    FileMode.Open,
                    FileFlagsAndAttributes.None,
                    out nestedFileHandle);

                // Hold open a handle to \file, but allow all file sharing permissions
                using (nestedFileHandle)
                {
                    // Sanity check that pending delete doesn't always return true
                    XAssert.IsFalse(FileUtilities.IsPendingDelete(nestedFileHandle));

                    Exception exception = null;
                    try
                    {
                        // Fails because of the open file that cannot be deleted
                        FileUtilities.DeleteDirectoryContents(dir, true);
                    }
                    catch (BuildXLException e)
                    {
                        exception = e;
                        XAssert.IsTrue(e.Message.StartsWith(FileUtilitiesMessages.DeleteDirectoryContentsFailed + NormalizeDirectoryPath(dir)));

                        // Rebuild the exception message using StringBuilder to handle breaklines
                        StringBuilder builder = new StringBuilder();
                        builder.AppendLine(NormalizeDirectoryPath(nestedFile));
                        builder.AppendLine(FileUtilitiesMessages.NoProcessesUsingHandle);
                        builder.AppendLine(FileUtilitiesMessages.PathMayBePendingDeletion);
                        XAssert.IsTrue(e.Message.Contains(builder.ToString()));
                    }

                    XAssert.IsTrue(exception != null);

                    // Check the open handle forced the file to be placed on the Windows pending deletion queue
                    XAssert.IsTrue(FileUtilities.IsPendingDelete(nestedFileHandle));
                }

                // After the file handle is closed, the delete goes through
                XAssert.IsFalse(File.Exists(nestedFile));

                // Check for retries and ERROR_DIR_NOT_EMPTY
                AssertVerboseEventLogged(EventId.RetryOnFailureException, Helpers.DefaultNumberOfAttempts);
                string logs       = EventListener.GetLog();
                var    numMatches = Regex.Matches(logs, Regex.Escape("Native: RemoveDirectoryW for RemoveDirectory failed (0x91: The directory is not empty")).Count;
                XAssert.AreEqual(Helpers.DefaultNumberOfAttempts, numMatches);
            }
            finally
            {
                // Re-enable POSIX delete for the remainder of tests
                FileUtilities.PosixDeleteMode = PosixDeleteMode.RunFirst;
            }
        }