Home PowerShell Core Fixing path not found of Mounted SMB in macOS
Post
Cancel

PowerShell Core Fixing path not found of Mounted SMB in macOS

Automation on the Mac is great, when it works. And it can, in this case. In this post, we’ll cover an issue where scripts resolving SMB paths fail with the ‘path not found’ error message, yet those same shares ARE accessible within the GUI. Confusing and frustrating? This is critical if you’re trying to automate data transfers to network locations using SMB.

The Problem

I’ve been working in a Mac environment where an Ant script transfers data to an SMB share on a local Windows server.

Sometimes, the Ant script would fail to transfer the data to the SMB share, and report “path not found”. However, the share was mounted on the desktop, and one could access and modify data without any issues.

Re-running the Ant script could have happened all day, but it would still fail. Remounting the share did not fix it either. Further down will explain why.

As a workaround, I instructed our team to restart into “Safe Boot” to clear the system cache, which worked for a time until the issue resurfaced.

Explaining the Fix

There are different ways to deal with SMB shares but I haven’t come across a “PowerShell Core” resource for doing it. So, let’s dig in.

Essentially, the Ant script was hard-coded to look for the actual mount point /Volumes/netShareA to access its child directories, but it couldn’t resolve that path. It appeared that, anywhere in the GUI was able to resolve that path…or so we thought.

What’s the difference?

By going to Terminal and typing mount, all the volumes mounted on the system are displayed.

And, ahah!

netShareA existed as an empty folder, AND a share existed with the name netShareA-1. This means the Ant script was looking at the empty folder, likely orphaned after an SMB reconnect. And no matter how many times we remount the share, the Ant script would never work!

So, instead of the old share being flushed out of /Volumes/ by the OS, it lingered as an empty directory, causing it to be named netShareA-1 upon re-connection. Restarting in Safe Boot flushed the empty folder out, allowing the native share name to be used, and hence, the Ant script would work.

The Solution: 2 PowerShell Scripts

There are a couple we can work around this:

  1. Build two scripts; one for getting mounted volumes and one for getting the SMB shares based on what is mounted (faster response time);
  2. Build one script to get only SMB shares by using mount | grep smb  (slightly slower response time)

Option 1 will be used to work around this issue until Apple can fix it (fix targeted for macOS 10.12 but it still exists in High Sierra), or the OS flushes its cache.

Script 1 - Get-MountedDevice

Purpose: Get-MountedDeviceGets everything that is mounted to the system and returns them as PowerShell objects:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Function Get-MountedDevice {
    <#
    .SYNOPSIS
    Gets mounted devices.

    .DESCRIPTION
    Get-MountedDevice gets all mounted devices connected to the system, including network paths.

    .INPUTS
    None. You cannot pipe objects to Get-MountedDevice.

    .OUTPUTS
    System.Management.Automation.PSCustomObject. Get-MountedDevice returns a PSCustomObject 
    containing the Device, MountPoint, and FileSystem.

    .NOTES
    Author: Aaron Hardy
    Name:   Get-MountedDevice
    #>
    [CmdletBinding()]
    param ()

    # Load all mounted volumes into array.
    $MountedDevices = @(mount)

    # Filter used to return only smbfs volumes.
    $rgxAfterDevice = ' on /.*'
    $rgxBeforeMountPoint = '.* on '
    $rgxFileSystem = ' (.*'

    foreach ($mntDevice in $MountedDevices) {
        $Device = $mntDevice -replace $rgxAfterDevice
        $MountPoint = $mntDevice -replace $rgxBeforeMountPoint -replace $rgxFileSystem
        $FileSystem = [regex]::matches($mntDevice, $rgxFileSystem).Value

        $props = @{
            Device     = $Device.Trim()
            MountPoint = $MountPoint.Trim()
            FileSystem = $FileSystem.Trim()
        }

        New-Object -TypeName psobject -Property $props
    }
}

Script 2 - Get-MacOsSmbShare

Purpose: Get-MacOsSmbShareGets the SMB shares with their correct paths and generates them as PowerShell objects:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
Function Get-MacOsSmbShare {
    <#
    .SYNOPSIS
    Gets SMB shares.

    .DESCRIPTION
    Get-MacOsSmbShare gets SMB shares mounted on the local system.

    .PARAMETER ShareName
    Specifies the share name to get.

    .INPUTS
    None. You cannot pipe objects to Get-MacOsSmbShare.

    .OUTPUTS
    System.Management.Automation.PSCustomObject. Get-MacOsSmbShare returns a PSCustomObject
    containing the Device, MountPoint, and FileSystem.

    .NOTES
    Author: Aaron Hardy
    Name: Get-MountedDevice
    #>
    [CmdletBinding()]
    param (
        [Parameter()]
        [string]$ShareName
    )

    # Filter used to return only smbfs volumes.
    $rgxVolSmbfs = '(smbfs,'

    if ($ShareName) {

        $Share = [regex]::Escape($ShareName)

        # RegEx to include numerically incremented share names
        $rgxShareSuffix = "(-(d{1}|d{2}))?$"

        # Valid path to share in /Volumes
        $SharePath = "/Volumes/$Share$rgxShareSuffix"

        $Mounts = Get-MountedDevice |
            Where-Object -FilterScript {
            ( $_.FileSystem -match $rgxVolSmbfs ) -and
            ( $_.MountPoint -match $SharePath)
        } # Where-Object
    }
    else {
        $Mounts = Get-MountedDevice |
            Where-Object -FilterScript {
            ( $_.FileSystem -match $rgxVolSmbfs )
        }
    } # else

    Write-Output $Mounts |
        Select-Object -Property MountPoint, Device, FileSystem

} # Get-MacOsSmbShare

Then to call it in any other script or the pscore command line:

1
2
PS > (Get-MacOsSmbShare -ShareName netShareA).VolumePath
/Volumes/netShareA-1

In the case of this example, this would have returned /Volumes/netShareA-1.

That’s it!

While there is much more that can be done to enhance this script, it has been a great solution to fixing broken SMB paths while using them for automation on a Mac, with no more transfers failing! I hope you can find it just as useful.

This post is licensed under CC BY 4.0 by the author.

Use PowerShell Core in VS Code

Double Click command File to Execute PowerShell Core Script

Comments powered by Disqus.