MegaBites-AI/Windows-powershell
0308
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Collections.ObjectModel;6using System.Linq;7using System.Management.Automation.Provider;8using System.Reflection;9using System.Threading;10 11using Dbg = System.Management.Automation;12 13namespace System.Management.Automation14{15 /// <summary>16 /// Information about a loaded Cmdlet Provider.17 /// </summary>18 /// <remarks>19 /// A cmdlet provider may want to derive from this class to provide their20 /// own public members to expose to the user or to cache information related to the provider.21 /// </remarks>22 public class ProviderInfo23 {24 /// <summary>25 /// Gets the System.Type of the class that implements the provider.26 /// </summary>27 public Type ImplementingType { get; }28 29 /// <summary>30 /// Gets the help file path for the provider.31 /// </summary>32 public string HelpFile { get; } = string.Empty;33 34 /// <summary>35 /// The instance of session state the provider belongs to.36 /// </summary>37 private readonly SessionState _sessionState;38 39 private string _fullName;40 private string _cachedModuleName;41 42 /// <summary>43 /// Gets the name of the provider.44 /// </summary>45 public string Name { get; }46 47 /// <summary>48 /// Gets the full name of the provider including the module name if available.49 /// </summary>50 internal string FullName51 {52 get53 {54 static string GetFullName(string name, string psSnapInName, string moduleName)55 {56 string result = name;57 if (!string.IsNullOrEmpty(psSnapInName))58 {59 result =60 string.Format(61 System.Globalization.CultureInfo.InvariantCulture,62 "{0}\\{1}",63 psSnapInName,64 name);65 }66 67 // After converting core snapins to load as modules, the providers will have Module property populated68 else if (!string.IsNullOrEmpty(moduleName))69 {70 result =71 string.Format(72 System.Globalization.CultureInfo.InvariantCulture,73 "{0}\\{1}",74 moduleName,75 name);76 }77 78 return result;79 }80 81 if (_fullName != null && ModuleName.Equals(_cachedModuleName, StringComparison.Ordinal))82 {83 return _fullName;84 }85 86 _cachedModuleName = ModuleName;87 return _fullName = GetFullName(Name, PSSnapInName, ModuleName);88 }89 }90 91 /// <summary>92 /// Gets the Snap-in in which the provider is implemented.93 /// </summary>94 public PSSnapInInfo PSSnapIn { get; }95 96 /// <summary>97 /// Gets the pssnapin name that the provider is implemented in.98 /// </summary>99 internal string PSSnapInName100 {101 get102 {103 string result = null;104 if (PSSnapIn != null)105 {106 result = PSSnapIn.Name;107 }108 109 return result;110 }111 }112 113 internal string ApplicationBase114 {115 get116 {117 string psHome = null;118 try119 {120 psHome = Utils.DefaultPowerShellAppBase;121 }122 catch (System.Security.SecurityException)123 {124 psHome = null;125 }126 127 return psHome;128 }129 }130 131 /// <summary>132 /// Get the name of the module exporting this provider.133 /// </summary>134 public string ModuleName135 {136 get137 {138 if (PSSnapIn != null)139 return PSSnapIn.Name;140 if (Module != null)141 return Module.Name;142 return string.Empty;143 }144 }145 146 /// <summary>147 /// Gets the module the defined this provider.148 /// </summary>149 public PSModuleInfo Module { get; private set; }150 151 internal void SetModule(PSModuleInfo module)152 {153 Module = module;154 _fullName = null;155 }156 157 /// <summary>158 /// Gets or sets the description for the provider.159 /// </summary>160 public string Description { get; set; }161 162 /// <summary>163 /// Gets the capabilities that are implemented by the provider.164 /// </summary>165 public Provider.ProviderCapabilities Capabilities166 {167 get168 {169 if (!_capabilitiesRead)170 {171 try172 {173 // Get the CmdletProvider declaration attribute174 175 Type providerType = this.ImplementingType;176 177 var attrs = providerType.GetCustomAttributes<CmdletProviderAttribute>(false);178 var cmdletProviderAttributes = attrs as CmdletProviderAttribute[] ?? attrs.ToArray();179 180 if (cmdletProviderAttributes.Length == 1)181 {182 _capabilities = cmdletProviderAttributes[0].ProviderCapabilities;183 _capabilitiesRead = true;184 }185 }186 catch (Exception) // Catch-all OK, 3rd party callout187 {188 // Assume no capabilities for now189 }190 }191 192 return _capabilities;193 }194 }195 196 private ProviderCapabilities _capabilities = ProviderCapabilities.None;197 private bool _capabilitiesRead;198 199 /// <summary>200 /// Gets or sets the home for the provider.201 /// </summary>202 /// <remarks>203 /// The location can be either a fully qualified provider path204 /// or a PowerShell path. This is the location that is substituted for the ~.205 /// </remarks>206 public string Home { get; set; }207 208 /// <summary>209 /// Gets an enumeration of drives that are available for210 /// this provider.211 /// </summary>212 public Collection<PSDriveInfo> Drives213 {214 get215 {216 return _sessionState.Drive.GetAllForProvider(FullName);217 }218 }219 220 /// <summary>221 /// A hidden drive for the provider that is used for setting222 /// the location to a provider-qualified path.223 /// </summary>224 private readonly PSDriveInfo _hiddenDrive;225 226 /// <summary>227 /// Gets the hidden drive for the provider that is used228 /// for setting a location to a provider-qualified path.229 /// </summary>230 internal PSDriveInfo HiddenDrive231 {232 get233 {234 return _hiddenDrive;235 }236 }237 238 /// <summary>239 /// Gets the string representation of the instance which is the name of the provider.240 /// </summary>241 /// <returns>242 /// The name of the provider. If single-shell, the name is pssnapin-qualified. If custom-shell,243 /// the name is just the provider name.244 /// </returns>245 public override string ToString()246 {247 return FullName;248 }249 250#if USE_TLS251 /// <summary>252 /// Allocates some thread local storage to an instance of the253 /// provider. We don't want to cache a single instance of the254 /// provider because that could lead to problems in a multi-threaded255 /// environment.256 /// </summary>257 private LocalDataStoreSlot instance =258 Thread.AllocateDataSlot();259#endif260 261 /// <summary>262 /// Gets or sets if the drive-root relative paths on drives of this provider263 /// are separated by a colon or not.264 ///265 /// This is true for all PSDrives on all platforms, except for filesystems on266 /// non-windows platforms.267 /// </summary>268 public bool VolumeSeparatedByColon { get; internal set; } = true;269 270 /// <summary>271 /// Gets the default item separator character for this provider.272 /// </summary>273 public char ItemSeparator { get; private set; }274 275 /// <summary>276 /// Gets the alternate item separator character for this provider.277 /// </summary>278 public char AltItemSeparator { get; private set; }279 280 /// <summary>281 /// Constructs an instance of the class using an existing reference282 /// as a template.283 /// </summary>284 /// <param name="providerInfo">285 /// The provider information to copy to this instance.286 /// </param>287 /// <remarks>288 /// This constructor should be used by derived types to easily copying289 /// the base class members from an existing ProviderInfo.290 /// This is designed for use by a <see cref="System.Management.Automation.Provider.CmdletProvider"/>291 /// during calls to their <see cref="System.Management.Automation.Provider.CmdletProvider.Start(ProviderInfo)"/> method.292 /// </remarks>293 /// <exception cref="ArgumentNullException">294 /// If <paramref name="providerInfo"/> is null.295 /// </exception>296 protected ProviderInfo(ProviderInfo providerInfo)297 {298 if (providerInfo == null)299 {300 throw PSTraceSource.NewArgumentNullException(nameof(providerInfo));301 }302 303 Name = providerInfo.Name;304 ImplementingType = providerInfo.ImplementingType;305 _capabilities = providerInfo._capabilities;306 Description = providerInfo.Description;307 _hiddenDrive = providerInfo._hiddenDrive;308 Home = providerInfo.Home;309 HelpFile = providerInfo.HelpFile;310 PSSnapIn = providerInfo.PSSnapIn;311 _sessionState = providerInfo._sessionState;312 VolumeSeparatedByColon = providerInfo.VolumeSeparatedByColon;313 ItemSeparator = providerInfo.ItemSeparator;314 AltItemSeparator = providerInfo.AltItemSeparator;315 }316 317 /// <summary>318 /// Constructor for the ProviderInfo class.319 /// </summary>320 /// <param name="sessionState">321 /// The instance of session state that the provider is being added to.322 /// </param>323 /// <param name="implementingType">324 /// The type that implements the provider325 /// </param>326 /// <param name="name">327 /// The name of the provider.328 /// </param>329 /// <param name="helpFile">330 /// The help file for the provider.331 /// </param>332 /// <param name="psSnapIn">333 /// The Snap-In name for the provider.334 /// </param>335 /// <exception cref="ArgumentException">336 /// If <paramref name="name"/> is null or empty.337 /// </exception>338 /// <exception cref="ArgumentNullException">339 /// If <paramref name="sessionState"/> is null.340 /// </exception>341 /// <exception cref="ArgumentNullException">342 /// If <paramref name="implementingType"/> is null.343 /// </exception>344 internal ProviderInfo(345 SessionState sessionState,346 Type implementingType,347 string name,348 string helpFile,349 PSSnapInInfo psSnapIn)350 : this(sessionState, implementingType, name, string.Empty, string.Empty, helpFile, psSnapIn)351 {352 }353 354 /// <summary>355 /// Constructor for the ProviderInfo class.356 /// </summary>357 /// <param name="sessionState">358 /// The instance of session state that the provider is being added to.359 /// </param>360 /// <param name="implementingType">361 /// The type that implements the provider362 /// </param>363 /// <param name="name">364 /// The alternate name to use for the provider instead of the one specified365 /// in the .cmdletprovider file.366 /// </param>367 /// <param name="description">368 /// The description of the provider.369 /// </param>370 /// <param name="home">371 /// The home path for the provider. This must be a PowerShell path.372 /// </param>373 /// <param name="helpFile">374 /// The help file for the provider.375 /// </param>376 /// <param name="psSnapIn">377 /// The Snap-In for the provider.378 /// </param>379 /// <exception cref="ArgumentNullException">380 /// If <paramref name="implementingType"/> or <paramref name="sessionState"/> is null.381 /// </exception>382 /// <exception cref="ArgumentException">383 /// If <paramref name="name"/> is null or empty.384 /// </exception>385 internal ProviderInfo(386 SessionState sessionState,387 Type implementingType,388 string name,389 string description,390 string home,391 string helpFile,392 PSSnapInInfo psSnapIn)393 {394 // Verify parameters395 if (sessionState == null)396 {397 throw PSTraceSource.NewArgumentNullException(nameof(sessionState));398 }399 400 if (implementingType == null)401 {402 throw PSTraceSource.NewArgumentNullException(nameof(implementingType));403 }404 405 if (string.IsNullOrEmpty(name))406 {407 throw PSTraceSource.NewArgumentException(nameof(name));408 }409 410 _sessionState = sessionState;411 412 Name = name;413 Description = description;414 Home = home;415 ImplementingType = implementingType;416 HelpFile = helpFile;417 PSSnapIn = psSnapIn;418 419 // Create the hidden drive. The name doesn't really420 // matter since we are not adding this drive to a scope.421 422 _hiddenDrive =423 new PSDriveInfo(424 this.FullName,425 this,426 string.Empty,427 string.Empty,428 null);429 430 _hiddenDrive.Hidden = true;431 432 // TODO:PSL433 // this is probably not right here434 if (implementingType == typeof(Microsoft.PowerShell.Commands.FileSystemProvider) && !Platform.IsWindows)435 {436 VolumeSeparatedByColon = false;437 }438 }439 440 /// <summary>441 /// Determines if the passed in name is either the fully-qualified pssnapin name or442 /// short name of the provider.443 /// </summary>444 /// <param name="providerName">445 /// The name to compare with the provider name.446 /// </param>447 /// <returns>448 /// True if the name is the fully-qualified pssnapin name or the short name of the provider.449 /// </returns>450 internal bool NameEquals(string providerName)451 {452 PSSnapinQualifiedName qualifiedProviderName = PSSnapinQualifiedName.GetInstance(providerName);453 454 bool result = false;455 if (qualifiedProviderName != null)456 {457 // If the pssnapin name and provider name are specified, then both must match458 do // false loop459 {460 if (!string.IsNullOrEmpty(qualifiedProviderName.PSSnapInName))461 {462 // After converting core snapins to load as modules, the providers will have Module property populated463 if (!string.Equals(qualifiedProviderName.PSSnapInName, this.PSSnapInName, StringComparison.OrdinalIgnoreCase) &&464 !string.Equals(qualifiedProviderName.PSSnapInName, this.ModuleName, StringComparison.OrdinalIgnoreCase))465 {466 break;467 }468 }469 470 result = string.Equals(qualifiedProviderName.ShortName, this.Name, StringComparison.OrdinalIgnoreCase);471 } while (false);472 }473 else474 {475 // If only the provider name is specified, then only the name must match476 result = string.Equals(providerName, Name, StringComparison.OrdinalIgnoreCase);477 }478 479 return result;480 }481 482 internal bool IsMatch(string providerName)483 {484 PSSnapinQualifiedName psSnapinQualifiedName = PSSnapinQualifiedName.GetInstance(providerName);485 486 WildcardPattern namePattern = null;487 488 if (psSnapinQualifiedName != null && WildcardPattern.ContainsWildcardCharacters(psSnapinQualifiedName.ShortName))489 {490 namePattern = WildcardPattern.Get(psSnapinQualifiedName.ShortName, WildcardOptions.IgnoreCase);491 }492 493 return IsMatch(namePattern, psSnapinQualifiedName);494 }495 496 internal bool IsMatch(WildcardPattern namePattern, PSSnapinQualifiedName psSnapinQualifiedName)497 {498 bool result = false;499 500 if (psSnapinQualifiedName == null)501 {502 result = true;503 }504 else505 {506 if (namePattern == null)507 {508 if (string.Equals(Name, psSnapinQualifiedName.ShortName, StringComparison.OrdinalIgnoreCase) &&509 IsPSSnapinNameMatch(psSnapinQualifiedName))510 {511 result = true;512 }513 }514 else if (namePattern.IsMatch(Name) && IsPSSnapinNameMatch(psSnapinQualifiedName))515 {516 result = true;517 }518 }519 520 return result;521 }522 523 private bool IsPSSnapinNameMatch(PSSnapinQualifiedName psSnapinQualifiedName)524 {525 bool result = false;526 527 if (string.IsNullOrEmpty(psSnapinQualifiedName.PSSnapInName) ||528 string.Equals(psSnapinQualifiedName.PSSnapInName, PSSnapInName, StringComparison.OrdinalIgnoreCase))529 {530 result = true;531 }532 533 return result;534 }535 536 /// <summary>537 /// Creates an instance of the provider.538 /// </summary>539 /// <returns>540 /// An instance of the provider or null if one could not be created.541 /// </returns>542 /// <exception cref="ProviderNotFoundException">543 /// If an instance of the provider could not be created because the544 /// type could not be found in the assembly.545 /// </exception>546 internal Provider.CmdletProvider CreateInstance()547 {548 // It doesn't really seem that using thread local storage to store an549 // instance of the provider is really much of a performance gain and it550 // still causes problems with the CmdletProviderContext when piping two551 // commands together that use the same provider.552 // get-child -filter a*.txt | get-content553 // This pipeline causes problems when using a cached provider instance because554 // the CmdletProviderContext gets changed when get-content gets called.555 // When get-content finishes writing content from the first output of get-child556 // get-child gets control back and writes out a FileInfo but the WriteObject557 // from get-content gets used because the CmdletProviderContext is still from558 // that cmdlet.559 // Possible solutions are to not cache the provider instance, or to maintain560 // a CmdletProviderContext stack in ProviderBase. Each method invocation pushes561 // the current context and the last action of the method pops back to the562 // previous context.563#if USE_TLS564 // Next see if we already have an instance in thread local storage565 566 object providerInstance = Thread.GetData(instance);567 568 if (providerInstance == null)569 {570#else571 object providerInstance = null;572#endif573 // Finally create an instance of the class574 Exception invocationException = null;575 576 try577 {578 providerInstance =579 Activator.CreateInstance(this.ImplementingType);580 }581 catch (TargetInvocationException targetException)582 {583 invocationException = targetException.InnerException;584 }585 catch (MissingMethodException)586 {587 }588 catch (MemberAccessException)589 {590 }591 catch (ArgumentException)592 {593 }594#if USE_TLS595 // cache the instance in thread local storage596 597 Thread.SetData(instance, providerInstance);598 }599#endif600 601 if (providerInstance == null)602 {603 ProviderNotFoundException e = null;604 605 if (invocationException != null)606 {607 e =608 new ProviderNotFoundException(609 this.Name,610 SessionStateCategory.CmdletProvider,611 "ProviderCtorException",612 SessionStateStrings.ProviderCtorException,613 invocationException.Message);614 }615 else616 {617 e =618 new ProviderNotFoundException(619 this.Name,620 SessionStateCategory.CmdletProvider,621 "ProviderNotFoundInAssembly",622 SessionStateStrings.ProviderNotFoundInAssembly);623 }624 625 throw e;626 }627 628 Provider.CmdletProvider result = providerInstance as Provider.CmdletProvider;629 ItemSeparator = result.ItemSeparator;630 AltItemSeparator = result.AltItemSeparator;631 632 Dbg.Diagnostics.Assert(633 result != null,634 "DiscoverProvider should verify that the class is derived from CmdletProvider so this is just validation of that");635 636 result.SetProviderInformation(this);637 return result;638 }639 640 /// <summary>641 /// Get the output types specified on this provider for the cmdlet requested.642 /// </summary>643 internal void GetOutputTypes(string cmdletname, List<PSTypeName> listToAppend)644 {645 if (_providerOutputType == null)646 {647 _providerOutputType = new Dictionary<string, List<PSTypeName>>();648 foreach (OutputTypeAttribute outputType in ImplementingType.GetCustomAttributes<OutputTypeAttribute>(false))649 {650 if (string.IsNullOrEmpty(outputType.ProviderCmdlet))651 {652 continue;653 }654 655 List<PSTypeName> l;656 if (!_providerOutputType.TryGetValue(outputType.ProviderCmdlet, out l))657 {658 l = new List<PSTypeName>();659 _providerOutputType[outputType.ProviderCmdlet] = l;660 }661 662 l.AddRange(outputType.Type);663 }664 }665 666 List<PSTypeName> cmdletOutputType = null;667 if (_providerOutputType.TryGetValue(cmdletname, out cmdletOutputType))668 {669 listToAppend.AddRange(cmdletOutputType);670 }671 }672 673 private Dictionary<string, List<PSTypeName>> _providerOutputType;674 675 private PSNoteProperty _noteProperty;676 677 internal PSNoteProperty GetNotePropertyForProviderCmdlets(string name)678 {679 if (_noteProperty == null)680 {681 Interlocked.CompareExchange(ref _noteProperty,682 new PSNoteProperty(name, this), null);683 }684 685 return _noteProperty;686 }687 }688}689 