// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System.Collections; using System.Collections.ObjectModel; using System.Management.Automation.Provider; using System.Management.Automation.Runspaces; using System.Management.Automation.Internal; using System.Reflection; using Dbg = System.Management.Automation; using System.IO; #pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings #pragma warning disable 56500 namespace System.Management.Automation { /// /// Holds the state of a Monad Shell session /// internal sealed partial class SessionStateInternal { #region ContainerCmdletProvider accessors #region ItemExists /// /// Determines if the monad virtual namespace path exists. /// /// /// /// The path to the object to determine if it exists. /// /// /// /// Passed on to providers to force operations. /// /// /// /// If true, globbing is not done on paths. /// /// /// /// true if the object specified by path exists, false otherwise. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal bool ItemExists(string path, bool force, bool literalPath) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); context.Force = force; context.SuppressWildcardExpansion = literalPath; bool result = ItemExists(path, context); context.ThrowFirstErrorOrDoNothing(); return result; } // ItemExists /// /// Determines if the monad virtual namespace path exists. /// /// /// /// The path to the object to determine if it exists. /// /// /// /// The context which the core command is running. /// /// /// /// true if the object specified by path exists, false otherwise. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal bool ItemExists( string path, CmdletProviderContext context) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } ProviderInfo provider = null; CmdletProvider providerInstance = null; bool result = false; try { Collection providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, true, context, out provider, out providerInstance); foreach (string providerPath in providerPaths) { result = ItemExists(providerInstance, providerPath, context); if (result == true) { break; } } } catch (ItemNotFoundException) { result = false; } return result; } // Exists /// /// Determines if the item at the specified path exists /// /// /// /// The provider instance to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The context which the core command is running. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// internal bool ItemExists( CmdletProvider providerInstance, string path, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerId before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ItemCmdletProvider itemCmdletProvider = GetItemProviderInstance(providerInstance); bool result = false; try { result = itemCmdletProvider.ItemExists(path, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "ItemExistsProviderException", SessionStateStrings.ItemExistsProviderException, itemCmdletProvider.ProviderInfo, path, e); } return result; } // Exists /// /// Gets the dynamic parameters for the test-path cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal object ItemExistsDynamicParameters(string path, CmdletProviderContext context) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } ProviderInfo provider = null; CmdletProvider providerInstance = null; CmdletProviderContext newContext = new CmdletProviderContext(context); newContext.SetFilters( new Collection(), new Collection(), null); Collection providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, true, newContext, out provider, out providerInstance); if (providerPaths.Count > 0) { // Get the dynamic parameters for the first resolved path return ItemExistsDynamicParameters(providerInstance, providerPaths[0], newContext); } return null; } // ItemExistsDynamicParameters /// /// Gets the dynamic parameters for the test-path cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The instance of the provider to use. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private object ItemExistsDynamicParameters( CmdletProvider providerInstance, string path, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); object result = null; try { result = containerCmdletProvider.ItemExistsDynamicParameters(path, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "ItemExistsDynamicParametersProviderException", SessionStateStrings.ItemExistsDynamicParametersProviderException, containerCmdletProvider.ProviderInfo, path, e); } return result; } // ITemExistsDynamicParameters #endregion Exists #region IsValidPath /// /// Determines if the MSH path is a syntactically and semantically valid path for the provider. /// /// /// /// The path to validate. /// /// /// /// true if the object specified by path is syntactically and semantically valid, false otherwise. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal bool IsValidPath(string path) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); bool result = IsValidPath(path, context); context.ThrowFirstErrorOrDoNothing(); return result; } // IsValidPath /// /// Determines if the MSH path is a syntactically and semantically valid path for the provider. /// /// /// /// The path to validate. /// /// /// /// The context which the core command is running. /// /// /// /// true if the object specified by path is syntactically and semantically valid, false otherwise. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal bool IsValidPath( string path, CmdletProviderContext context) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } ProviderInfo provider = null; PSDriveInfo driveInfo = null; string providerPath = Globber.GetProviderPath( path, context, out provider, out driveInfo); ItemCmdletProvider providerInstance = GetItemProviderInstance(provider); return IsValidPath(providerInstance, providerPath, context); } // IsValidPath /// /// Determines if the specified path is valid. /// /// /// /// The provider instance to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The context which the core command is running. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private bool IsValidPath( CmdletProvider providerInstance, string path, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ItemCmdletProvider itemCmdletProvider = GetItemProviderInstance(providerInstance); bool result = false; try { result = itemCmdletProvider.IsValidPath(path, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "IsValidPathProviderException", SessionStateStrings.IsValidPathProviderException, itemCmdletProvider.ProviderInfo, path, e); } return result; } // IsValidPath #endregion IsValidPath #region IsItemContainer /// /// Determines if the monad virtual namespace path is a container. /// /// /// /// The path to the object to determine if it is a container. /// /// /// /// true if the object specified by path is a container, false otherwise. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal bool IsItemContainer(string path) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); bool result = IsItemContainer(path, context); context.ThrowFirstErrorOrDoNothing(); return result; } // IsItemContainer /// /// Determines if the monad virtual namespace path is a container. /// /// /// /// The path to the object to determine if it is a container. /// /// /// /// The context which the core command is running. /// /// /// /// true if the object specified by path is a container, false otherwise. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal bool IsItemContainer( string path, CmdletProviderContext context) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } ProviderInfo provider = null; CmdletProvider providerInstance = null; bool result = false; try { Collection providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, true, context, out provider, out providerInstance); foreach (string providerPath in providerPaths) { result = IsItemContainer(providerInstance, providerPath, context); if (result == false) { break; } } } catch (ItemNotFoundException) { result = false; } return result; } // IsItemContainer /// /// Determines if the item at the specified path is a container. /// /// /// /// The provider instance to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The context which the core command is running. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private bool IsItemContainer( CmdletProvider providerInstance, string path, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); bool result = false; NavigationCmdletProvider navigationCmdletProvider = null; try { // If it is a NavigationProvider instance then we // can ask the provider if the path is to a container. navigationCmdletProvider = GetNavigationProviderInstance(providerInstance, false); try { result = navigationCmdletProvider.IsItemContainer(path, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "IsItemContainerProviderException", SessionStateStrings.IsItemContainerProviderException, navigationCmdletProvider.ProviderInfo, path, e); } } catch (NotSupportedException) { try { // If it is a ContainerProvider instance then the // root (empty path) is considered a container // but everything else is not. GetContainerProviderInstance(providerInstance); if (path.Length == 0) { result = true; } else { result = false; } } catch (NotSupportedException) { // If the provider is neither a NavigationProvider nor a // ContainerProvider then the path cannot be a container result = false; } } return result; } // IsItemContainer #endregion IsItemContainer #region RemoveItem /// /// Deletes the specified object /// /// /// /// A relative or absolute path to the object to be deleted. /// /// /// /// The delete should occur in all sub-containers of the specified path. /// /// /// /// Passed on to providers to force operations. /// /// /// /// If true, globbing is not done on paths. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal void RemoveItem(string[] paths, bool recurse, bool force, bool literalPath) { if (paths == null) { throw PSTraceSource.NewArgumentNullException("paths"); } CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); context.Force = force; context.SuppressWildcardExpansion = literalPath; RemoveItem(paths, recurse, context); context.ThrowFirstErrorOrDoNothing(); } // RemoveItem /// /// Deletes the specified object /// /// /// /// A relative or absolute path to the object to be deleted. /// /// /// /// The delete should occur in all sub-containers of the specified path. /// /// /// /// The context which the core command is running. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal void RemoveItem( string[] paths, bool recurse, CmdletProviderContext context) { if (paths == null) { throw PSTraceSource.NewArgumentNullException("paths"); } foreach (string path in paths) { if (path == null) { throw PSTraceSource.NewArgumentNullException("paths"); } ProviderInfo provider = null; CmdletProvider providerInstance = null; Collection providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, false, context, out provider, out providerInstance); foreach (string providerPath in providerPaths) { RemoveItem(providerInstance, providerPath, recurse, context); } } } // RemoveItem /// /// Internal remove item method that just calls the provider directly without globbing. /// /// /// /// The name of the provider to use. /// /// /// /// The path of the item to remove. /// /// /// /// True if all items should be removed recursively. /// /// /// /// The context under which the command is running. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal void RemoveItem( string providerId, string path, bool recurse, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerId != null, "Caller should validate providerId before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); CmdletProvider providerInstance = GetProviderInstance(providerId); RemoveItem(providerInstance, path, recurse, context); } /// /// Internal remove item method that just calls the provider directly without globbing. /// /// /// /// The instance of the provider to use. /// /// /// /// The path of the item to remove. /// /// /// /// True if all items should be removed recursively. /// /// /// /// The context under which the command is running. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal void RemoveItem( CmdletProvider providerInstance, string path, bool recurse, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); try { if (context.HasIncludeOrExclude) { int childrenNotMatchingFilterCriteria = 0; // Use the new code path only if either Include or Exclude is specified // This will take care of all the child items. // This will also take care of the case where "path" is not a container. ProcessPathItems(providerInstance, path, recurse, context, out childrenNotMatchingFilterCriteria, ProcessMode.Delete, skipIsItemContainerCheck: false); // Now delete the container if it matches the filter(s) // and the container does not have any children. if (IsItemContainer(providerInstance, path, context)) { string item = GetChildName(path, context, false); bool isIncludeMatch = SessionStateUtilities.MatchesAnyWildcardPattern( item, SessionStateUtilities.CreateWildcardsFromStrings( context.Include, WildcardOptions.IgnoreCase), true); if (isIncludeMatch) { if (!SessionStateUtilities.MatchesAnyWildcardPattern( item, SessionStateUtilities.CreateWildcardsFromStrings( context.Exclude, WildcardOptions.IgnoreCase), false)) { // Earlier, we used to check if "path" has any child items remaining. If not, we remove "path". // This does not work for some providers (for e.g. IIS provider) which do not support removing the child items // So, instead of checking for any child items remaining (which are applicable to only those providers which support them - like File, Registry), // we check if there are any items that were never intended to be deleted. If there are no such items, then, we can remove "path". // WinBlue: 289907 if (childrenNotMatchingFilterCriteria == 0) { containerCmdletProvider.RemoveItem(path, false, context); } } } } // IsItemContainer } else { containerCmdletProvider.RemoveItem(path, recurse, context); } } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "RemoveItemProviderException", SessionStateStrings.RemoveItemProviderException, containerCmdletProvider.ProviderInfo, path, e); } } // RemoveItem /// /// Gets the dynamic parameters for the remove-item cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The delete should occur in all sub-containers of the specified path. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal object RemoveItemDynamicParameters( string path, bool recurse, CmdletProviderContext context) { if (path == null) { return null; } ProviderInfo provider = null; CmdletProvider providerInstance = null; CmdletProviderContext newContext = new CmdletProviderContext(context); newContext.SetFilters( new Collection(), new Collection(), null); Collection providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, true, newContext, out provider, out providerInstance); if (providerPaths.Count > 0) { // Get the dynamic parameters for the first resolved path return RemoveItemDynamicParameters(providerInstance, providerPaths[0], recurse, newContext); } return null; } // RemoveItemDynamicParameters /// /// Gets the dynamic parameters for the remove-item cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// If true, all items in the subtree should be removed. /// /// /// /// The instance of the provider to use. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private object RemoveItemDynamicParameters( CmdletProvider providerInstance, string path, bool recurse, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); object result = null; try { result = containerCmdletProvider.RemoveItemDynamicParameters(path, recurse, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "RemoveItemProviderException", SessionStateStrings.RemoveItemProviderException, containerCmdletProvider.ProviderInfo, path, e); } return result; } // RemoveItemDynamicParameters #endregion RemoveItem #region GetChildItems /// /// Gets the children of the specified item. /// /// /// /// An array of relative or absolute paths to the object to get the children of. /// /// /// /// If true, gets all the children in all the sub-containers of the specified /// container. If false, only gets the immediate children of the specified /// container. /// /// /// /// Limits the depth of recursion; uint.MaxValue performs full recursion. /// /// /// /// Passed on to providers to force operations. /// /// /// /// If true, globbing is not done on paths. /// /// /// /// This method does not provider streaming of the results. If you want streaming /// then you must call the overload that takes a CmdletProviderContext. /// /// /// /// If or is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal Collection GetChildItems(string[] paths, bool recurse, uint depth, bool force, bool literalPath) { if (paths == null) { throw PSTraceSource.NewArgumentNullException("paths"); } CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); context.Force = force; context.SuppressWildcardExpansion = literalPath; foreach (string path in paths) { if (path == null) { throw PSTraceSource.NewArgumentNullException("paths"); } GetChildItems(path, recurse, depth, context); } context.ThrowFirstErrorOrDoNothing(); return context.GetAccumulatedObjects(); } // GetChildItems /// /// Gets the children of the specified item. /// /// /// /// A relative or absolute path to the object to get the children of. /// /// /// /// If true, gets all the children in all the sub-containers of the specified /// container. If false, only gets the immediate children of the specified /// container. /// /// /// /// Limits the depth of recursion; uint.MaxValue performs full recursion. /// /// /// /// The context which the core command is running. /// /// /// /// If or is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal void GetChildItems( string path, bool recurse, uint depth, CmdletProviderContext context) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } if (context == null) { throw PSTraceSource.NewArgumentNullException("context"); } ProviderInfo provider = null; if ((recurse && !context.SuppressWildcardExpansion) || LocationGlobber.ShouldPerformGlobbing(path, context)) { bool modifiedInclude = false; try { // If we're recursing, do some path fixups to match user // expectations: if (recurse) { string childName = GetChildName(path, context); // If -File or -Directory is specified and path is ended with '*', we should include the parent path as search path bool isFileOrDirectoryPresent = false; if(context.DynamicParameters is Microsoft.PowerShell.Commands.GetChildDynamicParameters dynParam) { isFileOrDirectoryPresent = dynParam.File.IsPresent || dynParam.Directory.IsPresent; } if (String.Equals(childName, "*", StringComparison.OrdinalIgnoreCase) && isFileOrDirectoryPresent) { string parentName = path.Substring(0, path.Length - childName.Length); path = parentName; } // dir c:\tem* -include *.ps1 -rec => No change if ((context.Include == null) || (context.Include.Count == 0)) { // dir c:\tem* -rec => dir c:\ -include tem* -rec // dir tem* -rec => dir -include tem* -rec // dir temp -rec // Should glob paths and files that match tem*, but then // recurse into all subdirectories and do the same for // those directories. if (!String.IsNullOrEmpty(path) && !IsItemContainer(path)) { if (!String.Equals(childName, "*", StringComparison.OrdinalIgnoreCase)) { if (context.Include != null) { context.Include.Add(childName); modifiedInclude = true; } } string parentName = path.Substring(0, path.Length - childName.Length); path = parentName; } } } // Save the include and exclude filters so that we can ignore // them when doing recursion Collection include = context.Include; Collection exclude = context.Exclude; string filter = context.Filter; if (recurse) { context.SetFilters( new Collection(), new Collection(), null); } CmdletProvider providerInstance = null; Collection providerPaths = null; try { providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, false, context, out provider, out providerInstance); } finally { // Reset the include and exclude filters context.SetFilters( include, exclude, filter); } // Ensure this is a container provider before recursing. // GetContainerProviderInstance throws otherwise (as was done in V2.) if (recurse) { ContainerCmdletProvider unused = GetContainerProviderInstance(provider); } bool getChildrenBecauseNoGlob = !LocationGlobber.StringContainsGlobCharacters(path); // If we are doing recursion and we have include or exclude // filters the recursion must be done manually. // Win8: 279933 Perf degradation: recursive get-childitem is 100% slower than win7 // To fix this Perf regression, using getChildrenBecauseNoGlob (+recurse) variables // to decide when to use ManualGetChildItems and when not to. if ( (recurse && !getChildrenBecauseNoGlob && (include != null) && (include.Count == 0)) || (include != null && include.Count > 0) || (exclude != null && exclude.Count > 0)) { // Do the recursion manually so that we can apply the // include and exclude filters foreach (string providerPath in providerPaths) { // Making sure to obey the StopProcessing. if (context.Stopping) { return; } int unUsedChildrenNotMatchingFilterCriteria = 0; ProcessPathItems(providerInstance, providerPath, recurse, depth, context, out unUsedChildrenNotMatchingFilterCriteria, ProcessMode.Enumerate); } } else { // If the path wasn't globbed or we are recursing then we want to get the // children of the path. If we were globbing and we are not recursing // then we just want to get the item for the resolved paths. foreach (string providerPath in providerPaths) { // Making sure to obey the StopProcessing. if (context.Stopping) { return; } if ((getChildrenBecauseNoGlob || recurse) && IsItemContainer(providerInstance, providerPath, context)) { GetChildItems(providerInstance, providerPath, recurse, depth, context); } else { GetItemPrivate(providerInstance, providerPath, context); } } } } finally { if (modifiedInclude) { context.Include.Clear(); } } } else { PSDriveInfo drive = null; string originalPath = path; path = Globber.GetProviderPath( path, context, out provider, out drive); if (drive != null) { context.Drive = drive; } ContainerCmdletProvider providerInstance = GetContainerProviderInstance(provider); if ( (context.Include != null && context.Include.Count > 0) || (context.Exclude != null && context.Exclude.Count > 0)) { // Do the recursion manually so that we can apply the // include and exclude filters int unUsedChildrenNotMatchingFilterCriteria = 0; try { // Temeporary set literal path as false to apply filter context.SuppressWildcardExpansion = false; ProcessPathItems(providerInstance, path, recurse, depth, context, out unUsedChildrenNotMatchingFilterCriteria, ProcessMode.Enumerate); } finally { context.SuppressWildcardExpansion = true; } } else if (path != null && this.ItemExists(providerInstance, path, context)) { if (IsItemContainer(providerInstance, path, context)) { GetChildItems(providerInstance, path, recurse, depth, context); } else { GetItemPrivate(providerInstance, path, context); } } else { ItemNotFoundException pathNotFound = new ItemNotFoundException( path, "PathNotFound", SessionStateStrings.PathNotFound); throw pathNotFound; } } } // GetChildItems /// /// Gets the child items of the item at the specified path. /// /// /// /// The provider instance to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// If true, all the child items in the subtree are returned. /// /// /// /// Limits the depth of recursion; uint.MaxValue performs full recursion. /// /// /// /// The context which the core command is running. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private void GetChildItems( CmdletProvider providerInstance, string path, bool recurse, uint depth, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); try { containerCmdletProvider.GetChildItems(path, recurse, depth, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "GetChildrenProviderException", SessionStateStrings.GetChildrenProviderException, containerCmdletProvider.ProviderInfo, path, e); } } // GetChildItems /// /// Determines if the item at the specified path is a container. /// /// /// /// The provider instance to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The context which the core command is running. /// /// private bool IsPathContainer( CmdletProvider providerInstance, string path, CmdletProviderContext context) { bool itemContainer = false; try { itemContainer = IsItemContainer(providerInstance, path, context); } catch (UnauthorizedAccessException accessException) { context.WriteError(new ErrorRecord(accessException, "GetItemUnauthorizedAccessError", ErrorCategory.PermissionDenied, path)); } catch (ProviderInvocationException accessException) { // if providerinvocationexception is wrapping access denied error, it is ok to not terminate the pipeline if (accessException.InnerException != null && accessException.InnerException.GetType().Equals(typeof(System.UnauthorizedAccessException))) { context.WriteError(new ErrorRecord(accessException, "GetItemUnauthorizedAccessError", ErrorCategory.PermissionDenied, path)); } else { throw; } } return itemContainer; } // IsPathContainer /// /// Since we can't do include and exclude filtering on items we have to /// do the recursion ourselves. We get each child name and see if it matches /// the include and exclude filters. If the child is a container we recurse /// into that container. /// /// /// /// The instance of the provider to use. /// /// /// /// The path to the item to get the children from. /// /// /// /// Recurse into sub-containers when getting children. /// /// /// /// The context under which the command is running. /// /// /// /// The count of items that do not match any include/exclude criteria. /// /// /// Indicates if this is a Enumerate/Remove operation /// /// a hint used to skip IsItemContainer checks /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// private void ProcessPathItems( CmdletProvider providerInstance, string path, bool recurse, CmdletProviderContext context, out int childrenNotMatchingFilterCriteria, ProcessMode processMode = ProcessMode.Enumerate, bool skipIsItemContainerCheck = false) { // Call ProcessPathItems with 'depth' set to maximum value for infinite recursion when needed. ProcessPathItems(providerInstance, path, recurse, uint.MaxValue, context, out childrenNotMatchingFilterCriteria, processMode, skipIsItemContainerCheck); } // ProcessPathItems /// /// Since we can't do include and exclude filtering on items we have to /// do the recursion ourselves. We get each child name and see if it matches /// the include and exclude filters. If the child is a container we recurse /// into that container. /// /// /// /// The instance of the provider to use. /// /// /// /// The path to the item to get the children from. /// /// /// /// Recurse into sub-containers when getting children. /// /// /// /// Limits the depth of recursion; uint.MaxValue performs full recursion. /// /// /// /// The context under which the command is running. /// /// /// /// The count of items that do not match any include/exclude criteria. /// /// /// Indicates if this is a Enumerate/Remove operation /// /// a hint used to skip IsItemContainer checks /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// private void ProcessPathItems( CmdletProvider providerInstance, string path, bool recurse, uint depth, CmdletProviderContext context, out int childrenNotMatchingFilterCriteria, ProcessMode processMode = ProcessMode.Enumerate, bool skipIsItemContainerCheck = false) { ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); childrenNotMatchingFilterCriteria = 0; Dbg.Diagnostics.Assert( providerInstance != null, "The caller should have verified the providerInstance"); Dbg.Diagnostics.Assert( path != null, "The caller should have verified the path"); Dbg.Diagnostics.Assert( context != null, "The caller should have verified the context"); // Construct the include filter Collection includeMatcher = SessionStateUtilities.CreateWildcardsFromStrings( context.Include, WildcardOptions.IgnoreCase); // Construct the exclude filter Collection excludeMatcher = SessionStateUtilities.CreateWildcardsFromStrings( context.Exclude, WildcardOptions.IgnoreCase); // If the item is a container we have to filter its children // Use a hint + lazy evaluation to skip a container check if (skipIsItemContainerCheck || IsPathContainer(providerInstance, path, context)) { CmdletProviderContext newContext = new CmdletProviderContext(context); Collection childNameObjects = null; System.Collections.Generic.Dictionary filteredChildNameDictionary = null; try { // Get all the child names GetChildNames( providerInstance, path, (recurse) ? ReturnContainers.ReturnAllContainers : ReturnContainers.ReturnMatchingContainers, newContext); newContext.WriteErrorsToContext(context); childNameObjects = newContext.GetAccumulatedObjects(); // The code above initially retrieves all of the containers so that it doesn't limit the recursion, // but then emits the non-matching container further down. The public API doesn't support a way to // differentiate the two, so we need to do a diff. // So if there was a filter, do it again to get the fully filtered items. if (recurse && (providerInstance.IsFilterSet())) { newContext.RemoveStopReferral(); newContext = new CmdletProviderContext(context); filteredChildNameDictionary = new System.Collections.Generic.Dictionary(); GetChildNames( providerInstance, path, ReturnContainers.ReturnMatchingContainers, newContext); var filteredChildNameObjects = newContext.GetAccumulatedObjects(); foreach (PSObject filteredChildName in filteredChildNameObjects) { string filteredName = filteredChildName.BaseObject as string; if (filteredName != null) { filteredChildNameDictionary[filteredName] = true; } } } } finally { newContext.RemoveStopReferral(); } // Now loop through all the child objects matching the filters and recursing // into containers for (int index = 0; index < childNameObjects.Count; ++index) { // Making sure to obey the StopProcessing. if (context.Stopping) { return; } string childName = childNameObjects[index].BaseObject as string; if (childName == null) { continue; } // Generate the provider path for the child string qualifiedPath = MakePath(providerInstance, path, childName, context); if (qualifiedPath == null) { continue; } bool isIncludeMatch = !context.SuppressWildcardExpansion && SessionStateUtilities.MatchesAnyWildcardPattern( childName, includeMatcher, true); if (isIncludeMatch) { if (!SessionStateUtilities.MatchesAnyWildcardPattern( childName, excludeMatcher, false)) { bool emitItem = true; if (filteredChildNameDictionary != null) { bool isChildNameInDictionary = false; emitItem = filteredChildNameDictionary.TryGetValue(childName, out isChildNameInDictionary); } if (emitItem) { if (processMode == ProcessMode.Delete) { containerCmdletProvider.RemoveItem(qualifiedPath, false, context); } else if (processMode != ProcessMode.Delete) { // The object is a match so get it and write it out. GetItemPrivate(providerInstance, qualifiedPath, context); } } } else { childrenNotMatchingFilterCriteria++; } } else { childrenNotMatchingFilterCriteria++; } // Now recurse if it is a container if (recurse && IsPathContainer(providerInstance, qualifiedPath, context) && depth > 0) { // Making sure to obey the StopProcessing. if (context.Stopping) { return; } // The item is a container so recurse into it. ProcessPathItems(providerInstance, qualifiedPath, recurse, depth - 1, context, out childrenNotMatchingFilterCriteria, processMode, skipIsItemContainerCheck: true); } } // for each childName } else { // The path is not a container so write it out if its name // matches the filter string childName = path; childName = GetChildName(providerInstance, path, context, true); // Write out the object if it is a match bool isIncludeMatch = SessionStateUtilities.MatchesAnyWildcardPattern( childName, includeMatcher, true); if (isIncludeMatch) { if (!SessionStateUtilities.MatchesAnyWildcardPattern( childName, excludeMatcher, false)) { if (processMode != ProcessMode.Delete) { // The object is a match so get it and write it out. GetItemPrivate(providerInstance, path, context); } else { // The object is a match so, remove it. containerCmdletProvider.RemoveItem(path, recurse, context); } } } } } // ProcessPathItems /// /// Gets the dynamic parameters for the get-childitem cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The delete should occur in all sub-containers of the specified path. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal object GetChildItemsDynamicParameters( string path, bool recurse, CmdletProviderContext context) { if (path == null) { return null; } ProviderInfo provider = null; CmdletProvider providerInstance = null; // Get the provider that will handle this path Globber.GetProviderPath(path, out provider); // See if it supports dynamic parameters. If not, we don't need to // glob the path. if (!HasGetChildItemDynamicParameters(provider)) { return null; } CmdletProviderContext newContext = new CmdletProviderContext(context); newContext.SetFilters( new Collection(), new Collection(), null); Collection providerPaths = null; try { providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, true, newContext, out provider, out providerInstance); } catch (ItemNotFoundException) { // If the provided path is like c:\fakepath\aa*, since we cannot resolve c:\fakepath, an // ItemNotFoundException will be thrown out. In this case, we catch the exception // and check if the "providerInstance" is identified. If providerInstance is not null, // we can carry on with the get-dynamic-parameters method. if (providerInstance == null) { throw; } } if (providerPaths != null && providerPaths.Count > 0) { // Get the dynamic parameters for the first resolved path return GetChildItemsDynamicParameters(providerInstance, providerPaths[0], recurse, newContext); } else { if (providerInstance != null) { PSDriveInfo drive = null; // If we get here, the GetProviderPath should always succeed. This method was already invoked // in the call to GetGlobbedProviderPathsFromMonadPath, and since "providerInstance" is not null, // the invocation in method GetGlobbedProviderPathsFromMonadPath should succeed. string providerPath = Globber.GetProviderPath(path, context, out provider, out drive); if (providerPath != null) { return GetChildItemsDynamicParameters(providerInstance, providerPath, recurse, newContext); } } } return null; } // GetChildItemsDynamicParameters // Detect if the GetChildItemDynamicParameters has been overridden. private bool HasGetChildItemDynamicParameters(ProviderInfo providerInfo) { Type providerType = providerInfo.ImplementingType; MethodInfo mi = null; do { mi = providerType.GetMethod("GetChildItemsDynamicParameters", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly); providerType = providerType.GetTypeInfo().BaseType; } while ( (mi == null) && (providerType != null) && (providerType != typeof(ContainerCmdletProvider)) ); return (mi != null); } /// /// Gets the dynamic parameters for the get-childitem cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// If true, all child items in the subtree should be returned. /// /// /// /// The instance of the provider to use. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private object GetChildItemsDynamicParameters( CmdletProvider providerInstance, string path, bool recurse, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); object result = null; try { result = containerCmdletProvider.GetChildItemsDynamicParameters(path, recurse, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "GetChildrenDynamicParametersProviderException", SessionStateStrings.GetChildrenDynamicParametersProviderException, containerCmdletProvider.ProviderInfo, path, e); } return result; } // GetChildItemsDynamicParameters #endregion GetChildItems #region GetChildNames /// /// Gets names of the children of the specified path. /// /// /// /// The paths to the items from which to retrieve the child names. /// /// /// /// Determines if all containers should be returned or only those containers that match the /// filter(s). /// /// /// /// If true, gets all the relative paths of all the children /// in all the sub-containers of the specified /// container. If false, only gets the immediate child names of the specified /// container. /// /// /// /// Limits the depth of recursion; uint.MaxValue performs full recursion. /// /// /// /// Passed on to providers to force operations. /// /// /// /// If true, globbing is not done on paths. /// /// /// /// An array of strings that contains the names of the children of the specified /// container. /// /// /// /// The child names are the leaf portion of the path. Example, for the file system /// the name for the path c:\windows\system32\foo.dll would be foo.dll or for /// the directory c:\windows\system32 would be system32. For Active Directory the /// child names would be RDN values of the child objects of the container. /// /// /// /// If or is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal Collection GetChildNames( string[] paths, ReturnContainers returnContainers, bool recurse, uint depth, bool force, bool literalPath) { if (paths == null) { throw PSTraceSource.NewArgumentNullException("paths"); } CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); context.Force = force; context.SuppressWildcardExpansion = literalPath; foreach (string path in paths) { if (path == null) { throw PSTraceSource.NewArgumentNullException("paths"); } GetChildNames(path, returnContainers, recurse, depth, context); } context.ThrowFirstErrorOrDoNothing(); Collection objectResults = context.GetAccumulatedObjects(); Collection results = new Collection(); foreach (PSObject resultObject in objectResults) { results.Add(resultObject.BaseObject as string); } return results; } // GetChildNames /// /// Gets names of the children of the specified path. /// /// /// /// The path to the item from which to retrieve the child names. /// /// /// /// Determines if all containers should be returned or only those containers that match the /// filter(s). /// /// /// /// If true, gets all the relative paths of all the children /// in all the sub-containers of the specified /// container. If false, only gets the immediate child names of the specified /// container. /// /// /// /// Limits the depth of recursion; uint.MaxValue performs full recursion. /// /// /// /// The context which the core command is running. /// /// /// /// Nothing is returned, but all names should be written to the context object. /// /// /// /// The child names are the leaf portion of the path. Example, for the file system /// the name for the path c:\windows\system32\foo.dll would be foo.dll or for /// the directory c:\windows\system32 would be system32. For Active Directory the /// child names would be RDN values of the child objects of the container. /// /// /// /// If or is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal void GetChildNames( string path, ReturnContainers returnContainers, bool recurse, uint depth, CmdletProviderContext context) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } // Construct the include filter Collection includeMatcher = SessionStateUtilities.CreateWildcardsFromStrings( context.Include, WildcardOptions.IgnoreCase); // Construct the exclude filter Collection excludeMatcher = SessionStateUtilities.CreateWildcardsFromStrings( context.Exclude, WildcardOptions.IgnoreCase); if (LocationGlobber.ShouldPerformGlobbing(path, context)) { ProviderInfo provider = null; CmdletProvider providerInstance = null; // We don't want to process include/exclude filters // when globbing the targets of the operation, so // copy the context without the filters. CmdletProviderContext resolvePathContext = new CmdletProviderContext(context); resolvePathContext.SetFilters( new Collection(), new Collection(), null); // Resolve the path Collection providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, false, resolvePathContext, out provider, out providerInstance); if (resolvePathContext.Drive != null) { context.Drive = resolvePathContext.Drive; } bool pathContainsGlobCharacters = LocationGlobber.StringContainsGlobCharacters(path); foreach (string providerPath in providerPaths) { // Making sure to obey the StopProcessing. if (context.Stopping) { return; } if ((!pathContainsGlobCharacters || recurse) && IsItemContainer(providerInstance, providerPath, context)) { // Since the path contained glob characters or we are recursing and the // path is a container, do the name enumeration manually DoGetChildNamesManually( providerInstance, providerPath, String.Empty, returnContainers, includeMatcher, excludeMatcher, context, recurse, depth); } else { // Since the original path did not contain glob characters, // if the provider is a NavigationCmdletProvider, write // out the child name, else write out the name as it // was resolved. if (providerInstance is NavigationCmdletProvider) { string childName = GetChildName( providerInstance, providerPath, context, false); bool isIncludeMatch = SessionStateUtilities.MatchesAnyWildcardPattern( childName, includeMatcher, true); bool isExcludeMatch = SessionStateUtilities.MatchesAnyWildcardPattern( childName, excludeMatcher, false); if (isIncludeMatch && !isExcludeMatch) { context.WriteObject(childName); } } else { context.WriteObject(providerPath); } } } } else { // Figure out which provider to use ProviderInfo provider = null; PSDriveInfo drive = null; string providerPath = Globber.GetProviderPath( path, context, out provider, out drive); ContainerCmdletProvider providerInstance = GetContainerProviderInstance(provider); if (drive != null) { context.Drive = drive; } if (!providerInstance.ItemExists(providerPath, context)) { ItemNotFoundException pathNotFound = new ItemNotFoundException( providerPath, "PathNotFound", SessionStateStrings.PathNotFound); throw pathNotFound; } if (recurse) { // The path did not contain glob characters but recurse was specified // so do the enumeration manually DoGetChildNamesManually( providerInstance, providerPath, String.Empty, returnContainers, includeMatcher, excludeMatcher, context, recurse, depth); } else { // Since the path did not contain glob characters and recurse wasn't // specified, we can have the provider write out the child names directly GetChildNames( providerInstance, providerPath, returnContainers, context); } } } // GetChildNames /// /// Gets the child names of the item at the specified path by /// manually recursing through all the containers instead of /// allowing the provider to do the recursion. /// /// /// /// The provider instance to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The path the name is relative to. /// /// /// /// If true all names in the subtree should be returned. /// /// /// /// Current depth of recursion; special case uint.MaxValue performs full recursion. /// /// /// /// Determines if all containers should be returned or only those containers that match the /// filter(s). /// /// /// /// A set of filters that the names must match to be returned. /// /// /// /// A set of filters that the names cannot match to be returned. /// /// /// /// The context which the core command is running. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private void DoGetChildNamesManually( CmdletProvider providerInstance, string providerPath, string relativePath, ReturnContainers returnContainers, Collection includeMatcher, Collection excludeMatcher, CmdletProviderContext context, bool recurse, uint depth) { Dbg.Diagnostics.Assert( providerInstance != null, "The providerInstance should have been verified by the caller"); Dbg.Diagnostics.Assert( providerPath != null, "The paths should have been verified by the caller"); Dbg.Diagnostics.Assert( context != null, "The context should have been verified by the caller"); string newProviderPath = MakePath( providerInstance, providerPath, relativePath, context); CmdletProviderContext childNamesContext = new CmdletProviderContext(context); try { // First get all children that match the filters and write them out GetChildNames( providerInstance, newProviderPath, ReturnContainers.ReturnMatchingContainers, childNamesContext); Collection results = childNamesContext.GetAccumulatedObjects(); foreach (PSObject result in results) { // Making sure to obey the StopProcessing. if (context.Stopping) { return; } string name = result.BaseObject as string; if (name == null) { continue; } bool isIncludeMatch = SessionStateUtilities.MatchesAnyWildcardPattern( name, includeMatcher, true); if (isIncludeMatch) { if (!SessionStateUtilities.MatchesAnyWildcardPattern( name, excludeMatcher, false)) { string resultPath = MakePath(providerInstance, relativePath, name, context); context.WriteObject(resultPath); } } } if (recurse) { // Now get all the children that are containers and recurse into them // Limiter for recursion if (depth > 0) // this includes special case 'depth == uint.MaxValue' for unlimited recursion { GetChildNames( providerInstance, newProviderPath, ReturnContainers.ReturnAllContainers, childNamesContext); results = childNamesContext.GetAccumulatedObjects(); foreach (PSObject result in results) { // Making sure to obey the StopProcessing. if (context.Stopping) { return; } string name = result.BaseObject as string; if (name == null) { continue; } // Generate the relative path from the provider path string resultRelativePath = MakePath( providerInstance, relativePath, name, context); // Generate the provider path for the child item to see // if it is a container string resultProviderPath = MakePath( providerInstance, providerPath, resultRelativePath, context); // If the item is a container recurse into it and output its // child names if (IsItemContainer(providerInstance, resultProviderPath, context)) { DoGetChildNamesManually( providerInstance, providerPath, resultRelativePath, returnContainers, includeMatcher, excludeMatcher, context, true, depth - 1); } } // foreach } // if } // recurse } finally { childNamesContext.RemoveStopReferral(); } } // DoGetChildNamesRecurseManually /// /// Gets the names of the children of the item at the specified path. /// /// /// /// The provider instance to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// Determines if all containers should be returned or only those containers that match the /// filter(s). /// /// /// /// The context which the core command is running. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private void GetChildNames( CmdletProvider providerInstance, string path, ReturnContainers returnContainers, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); try { containerCmdletProvider.GetChildNames(path, returnContainers, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "GetChildNamesProviderException", SessionStateStrings.GetChildNamesProviderException, containerCmdletProvider.ProviderInfo, path, e); } } // GetChildNames /// /// Gets the dynamic parameters for the get-childitem -name cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal object GetChildNamesDynamicParameters( string path, CmdletProviderContext context) { if (path == null) { return null; } ProviderInfo provider = null; CmdletProvider providerInstance = null; CmdletProviderContext newContext = new CmdletProviderContext(context); newContext.SetFilters( new Collection(), new Collection(), null); Collection providerPaths = null; try { providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, true, newContext, out provider, out providerInstance); } catch (ItemNotFoundException) { // If the provided path is like c:\fakepath\aa*, since we cannot resolve c:\fakepath, an // ItemNotFoundException will be thrown out. In this case, we catch the exception // and check if the "providerInstance" is identified. If providerInstance is not null, // we can carry on with the get-dynamic-parameters method. if (providerInstance == null) { throw; } } object result = null; if (providerPaths != null && providerPaths.Count > 0) { // Get the dynamic parameters for the first resolved path result = GetChildNamesDynamicParameters(providerInstance, providerPaths[0], newContext); } else { if (providerInstance != null) { PSDriveInfo drive = null; // If we get here, the GetProviderPath should always succeed. This method was already invoked // in the call to GetGlobbedProviderPathsFromMonadPath, and since "providerInstance" is not null, // the invocation in method GetGlobbedProviderPathsFromMonadPath should succeed. string providerPath = Globber.GetProviderPath(path, context, out provider, out drive); if (providerPath != null) { result = GetChildNamesDynamicParameters(providerInstance, providerPath, newContext); } } } return result; } // GetChildNamesDynamicParameters /// /// Gets the dynamic parameters for the get-childitem -names cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The instance of the provider to use. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private object GetChildNamesDynamicParameters( CmdletProvider providerInstance, string path, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerId before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); object result = null; try { result = containerCmdletProvider.GetChildNamesDynamicParameters(path, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "GetChildNamesDynamicParametersProviderException", SessionStateStrings.GetChildNamesDynamicParametersProviderException, containerCmdletProvider.ProviderInfo, path, e); } return result; } // GetChildNamesDynamicParameters #endregion GetChildNames #region RenameItem /// /// Renames the item at the specified path to the new name provided. /// /// /// /// The path to the item to rename. /// /// /// /// The name to which the item should be renamed. This name should always be /// relative to the parent container. /// /// /// /// Passed on to providers to force operations. /// /// /// /// The item that was renamed at the specified path. /// /// /// /// If or is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal Collection RenameItem(string path, string newName, bool force) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); context.Force = force; RenameItem(path, newName, context); context.ThrowFirstErrorOrDoNothing(); // Since there was no errors return the accumulated objects return context.GetAccumulatedObjects(); } // RenameItem /// /// Renames the item at the specified path to the new name provided. /// /// /// /// The path to the item to rename. /// /// /// /// The name to which the item should be renamed. This name should always be /// relative to the parent container. /// /// /// /// The context which the core command is running. /// /// /// /// Nothing. All items that are renamed are written into the context object. /// /// /// /// If or is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal void RenameItem( string path, string newName, CmdletProviderContext context) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } ProviderInfo provider = null; CmdletProvider providerInstance = null; Collection providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, false, context, out provider, out providerInstance); // Can only rename one item at a time, so if we glob more than // one item write out an error. if (providerPaths.Count == 1) { RenameItem(providerInstance, providerPaths[0], newName, context); } else { ArgumentException argException = PSTraceSource.NewArgumentException( "path", SessionStateStrings.RenameMultipleItemError); context.WriteError( new ErrorRecord( argException, "RenameMultipleItemError", ErrorCategory.InvalidArgument, providerPaths)); } } // RenameItem /// /// Renames the item at the specified path. /// /// /// /// The provider instance to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The new name of the item. /// /// /// /// The context which the core command is running. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private void RenameItem( CmdletProvider providerInstance, string path, string newName, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); try { containerCmdletProvider.RenameItem(path, newName, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "RenameItemProviderException", SessionStateStrings.RenameItemProviderException, containerCmdletProvider.ProviderInfo, path, e); } } // RenameItem /// /// Gets the dynamic parameters for the rename-item cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The name to which the item should be renamed. This name should always be /// relative to the parent container. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal object RenameItemDynamicParameters( string path, string newName, CmdletProviderContext context) { if (path == null) { return null; } ProviderInfo provider = null; CmdletProvider providerInstance = null; CmdletProviderContext newContext = new CmdletProviderContext(context); newContext.SetFilters( new Collection(), new Collection(), null); Collection providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, true, newContext, out provider, out providerInstance); if (providerPaths.Count > 0) { // Get the dynamic parameters for the first resolved path return RenameItemDynamicParameters(providerInstance, providerPaths[0], newName, newContext); } return null; } // RenameItemDynamicParameters /// /// Gets the dynamic parameters for the rename-item cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The new name of the item. /// /// /// /// The instance of the provider to use. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private object RenameItemDynamicParameters( CmdletProvider providerInstance, string path, string newName, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); object result = null; try { result = containerCmdletProvider.RenameItemDynamicParameters(path, newName, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "RenameItemDynamicParametersProviderException", SessionStateStrings.RenameItemDynamicParametersProviderException, containerCmdletProvider.ProviderInfo, path, e); } return result; } // RenameItemDynamicParameters #endregion RenameItem #region NewItem /// /// Creates a new item at the specified path. /// /// /// /// The path(s) to the container(s) to create the item in. /// /// /// /// The name of the item to create. /// /// /// /// The provider specific type of the object to be created. /// /// /// /// The content of the new item to create. /// /// /// /// Passed on to providers to force operations. /// /// /// /// The item(s) that was created. /// /// /// /// If or is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal Collection NewItem(string[] paths, string name, string type, object content, bool force) { if (paths == null) { throw PSTraceSource.NewArgumentNullException("paths"); } CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); context.Force = force; NewItem(paths, name, type, content, context); context.ThrowFirstErrorOrDoNothing(); // Since there was no errors return the accumulated objects return context.GetAccumulatedObjects(); } // NewItem /// /// Creates a new item at the specified path. /// /// /// /// The path(s) to the item(s) to create. /// /// /// /// The name of the item to create. /// /// /// /// The provider specific type of the item to be created. /// /// /// /// The content to create the new item with. /// /// /// /// The context which the core command is running. /// /// /// /// Nothing. The item created is written to the context object. /// /// /// /// If or is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal void NewItem( string[] paths, string name, string type, object content, CmdletProviderContext context) { if (paths == null) { throw PSTraceSource.NewArgumentNullException("paths"); } foreach (string path in paths) { string resolvePath = null; if (path == null) { PSTraceSource.NewArgumentNullException("paths"); } else { // To be compatible with Linux OS. Which will be either '/' or '\' depends on the OS type. char[] charsToTrim = {' ', Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar}; resolvePath = path.TrimEnd(charsToTrim); } ProviderInfo provider = null; PSDriveInfo driveInfo; CmdletProvider providerInstance = null; Collection providerPaths = new Collection(); // Only glob the path if the name is specified if (String.IsNullOrEmpty(name)) { string providerPath = Globber.GetProviderPath(resolvePath, context, out provider, out driveInfo); providerInstance = GetProviderInstance(provider); providerPaths.Add(providerPath); } else { providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( resolvePath, true, context, out provider, out providerInstance); } foreach (string providerPath in providerPaths) { // Compose the globbed container and the name together to get a path // to pass on to the provider. string composedPath = providerPath; if (!String.IsNullOrEmpty(name)) { composedPath = MakePath(providerInstance, providerPath, name, context); } // Don't support 'New-Item -Type Directory' on the Function provider // if the runspace has ever been in constrained language mode, as the mkdir // function can be abused if (context.ExecutionContext.HasRunspaceEverUsedConstrainedLanguageMode && (providerInstance is Microsoft.PowerShell.Commands.FunctionProvider) && (String.Equals(type, "Directory", StringComparison.OrdinalIgnoreCase))) { throw PSTraceSource.NewNotSupportedException(SessionStateStrings.DriveCmdletProvider_NotSupported); } bool isSymbolicJunctionOrHardLink = false; // Symbolic link targets are allowed to not exist on both Windows and Linux bool allowNonexistingPath = false; if (type != null) { WildcardPattern typeEvaluator = WildcardPattern.Get(type + "*", WildcardOptions.IgnoreCase | WildcardOptions.Compiled); if (typeEvaluator.IsMatch("symboliclink") || typeEvaluator.IsMatch("junction") || typeEvaluator.IsMatch("hardlink")) { isSymbolicJunctionOrHardLink = true; allowNonexistingPath = typeEvaluator.IsMatch("symboliclink"); } } if (isSymbolicJunctionOrHardLink) { if (content == null) { throw PSTraceSource.NewArgumentNullException(SessionStateStrings.NewItemValueNotSpecified, path); } string targetPath = content.ToString(); if (String.IsNullOrEmpty(targetPath)) { throw PSTraceSource.NewArgumentNullException(SessionStateStrings.PathNotFound, targetPath); } ProviderInfo targetProvider = null; CmdletProvider targetProviderInstance = null; var globbedTarget = Globber.GetGlobbedProviderPathsFromMonadPath( targetPath, allowNonexistingPath, context, out targetProvider, out targetProviderInstance); if (String.Compare(targetProvider.Name, "filesystem", StringComparison.OrdinalIgnoreCase) != 0) { throw PSTraceSource.NewNotSupportedException(SessionStateStrings.MustBeFileSystemPath); } if (globbedTarget.Count > 1) { throw PSTraceSource.NewInvalidOperationException(SessionStateStrings.PathResolvedToMultiple, targetPath); } if (globbedTarget.Count == 0) { throw PSTraceSource.NewInvalidOperationException(SessionStateStrings.PathNotFound, targetPath); } content = globbedTarget[0]; } NewItemPrivate(providerInstance, composedPath, type, content, context); } } } // NewItem /// /// Creates a new item at the specified path. /// /// /// /// The provider instance to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The type of the item to create. /// /// /// /// The content of the item to create. /// /// /// /// The context which the core command is running. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private void NewItemPrivate( CmdletProvider providerInstance, string path, string type, object content, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); try { containerCmdletProvider.NewItem(path, type, content, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "NewItemProviderException", SessionStateStrings.NewItemProviderException, containerCmdletProvider.ProviderInfo, path, e); } } // NewItem /// /// Gets the dynamic parameters for the new-item cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The provider specific type of the item to be created. /// /// /// /// The content to create the new item with. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal object NewItemDynamicParameters( string path, string type, object newItemValue, CmdletProviderContext context) { if (path == null) { return null; } ProviderInfo provider = null; CmdletProvider providerInstance = null; CmdletProviderContext newContext = new CmdletProviderContext(context); newContext.SetFilters( new Collection(), new Collection(), null); Collection providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, true, newContext, out provider, out providerInstance); if (providerPaths.Count > 0) { // Get the dynamic parameters for the first resolved path return NewItemDynamicParameters(providerInstance, providerPaths[0], type, newItemValue, newContext); } return null; } // NewItemDynamicParameters /// /// Gets the dynamic parameters for the new-item cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The type of the new item. /// /// /// /// The value of the new item /// /// /// /// The instance of the provider to use. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private object NewItemDynamicParameters( CmdletProvider providerInstance, string path, string type, object newItemValue, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); object result = null; try { result = containerCmdletProvider.NewItemDynamicParameters(path, type, newItemValue, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "NewItemDynamicParametersProviderException", SessionStateStrings.NewItemDynamicParametersProviderException, containerCmdletProvider.ProviderInfo, path, e); } return result; } // NewItemDynamicParameters #endregion NewItem #region HasChildItems /// /// Determines if the item at the specified path has children. /// /// /// /// The path to the item to see if it has children. /// /// /// /// Passed on to providers to force operations. /// /// /// /// If true, globbing is not done on paths. /// /// /// /// True if the item has children, false otherwise. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal bool HasChildItems(string path, bool force, bool literalPath) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); context.Force = force; context.SuppressWildcardExpansion = literalPath; bool result = HasChildItems(path, context); context.ThrowFirstErrorOrDoNothing(); return result; } // HasChildItems /// /// Determines if the item at the specified path has children. /// /// /// /// The path to the item to see if it has children. /// /// /// /// The context which the core command is running. /// /// /// /// True if the item has children, false otherwise. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal bool HasChildItems( string path, CmdletProviderContext context) { if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } ProviderInfo provider = null; CmdletProvider providerInstance = null; Collection providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, false, context, out provider, out providerInstance); bool result = false; foreach (string providerPath in providerPaths) { result = HasChildItems(providerInstance, providerPath, context); if (result == true) { break; } } return result; } // HasChildItems /// /// Determines if the item at the specified path has children. /// /// /// /// The provider to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// internal bool HasChildItems( string providerId, string path) { bool result = false; if (String.IsNullOrEmpty(providerId)) { throw PSTraceSource.NewArgumentException("providerId"); } if (path == null) { throw PSTraceSource.NewArgumentNullException("path"); } CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); result = HasChildItems(providerId, path, context); context.ThrowFirstErrorOrDoNothing(); return result; } /// /// Determines if the item at the specified path has children. /// /// /// /// The provider to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The context under which the command is running. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// internal bool HasChildItems( string providerId, string path, CmdletProviderContext context) { ContainerCmdletProvider providerInstance = GetContainerProviderInstance(providerId); return HasChildItems(providerInstance, path, context); } /// /// Determines if the item at the specified path has children. /// /// /// /// The provider instance to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The context which the core command is running. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private bool HasChildItems( CmdletProvider providerInstance, string path, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); bool result = false; try { result = containerCmdletProvider.HasChildItems(path, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "HasChildItemsProviderException", SessionStateStrings.HasChildItemsProviderException, containerCmdletProvider.ProviderInfo, path, e); } return result; } // HasChildItems #endregion HasChildItems #region CopyItem /// /// Copies an item at the specified path to an item at the . /// /// /// /// The path(s) of the item(s) to copy. /// /// /// /// The path of the item to copy to. /// /// /// /// Tells the provider to recurse sub-containers when copying. /// /// /// /// Determines how the source container is used in the copy operation. /// /// /// /// Passed on to providers to force operations. /// /// /// /// If true, globbing is not done on paths. /// /// /// /// The objects that were copied. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// internal Collection CopyItem(string[] paths, string copyPath, bool recurse, CopyContainers copyContainers, bool force, bool literalPath) { if (paths == null) { throw PSTraceSource.NewArgumentNullException("paths"); } if (copyPath == null) { copyPath = String.Empty; } CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); context.Force = force; context.SuppressWildcardExpansion = literalPath; CopyItem(paths, copyPath, recurse, copyContainers, context); context.ThrowFirstErrorOrDoNothing(); return context.GetAccumulatedObjects(); } // CopyItem /// /// Copies an item at the specified path to an item at the . /// /// /// /// The path(s) of the item(s) to copy. /// /// /// /// The path of the item to copy to. /// /// /// /// Tells the provider to recurse sub-containers when copying. /// /// /// /// Determines how the source container is used in the copy operation. /// /// /// /// The context which the core command is running. /// /// /// /// If is null. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal void CopyItem( string[] paths, string copyPath, bool recurse, CopyContainers copyContainers, CmdletProviderContext context) { if (paths == null) { throw PSTraceSource.NewArgumentNullException("paths"); } if (copyPath == null) { copyPath = String.Empty; } // Get the provider specific path for the destination PSDriveInfo unusedDrive = null; ProviderInfo destinationProvider = null; Microsoft.PowerShell.Commands.CopyItemDynamicParameters dynamicParams = context.DynamicParameters as Microsoft.PowerShell.Commands.CopyItemDynamicParameters; bool destinationIsRemote = false; bool sourceIsRemote = false; string providerDestinationPath; Runspaces.PSSession session = null; if (dynamicParams != null) { if (dynamicParams.FromSession != null) { sourceIsRemote = true; session = dynamicParams.FromSession; } if (dynamicParams.ToSession != null) { destinationIsRemote = true; session = dynamicParams.ToSession; } } if (sourceIsRemote && destinationIsRemote) { context.WriteError(new ErrorRecord( new ArgumentException( String.Format(System.Globalization.CultureInfo.InvariantCulture, SessionStateStrings.CopyItemFromSessionToSession, "FromSession", "ToSession")), "InvalidInput", ErrorCategory.InvalidArgument, dynamicParams)); return; } // Validate that the session is available and the language mode is not set to ConstrainedLanguage or NoLanguage. PSLanguageMode? remoteLanguageMode = null; if (sourceIsRemote || destinationIsRemote) { if (!isValidSession(session, context, out remoteLanguageMode)) { return; } } if (!destinationIsRemote) { providerDestinationPath = Globber.GetProviderPath( copyPath, context, out destinationProvider, out unusedDrive); } else { // Validate remote destination path providerDestinationPath = copyPath; if (String.IsNullOrEmpty(providerDestinationPath)) { context.WriteError(new ErrorRecord( new ArgumentNullException( String.Format( System.Globalization.CultureInfo.InvariantCulture, SessionStateStrings.CopyItemRemotelyPathIsNullOrEmpty, "Destination")), "CopyItemRemoteDestinationIsNullOrEmpty", ErrorCategory.InvalidArgument, providerDestinationPath)); return; } string root = ValidateRemotePathAndGetRoot(providerDestinationPath, session, context, remoteLanguageMode, false); if (root == null) { return; } } s_tracer.WriteLine("providerDestinationPath = {0}", providerDestinationPath); ProviderInfo provider = null; CmdletProvider providerInstance = null; foreach (string path in paths) { if (path == null) { throw PSTraceSource.NewArgumentNullException("paths"); } Collection providerPaths; if (sourceIsRemote) { // Get the root for the remote path string root = ValidateRemotePathAndGetRoot(path, session, context, remoteLanguageMode, true); if (root == null) { return; } providerInstance = ExecutionContext.SessionState.Internal.GetProviderInstance("FileSystem"); providerPaths = new Collection(); providerPaths.Add(path); } else { providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, false, context, out provider, out providerInstance); } // Ensure that the source and destination are the same provider. Copying between // providers is not supported if (!sourceIsRemote && !destinationIsRemote && provider != destinationProvider) { ArgumentException argException = PSTraceSource.NewArgumentException( "path", SessionStateStrings.CopyItemSourceAndDestinationNotSameProvider); context.WriteError( new ErrorRecord( argException, "CopyItemSourceAndDestinationNotSameProvider", ErrorCategory.InvalidArgument, providerPaths)); return; } bool destinationIsContainer = false; if (!destinationIsRemote) { destinationIsContainer = IsItemContainer( providerInstance, providerDestinationPath, context); s_tracer.WriteLine("destinationIsContainer = {0}", destinationIsContainer); } foreach (string providerPath in providerPaths) { // Making sure to obey the StopProcessing. if (context.Stopping) { return; } if (sourceIsRemote || destinationIsRemote) { CopyItem( providerInstance, providerPath, providerDestinationPath, recurse, context); continue; } bool sourceIsContainer = IsItemContainer( providerInstance, providerPath, context); s_tracer.WriteLine("sourceIsContainer = {0}", sourceIsContainer); if (sourceIsContainer) { if (destinationIsContainer) { if (!recurse && copyContainers == CopyContainers.CopyChildrenOfTargetContainer) { // Copying a container to a container without the -container or -recurse // switch is an error. Exception e = PSTraceSource.NewArgumentException( "path", SessionStateStrings.CopyContainerToContainerWithoutRecurseOrContainer); context.WriteError(new ErrorRecord( e, "CopyContainerToContainerWithoutRecurseOrContainer", ErrorCategory.InvalidArgument, providerPath)); } else if (recurse && copyContainers == CopyContainers.CopyChildrenOfTargetContainer) { // Copy all the leaf items to a single container CopyRecurseToSingleContainer( providerInstance, providerPath, providerDestinationPath, context); } else { // Call the provider to do a recurse copy of all the items CopyItem( providerInstance, providerPath, providerDestinationPath, recurse, context); } } else { // Since we know the destination isn't a container, check to // see if it exists. if (ItemExists(providerInstance, providerDestinationPath, context)) { // Since the item exists and is not a container it must // be a leaf. Copying a container to a leaf is an error Exception e = PSTraceSource.NewArgumentException( "path", SessionStateStrings.CopyContainerItemToLeafError); context.WriteError(new ErrorRecord( e, "CopyContainerItemToLeafError", ErrorCategory.InvalidArgument, providerPath)); } else { // Copy the container to a non-existing path CopyItem( providerInstance, providerPath, providerDestinationPath, recurse, context); } } } else { // Copy a leaf to the destination CopyItem( providerInstance, providerPath, providerDestinationPath, recurse, context); } } } } // CopyItem /// /// Copies the specified item(s) to the specified destination /// /// /// /// The provider instance to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The path to copy the item(s) to. /// /// /// /// If true all sub-containers and their children should be copied. /// /// /// /// The context which the core command is running. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private void CopyItem( CmdletProvider providerInstance, string path, string copyPath, bool recurse, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); try { containerCmdletProvider.CopyItem(path, copyPath, recurse, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "CopyItemProviderException", SessionStateStrings.CopyItemProviderException, containerCmdletProvider.ProviderInfo, path, e); } } // CopyItem /// /// Recursively copies many items to a single container /// /// /// /// The provider instance to use. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The path to copy the item(s) to. /// /// /// /// The context which the core command is running. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private void CopyRecurseToSingleContainer( CmdletProvider providerInstance, string sourcePath, string destinationPath, CmdletProviderContext context) { Dbg.Diagnostics.Assert( providerInstance != null, "The providerInstance should have been verified by the caller"); Dbg.Diagnostics.Assert( !String.IsNullOrEmpty(sourcePath), "The sourcePath should have been verified by the caller"); Dbg.Diagnostics.Assert( !String.IsNullOrEmpty(destinationPath), "The destinationPath should have been verified by the caller"); Dbg.Diagnostics.Assert( context != null, "The context should have been verified by the caller"); ContainerCmdletProvider containerProviderInstance = GetContainerProviderInstance(providerInstance); // Use GetChildNames to get the relative paths of all the children // to be copied Collection children = GetChildNames( new string[] { sourcePath }, ReturnContainers.ReturnMatchingContainers, true, uint.MaxValue, false, false); foreach (string childName in children) { // Making sure to obey the StopProcessing. if (context.Stopping) { return; } // Now convert each relative path into a provider-internal path string childPath = MakePath(providerInstance.ProviderInfo, sourcePath, childName, context); // And then copy the item to the destination CopyItem(containerProviderInstance, childPath, destinationPath, false, context); } } // CopyRecurseToSingleContainer /// /// Gets the dynamic parameters for the copy-item cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The path of the item to copy to. /// /// /// /// Tells the provider to recurse sub-containers when copying. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the refers to a provider that could not be found. /// /// /// /// If the refers to a drive that could not be found. /// /// /// /// If the provider that the refers to does /// not support this operation. /// /// /// /// If the provider threw an exception. /// /// /// /// If does not contain glob characters and /// could not be found. /// /// internal object CopyItemDynamicParameters( string path, string destination, bool recurse, CmdletProviderContext context) { if (path == null) { return null; } ProviderInfo provider = null; CmdletProvider providerInstance = null; CmdletProviderContext newContext = new CmdletProviderContext(context); newContext.SetFilters( new Collection(), new Collection(), null); string providerPath = null; bool pathNotFoundOnClient = false; try { Collection providerPaths = Globber.GetGlobbedProviderPathsFromMonadPath( path, true, newContext, out provider, out providerInstance); if (providerPaths.Count > 0) providerPath = providerPaths[0]; } catch (DriveNotFoundException) { // This exception is expected for remote sessions where drives exist in a remote session but not // on the client. pathNotFoundOnClient = true; } catch (ItemNotFoundException) { // This exception is expected for remote sessions where item exist in a remote session but not // on the client. pathNotFoundOnClient = true; } if (pathNotFoundOnClient) { // At this point, we don't know if this is a remote use of copy-item because the FromSession // and ToSession dynamic parameters have not been retrieved yet. // Ignore these exceptions and use the FileSystem provider as default. If this is a real drive // issue, or if the item does not exist, it will be caught later when the drive or item path is used. var fileSystemProviders = Providers["FileSystem"]; if (fileSystemProviders.Count > 0) { providerPath = path; providerInstance = ExecutionContext.EngineSessionState.GetProviderInstance( fileSystemProviders[0]); } } if (providerPath != null) { // Get the dynamic parameters for the first resolved path return CopyItemDynamicParameters(providerInstance, providerPath, destination, recurse, newContext); } return null; } // CopyItemDynamicParameters /// /// Gets the dynamic parameters for the copy-item cmdlet. /// /// /// /// The path to the item if it was specified on the command line. /// /// /// /// The path to copy the item to. /// /// /// /// If true, subcontainers and their children should be copied. /// /// /// /// The instance of the provider to use. /// /// /// /// The context which the core command is running. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the does not support this operation. /// /// /// /// If the pipeline is being stopped while executing the command. /// /// /// /// If the provider threw an exception. /// /// private object CopyItemDynamicParameters( CmdletProvider providerInstance, string path, string destination, bool recurse, CmdletProviderContext context) { // All parameters should have been validated by caller Dbg.Diagnostics.Assert( providerInstance != null, "Caller should validate providerInstance before calling this method"); Dbg.Diagnostics.Assert( path != null, "Caller should validate path before calling this method"); Dbg.Diagnostics.Assert( context != null, "Caller should validate context before calling this method"); ContainerCmdletProvider containerCmdletProvider = GetContainerProviderInstance(providerInstance); object result = null; try { result = containerCmdletProvider.CopyItemDynamicParameters(path, destination, recurse, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout. { throw NewProviderInvocationException( "CopyItemDynamicParametersProviderException", SessionStateStrings.CopyItemDynamicParametersProviderException, containerCmdletProvider.ProviderInfo, path, e); } return result; } // CopyItemDynamicParameters // This function validates a remote path, and if it exists, it returns the root path. // private string ValidateRemotePathAndGetRoot(string path, Runspaces.PSSession session, CmdletProviderContext context, PSLanguageMode? languageMode, bool sourceIsRemote) { Hashtable op = null; using (PowerShell ps = PowerShell.Create()) { ps.Runspace = session.Runspace; // Check to see if the remote PSSession is running in constrained or no language mode and if so // then also if the path validation function already exists in the session (for the User drive // custom endpoint case). Otherwise error out. if (languageMode.HasValue && (languageMode.Value == PSLanguageMode.ConstrainedLanguage || languageMode.Value == PSLanguageMode.NoLanguage)) { var psRemoteUtilsName = CopyFileRemoteUtils.PSCopyRemoteUtilsName; ps.Runspace = session.Runspace; ps.AddCommand("Get-Command").AddArgument(psRemoteUtilsName); var result = ps.Invoke(); if (result.Count == 0) { context.WriteError(new ErrorRecord( new InvalidOperationException( String.Format( System.Globalization.CultureInfo.InvariantCulture, SessionStateStrings.CopyItemSessionProperties, "LanguageMode", session.Runspace.SessionStateProxy.LanguageMode) ), "SessionIsNotInFullLanguageMode", ErrorCategory.InvalidOperation, session.Availability ) ); return null; } ps.Commands.Clear(); ps.Streams.ClearStreams(); ps.AddCommand(psRemoteUtilsName); } else { string remoteScript = CopyFileRemoteUtils.PSValidatePathDefinition; ps.AddScript(remoteScript); } ps.AddParameter("pathToValidate", path); if (sourceIsRemote) { ps.AddParameter("sourceIsRemote", true); } op = Microsoft.PowerShell.Commands.SafeInvokeCommand.Invoke(ps, null, context); } if (op == null) { context.WriteError(new ErrorRecord( new InvalidOperationException( String.Format( System.Globalization.CultureInfo.InvariantCulture, SessionStateStrings.CopyItemValidateRemotePath, path)), "FailedToValidateRemotePath", ErrorCategory.InvalidOperation, path)); return null; } // If the remote path is not absolute, display an error to the user. if (op["IsAbsolute"] != null) { bool isAbsolute = (bool)op["IsAbsolute"]; if (!isAbsolute) { context.WriteError(new ErrorRecord( new ArgumentException( String.Format( System.Globalization.CultureInfo.InvariantCulture, SessionStateStrings.CopyItemRemotelyPathIsNotAbsolute, path)), "RemotePathIsNotAbsolute", ErrorCategory.InvalidArgument, path)); return null; } } bool pathExist = false; string root = null; if (op["Exists"] != null) pathExist = (bool)op["Exists"]; if (op["Root"] != null) root = (string)op["Root"]; // Here there are two scenarios: // 1) If the source is remote and the path does not exist, error out. bool invalidRemoteSource = (sourceIsRemote && (!pathExist)); // 2) For a remote destination, if the root does not exist, error out. bool invalidRemoteDestination = (root == null); if (invalidRemoteSource || invalidRemoteDestination) { context.WriteError(new ErrorRecord( new ArgumentException( String.Format( System.Globalization.CultureInfo.InvariantCulture, SessionStateStrings.PathNotFound, path)), "RemotePathNotFound", ErrorCategory.InvalidArgument, path)); return null; } return root; } private bool isValidSession(PSSession session, CmdletProviderContext context, out PSLanguageMode? languageMode) { // session == null is validated by the parameter binding if (session.Availability != RunspaceAvailability.Available) { context.WriteError(new ErrorRecord( new InvalidOperationException( String.Format(System.Globalization.CultureInfo.InvariantCulture, SessionStateStrings.CopyItemSessionProperties, "Availability", session.Availability)), "SessionIsNotAvailable", ErrorCategory.InvalidOperation, session.Availability)); languageMode = null; return false; } languageMode = session.Runspace.SessionStateProxy.LanguageMode; return true; } #endregion CopyItem #endregion ContainerCmdletProvider accessors } // SessionStateInternal class /// /// Defines the action to be taken for Navigation cmdlets /// internal enum ProcessMode { /// /// Write out the details /// Enumerate = 1, /// /// Delete the item /// Delete = 2 } } #pragma warning restore 56500