Add a path for checking ZoneInformation without throwing an exception (#8025)

This commit is contained in:
Staffan Gustafsson 2018-10-24 06:34:15 +02:00 committed by Ilya
parent 05e9f78bcb
commit 3e4fa87901
2 changed files with 75 additions and 52 deletions

View file

@ -8242,24 +8242,54 @@ namespace System.Management.Automation.Internal
/// <returns>A FileStream that can be used to interact with the file.</returns>
internal static FileStream CreateFileStream(string path, string streamName, FileMode mode, FileAccess access, FileShare share)
{
if (path == null) throw new ArgumentNullException("path");
if (streamName == null) throw new ArgumentNullException("streamName");
if (!TryCreateFileStream(path, streamName, mode, access, share, out var stream))
{
string errorMessage = StringUtil.Format(
FileSystemProviderStrings.AlternateDataStreamNotFound, streamName, path);
throw new FileNotFoundException(errorMessage, $"{path}:{streamName}");
}
string adjustedStreamName = streamName.Trim();
adjustedStreamName = ":" + adjustedStreamName;
string resultPath = path + adjustedStreamName;
return stream;
}
if (mode == FileMode.Append) mode = FileMode.OpenOrCreate;
/// <summary>
/// Tries to create a file stream on a file.
/// </summary>
/// <param name="path">The fully-qualified path to the file.</param>
/// <param name="streamName">The name of the alternate data stream to open.</param>
/// <param name="mode">The FileMode of the file.</param>
/// <param name="access">The FileAccess of the file.</param>
/// <param name="share">The FileShare of the file.</param>
/// <param name="stream">A FileStream that can be used to interact with the file.</param>
/// <returns>true if the stream was successfully created, otherwise false.</returns>
internal static bool TryCreateFileStream(string path, string streamName, FileMode mode, FileAccess access, FileShare share, out FileStream stream)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (streamName == null)
{
throw new ArgumentNullException(nameof(streamName));
}
if (mode == FileMode.Append)
{
mode = FileMode.OpenOrCreate;
}
var resultPath = $"{path}:{streamName}";
SafeFileHandle handle = NativeMethods.CreateFile(resultPath, access, share, IntPtr.Zero, mode, 0, IntPtr.Zero);
if (handle.IsInvalid)
{
string errorMessage = StringUtil.Format(
FileSystemProviderStrings.AlternateDataStreamNotFound, streamName, path);
throw new FileNotFoundException(errorMessage, resultPath);
stream = null;
return false;
}
return new FileStream(handle, access);
stream = new FileStream(handle, access);
return true;
}
/// <summary>

View file

@ -224,57 +224,50 @@ namespace System.Management.Automation
/// </summary>
private static SecurityZone ReadFromZoneIdentifierDataStream(string filePath)
{
try
if (!AlternateDataStreamUtilities.TryCreateFileStream(filePath, "Zone.Identifier", FileMode.Open, FileAccess.Read, FileShare.Read, out var zoneDataStream))
{
FileStream zoneDataSteam = AlternateDataStreamUtilities.CreateFileStream(
filePath, "Zone.Identifier", FileMode.Open,
FileAccess.Read, FileShare.Read);
return SecurityZone.NoZone;
}
// If we successfully get the zone data stream, try to read the ZoneId information
using (StreamReader zoneDataReader = new StreamReader(zoneDataSteam, GetDefaultEncoding()))
// If we successfully get the zone data stream, try to read the ZoneId information
using (StreamReader zoneDataReader = new StreamReader(zoneDataStream, GetDefaultEncoding()))
{
string line = null;
bool zoneTransferMatched = false;
// After a lot experiments with Zone.CreateFromUrl/Zone.SecurityZone, the way it handles the alternate
// data stream 'Zone.Identifier' is observed as follows:
// 1. Read content of the data stream line by line. Each line is trimmed.
// 2. Try to match the current line with '^\[ZoneTransfer\]'.
// - if matching, then do step #3 starting from the next line
// - if not matching, then continue to do step #2 with the next line.
// 3. Try to match the current line with '^ZoneId\s*=\s*(.*)'
// - if matching, check if the ZoneId is valid. Then return the corresponding SecurityZone if valid, or 'NoZone' if invalid.
// - if not matching, then continue to do step #3 with the next line.
// 4. Reach EOF, then return 'NoZone'.
while ((line = zoneDataReader.ReadLine()) != null)
{
string line = null;
bool zoneTransferMatched = false;
// After a lot experiments with Zone.CreateFromUrl/Zone.SecurityZone, the way it handles the alternate
// data stream 'Zone.Identifier' is observed as follows:
// 1. Read content of the data stream line by line. Each line is trimmed.
// 2. Try to match the current line with '^\[ZoneTransfer\]'.
// - if matching, then do step #3 starting from the next line
// - if not matching, then continue to do step #2 with the next line.
// 3. Try to match the current line with '^ZoneId\s*=\s*(.*)'
// - if matching, check if the ZoneId is valid. Then return the corresponding SecurityZone if valid, or 'NoZone' if invalid.
// - if not matching, then continue to do step #3 with the next line.
// 4. Reach EOF, then return 'NoZone'.
while ((line = zoneDataReader.ReadLine()) != null)
line = line.Trim();
if (!zoneTransferMatched)
{
line = line.Trim();
if (!zoneTransferMatched)
{
zoneTransferMatched = Regex.IsMatch(line, @"^\[ZoneTransfer\]", RegexOptions.IgnoreCase);
}
else
{
Match match = Regex.Match(line, @"^ZoneId\s*=\s*(.*)", RegexOptions.IgnoreCase);
if (!match.Success) { continue; }
zoneTransferMatched = Regex.IsMatch(line, @"^\[ZoneTransfer\]", RegexOptions.IgnoreCase);
}
else
{
Match match = Regex.Match(line, @"^ZoneId\s*=\s*(.*)", RegexOptions.IgnoreCase);
if (!match.Success) { continue; }
// Match found. Validate ZoneId value.
string zoneIdRawValue = match.Groups[1].Value;
match = Regex.Match(zoneIdRawValue, @"^[+-]?\d+", RegexOptions.IgnoreCase);
if (!match.Success) { return SecurityZone.NoZone; }
// Match found. Validate ZoneId value.
string zoneIdRawValue = match.Groups[1].Value;
match = Regex.Match(zoneIdRawValue, @"^[+-]?\d+", RegexOptions.IgnoreCase);
if (!match.Success) { return SecurityZone.NoZone; }
string zoneId = match.Groups[0].Value;
SecurityZone result;
return LanguagePrimitives.TryConvertTo(zoneId, out result) ? result : SecurityZone.NoZone;
}
string zoneId = match.Groups[0].Value;
SecurityZone result;
return LanguagePrimitives.TryConvertTo(zoneId, out result) ? result : SecurityZone.NoZone;
}
}
}
catch (FileNotFoundException)
{
// FileNotFoundException may be thrown by AlternateDataStreamUtilities.CreateFileStream when the data stream 'Zone.Identifier'
// does not exist, or when the underlying file system doesn't support alternate data stream.
}
return SecurityZone.NoZone;
}