/* fhandler_netdrive.cc: fhandler for // and //MACHINE handling Copyright 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Red Hat, Inc. 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 #include "cygerrno.h" #include "security.h" #include "path.h" #include "fhandler.h" #include "dtable.h" #include "cygheap.h" #include "cygthread.h" #include #include 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 DWORD WINAPI thread_netdrive (void *arg) { netdriveinf *ndi = (netdriveinf *) arg; char provider[256], *dummy = NULL; LPNETRESOURCE nro; DWORD cnt, size; struct net_hdls *nh; ReleaseSemaphore (ndi->sem, 1, NULL); switch (ndi->what) { case GET_RESOURCE_OPENENUMTOP: nro = (LPNETRESOURCE) alloca (size = 4096); nh = (struct net_hdls *) ndi->out; ndi->ret = WNetGetProviderName (WNNC_NET_LANMAN, provider, (size = 256, &size)); if (ndi->ret != NO_ERROR) 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 = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, RESOURCEUSAGE_ALL, nro, &nh->net); if (ndi->ret != NO_ERROR) break; while ((ndi->ret = WNetEnumResource (nh->net, (cnt = 1, &cnt), nro, (size = 4096, &size))) == NO_ERROR) { ndi->ret = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, RESOURCEUSAGE_ALL, nro, &nh->dom); if (ndi->ret == NO_ERROR) break; } break; case GET_RESOURCE_OPENENUM: nro = (LPNETRESOURCE) alloca (size = 4096); nh = (struct net_hdls *) ndi->out; ndi->ret = WNetGetProviderName (WNNC_NET_LANMAN, provider, (size = 256, &size)); if (ndi->ret != NO_ERROR) break; ((LPNETRESOURCE) ndi->in)->lpProvider = provider; ndi->ret = WNetGetResourceInformation ((LPNETRESOURCE) ndi->in, nro, &size, &dummy); if (ndi->ret != NO_ERROR) break; ndi->ret = WNetOpenEnum (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; } while ((ndi->ret = WNetEnumResource (nh->dom, (cnt = 1, &cnt), (LPNETRESOURCE) ndi->out, &ndi->outsize)) != NO_ERROR && nh->net) { WNetCloseEnum (nh->dom); nh->dom = NULL; nro = (LPNETRESOURCE) alloca (size = 4096); while ((ndi->ret = WNetEnumResource (nh->net, (cnt = 1, &cnt), nro, (size = 4096, &size))) == NO_ERROR) { ndi->ret = WNetOpenEnum (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; } /* Returns 0 if path doesn't exist, >0 if path is a directory, -1 if path is a file, -2 if it's a symlink. */ 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]; for (to = namebuf, from = get_name (); *from; to++, from++) *to = (*from == '/') ? '\\' : *from; *to = '\0'; struct net_hdls nh = { NULL, NULL }; NETRESOURCE nr = {0}; nr.dwType = RESOURCETYPE_DISK; nr.lpRemoteName = namebuf; 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 __reg2 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) { NETRESOURCE *nro; DWORD ret; int res; if (!dir->__d_position) { size_t len = strlen (get_name ()); char *namebuf = NULL; NETRESOURCE nr = { 0 }; struct net_hdls *nh; if (len != 2) /* // */ { const char *from; char *to; namebuf = (char *) alloca (len + 1); for (to = namebuf, from = get_name (); *from; to++, from++) *to = (*from == '/') ? '\\' : *from; *to = '\0'; } nr.lpRemoteName = namebuf; 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; } ret = create_thread_and_wait (GET_RESOURCE_ENUM, dir->__handle, nro = (LPNETRESOURCE) alloca (16384), 16384, "WnetEnumResource"); if (ret != NO_ERROR) res = geterrno_from_win_error (ret); else { dir->__d_position++; char *bs = strrchr (nro->lpRemoteName, '\\'); strcpy (de->d_name, bs ? bs + 1 : nro->lpRemoteName); if (strlen (get_name ()) == 2) { strlwr (de->d_name); de->d_ino = hash_path_name (get_ino (), de->d_name); } else { de->d_ino = readdir_get_ino (nro->lpRemoteName, 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, nro->lpRemoteName); } 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 (); }