Skip to content

Commit 05be6e8

Browse files
committed
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.
1 parent c03b7fb commit 05be6e8

File tree

1 file changed

+56
-21
lines changed

1 file changed

+56
-21
lines changed

src/PowerShellEditorServices/Workspace/Workspace.cs

+56-21
Original file line numberDiff line numberDiff line change
@@ -65,20 +65,20 @@ public ScriptFile GetFile(string filePath)
6565
{
6666
Validate.IsNotNullOrEmptyString("filePath", filePath);
6767

68-
// Resolve the full file path
68+
// Resolve the full file path
6969
string resolvedFilePath = this.ResolveFilePath(filePath);
7070
string keyName = resolvedFilePath.ToLower();
7171

7272
// Make sure the file isn't already loaded into the workspace
7373
ScriptFile scriptFile = null;
7474
if (!this.workspaceFiles.TryGetValue(keyName, out scriptFile))
7575
{
76-
// This method allows FileNotFoundException to bubble up
76+
// This method allows FileNotFoundException to bubble up
7777
// if the file isn't found.
7878
using (FileStream fileStream = new FileStream(resolvedFilePath, FileMode.Open, FileAccess.Read))
7979
using (StreamReader streamReader = new StreamReader(fileStream, Encoding.UTF8))
8080
{
81-
scriptFile =
81+
scriptFile =
8282
new ScriptFile(
8383
resolvedFilePath,
8484
filePath,
@@ -115,15 +115,15 @@ public ScriptFile GetFileBuffer(string filePath, string initialBuffer)
115115
{
116116
Validate.IsNotNullOrEmptyString("filePath", filePath);
117117

118-
// Resolve the full file path
118+
// Resolve the full file path
119119
string resolvedFilePath = this.ResolveFilePath(filePath);
120120
string keyName = resolvedFilePath.ToLower();
121121

122122
// Make sure the file isn't already loaded into the workspace
123123
ScriptFile scriptFile = null;
124124
if (!this.workspaceFiles.TryGetValue(keyName, out scriptFile) && initialBuffer != null)
125125
{
126-
scriptFile =
126+
scriptFile =
127127
new ScriptFile(
128128
resolvedFilePath,
129129
filePath,
@@ -159,19 +159,19 @@ public void CloseFile(ScriptFile scriptFile)
159159
}
160160

161161
/// <summary>
162-
/// Gets all file references by recursively searching
162+
/// Gets all file references by recursively searching
163163
/// through referenced files in a scriptfile
164164
/// </summary>
165165
/// <param name="scriptFile">Contains the details and contents of an open script file</param>
166-
/// <returns>A scriptfile array where the first file
166+
/// <returns>A scriptfile array where the first file
167167
/// in the array is the "root file" of the search</returns>
168168
public ScriptFile[] ExpandScriptReferences(ScriptFile scriptFile)
169169
{
170170
Dictionary<string, ScriptFile> referencedScriptFiles = new Dictionary<string, ScriptFile>();
171171
List<ScriptFile> expandedReferences = new List<ScriptFile>();
172172

173173
// add original file so it's not searched for, then find all file references
174-
referencedScriptFiles.Add(scriptFile.Id, scriptFile);
174+
referencedScriptFiles.Add(scriptFile.Id, scriptFile);
175175
RecursivelyFindReferences(scriptFile, referencedScriptFiles);
176176

177177
// remove original file from referened file and add it as the first element of the
@@ -219,25 +219,60 @@ public string GetRelativePath(string filePath)
219219
/// <returns>An enumerator over the PowerShell files found in the workspace</returns>
220220
public IEnumerable<string> EnumeratePSFiles()
221221
{
222-
if (WorkspacePath == null
223-
|| !Directory.Exists(WorkspacePath))
222+
if (WorkspacePath == null || !Directory.Exists(WorkspacePath))
224223
{
225-
yield break;
224+
return Enumerable.Empty<string>();
226225
}
227226

227+
return this.RecursivelyEnumerateFiles(WorkspacePath);
228+
}
229+
230+
#endregion
231+
232+
#region Private Methods
233+
234+
private IEnumerable<string> RecursivelyEnumerateFiles(string folderPath)
235+
{
236+
var foundFiles = Enumerable.Empty<string>();
228237
var patterns = new string[] { @"*.ps1", @"*.psm1", @"*.psd1" };
229-
foreach(var pattern in patterns)
238+
239+
try
230240
{
231-
foreach (var file in Directory.EnumerateFiles(WorkspacePath, pattern, SearchOption.AllDirectories))
241+
IEnumerable<string> subDirs = Directory.EnumerateDirectories(folderPath);
242+
foreach (string dir in subDirs)
232243
{
233-
yield return file;
244+
foundFiles =
245+
foundFiles.Concat(
246+
RecursivelyEnumerateFiles(dir));
234247
}
235248
}
236-
}
249+
catch (UnauthorizedAccessException e)
250+
{
251+
Logger.WriteException(
252+
$"Could not enumerate files in the path '{folderPath}' due to the path not being accessible",
253+
e);
254+
}
237255

238-
#endregion
256+
foreach (var pattern in patterns)
257+
{
258+
try
259+
{
260+
foundFiles =
261+
foundFiles.Concat(
262+
Directory.EnumerateFiles(
263+
folderPath,
264+
pattern));
265+
}
266+
catch (UnauthorizedAccessException e)
267+
{
268+
Logger.WriteException(
269+
$"Could not enumerate files in the path '{folderPath}' due to a file not being accessible",
270+
e);
271+
}
272+
}
239273

240-
#region Private Methods
274+
return foundFiles;
275+
}
241276

242277
/// <summary>
243278
/// Recusrively searches through referencedFiles in scriptFiles
@@ -246,11 +281,11 @@ public IEnumerable<string> EnumeratePSFiles()
246281
/// <param name="scriptFile">Details an contents of "root" script file</param>
247282
/// <param name="referencedScriptFiles">A Dictionary of referenced script files</param>
248283
private void RecursivelyFindReferences(
249-
ScriptFile scriptFile,
284+
ScriptFile scriptFile,
250285
Dictionary<string, ScriptFile> referencedScriptFiles)
251286
{
252287
// Get the base path of the current script for use in resolving relative paths
253-
string baseFilePath =
288+
string baseFilePath =
254289
GetBaseFilePath(
255290
scriptFile.FilePath);
256291

@@ -347,12 +382,12 @@ private string GetBaseFilePath(string filePath)
347382
// TODO: Assert instead?
348383
throw new InvalidOperationException(
349384
string.Format(
350-
"Must provide a full path for originalScriptPath: {0}",
385+
"Must provide a full path for originalScriptPath: {0}",
351386
filePath));
352387
}
353388

354389
// Get the directory of the file path
355-
return Path.GetDirectoryName(filePath);
390+
return Path.GetDirectoryName(filePath);
356391
}
357392

358393
private string ResolveRelativeScriptPath(string baseFilePath, string relativePath)

0 commit comments

Comments
 (0)