Author eryksun
Recipients eryksun, iamsav, miss-islington, paul.moore, steve.dower, tim.golden, zach.ware
Date 2019-09-17.16:04:27
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1568736268.44.0.156644221586.issue38081@roundup.psfhosted.org>
In-reply-to
Content
Sorry, I mistakenly left out ERROR_BAD_NETPATH (53). It's at least used with mapped drives. For example, I have drive "M:" mapped to WebDAV "//live.sysinternals.com/tools", and I see this error if I disconnect the network:

    >>> try: nt._getfinalpathname('M:/')
    ... except OSError as e: print(e.winerror)
    ...
    53

whereas if I access the underlying UNC path directly the error in this case is ERROR_BAD_NET_NAME (67):

    >>> try: nt._getfinalpathname('//live.sysinternals.com/tools')
    ... except OSError as e: print(e.winerror)
    ...
    67

(Not that this case would normally succeed. A WebDAV share fails the internal request to get a normalized name, as expected for most network providers, but with an unexpected error code that's not handled by the API. It would succeed if we changed _getfinalpathname to fall back on getting the final name as opened, which skips expanding short component names.)

---
Discussion

The Multiple UNC Provider device (i.e. "\??\UNC" -> "\Device\Mup") resolves a UNC path prefix to a network provider (e.g. "Microsoft Windows Network" for SMB) by checking all providers in a registered order until one claims to handle the path. Typically a provider claims the server/share prefix, but the claimed prefix can be just the server, or a variable path length. Typically, if the "server" component isn't found, a provider returns STATUS_BAD_NETWORK_PATH. If the "share" component isn't found, it returns STATUS_BAD_NETWORK_NAME. However, since the request is to MUP, the final status is whatever MUP returns. As far as I can tell, post-Vista MUP prefix resolution returns STATUS_BAD_NETWORK_NAME even if all providers return STATUS_BAD_NETWORK_PATH. 

That said, MUP prefix resolution isn't used for mapped drives. A mapped drive sends the request directly to the provider that created the drive. Prior to Vista, this used to be a top-level named device such as "\Device\LanmanRedirector" (SMB). Since Vista, all redirected create/open requests are routed through MUP, but it doesn't use prefix resolution in this case. It has a sneaky way of implementing this. The provider's device name nowadays is an object SymbolicLink that targets MUP, but with a reserved component that indicates the redirector to use, e.g. "\Device\LanmanRedirector" -> "\Device\Mup\;LanmanRedirector". (A valid server name cannot begin with a semicolon, so this syntax is reserved by MUP. It also supports an optional second reserved component, with the drive name and logon session ID, such as ";Z:0000000000001234". These reserved components are removed from the parsed path, i.e. they are not included in the final path.) Nothing stops us from using this undocumented feature manually in a UNC path, as demonstrated by the examples below. 

The following shows that MUP parses ";LanmanRedirector" as the redirector name, not the "server" component.

    >>> os.path.samefile('//localhost/C$', '//;LanmanRedirector/localhost/C$')
    True

The following shows that an explicit redirector path does not use prefix resolution. This open fails because there's no WebDAV server on localhost.

    >>> try: os.stat('//;WebDavRedirector/localhost/C$')
    ... except OSError as e: print(e.winerror)
    ...
    53

The following shows that MUP fails an open with STATUS_OBJECT_PATH_INVALID (i.e. ERROR_BAD_PATHNAME, 161) if the redirector name is unknown:

    >>> try: os.stat('//;Lanman/localhost/C$')
    ... except OSError as e: print(e.winerror)
    ...
    161

When we misspell the server name as "localhos", we see that the error for an explicit redirector path, as is used in a mapped drive, is ERROR_BAD_NETPATH (53):

    >>> try: os.stat('//;LanmanRedirector/localhos/C$')
    ... except OSError as e: print(e.winerror)
    ...
    53

If we omit the explicit redirector name, then MUP tries prefix resolution, and the error is instead ERROR_BAD_NET_NAME (67):

    >>> try: os.stat('//localhos/C$')
    ... except OSError as e: print(e.winerror)
    ...
    67
History
Date User Action Args
2019-09-17 16:04:28eryksunsetrecipients: + eryksun, paul.moore, tim.golden, zach.ware, steve.dower, miss-islington, iamsav
2019-09-17 16:04:28eryksunsetmessageid: <1568736268.44.0.156644221586.issue38081@roundup.psfhosted.org>
2019-09-17 16:04:28eryksunlinkissue38081 messages
2019-09-17 16:04:27eryksuncreate