diff options
Diffstat (limited to 'winsup/cygwin/fhandler/netdrive.cc')
-rw-r--r-- | winsup/cygwin/fhandler/netdrive.cc | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler/netdrive.cc b/winsup/cygwin/fhandler/netdrive.cc new file mode 100644 index 000000000..58ab8811b --- /dev/null +++ b/winsup/cygwin/fhandler/netdrive.cc @@ -0,0 +1,366 @@ +/* fhandler_netdrive.cc: fhandler for // and //MACHINE handling + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "winsup.h" +#include <stdlib.h> +#include "cygerrno.h" +#include "security.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "cygheap.h" +#include "cygthread.h" +#include "tls_pbuf.h" + +#include <dirent.h> + +enum + { + GET_RESOURCE_OPENENUM = 1, + GET_RESOURCE_OPENENUMTOP = 2, + GET_RESOURCE_ENUM = 3 + }; + +struct netdriveinf + { + int what; + int ret; + PVOID in; + PVOID out; + DWORD outsize; + HANDLE sem; + }; + +struct net_hdls + { + HANDLE net; + HANDLE dom; + }; + +static void +wnet_dbg_out (const char *func, DWORD ndi_ret) +{ + DWORD gle_ret; + DWORD error; + WCHAR errorbuf[MAX_PATH]; + WCHAR namebuf[MAX_PATH]; + + if (ndi_ret != ERROR_EXTENDED_ERROR) + { + debug_printf ("%s failed: %u", func, ndi_ret); + return; + } + gle_ret = WNetGetLastErrorW (&error, errorbuf, MAX_PATH, namebuf, MAX_PATH); + if (gle_ret == NO_ERROR) + debug_printf ("%s failed: %u --> %u from '%W': '%W'", + func, ndi_ret, error, namebuf, errorbuf); + else + debug_printf ("WNetGetLastError failed: %u", gle_ret); +} + +static DWORD +thread_netdrive (void *arg) +{ + netdriveinf *ndi = (netdriveinf *) arg; + WCHAR provider[256], *dummy = NULL; + LPNETRESOURCEW nro; + DWORD cnt, size; + struct net_hdls *nh; + tmp_pathbuf tp; + + ReleaseSemaphore (ndi->sem, 1, NULL); + switch (ndi->what) + { + case GET_RESOURCE_OPENENUMTOP: + nro = (LPNETRESOURCEW) tp.c_get (); + nh = (struct net_hdls *) ndi->out; + ndi->ret = WNetGetProviderNameW (WNNC_NET_LANMAN, provider, + (size = 256, &size)); + if (ndi->ret != NO_ERROR) + { + wnet_dbg_out ("WNetGetProviderNameW", ndi->ret); + break; + } + memset (nro, 0, sizeof *nro); + nro->dwScope = RESOURCE_GLOBALNET; + nro->dwType = RESOURCETYPE_ANY; + nro->dwDisplayType = RESOURCEDISPLAYTYPE_GROUP; + nro->dwUsage = RESOURCEUSAGE_RESERVED | RESOURCEUSAGE_CONTAINER; + nro->lpRemoteName = provider; + nro->lpProvider = provider; + ndi->ret = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, + RESOURCEUSAGE_ALL, nro, &nh->net); + if (ndi->ret != NO_ERROR) + { + wnet_dbg_out ("WNetOpenEnumW", ndi->ret); + break; + } + while ((ndi->ret = WNetEnumResourceW (nh->net, (cnt = 1, &cnt), nro, + (size = NT_MAX_PATH, &size))) + == NO_ERROR) + { + ndi->ret = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, + RESOURCEUSAGE_ALL, nro, &nh->dom); + if (ndi->ret == NO_ERROR) + break; + } + break; + case GET_RESOURCE_OPENENUM: + nro = (LPNETRESOURCEW) tp.c_get (); + nh = (struct net_hdls *) ndi->out; + ndi->ret = WNetGetProviderNameW (WNNC_NET_LANMAN, provider, + (size = 256, &size)); + if (ndi->ret != NO_ERROR) + { + wnet_dbg_out ("WNetGetProviderNameW", ndi->ret); + break; + } + ((LPNETRESOURCEW) ndi->in)->lpProvider = provider; + ndi->ret = WNetGetResourceInformationW ((LPNETRESOURCEW) ndi->in, nro, + (size = NT_MAX_PATH, &size), + &dummy); + if (ndi->ret != NO_ERROR) + { + wnet_dbg_out ("WNetGetResourceInformationW", ndi->ret); + break; + } + ndi->ret = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, + RESOURCEUSAGE_ALL, nro, &nh->dom); + break; + case GET_RESOURCE_ENUM: + nh = (struct net_hdls *) ndi->in; + if (!nh->dom) + { + ndi->ret = ERROR_NO_MORE_ITEMS; + break; + } + nro = (LPNETRESOURCEW) tp.c_get (); + while ((ndi->ret = WNetEnumResourceW (nh->dom, (cnt = 1, &cnt), + (LPNETRESOURCEW) ndi->out, + &ndi->outsize)) != NO_ERROR + && nh->net) + { + WNetCloseEnum (nh->dom); + nh->dom = NULL; + while ((ndi->ret = WNetEnumResourceW (nh->net, (cnt = 1, &cnt), nro, + (size = NT_MAX_PATH, &size))) + == NO_ERROR) + { + ndi->ret = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, + RESOURCEUSAGE_ALL, nro, &nh->dom); + if (ndi->ret == NO_ERROR) + break; + } + if (ndi->ret != NO_ERROR) + break; + } + break; + } + ReleaseSemaphore (ndi->sem, 1, NULL); + return 0; +} + +static DWORD +create_thread_and_wait (int what, PVOID in, PVOID out, DWORD outsize, + const char *name) +{ + netdriveinf ndi = { what, 0, in, out, outsize, + CreateSemaphore (&sec_none_nih, 0, 2, NULL) }; + cygthread *thr = new cygthread (thread_netdrive, &ndi, name); + if (thr->detach (ndi.sem)) + ndi.ret = ERROR_OPERATION_ABORTED; + CloseHandle (ndi.sem); + return ndi.ret; +} + +virtual_ftype_t +fhandler_netdrive::exists () +{ + char *to; + const char *from; + size_t len = strlen (get_name ()); + if (len == 2) + return virt_rootdir; + + char namebuf[len + 1]; + tmp_pathbuf tp; + PWCHAR name = tp.w_get (); + + for (to = namebuf, from = get_name (); *from; to++, from++) + *to = (*from == '/') ? '\\' : *from; + *to = '\0'; + + struct net_hdls nh = { NULL, NULL }; + NETRESOURCEW nr = {0}; + nr.dwType = RESOURCETYPE_DISK; + sys_mbstowcs (name, NT_MAX_PATH, namebuf); + nr.lpRemoteName = name; + DWORD ret = create_thread_and_wait (GET_RESOURCE_OPENENUM, + &nr, &nh, 0, "WNetOpenEnum"); + if (nh.dom) + WNetCloseEnum (nh.dom); + return ret != NO_ERROR ? virt_none : virt_directory; +} + +fhandler_netdrive::fhandler_netdrive (): + fhandler_virtual () +{ +} + +int +fhandler_netdrive::fstat (struct stat *buf) +{ + const char *path = get_name (); + debug_printf ("fstat (%s)", path); + + fhandler_base::fstat (buf); + + buf->st_mode = S_IFDIR | STD_RBITS | STD_XBITS; + buf->st_ino = get_ino (); + + return 0; +} + +int +fhandler_netdrive::readdir (DIR *dir, dirent *de) +{ + NETRESOURCEW *nro; + DWORD ret; + int res; + tmp_pathbuf tp; + + if (!dir->__d_position) + { + size_t len = strlen (get_name ()); + PWCHAR name = NULL; + NETRESOURCEW nr = { 0 }; + struct net_hdls *nh; + + if (len != 2) /* // */ + { + const char *from; + char *to; + char *namebuf = (char *) alloca (len + 1); + for (to = namebuf, from = get_name (); *from; to++, from++) + *to = (*from == '/') ? '\\' : *from; + *to = '\0'; + name = tp.w_get (); + sys_mbstowcs (name, NT_MAX_PATH, namebuf); + } + nr.lpRemoteName = name; + nr.dwType = RESOURCETYPE_DISK; + nh = (struct net_hdls *) ccalloc (HEAP_FHANDLER, 1, sizeof *nh); + ret = create_thread_and_wait (len == 2 ? GET_RESOURCE_OPENENUMTOP + : GET_RESOURCE_OPENENUM, + &nr, nh, 0, "WNetOpenEnum"); + if (ret != NO_ERROR) + { + dir->__handle = INVALID_HANDLE_VALUE; + res = geterrno_from_win_error (ret); + goto out; + } + dir->__handle = (HANDLE) nh; + } + nro = (LPNETRESOURCEW) tp.c_get (); + ret = create_thread_and_wait (GET_RESOURCE_ENUM, dir->__handle, nro, + NT_MAX_PATH, "WnetEnumResource"); + if (ret != NO_ERROR) + res = geterrno_from_win_error (ret); + else + { + dir->__d_position++; + PWCHAR bs = wcsrchr (nro->lpRemoteName, L'\\'); + bs = bs ? bs + 1 : nro->lpRemoteName; + if (strlen (get_name ()) == 2) + { + UNICODE_STRING ss, ds; + + tp.u_get (&ds); + RtlInitUnicodeString (&ss, bs); + RtlDowncaseUnicodeString (&ds, &ss, FALSE); + sys_wcstombs (de->d_name, sizeof de->d_name, + ds.Buffer, ds.Length / sizeof (WCHAR)); + de->d_ino = hash_path_name (get_ino (), de->d_name); + } + else + { + sys_wcstombs (de->d_name, sizeof de->d_name, bs); + char *rpath = tp.c_get (); + sys_wcstombs (rpath, NT_MAX_PATH, nro->lpRemoteName); + de->d_ino = readdir_get_ino (rpath, false); + /* We can't trust remote inode numbers of only 32 bit. That means, + remote NT4 NTFS, as well as shares of Samba version < 3.0. */ + if (de->d_ino <= UINT32_MAX) + de->d_ino = hash_path_name (0, rpath); + } + de->d_type = DT_DIR; + + res = 0; + } +out: + syscall_printf ("%d = readdir(%p, %p)", res, dir, de); + return res; +} + +void +fhandler_netdrive::seekdir (DIR *dir, long pos) +{ + rewinddir (dir); + if (pos < 0) + return; + while (dir->__d_position < pos) + if (readdir (dir, dir->__d_dirent)) + break; +} + +void +fhandler_netdrive::rewinddir (DIR *dir) +{ + if (dir->__handle != INVALID_HANDLE_VALUE) + { + struct net_hdls *nh = (struct net_hdls *) dir->__handle; + if (nh->dom) + WNetCloseEnum (nh->dom); + if (nh->net) + WNetCloseEnum (nh->net); + cfree (nh); + } + dir->__handle = INVALID_HANDLE_VALUE; + return fhandler_virtual::rewinddir (dir); +} + +int +fhandler_netdrive::closedir (DIR *dir) +{ + rewinddir (dir); + return fhandler_virtual::closedir (dir); +} + +int +fhandler_netdrive::open (int flags, mode_t mode) +{ + if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + { + set_errno (EEXIST); + return 0; + } + if (flags & O_WRONLY) + { + set_errno (EISDIR); + return 0; + } + /* Open a fake handle to \\Device\\Null */ + return open_null (flags); +} + +int +fhandler_netdrive::close () +{ + /* Skip fhandler_virtual::close, which is a no-op. */ + return fhandler_base::close (); +} |