newlib-cygwin/winsup/cygwin/fhandler/netdrive.cc

588 lines
14 KiB
C++
Raw Normal View History

/* 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 "cygerrno.h"
#include "security.h"
#include "path.h"
#include "fhandler.h"
#include "dtable.h"
#include "cygheap.h"
#include "cygthread.h"
#include "tls_pbuf.h"
#define USE_SYS_TYPES_FD_SET
#include <shobjidl.h>
#include <shlobj.h>
#include <lm.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <dirent.h>
#include <wctype.h>
/* SMBv1 is deprectated and not even installed by default anymore on
Windows 10 and 11 machines or their servers.
As a result, neither WNetOpenEnumW() nor NetServerEnum() work as
expected anymore.
So this fhandler class now uses Network Discovery to enumerate
the "//" directory, which, unfortunately, requires to use the
shell API. */
/* There's something REALLY fishy going on in Windows. If the NFS
enumeration via WNet functions is called *before* the share enumeration
via Shell function, the Shell functions will enumerate the NFS shares
instead of the SMB shares. Un-be-lie-va-ble!
FWIW, we reverted the SMB share enumeration using WNet. */
#ifndef WNNC_NET_9P
#define WNNC_NET_9P 0x00480000
#endif
#define TERMSRV_DIR "tsclient"
#define PLAN9_DIR "wsl$"
/* 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 ()
{
while (num_entries > 0)
free (entry[--num_entries]);
free (entry);
}
size_t count () const { return num_entries; }
void add (const wchar_t *str, bool downcase = false)
{
if (num_entries >= max_entries)
{
wchar_t **newentry;
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])
{
if (downcase)
for (wchar_t *p = entry[num_entries]; (*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
{
DIR *dir;
int err;
DWORD provider;
HANDLE sem;
};
static inline int
hresult_to_errno (HRESULT wres)
{
if (SUCCEEDED (wres))
return 0;
if (((ULONG) wres & 0xffff0000)
== (ULONG) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, 0))
return geterrno_from_win_error ((ULONG) wres & 0xffff);
return EACCES;
}
/* Workaround incompatible definitions. */
#define u_long __ms_u_long
#define WS_FIONBIO _IOW('f', 126, u_long)
#define WS_POLLOUT 0x10
static bool
server_is_running_nfs (const wchar_t *servername)
{
/* Hack alarm: Only test TCP port 2049 */
struct addrinfoW hints = { 0 };
struct addrinfoW *ai = NULL, *aip;
bool ret = false;
INT wres;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
/* The services contains "nfs" only as UDP service... sigh. */
wres = GetAddrInfoW (servername, L"2049", &hints, &ai);
if (wres)
return false;
for (aip = ai; !ret && aip; aip = aip->ai_next)
{
SOCKET sock = ::socket (aip->ai_family, aip->ai_socktype,
aip->ai_flags);
if (sock != INVALID_SOCKET)
{
__ms_u_long nonblocking = 1;
::ioctlsocket (sock, WS_FIONBIO, &nonblocking);
wres = ::connect (sock, aip->ai_addr, aip->ai_addrlen);
if (wres == 0)
ret = true;
else if (WSAGetLastError () == WSAEWOULDBLOCK)
{
WSAPOLLFD fds = { .fd = sock,
.events = WS_POLLOUT };
wres = WSAPoll (&fds, 1, 1500L);
if (wres > 0 && fds.revents == WS_POLLOUT)
ret = true;
}
::closesocket (sock);
}
}
FreeAddrInfoW (ai);
return ret;
}
/* Use only to enumerate the Network top level. */
static DWORD
thread_netdrive_wsd (void *arg)
{
netdriveinf *ndi = (netdriveinf *) arg;
DIR *dir = ndi->dir;
IEnumShellItems *netitem_enum;
IShellItem *netparent;
HRESULT wres;
ReleaseSemaphore (ndi->sem, 1, NULL);
wres = CoInitialize (NULL);
if (FAILED (wres))
{
ndi->err = hresult_to_errno (wres);
goto out;
}
wres = SHGetKnownFolderItem (FOLDERID_NetworkFolder, KF_FLAG_DEFAULT,
NULL, IID_PPV_ARGS (&netparent));
if (FAILED (wres))
{
ndi->err = hresult_to_errno (wres);
goto out;
}
wres = netparent->BindToHandler (NULL, BHID_StorageEnum,
IID_PPV_ARGS (&netitem_enum));
if (FAILED (wres))
{
ndi->err = hresult_to_errno (wres);
netparent->Release ();
goto out;
}
netitem_enum->Reset ();
/* 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);
do
{
IShellItem *netitem = NULL;
LPWSTR item_name = NULL;
wres = netitem_enum->Next (1, &netitem, NULL);
if (wres == S_OK)
2005-08-12 10:39:13 +08:00
{
if (netitem->GetDisplayName (SIGDN_PARENTRELATIVEPARSING,
&item_name) == S_OK)
{
/* Skip "\\" on server names and downcase */
DIR_cache.add (item_name + 2, true);
CoTaskMemFree (item_name);
}
netitem->Release ();
}
}
while (wres == S_OK);
netitem_enum->Release ();
netparent->Release ();
ndi->err = 0;
out:
CoUninitialize ();
ReleaseSemaphore (ndi->sem, 1, NULL);
return 0;
}
static DWORD
thread_netdrive_wnet (void *arg)
{
netdriveinf *ndi = (netdriveinf *) arg;
DIR *dir = ndi->dir;
DWORD wres;
size_t entry_cache_size = DIR_cache.count ();
WCHAR provider[256], *dummy = NULL;
wchar_t srv_name[CYG_MAX_PATH];
NETRESOURCEW nri = { 0 };
LPNETRESOURCEW nro;
tmp_pathbuf tp;
HANDLE dom = NULL;
DWORD cnt, size;
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 ();
wres = WNetGetResourceInformationW (&nri, nro,
(size = NT_MAX_PATH, &size), &dummy);
if (wres != NO_ERROR)
{
ndi->err = geterrno_from_win_error (wres);
goto out;
}
wres = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
RESOURCEUSAGE_ALL, nro, &dom);
if (wres != NO_ERROR)
{
ndi->err = geterrno_from_win_error (wres);
goto out;
}
while ((wres = WNetEnumResourceW (dom, (cnt = 1, &cnt), nro,
(size = NT_MAX_PATH, &size))) == NO_ERROR)
{
size_t cache_idx;
/* Skip server name and trailing backslash */
wchar_t *name = nro->lpRemoteName + 2;
name = wcschr (name, L'\\');
if (!name)
continue;
++name;
if (ndi->provider == WNNC_NET_MS_NFS)
{
wchar_t *nm = name;
/* Convert from "ANSI embedded in widechar" to multibyte and convert
back to widechar. */
char mbname[wcslen (name) + 1];
char *mb = mbname;
while ((*mb++ = *nm++))
;
sys_mbstowcs_alloc (&name, HEAP_NOTHEAP, mbname);
if (!name)
{
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'/')
;
}
/* 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)
free (name);
}
out:
if (dom)
WNetCloseEnum (dom);
ReleaseSemaphore (ndi->sem, 1, NULL);
return 0;
}
static DWORD
create_thread_and_wait (DIR *dir)
{
netdriveinf ndi = { dir, 0, 0, NULL };
cygthread *thr;
/* 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");
if (thr->detach (ndi.sem))
ndi.err = EINTR;
CloseHandle (ndi.sem);
/* Add wsl$ if Plan 9 is installed */
if (WNetGetProviderNameW (WNNC_NET_9P, provider, (size = 256, &size))
== NO_ERROR)
DIR_cache.add (__CONCAT(L,PLAN9_DIR));
goto out;
}
/* For shares, use WNet functions. */
/* Try NFS first, if the name contains a dot (i. e., supposedly is a FQDN
as used in NFS server enumeration). */
if (strchr (dir->__d_dirname, '.'))
{
ndi.provider = WNNC_NET_MS_NFS;
ndi.sem = CreateSemaphore (&sec_none_nih, 0, 2, NULL);
thr = new cygthread (thread_netdrive_wnet, &ndi, "netdrive_nfs");
if (thr->detach (ndi.sem))
ndi.err = EINTR;
CloseHandle (ndi.sem);
if (ndi.err == EINTR)
goto out;
}
/* Eventually, try TERMSRV/P9/SMB via WNet for share enumeration,
depending on "server" name. */
if (!strcmp (dir->__d_dirname + 2, TERMSRV_DIR))
ndi.provider = WNNC_NET_TERMSRV;
else if (!strcmp (dir->__d_dirname + 2, PLAN9_DIR))
ndi.provider = WNNC_NET_9P;
else
ndi.provider = WNNC_NET_SMB;
ndi.sem = CreateSemaphore (&sec_none_nih, 0, 2, NULL);
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;
}
* Makefile.in (DLL_OFILES): Add fhandler_procsys.o. * devices.h (enum fh_devices): Add FH_PROCSYS. * devices.in (dev_procsys_storage): New device. * devices.cc: Regenerate. * dtable.cc (build_fh_pc): Add code to allocate fhandler_procsys. * fhandler.h (proc_len): Convert to size_t. (procsys): Declare. (procsys_len): Declare. (enum virtual_ftype_t): Move here from fhandler_virtual.h. Add members supported by fhandler_procsys. (fhandler_virtual::exists): Return virtual_ftype_t. Change in all derived classes. (class fhandler_procsys): New class. (fhandler_union): Add fhandler_procnet and fhandler_procsys members. * fhandler_disk_file.cc (__DIR_mounts::check_missing_mount): Use ro_u_proc. (fhandler_base::fstat_by_handle): Don't copy attributes if file is an NT device. (fhandler_base::fstat_by_name): Ditto. * fhandler_netdrive.cc (fhandler_netdrive::exists): Return virtual_ftype_t. * fhandler_proc.cc (proc_tab): Sort alphabetically. Use _VN macro to store length. (proc_len): Change to size_t. (proc_tab_cmp): New static function. (virt_tab_search): New function to search entry in virt_tab_t arrays. Use throughout in /proc and sibling classes instead of loop. (fhandler_proc::exists): Return virtual_ftype_t. * fhandler_process.cc (process_tab): Sort alphabetically. Use _VN macro to store length. (fhandler_process::exists): Return virtual_ftype_t. (fhandler_process::open): Simplify code. * fhandler_procnet.cc (procnet_tab): Sort alphabetically. Use _VN macro to store length. (fhandler_procnet::exists): Return virtual_ftype_t. (fhandler_procnet::open): Simplify. * fhandler_procsys.cc: New file. * fhandler_registry.cc (fhandler_registry::exists): Return virtual_ftype_t. * fhandler_virtual.cc (fhandler_virtual::exists): Ditto. * fhandler_virtual.h (enum virtual_ftype_t): Move to fhandler.h. (virt_tab_t): Add name_len member. (_VN): New macro. (virt_tab_search): Declare. * mount.cc (mount_info::conv_to_win32_path): Fix comment. Backslashify isprocsys_dev paths. * ntdll.h (STATUS_OBJECT_TYPE_MISMATCH): Define (STATUS_INSTANCE_NOT_AVAILABLE): Define. (STATUS_PIPE_NOT_AVAILABLE): Define. (STATUS_INVALID_PIPE_STATE): Define. (STATUS_PIPE_BUSY): Define. (SYMBOLIC_LINK_QUERY): Define. (NtOpenSymbolicLinkObject): Declare. (NtQuerySymbolicLinkObject): Declare. * path.cc (path_conv::check): Accommodate fact that exists method returns virtual_ftype_t now. Add cases for new virtual_ftype_t types. (cygwin_conv_path): Add GLOBALROOT prefix to native device paths. Make sure to strip \\?\ prefix only for actual filesystem-based paths, not for all paths. * path.h (isproc_dev): Add FH_PROCSYS. (isprocsys_dev): Define.
2010-09-06 17:47:01 +08:00
virtual_ftype_t
fhandler_netdrive::exists ()
{
if (strlen (get_name ()) == 2)
* Makefile.in (DLL_OFILES): Add fhandler_procsys.o. * devices.h (enum fh_devices): Add FH_PROCSYS. * devices.in (dev_procsys_storage): New device. * devices.cc: Regenerate. * dtable.cc (build_fh_pc): Add code to allocate fhandler_procsys. * fhandler.h (proc_len): Convert to size_t. (procsys): Declare. (procsys_len): Declare. (enum virtual_ftype_t): Move here from fhandler_virtual.h. Add members supported by fhandler_procsys. (fhandler_virtual::exists): Return virtual_ftype_t. Change in all derived classes. (class fhandler_procsys): New class. (fhandler_union): Add fhandler_procnet and fhandler_procsys members. * fhandler_disk_file.cc (__DIR_mounts::check_missing_mount): Use ro_u_proc. (fhandler_base::fstat_by_handle): Don't copy attributes if file is an NT device. (fhandler_base::fstat_by_name): Ditto. * fhandler_netdrive.cc (fhandler_netdrive::exists): Return virtual_ftype_t. * fhandler_proc.cc (proc_tab): Sort alphabetically. Use _VN macro to store length. (proc_len): Change to size_t. (proc_tab_cmp): New static function. (virt_tab_search): New function to search entry in virt_tab_t arrays. Use throughout in /proc and sibling classes instead of loop. (fhandler_proc::exists): Return virtual_ftype_t. * fhandler_process.cc (process_tab): Sort alphabetically. Use _VN macro to store length. (fhandler_process::exists): Return virtual_ftype_t. (fhandler_process::open): Simplify code. * fhandler_procnet.cc (procnet_tab): Sort alphabetically. Use _VN macro to store length. (fhandler_procnet::exists): Return virtual_ftype_t. (fhandler_procnet::open): Simplify. * fhandler_procsys.cc: New file. * fhandler_registry.cc (fhandler_registry::exists): Return virtual_ftype_t. * fhandler_virtual.cc (fhandler_virtual::exists): Ditto. * fhandler_virtual.h (enum virtual_ftype_t): Move to fhandler.h. (virt_tab_t): Add name_len member. (_VN): New macro. (virt_tab_search): Declare. * mount.cc (mount_info::conv_to_win32_path): Fix comment. Backslashify isprocsys_dev paths. * ntdll.h (STATUS_OBJECT_TYPE_MISMATCH): Define (STATUS_INSTANCE_NOT_AVAILABLE): Define. (STATUS_PIPE_NOT_AVAILABLE): Define. (STATUS_INVALID_PIPE_STATE): Define. (STATUS_PIPE_BUSY): Define. (SYMBOLIC_LINK_QUERY): Define. (NtOpenSymbolicLinkObject): Declare. (NtQuerySymbolicLinkObject): Declare. * path.cc (path_conv::check): Accommodate fact that exists method returns virtual_ftype_t now. Add cases for new virtual_ftype_t types. (cygwin_conv_path): Add GLOBALROOT prefix to native device paths. Make sure to strip \\?\ prefix only for actual filesystem-based paths, not for all paths. * path.h (isproc_dev): Add FH_PROCSYS. (isprocsys_dev): Define.
2010-09-06 17:47:01 +08:00
return virt_rootdir;
wchar_t name[MAX_PATH];
struct addrinfoW *ai;
INT ret;
DWORD protocol = 0;
/* Handle "tsclient" (Microsoft Terminal Services) and
"wsl$" (Plan 9 Network Provider) explicitely.
Both obviously don't resolve with GetAddrInfoW. */
if (!strcmp (get_name () + 2, TERMSRV_DIR))
protocol = WNNC_NET_TERMSRV;
else if (!strcmp (get_name () + 2, PLAN9_DIR))
protocol = WNNC_NET_9P;
if (protocol)
{
WCHAR provider[256];
DWORD size = 256;
if (WNetGetProviderNameW (protocol, provider, &size) == NO_ERROR)
return virt_directory;
return virt_none;
}
/* Hopefully we are allowed to assume an IP network with existing name
resolution these days. Therefore, just try to resolve the name
into IP addresses. This may take up to about 3 secs if the name
doesn't exist, or about 8 secs if DNS is unavailable. */
sys_mbstowcs (name, CYG_MAX_PATH, get_name ());
ret = GetAddrInfoW (name + 2, NULL, NULL, &ai);
if (!ret)
FreeAddrInfoW (ai);
return ret ? virt_none : virt_directory;
}
fhandler_netdrive::fhandler_netdrive ():
fhandler_virtual ()
{
}
int
2013-04-23 17:44:36 +08:00
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;
* smallprint.cc (__small_vswprintf): Fix uninitialized usage of `w'. Revamp advisory file locking to avoid cross reference pointers as well as to allow BSD flock semantics. More agressively delete unused nodes and sync objects. * fhandler.h (fhandler_base::ino): Rename from namehash. Fix comment. (fhandler_base::node): Remove. (fhandler_base::unique_id): Add. (fhandler_base::del_my_locks): New method. (get_ino): Rename from get_namehash. Change usage throughout Cygwin. (get_unique_id): New method. * fhandler.cc (fhandler_base::close): Call own del_my_locks method. Fix comment. (fhandler_base::fhandler_base): Accommodate new and changed members. (fhandler_base::fixup_after_fork): Call del_my_locks. (fhandler_base::fixup_after_exec): Ditto for files with close-on-exec flag set. * fhandler_disk_file.cc (get_ino_by_handle): Rename from readdir_get_ino_by_handle. Accommodate throughout. (fhandler_base::open_fs): Fill ino with inode number if FS has good inodes. Allocate a LUID and store in unique_id to recognize file descriptors referencing the same file object. * flock.cc: Drop flock TODO comments. Use explicit types __dev32_t and __ino64_t instead of dev_t and ino_t. (LOCK_OBJ_NAME_LEN): Change to reflect longer lf_id length. (get_obj_handle_count): New method. (lockf_t::lf_id): Change type to long long. (inode_t::get_lock_obj_handle_count): Drop in favor of static function get_obj_handle_count. (inode_t::del_locks): Remove. (inode_t::get): Add create_if_missing flag argument. (inode_t::del_my_locks): Reimplement to handle POSIX and BSD flock locks. Return if node can be deleted or not. (inode_t::~inode_t): Ditto. Close handles to i_dir and i_mtx. (fixup_lockf_after_fork): Remove. (fhandler_base::del_my_locks): New method. (fixup_lockf_after_exec): Check if node can be deleted. (inode_t::get): Only create node if create_if_missing is set. Lock the returned node here before unlocking the node list. (inode_t::get_all_locks_list): Accommodate new lf_id length. (inode_t::create_lock_obj): Ditto. (lockf_t::open_lock_obj): Ditto. Change return type to bool. De-const. Set lf_obj instead of returning a handle. (lockf_t::del_lock_obj): Call SetEvent only if new incoming parameters allow it. Explain how it's supposed to work. (fhandler_disk_file::lock): Only fetch file length in SEEK_END case. Use NtQueryInformationFile(FileStandardInformation) instead of calling fstat_by_handle. Always unlock node before returning. Use fhandler's unique id to create lf_id for BSD flock locks. Rely on node lock from inode_t::get. Call del_lock_obj on removed locks here to allow explicit unlocking. Delete node if no lock exists on the file anymore. (lf_setlock): Get file handle as additional parameter. Handle the fact that lf_getblock now always opens the attached event object. Reactivate erroneously applied patch which deactivates setting thread priority. Additionally handle blocking on BSD flock locks. (lf_clearlock): Get file handle as additional parameter. (lf_getlock): Close event handle opened by lf_getblock. (lf_getblock): Open potentially blocking event object here and check its signal state if it's a BSD flock lock. (lf_wakelock): Get file handle as additional parameter. * fork.cc (frok::child): Drop call to fixup_lockf_after_fork. * ntdll.h (struct _EVENT_BASIC_INFORMATION): Define. (enum _EVENT_INFORMATION_CLASS): Define. (NtQueryEvent): Declare. * fhandler.h (fhandler_base::fs_flags): Remove. (fhandler_base::set_fs_flags): Remove. (fhandler_base::get_fs_flags): Remove. * fhandler.cc (fhandler_base::write): Check for sparse file using pc.fs_flags(). * fhandler_disk_file.cc (fhandler_disk_file::ftruncate): Ditto. The return of the volume serial number in fs_info. * fhandler.h (get_dev): New method. * fhandler_disk_file.cc (fhandler_base::fstat_by_handle): Drop call to NtQueryVolumeInformationFile(FileFsVolumeInformation). Just use get_dev() method. * fhandler_fifo.cc (fhandler_fifo::open) Use device ID and inode number to generate fifo name. * path.h (fs_info::sernum): New member. (fs_info::serial_number): New method. (path_conv::fs_serial_number): New method. * path.cc (fs_info::update): Fetch volume serial number and store in sernum.
2008-04-01 02:03:25 +08:00
buf->st_ino = get_ino ();
return 0;
}
DIR *
fhandler_netdrive::opendir (int fd)
{
DIR *dir;
int ret;
dir = fhandler_virtual::opendir (fd);
dir->__handle = (char *) new dir_cache ();
if (dir && (ret = create_thread_and_wait (dir)))
{
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;
}
int
fhandler_netdrive::readdir (DIR *dir, dirent *de)
{
int ret;
if (!DIR_cache[dir->__d_position])
{
ret = ENMFILE;
goto out;
}
sys_wcstombs (de->d_name, sizeof de->d_name, DIR_cache[dir->__d_position]);
if (strlen (dir->__d_dirname) == 2)
de->d_ino = hash_path_name (get_ino (), de->d_name);
else
{
char full[2 * CYG_MAX_PATH];
char *s;
s = stpcpy (full, dir->__d_dirname);
*s++ = '/';
stpcpy (s, de->d_name);
de->d_ino = readdir_get_ino (full, false);
}
dir->__d_position++;
de->d_type = DT_DIR;
ret = 0;
out:
syscall_printf ("%d = readdir(%p, %p)", ret, dir, de);
return ret;
}
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)
{
dir->__d_position = 0;
}
int
fhandler_netdrive::closedir (DIR *dir)
{
if (dir->__handle != INVALID_HANDLE_VALUE)
delete &DIR_cache;
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 ();
}