Merge pull request #1285 from PowerShell/dongbo/miss-files-ops

Add back missing files for OPS
This commit is contained in:
Dongbo Wang
2016-07-06 16:54:16 -07:00
committed by GitHub
37 changed files with 3299 additions and 20 deletions
@@ -1,3 +1,5 @@
#if !LINUX
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
@@ -7384,3 +7386,5 @@ $result
#endregion
}//End namespace
#endif
@@ -0,0 +1,866 @@
#if !LINUX
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// A cmdlet to retrieve time zone information.
/// </summary>
[Cmdlet(VerbsCommon.Get, "TimeZone", DefaultParameterSetName = "Name",
HelpUri = "http://go.microsoft.com/fwlink/?LinkId=799468")]
[Alias("gtz")]
public class GetTimeZoneCommand : PSCmdlet
{
#region Parameters
/// <summary>
/// A list of the local time zone ids that the cmdlet should look up.
/// </summary>
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Id")]
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
public string[] Id { get; set; }
/// <summary>
/// Specifies that the cmdlet should produce a collection of the
/// TimeZoneInfo objects that are available on the system.
/// </summary>
[Parameter(Mandatory = true, ParameterSetName = "ListAvailable")]
public SwitchParameter ListAvailable { get; set; }
/// <summary>
/// A list of the local time zone names that the cmdlet should look up.
/// </summary>
[Parameter(Position = 0, ValueFromPipeline = true, ParameterSetName = "Name")]
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
public string[] Name { get; set; }
#endregion Parameters
/// <summary>
/// Implementation of the ProcessRecord method for Get-TimeZone
/// </summary>
protected override void ProcessRecord()
{
// make sure we've got the latest time zone settings
TimeZoneHelper.ClearCachedData();
if (this.ParameterSetName.Equals("ListAvailable", StringComparison.OrdinalIgnoreCase))
{
// output the list of all available time zones
WriteObject(TimeZoneInfo.GetSystemTimeZones(), true);
}
else if (this.ParameterSetName.Equals("Id", StringComparison.OrdinalIgnoreCase))
{
// lookup each time zone id
foreach (string tzid in Id)
{
try
{
WriteObject(TimeZoneInfo.FindSystemTimeZoneById(tzid));
}
#if CORECLR
// TimeZoneNotFoundException is thrown by TimeZoneInfo, but not
// publicly visible (so can't be caught), so for now we're catching
// the parent exception time. This should be removed once the more
// specific exception is available.
catch (Exception e)
#else
catch (TimeZoneNotFoundException e)
#endif
{
WriteError(new ErrorRecord(e, TimeZoneHelper.TimeZoneNotFoundError,
ErrorCategory.InvalidArgument, "Id"));
}
}
}
else // ParameterSetName == "Name"
{
if (null != Name)
{
// lookup each time zone name (or wildcard pattern)
foreach (string tzname in Name)
{
TimeZoneInfo[] timeZones = TimeZoneHelper.LookupSystemTimeZoneInfoByName(tzname);
if (0 < timeZones.Length)
{
// manually process each object in the array, so if there is only a single
// entry then the returned type is TimeZoneInfo and not TimeZoneInfo[], and
// it can be pipelined to Set-TimeZone more easily
foreach (TimeZoneInfo timeZone in timeZones)
{
WriteObject(timeZone);
}
}
else
{
string message = string.Format(CultureInfo.InvariantCulture,
TimeZoneResources.TimeZoneNameNotFound, tzname);
#if CORECLR
// Because .NET Core does not currently expose the TimeZoneNotFoundException
// we need to throw the more generic parent exception class for the time being.
// This should be removed once the correct exception class is available.
Exception e = new Exception(message);
#else
Exception e = new TimeZoneNotFoundException(message);
#endif
WriteError(new ErrorRecord(e, TimeZoneHelper.TimeZoneNotFoundError,
ErrorCategory.InvalidArgument, "Name"));
}
}
}
else
{
// return the current system local time zone
WriteObject(TimeZoneInfo.Local);
}
}
}
}
/// <summary>
/// A cmdlet to set the system's local time zone.
/// </summary>
[Cmdlet(VerbsCommon.Set, "TimeZone",
SupportsShouldProcess = true,
DefaultParameterSetName = "Name",
HelpUri = "http://go.microsoft.com/fwlink/?LinkId=799469")]
[Alias("stz")]
public class SetTimeZoneCommand : PSCmdlet
{
#region string constants
private const string TimeZoneTarget = "Local System";
#endregion string constants
#region Parameters
/// <summary>
/// The name of the local time zone that the system should use.
/// </summary>
[Parameter(Mandatory = true, ParameterSetName = "Id", ValueFromPipelineByPropertyName = true)]
public string Id { get; set; }
/// <summary>
/// A TimeZoneInfo object identifying the local time zone that the system should use.
/// </summary>
[Parameter(Mandatory = true, Position = 0, ParameterSetName = "InputObject", ValueFromPipeline = true)]
public TimeZoneInfo InputObject { get; set; }
/// <summary>
/// The name of the local time zone that the system should use.
/// </summary>
[Parameter(Mandatory = true, Position = 0, ParameterSetName = "Name")]
public string Name { get; set; }
/// <summary>
/// Request return of the new local time zone as a TimeZoneInfo object
/// </summary>
[Parameter]
public SwitchParameter PassThru { get; set; }
#endregion Parameters
/// <summary>
/// Implementation of the ProcessRecord method for Get-TimeZone
/// </summary>
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "Since Name is not a parameter of this method, it confuses FXCop. It is the appropriate value for the exception.")]
protected override void ProcessRecord()
{
// make sure we've got fresh data, in case the requested time zone was added
// to the system (registry) after our process was started
TimeZoneHelper.ClearCachedData();
// acquire a TimeZoneInfo if one wasn't supplied.
if (this.ParameterSetName.Equals("Id", StringComparison.OrdinalIgnoreCase))
{
try
{
InputObject = TimeZoneInfo.FindSystemTimeZoneById(Id);
}
#if CORECLR
// TimeZoneNotFoundException is thrown by TimeZoneInfo, but not
// publicly visible (so can't be caught), so for now we're catching
// the parent exception time. This should be removed once the more
// specific exception is available.
catch (Exception e)
#else
catch (TimeZoneNotFoundException e)
#endif
{
ThrowTerminatingError(new ErrorRecord(
e,
TimeZoneHelper.TimeZoneNotFoundError,
ErrorCategory.InvalidArgument,
"Id"));
}
}
else if (this.ParameterSetName.Equals("Name", StringComparison.OrdinalIgnoreCase))
{
// lookup the time zone name and make sure we have one (and only one) match
TimeZoneInfo[] timeZones = TimeZoneHelper.LookupSystemTimeZoneInfoByName(Name);
if (0 == timeZones.Length)
{
string message = string.Format(CultureInfo.InvariantCulture,
TimeZoneResources.TimeZoneNameNotFound, Name);
#if CORECLR
// Because .NET Core does not currently expose the TimeZoneNotFoundException
// we need to throw the more generic parent exception class for the time being.
// This should be removed once the correct exception class is available.
Exception e = new Exception(message);
#else
Exception e = new TimeZoneNotFoundException(message);
#endif
ThrowTerminatingError(new ErrorRecord(e,
TimeZoneHelper.TimeZoneNotFoundError,
ErrorCategory.InvalidArgument,
"Name"));
}
else if (1 < timeZones.Length)
{
string message = string.Format(CultureInfo.InvariantCulture,
TimeZoneResources.MultipleMatchingTimeZones, Name);
ThrowTerminatingError(new ErrorRecord(
new PSArgumentException(message, "Name"),
TimeZoneHelper.MultipleMatchingTimeZonesError,
ErrorCategory.InvalidArgument,
"Name"));
}
else
{
InputObject = timeZones[0];
}
}
else // ParameterSetName == "InputObject"
{
try
{
// a TimeZoneInfo object was supplied, so use it to make sure we can find
// a backing system time zone, otherwise it's an error condition
InputObject = TimeZoneInfo.FindSystemTimeZoneById(InputObject.Id);
}
#if CORECLR
// TimeZoneNotFoundException is thrown by TimeZoneInfo, but not
// publicly visible (so can't be caught), so for now we're catching
// the parent exception time. This should be removed once the more
// specific exception is available.
catch (Exception e)
#else
catch (TimeZoneNotFoundException e)
#endif
{
ThrowTerminatingError(new ErrorRecord(
e,
TimeZoneHelper.TimeZoneNotFoundError,
ErrorCategory.InvalidArgument,
"InputObject"));
}
}
if (ShouldProcess(TimeZoneTarget))
{
bool acquireAccess = false;
try
{
// check to see if permission to set the time zone is already enabled for this process
if (!HasAccess)
{
// acquire permissions to set the timezone
SetAccessToken(true);
acquireAccess = true;
}
}
catch (Win32Exception e)
{
ThrowTerminatingError(new ErrorRecord(e,
TimeZoneHelper.InsufficientPermissionsError,
ErrorCategory.PermissionDenied, null));
}
try
{
// construct and populate a new DYNAMIC_TIME_ZONE_INFORMATION structure
NativeMethods.DYNAMIC_TIME_ZONE_INFORMATION dtzi = new NativeMethods.DYNAMIC_TIME_ZONE_INFORMATION();
dtzi.Bias -= (int)InputObject.BaseUtcOffset.TotalMinutes;
dtzi.StandardName = InputObject.StandardName;
dtzi.DaylightName = InputObject.DaylightName;
dtzi.TimeZoneKeyName = InputObject.Id;
// Request time zone transition information for the current year
NativeMethods.TIME_ZONE_INFORMATION tzi = new NativeMethods.TIME_ZONE_INFORMATION();
if (!NativeMethods.GetTimeZoneInformationForYear((ushort)DateTime.Now.Year, ref dtzi, ref tzi))
{
ThrowWin32Error();
}
// copy over the transition times
dtzi.StandardBias = tzi.StandardBias;
dtzi.StandardDate = tzi.StandardDate;
dtzi.DaylightBias = tzi.DaylightBias;
dtzi.DaylightDate = tzi.DaylightDate;
// set the new local time zone for the system
if (!NativeMethods.SetDynamicTimeZoneInformation(ref dtzi))
{
ThrowWin32Error();
}
#if !CORECLR
// broadcast a WM_SETTINGCHANGE notification message to all top-level windows so that they
// know to update their notion of the current system time (and time zone) if applicable
int result = 0;
NativeMethods.SendMessageTimeout((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_SETTINGCHANGE,
(IntPtr)0, "intl", NativeMethods.SMTO_ABORTIFHUNG, 5000, ref result);
#endif
// clear the time zone data or this PowerShell session
// will not recognize the new time zone settings
TimeZoneHelper.ClearCachedData();
if (PassThru.IsPresent)
{
// return the TimeZoneInfo object for the (new) current local time zone
WriteObject(TimeZoneInfo.Local);
}
}
catch (Win32Exception e)
{
ThrowTerminatingError(new ErrorRecord(e,
TimeZoneHelper.SetTimeZoneFailedError,
ErrorCategory.FromStdErr, null));
}
finally
{
if (acquireAccess)
{
// reset the permissions
SetAccessToken(false);
}
}
}
else
{
if (PassThru.IsPresent)
{
// show the user the time zone settings that would have been used.
WriteObject(InputObject);
}
}
}
#region Helper functions
/// <summary>
/// True if the current process has access to change the time zone setting.
/// </summary>
protected bool HasAccess
{
get
{
bool hasAccess = false;
// open the access token for the current process
IntPtr hToken = IntPtr.Zero;
IntPtr hProcess = NativeMethods.GetCurrentProcess();
if (!NativeMethods.OpenProcessToken(hProcess,
NativeMethods.TOKEN_ADJUST_PRIVILEGES | NativeMethods.TOKEN_QUERY, ref hToken))
{
ThrowWin32Error();
}
try
{
// setup the privileges being checked
NativeMethods.PRIVILEGE_SET ps = new NativeMethods.PRIVILEGE_SET()
{
PrivilegeCount = 1,
Control = 1,
Luid = 0,
Attributes = NativeMethods.SE_PRIVILEGE_ENABLED,
};
// lookup the Luid of the SeTimeZonePrivilege
if (!NativeMethods.LookupPrivilegeValue(null, NativeMethods.SE_TIME_ZONE_NAME, ref ps.Luid))
{
ThrowWin32Error();
}
// set the privilege for the open access token
if (!NativeMethods.PrivilegeCheck(hToken, ref ps, ref hasAccess))
{
ThrowWin32Error();
}
}
finally
{
NativeMethods.CloseHandle(hToken);
}
return (hasAccess);
}
}
/// <summary>
/// Set the SeTimeZonePrivilege, which controls access to the SetDynamicTimeZoneInformation API
/// </summary>
/// <param name="enable">Set to true to enable (or false to disable) the privilege.</param>
protected void SetAccessToken(bool enable)
{
// open the access token for the current process
IntPtr hToken = IntPtr.Zero;
IntPtr hProcess = NativeMethods.GetCurrentProcess();
if (!NativeMethods.OpenProcessToken(hProcess,
NativeMethods.TOKEN_ADJUST_PRIVILEGES | NativeMethods.TOKEN_QUERY, ref hToken))
{
ThrowWin32Error();
}
try
{
// setup the privileges being requested
NativeMethods.TOKEN_PRIVILEGES tp = new NativeMethods.TOKEN_PRIVILEGES()
{
PrivilegeCount = 1,
Luid = 0,
Attributes = (enable ? NativeMethods.SE_PRIVILEGE_ENABLED : 0),
};
// lookup the Luid of the SeTimeZonePrivilege
if (!NativeMethods.LookupPrivilegeValue(null, NativeMethods.SE_TIME_ZONE_NAME, ref tp.Luid))
{
ThrowWin32Error();
}
// set the privilege for the open access token
if (!NativeMethods.AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero))
{
ThrowWin32Error();
}
}
finally
{
NativeMethods.CloseHandle(hToken);
}
}
/// <summary>
/// Get the Win32 error code from GetLastError and throw an exception.
/// </summary>
protected void ThrowWin32Error()
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
#endregion Helper functions
#region Win32 interop helper
internal class NativeMethods
{
/// <summary>
/// Private constructor to prevent instantiation
/// </summary>
private NativeMethods()
{
}
#region Native DLL locations
#if CORECLR
private const string SetDynamicTimeZoneApiDllName = "api-ms-win-core-timezone-l1-1-0.dll";
private const string GetTimeZoneInformationForYearApiDllName = "api-ms-win-core-timezone-l1-1-0.dll";
private const string GetCurrentProcessApiDllName = "api-ms-win-downlevel-kernel32-l1-1-0.dll";
private const string OpenProcessTokenApiDllName = "api-ms-win-downlevel-advapi32-l1-1-1.dll";
private const string LookupPrivilegeTokenApiDllName = "api-ms-win-downlevel-advapi32-l4-1-0.dll";
private const string PrivilegeCheckApiDllName = "api-ms-win-downlevel-advapi32-l1-1-1.dll";
private const string AdjustTokenPrivilegesApiDllName = "api-ms-win-downlevel-advapi32-l1-1-1.dll";
private const string CloseHandleApiDllName = "api-ms-win-downlevel-kernel32-l1-1-0.dll";
private const string SendMessageTimeoutApiDllName = "ext-ms-win-rtcore-ntuser-window-ext-l1-1-0.dll";
#else
private const string SetDynamicTimeZoneApiDllName = "kernel32.dll";
private const string GetTimeZoneInformationForYearApiDllName = "kernel32.dll";
private const string GetCurrentProcessApiDllName = "kernel32.dll";
private const string OpenProcessTokenApiDllName = "advapi32.dll";
private const string LookupPrivilegeTokenApiDllName = "advapi32.dll";
private const string PrivilegeCheckApiDllName = "advapi32.dll";
private const string AdjustTokenPrivilegesApiDllName = "advapi32.dll";
private const string CloseHandleApiDllName = "kernel32.dll";
private const string SendMessageTimeoutApiDllName = "user32.dll";
#endif
#endregion Native DLL locations
#region Win32 SetDynamicTimeZoneInformation imports
/// <summary>
/// Used to marshal win32 SystemTime structure to managed code layer
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct SystemTime
{
/// <summary>
/// The year.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public short Year;
/// <summary>
/// The month.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public short Month;
/// <summary>
/// The day of the week.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public short DayOfWeek;
/// <summary>
/// The day of the month.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public short Day;
/// <summary>
/// The hour.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public short Hour;
/// <summary>
/// The minute.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public short Minute;
/// <summary>
/// The second.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public short Second;
/// <summary>
/// The millisecond.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public short Milliseconds;
}
/// <summary>
/// Used to marshal win32 DYNAMIC_TIME_ZONE_INFORMATION structure to managed code layer
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DYNAMIC_TIME_ZONE_INFORMATION
{
/// <summary>
/// The current bias for local time translation on this computer, in minutes.
/// </summary>
[MarshalAs(UnmanagedType.I4)]
public int Bias;
/// <summary>
/// A description for standard time.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string StandardName;
/// <summary>
/// A SystemTime structure that contains a date and local time when the transition from daylight saving time to standard time occurs on this operating system.
/// </summary>
public SystemTime StandardDate;
/// <summary>
/// The bias value to be used during local time translations that occur during standard time.
/// </summary>
[MarshalAs(UnmanagedType.I4)]
public int StandardBias;
/// <summary>
/// A description for daylight saving time (DST).
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string DaylightName;
/// <summary>
/// A SystemTime structure that contains a date and local time when the transition from standard time to daylight saving time occurs on this operating system.
/// </summary>
public SystemTime DaylightDate;
/// <summary>
/// The bias value to be used during local time translations that occur during daylight saving time.
/// </summary>
[MarshalAs(UnmanagedType.I4)]
public int DaylightBias;
/// <summary>
/// The name of the time zone registry key on the local computer.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x80)]
public string TimeZoneKeyName;
/// <summary>
/// Indicates whether dynamic daylight saving time is disabled.
/// </summary>
[MarshalAs(UnmanagedType.U1)]
public bool DynamicDaylightTimeDisabled;
}
/// <summary>
/// Used to marshal win32 TIME_ZONE_INFORMATION structure to managed code layer
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct TIME_ZONE_INFORMATION
{
/// <summary>
/// The current bias for local time translation on this computer, in minutes.
/// </summary>
[MarshalAs(UnmanagedType.I4)]
public int Bias;
/// <summary>
/// A description for standard time.
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string StandardName;
/// <summary>
/// A SystemTime structure that contains a date and local time when the transition from daylight saving time to standard time occurs on this operating system.
/// </summary>
public SystemTime StandardDate;
/// <summary>
/// The bias value to be used during local time translations that occur during standard time.
/// </summary>
[MarshalAs(UnmanagedType.I4)]
public int StandardBias;
/// <summary>
/// A description for daylight saving time (DST).
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string DaylightName;
/// <summary>
/// A SystemTime structure that contains a date and local time when the transition from standard time to daylight saving time occurs on this operating system.
/// </summary>
public SystemTime DaylightDate;
/// <summary>
/// The bias value to be used during local time translations that occur during daylight saving time.
/// </summary>
[MarshalAs(UnmanagedType.I4)]
public int DaylightBias;
}
/// <summary>
/// PInvoke SetDynamicTimeZoneInformation API
/// </summary>
/// <param name="lpTimeZoneInformation">A DYNAMIC_TIME_ZONE_INFORMATION structure representing the desired local time zone.</param>
/// <returns></returns>
[DllImport(SetDynamicTimeZoneApiDllName, SetLastError = true)]
public static extern bool SetDynamicTimeZoneInformation([In] ref DYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation);
[DllImport(GetTimeZoneInformationForYearApiDllName, SetLastError = true)]
public static extern bool GetTimeZoneInformationForYear([In] ushort wYear, [In] ref DYNAMIC_TIME_ZONE_INFORMATION pdtzi, ref TIME_ZONE_INFORMATION ptzi);
#endregion Win32 SetDynamicTimeZoneInformation imports
#region Win32 AdjustTokenPrivilege imports
/// <summary>
/// Definition of TOKEN_QUERY constant from Win32 API
/// </summary>
public const int TOKEN_QUERY = 0x00000008;
/// <summary>
/// Definition of TOKEN_ADJUST_PRIVILEGES constant from Win32 API
/// </summary>
public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
/// <summary>
/// Definition of SE_PRIVILEGE_ENABLED constant from Win32 API
/// </summary>
public const int SE_PRIVILEGE_ENABLED = 0x00000002;
/// <summary>
/// Definition of SE_TIME_ZONE_NAME constant from Win32 API
/// </summary>
public const string SE_TIME_ZONE_NAME = "SeTimeZonePrivilege"; //http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx
/// <summary>
/// PInvoke GetCurrentProcess API
/// </summary>
/// <returns></returns>
[DllImport(GetCurrentProcessApiDllName, ExactSpelling = true)]
public static extern IntPtr GetCurrentProcess();
/// <summary>
/// PInvoke OpenProcessToken API
/// </summary>
/// <param name="ProcessHandle"></param>
/// <param name="DesiredAccess"></param>
/// <param name="TokenHandle"></param>
/// <returns></returns>
[DllImport(OpenProcessTokenApiDllName, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);
/// <summary>
/// PInvoke LookupPrivilegeValue API
/// </summary>
/// <param name="lpSystemName"></param>
/// <param name="lpName"></param>
/// <param name="lpLuid"></param>
/// <returns></returns>
[DllImport(LookupPrivilegeTokenApiDllName, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref long lpLuid);
/// <summary>
/// PInvoke PrivilegeCheck API
/// </summary>
/// <param name="ClientToken"></param>
/// <param name="RequiredPrivileges"></param>
/// <param name="pfResult"></param>
/// <returns></returns>
[DllImport(PrivilegeCheckApiDllName, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool PrivilegeCheck(IntPtr ClientToken, ref PRIVILEGE_SET RequiredPrivileges, ref bool pfResult);
/// <summary>
/// PInvoke AdjustTokenPrivilege API
/// </summary>
/// <param name="TokenHandle"></param>
/// <param name="DisableAllPrivilegs"></param>
/// <param name="NewState"></param>
/// <param name="BufferLength"></param>
/// <param name="PreviousState"></param>
/// <param name="ReturnLength"></param>
/// <returns></returns>
[DllImport(AdjustTokenPrivilegesApiDllName, SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivilegs,
ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength);
/// <summary>
/// PInvoke CloseHandle API
/// </summary>
/// <param name="hObject"></param>
/// <returns></returns>
[DllImport(CloseHandleApiDllName, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
/// <summary>
/// Used to marshal win32 PRIVILEGE_SET structure to managed code layer
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PRIVILEGE_SET
{
public int PrivilegeCount;
public int Control;
public long Luid;
public int Attributes;
}
/// <summary>
/// Used to marshal win32 TOKEN_PRIVILEGES structure to managed code layer
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TOKEN_PRIVILEGES
{
public int PrivilegeCount;
public long Luid;
public int Attributes;
}
#endregion Win32 AdjustTokenPrivilege imports
#region Win32 SendMessage imports
/// <summary>
/// Definition of WM_SETTINGCHANGE constant from Win32 API
/// </summary>
public const int WM_SETTINGCHANGE = 0x001A;
/// <summary>
/// Definition of HWND_BROADCAST constant from Win32 API
/// </summary>
public const int HWND_BROADCAST = (-1);
/// <summary>
/// Definition of SMTO_ABORTIFHUNG constant from Win32 API
/// </summary>
public const int SMTO_ABORTIFHUNG = 0x0002;
/// <summary>
/// PInvoke SendMessageTimeout API
/// </summary>
/// <param name="hWnd"></param>
/// <param name="Msg"></param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <param name="fuFlags"></param>
/// <param name="uTimeout"></param>
/// <param name="lpdwResult"></param>
/// <returns></returns>
[DllImport(SendMessageTimeoutApiDllName, SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr SendMessageTimeout(IntPtr hWnd, int Msg, IntPtr wParam, string lParam, int fuFlags, int uTimeout, ref int lpdwResult);
#endregion Win32 SendMessage imports
}
#endregion Win32 interop helper
}
/// <summary>
/// static Helper class for working with system time zones.
/// </summary>
internal static class TimeZoneHelper
{
#region Error Ids
internal const string TimeZoneNotFoundError = "TimeZoneNotFound";
internal const string MultipleMatchingTimeZonesError = "MultipleMatchingTimeZones";
internal const string InsufficientPermissionsError = "InsufficientPermissions";
internal const string SetTimeZoneFailedError = "SetTimeZoneFailed";
#endregion Error Ids
/// <summary>
/// Clear the cached TimeZoneInfo data. Note that since TimeZoneInfo.ClearCachedData
/// does not exist in .NET Core there is an alternative mechanism (which doesn't seem
/// to affect clearing out the cache under the full desktop version of .NET)
/// </summary>
internal static void ClearCachedData()
{
#if CORECLR
// NOTE: .NET Core does not expose the ClearCachedData method, but executing
// the specific ConvertTime request below should have the same effect.
TimeZoneInfo.ConvertTime(new DateTime(0), TimeZoneInfo.Local);
#else
TimeZoneInfo.ClearCachedData();
#endif
}
/// <summary>
/// Find the system time zone by checking first against StandardName and then,
/// if no matches were found, against the DaylightName.
/// </summary>
/// <param name="name">The name (or wildcard pattern) of the system time zone to find.</param>
/// <returns>A TimeZoneInfo object array containing information about the specified system time zones.</returns>
internal static TimeZoneInfo[] LookupSystemTimeZoneInfoByName(string name)
{
WildcardPattern namePattern = new WildcardPattern(name, WildcardOptions.IgnoreCase);
List<TimeZoneInfo> tzi = new List<TimeZoneInfo>();
// get the available system time zones
ReadOnlyCollection<TimeZoneInfo> zones = TimeZoneInfo.GetSystemTimeZones();
// check against the standard and daylight names for each TimeZoneInfo
foreach (TimeZoneInfo zone in zones)
{
if (namePattern.IsMatch(zone.StandardName) || namePattern.IsMatch(zone.DaylightName))
{
tzi.Add(zone);
}
}
return (tzi.ToArray());
}
}
}
#endif
@@ -85,6 +85,8 @@
"monad/src/cimSupport/cmdletization/cim/cimCmdletInvocationContext.cs": "cimSupport/cmdletization/cim/cimCmdletInvocationContext.cs",
"monad/src/commands/management/ParsePathCommand.cs": "commands/management/ParsePathCommand.cs",
"monad/src/commands/management/CopyPropertyCommand.cs": "commands/management/CopyPropertyCommand.cs",
"monad/src/commands/management/TimeZoneCommands.cs": "commands/management/TimeZoneCommands.cs",
"monad/src/commands/management/resources/TimeZoneResources.resx": "resources/TimeZoneResources.resx",
"monad/src/cimSupport/cmdletization/cim/TerminatingErrorTracker.cs": "cimSupport/cmdletization/cim/TerminatingErrorTracker.cs",
"monad/src/cimSupport/cmdletization/cim/cimChildJobBase.cs": "cimSupport/cmdletization/cim/cimChildJobBase.cs"
}
@@ -10,6 +10,14 @@
"allowUnsafe": true
},
"configurations": {
"Linux": {
"buildOptions": {
"define": [ "LINUX" ]
}
}
},
"dependencies": {
"Microsoft.PowerShell.Security": "1.0.0-*"
},
@@ -24,7 +32,6 @@
"commands/management/ClearRecycleBinCommand.cs",
"commands/management/ControlPanelItemCommand.cs",
"commands/management/CommitTransactionCommand.cs",
"commands/management/Computer.cs",
"commands/management/Eventlog.cs",
"commands/management/GetClipboardCommand.cs",
"commands/management/GetTransactionCommand.cs",
@@ -42,11 +49,25 @@
"commands/management/WebServiceProxy.cs",
"singleshell/installer/MshManagementMshSnapin.cs"
]
},
"embed": {
"exclude": [
"resources/EventlogResources.resx",
"resources/TransactionResources.resx",
"resources/WebServiceResources.resx",
"resources/HotFixResources.resx",
"resources/ControlPanelResources.resx",
"resources/WmiResources.resx",
"resources/ManagementMshSnapInResources.resx",
"resources/ClearRecycleBinResources.resx",
"resources/ClipboardResources.resx"
]
}
},
"dependencies": {
"System.ServiceProcess.ServiceController": "4.1.0",
"System.Net.NameResolution": "4.0.0"
"System.Net.NameResolution": "4.0.0",
"System.Net.Ping": "4.0.0"
}
},
"net451": {
@@ -64,6 +85,11 @@
"exclude": [
"commands/management/ControlPanelItemCommand.cs"
]
},
"embed": {
"exclude": [
"resources/ControlPanelResources.resx"
]
}
}
}
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="MultipleMatchingTimeZones" xml:space="preserve">
<value>Cannot set the local time zone because the name '{0}' resolves to multiple entries.</value>
</data>
<data name="TimeZoneNameNotFound" xml:space="preserve">
<value>The time zone name '{0}' was not found on the local computer.</value>
</data>
</root>
@@ -41,12 +41,16 @@
"commands/utility/ShowCommand/ShowCommandProxy.cs",
"commands/utility/WebCmdlet/BasicHtmlWebResponseObject.cs",
"commands/utility/WebCmdlet/ContentHelper.cs",
"commands/utility/WebCmdlet/FormObject.cs",
"commands/utility/WebCmdlet/FormObjectCollection.cs",
"commands/utility/WebCmdlet/HtmlWebResponseObject.cs",
"commands/utility/WebCmdlet/InvokeRestMethodCommand.cs",
"commands/utility/WebCmdlet/InvokeWebRequestCommand.cs",
"commands/utility/WebCmdlet/JsonObjectTypeResolver.cs",
"commands/utility/WebCmdlet/PSUserAgent.cs",
"commands/utility/WebCmdlet/StreamHelper.cs",
"commands/utility/WebCmdlet/WebCmdletElementCollection.cs",
"commands/utility/WebCmdlet/WebRequestMethod.cs",
"commands/utility/WebCmdlet/WebRequestPSCmdlet.cs",
"commands/utility/WebCmdlet/WebRequestSession.cs",
"commands/utility/WebCmdlet/WebResponseHelper.cs",
@@ -56,6 +60,21 @@
"commands/utility/update-list.cs",
"singleshell/installer/MshUtilityMshSnapin.cs"
]
},
"embed": {
"exclude": [
"resources/FormatAndOut_out_gridview.resx",
"resources/ImplicitRemotingStrings.resx",
"resources/UtilityMshSnapinResources.resx",
"resources/OutPrinterDisplayStrings.resx",
"resources/TraceCommandStrings.resx",
"resources/UpdateListStrings.resx",
"resources/SendMailMessageStrings.resx",
"resources/ConvertFromStringResources.resx",
"resources/ConvertStringResources.resx",
"resources/FlashExtractStrings.resx",
"resources/ImmutableStrings.resx"
]
}
},
"imports": [ "dnxcore50", "portable-net45+win8" ],
@@ -68,7 +68,18 @@
"netstandard1.6": {
"imports": [ "dnxcore50", "portable-net45+win8" ],
"buildOptions": {
"define": [ "CORECLR", "PORTABLE" ]
"define": [ "CORECLR" ],
"compile": {
"exclude": [
"singleshell/installer/EngineInstaller.cs",
"singleshell/installer/MshHostMshSnapin.cs"
]
},
"embed": {
"exclude": [
"resources/HostMshSnapinResources.resx"
]
}
},
"dependencies": {
"Newtonsoft.Json": "7.0.1",
@@ -1,4 +1,3 @@
#if !PORTABLE
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
@@ -87,4 +86,3 @@ namespace Microsoft.PowerShell
}
}
}
#endif
@@ -1,4 +1,3 @@
#if !PORTABLE
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
@@ -93,4 +92,3 @@ namespace Microsoft.PowerShell
}
}
}
#endif
@@ -17,5 +17,6 @@
"monad/src/security/ExecutionPolicyCommands.cs": "security/ExecutionPolicyCommands.cs",
"monad/src/security/CmsCommands.cs": "security/CmsCommands.cs",
"monad/src/security/AclCommands.cs": "security/AclCommands.cs",
"monad/src/security/CatalogCommands.cs": "security/CatalogCommands.cs",
"monad/src/security/resources/SignatureCommands.resx": "resources/SignatureCommands.resx"
}
@@ -10,6 +10,14 @@
"allowUnsafe": true
},
"configurations": {
"Linux": {
"buildOptions": {
"define": [ "LINUX" ]
}
}
},
"dependencies": {
"System.Management.Automation": "1.0.0-*"
},
@@ -25,6 +33,13 @@
"security/CmsCommands.cs",
"singleshell/installer/MshSecurityMshSnapin.cs"
]
},
"embed": {
"exclude": [
"resources/CertificateCommands.resx",
"resources/CmsCommands.resx",
"resources/SecurityMshSnapinResources.resx"
]
}
}
},
@@ -0,0 +1,286 @@
#if !LINUX
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System;
using System.Management.Automation;
using Dbg=System.Management.Automation.Diagnostics;
using System.Collections;
using System.IO;
using System.Management.Automation.Provider;
using System.Runtime.InteropServices;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.PowerShell.Commands
{
/// <summary>
/// Defines the base class from which all catalog commands are derived.
/// </summary>
public abstract class CatalogCommandsBase : PSCmdlet
{
/// <summary>
/// Path of folder/file to generate or validate the catalog file
/// </summary>
[Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByPath")]
public string CatalogFilePath
{
get
{
return catalogFilePath;
}
set
{
catalogFilePath = value;
}
}
private string catalogFilePath;
/// <summary>
/// Path of folder/file to generate or validate the catalog file
/// </summary>
[Parameter(Position = 1, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByPath")]
public string[] Path
{
get
{
return path;
}
set
{
path = value;
}
}
private string[] path;
//
// name of this command
//
string commandName;
/// <summary>
/// Initializes a new instance of the CatalogCommandsBase class,
/// using the given command name.
/// </summary>
/// <param name="name">
/// The name of the command.
/// </param>
protected CatalogCommandsBase(string name) : base()
{
commandName = name;
}
private CatalogCommandsBase() : base() {}
/// <summary>
/// Processes records from the input pipeline.
/// For each input object, the command either generate the Catalog or
/// Validates the existing Catalog
/// </summary>
protected override void ProcessRecord()
{
//
// this cannot happen as we have specified the Path
// property to be mandatory parameter
//
Dbg.Assert((CatalogFilePath != null) && (CatalogFilePath.Length > 0),
"CatalogCommands: Param binder did not bind catalogFilePath");
Collection<string> paths = new Collection<string>();
if (Path != null)
{
foreach (string p in Path)
{
foreach (PathInfo tempPath in SessionState.Path.GetResolvedPSPathFromPSPath(p))
{
paths.Add(tempPath.ProviderPath);
}
}
}
string drive = null;
// resolve catalog destination Path
if (!SessionState.Path.IsPSAbsolute(catalogFilePath, out drive) && !System.IO.Path.IsPathRooted(catalogFilePath))
{
catalogFilePath = SessionState.Path.GetUnresolvedProviderPathFromPSPath(catalogFilePath);
}
PerformAction(paths, catalogFilePath);
}
/// <summary>
/// Performs the action i.e. Generate or Validate the Windows Catalog File
/// </summary>
/// <param name="path">
/// The name of the Folder or file on which to perform the action.
/// </param>
/// <param name="catalogFilePath">
/// Path to Catalog
/// </param>
protected abstract void PerformAction(Collection<string> path, string catalogFilePath);
}
/// <summary>
/// Defines the implementation of the 'New-FileCatalog' cmdlet.
/// This cmdlet generates the catalog for File or Folder
/// </summary>
[Cmdlet(VerbsCommon.New, "FileCatalog", SupportsShouldProcess = true, DefaultParameterSetName = "ByPath",
HelpUri = "http://go.microsoft.com/fwlink/?LinkId=786749")]
[OutputType(typeof(FileInfo))]
public sealed class NewFileCatalogCommand : CatalogCommandsBase
{
/// <summary>
/// Initializes a new instance of the New-FileCatalog class.
/// </summary>
public NewFileCatalogCommand() : base("New-FileCatalog") { }
/// <summary>
/// Catalog version
/// </summary>
[Parameter()]
public int CatalogVersion
{
get
{
return catalogVersion;
}
set
{
catalogVersion = value;
}
}
// Based on the Catalog version we will decide which hashing Algorithm to use
private int catalogVersion = 1;
/// <summary>
/// Generate the Catalog for the Path
/// </summary>
/// <param name="path">
/// File or Folder Path
/// </param>
/// <param name="catalogFilePath">
/// Path to Catalog
/// </param>
/// <returns>
/// True if able to Create Catalog or else False
/// </returns>
protected override void PerformAction(Collection<string> path,string catalogFilePath)
{
if (path.Count == 0)
{
// if user has not provided the path use current directory to generate catalog
path.Add(SessionState.Path.CurrentFileSystemLocation.Path);
}
FileInfo catalogFileInfo = new FileInfo(catalogFilePath);
// If Path points to the expected cat file make sure
// parent Directory exists other wise CryptoAPI fails to create a .cat file
if (catalogFileInfo.Extension.Equals(".cat", StringComparison.Ordinal))
{
System.IO.Directory.CreateDirectory(catalogFileInfo.Directory.FullName);
}
else
{
// This only creates Directory if it does not exists, Append a default name
System.IO.Directory.CreateDirectory(catalogFilePath);
catalogFilePath = System.IO.Path.Combine(catalogFilePath, "catalog.cat");
}
FileInfo catalogFile = CatalogHelper.GenerateCatalog(this, path, catalogFilePath, catalogVersion);
if (catalogFile != null)
{
WriteObject(catalogFile);
}
}
}
/// <summary>
/// Defines the implementation of the 'Test-FileCatalog' cmdlet.
/// This cmdlet validaes the Intgerity of catalog
/// </summary>
[Cmdlet(VerbsDiagnostic.Test, "FileCatalog", SupportsShouldProcess = true, DefaultParameterSetName = "ByPath",
HelpUri = "http://go.microsoft.com/fwlink/?LinkId=786750")]
[OutputType(typeof(CatalogValidationStatus))]
[OutputType(typeof(CatalogInformation))]
public sealed class TestFileCatalogCommand : CatalogCommandsBase
{
/// <summary>
/// Initializes a new instance of the New-FileCatalog class.
/// </summary>
public TestFileCatalogCommand() : base("Test-FileCatalog") { }
/// <summary>
/// </summary>
[Parameter()]
public SwitchParameter Detailed
{
get { return detailed; }
set { detailed = value; }
}
private bool detailed = false;
/// <summary>
/// Patterns used to exclude files from DiskPaths and Catalog
/// </summary>
[Parameter()]
public string[] FilesToSkip
{
get
{
return filesToSkip;
}
set
{
filesToSkip = value;
this.excludedPatterns = new WildcardPattern[filesToSkip.Length];
for (int i = 0; i < filesToSkip.Length; i++)
{
this.excludedPatterns[i] = WildcardPattern.Get(filesToSkip[i], WildcardOptions.IgnoreCase);
}
}
}
string[] filesToSkip = null;
internal WildcardPattern[] excludedPatterns = null;
/// <summary>
/// Validate the Integrity of given Catalog
/// </summary>
/// <param name="path">
/// File or Folder Path
/// </param>
/// <param name="catalogFilePath">
/// Path to Catalog
/// </param>
/// <returns>
/// True if able to Validate the Catalog and its not tampered or else False
/// </returns>
protected override void PerformAction(Collection<string> path, string catalogFilePath)
{
if (path.Count == 0)
{
// if user has not provided the path use the path of catalog file itself.
path.Add(new FileInfo(catalogFilePath).Directory.FullName);
}
CatalogInformation catalogInfo = CatalogHelper.ValidateCatalog(this, path, catalogFilePath, excludedPatterns);
if (detailed)
{
WriteObject(catalogInfo);
}
else
{
WriteObject(catalogInfo.Status);
}
}
}
}
#endif
@@ -743,6 +743,8 @@
"monad/src/namespaces/Buffer.cs": "namespaces/Buffer.cs",
"monad/src/engine/interpreter/LocalAccess.cs": "engine/interpreter/LocalAccess.cs",
"monad/src/security/SecuritySupport.cs": "security/SecuritySupport.cs",
"monad/src/security/CatalogHelper.cs": "security/CatalogHelper.cs",
"monad/src/engine/resources/CatalogStrings.resx": "resources/CatalogStrings.resx",
"monad/src/engine/remoting/client/remotepipeline.cs": "engine/remoting/client/remotepipeline.cs",
"monad/src/engine/remoting/common/remotingexceptions.cs": "engine/remoting/common/remotingexceptions.cs",
"monad/src/help/BaseCommandHelpInfo.cs": "help/BaseCommandHelpInfo.cs",
@@ -125,6 +125,12 @@
"utils/perfCounters/CounterSetRegistrarBase.cs",
"utils/perfCounters/PSPerfCountersMgr.cs"
]
},
"embed": {
"exclude": [
"resources/CoreMshSnapinResources.resx",
"resources/ErrorPackageRemoting.resx"
]
}
},
"dependencies": {
@@ -164,6 +170,11 @@
}
},
"net451": {
"buildOptions": {
"embed": {
"exclude": [ "resources/CoreClrStubResources.resx" ]
}
},
"frameworkAssemblies": {
"System.Runtime": "",
"System.Xml": "",
@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CatalogDefintionFileNotGenerated" xml:space="preserve">
<value>Unable to generate catalog defintion file.</value>
</data>
<data name="AddFileToCatalog" xml:space="preserve">
<value>Adding file '{0}' to the catalog. Relative path of file in catalog is '{1}'.</value>
</data>
<data name="SkipValidationOfCatalogFile" xml:space="preserve">
<value>Skipping validating file {0} from catalog. </value>
</data>
<data name="FoundFileHashInCatlogItem" xml:space="preserve">
<value>Found file {0} in the catalog with hash of {1}. </value>
</data>
<data name="FoundDuplicateFilesRelativePath" xml:space="preserve">
<value>Paths for catalog contains multiple files with same relative path {0}. </value>
</data>
<data name="FoundFileInPath" xml:space="preserve">
<value>Found file {0} on disk with hash of {1}.</value>
</data>
<data name="SkipValidationOfPathFile" xml:space="preserve">
<value>Skipping validating file {0} from path.</value>
</data>
<data name="UnableToAcquireHashAlgorithmContext" xml:space="preserve">
<value>Unable to acquire a handle to a catalog administrator context for a given hash algorithm {0}.</value>
</data>
<data name="UnableToCreateFileHash" xml:space="preserve">
<value>Unable to create the hash for file {0}. </value>
</data>
<data name="UnableToOpenCatalogFile" xml:space="preserve">
<value>Unable to open catalog file {0}.</value>
</data>
<data name="UnKnownCatalogVersion" xml:space="preserve">
<value>Catalog version is not valid. We only support catalog version {0} and version {1}.</value>
</data>
<data name="UnableToOpenCatalogDefinitionFile" xml:space="preserve">
<value>Unable to open catalog defintion file.</value>
</data>
<data name="FoundDuplicateFileMemberInCatalog" xml:space="preserve">
<value>Found multiple entries of file member {0} in the catalog.</value>
</data>
<data name="UnableToFindFileNameOrPathForCatalogMember" xml:space="preserve">
<value>Unable to find file name or path for catalog member {0}.</value>
</data>
<data name="UnableToFindFileToHash" xml:space="preserve">
<value>Unable to find file {0} to hash.</value>
</data>
<data name="UnableToReadFileToHash" xml:space="preserve">
<value>Unable to read file {0} to calculate its hash.</value>
</data>
</root>
@@ -0,0 +1,848 @@
#if !LINUX
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using Dbg = System.Management.Automation;
using System;
using System.Text;
using System.Security.Cryptography;
using System.Collections.Generic;
using System.Collections;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Management.Automation.Internal;
using System.Management.Automation.Provider;
using System.Management.Automation.Security;
using System.Runtime.InteropServices;
using DWORD = System.UInt32;
namespace System.Management.Automation
{
/// <summary>
/// Defines the possible status when validating integrity of catalog
/// </summary>
public enum CatalogValidationStatus
{
/// <summary>
/// Status when catalog is not tampered.
/// </summary>
Valid,
/// <summary>
/// Status when catalog is tampered.
/// </summary>
ValidationFailed
}
/// <summary>
/// Object returned by Catalog Cmdlets
/// </summary>
public class CatalogInformation
{
/// <summary>
/// status of catalog
/// </summary>
public CatalogValidationStatus Status { get; set; }
/// <summary>
/// Hash Algorithm used to calculate the hashes of files in Catalog
/// </summary>
public string HashAlgorithm { get; set; }
/// <summary>
/// Dictionary mapping files relative paths to their hash values found from Catalog
/// </summary>
public Dictionary<String, String> CatalogItems { get; set;}
/// <summary>
/// Dictionary mapping files relative paths to their hash values
/// </summary>
public Dictionary<String, String> PathItems { get; set;}
/// <summary>
/// Signature for the catalog
/// </summary>
public Signature Signature { get; set; }
}
/// <summary>
/// Helper functions for Windows Catalog functionality
/// </summary>
internal static class CatalogHelper
{
// Catalog Version is (0X100 = 256) for Catalog Version 1
private static int catalogVersion1 = 256;
// Catalog Version is (0X200 = 512) for Catalog Version 2
private static int catalogVersion2 = 512;
// Hash Algorithms supported by Windows Catalog
private static string HashAlgorithmSHA1 = "SHA1";
private static string HashAlgorithmSHA256 = "SHA256";
private static PSCmdlet _cmdlet = null;
/// <summary>
/// Find out the Version of Catalog by reading its Meta data. We can have either version 1 or version 2 catalog
/// </summary>
/// <param name="catalogHandle"> Handle to open catalog file </param>
/// <returns> Version of the catalog </returns>
private static int GetCatalogVersion(IntPtr catalogHandle)
{
int catalogVersion = -1;
IntPtr catalogData = NativeMethods.CryptCATStoreFromHandle(catalogHandle);
NativeMethods.CRYPTCATSTORE catalogInfo = ClrFacade.PtrToStructure<NativeMethods.CRYPTCATSTORE>(catalogData);
if (catalogInfo.dwPublicVersion == catalogVersion2)
{
catalogVersion = 2;
}
// One Windows 7 this API sent version information as decimal 1 not hex (0X100 = 256)
// so we are checking for that value as well. Reason we are not checking for version 2 above in
// this scenario because catalog verion 2 is not supported on win7.
else if ((catalogInfo.dwPublicVersion == catalogVersion1) || (catalogInfo.dwPublicVersion == 1))
{
catalogVersion = 1;
}
else
{
// catalog version we don't understand
Exception exception = new InvalidOperationException(StringUtil.Format(CatalogStrings.UnKnownCatalogVersion,
catalogVersion1.ToString("X"),
catalogVersion2.ToString("X")));
ErrorRecord errorRecord = new ErrorRecord(exception, "UnKnownCatalogVersion", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
}
return catalogVersion;
}
/// <summary>
/// HashAlgorithm used by the Catalog. It is based on the version of Catalog.
/// </summary>
/// <param name="catalogVersion"> Path of the output catalog file </param>
/// <returns> Version of the catalog </returns>
private static string GetCatalogHashAlgorithm(int catalogVersion)
{
string hashAlgorithm = string.Empty;
if (catalogVersion == 1)
{
hashAlgorithm = HashAlgorithmSHA1;
}
else if (catalogVersion == 2)
{
hashAlgorithm = HashAlgorithmSHA256;
}
else
{
// version we don't understand
Exception exception = new InvalidOperationException(StringUtil.Format(CatalogStrings.UnKnownCatalogVersion,
"1.0",
"2.0"));
ErrorRecord errorRecord = new ErrorRecord(exception, "UnKnownCatalogVersion", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
}
return hashAlgorithm;
}
/// <summary>
/// Generate the Catalog Defintion File representing files and folders
/// </summary>
///
/// <param name="Path"> Path of expected output .cdf file </param>
///
/// <param name="catalogFilePath"> Path of the output catalog file </param>
///
/// <param name="cdfFilePath"> Path of the catalog definiton file </param>
///
/// <param name="catalogVersion"> Version of catalog</param>
///
/// <param name="hashAlgorithm"> hash method used to generate hashes for the Catalog </param>
/// <returns> HashSet for the relative Path for files in Catalog </returns>
internal static string GenerateCDFFile(Collection<string> Path, string catalogFilePath, string cdfFilePath, int catalogVersion, string hashAlgorithm)
{
HashSet<string> relativePaths = new HashSet<string>();
string cdfHeaderContent = string.Empty;
string cdfFilesContent = string.Empty;
int catAttributeCount = 0;
// First create header and files section for the catalog then write in file
cdfHeaderContent += "[CatalogHeader]" + Environment.NewLine;
cdfHeaderContent += @"Name=" + catalogFilePath + Environment.NewLine;
cdfHeaderContent += "CatalogVersion=" + catalogVersion + Environment.NewLine;
cdfHeaderContent += "HashAlgorithms=" + hashAlgorithm + Environment.NewLine;
cdfFilesContent += "[CatalogFiles]" + Environment.NewLine;
foreach (string catalogFile in Path)
{
if (System.IO.Directory.Exists(catalogFile))
{
var directoryItems = Directory.EnumerateFiles(catalogFile, "*.*", SearchOption.AllDirectories);
foreach (string fileItem in directoryItems)
{
ProcessFileToBeAddedInCatalogDefinitionFile(new FileInfo(fileItem), new DirectoryInfo(catalogFile), ref relativePaths, ref cdfHeaderContent, ref cdfFilesContent, ref catAttributeCount);
}
}
else if (System.IO.File.Exists(catalogFile))
{
ProcessFileToBeAddedInCatalogDefinitionFile(new FileInfo(catalogFile), null, ref relativePaths, ref cdfHeaderContent, ref cdfFilesContent, ref catAttributeCount);
}
}
using (System.IO.StreamWriter fileWriter = new System.IO.StreamWriter(new FileStream(cdfFilePath, FileMode.Create)))
{
fileWriter.WriteLine(cdfHeaderContent);
fileWriter.WriteLine();
fileWriter.WriteLine(cdfFilesContent);
}
return cdfFilePath;
}
/// <summary>
/// Get file attribute (Relative path in our case) from catalog
/// </summary>
/// <param name="fileToHash"> file to hash </param>
/// <param name="dirInfo"> directory information about file needed to calculate relative file path. </param>
/// <param name="relativePaths"> working set of relative paths of all files. </param>
/// <param name="cdfHeaderContent"> content to be added in CatalogHeader section of cdf File</param>
/// <param name="cdfFilesContent"> content to be added in CatalogFiles section of cdf File </param>
/// <param name="catAttributeCount"> indictaing the current no of catalog header level attributes </param>
/// <returns> void </returns>
internal static void ProcessFileToBeAddedInCatalogDefinitionFile(FileInfo fileToHash, DirectoryInfo dirInfo, ref HashSet<string> relativePaths, ref string cdfHeaderContent, ref string cdfFilesContent, ref int catAttributeCount)
{
string relativePath = string.Empty;
if (dirInfo != null)
{
//Relative path of the file is the path inside the containing folder excluding folder Name
relativePath = fileToHash.FullName.Substring(dirInfo.FullName.Length).TrimStart('\\');
}
else
{
relativePath = fileToHash.Name;
}
if (!relativePaths.Contains(relativePath))
{
relativePaths.Add(relativePath);
if (fileToHash.Length != 0)
{
cdfFilesContent += "<HASH>" + fileToHash.FullName + "=" + fileToHash.FullName + Environment.NewLine;
cdfFilesContent += "<HASH>" + fileToHash.FullName + "ATTR1=0x10010001:FilePath:" + relativePath + Environment.NewLine;
}
else
{
// zero length files are added as catalog level attributes because they can not be hashed
cdfHeaderContent += "CATATTR" + (++catAttributeCount) + "=0x10010001:FilePath:" + relativePath + Environment.NewLine;
}
}
else
{
// If Files have same relative paths we can not distinguish them for
// Validation. So failing.
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFilesRelativePath, relativePath)), "FoundDuplicateFilesRelativePath", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
}
}
/// <summary>
/// Generate the Catalog file for Input Catalog Defintion File
/// </summary>
///
/// <param name="cdfFilePath"> Path to the Input .cdf file </param>
internal static void GenerateCatalogFile(string cdfFilePath)
{
string pwszFilePath = cdfFilePath;
NativeMethods.CryptCATCDFOpenCallBack catOpenCallBack = new NativeMethods.CryptCATCDFOpenCallBack(ParseErrorCallback);
// Open CDF File
IntPtr resultCDF = NativeMethods.CryptCATCDFOpen(pwszFilePath, catOpenCallBack);
// navigate CDF header and files sections
if (resultCDF != IntPtr.Zero)
{
// First navigate all catalog level attributes entries first, they represent zero size files
IntPtr catalogAttr = IntPtr.Zero;
do
{
catalogAttr = NativeMethods.CryptCATCDFEnumCatAttributes(resultCDF, catalogAttr, catOpenCallBack);
if (catalogAttr != IntPtr.Zero)
{
string filePath = ProcessFilePathAttributeInCatalog(catalogAttr);
_cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.AddFileToCatalog, filePath, filePath));
}
} while (catalogAttr != IntPtr.Zero);
// navigate all the files hash entries in the .cdf file
IntPtr memberInfo = IntPtr.Zero;
try
{
IntPtr memberFile = IntPtr.Zero;
NativeMethods.CryptCATCDFEnumMembersByCDFTagExErrorCallBack memberCallBack = new NativeMethods.CryptCATCDFEnumMembersByCDFTagExErrorCallBack(ParseErrorCallback);
string fileName = string.Empty;
do
{
memberFile = NativeMethods.CryptCATCDFEnumMembersByCDFTagEx(resultCDF, memberFile, memberCallBack, ref memberInfo, true, IntPtr.Zero);
fileName = Marshal.PtrToStringUni(memberFile);
if (!String.IsNullOrEmpty(fileName))
{
IntPtr memberAttr = IntPtr.Zero;
string fileRelativePath = String.Empty;
do
{
memberAttr = NativeMethods.CryptCATCDFEnumAttributesWithCDFTag(resultCDF, memberFile, memberInfo, memberAttr, memberCallBack);
if (memberAttr != IntPtr.Zero)
{
fileRelativePath = ProcessFilePathAttributeInCatalog(memberAttr);
if (!String.IsNullOrEmpty(fileRelativePath))
{
// Found the attribute we are looking for
// Filename we read from the above API has <Hash> appended to its name as per CDF file tags convention
// Truncating that Information from the string.
string itemName = fileName.Substring(6);
_cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.AddFileToCatalog, itemName, fileRelativePath));
break;
}
}
} while (memberAttr != IntPtr.Zero);
}
} while (fileName != null);
}
finally
{
NativeMethods.CryptCATCDFClose(resultCDF);
}
}
else
{
// If we are not able to open CDF file we can not continue generating catalog
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(CatalogStrings.UnableToOpenCatalogDefinitionFile), "UnableToOpenCatalogDefinitionFile", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
}
}
/// <summary>
/// To generate Catalog for the folder
/// </summary>
///
/// <param name="Path"> Path to folder or File </param>
/// <param name="catalogFilePath"> Catalog File Path </param>
/// <param name="catalogVersion"> Catalog File Path </param>
/// <param name="cmdlet"> Instance of cmdlet calling this method </param>
/// <returns> true if able to generate .cat file or false </returns>
internal static FileInfo GenerateCatalog(PSCmdlet cmdlet, Collection<string> Path, string catalogFilePath, int catalogVersion)
{
_cmdlet = cmdlet;
string hashAlgorithm = GetCatalogHashAlgorithm(catalogVersion);
if (!String.IsNullOrEmpty(hashAlgorithm))
{
// Generate Path for Catalog Defintion File
string cdfFilePath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());
cdfFilePath = cdfFilePath + ".cdf";
try
{
cdfFilePath = GenerateCDFFile(Path, catalogFilePath, cdfFilePath, catalogVersion, hashAlgorithm);
if (!File.Exists(cdfFilePath))
{
// If we are not able to generate catalog defintion file we can not continue generating catalog
// throw PSTraceSource.NewInvalidOperationException("catalog", CatalogStrings.CatalogDefintionFileNotGenerated);
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(CatalogStrings.CatalogDefintionFileNotGenerated), "CatalogDefintionFileNotGenerated", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
}
GenerateCatalogFile(cdfFilePath);
if (File.Exists(catalogFilePath))
{
return new FileInfo(catalogFilePath);
}
}
finally
{
File.Delete(cdfFilePath);
}
}
return null;
}
/// <summary>
/// Get file attribute (Relative path in our case) from catalog
/// </summary>
/// <param name="memberAttrInfo"> Pointer to current attribute of catalog member. </param>
/// <returns> value of the attribute </returns>
internal static string ProcessFilePathAttributeInCatalog(IntPtr memberAttrInfo)
{
string relativePath = string.Empty;
NativeMethods.CRYPTCATATTRIBUTE currentMemberAttr = ClrFacade.PtrToStructure<NativeMethods.CRYPTCATATTRIBUTE>(memberAttrInfo);
// check if this is the attribute we are looking for
// catalog generated other way not using New-FileCatalog can have attributes we don't understand
if (currentMemberAttr.pwszReferenceTag.Equals("FilePath", StringComparison.OrdinalIgnoreCase))
{
// find the size for the current attribute value and then allocate buffer and copy from byte array
int attrValueSize = (int)currentMemberAttr.cbValue;
byte[] attrValue = new byte[attrValueSize];
Marshal.Copy(currentMemberAttr.pbValue, attrValue, 0, attrValueSize);
relativePath = System.Text.Encoding.Unicode.GetString(attrValue);
relativePath = relativePath.TrimEnd('\0');
}
return relativePath;
}
/// <summary>
/// Make a hash for the file
/// </summary>
///
/// <param name="filePath"> Path of the file </param>
/// <param name="hashAlgorithm"> Used to calculate Hash </param>
/// <returns> HashValue for the file </returns>
internal static string CalculateFileHash(string filePath, string hashAlgorithm)
{
string hashValue = string.Empty;
IntPtr catAdmin = IntPtr.Zero;
// To get handle to the hash algorithm to be used to calculate hashes
if (!NativeMethods.CryptCATAdminAcquireContext2(ref catAdmin, IntPtr.Zero, hashAlgorithm, IntPtr.Zero, 0))
{
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToAcquireHashAlgorithmContext, hashAlgorithm)), "UnableToAcquireHashAlgorithmContext", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
}
DWORD GENERIC_READ = 0x80000000;
DWORD OPEN_EXISTING = 3;
IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
// Open the file that is to be hashed for reading and get its handle
IntPtr fileHandle = NativeMethods.CreateFile(filePath, GENERIC_READ, 0, 0 , OPEN_EXISTING, 0, IntPtr.Zero);
if (fileHandle != INVALID_HANDLE_VALUE)
{
try
{
DWORD hashBufferSize = 0;
IntPtr hashBuffer = IntPtr.Zero;
// Call first time to get the size of expected buffer to hold new hash value
if (!NativeMethods.CryptCATAdminCalcHashFromFileHandle2(catAdmin, fileHandle, ref hashBufferSize, hashBuffer, 0))
{
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, filePath)), "UnableToCreateFileHash", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
}
int size = (int)hashBufferSize;
hashBuffer = Marshal.AllocHGlobal(size);
try
{
// Call second time to actually get the hash value
if (!NativeMethods.CryptCATAdminCalcHashFromFileHandle2(catAdmin, fileHandle, ref hashBufferSize, hashBuffer, 0))
{
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, filePath)), "UnableToCreateFileHash", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
}
byte[] hashBytes = new byte[size];
Marshal.Copy(hashBuffer, hashBytes, 0, size);
hashValue = BitConverter.ToString(hashBytes).Replace("-", "");
}
finally
{
if (hashBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(hashBuffer);
}
}
}
finally
{
NativeMethods.CryptCATAdminReleaseContext(catAdmin, 0);
NativeMethods.CloseHandle(fileHandle);
}
}
else
{
// If we are not able to open file that is to be hashed we can not continue with catalog validation
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToReadFileToHash, filePath)), "UnableToReadFileToHash", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
}
return hashValue;
}
/// <summary>
/// Make list of hashes for given Catalog File
/// </summary>
/// <param name="catalogFilePath"> Path to the folder having catalog file </param>
/// <param name="excludedPatterns"></param>
/// <param name="catalogVersion"> The version of input catalog we read from catalog meta data after opening it.</param>
/// <returns> Dictionary mapping files relative paths to HashValues </returns>
internal static Dictionary<String, String> GetHashesFromCatalog(string catalogFilePath, WildcardPattern[] excludedPatterns,out int catalogVersion)
{
IntPtr resultCatalog = NativeMethods.CryptCATOpen(catalogFilePath, 0, IntPtr.Zero, 1, 0);
IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
Dictionary<String, String> catalogHashes = new Dictionary<String, String>(StringComparer.CurrentCultureIgnoreCase);
catalogVersion = 0;
if (resultCatalog != INVALID_HANDLE_VALUE)
{
try
{
IntPtr catAttrInfo = IntPtr.Zero;
// First traverse all catalog level attributes to get information about zero size file.
do
{
catAttrInfo = NativeMethods.CryptCATEnumerateCatAttr(resultCatalog, catAttrInfo);
// If we found attribute it is a file information retrieve its relative path
// and add it to catalog hash collection if its not in excluded files criteria
if (catAttrInfo != IntPtr.Zero)
{
string relativePath = ProcessFilePathAttributeInCatalog(catAttrInfo);
if (!String.IsNullOrEmpty(relativePath))
{
ProcessCatalogFile(relativePath, string.Empty, excludedPatterns, ref catalogHashes);
}
}
} while (catAttrInfo != IntPtr.Zero);
catalogVersion = GetCatalogVersion(resultCatalog);
IntPtr memberInfo = IntPtr.Zero;
// Next Navigate all members in Catalog files and get their relative paths and hashes
do
{
memberInfo = NativeMethods.CryptCATEnumerateMember(resultCatalog, memberInfo);
if (memberInfo != IntPtr.Zero)
{
NativeMethods.CRYPTCATMEMBER currentMember = ClrFacade.PtrToStructure<NativeMethods.CRYPTCATMEMBER>(memberInfo);
NativeMethods.SIP_INDIRECT_DATA pIndirectData = ClrFacade.PtrToStructure<NativeMethods.SIP_INDIRECT_DATA>(currentMember.pIndirectData);
// For Catalog version 2 CryptoAPI puts hashes of file attributes(relative path in our case) in Catalog as well
// We validate those along with file hashes so we are skipping duplicate entries
if (!((catalogVersion == 2) && (pIndirectData.DigestAlgorithm.pszObjId.Equals(new Oid("SHA1").Value, StringComparison.OrdinalIgnoreCase))))
{
string relativePath = String.Empty;
IntPtr memberAttrInfo = IntPtr.Zero;
do
{
memberAttrInfo = NativeMethods.CryptCATEnumerateAttr(resultCatalog, memberInfo, memberAttrInfo);
if (memberAttrInfo != IntPtr.Zero)
{
relativePath = ProcessFilePathAttributeInCatalog(memberAttrInfo);
if (!String.IsNullOrEmpty(relativePath))
{
break;
}
}
}
while (memberAttrInfo != IntPtr.Zero);
// If we did not find any Relative Path for the item in catalog we should quit
// This catalog must not be valid for our use as catalogs generated using New-FileCatalog
// always contains relative file Paths
if (String.IsNullOrEmpty(relativePath))
{
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToOpenCatalogFile, catalogFilePath)), "UnableToOpenCatalogFile", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
}
ProcessCatalogFile(relativePath, currentMember.pwszReferenceTag, excludedPatterns, ref catalogHashes);
}
}
} while (memberInfo != IntPtr.Zero);
}
finally
{
NativeMethods.CryptCATClose(resultCatalog);
}
}
else
{
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToOpenCatalogFile, catalogFilePath)), "UnableToOpenCatalogFile", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
}
return catalogHashes;
}
/// <summary>
/// Process file in path for its relative paths
/// </summary>
/// <param name="relativePath"> relative path of file found in catalog </param>
/// <param name="fileHash"> hash of file found in catalog. </param>
/// <param name="excludedPatterns"> skip file from validation if it matches these patterns </param>
/// <param name="catalogHashes"> collection of hashes of catalog </param>
/// <returns> void </returns>
internal static void ProcessCatalogFile(string relativePath, string fileHash, WildcardPattern[] excludedPatterns, ref Dictionary<String, String> catalogHashes)
{
// Found the attribute we are looking for
_cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.FoundFileHashInCatlogItem, relativePath, fileHash));
// Only add the file for validation if it does not meet exclusion criteria
if (!CheckExcludedCriteria((new FileInfo(relativePath)).Name, excludedPatterns))
{
// Add relativePath mapping to hashvalue for each file
catalogHashes.Add(relativePath, fileHash);
}
else
{
// Verbose about skipping file from catalog
_cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.SkipValidationOfCatalogFile, relativePath));
}
}
/// <summary>
/// Process file in path for its relative paths
/// </summary>
/// <param name="fileToHash"> file to hash </param>
/// <param name="dirInfo"> directory information about file needed to calculate relative file path. </param>
/// <param name="hashAlgorithm"> Used to calculate Hash </param>
/// <param name="excludedPatterns"> skip file if it matches these patterns </param>
/// <param name="fileHashes"> collection of hashes of files </param>
/// <returns> void </returns>
internal static void ProcessPathFile(FileInfo fileToHash, DirectoryInfo dirInfo, string hashAlgorithm, WildcardPattern[] excludedPatterns, ref Dictionary<String, String> fileHashes)
{
string relativePath = string.Empty;
string exclude = string.Empty;
if (dirInfo != null)
{
//Relative path of the file is the path inside the containing folder excluding folder Name
relativePath = fileToHash.FullName.Substring(dirInfo.FullName.Length).TrimStart('\\');
exclude = fileToHash.Name;
}
else
{
relativePath = fileToHash.Name;
exclude = relativePath;
}
if (!CheckExcludedCriteria(exclude, excludedPatterns))
{
string fileHash = string.Empty;
if(fileToHash.Length != 0)
{
fileHash = CalculateFileHash(fileToHash.FullName, hashAlgorithm);
}
if (!fileHashes.ContainsKey(relativePath))
{
fileHashes.Add(relativePath, fileHash);
_cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.FoundFileInPath, relativePath, fileHash));
}
else
{
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFilesRelativePath, relativePath)), "FoundDuplicateFilesRelativePath", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
}
}
else
{
// Verbose about skipping file from path
_cmdlet.WriteVerbose(StringUtil.Format(CatalogStrings.SkipValidationOfPathFile, relativePath));
}
}
/// <summary>
/// Generate the hashes of all the files in given folder
/// </summary>
///
/// <param name="folderPaths"> Path to folder or File </param>
/// <param name="catalogFilePath"> catalog file path it should be skipped when calculating the hashes </param>
/// <param name="hashAlgorithm"> Used to calculate Hash </param>
/// <param name="excludedPatterns"></param>
/// <returns> Dictionary mapping file relative paths to hashes. </returns>
internal static Dictionary<String, String> CalculateHashesFromPath(Collection<string> folderPaths, string catalogFilePath, string hashAlgorithm, WildcardPattern[] excludedPatterns)
{
// Create a HashTable of file Hashes
Dictionary<String, String> fileHashes = new Dictionary<String, String>(StringComparer.CurrentCultureIgnoreCase);
foreach (string folderPath in folderPaths)
{
if (System.IO.Directory.Exists(folderPath))
{
var directoryItems = Directory.EnumerateFiles(folderPath, "*.*", SearchOption.AllDirectories);
foreach (string fileItem in directoryItems)
{
// if its the catalog file we are validating we will skip it
if (String.Equals(fileItem, catalogFilePath, StringComparison.OrdinalIgnoreCase))
continue;
ProcessPathFile(new FileInfo(fileItem), new DirectoryInfo(folderPath), hashAlgorithm, excludedPatterns, ref fileHashes);
}
}
else if (System.IO.File.Exists(folderPath))
{
ProcessPathFile(new FileInfo(folderPath), null , hashAlgorithm, excludedPatterns, ref fileHashes);
}
}
return fileHashes;
}
/// <summary>
/// Compare Dictionary objects
/// </summary>
///
/// <param name="catalogItems"> Hashes extracted from Catalog </param>
///
/// <param name="pathItems"> Hashes created from folders path </param>
///
/// <returns> True if both collections are same </returns>
internal static bool CompareDictionaries(Dictionary<String, String> catalogItems, Dictionary<String, String> pathItems)
{
bool Status = true;
List<string> relativePathsFromFolder = pathItems.Keys.ToList();
List<string> relativePathsFromCatalog = catalogItems.Keys.ToList();
// Find entires those are not in both list lists. These should be empty lists for success
// Hashes in Catalog should be exact similar to the ones from folder
List<string> relativePathsNotInFolder = relativePathsFromFolder.Except(relativePathsFromCatalog, StringComparer.CurrentCultureIgnoreCase).ToList();
List<string> relativePathsNotInCatalog = relativePathsFromCatalog.Except(relativePathsFromFolder, StringComparer.CurrentCultureIgnoreCase).ToList();
//Found extra hashes in Folder
if ((relativePathsNotInFolder.Count != 0) || (relativePathsNotInCatalog.Count != 0))
{
Status = false;
}
foreach (KeyValuePair<String, String> item in catalogItems)
{
string catalogHashValue = (string)catalogItems[item.Key];
if (pathItems.ContainsKey(item.Key))
{
string folderHashValue = (string)pathItems[item.Key];
if (folderHashValue.Equals(catalogHashValue))
{
continue;
}
else
{
Status = false;
}
}
}
return Status;
}
/// <summary>
/// To Validate the Integrity of Catalog
/// </summary>
///
/// <param name="catalogFolders"> Folder for which catalog is created </param>
/// <param name="catalogFilePath"> File Name of the Catalog </param>
/// <param name="excludedPatterns"></param>
/// <param name="cmdlet"> Instance of cmdlet calling this method </param>
/// <returns> Information about Catalog </returns>
internal static CatalogInformation ValidateCatalog(PSCmdlet cmdlet, Collection<string> catalogFolders, String catalogFilePath, WildcardPattern[] excludedPatterns)
{
_cmdlet = cmdlet;
int catalogVersion = 0;
Dictionary<String, String> catalogHashes = GetHashesFromCatalog(catalogFilePath, excludedPatterns, out catalogVersion);
string hashAlgorithm = GetCatalogHashAlgorithm(catalogVersion);
if (!String.IsNullOrEmpty(hashAlgorithm))
{
Dictionary<String, String> fileHashes = CalculateHashesFromPath(catalogFolders, catalogFilePath, hashAlgorithm, excludedPatterns);
CatalogInformation catalog = new CatalogInformation();
catalog.CatalogItems = catalogHashes;
catalog.PathItems = fileHashes;
bool status = CompareDictionaries(catalogHashes, fileHashes);
if (status == true)
{
catalog.Status = CatalogValidationStatus.Valid;
}
else
{
catalog.Status = CatalogValidationStatus.ValidationFailed;
}
catalog.HashAlgorithm = hashAlgorithm;
catalog.Signature = SignatureHelper.GetSignature(catalogFilePath, null);
return catalog;
}
return null;
}
/// <summary>
/// Check if file meets the skip validation criteria
/// </summary>
/// <param name="filename"></param>
/// <param name="excludedPatterns"></param>
/// <returns>True if match is found else false</returns>
internal static bool CheckExcludedCriteria(string filename, WildcardPattern[] excludedPatterns)
{
if (excludedPatterns != null)
{
foreach (WildcardPattern patternItem in excludedPatterns)
{
if (patternItem.IsMatch(filename))
{
return true;
}
}
}
return false;
}
/// <summary>
/// Call back when error is thrown by catalog API's
/// </summary>
private static void ParseErrorCallback(DWORD dwErrorArea, DWORD dwLocalError, string pwszLine)
{
switch (dwErrorArea)
{
case NativeConstants.CRYPTCAT_E_AREA_HEADER: break;
case NativeConstants.CRYPTCAT_E_AREA_MEMBER: break;
case NativeConstants.CRYPTCAT_E_AREA_ATTRIBUTE: break;
default: break;
}
switch (dwLocalError)
{
case NativeConstants.CRYPTCAT_E_CDF_MEMBER_FILE_PATH:
{
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToFindFileNameOrPathForCatalogMember, pwszLine)), "UnableToFindFileNameOrPathForCatalogMember", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
break;
}
case NativeConstants.CRYPTCAT_E_CDF_MEMBER_INDIRECTDATA:
{
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToCreateFileHash, pwszLine)), "UnableToCreateFileHash", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
break;
}
case NativeConstants.CRYPTCAT_E_CDF_MEMBER_FILENOTFOUND:
{
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.UnableToFindFileToHash, pwszLine)), "UnableToFindFileToHash", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
break;
}
case NativeConstants.CRYPTCAT_E_CDF_BAD_GUID_CONV: break;
case NativeConstants.CRYPTCAT_E_CDF_ATTR_TYPECOMBO: break;
case NativeConstants.CRYPTCAT_E_CDF_ATTR_TOOFEWVALUES: break;
case NativeConstants.CRYPTCAT_E_CDF_UNSUPPORTED: break;
case NativeConstants.CRYPTCAT_E_CDF_DUPLICATE:
{
ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(StringUtil.Format(CatalogStrings.FoundDuplicateFileMemberInCatalog, pwszLine)), "FoundDuplicateFileMemberInCatalog", ErrorCategory.InvalidOperation, null);
_cmdlet.ThrowTerminatingError(errorRecord);
break;
}
case NativeConstants.CRYPTCAT_E_CDF_TAGNOTFOUND: break;
default: break;
}
}
}
}
#endif
+4
View File
@@ -0,0 +1,4 @@
{
"monad/tests/MultiMachine/Tests/Commands/Cmdlets/Microsoft.PowerShell.Management/Pester.Commands.Cmdlets.Management.TimeZone.Tests.ps1": "powershell/Modules/Microsoft.PowerShell.Management/TimeZone.Tests.ps1",
"monad/tests/MultiMachine/Tests/Commands/Cmdlets/Microsoft.PowerShell.Security/Pester.Commands.Cmdlets.CatalogTests.ps1": "powershell/Modules/Microsoft.PowerShell.Security/FileCatalog.Tests.ps1"
}
@@ -1,4 +1,4 @@
if ($IsWindows) {
if ($IsWindows -and !$IsCore) {
#check to see whether we're running as admin in Windows...
$windowsIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$windowsPrincipal = new-object 'Security.Principal.WindowsPrincipal' $windowsIdentity
@@ -28,4 +28,4 @@ if ($IsWindows) {
} catch {$_.FullyQualifiedErrorId | Should Be "Microsoft.PowerShell.Commands.ClearEventLogCommand"}
}
}
}
}
@@ -1,4 +1,4 @@
if ($IsWindows) {
if ($IsWindows -and !$IsCore) {
#check to see whether we're running as admin in Windows...
$windowsIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$windowsPrincipal = new-object 'Security.Principal.WindowsPrincipal' $windowsIdentity
@@ -1,4 +1,4 @@
if ($IsWindows) {
if ($IsWindows -and !$IsCore) {
#check to see whether we're running as admin in Windows...
$windowsIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$windowsPrincipal = new-object 'Security.Principal.WindowsPrincipal' $windowsIdentity
@@ -44,4 +44,4 @@
$result.EventID | Should be 4
}
}
}
}
@@ -1,4 +1,4 @@
if ($IsWindows) {
if ($IsWindows -and !$IsCore) {
#check to see whether we're running as admin in Windows...
$windowsIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$windowsPrincipal = new-object 'Security.Principal.WindowsPrincipal' $windowsIdentity
@@ -28,4 +28,4 @@
} catch {$_.FullyQualifiedErrorId | Should Be "System.InvalidOperationException,Microsoft.PowerShell.Commands.GetEventLogCommand"}
}
}
}
}
@@ -0,0 +1,227 @@
# This is a Pester test suite to validate the cmdlets in the TimeZone module
#
# Copyright (c) Microsoft Corporation, 2016
<#
--------------------------------------
Script Notes: opportunities for improving this test script
--------------------------------------
Localization
Many of the tests below looking-up timezones by Name do not support localization.
That is, the current tests use us english versions of StandardName and DaylighName for tests.
ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms725481.aspx
[snippet] Both StandardName and DaylightName are localized according to the current user default UI language.
#>
if ($IsWindows) {
function Assert-ListsSame
{
param([object[]] $expected, [object[]] $observed )
$compResult = Compare-Object $observed $expected | Select-Object -ExpandProperty InputObject
if ($compResult)
{
$observedList = ([string]::Join("|",$observed))
$expectedList = ([string]::Join("|",$expected))
$observedList | Should Be $expectedList
}
}
Describe "Get-Timezone test case no switches" -tags 'BVT' {
It "Call without ListAvailable switch returns current TimeZoneInfo" {
$observed = (Get-TimeZone).Id
$expected = ([System.TimeZoneInfo]::Local).Id
$observed -eq $expected | Should Be $true
}
}
Describe "Get-Timezone test cases" -tags 'Innerloop','RI' {
It "Call without ListAvailable switch returns an object of type TimeZoneInfo" {
$result = (Get-TimeZone).GetType().Name
$result | Should Be "TimeZoneInfo"
}
It "Call WITH ListAvailable switch returns ArrayList of TimeZoneInfo objects where the list is greater than 0 item" {
$list = Get-TimeZone -ListAvailable
$list.Count -gt 0 | Should Be $true
$list.GetType().Name | Should Be "Object[]"
$list[0].GetType().Name | Should Be "TimeZoneInfo"
}
It "Call with ListAvailable switch returns a list containing TimeZoneInfo.Local" {
$observedIdList = Get-TimeZone -ListAvailable | select -ExpandProperty Id
$oneExpectedId = ([System.TimeZoneInfo]::Local).Id
$observedIdList -contains $oneExpectedId | Should Be $true
}
It "Call with ListAvailable switch returns a list containing one returned by Get-TimeZone" {
$observedIdList = Get-TimeZone -ListAvailable | select -ExpandProperty Id
$oneExpectedId = (Get-TimeZone).Id
$observedIdList -contains $oneExpectedId | Should Be $true
}
It "Call Get-TimeZone using ID param and single item" {
(Get-TimeZone -Id "Cape Verde Standard Time").Id -eq "Cape Verde Standard Time" | Should Be $true
}
It "Call Get-TimeZone using ID param and multiple items" {
$idList = @("Cape Verde Standard Time","Morocco Standard Time","Azores Standard Time")
$result = (Get-TimeZone -Id $idList).Id
Assert-ListsSame $result $idList
}
It "Call Get-TimeZone using ID param and multiple items, where first and third are invalid ids - expect error" {
$null = Get-TimeZone -Id @("Cape Verde Standard","Morocco Standard Time","Azores Standard") `
-ErrorVariable errVar -ErrorAction SilentlyContinue
$errVar.Count -eq 2 | Should Be $true
$errVar[0].FullyQualifiedErrorID | Should Be "TimeZoneNotFound,Microsoft.PowerShell.Commands.GetTimeZoneCommand"
}
It "Call Get-TimeZone using ID param and multiple items, one is wild card but error action ignore works as expected" {
$result = Get-TimeZone -Id @("Cape Verde Standard Time","Morocco Standard Time","*","Azores Standard Time") `
-ErrorAction SilentlyContinue | % Id
$expectedIdList = @("Cape Verde Standard Time","Morocco Standard Time","Azores Standard Time")
Assert-ListsSame $expectedIdList $result
}
It "Call Get-TimeZone using Name param and singe item" {
$timezoneList = Get-TimeZone -ListAvailable
$timezoneName = $timezoneList[0].StandardName
$observed = Get-TimeZone -Name $timezoneName
$observed.StandardName -eq $timezoneName | Should Be $true
}
It "Call Get-TimeZone using Name param with wild card" {
$result = (Get-TimeZone -Name "Pacific*").Id
$expectedIdList = @("Pacific Standard Time (Mexico)","Pacific Standard Time","Pacific SA Standard Time")
Assert-ListsSame $expectedIdList $result
}
It "Verify that alias 'gtz' exists" {
(Get-Alias -Name "gtz").Name | Should Be "gtz"
}
It "Call Get-TimeZone Name parameter from pipeline by value " {
$result = ("Pacific*" | Get-TimeZone).Id
$expectedIdList = @("Pacific Standard Time (Mexico)","Pacific Standard Time","Pacific SA Standard Time")
Assert-ListsSame $expectedIdList $result
}
It "Call Get-TimeZone Id parameter from pipeline by ByPropertyName" {
$timezoneList = Get-TimeZone -ListAvailable
$timezone = $timezoneList[0]
$observed = $timezone | Get-TimeZone
$observed.StandardName -eq $timezone.StandardName | Should Be $true
}
}
Describe "Set-Timezone test case: call by single Id" -tags 'BVT' {
$originalTimeZoneId
BeforeAll {
$originalTimeZoneId = (Get-TimeZone).Id
}
AfterAll {
Set-TimeZone -ID $originalTimeZoneId
}
It "Call Set-TimeZone by Id" {
$origTimeZoneID = (Get-TimeZone).Id
$timezoneList = Get-TimeZone -ListAvailable
$testTimezone = $null
foreach($timezone in $timezoneList)
{
if ($timezone.Id -ne $origTimeZoneID)
{
$testTimezone = $timezone
break
}
}
Set-TimeZone -Id $testTimezone.Id
$observed = Get-TimeZone
$testTimezone.Id -eq $observed.Id | Should Be $true
}
}
Describe "Set-Timezone test cases" -tags 'Innerloop','RI' {
$originalTimeZoneId
BeforeAll {
$originalTimeZoneId = (Get-TimeZone).Id
}
AfterAll {
Set-TimeZone -ID $originalTimeZoneId
}
It "Call Set-TimeZone with invalid Id" {
$exception = $null
try { Set-TimeZone -Id "zzInvalidID" } catch { $exception = $_ }
$exception.FullyQualifiedErrorID | Should Be "TimeZoneNotFound,Microsoft.PowerShell.Commands.SetTimeZoneCommand"
}
It "Call Set-TimeZone by Name" {
$origTimeZoneName = (Get-TimeZone).StandardName
$timezoneList = Get-TimeZone -ListAvailable
$testTimezone = $null
foreach($timezone in $timezoneList)
{
if ($timezone.StandardName -ne $origTimeZoneName)
{
$testTimezone = $timezone
break
}
}
Set-TimeZone -Name $testTimezone.StandardName
$observed = Get-TimeZone
$testTimezone.StandardName -eq $observed.StandardName | Should Be $true
}
It "Call Set-TimeZone with invalid Name" {
$exception = $null
try { Set-TimeZone -Name "zzINVALID_Name" } catch { $exception = $_ }
$exception.FullyQualifiedErrorID | Should Be "TimeZoneNotFound,Microsoft.PowerShell.Commands.SetTimeZoneCommand"
}
It "Verify that alias 'stz' exists" {
(Get-Alias -Name "stz").Name | Should Be "stz"
}
It "Call Set-TimeZone from pipeline input object of type TimeZoneInfo" {
$origTimeZoneID = (Get-TimeZone).Id
$timezoneList = Get-TimeZone -ListAvailable
$testTimezone = $null
foreach($timezone in $timezoneList)
{
if ($timezone.Id -ne $origTimeZoneID)
{
$testTimezone = $timezone
break
}
}
$testTimezone | Set-TimeZone
$observed = Get-TimeZone
$observed.ID -eq $testTimezone.Id | Should Be $true
}
It "Call Set-TimeZone from pipeline input object of type TimeZoneInfo, verify supports whatif" {
$origTimeZoneID = (Get-TimeZone).Id
$timezoneList = Get-TimeZone -ListAvailable
$testTimezone = $null
foreach($timezone in $timezoneList)
{
if ($timezone.Id -ne $origTimeZoneID)
{
$testTimezone = $timezone
break
}
}
Set-TimeZone -Id $testTimezone.Id -WhatIf > $null
$observed = Get-TimeZone
$observed.Id -eq $origTimeZoneID | Should Be $true
}
}
}
@@ -0,0 +1,381 @@
# This is a Pester test suite to validate the New-FileCatalog & Test-FileCatalog cmdlets on PowerShell Core.
#
# Copyright (c) Microsoft Corporation, 2016
#
if ($IsWindows) {
$script:catalogPath = ""
Describe "Test suite for NewFileCatalogAndTestFileCatalogCmdlets" -Tags "Innerloop","BVT" {
#compare two hashtables
function CompareHashTables
{
param
(
$hashTable1,
$hashTable2
)
foreach ($key in $hashTable1.keys)
{
$keyValue1 = $hashTable1["$key"]
if($hashTable2.ContainsKey($key))
{
$keyValue2 = $hashTable2["$key"]
$keyValue1 | Should Be $keyValue2
}
else
{
throw "Failed to find the file $keyValue1 for $key in Hashtable"
}
}
}
BeforeAll {
$testDataPath = "$PSScriptRoot\TestData\CatalogTestData"
}
Context "NewAndTestCatalogTests PositiveTestCases when validation Succeeds" {
It "NewFileCatalogFolder" {
$sourcePath = Join-Path $testDataPath 'UserConfigProv\DSCResources\scriptdsc'
$catalogPath = "$testDataPath\NewFileCatalogFolder.cat"
try
{
$null = New-FileCatalog -Path $sourcePath -CatalogFilePath $catalogPath -CatalogVersion 1.0
$result = Test-FileCatalog -Path $sourcePath -CatalogFilePath $catalogPath -Detailed
}
finally
{
Remove-Item $catalogPath -Force -ErrorAction SilentlyContinue
}
# Validate result properties
$result.Status | Should Be "Valid"
$result.Signature.Status | Should Be "NotSigned"
$result.HashAlgorithm | Should Be "SHA1"
}
It "NewFileCatalogFolderWithSubFolders" {
$sourcePath = Join-Path $testDataPath 'UserConfigProv'
# use non existant Path for the directory when .cat file name is specified
$catalogPath = "$testDataPath\OutPutCatalog\NewFileCatalogFolderWithSubFolders.cat"
try
{
$null = New-FileCatalog -Path $sourcePath -CatalogFilePath $catalogPath
$result = Test-FileCatalog -Path $sourcePath -CatalogFilePath $catalogPath -Detailed
}
finally
{
Remove-Item "$sourcePath\OutPutCatalog" -Force -ErrorAction SilentlyContinue -Recurse
}
# Validate result properties
$result.Status | Should Be "Valid"
$result.Signature.Status | Should Be "NotSigned"
$result.HashAlgorithm | Should Be "SHA1"
}
It "NewFileCatalogWithSingleFile" {
$sourcePath = Join-Path $testDataPath '\CatalogTestFile1.mof'
# use existant Path for the directory when .cat file name is not specified
$catalogPath = $testDataPath
try
{
$null = New-FileCatalog -Path $sourcePath -CatalogFilePath $catalogPath
$result = Test-FileCatalog -Path $sourcePath -CatalogFilePath ($catalogPath + "\catalog.cat")
}
finally
{
Remove-Item "$catalogPath\catalog.cat" -Force -ErrorAction SilentlyContinue
}
# Validate result properties
$result | Should Be "Valid"
}
It "NewFileCatalogForFilesThatDoNotSupportEmbeddedSignatures" {
$expectedPathsAndHashes = @{ "TestImage.gif" = "B0E4B9F0BB21284AA0AF0D525C913420AD73DA6A" ;
"TestFileCatalog.txt" = "925834D22A8AEB8E0A5EAFABC739F8AFAAD3E490" }
# use non existant Path for the directory when .cat file name is not specified
$catalogPath = "$testDataPath\OutPutCatalog"
try
{
$null = New-FileCatalog -Path "$testDataPath\TestImage.gif","$testDataPath\TestFileCatalog.txt" -CatalogFilePath $catalogPath -CatalogVersion 1.0
$result = Test-FileCatalog -Path "$testDataPath\TestImage.gif","$testDataPath\TestFileCatalog.txt" -CatalogFilePath ($catalogPath + "\catalog.cat") -Detailed
}
finally
{
Remove-Item "$catalogPath" -Force -ErrorAction SilentlyContinue -Recurse
}
$result.Status | Should Be "Valid"
$result.CatalogItems.Count | Should Be 2
$result.PathItems.Count | Should Be 2
CompareHashTables $result.CatalogItems $result.PathItems
CompareHashTables $result.CatalogItems $expectedPathsAndHashes
}
It "NewFileCatalogWithMutipleFoldersAndFiles" -Skip:$true {
$expectedPathsAndHashes = @{
"UserConfigProv.psd1" = "748E5486814051DA3DFB79FE8964152727213248" ;
"DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.schema.mof" ="F7CAB050E32CF0C9B2AC2807C4F24D31EFCC8B61";
"dscresources\UserConfigProviderModVersion3\UserConfigProviderModVersion3.psm1" = "F9DD6B02C7BD0FB98A25BE0D41210B2A2333E139";
"DSCResources\scriptdsc\scriptDSC.schema.psm1"= "CDBAF85FEDE2E0CD09B1AEA0532010CEFCECBC12";
"DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1" = "7599777B85B60377B1F3E492C817190090A754A7"
"DSCResources\scriptdsc\scriptdsc.psd1"= "CDDC68AF9B863760A14031772DC9ADDAFD209D80";
"DSCResources\UserConfigProviderModVersion3\UserConfigProviderModVersion3.schema.mof" ="AFEB46104F506FC64CAB4B0B2A9C6C50622B487A";
"DSCResources\UserConfigProviderModVersion2\UserConfigProviderModVersion2.psm1"= "60CB9C8AEDA7A64127D34361ED4F30DEAFE37022";
"DSCResources\UserConfigProviderModVersion2\UserConfigProviderModVersion2.schema.mof" = "E33FBFEA28E9A8FBA793FBC3D8015BCC9A10944B";
"CatalogTestFile1.mof" = "083B0953D0D70FFF62710F0356FEB86BCE327FE7";
"CatalogTestFile2.xml" = "E73BB7A0DD9FAC6A8182F67B750D9CA3094490F1" }
$catalogPath = "$env:TEMP\NewFileCatalogWithMutipleFoldersAndFiles.cat"
$catalogDataPath = @("$testDataPath\UserConfigProv\","$testDataPath\CatalogTestFile1.mof","$testDataPath\CatalogTestFile2.xml")
try
{
$null =New-FileCatalog -Path $catalogDataPath -CatalogFilePath $catalogPath -CatalogVersion 1.0
$result = Test-FileCatalog -Path $catalogDataPath -CatalogFilePath $catalogPath -Detailed
}
finally
{
Remove-Item "$catalogPath" -Force -ErrorAction SilentlyContinue
}
$result.Status | Should Be "Valid"
$result.Signature.Status | Should Be "NotSigned"
$result.HashAlgorithm | Should Be "SHA1"
$result.CatalogItems.Count | Should Be 11
$result.PathItems.Count | Should Be 11
CompareHashTables $result.CatalogItems $result.PathItems
CompareHashTables $result.CatalogItems $expectedPathsAndHashes
}
It "NewFileCatalogVersion2WithMutipleFoldersAndFiles" -Skip:$true {
$expectedPathsAndHashes = @{
"UserConfigProv.psd1" = "9FFE4CA2873CD91CDC9D71362526446ECACDA64D26DEA768E6CE489B84D888E4" ;
"DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.schema.mof" ="517F625CB6C465928586F5C613F768B33C20F477DAF843C179071B8C74B992AA";
"DSCResources\UserConfigProviderModVersion3\UserConfigProviderModVersion3.psm1" = "0774A539E73B1A480E38CFFE2CF0B8AC46120A0B2E0377E0DE2630031BE83347";
"DSCResources\scriptdsc\scriptdsc.schema.psm1"= "7DE80DED0F96FA7D34CF34089A1B088E91CD7B1D80251949FC7C78A6308D51C3";
"DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1" = "EB0310C630EDFDFBDD1D993A636EC9B75BB1F04DF7E7FFE39CF6357679C852C7"
"DSCResources\scriptdsc\scriptdsc.psd1"= "AB8E8D0840D4854CDCDE25058872413AF417FC016BD77FD5EC677BBB7393532B";
"DSCResources\UserConfigProviderModVersion3\UserConfigProviderModVersion3.schema.mof" ="7163E607F067A3C4F91D3AFF3C466ECA47C0CF84B5F0DDA22B2C0E99929B5E21";
"DSCResources\UserConfigProviderModVersion2\UserConfigProviderModVersion2.psm1"= "6591FE02528D7FB66F00E09D7F1A025D5D5BAF30A49C5FF1EC562FAE39B38F43";
"DSCResources\UserConfigProviderModVersion2\UserConfigProviderModVersion2.schema.mof" = "679318201B012CC5936B29C095956B2131FAF828C0CCA4342A5914F721480FB9";
"CatalogTestFile1.mof" = "7C1885AE5F76F58DAA232A5E962875F90308C3CB8580400EE12F999B4E10F940";
"CatalogTestFile2.xml" = "00B7DA28CD285F796660D36B77B2EC6054F21A44D5B329EB6BC4EC7687D70B13";
"TestImage.gif" = "2D938D255D0D6D547747BD21447CF7295318D34D9B4105D04C1C27487D2FF402" }
$catalogPath = "$env:TEMP\NewFileCatalogVersion2WithMutipleFoldersAndFiles.cat"
$catalogDataPath = @("$testDataPath\UserConfigProv\","$testDataPath\CatalogTestFile1.mof","$testDataPath\CatalogTestFile2.xml", "$testDataPath\TestImage.gif")
try
{
$null = New-FileCatalog -Path $catalogDataPath -CatalogFilePath $catalogPath -CatalogVersion 2.0
$result = Test-FileCatalog -Path $catalogDataPath -CatalogFilePath $catalogPath -Detailed
}
finally
{
Remove-Item "$catalogPath" -Force -ErrorAction SilentlyContinue
}
$result.Status | Should Be "Valid"
$result.Signature.Status | Should Be "NotSigned"
$result.HashAlgorithm | Should Be "SHA256"
$result.CatalogItems.Count | Should Be 12
$result.PathItems.Count | Should Be 12
CompareHashTables $result.CatalogItems $result.PathItems
CompareHashTables $result.CatalogItems $expectedPathsAndHashes
}
It "NewFileCatalogFolderWhenCatlogFileIsCreatedInsideSameFolder" {
$catalogPath = "$env:TEMP\UserConfigProv\NewFileCatalogFolderWhenCatlogFileIsCreatedInsideSameFolder.cat"
try
{
copy-item $testDataPath\UserConfigProv $env:temp -Recurse -ErrorAction SilentlyContinue
$null = New-FileCatalog -Path $env:temp\UserConfigProv\ -CatalogFilePath $catalogPath -CatalogVersion 1.0
$result = Test-FileCatalog -Path $env:temp\UserConfigProv\ -CatalogFilePath $catalogPath
}
finally
{
Remove-Item "$catalogPath" -Force -ErrorAction SilentlyContinue
Remove-Item "$env:temp\UserConfigProv\" -Force -ErrorAction SilentlyContinue -Recurse
}
$result | Should Be "Valid"
}
It "NewFileCatalogWithUnicodeCharactersInFileNames" -Skip:$true {
$expectedPathsAndHashes = @{
"UserConfigProv.psd1" = "9FFE4CA2873CD91CDC9D71362526446ECACDA64D26DEA768E6CE489B84D888E4" ;
"DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.schema.mof" ="517F625CB6C465928586F5C613F768B33C20F477DAF843C179071B8C74B992AA";
"DSCResources\UserConfigProviderModVersion3\UserConfigProviderModVersion3.psm1" = "0774A539E73B1A480E38CFFE2CF0B8AC46120A0B2E0377E0DE2630031BE83347";
"DSCResources\scriptdsc\scriptdsc.schema.psm1"= "7DE80DED0F96FA7D34CF34089A1B088E91CD7B1D80251949FC7C78A6308D51C3";
"DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1" = "EB0310C630EDFDFBDD1D993A636EC9B75BB1F04DF7E7FFE39CF6357679C852C7"
"DSCResources\scriptdsc\scriptdsc.psd1"= "AB8E8D0840D4854CDCDE25058872413AF417FC016BD77FD5EC677BBB7393532B";
"DSCResources\UserConfigProviderModVersion3\UserConfigProviderModVersion3.schema.mof" ="7163E607F067A3C4F91D3AFF3C466ECA47C0CF84B5F0DDA22B2C0E99929B5E21";
"DSCResources\UserConfigProviderModVersion2\UserConfigProviderModVersion2.psm1"= "6591FE02528D7FB66F00E09D7F1A025D5D5BAF30A49C5FF1EC562FAE39B38F43";
"DSCResources\UserConfigProviderModVersion2\UserConfigProviderModVersion2.schema.mof" = "679318201B012CC5936B29C095956B2131FAF828C0CCA4342A5914F721480FB9";
"ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ.txt" = "EFD0AE8FF12C7387D51FFC03259B60E06DA012BF7D3B7B9D3480FAB2864846CE";
"ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ.txt" = "9FB57660EDD8DA898A9F1E7F5A36B8B760B4A21625F9968D87A32A55B3546BF9"}
# Create Test Files with unicode characters in names and content
$unicodeTempDir = Join-Path -Path $testDataPath -ChildPath "UnicodeTestDir"
$null = New-Item -ItemType Directory -Path $unicodeTempDir -Force
$null = New-Item -ItemType File -Path "$unicodeTempDir\ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ.txt" -Force -ErrorAction SilentlyContinue
$null = Add-Content -Path "$unicodeTempDir\ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ.txt" -Value "Testing unicode"
$null = Out-File -FilePath "$unicodeTempDir\ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ.txt" -Encoding unicode -InputObject "ɗ ɘ ə ɚ ɛ ɜ ɝ ɞ ɟ ɠ ɡ ɢ ɣ ɤ ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ ɭ ɮ ɯ ɰ ɱ ɲ ɳ ɴ ɵ ɶ ɷ ɸ ɹ ɺ ɻ ɼ ɽ ɾ ɿ ʀ ʁ ʂ ʃ ʄ ʅ" -Append
$null = New-Item -ItemType File -Path "$unicodeTempDir\ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ.txt" -Force -ErrorAction SilentlyContinue
$null = Out-File -FilePath "$unicodeTempDir\ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ.txt" -Encoding unicode -InputObject "ਅ ਆ ਇ ਈ ਉ ਊ ਏ ਐ ਓ ਔ ਕ ਖ ਗ ਘ ਙ ਚ ਛ ਜ ਝ ਞ ਟ ਠ ਡ ਢ ਣ ਤ ਥ ਦ ਧ ਨ ਪ ਫ ਬ ਭ ਮ ਯ ਰ ਲ ਲ਼ ਵ " -Append
$null = Out-File -FilePath "$unicodeTempDir\ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ.txt" -Encoding unicode -InputObject "அ ஆ இ ஈ உ ஊ எ ஏ ஐ ஒ ஓ ஔ க ங ச ஜ ஞ ட ண த ந ன ப ம ய ர ற ல ள ழ வ ஷ ஸ ஹ " -Append
$catalogPath = "$env:TEMP\క ఖ గ ఘ ఙ చ ఛ జ ఝ ఞ.cat"
$catalogDataPath = @("$testDataPath\UserConfigProv\", "$unicodeTempDir\ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ.txt" ,"$unicodeTempDir\ɥ ɦ ɧ ɨ ɩ ɪ ɫ ɬ.txt")
try
{
$null = New-FileCatalog -Path $catalogDataPath -CatalogFilePath $catalogPath -CatalogVersion 2.0
$result = Test-FileCatalog -Path $catalogDataPath -CatalogFilePath $catalogPath -Detailed
}
finally
{
Remove-Item $unicodeTempDir -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item "$catalogPath" -Force -ErrorAction SilentlyContinue
}
$result.Status | Should Be "Valid"
$result.Signature.Status | Should Be "NotSigned"
$result.HashAlgorithm | Should Be "SHA256"
$result.CatalogItems.Count | Should Be 11
$result.PathItems.Count | Should Be 11
CompareHashTables $result.CatalogItems $result.PathItems
CompareHashTables $result.CatalogItems $expectedPathsAndHashes
}
}
Context "NewAndTestCatalogTests NegativeTestCases when creation or validation Fails"{
AfterEach {
Remove-Item "$script:catalogPath" -Force -ErrorAction SilentlyContinue
Remove-Item "$env:temp\UserConfigProv" -Force -Recurse -ErrorAction SilentlyContinue
}
It "TestCatalogWhenNewFileAddedtoFolderBeforeValidation" {
$script:catalogPath = "$env:TEMP\TestCatalogWhenNewFileAddedtoFolderBeforeValidation.cat"
$null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 2.0
$null = copy-item $testDataPath\UserConfigProv $env:temp -Recurse -ErrorAction SilentlyContinue
$null = New-Item $env:temp\UserConfigProv\DSCResources\NewFile.txt -ItemType File
Add-Content $env:temp\UserConfigProv\DSCResources\NewFile.txt -Value "More Data" -force
$result = Test-FileCatalog -Path $env:temp\UserConfigProv -CatalogFilePath $script:catalogPath -Detailed
$result.Status | Should Be "ValidationFailed"
$result.CatalogItems.Count | Should Be 9
$result.PathItems.Count | Should Be 10
$result.CatalogItems.ContainsKey("DSCResources\NewFile.txt") | Should Be $false
$result.PathItems.ContainsKey("DSCResources\NewFile.txt") | Should Be $true
# By Skipping the new added file validation will pass
$result = Test-FileCatalog -Path $env:temp\UserConfigProv -CatalogFilePath $script:catalogPath -Detailed -FilesToSkip "NewFile.txt"
$result.Status | Should Be "Valid"
}
It "TestCatalogWhenNewFileDeletedFromFolderBeforeValidation" {
$script:catalogPath = "$env:TEMP\TestCatalogWhenNewFileDeletedFromFolderBeforeValidation.cat"
$null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 1.0
$null = copy-item $testDataPath\UserConfigProv $env:temp -Recurse -ErrorAction SilentlyContinue
del $env:temp\UserConfigProv\DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1 -force -ErrorAction SilentlyContinue
$result = Test-FileCatalog -Path $env:temp\UserConfigProv -CatalogFilePath $script:catalogPath -Detailed
$result.Status | Should Be "ValidationFailed"
$result.CatalogItems.Count | Should Be 9
$result.PathItems.Count | Should Be 8
$result.CatalogItems.ContainsKey("DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1") | Should Be $true
$result.PathItems.ContainsKey("DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1") | Should Be $false
# By Skipping the deleted file validation will pass
$result = Test-FileCatalog -Path $env:temp\UserConfigProv -CatalogFilePath $script:catalogPath -Detailed -FilesToSkip "UserConfigProviderModVersion1.psm1"
$result.Status | Should Be "Valid"
}
It "TestCatalogWhenFileContentModifiedBeforeValidation" {
$script:catalogPath = "$env:TEMP\TestCatalogWhenFileContentModifiedBeforeValidation.cat"
$null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 1.0
$null = copy-item $testDataPath\UserConfigProv $env:temp -Recurse -ErrorAction SilentlyContinue
Add-Content $env:temp\UserConfigProv\DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1 -Value "More Data" -Force
$result = Test-FileCatalog -Path $env:temp\UserConfigProv -CatalogFilePath $script:catalogPath -Detailed
$result.Status | Should Be "ValidationFailed"
$result.CatalogItems.Count | Should Be 9
$result.PathItems.Count | Should Be 9
$catalogHashValue = $result.CatalogItems["DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1"]
$pathHashValue = $result.PathItems["DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.psm1"]
($catalogHashValue -eq $pathHashValue) | Should Be $false
# By Skipping the file with modifed contents validation will pass
$result = Test-FileCatalog -Path $env:temp\UserConfigProv -CatalogFilePath $script:catalogPath -Detailed -FilesToSkip "UserConfigProviderModVersion1.psm1"
$result.Status | Should Be "Valid"
}
}
Context "TestCatalog Skip Validation Tests"{
AfterEach {
Remove-Item "$script:catalogPath" -Force -ErrorAction SilentlyContinue
}
It "TestCatalogSkipSingleFileDuringValidation" {
$script:catalogPath = "$env:TEMP\TestCatalogSkipSingleFileDuringValidation.cat"
$null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 2.0
$result = Test-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -FilesToSkip "scriptdsc.schema"
$result | Should Be "Valid"
}
It "TestCatalogSkipCertainFileTypeDuringValidation" {
$script:catalogPath = "$env:TEMP\TestCatalogSkipCertainFileTypeDuringValidation.cat"
$null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 2.0
$result = Test-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -FilesToSkip "*.mof"
$result | Should Be "Valid"
}
It "TestCatalogSkipWildCardPatternDuringValidation" {
$script:catalogPath = "$env:TEMP\TestCatalogSkipWildCardPatternDuringValidation.cat"
$null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 1.0
$result = Test-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -FilesToSkip "UserConfigProvider*.psm1"
$result | Should Be "Valid"
}
It "TestCatalogSkipMuitplePattensDuringValidation" {
$script:catalogPath = "$env:TEMP\TestCatalogSkipMuitplePattensDuringValidation.cat"
$null = New-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -CatalogVersion 1.0
$result = Test-FileCatalog -Path $testDataPath\UserConfigProv\ -CatalogFilePath $script:catalogPath -FilesToSkip "*.psd1","UserConfigProviderModVersion2.psm1","*ModVersion1.schema.mof"
$result | Should Be "Valid"
}
}
}
}
@@ -0,0 +1,49 @@
<MachineInventory aliasing="True">
<Machine machineRole="Server">
<Accounts>
<LocalAdmin user="root" password="Bull_dog1" />
</Accounts>
<BMCMachine>No</BMCMachine>
<BranchName>Linux</BranchName>
<BuildNumber>CentOS</BuildNumber>
<BuildType>Fre</BuildType>
<Factory>00000000-0000-0000-0000-000000000000</Factory>
<HyperVEnabled>No</HyperVEnabled>
<IPV4>Yes</IPV4>
<IPV4Address>
<ip>$ipAddress</ip>
</IPV4Address>
<IPV6>Yes</IPV6>
<IPV6Address>
<ip>2001:4898:28:3:215:5dff:feb8:b9b3</ip>
</IPV6Address>
<MachineName>Linux1</MachineName>
<Membership>Workgroup</Membership>
<NetworkLocation>None</NetworkLocation>
<OSArchitecture>X64</OSArchitecture>
<OSBaseLanguage>en-US</OSBaseLanguage>
<Platform>Windows 8 Server</Platform>
<ProcessorArchitecture>X64</ProcessorArchitecture>
<SKU>Server DataCenter</SKU>
<ServicePack>None</ServicePack>
<Subnet>Unknown</Subnet>
<SystemDrive>C:\</SystemDrive>
<VirtualMachine>No</VirtualMachine>
<WSManListeners>
<WSManListener Port="5985" URLPrefix="wsman" UseSSL="False" DefaultListener="True" ServiceType="OMI">
<AuthSchemes>
<AuthScheme>Basic</AuthScheme>
</AuthSchemes>
<Capabilities>
</Capabilities>
</WSManListener>
<WSManListener Port="5986" URLPrefix="wsman" UseSSL="True" ServiceType="OMI">
<AuthSchemes>
<AuthScheme>Basic</AuthScheme>
</AuthSchemes>
<Capabilities>
</Capabilities>
</WSManListener>
</WSManListeners>
</Machine>
</MachineInventory>
@@ -0,0 +1 @@
Text File to test catalog
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

@@ -0,0 +1,61 @@

# The Get-TargetResource cmdlet is used to fetch the desired state of the DSC managed node through a powershell script.
# This cmdlet executes the user supplied script (i.e., the script is responsible for validating the desired state of the
# DSC managed node). The result of the script execution is in the form of a hashtable containing all the inormation
# gathered from the GetScript execution.
function Get-TargetResource
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
$text
)
$result = @{
Text = "Hello from Get!";
}
$result;
}
# The Set-TargetResource cmdlet is used to Set the desired state of the DSC managed node through a powershell script.
# The method executes the user supplied script (i.e., the script is responsible for validating the desired state of the
# DSC managed node). If the DSC managed node requires a restart either during or after the execution of the SetScript,
# the SetScript notifies the PS Infrasturcure by setting the variable $DSCMachineStatus.IsRestartRequired to $true.
function Set-TargetResource
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
$text
)
$path = "$env:SystemDrive\dscTestPath\hello1.txt"
New-Item -Path $path -Type File -force
Add-Content -Path $path -Value $text
}
# The Test-TargetResource cmdlet is used to validate the desired state of the DSC managed node through a powershell script.
# The method executes the user supplied script (i.e., the script is responsible for validating the desired state of the
# DSC managed node). The result of the script execution should be true if the DSC managed machine is in the desired state
# or else false should be returned.
function Test-TargetResource
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
$text
)
$false
}
@@ -0,0 +1,8 @@
[ClassVersion("1.0.0")]
class UserConfigProviderModVersion1 : OMI_BaseResource
{
[key] string Text;
};
@@ -0,0 +1,62 @@

# The Get-TargetResource cmdlet is used to fetch the desired state of the DSC managed node through a powershell script.
# This cmdlet executes the user supplied script (i.e., the script is responsible for validating the desired state of the
# DSC managed node). The result of the script execution is in the form of a hashtable containing all the inormation
# gathered from the GetScript execution.
function Get-TargetResource
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
$text
)
$result = @{
Text = "Hello from Get!";
}
$result;
}
# The Set-TargetResource cmdlet is used to Set the desired state of the DSC managed node through a powershell script.
# The method executes the user supplied script (i.e., the script is responsible for validating the desired state of the
# DSC managed node). If the DSC managed node requires a restart either during or after the execution of the SetScript,
# the SetScript notifies the PS Infrasturcure by setting the variable $DSCMachineStatus.IsRestartRequired to $true.
function Set-TargetResource
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
$text
)
$path = "$env:SystemDrive\dscTestPath\hello2.txt"
New-Item -Path $path -Type File -force
Add-Content -Path $path -Value $text
}
# The Test-TargetResource cmdlet is used to validate the desired state of the DSC managed node through a powershell script.
# The method executes the user supplied script (i.e., the script is responsible for validating the desired state of the
# DSC managed node). The result of the script execution should be true if the DSC managed machine is in the desired state
# or else false should be returned.
function Test-TargetResource
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
$text
)
$false
}
@@ -0,0 +1,8 @@
[ClassVersion("1.0.0")]
class UserConfigProviderModVersion2 : OMI_BaseResource
{
[key] string Text;
};
@@ -0,0 +1,62 @@

# The Get-TargetResource cmdlet is used to fetch the desired state of the DSC managed node through a powershell script.
# This cmdlet executes the user supplied script (i.e., the script is responsible for validating the desired state of the
# DSC managed node). The result of the script execution is in the form of a hashtable containing all the inormation
# gathered from the GetScript execution.
function Get-TargetResource
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
$text
)
$result = @{
Text = "Hello from Get!";
}
$result;
}
# The Set-TargetResource cmdlet is used to Set the desired state of the DSC managed node through a powershell script.
# The method executes the user supplied script (i.e., the script is responsible for validating the desired state of the
# DSC managed node). If the DSC managed node requires a restart either during or after the execution of the SetScript,
# the SetScript notifies the PS Infrasturcure by setting the variable $DSCMachineStatus.IsRestartRequired to $true.
function Set-TargetResource
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
$text
)
$path = "$env:SystemDrive\dscTestPath\hello3.txt"
New-Item -Path $path -Type File -force
Add-Content -Path $path -Value $text
}
# The Test-TargetResource cmdlet is used to validate the desired state of the DSC managed node through a powershell script.
# The method executes the user supplied script (i.e., the script is responsible for validating the desired state of the
# DSC managed node). The result of the script execution should be true if the DSC managed machine is in the desired state
# or else false should be returned.
function Test-TargetResource
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]
$text
)
$false
}
@@ -0,0 +1,8 @@
[ClassVersion("1.0.0")]
class UserConfigProviderModVersion3 : OMI_BaseResource
{
[key] string Text;
};
@@ -0,0 +1,15 @@
configuration scriptdsc
{
param ($one, $two, $three)
Import-DscResource -ModuleName UserConfigProv
Log scriptDscLogResource
{
Message = "This is the message from the embedded log resource: one=$one two=$two three=$three"
}
}
Export-ModuleMember -Function scriptdsc
@@ -0,0 +1,12 @@
@{
ModuleVersion = '3.0.0.1'
Author = 'fayzas'
CompanyName = 'Microsoft Corporation'
Copyright = '(c) 2013 Microsoft Corporation. All rights reserved.'
Description = 'Hello World!'
PowerShellVersion = '3.0'
CLRVersion = '4.0'
RequiredModules = @()
# NestedModules = @("UserConfigProviderModVersion.psm1")
# FunctionsToExport = @("Get-TargetResource", "Set-TargetResource", "Test-TargetResource")
}
@@ -1,6 +1,6 @@
Describe "Compare-Object" {
BeforeAll {
$nl = [Environment]::NewLine
$nl = [Environment]::NewLine
$content1 = "line 1" + $nl + "line 2"
$content2 = "line 1" + $nl + "line 2.1"
@@ -12,10 +12,10 @@ Describe "Compare-Object" {
$file3 = Join-Path -Path $TestDrive -ChildPath "test3.txt"
$file4 = Join-Path -Path $TestDrive -ChildPath "test4.txt"
New-Item $file1 -ItemType file -Value $content1 -Force
New-Item $file2 -ItemType file -Value $content2 -Force
New-Item $file3 -ItemType file -Value $content3 -Force
New-Item $file4 -ItemType file -Value $content4 -Force
$null = New-Item $file1 -ItemType file -Value $content1 -Force
$null = New-Item $file2 -ItemType file -Value $content2 -Force
$null = New-Item $file3 -ItemType file -Value $content3 -Force
$null = New-Item $file4 -ItemType file -Value $content4 -Force
}
It "Should be able to compare the same object using the referenceObject and differenceObject switches" {
@@ -454,4 +454,4 @@ Describe "Compare-Object DRT basic functionality" -Tags DRT{
$result.InputObject | Should Be "refObject"
$result.SideIndicator | Should Be "<="
}
}
}