diff --git a/src/Middleware/StaticFiles/src/HtmlDirectoryFormatter.cs b/src/Middleware/StaticFiles/src/HtmlDirectoryFormatter.cs index a5ac8e7ea1..e27624ec1c 100644 --- a/src/Middleware/StaticFiles/src/HtmlDirectoryFormatter.cs +++ b/src/Middleware/StaticFiles/src/HtmlDirectoryFormatter.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.IO; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -33,7 +34,7 @@ namespace Microsoft.AspNetCore.StaticFiles throw new ArgumentNullException(nameof(encoder)); } _htmlEncoder = encoder; - } + } /// /// Generates an HTML view for a directory. @@ -81,8 +82,8 @@ namespace Microsoft.AspNetCore.StaticFiles margin-top: 5px; margin-bottom: 0px;} #index { - border-collapse: separate; - border-spacing: 0; + border-collapse: separate; + border-spacing: 0; margin: 0 0 20px; } #index th { vertical-align: bottom; @@ -132,27 +133,66 @@ namespace Microsoft.AspNetCore.StaticFiles foreach (var subdir in contents.Where(info => info.IsDirectory)) { - builder.AppendFormat(@" + // Collect directory metadata in a try...catch in case the file is deleted while we're getting the data. + // The metadata is retrieved prior to calling AppendFormat so if it throws, we won't have written a row + // to the table. + try + { + builder.AppendFormat(@" {0}/ {1} ", - HtmlEncode(subdir.Name), - HtmlEncode(subdir.LastModified.ToString(CultureInfo.CurrentCulture))); + HtmlEncode(subdir.Name), + HtmlEncode(subdir.LastModified.ToString(CultureInfo.CurrentCulture))); + } + catch (DirectoryNotFoundException) + { + // The physical DirectoryInfo class doesn't appear to throw for either + // of Name or LastWriteTimeUtc (which backs LastModified in the physical provider) + // if the directory doesn't exist. However, we don't know what other providers might do. + + // Just skip this directory. It was deleted while we were enumerating. + } + catch (FileNotFoundException) + { + // The physical DirectoryInfo class doesn't appear to throw for either + // of Name or LastWriteTimeUtc (which backs LastModified in the physical provider) + // if the directory doesn't exist. However, we don't know what other providers might do. + + // Just skip this directory. It was deleted while we were enumerating. + } } foreach (var file in contents.Where(info => !info.IsDirectory)) { - builder.AppendFormat(@" + // Collect file metadata in a try...catch in case the file is deleted while we're getting the data. + // The metadata is retrieved prior to calling AppendFormat so if it throws, we won't have written a row + // to the table. + try + { + builder.AppendFormat(@" {0} {1} {2} ", - HtmlEncode(file.Name), - HtmlEncode(file.Length.ToString("n0", CultureInfo.CurrentCulture)), - HtmlEncode(file.LastModified.ToString(CultureInfo.CurrentCulture))); + HtmlEncode(file.Name), + HtmlEncode(file.Length.ToString("n0", CultureInfo.CurrentCulture)), + HtmlEncode(file.LastModified.ToString(CultureInfo.CurrentCulture))); + } + catch (DirectoryNotFoundException) + { + // There doesn't appear to be a case where DirectoryNotFound is thrown in the physical provider, + // but we don't know what other providers might do. + + // Just skip this file. It was deleted while we were enumerating. + } + catch (FileNotFoundException) + { + // Just skip this file. It was deleted while we were enumerating. + } } builder.Append(@"