2005-05-09 02:39:34 +00:00
|
|
|
/* fhandler_netdrive.cc: fhandler for // and //MACHINE handling
|
2005-05-06 04:06:17 +00:00
|
|
|
|
|
|
|
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. */
|
|
|
|
|
2005-05-09 02:39:34 +00:00
|
|
|
#include "winsup.h"
|
|
|
|
#include "cygerrno.h"
|
|
|
|
#include "security.h"
|
|
|
|
#include "path.h"
|
|
|
|
#include "fhandler.h"
|
2008-07-19 12:26:09 +00:00
|
|
|
#include "dtable.h"
|
|
|
|
#include "cygheap.h"
|
2005-05-17 20:34:15 +00:00
|
|
|
#include "cygthread.h"
|
2005-05-13 20:20:02 +00:00
|
|
|
|
2024-03-20 14:32:49 +01:00
|
|
|
#include <shobjidl.h>
|
|
|
|
#include <shlobj.h>
|
|
|
|
#include <lm.h>
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
2005-05-13 20:20:02 +00:00
|
|
|
#include <dirent.h>
|
2024-03-20 14:32:49 +01:00
|
|
|
#include <wctype.h>
|
2005-05-09 02:39:34 +00:00
|
|
|
|
2024-03-20 14:32:49 +01:00
|
|
|
/* SMBv1 is deprectated and not even installed by default anymore on
|
|
|
|
Windows 10 and 11 machines or their servers.
|
2005-05-17 20:34:15 +00:00
|
|
|
|
2024-03-20 14:32:49 +01:00
|
|
|
So this fhandler class now uses Network Discovery, which, unfortunately,
|
|
|
|
requires to use the shell API. */
|
|
|
|
|
|
|
|
/* Define the required GUIDs here to avoid linking with libuuid.a */
|
|
|
|
const GUID FOLDERID_NetworkFolder = {
|
|
|
|
0xd20beec4, 0x5ca8, 0x4905,
|
|
|
|
{ 0xae, 0x3b, 0xbf, 0x25, 0x1e, 0xa0, 0x9b, 0x53 }
|
|
|
|
};
|
|
|
|
|
|
|
|
const GUID BHID_StorageEnum = {
|
|
|
|
0x4621a4e3, 0xf0d6, 0x4773,
|
|
|
|
{ 0x8a, 0x9c, 0x46, 0xe7, 0x7b, 0x17, 0x48, 0x40 }
|
|
|
|
};
|
|
|
|
|
|
|
|
const GUID BHID_EnumItems = {
|
|
|
|
0x94f60519, 0x2850, 0x4924,
|
|
|
|
{ 0xaa, 0x5a, 0xd1, 0x5e, 0x84, 0x86, 0x80, 0x39 }
|
|
|
|
};
|
|
|
|
|
|
|
|
class dir_cache
|
|
|
|
{
|
|
|
|
size_t max_entries;
|
|
|
|
size_t num_entries;
|
|
|
|
wchar_t **entry;
|
|
|
|
public:
|
|
|
|
dir_cache () : max_entries (0), num_entries (0), entry (NULL) {}
|
|
|
|
~dir_cache ()
|
2005-05-17 20:34:15 +00:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
while (num_entries > 0)
|
|
|
|
free (entry[--num_entries]);
|
|
|
|
free (entry);
|
|
|
|
}
|
|
|
|
void add (wchar_t *str)
|
2008-07-19 12:26:09 +00:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
if (num_entries >= max_entries)
|
|
|
|
{
|
|
|
|
wchar_t **newentry;
|
2008-07-19 12:26:09 +00:00
|
|
|
|
2024-03-20 14:32:49 +01:00
|
|
|
newentry = (wchar_t **) realloc (entry, (max_entries + 10)
|
|
|
|
* sizeof (wchar_t *));
|
|
|
|
if (!newentry)
|
|
|
|
return;
|
|
|
|
entry = newentry;
|
|
|
|
max_entries += 10;
|
|
|
|
}
|
|
|
|
entry[num_entries] = wcsdup (str);
|
|
|
|
if (entry[num_entries])
|
|
|
|
{
|
|
|
|
wchar_t *p = entry[num_entries];
|
|
|
|
while ((*p = towlower (*p)))
|
|
|
|
++p;
|
|
|
|
++num_entries;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inline wchar_t *operator [](size_t idx) const
|
|
|
|
{
|
|
|
|
if (idx < num_entries)
|
|
|
|
return entry[idx];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#define DIR_cache (*reinterpret_cast<dir_cache *> (dir->__handle))
|
|
|
|
|
|
|
|
struct netdriveinf
|
2021-11-19 12:19:31 +01:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
DIR *dir;
|
|
|
|
int err;
|
|
|
|
bool test_only;
|
|
|
|
HANDLE sem;
|
|
|
|
};
|
2021-11-19 12:19:31 +01:00
|
|
|
|
2024-03-20 14:32:49 +01:00
|
|
|
static inline int
|
|
|
|
hresult_to_errno (HRESULT wres, bool test_only = false)
|
|
|
|
{
|
|
|
|
if (SUCCEEDED (wres))
|
|
|
|
return 0;
|
|
|
|
/* IEnumShellItems::Reset returns E_NOTIMPL when called for share
|
|
|
|
enumeration. However, if the machine doesn't exist, the Win32
|
|
|
|
error ERROR_BAD_NETPATH (converted into a HRESULT) is returned. In
|
|
|
|
test_only mode, we exploit this. Also, E_ACCESSDENIED is a funny
|
|
|
|
one. It means, the machine exists, you just have no right to
|
|
|
|
access the share list, or SMB doesn't run. */
|
|
|
|
if (test_only && (wres == E_NOTIMPL || wres == E_ACCESSDENIED))
|
|
|
|
return 0;
|
|
|
|
if (((ULONG) wres & 0xffff0000)
|
|
|
|
== (ULONG) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, 0))
|
|
|
|
return geterrno_from_win_error ((ULONG) wres & 0xffff);
|
|
|
|
return EACCES;
|
2021-11-19 12:19:31 +01:00
|
|
|
}
|
|
|
|
|
2022-08-04 21:16:32 +02:00
|
|
|
static DWORD
|
2005-05-17 20:34:15 +00:00
|
|
|
thread_netdrive (void *arg)
|
|
|
|
{
|
|
|
|
netdriveinf *ndi = (netdriveinf *) arg;
|
2024-03-20 14:32:49 +01:00
|
|
|
DIR *dir = ndi->dir;
|
|
|
|
IEnumShellItems *netitem_enum;
|
|
|
|
IShellItem *netparent;
|
|
|
|
HRESULT wres;
|
2005-05-17 20:34:15 +00:00
|
|
|
|
|
|
|
ReleaseSemaphore (ndi->sem, 1, NULL);
|
2024-03-20 14:32:49 +01:00
|
|
|
|
|
|
|
size_t len = strlen (dir->__d_dirname);
|
|
|
|
wres = CoInitialize (NULL);
|
|
|
|
if (FAILED (wres))
|
2005-05-17 20:34:15 +00:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
ndi->err = hresult_to_errno (wres);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 2) /* // */
|
|
|
|
{
|
|
|
|
wres = SHGetKnownFolderItem (FOLDERID_NetworkFolder, KF_FLAG_DEFAULT,
|
|
|
|
NULL, IID_PPV_ARGS (&netparent));
|
|
|
|
if (FAILED (wres))
|
2021-11-19 12:19:31 +01:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
ndi->err = hresult_to_errno (wres);
|
|
|
|
goto out;
|
2021-11-19 12:19:31 +01:00
|
|
|
}
|
2024-03-20 14:32:49 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wchar_t name[CYG_MAX_PATH];
|
|
|
|
|
|
|
|
sys_mbstowcs (name, CYG_MAX_PATH, dir->__d_dirname);
|
|
|
|
name[0] = L'\\';
|
|
|
|
name[1] = L'\\';
|
|
|
|
wres = SHCreateItemFromParsingName (name, NULL,
|
|
|
|
IID_PPV_ARGS (&netparent));
|
|
|
|
if (FAILED (wres))
|
2021-11-19 12:19:31 +01:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
ndi->err = hresult_to_errno (wres);
|
|
|
|
goto out;
|
2021-11-19 12:19:31 +01:00
|
|
|
}
|
2024-03-20 14:32:49 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
wres = netparent->BindToHandler (NULL, len == 2 ? BHID_StorageEnum
|
|
|
|
: BHID_EnumItems,
|
|
|
|
IID_PPV_ARGS (&netitem_enum));
|
|
|
|
if (FAILED (wres))
|
|
|
|
{
|
|
|
|
ndi->err = hresult_to_errno (wres);
|
|
|
|
netparent->Release ();
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 2 || ndi->test_only)
|
|
|
|
{
|
|
|
|
wres = netitem_enum->Reset ();
|
|
|
|
|
|
|
|
if (FAILED (wres) || ndi->test_only)
|
2008-11-26 17:21:04 +00:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
ndi->err = hresult_to_errno (wres, ndi->test_only);
|
|
|
|
netitem_enum->Release ();
|
|
|
|
netparent->Release ();
|
|
|
|
goto out;
|
2008-07-19 12:26:09 +00:00
|
|
|
}
|
2024-03-20 14:32:49 +01:00
|
|
|
|
|
|
|
/* Don't look at me!
|
|
|
|
|
|
|
|
Network discovery is very unreliable and the list of machines
|
|
|
|
returned is just fly-by-night, if the enumerator doesn't have
|
|
|
|
enough time. The fact that you see *most* (but not necessarily
|
|
|
|
*all*) machines on the network in Windows Explorer is a result of
|
|
|
|
the enumeration running in a loop. You can observe this when
|
|
|
|
rebooting a remote machine and it disappears and reappears in the
|
|
|
|
Explorer Network list.
|
|
|
|
|
|
|
|
However, this is no option for the command line. We need to be able
|
|
|
|
to enumerate in a single go, since we can't just linger during
|
|
|
|
readdir() and reset the enumeration multiple times until we have a
|
|
|
|
supposedly full list.
|
|
|
|
|
|
|
|
This makes the following Sleep necessary. Sleeping ~3secs after
|
|
|
|
Reset fills the enumeration with high probability with almost all
|
|
|
|
available machines. */
|
|
|
|
Sleep (3000L);
|
|
|
|
}
|
|
|
|
|
|
|
|
dir->__handle = (char *) new dir_cache ();
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
IShellItem *netitem[10] = { 0 };
|
|
|
|
LPWSTR item_name = NULL;
|
|
|
|
ULONG count;
|
|
|
|
|
|
|
|
wres = netitem_enum->Next (10, netitem, &count);
|
|
|
|
if (SUCCEEDED (wres) && count > 0)
|
2005-08-12 02:39:13 +00:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
for (ULONG idx = 0; idx < count; ++idx)
|
2008-07-19 12:26:09 +00:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
if (netitem[idx]->GetDisplayName (SIGDN_PARENTRELATIVEPARSING,
|
|
|
|
&item_name) == S_OK)
|
|
|
|
{
|
|
|
|
DIR_cache.add (item_name);
|
|
|
|
CoTaskMemFree (item_name);
|
|
|
|
}
|
|
|
|
netitem[idx]->Release ();
|
2008-07-19 12:26:09 +00:00
|
|
|
}
|
2005-05-17 20:34:15 +00:00
|
|
|
}
|
|
|
|
}
|
2024-03-20 14:32:49 +01:00
|
|
|
while (wres == S_OK);
|
|
|
|
|
|
|
|
netitem_enum->Release ();
|
|
|
|
netparent->Release ();
|
|
|
|
|
|
|
|
ndi->err = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
CoUninitialize ();
|
2005-05-17 20:34:15 +00:00
|
|
|
ReleaseSemaphore (ndi->sem, 1, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DWORD
|
2024-03-20 14:32:49 +01:00
|
|
|
create_thread_and_wait (DIR *dir, bool test_only)
|
2005-05-17 20:34:15 +00:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
netdriveinf ndi = { dir, 0, test_only,
|
2005-05-17 20:34:15 +00:00
|
|
|
CreateSemaphore (&sec_none_nih, 0, 2, NULL) };
|
2024-03-20 14:32:49 +01:00
|
|
|
|
|
|
|
cygthread *thr = new cygthread (thread_netdrive, &ndi, "netdrive");
|
2005-05-17 20:34:15 +00:00
|
|
|
if (thr->detach (ndi.sem))
|
2024-03-20 14:32:49 +01:00
|
|
|
ndi.err = EINTR;
|
2005-05-17 20:34:15 +00:00
|
|
|
CloseHandle (ndi.sem);
|
2024-03-20 14:32:49 +01:00
|
|
|
return ndi.err;
|
2005-05-17 20:34:15 +00:00
|
|
|
}
|
|
|
|
|
2010-09-06 09:47:01 +00:00
|
|
|
virtual_ftype_t
|
2005-05-09 02:39:34 +00:00
|
|
|
fhandler_netdrive::exists ()
|
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
if (strlen (get_name ()) == 2)
|
2010-09-06 09:47:01 +00:00
|
|
|
return virt_rootdir;
|
2014-08-19 08:41:40 +00:00
|
|
|
|
2024-03-20 14:32:49 +01:00
|
|
|
DIR dir = { 0 };
|
|
|
|
dir.__d_dirname = (char *) get_name ();
|
|
|
|
int ret = create_thread_and_wait (&dir, true);
|
|
|
|
|
|
|
|
return ret ? virt_none : virt_directory;
|
2005-05-09 02:39:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fhandler_netdrive::fhandler_netdrive ():
|
|
|
|
fhandler_virtual ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-05-23 15:52:52 -04:00
|
|
|
int
|
2013-04-23 09:44:36 +00:00
|
|
|
fhandler_netdrive::fstat (struct stat *buf)
|
2005-05-09 02:39:34 +00:00
|
|
|
{
|
|
|
|
const char *path = get_name ();
|
|
|
|
debug_printf ("fstat (%s)", path);
|
|
|
|
|
2005-07-06 20:05:03 +00:00
|
|
|
fhandler_base::fstat (buf);
|
2005-05-09 02:39:34 +00:00
|
|
|
|
2005-05-14 21:12:10 +00:00
|
|
|
buf->st_mode = S_IFDIR | STD_RBITS | STD_XBITS;
|
2008-03-31 18:03:25 +00:00
|
|
|
buf->st_ino = get_ino ();
|
2005-05-09 02:39:34 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-03-20 14:32:49 +01:00
|
|
|
DIR *
|
|
|
|
fhandler_netdrive::opendir (int fd)
|
|
|
|
{
|
|
|
|
DIR *dir;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
dir = fhandler_virtual::opendir (fd);
|
|
|
|
if (dir && (ret = create_thread_and_wait (dir, false)))
|
|
|
|
{
|
|
|
|
free (dir->__d_dirname);
|
|
|
|
free (dir->__d_dirent);
|
|
|
|
free (dir);
|
|
|
|
dir = NULL;
|
|
|
|
set_errno (ret);
|
|
|
|
syscall_printf ("%p = opendir (%s)", dir, get_name ());
|
|
|
|
}
|
|
|
|
return dir;
|
|
|
|
}
|
|
|
|
|
2005-08-20 06:19:55 +00:00
|
|
|
int
|
|
|
|
fhandler_netdrive::readdir (DIR *dir, dirent *de)
|
2005-05-13 20:20:02 +00:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
int ret;
|
2005-05-13 20:20:02 +00:00
|
|
|
|
2024-03-20 14:32:49 +01:00
|
|
|
if (!DIR_cache[dir->__d_position])
|
2005-05-13 20:20:02 +00:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
ret = ENMFILE;
|
|
|
|
goto out;
|
|
|
|
}
|
2005-05-13 20:20:02 +00:00
|
|
|
|
2024-03-20 14:32:49 +01:00
|
|
|
if (strlen (dir->__d_dirname) == 2)
|
|
|
|
{
|
|
|
|
sys_wcstombs (de->d_name, sizeof de->d_name,
|
|
|
|
DIR_cache[dir->__d_position] + 2);
|
|
|
|
de->d_ino = hash_path_name (get_ino (), de->d_name);
|
2005-05-13 20:20:02 +00:00
|
|
|
}
|
2005-08-20 06:19:55 +00:00
|
|
|
else
|
2005-05-13 20:20:02 +00:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
char full[2 * CYG_MAX_PATH];
|
|
|
|
char *s;
|
2006-02-27 17:46:29 +00:00
|
|
|
|
2024-03-20 14:32:49 +01:00
|
|
|
sys_wcstombs (de->d_name, sizeof de->d_name,
|
|
|
|
DIR_cache[dir->__d_position]);
|
|
|
|
s = stpcpy (full, dir->__d_dirname);
|
|
|
|
*s++ = '/';
|
|
|
|
stpcpy (s, de->d_name);
|
|
|
|
de->d_ino = readdir_get_ino (full, false);
|
2005-05-13 20:20:02 +00:00
|
|
|
}
|
2024-03-20 14:32:49 +01:00
|
|
|
dir->__d_position++;
|
|
|
|
de->d_type = DT_DIR;
|
|
|
|
ret = 0;
|
|
|
|
|
2005-08-20 06:19:55 +00:00
|
|
|
out:
|
2024-03-20 14:32:49 +01:00
|
|
|
syscall_printf ("%d = readdir(%p, %p)", ret, dir, de);
|
|
|
|
return ret;
|
2005-05-13 20:20:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-07-05 16:59:56 +00:00
|
|
|
fhandler_netdrive::seekdir (DIR *dir, long pos)
|
2005-05-13 20:20:02 +00:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
::rewinddir (dir);
|
2005-05-18 10:23:40 +00:00
|
|
|
if (pos < 0)
|
|
|
|
return;
|
|
|
|
while (dir->__d_position < pos)
|
2010-09-01 10:30:52 +00:00
|
|
|
if (readdir (dir, dir->__d_dirent))
|
2005-05-18 10:23:40 +00:00
|
|
|
break;
|
2005-05-13 20:20:02 +00:00
|
|
|
}
|
|
|
|
|
2005-05-17 20:34:15 +00:00
|
|
|
void
|
|
|
|
fhandler_netdrive::rewinddir (DIR *dir)
|
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
dir->__d_position = 0;
|
2005-05-17 20:34:15 +00:00
|
|
|
}
|
|
|
|
|
2005-05-13 20:20:02 +00:00
|
|
|
int
|
|
|
|
fhandler_netdrive::closedir (DIR *dir)
|
2005-05-09 02:39:34 +00:00
|
|
|
{
|
2024-03-20 14:32:49 +01:00
|
|
|
if (dir->__handle != INVALID_HANDLE_VALUE)
|
|
|
|
delete &DIR_cache;
|
2005-05-13 20:20:02 +00:00
|
|
|
return fhandler_virtual::closedir (dir);
|
2005-05-09 02:39:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
fhandler_netdrive::open (int flags, mode_t mode)
|
|
|
|
{
|
|
|
|
if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
|
|
|
|
{
|
|
|
|
set_errno (EEXIST);
|
2013-10-31 14:26:42 +00:00
|
|
|
return 0;
|
2005-05-09 02:39:34 +00:00
|
|
|
}
|
2013-10-31 14:26:42 +00:00
|
|
|
if (flags & O_WRONLY)
|
2005-05-09 02:39:34 +00:00
|
|
|
{
|
|
|
|
set_errno (EISDIR);
|
2013-10-31 14:26:42 +00:00
|
|
|
return 0;
|
2005-05-09 02:39:34 +00:00
|
|
|
}
|
2013-10-31 14:26:42 +00:00
|
|
|
/* Open a fake handle to \\Device\\Null */
|
|
|
|
return open_null (flags);
|
2005-05-09 02:39:34 +00:00
|
|
|
}
|
|
|
|
|
2013-10-31 14:26:42 +00:00
|
|
|
int
|
|
|
|
fhandler_netdrive::close ()
|
|
|
|
{
|
|
|
|
/* Skip fhandler_virtual::close, which is a no-op. */
|
|
|
|
return fhandler_base::close ();
|
|
|
|
}
|