mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Merge pull request #1285 from PowerShell/dongbo/miss-files-ops
Add back missing files for OPS
This commit is contained in:
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
BIN
Binary file not shown.
+49
@@ -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>
|
||||
+1
@@ -0,0 +1 @@
|
||||
Text File to test catalog
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
+61
@@ -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
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
|
||||
[ClassVersion("1.0.0")]
|
||||
class UserConfigProviderModVersion1 : OMI_BaseResource
|
||||
{
|
||||
[key] string Text;
|
||||
};
|
||||
|
||||
|
||||
+62
@@ -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
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
|
||||
[ClassVersion("1.0.0")]
|
||||
class UserConfigProviderModVersion2 : OMI_BaseResource
|
||||
{
|
||||
[key] string Text;
|
||||
};
|
||||
|
||||
|
||||
+62
@@ -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
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
|
||||
[ClassVersion("1.0.0")]
|
||||
class UserConfigProviderModVersion3 : OMI_BaseResource
|
||||
{
|
||||
[key] string Text;
|
||||
};
|
||||
|
||||
|
||||
BIN
Binary file not shown.
+15
@@ -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
|
||||
+12
@@ -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 "<="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user