From 05be6e80da7f3f02a0beaf38505f9724513241de Mon Sep 17 00:00:00 2001 From: David Wilson Date: Thu, 1 Jun 2017 11:36:53 -0700 Subject: [PATCH] Catch UnauthorizedAccessException in file path enumeration This change fixes an issue where the Workspace's file enumeration logic would throw an UnauthorizedAccessException when it encounters an unaccessible file or path. The fix is to make the file enumeration code more robust so that it catches these exceptions and continues enumerating as far as it can. Resolves PowerShell/vscode-powershell#813. --- .../Workspace/Workspace.cs | 77 ++++++++++++++----- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/src/PowerShellEditorServices/Workspace/Workspace.cs b/src/PowerShellEditorServices/Workspace/Workspace.cs index 79fbb9896..283831d17 100644 --- a/src/PowerShellEditorServices/Workspace/Workspace.cs +++ b/src/PowerShellEditorServices/Workspace/Workspace.cs @@ -65,7 +65,7 @@ public ScriptFile GetFile(string filePath) { Validate.IsNotNullOrEmptyString("filePath", filePath); - // Resolve the full file path + // Resolve the full file path string resolvedFilePath = this.ResolveFilePath(filePath); string keyName = resolvedFilePath.ToLower(); @@ -73,12 +73,12 @@ public ScriptFile GetFile(string filePath) ScriptFile scriptFile = null; if (!this.workspaceFiles.TryGetValue(keyName, out scriptFile)) { - // This method allows FileNotFoundException to bubble up + // This method allows FileNotFoundException to bubble up // if the file isn't found. using (FileStream fileStream = new FileStream(resolvedFilePath, FileMode.Open, FileAccess.Read)) using (StreamReader streamReader = new StreamReader(fileStream, Encoding.UTF8)) { - scriptFile = + scriptFile = new ScriptFile( resolvedFilePath, filePath, @@ -115,7 +115,7 @@ public ScriptFile GetFileBuffer(string filePath, string initialBuffer) { Validate.IsNotNullOrEmptyString("filePath", filePath); - // Resolve the full file path + // Resolve the full file path string resolvedFilePath = this.ResolveFilePath(filePath); string keyName = resolvedFilePath.ToLower(); @@ -123,7 +123,7 @@ public ScriptFile GetFileBuffer(string filePath, string initialBuffer) ScriptFile scriptFile = null; if (!this.workspaceFiles.TryGetValue(keyName, out scriptFile) && initialBuffer != null) { - scriptFile = + scriptFile = new ScriptFile( resolvedFilePath, filePath, @@ -159,11 +159,11 @@ public void CloseFile(ScriptFile scriptFile) } /// - /// Gets all file references by recursively searching + /// Gets all file references by recursively searching /// through referenced files in a scriptfile /// /// Contains the details and contents of an open script file - /// A scriptfile array where the first file + /// A scriptfile array where the first file /// in the array is the "root file" of the search public ScriptFile[] ExpandScriptReferences(ScriptFile scriptFile) { @@ -171,7 +171,7 @@ public ScriptFile[] ExpandScriptReferences(ScriptFile scriptFile) List expandedReferences = new List(); // add original file so it's not searched for, then find all file references - referencedScriptFiles.Add(scriptFile.Id, scriptFile); + referencedScriptFiles.Add(scriptFile.Id, scriptFile); RecursivelyFindReferences(scriptFile, referencedScriptFiles); // remove original file from referened file and add it as the first element of the @@ -219,25 +219,60 @@ public string GetRelativePath(string filePath) /// An enumerator over the PowerShell files found in the workspace public IEnumerable EnumeratePSFiles() { - if (WorkspacePath == null - || !Directory.Exists(WorkspacePath)) + if (WorkspacePath == null || !Directory.Exists(WorkspacePath)) { - yield break; + return Enumerable.Empty(); } + return this.RecursivelyEnumerateFiles(WorkspacePath); + } + + #endregion + + #region Private Methods + + private IEnumerable RecursivelyEnumerateFiles(string folderPath) + { + var foundFiles = Enumerable.Empty(); var patterns = new string[] { @"*.ps1", @"*.psm1", @"*.psd1" }; - foreach(var pattern in patterns) + + try { - foreach (var file in Directory.EnumerateFiles(WorkspacePath, pattern, SearchOption.AllDirectories)) + IEnumerable subDirs = Directory.EnumerateDirectories(folderPath); + foreach (string dir in subDirs) { - yield return file; + foundFiles = + foundFiles.Concat( + RecursivelyEnumerateFiles(dir)); } } - } + catch (UnauthorizedAccessException e) + { + Logger.WriteException( + $"Could not enumerate files in the path '{folderPath}' due to the path not being accessible", + e); + } - #endregion + foreach (var pattern in patterns) + { + try + { + foundFiles = + foundFiles.Concat( + Directory.EnumerateFiles( + folderPath, + pattern)); + } + catch (UnauthorizedAccessException e) + { + Logger.WriteException( + $"Could not enumerate files in the path '{folderPath}' due to a file not being accessible", + e); + } + } - #region Private Methods + return foundFiles; + } /// /// Recusrively searches through referencedFiles in scriptFiles @@ -246,11 +281,11 @@ public IEnumerable EnumeratePSFiles() /// Details an contents of "root" script file /// A Dictionary of referenced script files private void RecursivelyFindReferences( - ScriptFile scriptFile, + ScriptFile scriptFile, Dictionary referencedScriptFiles) { // Get the base path of the current script for use in resolving relative paths - string baseFilePath = + string baseFilePath = GetBaseFilePath( scriptFile.FilePath); @@ -347,12 +382,12 @@ private string GetBaseFilePath(string filePath) // TODO: Assert instead? throw new InvalidOperationException( string.Format( - "Must provide a full path for originalScriptPath: {0}", + "Must provide a full path for originalScriptPath: {0}", filePath)); } // Get the directory of the file path - return Path.GetDirectoryName(filePath); + return Path.GetDirectoryName(filePath); } private string ResolveRelativeScriptPath(string baseFilePath, string relativePath)