MegaBites-AI/Windows-powershell
0308
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.ObjectModel;5using System.Management.Automation.Provider;6 7using Dbg = System.Management.Automation;8 9#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings10#pragma warning disable 5650011 12namespace System.Management.Automation13{14 /// <summary>15 /// Holds the state of a Monad Shell session.16 /// </summary>17 internal sealed partial class SessionStateInternal18 {19 #region NavigationCmdletProvider accessors20 21 #region GetParentPath22 23 /// <summary>24 /// Gets the path to the parent object for the given object.25 /// </summary>26 /// <param name="path">27 /// The path to the object to get the parent path from28 /// </param>29 /// <param name="root">30 /// The root of the drive.31 /// </param>32 /// <returns>33 /// The path to the parent object34 /// </returns>35 /// <exception cref="ArgumentNullException">36 /// If <paramref name="path"/> is null.37 /// </exception>38 /// <exception cref="NotSupportedException">39 /// If the <paramref name="providerInstance"/> does not support this operation.40 /// </exception>41 /// <exception cref="PipelineStoppedException">42 /// If the pipeline is being stopped while executing the command.43 /// </exception>44 /// <exception cref="ProviderInvocationException">45 /// If the provider threw an exception.46 /// </exception>47 internal string GetParentPath(string path, string root)48 {49 if (path == null)50 {51 throw PSTraceSource.NewArgumentNullException(nameof(path));52 }53 54 CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);55 56 string result = GetParentPath(path, root, context);57 58 context.ThrowFirstErrorOrDoNothing();59 60 return result;61 }62 63 /// <summary>64 /// Gets the path to the parent object for the given object.65 /// </summary>66 /// <param name="path">67 /// The path to the object to get the parent path from68 /// </param>69 /// <param name="root">70 /// The root of the drive. Namespace providers should71 /// return the root if GetParentPath is called for the root.72 /// </param>73 /// <param name="context">74 /// The context which the core command is running.75 /// </param>76 /// <returns>77 /// The path to the parent object78 /// </returns>79 /// <exception cref="ArgumentNullException">80 /// If <paramref name="path"/> is null.81 /// </exception>82 /// <exception cref="NotSupportedException">83 /// If the <paramref name="providerInstance"/> does not support this operation.84 /// </exception>85 /// <exception cref="PipelineStoppedException">86 /// If the pipeline is being stopped while executing the command.87 /// </exception>88 /// <exception cref="ProviderInvocationException">89 /// If the provider threw an exception.90 /// </exception>91 internal string GetParentPath(92 string path,93 string root,94 CmdletProviderContext context)95 {96 return GetParentPath(path, root, context, false);97 }98 99 /// <summary>100 /// Gets the path to the parent object for the given object.101 /// Allow to use FileSystem as the default provider when the102 /// given path is drive-qualified and the drive cannot be found.103 /// </summary>104 /// <param name="path">105 /// The path to the object to get the parent path from106 /// </param>107 /// <param name="root">108 /// The root of the drive. Namespace providers should109 /// return the root if GetParentPath is called for the root.110 /// </param>111 /// <param name="context">112 /// The context which the core command is running.113 /// </param>114 /// <param name="useDefaultProvider">115 /// Specify whether to use default provider when needed.116 /// </param>117 /// <returns>118 /// The path to the parent object119 /// </returns>120 internal string GetParentPath(121 string path,122 string root,123 CmdletProviderContext context,124 bool useDefaultProvider)125 {126 if (path == null)127 {128 throw PSTraceSource.NewArgumentNullException(nameof(path));129 }130 131 CmdletProviderContext getProviderPathContext =132 new CmdletProviderContext(context);133 134 try135 {136 PSDriveInfo drive = null;137 ProviderInfo provider = null;138 139 try140 {141 Globber.GetProviderPath(142 path,143 getProviderPathContext,144 out provider,145 out drive);146 }147 catch (DriveNotFoundException)148 {149 // the path is sure to be drive_qualified and it is absolute path, otherwise the150 // drive would be set to the current drive and the DriveNotFoundException will not happen151 if (useDefaultProvider)152 {153 // the default provider is FileSystem154 provider = PublicSessionState.Internal.GetSingleProvider(Microsoft.PowerShell.Commands.FileSystemProvider.ProviderName);155 }156 else157 {158 throw;159 }160 }161 162 if (getProviderPathContext.HasErrors())163 {164 getProviderPathContext.WriteErrorsToContext(context);165 return null;166 }167 168 if (drive != null)169 {170 context.Drive = drive;171 }172 173 bool isProviderQualified = false;174 bool isDriveQualified = false;175 string qualifier = null;176 string pathNoQualifier = RemoveQualifier(path, provider, out qualifier, out isProviderQualified, out isDriveQualified);177 178 string result = GetParentPath(provider, pathNoQualifier, root, context);179 180 if (!string.IsNullOrEmpty(qualifier) && !string.IsNullOrEmpty(result))181 {182 result = AddQualifier(result, provider, qualifier, isProviderQualified, isDriveQualified);183 }184 185 return result;186 }187 finally188 {189 getProviderPathContext.RemoveStopReferral();190 }191 }192 193 private static string AddQualifier(string path, ProviderInfo provider, string qualifier, bool isProviderQualified, bool isDriveQualified)194 {195 string result = path;196 197 string formatString = "{1}";198 if (isProviderQualified)199 {200 formatString = "{0}::{1}";201 }202 else if (isDriveQualified)203 {204 // Porting note: on non-windows filesystem paths, there should be no colon in the path205 if (provider.VolumeSeparatedByColon)206 {207 formatString = "{0}:{1}";208 }209 else210 {211 formatString = "{0}{1}";212 }213 }214 215 result =216 string.Format(217 System.Globalization.CultureInfo.InvariantCulture,218 formatString,219 qualifier,220 path);221 222 return result;223 }224 225 /// <summary>226 /// Removes either the drive or provider qualifier or both from the path.227 /// </summary>228 /// <param name="path">229 /// The path to strip the provider qualifier from.230 /// </param>231 /// <param name="provider">232 /// The provider that should handle the RemoveQualifier call.233 /// </param>234 /// <param name="qualifier">235 /// Returns the qualifier of the path.236 /// </param>237 /// <param name="isProviderQualified">238 /// Returns true if the path is a provider-qualified path.239 /// </param>240 /// <param name="isDriveQualified">241 /// Returns true if the path is a drive-qualified path.242 /// </param>243 /// <returns>244 /// The path without the qualifier.245 /// </returns>246 private string RemoveQualifier(string path, ProviderInfo provider, out string qualifier, out bool isProviderQualified, out bool isDriveQualified)247 {248 Dbg.Diagnostics.Assert(249 path != null,250 "Path should be verified by the caller");251 252 string result = path;253 qualifier = null;254 isProviderQualified = false;255 isDriveQualified = false;256 257 if (LocationGlobber.IsProviderQualifiedPath(path, out qualifier))258 {259 isProviderQualified = true;260 261 int index = path.IndexOf("::", StringComparison.Ordinal);262 263 if (index != -1)264 {265 // remove the qualifier266 result = path.Substring(index + 2);267 }268 }269 else270 {271 if (Globber.IsAbsolutePath(path, out qualifier))272 {273 isDriveQualified = true;274 275 // Remove the drive name and colon, or just the drive name276 277 // Porting note: on non-windows there is no colon for qualified paths278 if (provider.VolumeSeparatedByColon)279 {280 result = path.Substring(qualifier.Length + 1);281 }282 else283 {284 result = path.Substring(qualifier.Length);285 }286 }287 }288 289 return result;290 }291 292 /// <summary>293 /// Gets the path to the parent object for the given object.294 /// </summary>295 /// <param name="provider">296 /// The provider that should handle the GetParentPath call.297 /// </param>298 /// <param name="path">299 /// The path to the object to get the parent path from300 /// </param>301 /// <param name="root">302 /// The root of the drive. Namespace providers should303 /// return the root if GetParentPath is called for the root.304 /// </param>305 /// <param name="context">306 /// The context which the core command is running.307 /// </param>308 /// <returns>309 /// The path to the parent object310 /// </returns>311 /// <remarks>312 /// This is internal so that it can be called from the LocationGlobber.313 /// </remarks>314 /// <exception cref="NotSupportedException">315 /// If the <paramref name="providerInstance"/> does not support this operation.316 /// </exception>317 /// <exception cref="PipelineStoppedException">318 /// If the pipeline is being stopped while executing the command.319 /// </exception>320 /// <exception cref="ProviderInvocationException">321 /// If the provider threw an exception.322 /// </exception>323 internal string GetParentPath(324 ProviderInfo provider,325 string path,326 string root,327 CmdletProviderContext context)328 {329 // All parameters should have been validated by caller330 Dbg.Diagnostics.Assert(331 provider != null,332 "Caller should validate provider before calling this method");333 334 Dbg.Diagnostics.Assert(335 path != null,336 "Caller should validate path before calling this method");337 338 Dbg.Diagnostics.Assert(339 root != null,340 "Caller should validate root before calling this method");341 342 Dbg.Diagnostics.Assert(343 context != null,344 "Caller should validate context before calling this method");345 346 CmdletProvider providerInstance = GetProviderInstance(provider);347 return GetParentPath(providerInstance, path, root, context);348 }349 350 /// <summary>351 /// Gets the path to the parent object for the given object.352 /// </summary>353 /// <param name="providerInstance">354 /// The instance of the provider that should handle the GetParentPath call.355 /// </param>356 /// <param name="path">357 /// The path to the object to get the parent path from358 /// </param>359 /// <param name="root">360 /// The root of the drive. Namespace providers should361 /// return the root if GetParentPath is called for the root.362 /// </param>363 /// <param name="context">364 /// The context which the core command is running.365 /// </param>366 /// <returns>367 /// The path to the parent object368 /// </returns>369 /// <remarks>370 /// This is internal so that it can be called from the LocationGlobber.371 /// </remarks>372 /// <exception cref="NotSupportedException">373 /// If the <paramref name="providerInstance"/> does not support this operation.374 /// </exception>375 /// <exception cref="PipelineStoppedException">376 /// If the pipeline is being stopped while executing the command.377 /// </exception>378 /// <exception cref="ProviderInvocationException">379 /// If the provider threw an exception.380 /// </exception>381 internal string GetParentPath(382 CmdletProvider providerInstance,383 string path,384 string root,385 CmdletProviderContext context)386 {387 // All parameters should have been validated by caller388 Dbg.Diagnostics.Assert(389 providerInstance != null,390 "Caller should validate providerInstance before calling this method");391 392 Dbg.Diagnostics.Assert(393 path != null,394 "Caller should validate path before calling this method");395 396 Dbg.Diagnostics.Assert(397 root != null,398 "Caller should validate root before calling this method");399 400 Dbg.Diagnostics.Assert(401 context != null,402 "Caller should validate context before calling this method");403 404 NavigationCmdletProvider navigationCmdletProvider =405 GetNavigationProviderInstance(providerInstance, false);406 407 string result = null;408 409 try410 {411 result = navigationCmdletProvider.GetParentPath(path, root, context);412 }413 catch (LoopFlowException)414 {415 throw;416 }417 catch (PipelineStoppedException)418 {419 throw;420 }421 catch (ActionPreferenceStopException)422 {423 throw;424 }425 catch (Exception e) // Catch-all OK, 3rd party callout.426 {427 throw NewProviderInvocationException(428 "GetParentPathProviderException",429 SessionStateStrings.GetParentPathProviderException,430 navigationCmdletProvider.ProviderInfo,431 path,432 e);433 }434 435 return result;436 }437 438 #endregion GetParentPath439 440 #region NormalizeRelativePath441 442 /// <summary>443 /// Normalizes the path that was passed in and returns the normalized path444 /// as a relative path to the basePath that was passed.445 /// </summary>446 /// <param name="path">447 /// An MSH path to an item. The item should exist448 /// or the provider should write out an error.449 /// </param>450 /// <param name="basePath">451 /// The path that the return value should be relative to.452 /// </param>453 /// <returns>454 /// A normalized path that is relative to the basePath that was passed.455 /// </returns>456 /// <exception cref="ArgumentNullException">457 /// If <paramref name="path"/> is null.458 /// </exception>459 /// <exception cref="NotSupportedException">460 /// If the <paramref name="providerInstance"/> does not support this operation.461 /// </exception>462 /// <exception cref="PipelineStoppedException">463 /// If the pipeline is being stopped while executing the command.464 /// </exception>465 /// <exception cref="ProviderInvocationException">466 /// If the provider threw an exception.467 /// </exception>468 internal string NormalizeRelativePath(string path, string basePath)469 {470 if (path == null)471 {472 throw PSTraceSource.NewArgumentNullException(nameof(path));473 }474 475 CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);476 477 string result = NormalizeRelativePath(path, basePath, context);478 479 context.ThrowFirstErrorOrDoNothing();480 481 return result;482 }483 484 /// <summary>485 /// Normalizes the path that was passed in and returns the normalized path486 /// as a relative path to the basePath that was passed.487 /// </summary>488 /// <param name="path">489 /// An MSH path to an item. The item should exist490 /// or the provider should write out an error.491 /// </param>492 /// <param name="basePath">493 /// The path that the return value should be relative to.494 /// </param>495 /// <param name="context">496 /// The context under which the command is running.497 /// </param>498 /// <returns>499 /// A normalized path that is relative to the basePath that was passed.500 /// </returns>501 /// <exception cref="ArgumentNullException">502 /// If <paramref name="path"/> is null.503 /// </exception>504 /// <exception cref="NotSupportedException">505 /// If the <paramref name="providerInstance"/> does not support this operation.506 /// </exception>507 /// <exception cref="PipelineStoppedException">508 /// If the pipeline is being stopped while executing the command.509 /// </exception>510 /// <exception cref="ProviderInvocationException">511 /// If the provider threw an exception.512 /// </exception>513 internal string NormalizeRelativePath(514 string path,515 string basePath,516 CmdletProviderContext context)517 {518 if (path == null)519 {520 throw PSTraceSource.NewArgumentNullException(nameof(path));521 }522 523 CmdletProviderContext getProviderPathContext =524 new CmdletProviderContext(context);525 526 try527 {528 PSDriveInfo drive = null;529 ProviderInfo provider = null;530 531 string workingPath = Globber.GetProviderPath(532 path,533 getProviderPathContext,534 out provider,535 out drive);536 537 if (getProviderPathContext.HasErrors())538 {539 getProviderPathContext.WriteErrorsToContext(context);540 return null;541 }542 543 if (workingPath == null ||544 provider == null)545 {546 // Since the provider didn't write an error, and we didn't get any547 // results ourselves, we need to write out our own error.548 549 Exception e = PSTraceSource.NewArgumentException(nameof(path));550 context.WriteError(new ErrorRecord(e, "NormalizePathNullResult", ErrorCategory.InvalidArgument, path));551 return null;552 }553 554 if (basePath != null)555 {556 PSDriveInfo baseDrive = null;557 ProviderInfo baseProvider = null;558 559 Globber.GetProviderPath(560 basePath,561 getProviderPathContext,562 out baseProvider,563 out baseDrive);564 565 if (drive != null && baseDrive != null)566 {567 if (!drive.Name.Equals(baseDrive.Name, StringComparison.OrdinalIgnoreCase))568 {569 // Make sure they are from physically different drives570 // Doing StartsWith from both directions covers the following cases571 // C:\ and C:\Temp572 // C:\Temp and C:\573 if (!(drive.Root.StartsWith(baseDrive.Root, StringComparison.OrdinalIgnoreCase) ||574 (baseDrive.Root.StartsWith(drive.Root, StringComparison.OrdinalIgnoreCase))))575 {576 // In this case, no normalization is necessary577 return path;578 }579 }580 }581 }582 583 if (drive != null)584 {585 context.Drive = drive;586 587 // Detect if the original path was already a588 // provider path. This happens when a drive doesn't589 // have a rooted root -- such as HKEY_LOCAL_MACHINE instead of590 // \\HKEY_LOCAL_MACHINE591 if (592 (GetProviderInstance(provider) is NavigationCmdletProvider) &&593 (!string.IsNullOrEmpty(drive.Root)) &&594 (path.StartsWith(drive.Root, StringComparison.OrdinalIgnoreCase)))595 {596 //597 // If the drive root doesn't end with a path separator then there is a chance the598 // path starts with the drive root name but doesn't actually refer to it. For example,599 // (see Win8 bug 922001) consider drive with root HKEY_LOCAL_MACHINE named600 // HKEY_LOCAL_MACHINE_foo. The path would start with the drive root but is not a provider601 // path.602 //603 // We will remediate this by only considering this a provider path if604 // 1. The drive root ends with a path separator.605 // OR606 // 2. The path starts with the drive root followed by a path separator607 // OR608 // 3. The path exactly matches the drive root.609 //610 611 // 1. Test for the drive root ending with a path separator.612 bool driveRootEndsWithPathSeparator = IsPathSeparator(drive.Root[drive.Root.Length - 1]);613 614 // 2. Test for the path starting with the drive root followed by a path separator615 int indexAfterDriveRoot = drive.Root.Length;616 bool pathStartsWithDriveRootAndPathSeparator = indexAfterDriveRoot < path.Length && IsPathSeparator(path[indexAfterDriveRoot]);617 618 // 3. Test for the drive root exactly matching the path.619 // Since we know the path starts with the drive root then they are equal if the lengths are equal.620 bool pathEqualsDriveRoot = drive.Root.Length == path.Length;621 622 if (driveRootEndsWithPathSeparator || pathStartsWithDriveRootAndPathSeparator || pathEqualsDriveRoot)623 {624 workingPath = path;625 }626 }627 }628 629 return NormalizeRelativePath(provider, workingPath, basePath, context);630 }631 finally632 {633 getProviderPathContext.RemoveStopReferral();634 }635 }636 637 /// <summary>638 /// Tests the specified character for equality with one of the powershell path separators and639 /// returns true if it matches.640 /// </summary>641 /// <param name="c">The character to test.</param>642 /// <returns>True if the character is a path separator.</returns>643 private static bool IsPathSeparator(char c)644 {645 return c == StringLiterals.DefaultPathSeparator || c == StringLiterals.AlternatePathSeparator;646 }647 648 /// <summary>649 /// Normalizes the path that was passed in and returns the normalized path650 /// as a relative path to the basePath that was passed.651 /// </summary>652 /// <param name="provider">653 /// The provider to use to normalize the path.654 /// </param>655 /// <param name="path">656 /// An provider internal path to normalize.657 /// </param>658 /// <param name="basePath">659 /// The path that the return value should be relative to.660 /// </param>661 /// <param name="context">662 /// The context under which the command is running.663 /// </param>664 /// <returns>665 /// A normalized path that is relative to the basePath that was passed.666 /// </returns>667 /// <exception cref="NotSupportedException">668 /// If the <paramref name="providerInstance"/> does not support this operation.669 /// </exception>670 /// <exception cref="PipelineStoppedException">671 /// If the pipeline is being stopped while executing the command.672 /// </exception>673 /// <exception cref="ProviderInvocationException">674 /// If the provider threw an exception.675 /// </exception>676 internal string NormalizeRelativePath(677 ProviderInfo provider,678 string path,679 string basePath,680 CmdletProviderContext context)681 {682 // All parameters should have been validated by caller683 Dbg.Diagnostics.Assert(684 provider != null,685 "Caller should validate provider before calling this method");686 687 Dbg.Diagnostics.Assert(688 path != null,689 "Caller should validate path before calling this method");690 691 Dbg.Diagnostics.Assert(692 context != null,693 "Caller should validate context before calling this method");694 695 // Get an instance of the provider696 697 Provider.CmdletProvider providerInstance = GetProviderInstance(provider);698 699 NavigationCmdletProvider navigationCmdletProvider = providerInstance as NavigationCmdletProvider;700 if (navigationCmdletProvider != null)701 {702 try703 {704 path = navigationCmdletProvider.NormalizeRelativePath(path, basePath, context);705 }706 catch (LoopFlowException)707 {708 throw;709 }710 catch (PipelineStoppedException)711 {712 throw;713 }714 catch (ActionPreferenceStopException)715 {716 throw;717 }718 catch (Exception e) // Catch-all OK, 3rd party callout.719 {720 throw NewProviderInvocationException(721 "NormalizeRelativePathProviderException",722 SessionStateStrings.NormalizeRelativePathProviderException,723 navigationCmdletProvider.ProviderInfo,724 path,725 e);726 }727 }728 else if (providerInstance is ContainerCmdletProvider)729 {730 // Do nothing and return the path as-is731 }732 else733 {734 throw PSTraceSource.NewNotSupportedException();735 }736 737 return path;738 }739 740 #endregion NormalizeRelativePath741 742 #region MakePath743 744 /// <summary>745 /// Generates a path from the given parts.746 /// </summary>747 /// <param name="parent">748 /// The parent segment of the path to be joined with the child.749 /// </param>750 /// <param name="child">751 /// The child segment of the ath to be joined with the parent.752 /// </param>753 /// <returns>754 /// The generated path.755 /// </returns>756 /// <exception cref="ArgumentNullException">757 /// If <paramref name="context"/> is null.758 /// </exception>759 /// <exception cref="ArgumentException">760 /// If both <paramref name="parent"/> and <paramref name="child"/> is null.761 /// </exception>762 /// <exception cref="NotSupportedException">763 /// If the <paramref name="providerId"/> does not support this operation.764 /// </exception>765 /// <exception cref="PipelineStoppedException">766 /// If the pipeline is being stopped while executing the command.767 /// </exception>768 /// <exception cref="ProviderInvocationException">769 /// If the provider threw an exception.770 /// </exception>771 internal string MakePath(772 string parent,773 string child)774 {775 CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);776 777 return MakePath(parent, child, context);778 }779 780 /// <summary>781 /// Generates a path from the given parts.782 /// </summary>783 /// <param name="parent">784 /// The parent segment of the path to be joined with the child.785 /// </param>786 /// <param name="child">787 /// The child segment of the ath to be joined with the parent.788 /// </param>789 /// <param name="context">790 /// The context which the core command is running.791 /// </param>792 /// <returns>793 /// The generated path.794 /// </returns>795 /// <exception cref="ArgumentNullException">796 /// If <paramref name="context"/> is null.797 /// </exception>798 /// <exception cref="ArgumentException">799 /// If both <paramref name="parent"/> and <paramref name="child"/> is null.800 /// </exception>801 /// <exception cref="NotSupportedException">802 /// If the <paramref name="providerId"/> does not support this operation.803 /// </exception>804 /// <exception cref="PipelineStoppedException">805 /// If the pipeline is being stopped while executing the command.806 /// </exception>807 /// <exception cref="ProviderInvocationException">808 /// If the provider threw an exception.809 /// </exception>810 internal string MakePath(811 string parent,812 string child,813 CmdletProviderContext context)814 {815 string result = null;816 817 if (context == null)818 {819 throw PSTraceSource.NewArgumentNullException(nameof(context));820 }821 822 if (parent == null &&823 child == null)824 {825 throw PSTraceSource.NewArgumentException(nameof(parent));826 }827 828 // Set the drive data for the context829 830 ProviderInfo provider = null;831 if (CurrentDrive != null)832 {833 provider = CurrentDrive.Provider;834 }835 836 if (context.Drive == null)837 {838 bool isProviderQualified = LocationGlobber.IsProviderQualifiedPath(parent);839 bool isAbsolute = LocationGlobber.IsAbsolutePath(parent);840 if (isProviderQualified || isAbsolute)841 {842 PSDriveInfo drive = null;843 844 // Ignore the result. Just using this to get the providerId and drive845 Globber.GetProviderPath(parent, context, out provider, out drive);846 847 if (drive == null && isProviderQualified)848 {849 drive = provider.HiddenDrive;850 }851 852 context.Drive = drive;853 }854 else855 {856 context.Drive = CurrentDrive;857 }858 859 result = MakePath(provider, parent, child, context);860 861 if (isAbsolute)862 {863 result = LocationGlobber.GetDriveQualifiedPath(result, context.Drive);864 }865 else if (isProviderQualified)866 {867 result = LocationGlobber.GetProviderQualifiedPath(result, provider);868 }869 }870 else871 {872 provider = context.Drive.Provider;873 result = MakePath(provider, parent, child, context);874 }875 876 return result;877 }878 879 /// <summary>880 /// Uses the specified provider to put the two parts of a path together.881 /// </summary>882 /// <param name="provider">883 /// The provider to use.884 /// </param>885 /// <param name="parent">886 /// The parent part of the path to join with the child.887 /// </param>888 /// <param name="child">889 /// The child part of the path to join with the parent.890 /// </param>891 /// <param name="context">892 /// The context under which the command is running.893 /// </param>894 /// <returns>895 /// The combined path.896 /// </returns>897 /// <exception cref="NotSupportedException">898 /// If the <paramref name="providerId"/> does not support this operation.899 /// </exception>900 /// <exception cref="PipelineStoppedException">901 /// If the pipeline is being stopped while executing the command.902 /// </exception>903 /// <exception cref="ProviderInvocationException">904 /// If the provider threw an exception.905 /// </exception>906 internal string MakePath(907 ProviderInfo provider,908 string parent,909 string child,910 CmdletProviderContext context)911 {912 // All parameters should have been validated by caller913 Dbg.Diagnostics.Assert(914 provider != null,915 "Caller should validate provider before calling this method");916 917 Dbg.Diagnostics.Assert(918 context != null,919 "Caller should validate context before calling this method");920 921 // Get an instance of the provider922 923 Provider.CmdletProvider providerInstance = provider.CreateInstance();924 925 return MakePath(providerInstance, parent, child, context);926 }927 928 /// <summary>929 /// Uses the specified provider to put the two parts of a path together.930 /// </summary>931 /// <param name="providerInstance">932 /// The provider instance to use.933 /// </param>934 /// <param name="parent">935 /// The parent part of the path to join with the child.936 /// </param>937 /// <param name="child">938 /// The child part of the path to join with the parent.939 /// </param>940 /// <param name="context">941 /// The context under which the command is running.942 /// </param>943 /// <returns>944 /// The combined path.945 /// </returns>946 /// <exception cref="NotSupportedException">947 /// If the <paramref name="providerId"/> does not support this operation.948 /// </exception>949 /// <exception cref="PipelineStoppedException">950 /// If the pipeline is being stopped while executing the command.951 /// </exception>952 /// <exception cref="ProviderInvocationException">953 /// If the provider threw an exception.954 /// </exception>955 internal string MakePath(956 CmdletProvider providerInstance,957 string parent,958 string child,959 CmdletProviderContext context)960 {961 // All parameters should have been validated by caller962 Dbg.Diagnostics.Assert(963 providerInstance != null,964 "Caller should validate providerInstance before calling this method");965 966 Dbg.Diagnostics.Assert(967 context != null,968 "Caller should validate context before calling this method");969 970 // Get an instance of the provider971 972 string result = null;973 974 NavigationCmdletProvider navigationCmdletProvider = providerInstance as NavigationCmdletProvider;975 976 if (navigationCmdletProvider != null)977 {978 try979 {980 result = navigationCmdletProvider.MakePath(parent, child, context);981 }982 catch (LoopFlowException)983 {984 throw;985 }986 catch (PipelineStoppedException)987 {988 throw;989 }990 catch (ActionPreferenceStopException)991 {992 throw;993 }994 catch (Exception e) // Catch-all OK, 3rd party callout.995 {996 throw NewProviderInvocationException(997 "MakePathProviderException",998 SessionStateStrings.MakePathProviderException,999 navigationCmdletProvider.ProviderInfo,1000 parent,1001 e);1002 }1003 }1004 else if (providerInstance is ContainerCmdletProvider)1005 {1006 result = child;1007 }1008 else1009 {1010 throw PSTraceSource.NewNotSupportedException();1011 }1012 1013 return result;1014 }1015 1016 #endregion MakePath1017 1018 #region GetChildName1019 1020 /// <summary>1021 /// Gets the name of the leaf element in the specified path.1022 /// </summary>1023 /// <param name="path">1024 /// The fully qualified path to the item1025 /// </param>1026 /// <returns>1027 /// The leaf element in the path.1028 /// </returns>1029 /// <exception cref="ArgumentNullException">1030 /// If <paramref name="path"/> is null.1031 /// </exception>1032 /// <exception cref="ProviderNotFoundException">1033 /// If the <paramref name="path"/> refers to a provider that could not be found.1034 /// </exception>1035 /// <exception cref="DriveNotFoundException">1036 /// If the <paramref name="path"/> refers to a drive that could not be found.1037 /// </exception>1038 /// <exception cref="NotSupportedException">1039 /// If the provider that the <paramref name="path"/> refers to does1040 /// not support this operation.1041 /// </exception>1042 /// <exception cref="ProviderInvocationException">1043 /// If the provider threw an exception.1044 /// </exception>1045 internal string GetChildName(string path)1046 {1047 if (path == null)1048 {1049 throw PSTraceSource.NewArgumentNullException(nameof(path));1050 }1051 1052 CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);1053 1054 string result = GetChildName(path, context);1055 1056 context.ThrowFirstErrorOrDoNothing();1057 1058 return result;1059 }1060 1061 /// <summary>1062 /// Gets the name of the leaf element in the specified path.1063 /// </summary>1064 /// <param name="path">1065 /// The fully qualified path to the item1066 /// </param>1067 /// <param name="context">1068 /// The context which the core command is running.1069 /// </param>1070 /// <returns>1071 /// The leaf element in the path.1072 /// </returns>1073 /// <exception cref="ArgumentNullException">1074 /// If <paramref name="path"/> is null.1075 /// </exception>1076 /// <exception cref="ProviderNotFoundException">1077 /// If the <paramref name="path"/> refers to a provider that could not be found.1078 /// </exception>1079 /// <exception cref="DriveNotFoundException">1080 /// If the <paramref name="path"/> refers to a drive that could not be found.1081 /// </exception>1082 /// <exception cref="NotSupportedException">1083 /// If the provider that the <paramref name="path"/> refers to does1084 /// not support this operation.1085 /// </exception>1086 /// <exception cref="ProviderInvocationException">1087 /// If the provider threw an exception.1088 /// </exception>1089 internal string GetChildName(1090 string path,1091 CmdletProviderContext context)1092 {1093 return GetChildName(path, context, false);1094 }1095 1096 /// <summary>1097 /// Gets the name of the leaf element in the specified path.1098 /// Allow to use FileSystem as the default provider when the1099 /// given path is drive-qualified and the drive cannot be found.1100 /// </summary>1101 /// <param name="path">1102 /// The fully qualified path to the item1103 /// </param>1104 /// <param name="context">1105 /// The context which the core command is running.1106 /// </param>1107 /// <param name="useDefaultProvider">1108 /// to use default provider when needed.1109 /// </param>1110 /// <returns>1111 /// The leaf element in the path.1112 /// </returns>1113 internal string GetChildName(1114 string path,1115 CmdletProviderContext context,1116 bool useDefaultProvider)1117 {1118 if (path == null)1119 {1120 throw PSTraceSource.NewArgumentNullException(nameof(path));1121 }1122 1123 PSDriveInfo drive = null;1124 ProviderInfo provider = null;1125 string workingPath = null;1126 1127 try1128 {1129 workingPath = Globber.GetProviderPath(path, context, out provider, out drive);1130 }1131 catch (DriveNotFoundException)1132 {1133 // the path is sure to be drive_qualified and it is an absolute path, otherwise the1134 // drive would be set to the current drive and the DriveNotFoundException will not happen.1135 if (useDefaultProvider)1136 {1137 // the default provider is FileSystem1138 provider = PublicSessionState.Internal.GetSingleProvider(Microsoft.PowerShell.Commands.FileSystemProvider.ProviderName);1139 1140 workingPath = path.Replace(StringLiterals.AlternatePathSeparator, StringLiterals.DefaultPathSeparator);1141 workingPath = workingPath.TrimEnd(StringLiterals.DefaultPathSeparator);1142 }1143 else1144 {1145 throw;1146 }1147 }1148 1149 Dbg.Diagnostics.Assert(1150 workingPath != null,1151 "There should always be a way to generate a UniversalResourceName for a " +1152 "given path");1153 1154 Dbg.Diagnostics.Assert(1155 provider != null,1156 "There should always be a way to get the provider ID for a given path or else GetProviderPath should have thrown an exception");1157 1158 if (drive != null)1159 {1160 context.Drive = drive;1161 }1162 1163 return GetChildName(provider, workingPath, context);1164 }1165 1166 /// <summary>1167 /// Gets the leaf element of the specified path.1168 /// </summary>1169 /// <param name="provider">1170 /// The provider to use.1171 /// </param>1172 /// <param name="path">1173 /// The path to the item if it was specified on the command line.1174 /// </param>1175 /// <param name="context">1176 /// The context which the core command is running.1177 /// </param>1178 /// <exception cref="NotSupportedException">1179 /// If the <paramref name="providerInstance"/> does not support this operation.1180 /// </exception>1181 /// <exception cref="PipelineStoppedException">1182 /// If the pipeline is being stopped while executing the command.1183 /// </exception>1184 /// <exception cref="ProviderInvocationException">1185 /// If the provider threw an exception.1186 /// </exception>1187 private string GetChildName(1188 ProviderInfo provider,1189 string path,1190 CmdletProviderContext context)1191 {1192 // All parameters should have been validated by caller1193 Dbg.Diagnostics.Assert(1194 provider != null,1195 "Caller should validate provider before calling this method");1196 1197 Dbg.Diagnostics.Assert(1198 path != null,1199 "Caller should validate path before callin g this method");1200 