https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=323729f654ae3524642115ed35330819f647a023
commit 323729f654ae3524642115ed35330819f647a023 Author: Corinna Vinschen <cori...@vinschen.de> AuthorDate: Wed Feb 26 15:37:56 2025 +0100 Commit: Corinna Vinschen <cori...@vinschen.de> CommitDate: Wed Feb 26 15:37:56 2025 +0100 Cygwin: fhandler_netdrive: rework share enumeration - As before, first try NFS for dotted names, but additionally check if the NFS profvider is installed. - Next try *any* provider (i.e. provider = 0). Move the necessary heuristics into thread_netdrive_wnet with lots of comments. - Last, but not least, if the server was already tried for NFS, but WNetGetResourceInformationW returns WNNC_NET_MS_NFS again when trying any provider, bail out and retry with SMB, since many NFS servers could be SMB servers as well. Signed-off-by: Corinna Vinschen <cori...@vinschen.de> Diff: --- winsup/cygwin/autoload.cc | 1 + winsup/cygwin/fhandler/netdrive.cc | 155 +++++++++++++++++++++++-------------- 2 files changed, 97 insertions(+), 59 deletions(-) diff --git a/winsup/cygwin/autoload.cc b/winsup/cygwin/autoload.cc index 7e882ef1e8e7..35b207e9daf3 100644 --- a/winsup/cygwin/autoload.cc +++ b/winsup/cygwin/autoload.cc @@ -504,6 +504,7 @@ LoadDLLfunc (LdapMapErrorToWin32, wldap32) LoadDLLfunc (WNetCloseEnum, mpr) LoadDLLfunc (WNetEnumResourceW, mpr) +LoadDLLfunc (WNetGetNetworkInformationW, mpr) LoadDLLfunc (WNetGetProviderNameW, mpr) LoadDLLfunc (WNetGetResourceInformationW, mpr) LoadDLLfunc (WNetOpenEnumW, mpr) diff --git a/winsup/cygwin/fhandler/netdrive.cc b/winsup/cygwin/fhandler/netdrive.cc index 6b4535fff409..345ebddfd395 100644 --- a/winsup/cygwin/fhandler/netdrive.cc +++ b/winsup/cygwin/fhandler/netdrive.cc @@ -110,6 +110,8 @@ public: #define DIR_cache (*reinterpret_cast<dir_cache *> (dir->__handle)) +#define RETRY_SMB INT_MAX + struct netdriveinf { DIR *dir; @@ -175,6 +177,7 @@ server_is_running_nfs (const wchar_t *servername) return ret; } + /* Use only to enumerate the Network top level. */ static DWORD thread_netdrive_wsd (void *arg) @@ -275,41 +278,94 @@ thread_netdrive_wnet (void *arg) wchar_t srv_name[CYG_MAX_PATH]; NETRESOURCEW nri = { 0 }; LPNETRESOURCEW nro; - tmp_pathbuf tp; + NETINFOSTRUCT netinfo; + DWORD net_type = 0; HANDLE dom = NULL; DWORD cnt, size; + tmp_pathbuf tp; ReleaseSemaphore (ndi->sem, 1, NULL); - wres = WNetGetProviderNameW (ndi->provider, provider, (size = 256, &size)); - if (wres != NO_ERROR) - { - ndi->err = geterrno_from_win_error (wres); - goto out; - } - sys_mbstowcs (srv_name, CYG_MAX_PATH, dir->__d_dirname); srv_name[0] = L'\\'; srv_name[1] = L'\\'; - - if (ndi->provider == WNNC_NET_MS_NFS - && !server_is_running_nfs (srv_name + 2)) - { - ndi->err = ENOENT; - goto out; - } - nri.lpRemoteName = srv_name; - nri.lpProvider = provider; nri.dwType = RESOURCETYPE_DISK; nro = (LPNETRESOURCEW) tp.c_get (); + + if (ndi->provider) + { + wres = WNetGetProviderNameW (ndi->provider, provider, + (size = 256, &size)); + if (wres != NO_ERROR) + { + ndi->err = geterrno_from_win_error (wres); + goto out; + } + nri.lpProvider = provider; + + } wres = WNetGetResourceInformationW (&nri, nro, (size = NT_MAX_PATH, &size), &dummy); if (wres != NO_ERROR) { - ndi->err = geterrno_from_win_error (wres); + /* WNetGetResourceInformationW fails for WebDAV server names. + We don't want a "No such file or directory" in this case, but since + neither WNetGetResourceInformationW, nor WNetGetProviderNameW works + for them, we have to skip the error message heuristically... + FIXME: While WNetGetResourceInformationW fails, enumerating the + entire WNet namespace recursively down to the server level will + actually show the connected WebDAV servers. */ + if (wres != ERROR_BAD_NET_NAME || !wcschr (srv_name, L'@')) + ndi->err = geterrno_from_win_error (wres); goto out; } + + if (ndi->provider) + net_type = ndi->provider; + else + { + netinfo.cbStructure = sizeof netinfo; + wres = WNetGetNetworkInformationW (nro->lpProvider, &netinfo); + if (wres == NO_ERROR) + net_type = ((DWORD) netinfo.wNetType << 16); + } + + /* More heuristics... */ + switch (net_type) + { + case WNNC_NET_MS_NFS: + /* If ndi->provider is 0 and the machine name contains dots, we already + handled NFS. However, if the machine supports both, NFS and SMB, + sometimes WNetGetNetworkInformationW returns the NFS provider, + sometimes the SMB provider. So if we get the NFS provider again + here, enforce the SMB provider. */ + if (ndi->provider == 0) + { + ndi->err = RETRY_SMB; + goto out; + } + /* Check on port 2049 if the server is replying. Otherwise the + timeout on WNetOpenEnumW is excessive! */ + if (!server_is_running_nfs (srv_name + 2)) + { + ndi->err = ENOENT; + goto out; + } + break; + case WNNC_NET_DAV: + /* FIXME: WebDAV enumeration isn't supported, but we could try + to find the connected shares by enumerating all remembered + connections. */ + goto out; + case WNNC_NET_RDR2SAMPLE: + /* Lots of OSS drivers uses this provider. No idea yet, what + to do with them. */ + fallthrough; + default: + break; + } + wres = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, RESOURCEUSAGE_ALL, nro, &dom); if (wres != NO_ERROR) @@ -330,7 +386,7 @@ thread_netdrive_wnet (void *arg) continue; ++name; - if (ndi->provider == WNNC_NET_MS_NFS) + if (net_type == WNNC_NET_MS_NFS) { wchar_t *nm = name; /* Convert from "ANSI embedded in widechar" to multibyte and convert @@ -345,17 +401,17 @@ thread_netdrive_wnet (void *arg) ndi->err = ENOMEM; goto out; } - /* NFS has deep links so convert embedded '\\' to '/' here */ - for (wchar_t *bs = name; (bs = wcschr (bs, L'\\')); *bs++ = L'/') - ; } + /* Some providers have deep links so convert embedded '\\' to '/' here */ + for (wchar_t *bs = name; (bs = wcschr (bs, L'\\')); *bs++ = L'/') + ; /* If we already collected shares, drop duplicates. */ for (cache_idx = 0; cache_idx < entry_cache_size; ++ cache_idx) if (!wcscmp (name, DIR_cache[cache_idx])) // wcscasecmp? break; if (cache_idx >= entry_cache_size) DIR_cache.add (name); - if (ndi->provider == WNNC_NET_MS_NFS) + if (net_type == WNNC_NET_MS_NFS) free (name); } out: @@ -369,15 +425,13 @@ static DWORD create_thread_and_wait (DIR *dir) { netdriveinf ndi = { dir, 0, 0, NULL }; + WCHAR provider[256]; cygthread *thr; - const char *thread_name = NULL; + DWORD size; /* For the Network root, fetch WSD info. */ if (strlen (dir->__d_dirname) == 2) { - WCHAR provider[256]; - DWORD size; - ndi.provider = WNNC_NET_SMB; ndi.sem = CreateSemaphore (&sec_none_nih, 0, 2, NULL); thr = new cygthread (thread_netdrive_wsd, &ndi, "netdrive_wsd"); @@ -395,7 +449,9 @@ create_thread_and_wait (DIR *dir) /* Try NFS first, if the name contains a dot (i. e., supposedly is a FQDN as used in NFS server enumeration) but no at-sign. */ - if (strchr (dir->__d_dirname, '.') && !strchr (dir->__d_dirname + 2, '@')) + if (strchr (dir->__d_dirname, '.') && !strchr (dir->__d_dirname + 2, '@') + && WNetGetProviderNameW (WNNC_NET_MS_NFS, provider, (size = 256, &size)) + == NO_ERROR) { ndi.provider = WNNC_NET_MS_NFS; ndi.sem = CreateSemaphore (&sec_none_nih, 0, 2, NULL); @@ -409,42 +465,23 @@ create_thread_and_wait (DIR *dir) } - /* Eventually, try TERMSRV/P9/SMB via WNet for share enumeration, - depending on "server" name. - - TODO: There's no known way yet to enumerate connected WebDAV resources. - Whatever I try, WNetGetResourceInformationW or WNetOpenEnumW return - ERROR_BAD_NET_NAME (67). Therefore, short-circuit WebDAV here for - the time being. */ - if (!strcmp (dir->__d_dirname + 2, TERMSRV_DIR)) - { - ndi.provider = WNNC_NET_TERMSRV; - thread_name = "netdrive_termsrv"; - } - else if (!strcmp (dir->__d_dirname + 2, PLAN9_DIR)) - { - ndi.provider = WNNC_NET_9P; - thread_name = "netdrive_9p"; - } - else if (strchr (dir->__d_dirname + 2, '@') != NULL) - { - /* ndi.provider = WNNC_NET_DAV; */ - /* thread_name = "netdrive_dav"; */ - ndi.err = 0; - goto out; - } - else - { - ndi.provider = WNNC_NET_SMB; - thread_name = "netdrive_smb"; - } - ndi.sem = CreateSemaphore (&sec_none_nih, 0, 2, NULL); - thr = new cygthread (thread_netdrive_wnet, &ndi, thread_name); + ndi.provider = 0; + thr = new cygthread (thread_netdrive_wnet, &ndi, "netdrive_wnet"); if (thr->detach (ndi.sem)) ndi.err = EINTR; CloseHandle (ndi.sem); + if (ndi.err == RETRY_SMB) + { + ndi.sem = CreateSemaphore (&sec_none_nih, 0, 2, NULL); + ndi.provider = WNNC_NET_SMB; + thr = new cygthread (thread_netdrive_wnet, &ndi, "netdrive_smb"); + if (thr->detach (ndi.sem)) + ndi.err = EINTR; + CloseHandle (ndi.sem); + } + out: return DIR_cache.count() > 0 ? 0 : ndi.err; }