diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 1c918ea8c2..141b7803c9 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -8242,24 +8242,54 @@ namespace System.Management.Automation.Internal /// A FileStream that can be used to interact with the file. 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; + /// + /// Tries to create a file stream on a file. + /// + /// The fully-qualified path to the file. + /// The name of the alternate data stream to open. + /// The FileMode of the file. + /// The FileAccess of the file. + /// The FileShare of the file. + /// A FileStream that can be used to interact with the file. + /// true if the stream was successfully created, otherwise false. + 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; } /// diff --git a/src/System.Management.Automation/utils/ClrFacade.cs b/src/System.Management.Automation/utils/ClrFacade.cs index 144844c3b4..02ecae8f6a 100644 --- a/src/System.Management.Automation/utils/ClrFacade.cs +++ b/src/System.Management.Automation/utils/ClrFacade.cs @@ -224,57 +224,50 @@ namespace System.Management.Automation /// 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; }