/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Management.Automation.Provider; using Dbg=System.Management.Automation; using System.Globalization; #pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings #pragma warning disable 56500 namespace System.Management.Automation { /// /// Holds the state of a Monad Shell session /// internal sealed partial class SessionStateInternal { /// /// The currently active drive. It determines the current working directory. /// private PSDriveInfo currentDrive; #region NewDrive /// /// Adds the specified drive to the current scope. /// /// /// /// The drive to be added to the current scope. /// /// /// /// The ID for the scope to add the drive to. The scope ID can be any of the /// "special" scope identifiers like "global", "local", or "private" or it /// can be a numeric identifier that is a count of the number of parent /// scopes up from the current scope to put the drive in. /// If this parameter is null or empty the drive will be placed in the /// current scope. /// /// /// /// The drive that was added, if any. /// /// /// /// If is null. /// /// /// /// If the drive already exists, /// or /// If .Name contains one or more invalid characters; ~ / \\ . : /// /// /// /// If the provider is not a DriveCmdletProvider. /// /// /// /// The provider for the could not be found. /// /// /// /// If the provider threw an exception or returned null. /// /// /// /// If creating the drive will overflow the MaximumDriveCount limit. /// /// internal PSDriveInfo NewDrive(PSDriveInfo drive, string scopeID) { if (drive == null) { throw PSTraceSource.NewArgumentNullException("drive"); } PSDriveInfo result = null; // Construct a CmdletProviderContext and call the override CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); NewDrive(drive, scopeID, context); context.ThrowFirstErrorOrDoNothing(); Collection successObjects = context.GetAccumulatedObjects(); if (successObjects != null && successObjects.Count > 0) { Dbg.Diagnostics.Assert( successObjects.Count == 1, "NewDrive should only add one PSDriveInfo object to the pipeline"); // set the return value to the first drive (should only be one). if (!successObjects[0].immediateBaseObjectIsEmpty) { result = (PSDriveInfo)successObjects[0].BaseObject; } } return result; } // NewDrive /// /// Adds a drive to the PowerShell namespace. /// /// /// /// The new drive to be added. /// /// /// /// The ID for the scope to add the drive to. The scope ID can be any of the /// "special" scope identifiers like "global", "local", or "private" or it /// can be a numeric identifier that is a count of the number of parent /// scopes up from the current scope to put the drive in. /// If this parameter is null or empty the drive will be placed in the /// current scope. /// /// /// /// The context which the core command is running. /// /// /// /// If or is null. /// /// /// /// If the drive already exists /// or /// If .Name contains one or more invalid characters; ~ / \\ . : /// /// /// /// If is less than zero or greater than the number of currently /// active scopes. /// /// /// /// If the provider is not a DriveCmdletProvider. /// /// /// /// The provider for the could not be found. /// /// /// /// If the provider threw an exception or returned null. /// /// /// /// If creating the drive will overflow the MaximumDriveCount limit. /// /// internal void NewDrive(PSDriveInfo drive, string scopeID, CmdletProviderContext context) { if (drive == null) { throw PSTraceSource.NewArgumentNullException("drive"); } if (context == null) { throw PSTraceSource.NewArgumentNullException("context"); } if (!IsValidDriveName(drive.Name)) { ArgumentException e = PSTraceSource.NewArgumentException( "drive.Name", SessionStateStrings.DriveNameIllegalCharacters); throw e; } // Allow the provider a chance to approve the drive and set // provider specific data PSDriveInfo result = ValidateDriveWithProvider(drive, context, true); // We assume that the provider wrote the error message as they // are suppose to. if (result == null) { return; } if (String.Compare(result.Name, drive.Name, StringComparison.CurrentCultureIgnoreCase) == 0) { // Set the drive in the current scope. try { SessionStateScope scope = currentScope; if (!String.IsNullOrEmpty(scopeID)) { scope = GetScopeByID(scopeID); } scope.NewDrive(result); } catch (ArgumentException argumentException) { // Wrap up the exception and write it to the error stream context.WriteError( new ErrorRecord( argumentException, "NewDriveError", ErrorCategory.InvalidArgument, result)); return; } catch (SessionStateException) { // This should be a pipeline terminating condition throw; } if (ProvidersCurrentWorkingDrive[drive.Provider] == null) { // Set the new drive as the current // drive for the provider since there isn't one set. ProvidersCurrentWorkingDrive[drive.Provider] = drive; } // Upon success, write the drive to the pipeline context.WriteObject(result); } else { ProviderInvocationException e = NewProviderInvocationException( "NewDriveProviderFailed", SessionStateStrings.NewDriveProviderFailed, drive.Provider, drive.Root, PSTraceSource.NewArgumentException("root")); throw e; } } // NewDrive private static bool IsValidDriveName(string name) { bool result = true; do { if (String.IsNullOrEmpty(name)) { result = false; break; } if (name.IndexOfAny( _charactersInvalidInDriveName ) >= 0) { result = false; break; } } while (false); return result; } // IsValidDriveName private static char[] _charactersInvalidInDriveName = new char[] { ':', '/', '\\', '.', '~' }; /// /// Tries to resolve the drive root as an MSH path. If it successfully resolves /// to a single path then the resolved provider internal path is returned. If it /// does not resolve to a single MSH path the root is returned as it was passed. /// /// /// /// The root path of the drive to be resolved. /// /// /// /// The provider that should be used when resolving the path. /// /// /// /// The new root path of the drive. /// /// private string GetProviderRootFromSpecifiedRoot(string root, ProviderInfo provider) { Dbg.Diagnostics.Assert( root != null, "Caller should have verified the root"); Dbg.Diagnostics.Assert( provider != null, "Caller should have verified the provider"); string result = root; SessionState sessionState = new SessionState(_context.TopLevelSessionState); Collection resolvedPaths = null; ProviderInfo resolvedProvider = null; try { // First try to resolve the root as an MSH path resolvedPaths = sessionState.Path.GetResolvedProviderPathFromPSPath(root, out resolvedProvider); // If a single path was resolved... if (resolvedPaths != null && resolvedPaths.Count == 1) { // and the provider used to resolve the path, // matches the one specified by the drive... if (provider.NameEquals(resolvedProvider.FullName)) { // and the item exists ProviderIntrinsics providerIntrinsics = new ProviderIntrinsics(this); if (providerIntrinsics.Item.Exists(root)) { // then use the resolved path as the root of the drive result = resolvedPaths[0]; } } } } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } // If any of the following exceptions are thrown we assume that // the path is a file system path not an MSH path and try // to create the drive with that root. catch (DriveNotFoundException) { } catch (ProviderNotFoundException) { } catch (ItemNotFoundException) { } catch (NotSupportedException) { } catch (InvalidOperationException) { } catch (ProviderInvocationException) { } catch (ArgumentException) { } return result; } // GetProviderRootFromSpecifiedRoot /// /// Gets an object that defines the additional parameters for the NewDrive implementation /// for a provider. /// /// /// /// The provider ID for the drive that is being created. /// /// /// /// The context under which this method is being called. /// /// /// /// An object that has properties and fields decorated with /// parsing attributes similar to a cmdlet class. /// /// /// /// If the is not a DriveCmdletProvider. /// /// /// /// If does not exist. /// /// internal object NewDriveDynamicParameters(string providerId, CmdletProviderContext context) { if (providerId == null) { // If the provider hasn't been specified yet, just return null. // The provider can be specified as pipeline input. return null; } DriveCmdletProvider provider = GetDriveProviderInstance(providerId); object result = null; try { result = provider.NewDriveDynamicParameters(context); } catch (Exception e) // Catch-all OK, 3rd party callout { CommandProcessorBase.CheckForSevereException(e); throw NewProviderInvocationException( "NewDriveDynamicParametersProviderException", SessionStateStrings.NewDriveDynamicParametersProviderException, provider.ProviderInfo, null, e); } return result; } // NewDriveDynamicParameters #endregion NewDrive #region GetDrive /// /// Searches through the session state scopes to find a drive /// /// /// /// The name of a drive to find. /// /// /// /// The drive information if the drive is found. /// /// /// /// If is null. /// /// /// /// If there is no drive with . /// /// internal PSDriveInfo GetDrive(string name) { return GetDrive(name, true); } private PSDriveInfo GetDrive(string name, bool automount) { if (name == null) { throw PSTraceSource.NewArgumentNullException("name"); } PSDriveInfo result = null; // Start searching through the scopes for the drive until the drive // is found or the global scope is reached. SessionStateScopeEnumerator scopeEnumerator = new SessionStateScopeEnumerator(CurrentScope); int scopeID = 0; foreach (SessionStateScope processingScope in scopeEnumerator) { result = processingScope.GetDrive(name); if (result != null) { if (result.IsAutoMounted) { // Validate or remove the auto-mounted drive if (!ValidateOrRemoveAutoMountedDrive(result, processingScope)) { result = null; } } if (result != null) { tracer.WriteLine("Drive found in scope {0}", scopeID); break; } } // Increment the scope ID ++scopeID; } // foreach scope if (result == null && automount) { result = AutomountBuiltInDrive(name); } if (result == null && this == _context.TopLevelSessionState) { result = AutomountFileSystemDrive(name); } if (result == null) { DriveNotFoundException driveNotFound = new DriveNotFoundException( name, "DriveNotFound", SessionStateStrings.DriveNotFound); throw driveNotFound; } return result; } // GetDrive /// /// Searches through the session state scopes looking /// for a drive of the specified name. /// /// /// /// The name of the drive to return. /// /// /// /// The scope ID of the scope to look in for the drive. /// If this parameter is null or empty the drive will be /// found by searching the scopes using the dynamic scoping /// rules. /// /// /// /// The drive for the given name in the given scope or null if /// the drive was not found. /// /// /// /// If is null. /// /// /// /// If is less than zero, or not /// a number and not "script", "global", "local", or "private" /// /// /// /// If is less than zero or greater than the number of currently /// active scopes. /// /// internal PSDriveInfo GetDrive(string name, string scopeID) { if (name == null) { throw PSTraceSource.NewArgumentNullException("name"); } PSDriveInfo result = null; // The scope ID wasn't defined or wasn't recognizable // so do a search through the scopes looking for the // drive. if (String.IsNullOrEmpty(scopeID)) { SessionStateScopeEnumerator scopeEnumerator = new SessionStateScopeEnumerator(CurrentScope); foreach (SessionStateScope scope in scopeEnumerator) { result = scope.GetDrive(name); if (result != null) { if (result.IsAutoMounted) { // Validate or remove the auto-mounted drive if (!ValidateOrRemoveAutoMountedDrive(result, scope)) { result = null; } } if (result != null) { break; } } } if (result == null) { result = AutomountFileSystemDrive(name); } } else { SessionStateScope scope = GetScopeByID(scopeID); result = scope.GetDrive(name); if (result != null) { if (result.IsAutoMounted) { // Validate or remove the auto-mounted drive if (!ValidateOrRemoveAutoMountedDrive(result, scope)) { result = null; } } } else { if (scope == _globalScope) { result = AutomountFileSystemDrive(name); } } } return result; } // GetDrive private PSDriveInfo AutomountFileSystemDrive(string name) { PSDriveInfo result = null; // Check to see if it could be a "auto-mounted" // file system drive. If so, add the new drive // to the global scope and return it if (name.Length == 1) { try { System.IO.DriveInfo driveInfo = new System.IO.DriveInfo(name); result = AutomountFileSystemDrive(driveInfo); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) { CommandProcessorBase.CheckForSevereException(e); // Catch all exceptions and continue since the drive does not exist // This action wasn't requested by the user and as such we don't // want to expose the user to any error conditions other than // DriveNotFoundException which will be thrown by the caller } } return result; } // AutomountFileSystemDrive private PSDriveInfo AutomountFileSystemDrive(System.IO.DriveInfo systemDriveInfo) { PSDriveInfo result = null; if (!IsProviderLoaded(this.ExecutionContext.ProviderNames.FileSystem)) { tracer.WriteLine("The {0} provider is not loaded", this.ExecutionContext.ProviderNames.FileSystem); return result; } // Since the drive does exist, add it. try { // Get the FS provider DriveCmdletProvider driveProvider = GetDriveProviderInstance(this.ExecutionContext.ProviderNames.FileSystem); if (driveProvider != null) { // Create a new drive string systemDriveName = systemDriveInfo.Name.Substring(0, 1); string volumeLabel = String.Empty; string displayRoot = null; try { // When run in an AppContainer, we may not have access to the volume label. volumeLabel = systemDriveInfo.VolumeLabel; } catch (UnauthorizedAccessException) { } // Get the actual root path for Network type drives if (systemDriveInfo.DriveType == DriveType.Network) { try { displayRoot = Microsoft.PowerShell.Commands.FileSystemProvider .GetRootPathForNetworkDriveOrDosDevice(systemDriveInfo); } // We want to get root path of the network drive as extra information to display to the user. // It's okay we failed to get the root path for some reason. We don't want to throw exception // here as it would break the current behavior. catch (Win32Exception) { } catch (InvalidOperationException) { } } PSDriveInfo newPSDriveInfo = new PSDriveInfo( systemDriveName, driveProvider.ProviderInfo, systemDriveInfo.RootDirectory.FullName, volumeLabel, null, displayRoot); newPSDriveInfo.IsAutoMounted = true; CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); newPSDriveInfo.DriveBeingCreated = true; // Validate the drive with the provider result = ValidateDriveWithProvider(driveProvider, newPSDriveInfo, context, false); newPSDriveInfo.DriveBeingCreated = false; if (result != null && !context.HasErrors()) { // Create the drive in the global scope. _globalScope.NewDrive(result); } } } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) { CommandProcessorBase.CheckForSevereException(e); // Since the user isn't expecting this behavior, we don't // want to let errors find their way out. If there are any // failures we just don't mount the drive. MshLog.LogProviderHealthEvent( this.ExecutionContext, this.ExecutionContext.ProviderNames.FileSystem, e, Severity.Warning); } return result; } // AutomountFileSystemDrive /// /// Auto-mounts a built-in drive. /// /// The name of the drive to load /// internal PSDriveInfo AutomountBuiltInDrive(string name) { MountDefaultDrive(name, _context); PSDriveInfo result = GetDrive(name, false); return result; } // AutomountFileSystemDrive internal static void MountDefaultDrive(string name, ExecutionContext context) { PSModuleAutoLoadingPreference moduleAutoLoadingPreference = CommandDiscovery.GetCommandDiscoveryPreference(context, SpecialVariables.PSModuleAutoLoadingPreferenceVarPath, "PSModuleAutoLoadingPreference"); if (moduleAutoLoadingPreference == PSModuleAutoLoadingPreference.None) { return; } string moduleName = null; // Note: For the certificate provider, we actually support the provider name as an alternative to // mount the default drive, since the provider names can be used for provider-qualified paths. // The WSMAN drive is the same as the provider name. if ( String.Equals("Cert", name, StringComparison.OrdinalIgnoreCase) || String.Equals("Certificate", name, StringComparison.OrdinalIgnoreCase) ) { moduleName = "Microsoft.PowerShell.Security"; } else if(String.Equals("WSMan", name, StringComparison.OrdinalIgnoreCase)) { moduleName = "Microsoft.WSMan.Management"; } if (!String.IsNullOrEmpty(moduleName)) { tracer.WriteLine("Auto-mounting built-in drive: {0}", name); CommandInfo commandInfo = new CmdletInfo("Import-Module", typeof(Microsoft.PowerShell.Commands.ImportModuleCommand), null, null, context); Exception exception = null; tracer.WriteLine("Attempting to load module: {0}", moduleName); CommandDiscovery.AutoloadSpecifiedModule(moduleName, context, commandInfo.Visibility, out exception); if (exception != null) { // Call-out to user code, catch-all OK CommandProcessorBase.CheckForSevereException(exception); } } } /// /// Determines if the specified automounted drive still exists. If not, /// the drive is removed. /// /// /// /// The drive to validate or remove. /// /// /// /// The scope the drive is in. This will be used to remove the drive /// if necessary. /// /// /// /// True if the drive is still valid, false if the drive was removed. /// /// private bool ValidateOrRemoveAutoMountedDrive(PSDriveInfo drive, SessionStateScope scope) { bool result = true; try { System.IO.DriveInfo systemDriveInfo = new System.IO.DriveInfo(drive.Name); result = systemDriveInfo.DriveType != DriveType.NoRootDirectory; } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) { CommandProcessorBase.CheckForSevereException(e); // Assume any exception means the drive is no longer valid and needs // to be removed. result = false; } if (!result) { DriveCmdletProvider driveProvider = null; try { driveProvider = GetDriveProviderInstance(this.ExecutionContext.ProviderNames.FileSystem); } catch (NotSupportedException) { } catch (ProviderNotFoundException) { } if (driveProvider != null) { CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); try { // Give the provider a chance to cleanup driveProvider.RemoveDrive(drive, context); } // Ignore any exceptions the provider throws because we // are doing this without an explicit request from the // user. Since the provider can throw any exception // we must catch all exceptions here. catch (Exception e) { CommandProcessorBase.CheckForSevereException(e); } scope.RemoveDrive(drive); } } return result; } // ValidateOrRemoveAutoMountedDrive /// /// If a VHD is mounted to a drive prior to the PowerShell session being launched, /// then such a drive has to be validated for its existance before performing /// any operations on that drive to make sure that the drive is not unmounted. /// /// /// Absence of mounted drive for FileSystem provider or False for other provider types. private bool IsAStaleVhdMountedDrive(PSDriveInfo drive) { bool result = false; // check that drive's provider type is FileSystem if ((drive.Provider != null) && (!drive.Provider.NameEquals(this.ExecutionContext.ProviderNames.FileSystem))) { return false; } // A VHD mounted drive gets detected with a DriveType of DriveType.Fixed // when the VHD is mounted, however if the drive is unmounted, such a // stale drive is no longer valid and gets detected with DriveType.NoRootDirectory. // We would hit this situaltion in the following scenario: // 1. Launch Powershell session 'A' and mount the VHD. // 2. Launch different powershell session 'B'. // 3. Unmount the VHD in session 'A'. // The drive pointing to VHD in session 'B' gets detected as DriveType.NoRootDirectory // after the VHD is removed in session 'A'. if (drive != null && !string.IsNullOrEmpty(drive.Name) && drive.Name.Length == 1) { try { char driveChar = Convert.ToChar(drive.Name, CultureInfo.InvariantCulture); if (Char.ToUpperInvariant(driveChar) >= 'A' && Char.ToUpperInvariant(driveChar) <= 'Z') { DriveInfo systemDriveInfo = new DriveInfo(drive.Name); if (systemDriveInfo.DriveType == DriveType.NoRootDirectory) { try { // Checking for the presence of mounted drive locally using Utils.NativeDirectoryExists API as // the calls to this API is faster than normal Directory.Exist API. bool validDrive = Utils.NativeDirectoryExists(drive.Root); if (!validDrive) { result = true; } } // We don't want to have automounting cause an exception. We // rather it just fail silently as it wasn't a result of an // explicit request by the user anyway. // Following the same pattern as the Calling API. catch (IOException) { } catch (UnauthorizedAccessException) { } } } } catch (ArgumentException) { // At this point, We dont care if the drive is not a valid drive that does not host the VHD. } } return result; } /// /// Gets all the drives for a specific provider /// /// /// /// The identifier for the provider to retrieve the drives for. /// /// /// /// An IEnumerable that contains the drives for the specified provider. /// /// internal Collection GetDrivesForProvider(string providerId) { if (String.IsNullOrEmpty(providerId)) { return Drives(null); } // Ensure that the provider name resolves to a single provider GetSingleProvider(providerId); Collection drives = new Collection(); foreach (PSDriveInfo drive in Drives(null)) { if (drive != null && drive.Provider.NameEquals(providerId)) { drives.Add(drive); } } return drives; } // GetDrivesForProvider #endregion GetDrive #region RemoveDrive /// /// Removes the drive with the specified name. /// /// /// /// The name of the drive to remove. /// /// /// /// Determines whether drive should be forcefully removed even if there was errors. /// /// /// /// The ID of the scope from which to remove the drive. /// If the scope ID is null or empty, the scope hierarchy will be searched /// starting at the current scope through all the parent scopes to the /// global scope until a drive of the given name is found to remove. /// /// internal void RemoveDrive(string driveName, bool force, string scopeID) { if (driveName == null) { throw PSTraceSource.NewArgumentNullException("driveName"); } PSDriveInfo drive = GetDrive(driveName, scopeID); if (drive == null) { DriveNotFoundException e = new DriveNotFoundException( driveName, "DriveNotFound", SessionStateStrings.DriveNotFound); throw e; } RemoveDrive(drive, force, scopeID); } // RemoveDrive /// /// Removes the drive with the specified name. /// /// /// /// The name of the drive to remove. /// /// /// /// Determines whether drive should be forcefully removed even if there was errors. /// /// /// /// The ID of the scope from which to remove the drive. /// If the scope ID is null or empty, the scope hierarchy will be searched /// starting at the current scope through all the parent scopes to the /// global scope until a drive of the given name is found to remove. /// /// /// /// The context of the command. /// /// internal void RemoveDrive( string driveName, bool force, string scopeID, CmdletProviderContext context) { if (driveName == null) { throw PSTraceSource.NewArgumentNullException("driveName"); } Dbg.Diagnostics.Assert( context != null, "The caller should verify the context"); PSDriveInfo drive = GetDrive(driveName, scopeID); if (drive == null) { DriveNotFoundException e = new DriveNotFoundException( driveName, "DriveNotFound", SessionStateStrings.DriveNotFound); context.WriteError(new ErrorRecord(e.ErrorRecord, e)); } else { RemoveDrive(drive, force, scopeID, context); } } // RemoveDrive /// /// Removes the specified drive. /// /// /// /// The drive to be removed. /// /// /// /// Determines whether drive should be forcefully removed even if there was errors. /// /// /// /// The ID of the scope from which to remove the drive. /// If the scope ID is null or empty, the scope hierarchy will be searched /// starting at the current scope through all the parent scopes to the /// global scope until a drive of the given name is found to remove. /// /// internal void RemoveDrive(PSDriveInfo drive, bool force, string scopeID) { if (drive == null) { throw PSTraceSource.NewArgumentNullException("drive"); } CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); RemoveDrive(drive, force, scopeID, context); if (context.HasErrors() && !force) { context.ThrowFirstErrorOrDoNothing(); } } // RemoveDrive /// /// Removes the specified drive. /// /// /// /// The drive to be removed. /// /// /// /// Determines whether drive should be forcefully removed even if there was errors. /// /// /// /// The ID of the scope from which to remove the drive. /// If the scope ID is null or empty, the scope hierarchy will be searched /// starting at the current scope through all the parent scopes to the /// global scope until a drive of the given name is found to remove. /// /// /// /// The context which the core command is running. /// /// /// /// If is less than zero or greater than the number of currently /// active scopes. /// /// internal void RemoveDrive( PSDriveInfo drive, bool force, string scopeID, CmdletProviderContext context) { // Make sure that the CanRemoveDrive is called even if we are forcing // the removal because we want the provider to have a chance to // cleanup. bool canRemove = false; try { canRemove = CanRemoveDrive(drive, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (ProviderInvocationException) { if (!force) { throw; } } // Now remove the drive if there was no error or we are forcing the removal if (canRemove || force) { // The scope ID wasn't defined or wasn't recognizable // so do a search through the scopes looking for the // drive. if (String.IsNullOrEmpty(scopeID)) { SessionStateScopeEnumerator scopeEnumerator = new SessionStateScopeEnumerator(CurrentScope); foreach (SessionStateScope scope in scopeEnumerator) { try { PSDriveInfo result = scope.GetDrive(drive.Name); if (result != null) { scope.RemoveDrive(drive); // If the drive is the current drive for the provider, remove // it from the current drive list. if (ProvidersCurrentWorkingDrive[drive.Provider] == result) { ProvidersCurrentWorkingDrive[drive.Provider] = null; } break; } } catch (ArgumentException) { } } } else { SessionStateScope scope = GetScopeByID(scopeID); scope.RemoveDrive(drive); // If the drive is the current drive for the provider, remove // it from the current drive list. if (ProvidersCurrentWorkingDrive[drive.Provider] == drive) { ProvidersCurrentWorkingDrive[drive.Provider] = null; } } } else { PSInvalidOperationException e = (PSInvalidOperationException) PSTraceSource.NewInvalidOperationException ( SessionStateStrings.DriveRemovalPreventedByProvider, drive.Name, drive.Provider); context.WriteError ( new ErrorRecord ( e.ErrorRecord, e)); } } // RemoveDrive /// /// Determines if the drive can be removed by calling the provider /// for the drive. /// /// /// /// The drive to test for removal. /// /// /// /// The context under which the command is running. /// /// /// /// True if the drive can be removed, false otherwise. /// /// /// /// If or is null. /// /// /// /// If the provider threw an exception when RemoveDrive was called. /// /// private bool CanRemoveDrive(PSDriveInfo drive, CmdletProviderContext context) { if (context == null) { throw PSTraceSource.NewArgumentNullException("context"); } if (drive == null) { throw PSTraceSource.NewArgumentNullException("drive"); } tracer.WriteLine("Drive name = {0}", drive.Name); // First set the drive data context.Drive = drive; // Now see if the provider will let us remove the drive DriveCmdletProvider driveCmdletProvider = GetDriveProviderInstance(drive.Provider); bool driveRemovable = false; PSDriveInfo result = null; try { result = driveCmdletProvider.RemoveDrive(drive, context); } catch (LoopFlowException) { throw; } catch (PipelineStoppedException) { throw; } catch (ActionPreferenceStopException) { throw; } catch (Exception e) // Catch-all OK, 3rd party callout { CommandProcessorBase.CheckForSevereException(e); throw NewProviderInvocationException( "RemoveDriveProviderException", SessionStateStrings.RemoveDriveProviderException, driveCmdletProvider.ProviderInfo, null, e); } if (result != null) { // Make sure the provider didn't try to pull a fast one on us // and substitute a different drive. if (String.Compare(result.Name, drive.Name, StringComparison.CurrentCultureIgnoreCase) == 0) { driveRemovable = true; } } return driveRemovable; } // CanRemoveDrive #endregion RemoveDrive #region Drives /// /// Gets an enumerable list of the drives that are mounted in /// the specified scope. /// /// /// /// The scope to retrieve the drives from. If null or empty, /// all drives from all scopes will be retrieved. /// /// /// /// If is less than zero, or not /// a number and not "script", "global", "local", or "private" /// /// /// /// If is less than zero or greater than the number of currently /// active scopes. /// /// internal Collection Drives(string scope) { Dictionary driveTable = new Dictionary(); SessionStateScope startingScope = this.currentScope; if (!String.IsNullOrEmpty(scope)) { startingScope = GetScopeByID(scope); } SessionStateScopeEnumerator scopeEnumerator = new SessionStateScopeEnumerator(startingScope); DriveInfo[] alldrives = DriveInfo.GetDrives(); Collection driveNames = new Collection(); foreach(DriveInfo drive in alldrives) { driveNames.Add(drive.Name.Substring(0,1)); } foreach (SessionStateScope lookupScope in scopeEnumerator) { foreach (PSDriveInfo drive in lookupScope.Drives) { // It is the correct behavior for child scope // drives to overwrite parent scope drives of // the same name. if (drive != null) { bool driveIsValid = true; // If the drive is auto-mounted, ensure that it still exists, or remove the drive. #if !UNIX if (drive.IsAutoMounted || IsAStaleVhdMountedDrive(drive)) { driveIsValid = ValidateOrRemoveAutoMountedDrive(drive, lookupScope); } #endif if(drive.Name.Length == 1) { if(!(driveNames.Contains(drive.Name))) driveTable.Remove(drive.Name); } if (driveIsValid && !driveTable.ContainsKey(drive.Name)) { driveTable[drive.Name] = drive; } } } // If the scope was specified then don't loop // through the other scopes if (scope != null && scope.Length > 0) { break; } } // foreach scope // Now lookup all the file system drives and automount any that are not // present try { foreach (System.IO.DriveInfo fsDriveInfo in alldrives) { if (fsDriveInfo != null) { string fsDriveName = fsDriveInfo.Name.Substring(0, 1); if (!driveTable.ContainsKey(fsDriveName)) { PSDriveInfo automountedDrive = AutomountFileSystemDrive(fsDriveInfo); if (automountedDrive != null) { driveTable[automountedDrive.Name] = automountedDrive; } } } } } // We don't want to have automounting cause an exception. We // rather it just fail silently as it wasn't a result of an // explicit request by the user anyway. catch (IOException) { } catch (UnauthorizedAccessException) { } Collection results = new Collection(); foreach (PSDriveInfo drive in driveTable.Values) { results.Add(drive); } return results; } // Drives #endregion Drives /// /// Gets or sets the current working drive /// internal PSDriveInfo CurrentDrive { get { if (this != _context.TopLevelSessionState) return _context.TopLevelSessionState.CurrentDrive; else return currentDrive; } // get set { if (this != _context.TopLevelSessionState) _context.TopLevelSessionState.CurrentDrive = value; else currentDrive = value; } // set } // CurrentDrive } // SessionStateInternal class } #pragma warning restore 56500