Browse Source

fix(links): name the items in the errors and continue after an invalid path

From the security-reviewer pass over be04cb7..4ee01e5 (Minor 1, 2, and
5, Nit 9):

- A path with a character that Windows doesn't allow, such as |, stopped
  the pipeline in Windows PowerShell, where resolving the path throws an
  ArgumentException; both cmdlets now write a non-terminating error with
  the category InvalidArgument, also in PowerShell 7, where AlphaFS
  rejects the path when it creates the link.
- The errors of New-NTFSSymbolicLink for an existing -Path and a missing
  -Target, and of New-NTFSHardLink for a folder as -Target, named no path.
- New-NTFSSymbolicLink checked -Target before -Path, so that the two
  cmdlets reported different errors for an existing -Path with a missing
  -Target; both check -Path first now.
- The pages list objects with Path and Target as input of their own.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
pull/116/head
Raimund Andree 3 days ago
parent
commit
7936d9fc71
  1. 11
      CHANGELOG.md
  2. 10
      Docs/Cmdlets/New-NTFSHardLink.md
  3. 12
      Docs/Cmdlets/New-NTFSSymbolicLink.md
  4. 31
      NTFSSecurity/LinkCmdlets/NewHardLink.cs
  5. 31
      NTFSSecurity/LinkCmdlets/NewSymbolicLink.cs
  6. 30
      NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml
  7. 74
      Tests/Links.Tests.ps1

11
CHANGELOG.md

@ -83,9 +83,10 @@ The format is based on
`New-NTFSSymbolicLink -Path Link` created a link to the current folder
- **Breaking:** write a non-terminating error in `New-NTFSHardLink` and
`New-NTFSSymbolicLink` for a link that they can't create, such as for an
existing `-Path` or a missing `-Target`, and continue with the next link;
they stopped with a terminating error. A script that relies on the stop
needs `-ErrorAction Stop`
existing `-Path`, a missing `-Target`, or a path with a character that
Windows doesn't allow, and continue with the next link; they stopped
with a terminating error. A script that relies on the stop needs
`-ErrorAction Stop`
- Name the computer in the warning of `Get-NTFSEffectiveAccess` when the
computer of `-ServerName` can't be reached
@ -357,5 +358,9 @@ The format is based on
- Fix `New-NTFSHardLink` and `New-NTFSSymbolicLink`, which failed with
`GetDefaultValueFailed` for every object piped to them, such as the rows
of a CSV file with the columns `Path` and `Target`
- Fix the errors of `New-NTFSSymbolicLink` for an existing `-Path` and a
missing `-Target`, and of `New-NTFSHardLink` for a folder as `-Target`,
which named no path. `New-NTFSSymbolicLink` now checks `-Path` first,
like `New-NTFSHardLink`
[Unreleased]: https://github.com/raandree/NTFSSecurity/compare/4.2.6...HEAD

10
Docs/Cmdlets/New-NTFSHardLink.md

@ -128,7 +128,11 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
### System.String
You can pass the path of the new link and the path of the target as strings, or pipe objects whose `Path` or `FullName` property names the new link and whose `Target` property names its target.
You can pass the path of the new link and the path of the target as strings.
### System.Management.Automation.PSObject
You can pipe objects whose `Path` or `FullName` property names the new link and whose `Target` property names its target, such as the rows of a CSV file that `Import-Csv` reads.
## OUTPUTS
@ -146,13 +150,13 @@ Windows supports hard links only for files on the same NTFS volume. A link that
The cmdlet creates hard links on a network share as well, but Windows can't list the names of a file there. With `-PassThru` on a share, the cmdlet creates the link and writes a non-terminating `GetHardLinkError` with the message "The request is not supported" instead of the objects. Before 5.0.0, it stopped with a terminating error after it had created the link.
The cmdlet does not overwrite anything. If `-Path` already exists, or if `-Target` is missing or is a folder, the cmdlet writes a non-terminating `CreateHardLinkError` with the category `ResourceExists`, `ObjectNotFound`, or `InvalidArgument`, leaves the file system unchanged, and continues with the next object from the pipeline. When Windows refuses the link, such as for a target on another volume, the cmdlet writes a `CreateHardLinkError` as well.
The cmdlet does not overwrite anything. If `-Path` already exists, if `-Target` is missing or is a folder, or if a path contains a character that Windows doesn't allow, such as `|`, the cmdlet writes a non-terminating `CreateHardLinkError` with the category `ResourceExists`, `ObjectNotFound`, or `InvalidArgument`, leaves the file system unchanged, and continues with the next object from the pipeline. It checks `-Path` before `-Target`, and the error for an existing `-Path`, a missing `-Target`, or a folder as `-Target` names that path. When Windows refuses the link, such as for a target on another volume, the cmdlet writes a `CreateHardLinkError` as well.
Because all names of a file share the same data, the number of hard links is a property of the file, not of an individual name. Use `Get-NTFSHardLink` to list them, and delete a link with `Remove-Item2` or `Remove-Item`, which removes only that name as long as other names remain.
Before 5.0.0, the error for a missing `-Target` said "The target path exist", the opposite of the cause.
Before 5.0.0-rc7, `-Path` and `-Target` were optional: without `-Path`, the cmdlet failed with an index error, and without `-Target`, it used the current location, which is a folder. It stopped with a terminating error for an existing `-Path`, a missing `-Target`, a folder as `-Target`, or a link that Windows refused, and every object piped to it failed with `GetDefaultValueFailed`.
Before 5.0.0-rc7, `-Path` and `-Target` were optional: without `-Path`, the cmdlet failed with an index error, and without `-Target`, it used the current location, which is a folder. It stopped with a terminating error for an existing `-Path`, a missing `-Target`, a folder as `-Target`, or a link that Windows refused, in Windows PowerShell also for a path with a character that Windows doesn't allow, and every object piped to it failed with `GetDefaultValueFailed`.
## RELATED LINKS

12
Docs/Cmdlets/New-NTFSSymbolicLink.md

@ -21,7 +21,7 @@ New-NTFSSymbolicLink [-Path] <String> [-Target] <String> [-PassThru] [<CommonPar
The `New-NTFSSymbolicLink` cmdlet creates a symbolic link that redirects to another file or folder. `-Path` is the new link that the cmdlet creates, and `-Target` is the existing item that the link points to. Read the command as "create *Path*, which points to *Target*". Both parameters are required.
The cmdlet inspects the target first and creates a file symbolic link when the target is a file and a directory symbolic link when the target is a folder, so you do not select the link type yourself. `-Target` must exist when the link is created, and `-Path` must not exist yet, so the cmdlet never overwrites an existing item.
Before it creates the link, the cmdlet inspects the target and creates a file symbolic link when the target is a file and a directory symbolic link when the target is a folder, so you do not select the link type yourself. `-Target` must exist when the link is created, and `-Path` must not exist yet, so the cmdlet never overwrites an existing item.
Relative paths are resolved against the current location before the link is created, which means that the link always stores an absolute target path.
@ -128,7 +128,11 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
### System.String
You can pass the path of the new link and the path of the target as strings, or pipe objects whose `Path` or `FullName` property names the new link and whose `Target` property names its target.
You can pass the path of the new link and the path of the target as strings.
### System.Management.Automation.PSObject
You can pipe objects whose `Path` or `FullName` property names the new link and whose `Target` property names its target, such as the rows of a CSV file that `Import-Csv` reads.
## OUTPUTS
@ -146,11 +150,11 @@ Creating a symbolic link on Windows requires the "Create symbolic links" user ri
Unlike a hard link, a symbolic link is a separate file system entry that stores a path, so it can point to an item on another volume and the link and its target can be managed independently. The cmdlet still requires the target to exist at the moment the link is created. If the target is removed later, the link remains and stops resolving.
If `-Path` already exists or `-Target` is missing, the cmdlet writes a non-terminating `CreateSymbolicLinkError` with the category `ResourceExists` or `ObjectNotFound`, leaves the file system unchanged, and continues with the next object from the pipeline. When Windows refuses the link, such as with error 1314 without the right to create symbolic links, the cmdlet writes a `CreateSymbolicLinkError` as well.
If `-Path` already exists, `-Target` is missing, or a path contains a character that Windows doesn't allow, such as `|`, the cmdlet writes a non-terminating `CreateSymbolicLinkError` with the category `ResourceExists`, `ObjectNotFound`, or `InvalidArgument`, leaves the file system unchanged, and continues with the next object from the pipeline. It checks `-Path` before `-Target`, and the error for an existing `-Path` or a missing `-Target` names that path. When Windows refuses the link, such as with error 1314 without the right to create symbolic links, the cmdlet writes a `CreateSymbolicLinkError` as well.
Deleting a symbolic link removes the link only and leaves the target untouched. Delete a directory symbolic link as a link rather than recursively, so that the content of the target folder is not affected.
Before 5.0.0-rc7, `-Path` and `-Target` were optional: without `-Path`, the cmdlet failed with an index error, and without `-Target`, it created a link to the current folder. It stopped with a terminating error for an existing `-Path` or a link that Windows refused, and every object piped to it failed with `GetDefaultValueFailed`.
Before 5.0.0-rc7, `-Path` and `-Target` were optional: without `-Path`, the cmdlet failed with an index error, and without `-Target`, it created a link to the current folder. It stopped with a terminating error for an existing `-Path` or a link that Windows refused, in Windows PowerShell also for a path with a character that Windows doesn't allow, and every object piped to it failed with `GetDefaultValueFailed`. It checked `-Target` before `-Path`, and its errors for an existing `-Path` and a missing `-Target` named no path.
## RELATED LINKS

31
NTFSSecurity/LinkCmdlets/NewHardLink.cs

@ -57,8 +57,19 @@ namespace NTFSSecurity
protected override void ProcessRecord()
{
var path = GetRelativePath(paths[0]);
var targetPath = GetRelativePath(target);
string path;
string targetPath;
try
{
path = GetRelativePath(paths[0]);
targetPath = GetRelativePath(target);
}
// Windows PowerShell rejects a character that Windows doesn't allow in a path, such as |, already here.
catch (ArgumentException ex)
{
WriteError(new ErrorRecord(ex, "CreateHardLinkError", ErrorCategory.InvalidArgument, paths[0]));
return;
}
var root = System.IO.Path.GetPathRoot(path);
// Non-terminating errors, so that the links that follow in the pipeline are created as well. Before
@ -80,7 +91,7 @@ namespace NTFSSecurity
if (temp is DirectoryInfo)
{
var folder = new ArgumentException("The target is not a file, cannot create the link");
var folder = new ArgumentException(string.Format("The target '{0}' is not a file, cannot create the link", targetPath));
WriteError(new ErrorRecord(folder, "CreateHardLinkError", ErrorCategory.InvalidArgument, path));
return;
}
@ -91,7 +102,7 @@ namespace NTFSSecurity
}
catch (Exception ex)
{
WriteError(new ErrorRecord(ex, "CreateHardLinkError", ex is UnauthorizedAccessException ? ErrorCategory.PermissionDenied : ErrorCategory.WriteError, path));
WriteError(new ErrorRecord(ex, "CreateHardLinkError", GetErrorCategory(ex), path));
return;
}
@ -123,5 +134,17 @@ namespace NTFSSecurity
{
base.EndProcessing();
}
// In PowerShell 7, AlphaFS rejects a character that Windows doesn't allow in a path only when it creates the link.
internal static ErrorCategory GetErrorCategory(Exception ex)
{
if (ex is UnauthorizedAccessException)
return ErrorCategory.PermissionDenied;
if (ex is ArgumentException)
return ErrorCategory.InvalidArgument;
return ErrorCategory.WriteError;
}
}
}

31
NTFSSecurity/LinkCmdlets/NewSymbolicLink.cs

@ -55,30 +55,39 @@ namespace NTFSSecurity
protected override void ProcessRecord()
{
var path = GetRelativePath(paths[0]);
var targetPath = GetRelativePath(target);
// Non-terminating errors, so that the links that follow in the pipeline are created as well. Before
// 5.0.0-rc7, an existing path and a failure to create the link stopped the pipeline.
FileSystemInfo targetItem = null;
string path;
string targetPath;
try
{
targetItem = GetFileSystemInfo2(targetPath);
path = GetRelativePath(paths[0]);
targetPath = GetRelativePath(target);
}
catch (System.IO.FileNotFoundException ex)
// Windows PowerShell rejects a character that Windows doesn't allow in a path, such as |, already here.
catch (ArgumentException ex)
{
WriteError(new ErrorRecord(ex, "CreateSymbolicLinkError", ErrorCategory.ObjectNotFound, path));
WriteError(new ErrorRecord(ex, "CreateSymbolicLinkError", ErrorCategory.InvalidArgument, paths[0]));
return;
}
// Non-terminating errors, so that the links that follow in the pipeline are created as well. Before
// 5.0.0-rc7, an existing path and a failure to create the link stopped the pipeline, the cmdlet checked
// the target first, and its errors named neither path.
FileSystemInfo temp;
if (TryGetFileSystemInfo2(path, out temp))
{
var exists = new ArgumentException("The path does already exist, cannot create link");
var exists = new ArgumentException(string.Format("The path '{0}' does already exist, cannot create the link", path));
WriteError(new ErrorRecord(exists, "CreateSymbolicLinkError", ErrorCategory.ResourceExists, path));
return;
}
FileSystemInfo targetItem;
if (!TryGetFileSystemInfo2(targetPath, out targetItem))
{
var missing = new System.IO.FileNotFoundException(string.Format("The target '{0}' does not exist, cannot create the link", targetPath), targetPath);
WriteError(new ErrorRecord(missing, "CreateSymbolicLinkError", ErrorCategory.ObjectNotFound, path));
return;
}
try
{
File.CreateSymbolicLink(path, targetPath, targetItem is FileInfo ? SymbolicLinkTarget.File : SymbolicLinkTarget.Directory);
@ -86,7 +95,7 @@ namespace NTFSSecurity
// Without the right to create symbolic links: (1314) A required privilege is not held by the client.
catch (Exception ex)
{
WriteError(new ErrorRecord(ex, "CreateSymbolicLinkError", ex is UnauthorizedAccessException ? ErrorCategory.PermissionDenied : ErrorCategory.WriteError, path));
WriteError(new ErrorRecord(ex, "CreateSymbolicLinkError", NewHardLink.GetErrorCategory(ex), path));
return;
}

30
NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml

@ -7143,7 +7143,15 @@ PS C:\Data&gt; Get-NTFSSecurityDescriptor</dev:code>
<maml:name>System.String</maml:name>
</dev:type>
<maml:description>
<maml:para>You can pass the path of the new link and the path of the target as strings, or pipe objects whose `Path` or `FullName` property names the new link and whose `Target` property names its target.</maml:para>
<maml:para>You can pass the path of the new link and the path of the target as strings.</maml:para>
</maml:description>
</command:inputType>
<command:inputType>
<dev:type>
<maml:name>System.Management.Automation.PSObject</maml:name>
</dev:type>
<maml:description>
<maml:para>You can pipe objects whose `Path` or `FullName` property names the new link and whose `Target` property names its target, such as the rows of a CSV file that `Import-Csv` reads.</maml:para>
</maml:description>
</command:inputType>
</command:inputTypes>
@ -7169,10 +7177,10 @@ PS C:\Data&gt; Get-NTFSSecurityDescriptor</dev:code>
<maml:alert>
<maml:para>Windows supports hard links only for files on the same NTFS volume. A link that points to a file on another volume, or a target on a file system that does not implement hard links, cannot be created.</maml:para>
<maml:para>The cmdlet creates hard links on a network share as well, but Windows can't list the names of a file there. With `-PassThru` on a share, the cmdlet creates the link and writes a non-terminating `GetHardLinkError` with the message "The request is not supported" instead of the objects. Before 5.0.0, it stopped with a terminating error after it had created the link.</maml:para>
<maml:para>The cmdlet does not overwrite anything. If `-Path` already exists, or if `-Target` is missing or is a folder, the cmdlet writes a non-terminating `CreateHardLinkError` with the category `ResourceExists`, `ObjectNotFound`, or `InvalidArgument`, leaves the file system unchanged, and continues with the next object from the pipeline. When Windows refuses the link, such as for a target on another volume, the cmdlet writes a `CreateHardLinkError` as well.</maml:para>
<maml:para>The cmdlet does not overwrite anything. If `-Path` already exists, if `-Target` is missing or is a folder, or if a path contains a character that Windows doesn't allow, such as `|`, the cmdlet writes a non-terminating `CreateHardLinkError` with the category `ResourceExists`, `ObjectNotFound`, or `InvalidArgument`, leaves the file system unchanged, and continues with the next object from the pipeline. It checks `-Path` before `-Target`, and the error for an existing `-Path`, a missing `-Target`, or a folder as `-Target` names that path. When Windows refuses the link, such as for a target on another volume, the cmdlet writes a `CreateHardLinkError` as well.</maml:para>
<maml:para>Because all names of a file share the same data, the number of hard links is a property of the file, not of an individual name. Use `Get-NTFSHardLink` to list them, and delete a link with `Remove-Item2` or `Remove-Item`, which removes only that name as long as other names remain.</maml:para>
<maml:para>Before 5.0.0, the error for a missing `-Target` said "The target path exist", the opposite of the cause.</maml:para>
<maml:para>Before 5.0.0-rc7, `-Path` and `-Target` were optional: without `-Path`, the cmdlet failed with an index error, and without `-Target`, it used the current location, which is a folder. It stopped with a terminating error for an existing `-Path`, a missing `-Target`, a folder as `-Target`, or a link that Windows refused, and every object piped to it failed with `GetDefaultValueFailed`.</maml:para>
<maml:para>Before 5.0.0-rc7, `-Path` and `-Target` were optional: without `-Path`, the cmdlet failed with an index error, and without `-Target`, it used the current location, which is a folder. It stopped with a terminating error for an existing `-Path`, a missing `-Target`, a folder as `-Target`, or a link that Windows refused, in Windows PowerShell also for a path with a character that Windows doesn't allow, and every object piped to it failed with `GetDefaultValueFailed`.</maml:para>
</maml:alert>
</maml:alertSet>
<command:examples>
@ -7246,7 +7254,7 @@ PS C:\Data&gt; Get-NTFSSecurityDescriptor</dev:code>
</command:details>
<maml:description>
<maml:para>The `New-NTFSSymbolicLink` cmdlet creates a symbolic link that redirects to another file or folder. `-Path` is the new link that the cmdlet creates, and `-Target` is the existing item that the link points to. Read the command as "create Path , which points to Target ". Both parameters are required.</maml:para>
<maml:para>The cmdlet inspects the target first and creates a file symbolic link when the target is a file and a directory symbolic link when the target is a folder, so you do not select the link type yourself. `-Target` must exist when the link is created, and `-Path` must not exist yet, so the cmdlet never overwrites an existing item.</maml:para>
<maml:para>Before it creates the link, the cmdlet inspects the target and creates a file symbolic link when the target is a file and a directory symbolic link when the target is a folder, so you do not select the link type yourself. `-Target` must exist when the link is created, and `-Path` must not exist yet, so the cmdlet never overwrites an existing item.</maml:para>
<maml:para>Relative paths are resolved against the current location before the link is created, which means that the link always stores an absolute target path.</maml:para>
<maml:para>To create several links, pipe objects with the properties `Path` and `Target` to the cmdlet, one link per object. For a link that it can't create, the cmdlet writes a non-terminating error and continues with the next object.</maml:para>
<maml:para>By default the cmdlet produces no output. With `-PassThru` it returns an object for the new link: a file object for a link to a file, and a folder object for a link to a folder.</maml:para>
@ -7335,7 +7343,15 @@ PS C:\Data&gt; Get-NTFSSecurityDescriptor</dev:code>
<maml:name>System.String</maml:name>
</dev:type>
<maml:description>
<maml:para>You can pass the path of the new link and the path of the target as strings, or pipe objects whose `Path` or `FullName` property names the new link and whose `Target` property names its target.</maml:para>
<maml:para>You can pass the path of the new link and the path of the target as strings.</maml:para>
</maml:description>
</command:inputType>
<command:inputType>
<dev:type>
<maml:name>System.Management.Automation.PSObject</maml:name>
</dev:type>
<maml:description>
<maml:para>You can pipe objects whose `Path` or `FullName` property names the new link and whose `Target` property names its target, such as the rows of a CSV file that `Import-Csv` reads.</maml:para>
</maml:description>
</command:inputType>
</command:inputTypes>
@ -7361,9 +7377,9 @@ PS C:\Data&gt; Get-NTFSSecurityDescriptor</dev:code>
<maml:alert>
<maml:para>Creating a symbolic link on Windows requires the "Create symbolic links" user right, `SeCreateSymbolicLinkPrivilege`, which is granted to the Administrators group by default. Without that right, Windows rejects the operation with error 1314, "A required privilege is not held by the client", so run the cmdlet from an elevated session or grant the right to the account. Windows Developer Mode doesn't change this: it lets accounts without that right create symbolic links only in programs that request it, such as `mklink`, and the cmdlet doesn't.</maml:para>
<maml:para>Unlike a hard link, a symbolic link is a separate file system entry that stores a path, so it can point to an item on another volume and the link and its target can be managed independently. The cmdlet still requires the target to exist at the moment the link is created. If the target is removed later, the link remains and stops resolving.</maml:para>
<maml:para>If `-Path` already exists or `-Target` is missing, the cmdlet writes a non-terminating `CreateSymbolicLinkError` with the category `ResourceExists` or `ObjectNotFound`, leaves the file system unchanged, and continues with the next object from the pipeline. When Windows refuses the link, such as with error 1314 without the right to create symbolic links, the cmdlet writes a `CreateSymbolicLinkError` as well.</maml:para>
<maml:para>If `-Path` already exists, `-Target` is missing, or a path contains a character that Windows doesn't allow, such as `|`, the cmdlet writes a non-terminating `CreateSymbolicLinkError` with the category `ResourceExists`, `ObjectNotFound`, or `InvalidArgument`, leaves the file system unchanged, and continues with the next object from the pipeline. It checks `-Path` before `-Target`, and the error for an existing `-Path` or a missing `-Target` names that path. When Windows refuses the link, such as with error 1314 without the right to create symbolic links, the cmdlet writes a `CreateSymbolicLinkError` as well.</maml:para>
<maml:para>Deleting a symbolic link removes the link only and leaves the target untouched. Delete a directory symbolic link as a link rather than recursively, so that the content of the target folder is not affected.</maml:para>
<maml:para>Before 5.0.0-rc7, `-Path` and `-Target` were optional: without `-Path`, the cmdlet failed with an index error, and without `-Target`, it created a link to the current folder. It stopped with a terminating error for an existing `-Path` or a link that Windows refused, and every object piped to it failed with `GetDefaultValueFailed`.</maml:para>
<maml:para>Before 5.0.0-rc7, `-Path` and `-Target` were optional: without `-Path`, the cmdlet failed with an index error, and without `-Target`, it created a link to the current folder. It stopped with a terminating error for an existing `-Path` or a link that Windows refused, in Windows PowerShell also for a path with a character that Windows doesn't allow, and every object piped to it failed with `GetDefaultValueFailed`. It checked `-Target` before `-Path`, and its errors for an existing `-Path` and a missing `-Target` named no path.</maml:para>
</maml:alert>
</maml:alertSet>
<command:examples>

74
Tests/Links.Tests.ps1

@ -146,6 +146,8 @@ Describe 'New-NTFSHardLink' {
$linkErrors | ForEach-Object -Process { $_.FullyQualifiedErrorId | Should -BeLike 'CreateHardLinkError,*' }
($linkErrors | ForEach-Object -Process { $_.CategoryInfo.Category }) -join ',' | Should -Be 'ResourceExists,ObjectNotFound,InvalidArgument'
($linkErrors | ForEach-Object -Process { $_.TargetObject }) -join '|' | Should -Be (($existing, $missingLink, $folderLink) -join '|')
# Since 5.0.0-rc7, the error for a folder as -Target names the folder.
$linkErrors[2].Exception.Message | Should -BeLike ("*'{0}'*" -f [WildcardPattern]::Escape($folder))
$link | Should -Exist
$missingLink | Should -Not -Exist
$folderLink | Should -Not -Exist
@ -377,6 +379,78 @@ Describe 'New-NTFSSymbolicLink' {
}
}
# Each error names its item, so that the errors of many links can be told apart. Before 5.0.0-rc7, the errors of
# New-NTFSSymbolicLink for an existing -Path and a missing -Target named no path. No link is created, so the tests run
# without the right to create symbolic links as well.
Describe 'Errors of the cmdlets that create links' {
It '<Command> should name the existing -Path and the missing -Target in its errors' -ForEach @(
@{ Command = 'New-NTFSHardLink' }
@{ Command = 'New-NTFSSymbolicLink' }
) {
$target = New-TestSandboxItem -Sandbox $sandbox -Name 'NamedTarget'
$existing = New-TestSandboxItem -Sandbox $sandbox -Name 'NamedExisting'
$missing = Join-Path -Path $sandbox -ChildPath ('NamedMissing-{0}.txt' -f [guid]::NewGuid().ToString('N').Substring(0, 8))
$link = Join-Path -Path $sandbox -ChildPath ('NamedLink-{0}.txt' -f [guid]::NewGuid().ToString('N').Substring(0, 8))
Assert-TestSandboxPath -Sandbox $sandbox -Path $missing, $link
$requests = @(
[pscustomobject]@{ Path = $existing; Target = $target }
[pscustomobject]@{ Path = $link; Target = $missing }
)
$requests | & $Command -ErrorVariable linkErrors -ErrorAction SilentlyContinue
$linkErrors | Should -HaveCount 2
$linkErrors[0].Exception.Message | Should -BeLike ("*'{0}'*" -f [WildcardPattern]::Escape($existing))
$linkErrors[1].Exception.Message | Should -BeLike ("*'{0}'*" -f [WildcardPattern]::Escape($missing))
Test-Path2 -Path $link | Should -BeFalse
}
# Both cmdlets check -Path before -Target. Before 5.0.0-rc7, New-NTFSSymbolicLink checked -Target first and
# reported a missing target instead.
It '<Command> should report an existing -Path before a missing -Target' -ForEach @(
@{ Command = 'New-NTFSHardLink' }
@{ Command = 'New-NTFSSymbolicLink' }
) {
$existing = New-TestSandboxItem -Sandbox $sandbox -Name 'FirstExisting'
$missing = Join-Path -Path $sandbox -ChildPath ('FirstMissing-{0}.txt' -f [guid]::NewGuid().ToString('N').Substring(0, 8))
Assert-TestSandboxPath -Sandbox $sandbox -Path $missing
& $Command -Path $existing -Target $missing -ErrorVariable linkErrors -ErrorAction SilentlyContinue
$linkErrors | Should -HaveCount 1
$linkErrors[0].CategoryInfo.Category | Should -Be 'ResourceExists'
$linkErrors[0].TargetObject | Should -Be $existing
Get-Content -LiteralPath $existing | Should -Be 'FirstExisting'
}
}
# Windows PowerShell rejects a character that Windows doesn't allow in a path, such as |, before Windows sees the path.
# Before 5.0.0-rc7, that stopped the pipeline in Windows PowerShell, and PowerShell 7 reported it as a WriteError.
Describe 'Paths that Windows does not allow in the cmdlets that create links' {
It '<Command> should write a non-terminating InvalidArgument error and continue with the next link' -ForEach @(
@{ Command = 'New-NTFSHardLink'; ErrorId = 'CreateHardLinkError' }
@{ Command = 'New-NTFSSymbolicLink'; ErrorId = 'CreateSymbolicLinkError' }
) {
$target = New-TestSandboxItem -Sandbox $sandbox -Name 'InvalidTarget'
$existing = New-TestSandboxItem -Sandbox $sandbox -Name 'InvalidExisting'
$invalid = Join-Path -Path $sandbox -ChildPath 'Invalid|Link.txt'
# The second link exists, so that the cmdlet rejects it before it creates anything, also without the right to
# create symbolic links; its error shows that the cmdlet went on.
$requests = @(
[pscustomobject]@{ Path = $invalid; Target = $target }
[pscustomobject]@{ Path = $existing; Target = $target }
)
$requests | & $Command -ErrorVariable linkErrors -ErrorAction SilentlyContinue
$linkErrors | Should -HaveCount 2
$linkErrors | ForEach-Object -Process { $_.FullyQualifiedErrorId | Should -BeLike "$ErrorId,*" }
($linkErrors | ForEach-Object -Process { $_.CategoryInfo.Category }) -join ',' | Should -Be 'InvalidArgument,ResourceExists'
$linkErrors[0].TargetObject | Should -Be $invalid
Get-Content -LiteralPath $existing | Should -Be 'InvalidExisting'
}
}
# Before 5.0.0-rc7, both parameters were optional. Without -Path, the cmdlets failed with an index error; without -Target,
# they used the current location, so New-NTFSSymbolicLink -Path Link created a link to the current folder.
Describe 'Parameters of the cmdlets that create links' {

Loading…
Cancel
Save