mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
4937 lines
190 KiB
C#
4937 lines
190 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using System.Collections;
|
|
using System.Collections.ObjectModel;
|
|
using System.IO;
|
|
using System.Management.Automation.Internal;
|
|
using System.Management.Automation.Provider;
|
|
using System.Management.Automation.Runspaces;
|
|
using System.Reflection;
|
|
|
|
using Dbg = System.Management.Automation;
|
|
|
|
#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings
|
|
#pragma warning disable 56500
|
|
|
|
namespace System.Management.Automation
|
|
{
|
|
/// <summary>
|
|
/// Holds the state of a Monad Shell session.
|
|
/// </summary>
|
|
internal sealed partial class SessionStateInternal
|
|
{
|
|
#region ContainerCmdletProvider accessors
|
|
|
|
#region ItemExists
|
|
|
|
/// <summary>
|
|
/// Determines if the monad virtual namespace path exists.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the object to determine if it exists.
|
|
/// </param>
|
|
/// <param name="force">
|
|
/// Passed on to providers to force operations.
|
|
/// </param>
|
|
/// <param name="literalPath">
|
|
/// If true, globbing is not done on paths.
|
|
/// </param>
|
|
/// <returns>
|
|
/// true if the object specified by path exists, false otherwise.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal bool ItemExists(string path, bool force, bool literalPath)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(path));
|
|
}
|
|
|
|
CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);
|
|
context.Force = force;
|
|
context.SuppressWildcardExpansion = literalPath;
|
|
|
|
bool result = ItemExists(path, context);
|
|
|
|
context.ThrowFirstErrorOrDoNothing();
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the monad virtual namespace path exists.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the object to determine if it exists.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// true if the object specified by path exists, false otherwise.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal bool ItemExists(
|
|
string path,
|
|
CmdletProviderContext context)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(path));
|
|
}
|
|
|
|
ProviderInfo provider = null;
|
|
CmdletProvider providerInstance = null;
|
|
|
|
bool result = false;
|
|
try
|
|
{
|
|
Collection<string> providerPaths =
|
|
Globber.GetGlobbedProviderPathsFromMonadPath(
|
|
path,
|
|
true,
|
|
context,
|
|
out provider,
|
|
out providerInstance);
|
|
|
|
foreach (string providerPath in providerPaths)
|
|
{
|
|
result = ItemExists(providerInstance, providerPath, context);
|
|
if (result)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch (ItemNotFoundException)
|
|
{
|
|
result = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the item at the specified path exists.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The provider instance to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the test-path cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
internal object ItemExistsDynamicParameters(string path, CmdletProviderContext context)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(path));
|
|
}
|
|
|
|
ProviderInfo provider = null;
|
|
CmdletProvider providerInstance = null;
|
|
|
|
CmdletProviderContext newContext =
|
|
new CmdletProviderContext(context);
|
|
newContext.SetFilters(
|
|
new Collection<string>(),
|
|
new Collection<string>(),
|
|
null);
|
|
|
|
Collection<string> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the test-path cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="providerInstance">
|
|
/// The instance of the provider to use.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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;
|
|
}
|
|
|
|
#endregion Exists
|
|
|
|
#region IsValidPath
|
|
|
|
/// <summary>
|
|
/// Determines if the MSH path is a syntactically and semantically valid path for the provider.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to validate.
|
|
/// </param>
|
|
/// <returns>
|
|
/// true if the object specified by path is syntactically and semantically valid, false otherwise.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal bool IsValidPath(string path)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(path));
|
|
}
|
|
|
|
CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);
|
|
|
|
bool result = IsValidPath(path, context);
|
|
|
|
context.ThrowFirstErrorOrDoNothing();
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the MSH path is a syntactically and semantically valid path for the provider.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to validate.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// true if the object specified by path is syntactically and semantically valid, false otherwise.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal bool IsValidPath(
|
|
string path,
|
|
CmdletProviderContext context)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the specified path is valid.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The provider instance to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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;
|
|
}
|
|
|
|
#endregion IsValidPath
|
|
|
|
#region IsItemContainer
|
|
|
|
/// <summary>
|
|
/// Determines if the monad virtual namespace path is a container.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the object to determine if it is a container.
|
|
/// </param>
|
|
/// <returns>
|
|
/// true if the object specified by path is a container, false otherwise.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal bool IsItemContainer(string path)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(path));
|
|
}
|
|
|
|
CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);
|
|
|
|
bool result = IsItemContainer(path, context);
|
|
|
|
context.ThrowFirstErrorOrDoNothing();
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the monad virtual namespace path is a container.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the object to determine if it is a container.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// true if the object specified by path is a container, false otherwise.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal bool IsItemContainer(
|
|
string path,
|
|
CmdletProviderContext context)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(path));
|
|
}
|
|
|
|
ProviderInfo provider = null;
|
|
CmdletProvider providerInstance = null;
|
|
|
|
bool result = false;
|
|
try
|
|
{
|
|
Collection<string> providerPaths =
|
|
Globber.GetGlobbedProviderPathsFromMonadPath(
|
|
path,
|
|
true,
|
|
context,
|
|
out provider,
|
|
out providerInstance);
|
|
|
|
foreach (string providerPath in providerPaths)
|
|
{
|
|
result = IsItemContainer(providerInstance, providerPath, context);
|
|
if (!result)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch (ItemNotFoundException)
|
|
{
|
|
result = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the item at the specified path is a container.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The provider instance to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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;
|
|
}
|
|
|
|
#endregion IsItemContainer
|
|
|
|
#region RemoveItem
|
|
|
|
/// <summary>
|
|
/// Deletes the specified object.
|
|
/// </summary>
|
|
/// <param name="paths">
|
|
/// A relative or absolute path to the object to be deleted.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// The delete should occur in all sub-containers of the specified path.
|
|
/// </param>
|
|
/// <param name="force">
|
|
/// Passed on to providers to force operations.
|
|
/// </param>
|
|
/// <param name="literalPath">
|
|
/// If true, globbing is not done on paths.
|
|
/// </param>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal void RemoveItem(string[] paths, bool recurse, bool force, bool literalPath)
|
|
{
|
|
if (paths == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(paths));
|
|
}
|
|
|
|
CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);
|
|
context.Force = force;
|
|
context.SuppressWildcardExpansion = literalPath;
|
|
|
|
RemoveItem(paths, recurse, context);
|
|
|
|
context.ThrowFirstErrorOrDoNothing();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes the specified object.
|
|
/// </summary>
|
|
/// <param name="paths">
|
|
/// A relative or absolute path to the object to be deleted.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// The delete should occur in all sub-containers of the specified path.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
internal void RemoveItem(
|
|
string[] paths,
|
|
bool recurse,
|
|
CmdletProviderContext context)
|
|
{
|
|
if (paths == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(paths));
|
|
}
|
|
|
|
foreach (string path in paths)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(paths));
|
|
}
|
|
|
|
ProviderInfo provider = null;
|
|
CmdletProvider providerInstance = null;
|
|
|
|
Collection<string> providerPaths =
|
|
Globber.GetGlobbedProviderPathsFromMonadPath(
|
|
path,
|
|
false,
|
|
context,
|
|
out provider,
|
|
out providerInstance);
|
|
|
|
foreach (string providerPath in providerPaths)
|
|
{
|
|
RemoveItem(providerInstance, providerPath, recurse, context);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Internal remove item method that just calls the provider directly without globbing.
|
|
/// </summary>
|
|
/// <param name="providerId">
|
|
/// The name of the provider to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path of the item to remove.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// True if all items should be removed recursively.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context under which the command is running.
|
|
/// </param>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Internal remove item method that just calls the provider directly without globbing.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The instance of the provider to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path of the item to remove.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// True if all items should be removed recursively.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context under which the command is running.
|
|
/// </param>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the remove-item cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// The delete should occur in all sub-containers of the specified path.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
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<string>(),
|
|
new Collection<string>(),
|
|
null);
|
|
|
|
Collection<string> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the remove-item cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// If true, all items in the subtree should be removed.
|
|
/// </param>
|
|
/// <param name="providerInstance">
|
|
/// The instance of the provider to use.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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;
|
|
}
|
|
|
|
#endregion RemoveItem
|
|
|
|
#region GetChildItems
|
|
|
|
/// <summary>
|
|
/// Gets the children of the specified item.
|
|
/// </summary>
|
|
/// <param name="paths">
|
|
/// An array of relative or absolute paths to the object to get the children of.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// 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.
|
|
/// </param>
|
|
/// <param name="depth">
|
|
/// Limits the depth of recursion; uint.MaxValue performs full recursion.
|
|
/// </param>
|
|
/// <param name="force">
|
|
/// Passed on to providers to force operations.
|
|
/// </param>
|
|
/// <param name="literalPath">
|
|
/// If true, globbing is not done on paths.
|
|
/// </param>
|
|
/// <remarks>
|
|
/// This method does not provider streaming of the results. If you want streaming
|
|
/// then you must call the overload that takes a CmdletProviderContext.
|
|
/// </remarks>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> or <paramref name="context"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal Collection<PSObject> GetChildItems(string[] paths, bool recurse, uint depth, bool force, bool literalPath)
|
|
{
|
|
if (paths == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(paths));
|
|
}
|
|
|
|
CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);
|
|
context.Force = force;
|
|
context.SuppressWildcardExpansion = literalPath;
|
|
|
|
foreach (string path in paths)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(paths));
|
|
}
|
|
|
|
GetChildItems(path, recurse, depth, context);
|
|
}
|
|
|
|
context.ThrowFirstErrorOrDoNothing();
|
|
|
|
return context.GetAccumulatedObjects();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the children of the specified item.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// A relative or absolute path to the object to get the children of.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// 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.
|
|
/// </param>
|
|
/// <param name="depth">
|
|
/// Limits the depth of recursion; uint.MaxValue performs full recursion.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> or <paramref name="context"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
internal void GetChildItems(
|
|
string path,
|
|
bool recurse,
|
|
uint depth,
|
|
CmdletProviderContext context)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(path));
|
|
}
|
|
|
|
if (context == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(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<string> include = context.Include;
|
|
Collection<string> exclude = context.Exclude;
|
|
string filter = context.Filter;
|
|
|
|
if (recurse)
|
|
{
|
|
context.SetFilters(
|
|
new Collection<string>(),
|
|
new Collection<string>(),
|
|
null);
|
|
}
|
|
|
|
CmdletProvider providerInstance = null;
|
|
Collection<string> 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(
|
|
context.SuppressWildcardExpansion ? path : WildcardPattern.Unescape(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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the child items of the item at the specified path.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The provider instance to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// If true, all the child items in the subtree are returned.
|
|
/// </param>
|
|
/// <param name="depth">
|
|
/// Limits the depth of recursion; uint.MaxValue performs full recursion.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the item at the specified path is a container.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The provider instance to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The instance of the provider to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item to get the children from.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// Recurse into sub-containers when getting children.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context under which the command is running.
|
|
/// </param>
|
|
/// <param name="childrenNotMatchingFilterCriteria">
|
|
/// The count of items that do not match any include/exclude criteria.
|
|
/// </param>
|
|
/// <param name="processMode">Indicates if this is a Enumerate/Remove operation.</param>
|
|
/// <param name="skipIsItemContainerCheck">A hint used to skip IsItemContainer checks.</param>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The instance of the provider to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item to get the children from.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// Recurse into sub-containers when getting children.
|
|
/// </param>
|
|
/// <param name="depth">
|
|
/// Limits the depth of recursion; uint.MaxValue performs full recursion.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context under which the command is running.
|
|
/// </param>
|
|
/// <param name="childrenNotMatchingFilterCriteria">
|
|
/// The count of items that do not match any include/exclude criteria.
|
|
/// </param>
|
|
/// <param name="processMode">Indicates if this is a Enumerate/Remove operation.</param>
|
|
/// <param name="skipIsItemContainerCheck">A hint used to skip IsItemContainer checks.</param>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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<WildcardPattern> includeMatcher =
|
|
SessionStateUtilities.CreateWildcardsFromStrings(
|
|
context.Include,
|
|
WildcardOptions.IgnoreCase);
|
|
|
|
// Construct the exclude filter
|
|
|
|
Collection<WildcardPattern> 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<PSObject> childNameObjects = null;
|
|
System.Collections.Generic.Dictionary<string, bool> 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<string, bool>();
|
|
|
|
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;
|
|
}
|
|
|
|
if (!(childNameObjects[index].BaseObject is string childName))
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the get-childitem cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// The delete should occur in all sub-containers of the specified path.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
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<string>(),
|
|
new Collection<string>(),
|
|
null);
|
|
|
|
Collection<string> 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;
|
|
}
|
|
|
|
// Detect if the GetChildItemDynamicParameters has been overridden.
|
|
private static bool HasGetChildItemDynamicParameters(ProviderInfo providerInfo)
|
|
{
|
|
Type providerType = providerInfo.ImplementingType;
|
|
|
|
MethodInfo mi = null;
|
|
|
|
do
|
|
{
|
|
mi = providerType.GetMethod("GetChildItemsDynamicParameters",
|
|
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
|
|
providerType = providerType.BaseType;
|
|
} while (
|
|
(mi == null) &&
|
|
(providerType != null) &&
|
|
(providerType != typeof(ContainerCmdletProvider))
|
|
);
|
|
|
|
return (mi != null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the get-childitem cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// If true, all child items in the subtree should be returned.
|
|
/// </param>
|
|
/// <param name="providerInstance">
|
|
/// The instance of the provider to use.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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;
|
|
}
|
|
|
|
#endregion GetChildItems
|
|
|
|
#region GetChildNames
|
|
|
|
/// <summary>
|
|
/// Gets names of the children of the specified path.
|
|
/// </summary>
|
|
/// <param name="paths">
|
|
/// The paths to the items from which to retrieve the child names.
|
|
/// </param>
|
|
/// <param name="returnContainers">
|
|
/// Determines if all containers should be returned or only those containers that match the
|
|
/// filter(s).
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// 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.
|
|
/// </param>
|
|
/// <param name="depth">
|
|
/// Limits the depth of recursion; uint.MaxValue performs full recursion.
|
|
/// </param>
|
|
/// <param name="force">
|
|
/// Passed on to providers to force operations.
|
|
/// </param>
|
|
/// <param name="literalPath">
|
|
/// If true, globbing is not done on paths.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An array of strings that contains the names of the children of the specified
|
|
/// container.
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// 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.
|
|
/// </remarks>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> or <paramref name="propertyToClear"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal Collection<string> GetChildNames(
|
|
string[] paths,
|
|
ReturnContainers returnContainers,
|
|
bool recurse,
|
|
uint depth,
|
|
bool force,
|
|
bool literalPath)
|
|
{
|
|
if (paths == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(paths));
|
|
}
|
|
|
|
CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);
|
|
context.Force = force;
|
|
context.SuppressWildcardExpansion = literalPath;
|
|
|
|
foreach (string path in paths)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(paths));
|
|
}
|
|
|
|
GetChildNames(path, returnContainers, recurse, depth, context);
|
|
}
|
|
|
|
context.ThrowFirstErrorOrDoNothing();
|
|
|
|
Collection<PSObject> objectResults = context.GetAccumulatedObjects();
|
|
|
|
Collection<string> results = new Collection<string>();
|
|
|
|
foreach (PSObject resultObject in objectResults)
|
|
{
|
|
results.Add(resultObject.BaseObject as string);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets names of the children of the specified path.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item from which to retrieve the child names.
|
|
/// </param>
|
|
/// <param name="returnContainers">
|
|
/// Determines if all containers should be returned or only those containers that match the
|
|
/// filter(s).
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// 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.
|
|
/// </param>
|
|
/// <param name="depth">
|
|
/// Limits the depth of recursion; uint.MaxValue performs full recursion.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Nothing is returned, but all names should be written to the context object.
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// 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.
|
|
/// </remarks>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> or <paramref name="propertyToClear"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
internal void GetChildNames(
|
|
string path,
|
|
ReturnContainers returnContainers,
|
|
bool recurse,
|
|
uint depth,
|
|
CmdletProviderContext context)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(path));
|
|
}
|
|
|
|
// Construct the include filter
|
|
|
|
Collection<WildcardPattern> includeMatcher =
|
|
SessionStateUtilities.CreateWildcardsFromStrings(
|
|
context.Include,
|
|
WildcardOptions.IgnoreCase);
|
|
|
|
// Construct the exclude filter
|
|
|
|
Collection<WildcardPattern> 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<string>(),
|
|
new Collection<string>(),
|
|
null);
|
|
|
|
// Resolve the path
|
|
|
|
Collection<string> 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(
|
|
context.SuppressWildcardExpansion ? path : WildcardPattern.Unescape(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The provider instance to use.
|
|
/// </param>
|
|
/// <param name="providerPath">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="relativePath">
|
|
/// The path the name is relative to.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// If true all names in the subtree should be returned.
|
|
/// </param>
|
|
/// <param name="depth">
|
|
/// Current depth of recursion; special case uint.MaxValue performs full recursion.
|
|
/// </param>
|
|
/// <param name="returnContainers">
|
|
/// Determines if all containers should be returned or only those containers that match the
|
|
/// filter(s).
|
|
/// </param>
|
|
/// <param name="includeMatcher">
|
|
/// A set of filters that the names must match to be returned.
|
|
/// </param>
|
|
/// <param name="excludeMatcher">
|
|
/// A set of filters that the names cannot match to be returned.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
private void DoGetChildNamesManually(
|
|
CmdletProvider providerInstance,
|
|
string providerPath,
|
|
string relativePath,
|
|
ReturnContainers returnContainers,
|
|
Collection<WildcardPattern> includeMatcher,
|
|
Collection<WildcardPattern> 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<PSObject> results = childNamesContext.GetAccumulatedObjects();
|
|
|
|
foreach (PSObject result in results)
|
|
{
|
|
// Making sure to obey the StopProcessing.
|
|
if (context.Stopping)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!(result.BaseObject is string name))
|
|
{
|
|
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;
|
|
}
|
|
|
|
if (!(result.BaseObject is string name))
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
childNamesContext.RemoveStopReferral();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the names of the children of the item at the specified path.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The provider instance to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="returnContainers">
|
|
/// Determines if all containers should be returned or only those containers that match the
|
|
/// filter(s).
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the get-childitem -name cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
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<string>(),
|
|
new Collection<string>(),
|
|
null);
|
|
|
|
Collection<string> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the get-childitem -names cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="providerInstance">
|
|
/// The instance of the provider to use.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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;
|
|
}
|
|
|
|
#endregion GetChildNames
|
|
|
|
#region RenameItem
|
|
|
|
/// <summary>
|
|
/// Renames the item at the specified path to the new name provided.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item to rename.
|
|
/// </param>
|
|
/// <param name="newName">
|
|
/// The name to which the item should be renamed. This name should always be
|
|
/// relative to the parent container.
|
|
/// </param>
|
|
/// <param name="force">
|
|
/// Passed on to providers to force operations.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The item that was renamed at the specified path.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> or <paramref name="propertyToClear"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal Collection<PSObject> RenameItem(string path, string newName, bool force)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renames the item at the specified path to the new name provided.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item to rename.
|
|
/// </param>
|
|
/// <param name="newName">
|
|
/// The name to which the item should be renamed. This name should always be
|
|
/// relative to the parent container.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Nothing. All items that are renamed are written into the context object.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> or <paramref name="propertyToClear"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
internal void RenameItem(
|
|
string path,
|
|
string newName,
|
|
CmdletProviderContext context)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(path));
|
|
}
|
|
|
|
ProviderInfo provider = null;
|
|
CmdletProvider providerInstance = null;
|
|
|
|
Collection<string> 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(
|
|
nameof(path),
|
|
SessionStateStrings.RenameMultipleItemError);
|
|
|
|
context.WriteError(
|
|
new ErrorRecord(
|
|
argException,
|
|
"RenameMultipleItemError",
|
|
ErrorCategory.InvalidArgument,
|
|
providerPaths));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renames the item at the specified path.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The provider instance to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="newName">
|
|
/// The new name of the item.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the rename-item cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="newName">
|
|
/// The name to which the item should be renamed. This name should always be
|
|
/// relative to the parent container.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
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<string>(),
|
|
new Collection<string>(),
|
|
null);
|
|
|
|
Collection<string> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the rename-item cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="newName">
|
|
/// The new name of the item.
|
|
/// </param>
|
|
/// <param name="providerInstance">
|
|
/// The instance of the provider to use.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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;
|
|
}
|
|
|
|
#endregion RenameItem
|
|
|
|
#region NewItem
|
|
|
|
/// <summary>
|
|
/// Creates a new item at the specified path.
|
|
/// </summary>
|
|
/// <param name="paths">
|
|
/// The path(s) to the container(s) to create the item in.
|
|
/// </param>
|
|
/// <param name="name">
|
|
/// The name of the item to create.
|
|
/// </param>
|
|
/// <param name="type">
|
|
/// The provider specific type of the object to be created.
|
|
/// </param>
|
|
/// <param name="content">
|
|
/// The content of the new item to create.
|
|
/// </param>
|
|
/// <param name="force">
|
|
/// Passed on to providers to force operations.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The item(s) that was created.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> or <paramref name="propertyToClear"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal Collection<PSObject> NewItem(string[] paths, string name, string type, object content, bool force)
|
|
{
|
|
if (paths == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new item at the specified path.
|
|
/// </summary>
|
|
/// <param name="paths">
|
|
/// The path(s) to the item(s) to create.
|
|
/// </param>
|
|
/// <param name="name">
|
|
/// The name of the item to create.
|
|
/// </param>
|
|
/// <param name="type">
|
|
/// The provider specific type of the item to be created.
|
|
/// </param>
|
|
/// <param name="content">
|
|
/// The content to create the new item with.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Nothing. The item created is written to the context object.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> or <paramref name="propertyToClear"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
internal void NewItem(
|
|
string[] paths,
|
|
string name,
|
|
string type,
|
|
object content,
|
|
CmdletProviderContext context)
|
|
{
|
|
if (paths == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(paths));
|
|
}
|
|
|
|
foreach (string path in paths)
|
|
{
|
|
string resolvePath = null;
|
|
if (path == null)
|
|
{
|
|
PSTraceSource.NewArgumentNullException(nameof(paths));
|
|
}
|
|
else if (path.EndsWith((":" + Path.DirectorySeparatorChar), StringComparison.Ordinal) ||
|
|
path.EndsWith((":" + Path.AltDirectorySeparatorChar), StringComparison.Ordinal))
|
|
{
|
|
// path is Windows root
|
|
resolvePath = path;
|
|
}
|
|
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<string> providerPaths = new Collection<string>();
|
|
|
|
// 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)
|
|
{
|
|
string targetPath;
|
|
|
|
if (content is null || string.IsNullOrEmpty(targetPath = content.ToString()))
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(content), SessionStateStrings.NewLinkTargetNotSpecified, path);
|
|
}
|
|
|
|
ProviderInfo targetProvider = null;
|
|
CmdletProvider targetProviderInstance = null;
|
|
|
|
var globbedTarget = Globber.GetGlobbedProviderPathsFromMonadPath(
|
|
targetPath,
|
|
allowNonexistingPath,
|
|
context,
|
|
out targetProvider,
|
|
out targetProviderInstance);
|
|
|
|
if (!string.Equals(targetProvider.Name, "filesystem", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
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);
|
|
}
|
|
|
|
// If the original target was a relative path, we want to leave it as relative if it did not require
|
|
// globbing to resolve.
|
|
if (WildcardPattern.ContainsWildcardCharacters(targetPath))
|
|
{
|
|
content = globbedTarget[0];
|
|
}
|
|
}
|
|
|
|
NewItemPrivate(providerInstance, composedPath, type, content, context);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new item at the specified path.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The provider instance to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="type">
|
|
/// The type of the item to create.
|
|
/// </param>
|
|
/// <param name="content">
|
|
/// The content of the item to create.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the new-item cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="type">
|
|
/// The provider specific type of the item to be created.
|
|
/// </param>
|
|
/// <param name="newItemValue">
|
|
/// The content to create the new item with.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
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<string>(),
|
|
new Collection<string>(),
|
|
null);
|
|
|
|
Collection<string> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the new-item cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="type">
|
|
/// The type of the new item.
|
|
/// </param>
|
|
/// <param name="newItemValue">
|
|
/// The value of the new item
|
|
/// </param>
|
|
/// <param name="providerInstance">
|
|
/// The instance of the provider to use.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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;
|
|
}
|
|
|
|
#endregion NewItem
|
|
|
|
#region HasChildItems
|
|
|
|
/// <summary>
|
|
/// Determines if the item at the specified path has children.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item to see if it has children.
|
|
/// </param>
|
|
/// <param name="force">
|
|
/// Passed on to providers to force operations.
|
|
/// </param>
|
|
/// <param name="literalPath">
|
|
/// If true, globbing is not done on paths.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the item has children, false otherwise.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal bool HasChildItems(string path, bool force, bool literalPath)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(path));
|
|
}
|
|
|
|
CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);
|
|
context.Force = force;
|
|
context.SuppressWildcardExpansion = literalPath;
|
|
|
|
bool result = HasChildItems(path, context);
|
|
|
|
context.ThrowFirstErrorOrDoNothing();
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the item at the specified path has children.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item to see if it has children.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the item has children, false otherwise.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
internal bool HasChildItems(
|
|
string path,
|
|
CmdletProviderContext context)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(path));
|
|
}
|
|
|
|
ProviderInfo provider = null;
|
|
|
|
CmdletProvider providerInstance = null;
|
|
Collection<string> 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)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the item at the specified path has children.
|
|
/// </summary>
|
|
/// <param name="providerId">
|
|
/// The provider to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal bool HasChildItems(
|
|
string providerId,
|
|
string path)
|
|
{
|
|
bool result = false;
|
|
|
|
if (string.IsNullOrEmpty(providerId))
|
|
{
|
|
throw PSTraceSource.NewArgumentException(nameof(providerId));
|
|
}
|
|
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(path));
|
|
}
|
|
|
|
CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);
|
|
|
|
result = HasChildItems(providerId, path, context);
|
|
context.ThrowFirstErrorOrDoNothing();
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the item at the specified path has children.
|
|
/// </summary>
|
|
/// <param name="providerId">
|
|
/// The provider to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context under which the command is running.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal bool HasChildItems(
|
|
string providerId,
|
|
string path,
|
|
CmdletProviderContext context)
|
|
{
|
|
ContainerCmdletProvider providerInstance = GetContainerProviderInstance(providerId);
|
|
|
|
return HasChildItems(providerInstance, path, context);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the item at the specified path has children.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The provider instance to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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;
|
|
}
|
|
|
|
#endregion HasChildItems
|
|
|
|
#region CopyItem
|
|
|
|
/// <summary>
|
|
/// Copies an item at the specified path to an item at the <paramref name="copyPath"/>.
|
|
/// </summary>
|
|
/// <param name="paths">
|
|
/// The path(s) of the item(s) to copy.
|
|
/// </param>
|
|
/// <param name="copyPath">
|
|
/// The path of the item to copy to.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// Tells the provider to recurse sub-containers when copying.
|
|
/// </param>
|
|
/// <param name="copyContainers">
|
|
/// Determines how the source container is used in the copy operation.
|
|
/// </param>
|
|
/// <param name="force">
|
|
/// Passed on to providers to force operations.
|
|
/// </param>
|
|
/// <param name="literalPath">
|
|
/// If true, globbing is not done on paths.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The objects that were copied.
|
|
/// </returns>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
internal Collection<PSObject> CopyItem(string[] paths,
|
|
string copyPath,
|
|
bool recurse,
|
|
CopyContainers copyContainers,
|
|
bool force,
|
|
bool literalPath)
|
|
{
|
|
if (paths == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies an item at the specified path to an item at the <paramref name="copyPath"/>.
|
|
/// </summary>
|
|
/// <param name="paths">
|
|
/// The path(s) of the item(s) to copy.
|
|
/// </param>
|
|
/// <param name="copyPath">
|
|
/// The path of the item to copy to.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// Tells the provider to recurse sub-containers when copying.
|
|
/// </param>
|
|
/// <param name="copyContainers">
|
|
/// Determines how the source container is used in the copy operation.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="ArgumentNullException">
|
|
/// If <paramref name="path"/> is null.
|
|
/// </exception>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
internal void CopyItem(
|
|
string[] paths,
|
|
string copyPath,
|
|
bool recurse,
|
|
CopyContainers copyContainers,
|
|
CmdletProviderContext context)
|
|
{
|
|
if (paths == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(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(nameof(paths));
|
|
}
|
|
|
|
Collection<string> 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<string>();
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies the specified item(s) to the specified destination.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The provider instance to use.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="copyPath">
|
|
/// The path to copy the item(s) to.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// If true all sub-containers and their children should be copied.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recursively copies many items to a single container.
|
|
/// </summary>
|
|
/// <param name="providerInstance">
|
|
/// The provider instance to use.
|
|
/// </param>
|
|
/// <param name="sourcePath">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="destinationPath">
|
|
/// The path to copy the item(s) to.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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<string> 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the copy-item cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="destination">
|
|
/// The path of the item to copy to.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// Tells the provider to recurse sub-containers when copying.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="ProviderNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a provider that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="DriveNotFoundException">
|
|
/// If the <paramref name="path"/> refers to a drive that could not be found.
|
|
/// </exception>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the provider that the <paramref name="path"/> refers to does
|
|
/// not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </exception>
|
|
/// <exception cref="ItemNotFoundException">
|
|
/// If <paramref name="path"/> does not contain glob characters and
|
|
/// could not be found.
|
|
/// </exception>
|
|
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<string>(),
|
|
new Collection<string>(),
|
|
null);
|
|
|
|
string providerPath = null;
|
|
bool pathNotFoundOnClient = false;
|
|
try
|
|
{
|
|
Collection<string> 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 (providerInstance != null)
|
|
{
|
|
// Get the dynamic parameters for the first resolved path
|
|
return CopyItemDynamicParameters(providerInstance, providerPath, destination, recurse, newContext);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the copy-item cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item if it was specified on the command line.
|
|
/// </param>
|
|
/// <param name="destination">
|
|
/// The path to copy the item to.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// If true, subcontainers and their children should be copied.
|
|
/// </param>
|
|
/// <param name="providerInstance">
|
|
/// The instance of the provider to use.
|
|
/// </param>
|
|
/// <param name="context">
|
|
/// The context which the core command is running.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
/// <exception cref="NotSupportedException">
|
|
/// If the <paramref name="providerInstance"/> does not support this operation.
|
|
/// </exception>
|
|
/// <exception cref="PipelineStoppedException">
|
|
/// If the pipeline is being stopped while executing the command.
|
|
/// </exception>
|
|
/// <exception cref="ProviderInvocationException">
|
|
/// If the provider threw an exception.
|
|
/// </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(
|
|
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;
|
|
}
|
|
|
|
// This function validates a remote path, and if it exists, it returns the root path.
|
|
//
|
|
private static 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))
|
|
{
|
|
ps.Runspace = session.Runspace;
|
|
ps.AddCommand("Get-Command").AddArgument(CopyFileRemoteUtils.PSCopyRemoteUtilsName);
|
|
var result = ps.Invoke<bool>();
|
|
|
|
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(CopyFileRemoteUtils.PSCopyRemoteUtilsName);
|
|
}
|
|
else
|
|
{
|
|
ps.AddScript(CopyFileRemoteUtils.PSValidatePathDefinition);
|
|
}
|
|
|
|
ps.AddParameter("pathToValidate", path);
|
|
|
|
if (sourceIsRemote)
|
|
{
|
|
ps.AddParameter(nameof(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 static 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
|
|
}
|
|
|
|
/// <summary>
|
|
/// Defines the action to be taken for Navigation cmdlets.
|
|
/// </summary>
|
|
internal enum ProcessMode
|
|
{
|
|
/// <summary>
|
|
/// Write out the details.
|
|
/// </summary>
|
|
Enumerate = 1,
|
|
|
|
/// <summary>
|
|
/// Delete the item.
|
|
/// </summary>
|
|
Delete = 2
|
|
}
|
|
}
|
|
|
|
#pragma warning restore 56500
|