Cygwin: AF_UNIX: Redesign various aspects

* Change set_socket_type/get_socket_type to virtual methods
* Move various variables into af_unix_shmem_t
* Change sun_name_t to match new usage pattern
* Move shut_state definition and add a name for the 0 value
* Allow marking packet as administrative packet.  This allows
  filtering out info packets exchange between peers and tweak
  data accordingly.
* Rename send_my_name to send_sock_info and send credentials
  if not called from bind (so the socket was already connected)
* Handle SO_PASSCRED in setsockopt/getsockopt
* Add input size checking to setsockopt/getsockopt
* Use NT functions where appropriate

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2018-03-18 20:46:43 +01:00
parent a366a8fc42
commit 4fe086c84f
2 changed files with 442 additions and 190 deletions

View File

@ -579,8 +579,8 @@ class fhandler_socket: public fhandler_base
void set_addr_family (int af) {addr_family = af;} void set_addr_family (int af) {addr_family = af;}
int get_addr_family () {return addr_family;} int get_addr_family () {return addr_family;}
void set_socket_type (int st) { type = st;} virtual void set_socket_type (int st) { type = st;}
int get_socket_type () {return type;} virtual int get_socket_type () {return type;}
/* select.cc */ /* select.cc */
virtual select_record *select_read (select_stuff *) = 0; virtual select_record *select_read (select_stuff *) = 0;
@ -851,6 +851,14 @@ public:
} }
}; };
/* Internal representation of shutdown states */
enum shut_state {
_SHUT_NONE = 0,
_SHUT_RECV = 1,
_SHUT_SEND = 2,
_SHUT_MASK = 3
};
class sun_name_t class sun_name_t
{ {
public: public:
@ -861,19 +869,10 @@ class sun_name_t
/* Allows 108 bytes sun_path plus trailing NUL */ /* Allows 108 bytes sun_path plus trailing NUL */
char _nul[sizeof (struct sockaddr_un) + 1]; char _nul[sizeof (struct sockaddr_un) + 1];
}; };
sun_name_t (); sun_name_t () { set (NULL, 0); }
sun_name_t (const struct sockaddr *name, __socklen_t namelen); sun_name_t (const struct sockaddr *name, __socklen_t namelen)
{ set ((const struct sockaddr_un *) name, namelen); }
void *operator new (size_t) __attribute__ ((nothrow)) void set (const struct sockaddr_un *name, __socklen_t namelen);
{ return cmalloc_abort (HEAP_FHANDLER, sizeof (sun_name_t)); }
void operator delete (void *p) {cfree (p);}
};
/* Internal representation of shutdown states */
enum shut_state {
_SHUT_READ = 1,
_SHUT_WRITE = 2,
_SHUT_RW = 3
}; };
/* For each AF_UNIX socket, we need to maintain socket-wide data, /* For each AF_UNIX socket, we need to maintain socket-wide data,
@ -893,7 +892,13 @@ class af_unix_shmem_t
LONG _binding_state; /* bind_state */ LONG _binding_state; /* bind_state */
LONG _shutdown; /* shut_state */ LONG _shutdown; /* shut_state */
LONG _so_error; /* SO_ERROR */ LONG _so_error; /* SO_ERROR */
LONG _so_passcred; /* SO_PASSCRED */
LONG _reuseaddr; /* dummy */ LONG _reuseaddr; /* dummy */
int _type; /* socket type */
sun_name_t _sun_path;
sun_name_t _peer_sun_path;
struct ucred _sock_cred; /* filled at listen time */
struct ucred _peer_cred; /* filled at connect time */
public: public:
void bind_lock () { _bind_lock.lock (); } void bind_lock () { _bind_lock.lock (); }
@ -917,13 +922,31 @@ class af_unix_shmem_t
{ return (int) InterlockedExchange (&_shutdown, shut); } { return (int) InterlockedExchange (&_shutdown, shut); }
int shutdown () const { return (int) _shutdown; } int shutdown () const { return (int) _shutdown; }
int so_error (int err) int so_error (int err) { return (int) InterlockedExchange (&_so_error, err); }
{ return (int) InterlockedExchange (&_so_error, err); }
int so_error () const { return _so_error; } int so_error () const { return _so_error; }
bool so_passcred (bool pc)
{ return (bool) InterlockedExchange (&_so_passcred, pc); }
bool so_passcred () const { return _so_passcred; }
int reuseaddr (int val) int reuseaddr (int val)
{ return (int) InterlockedExchange (&_reuseaddr, val); } { return (int) InterlockedExchange (&_reuseaddr, val); }
int reuseaddr () const { return _reuseaddr; } int reuseaddr () const { return _reuseaddr; }
void set_socket_type (int val) { _type = val; }
int get_socket_type () const { return _type; }
void sun_path (struct sockaddr_un *un, __socklen_t unlen)
{ _sun_path.set (un, unlen); }
void peer_sun_path (struct sockaddr_un *un, __socklen_t unlen)
{ _peer_sun_path.set (un, unlen); }
sun_name_t *sun_path () {return &_sun_path;}
sun_name_t *peer_sun_path () {return &_peer_sun_path;}
void sock_cred (struct ucred *uc) { _sock_cred = *uc; }
struct ucred *sock_cred () { return &_sock_cred; }
void peer_cred (struct ucred *uc) { _peer_cred = *uc; }
struct ucred *peer_cred () { return &_peer_cred; }
}; };
class fhandler_socket_unix : public fhandler_socket class fhandler_socket_unix : public fhandler_socket
@ -938,9 +961,6 @@ class fhandler_socket_unix : public fhandler_socket
HANDLE connect_wait_thr; HANDLE connect_wait_thr;
HANDLE cwt_termination_evt; HANDLE cwt_termination_evt;
PVOID cwt_param; PVOID cwt_param;
sun_name_t *sun_path;
sun_name_t *peer_sun_path;
struct ucred peer_cred;
void bind_lock () { shmem->bind_lock (); } void bind_lock () { shmem->bind_lock (); }
void bind_unlock () { shmem->bind_unlock (); } void bind_unlock () { shmem->bind_unlock (); }
@ -960,8 +980,12 @@ class fhandler_socket_unix : public fhandler_socket
int saw_shutdown () const { return shmem->shutdown (); } int saw_shutdown () const { return shmem->shutdown (); }
int so_error (int err) { return shmem->so_error (err); } int so_error (int err) { return shmem->so_error (err); }
int so_error () const { return shmem->so_error (); } int so_error () const { return shmem->so_error (); }
bool so_passcred (bool pc) { return shmem->so_passcred (pc); }
bool so_passcred () const { return shmem->so_passcred (); }
int reuseaddr (int err) { return shmem->reuseaddr (err); } int reuseaddr (int err) { return shmem->reuseaddr (err); }
int reuseaddr () const { return shmem->reuseaddr (); } int reuseaddr () const { return shmem->reuseaddr (); }
void set_socket_type (int val) { shmem->set_socket_type (val); }
int get_socket_type () const { return shmem->get_socket_type (); }
int create_shmem (); int create_shmem ();
int reopen_shmem (); int reopen_shmem ();
@ -977,26 +1001,37 @@ class fhandler_socket_unix : public fhandler_socket
HANDLE autobind (sun_name_t *sun); HANDLE autobind (sun_name_t *sun);
wchar_t get_type_char (); wchar_t get_type_char ();
void set_pipe_non_blocking (bool nonblocking); void set_pipe_non_blocking (bool nonblocking);
int send_my_name (); int send_sock_info (bool from_bind);
int recv_peer_name (); int grab_admin_pkg ();
int recv_peer_info ();
static NTSTATUS npfs_handle (HANDLE &nph); static NTSTATUS npfs_handle (HANDLE &nph);
HANDLE create_pipe (bool single_instance); HANDLE create_pipe (bool single_instance);
HANDLE create_pipe_instance (); HANDLE create_pipe_instance ();
NTSTATUS open_pipe (PUNICODE_STRING pipe_name, bool send_name); NTSTATUS open_pipe (PUNICODE_STRING pipe_name, bool xchg_sock_info);
int wait_pipe (PUNICODE_STRING pipe_name); int wait_pipe (PUNICODE_STRING pipe_name);
int connect_pipe (PUNICODE_STRING pipe_name); int connect_pipe (PUNICODE_STRING pipe_name);
int listen_pipe (); int listen_pipe ();
ULONG peek_pipe (PFILE_PIPE_PEEK_BUFFER pbuf, ULONG psize, HANDLE evt);
int disconnect_pipe (HANDLE ph); int disconnect_pipe (HANDLE ph);
sun_name_t *get_sun_path () {return sun_path;} /* The NULL pointer check is required for FS methods like fstat. When
sun_name_t *get_peer_sun_path () {return peer_sun_path;} called via stat or lstat, there's no shared memory, just a path in pc. */
void set_sun_path (struct sockaddr_un *un, __socklen_t unlen); sun_name_t *sun_path () {return shmem ? shmem->sun_path () : NULL;}
void set_sun_path (sun_name_t *snt) sun_name_t *peer_sun_path () {return shmem->peer_sun_path ();}
{ snt ? set_sun_path (&snt->un, snt->un_len) : set_sun_path (NULL, 0); } void sun_path (struct sockaddr_un *un, __socklen_t unlen)
void set_peer_sun_path (struct sockaddr_un *un, __socklen_t unlen); { shmem->sun_path (un, unlen); }
void set_peer_sun_path (sun_name_t *snt) void sun_path (sun_name_t *snt)
{ snt ? set_peer_sun_path (&snt->un, snt->un_len) { snt ? sun_path (&snt->un, snt->un_len) : sun_path (NULL, 0); }
: set_peer_sun_path (NULL, 0); } void peer_sun_path (struct sockaddr_un *un, __socklen_t unlen)
{ shmem->peer_sun_path (un, unlen); }
void peer_sun_path (sun_name_t *snt)
{ snt ? peer_sun_path (&snt->un, snt->un_len)
: peer_sun_path (NULL, 0); }
void init_cred ();
void set_cred (); void set_cred ();
void sock_cred (struct ucred *uc) { shmem->sock_cred (uc); }
struct ucred *sock_cred () { return shmem->sock_cred (); }
void peer_cred (struct ucred *uc) { shmem->peer_cred (uc); }
struct ucred *peer_cred () { return shmem->peer_cred (); }
void fixup_after_fork (HANDLE parent); void fixup_after_fork (HANDLE parent);
void fixup_after_exec (); void fixup_after_exec ();
void set_close_on_exec (bool val); void set_close_on_exec (bool val);

View File

@ -58,8 +58,8 @@
- <uniq_id> is an 8 byte hex unique number - <uniq_id> is an 8 byte hex unique number
Note: We use MAX_PATH below for convenience where sufficient. It's Note: We use MAX_PATH below for convenience where sufficient. It's
big enough to hold sun_paths as well as pipe names so we don't have big enough to hold sun_paths as well as pipe names as well as packet
to use tmp_pathbuf as often. headers etc., so we don't have to use tmp_pathbuf as often.
Every packet sent to a peer is a combination of the socket name of the Every packet sent to a peer is a combination of the socket name of the
local socket, the ancillary data, and the actual user data. The data local socket, the ancillary data, and the actual user data. The data
@ -78,18 +78,17 @@ class af_unix_pkt_hdr_t
{ {
public: public:
uint16_t pckt_len; /* size of packet including header */ uint16_t pckt_len; /* size of packet including header */
uint8_t shut_info; /* shutdown info. SHUT_RD means bool admin_pkg : 1; /* admin packets are marked as such */
SHUT_RD on the local side, so the shut_state shut_info : 2; /* _SHUT_RECV /_SHUT_SEND. */
peer must not send further packets,
vice versa for SHUT_WR. SHUT_RDWR
is followed by closing the pipe
handle. */
uint8_t name_len; /* size of name, a sockaddr_un */ uint8_t name_len; /* size of name, a sockaddr_un */
uint16_t cmsg_len; /* size of ancillary data block */ uint16_t cmsg_len; /* size of ancillary data block */
uint16_t data_len; /* size of user data */ uint16_t data_len; /* size of user data */
void init (uint8_t s, uint8_t n, uint16_t c, uint16_t d) af_unix_pkt_hdr_t (bool a, shut_state s, uint8_t n, uint16_t c, uint16_t d)
{ init (a, s, n, c, d); }
void init (bool a, shut_state s, uint8_t n, uint16_t c, uint16_t d)
{ {
admin_pkg = a;
shut_info = s; shut_info = s;
name_len = n; name_len = n;
cmsg_len = c; cmsg_len = c;
@ -116,7 +115,7 @@ class af_unix_pkt_hdr_t
#define AF_UNIX_PKT_CMSG(phdr) \ #define AF_UNIX_PKT_CMSG(phdr) \
({ \ ({ \
af_unix_pkt_hdr_t *_p = phdr; \ af_unix_pkt_hdr_t *_p = phdr; \
(void *)(((PBYTE)(_p)) + AF_UNIX_PKT_OFFSETOF_CMSG (_p)); \ (struct cmsghdr *)(((PBYTE)(_p)) + AF_UNIX_PKT_OFFSETOF_CMSG (_p)); \
}) })
#define AF_UNIX_PKT_DATA(phdr) \ #define AF_UNIX_PKT_DATA(phdr) \
({ \ ({ \
@ -157,21 +156,16 @@ GUID __cygwin_socket_guid = {
/* Default timeout value of connect: 20 secs, as on Linux. */ /* Default timeout value of connect: 20 secs, as on Linux. */
#define AF_UNIX_CONNECT_TIMEOUT (-20 * NS100PERSEC) #define AF_UNIX_CONNECT_TIMEOUT (-20 * NS100PERSEC)
sun_name_t::sun_name_t () void
{ sun_name_t::set (const struct sockaddr_un *name, socklen_t namelen)
un_len = sizeof (sa_family_t);
un.sun_family = AF_UNIX;
_nul[sizeof (struct sockaddr_un)] = '\0';
}
sun_name_t::sun_name_t (const struct sockaddr *name, socklen_t namelen)
{ {
if (namelen < 0) if (namelen < 0)
namelen = 0; namelen = 0;
un_len = namelen < (__socklen_t) sizeof un ? namelen : sizeof un; un_len = namelen < (__socklen_t) sizeof un ? namelen : sizeof un;
if (name) un.sun_family = AF_UNIX;
if (name && un_len)
memcpy (&un, name, un_len); memcpy (&un, name, un_len);
_nul[sizeof (struct sockaddr_un)] = '\0'; _nul[un_len] = '\0';
} }
static HANDLE static HANDLE
@ -618,24 +612,49 @@ fhandler_socket_unix::set_pipe_non_blocking (bool nonblocking)
} }
} }
/* Apart from being called from bind(), from_bind indicates that the caller
already locked state_lock, so send_sock_info doesn't lock, only unlocks
state_lock. */
int int
fhandler_socket_unix::send_my_name () fhandler_socket_unix::send_sock_info (bool from_bind)
{ {
sun_name_t *sun; sun_name_t *sun;
size_t name_len = 0; size_t plen;
size_t clen = 0;
af_unix_pkt_hdr_t *packet; af_unix_pkt_hdr_t *packet;
NTSTATUS status; NTSTATUS status;
IO_STATUS_BLOCK io; IO_STATUS_BLOCK io;
bind_lock (); if (!from_bind)
sun = get_sun_path (); {
name_len = sun ? sun->un_len : 0; state_lock ();
packet = (af_unix_pkt_hdr_t *) alloca (sizeof *packet + name_len); /* When called from connect, initialize credentials. accept4 already
did it (copied from listening socket). */
if (sock_cred ()->pid == 0)
set_cred ();
}
sun = sun_path ();
plen = sizeof *packet + sun->un_len;
/* When called from connect/accept4, send SCM_CREDENTIALS, too. */
if (!from_bind)
{
clen = CMSG_SPACE (sizeof (struct ucred));
plen += clen;
}
packet = (af_unix_pkt_hdr_t *) alloca (plen);
packet->init (true, _SHUT_NONE, sun->un_len, clen, 0);
if (sun) if (sun)
memcpy (AF_UNIX_PKT_NAME (packet), &sun->un, name_len); memcpy (AF_UNIX_PKT_NAME (packet), &sun->un, sun->un_len);
bind_unlock (); if (!from_bind)
{
struct cmsghdr *cmsg = AF_UNIX_PKT_CMSG (packet);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_CREDENTIALS;
cmsg->cmsg_len = CMSG_LEN (sizeof (struct ucred));
memcpy (CMSG_DATA(cmsg), sock_cred (), sizeof (struct ucred));
}
packet->init (0, name_len, 0, 0); state_unlock ();
/* The theory: Fire and forget. */ /* The theory: Fire and forget. */
io_lock (); io_lock ();
@ -652,10 +671,79 @@ fhandler_socket_unix::send_my_name ()
return 0; return 0;
} }
/* Returns an error code. Locking is not required, user space doesn't know /* Checks if the next packet in the pipe is an administrative packet.
about this socket yet. */ If so, it reads it from the pipe, handles it. Returns an error code. */
int int
fhandler_socket_unix::recv_peer_name () fhandler_socket_unix::grab_admin_pkg ()
{
HANDLE evt;
NTSTATUS status;
IO_STATUS_BLOCK io;
/* MAX_PATH is more than sufficient for admin packets. */
PFILE_PIPE_PEEK_BUFFER pbuf = (PFILE_PIPE_PEEK_BUFFER) alloca (MAX_PATH);
if (!(evt = create_event ()))
return 0;
io_lock ();
ULONG ret_len = peek_pipe (pbuf, MAX_PATH, evt);
if (pbuf->NumberOfMessages == 0 || ret_len < sizeof (af_unix_pkt_hdr_t))
{
io_unlock ();
NtClose (evt);
return 0;
}
af_unix_pkt_hdr_t *packet = (af_unix_pkt_hdr_t *) pbuf->Data;
if (!packet->admin_pkg)
io_unlock ();
else
{
packet = (af_unix_pkt_hdr_t *) pbuf;
status = NtReadFile (get_handle (), evt, NULL, NULL, &io, packet,
MAX_PATH, NULL, NULL);
if (status == STATUS_PENDING)
{
/* Very short-lived */
status = NtWaitForSingleObject (evt, FALSE, NULL);
if (NT_SUCCESS (status))
status = io.Status;
}
io_unlock ();
if (NT_SUCCESS (status))
{
state_lock ();
if (packet->shut_info)
{
/* Peer's shutdown sends the SHUT flags as used by the peer.
They have to be reversed for our side. */
int shut_info = saw_shutdown ();
if (packet->shut_info & _SHUT_RECV)
shut_info |= _SHUT_SEND;
if (packet->shut_info & _SHUT_SEND)
shut_info |= _SHUT_RECV;
saw_shutdown (shut_info);
/* FIXME: anything else here? */
}
if (packet->name_len > 0)
peer_sun_path (AF_UNIX_PKT_NAME (packet), packet->name_len);
if (packet->cmsg_len > 0)
{
struct cmsghdr *cmsg = (struct cmsghdr *)
alloca (packet->cmsg_len);
memcpy (cmsg, AF_UNIX_PKT_CMSG (packet), packet->cmsg_len);
if (cmsg->cmsg_level == SOL_SOCKET
&& cmsg->cmsg_type == SCM_CREDENTIALS)
peer_cred ((struct ucred *) CMSG_DATA(cmsg));
}
state_unlock ();
}
}
NtClose (evt);
return 0;
}
/* Returns an error code. Locking is not required when called from accept4,
user space doesn't know about this socket yet. */
int
fhandler_socket_unix::recv_peer_info ()
{ {
HANDLE evt; HANDLE evt;
NTSTATUS status; NTSTATUS status;
@ -667,7 +755,7 @@ fhandler_socket_unix::recv_peer_name ()
if (!(evt = create_event ())) if (!(evt = create_event ()))
return ENOBUFS; return ENOBUFS;
len = sizeof *packet + sizeof *un; len = sizeof *packet + sizeof *un + CMSG_SPACE (sizeof (struct ucred));
packet = (af_unix_pkt_hdr_t *) alloca (len); packet = (af_unix_pkt_hdr_t *) alloca (len);
set_pipe_non_blocking (false); set_pipe_non_blocking (false);
status = NtReadFile (get_handle (), evt, NULL, NULL, &io, packet, len, status = NtReadFile (get_handle (), evt, NULL, NULL, &io, packet, len,
@ -695,11 +783,23 @@ fhandler_socket_unix::recv_peer_name ()
break; break;
} }
} }
set_pipe_non_blocking (is_nonblocking ());
NtClose (evt);
if (!NT_SUCCESS (status) && ret == 0) if (!NT_SUCCESS (status) && ret == 0)
ret = geterrno_from_nt_status (status); ret = geterrno_from_nt_status (status);
if (ret == 0 && packet->name_len > 0) if (ret == 0)
set_peer_sun_path (AF_UNIX_PKT_NAME (packet), packet->name_len); {
set_pipe_non_blocking (is_nonblocking ()); if (packet->name_len > 0)
peer_sun_path (AF_UNIX_PKT_NAME (packet), packet->name_len);
if (packet->cmsg_len > 0)
{
struct cmsghdr *cmsg = (struct cmsghdr *) alloca (packet->cmsg_len);
memcpy (cmsg, AF_UNIX_PKT_CMSG (packet), packet->cmsg_len);
if (cmsg->cmsg_level == SOL_SOCKET
&& cmsg->cmsg_type == SCM_CREDENTIALS)
peer_cred ((struct ucred *) CMSG_DATA(cmsg));
}
}
return ret; return ret;
} }
@ -818,7 +918,7 @@ fhandler_socket_unix::create_pipe_instance ()
} }
NTSTATUS NTSTATUS
fhandler_socket_unix::open_pipe (PUNICODE_STRING pipe_name, bool send_name) fhandler_socket_unix::open_pipe (PUNICODE_STRING pipe_name, bool xchg_sock_info)
{ {
NTSTATUS status; NTSTATUS status;
HANDLE npfsh; HANDLE npfsh;
@ -838,8 +938,8 @@ fhandler_socket_unix::open_pipe (PUNICODE_STRING pipe_name, bool send_name)
if (NT_SUCCESS (status)) if (NT_SUCCESS (status))
{ {
set_io_handle (ph); set_io_handle (ph);
if (send_name) if (xchg_sock_info)
send_my_name (); send_sock_info (false);
} }
return status; return status;
} }
@ -902,13 +1002,13 @@ fhandler_socket_unix::wait_pipe (PUNICODE_STRING pipe_name)
else else
{ {
SetEvent (cwt_termination_evt); SetEvent (cwt_termination_evt);
WaitForSingleObject (connect_wait_thr, INFINITE); NtWaitForSingleObject (connect_wait_thr, FALSE, NULL);
GetExitCodeThread (connect_wait_thr, &err); GetExitCodeThread (connect_wait_thr, &err);
waitret = WAIT_SIGNALED; waitret = WAIT_SIGNALED;
} }
thr = InterlockedExchangePointer (&connect_wait_thr, NULL); thr = InterlockedExchangePointer (&connect_wait_thr, NULL);
if (thr) if (thr)
CloseHandle (thr); NtClose (thr);
param = InterlockedExchangePointer (&cwt_param, NULL); param = InterlockedExchangePointer (&cwt_param, NULL);
if (param) if (param)
cfree (param); cfree (param);
@ -988,6 +1088,27 @@ fhandler_socket_unix::listen_pipe ()
return (status == STATUS_PIPE_CONNECTED) ? 0 : -1; return (status == STATUS_PIPE_CONNECTED) ? 0 : -1;
} }
ULONG
fhandler_socket_unix::peek_pipe (PFILE_PIPE_PEEK_BUFFER pbuf, ULONG psize,
HANDLE evt)
{
NTSTATUS status;
IO_STATUS_BLOCK io;
status = NtFsControlFile (get_handle (), evt, NULL, NULL, &io,
FSCTL_PIPE_PEEK, NULL, 0, pbuf, psize);
if (status == STATUS_PENDING)
{
/* Very short-lived */
status = NtWaitForSingleObject (evt ?: get_handle (), FALSE, NULL);
if (NT_SUCCESS (status))
status = io.Status;
}
return NT_SUCCESS (status) ? (io.Information
- offsetof (FILE_PIPE_PEEK_BUFFER, Data))
: 0;
}
int int
fhandler_socket_unix::disconnect_pipe (HANDLE ph) fhandler_socket_unix::disconnect_pipe (HANDLE ph)
{ {
@ -998,7 +1119,7 @@ fhandler_socket_unix::disconnect_pipe (HANDLE ph)
NULL, 0, NULL, 0); NULL, 0, NULL, 0);
/* Short-lived. Don't use cygwait. We don't want to be interrupted. */ /* Short-lived. Don't use cygwait. We don't want to be interrupted. */
if (status == STATUS_PENDING if (status == STATUS_PENDING
&& WaitForSingleObject (ph, INFINITE) == WAIT_OBJECT_0) && NtWaitForSingleObject (ph, FALSE, NULL) == WAIT_OBJECT_0)
status = io.Status; status = io.Status;
if (!NT_SUCCESS (status)) if (!NT_SUCCESS (status))
{ {
@ -1009,32 +1130,22 @@ fhandler_socket_unix::disconnect_pipe (HANDLE ph)
} }
void void
fhandler_socket_unix::set_sun_path (struct sockaddr_un *un, socklen_t unlen) fhandler_socket_unix::init_cred ()
{ {
if (peer_sun_path) struct ucred *scred = shmem->sock_cred ();
delete peer_sun_path; struct ucred *pcred = shmem->peer_cred ();
if (!un) scred->pid = pcred->pid = (pid_t) 0;
sun_path = NULL; scred->uid = pcred->uid = (uid_t) -1;
sun_path = new sun_name_t ((const struct sockaddr *) un, unlen); scred->gid = pcred->gid = (gid_t) -1;
}
void
fhandler_socket_unix::set_peer_sun_path (struct sockaddr_un *un,
socklen_t unlen)
{
if (peer_sun_path)
delete peer_sun_path;
if (!un)
peer_sun_path = NULL;
peer_sun_path = new sun_name_t ((const struct sockaddr *) un, unlen);
} }
void void
fhandler_socket_unix::set_cred () fhandler_socket_unix::set_cred ()
{ {
peer_cred.pid = (pid_t) 0; struct ucred *scred = shmem->sock_cred ();
peer_cred.uid = (uid_t) -1; scred->pid = myself->pid;
peer_cred.gid = (gid_t) -1; scred->uid = myself->uid;
scred->gid = myself->gid;
} }
/* ========================== public methods ========================= */ /* ========================== public methods ========================= */
@ -1074,15 +1185,10 @@ fhandler_socket_unix::set_close_on_exec (bool val)
fhandler_socket_unix::fhandler_socket_unix () fhandler_socket_unix::fhandler_socket_unix ()
{ {
set_cred ();
} }
fhandler_socket_unix::~fhandler_socket_unix () fhandler_socket_unix::~fhandler_socket_unix ()
{ {
if (sun_path)
delete sun_path;
if (peer_sun_path)
delete peer_sun_path;
} }
int int
@ -1094,6 +1200,12 @@ fhandler_socket_unix::dup (fhandler_base *child, int flags)
return -1; return -1;
} }
fhandler_socket_unix *fhs = (fhandler_socket_unix *) child; fhandler_socket_unix *fhs = (fhandler_socket_unix *) child;
if (reopen_shmem () < 0)
{
__seterrno ();
fhs->close ();
return -1;
}
if (backing_file_handle && backing_file_handle != INVALID_HANDLE_VALUE if (backing_file_handle && backing_file_handle != INVALID_HANDLE_VALUE
&& !DuplicateHandle (GetCurrentProcess (), backing_file_handle, && !DuplicateHandle (GetCurrentProcess (), backing_file_handle,
GetCurrentProcess (), &fhs->backing_file_handle, GetCurrentProcess (), &fhs->backing_file_handle,
@ -1111,14 +1223,8 @@ fhandler_socket_unix::dup (fhandler_base *child, int flags)
fhs->close (); fhs->close ();
return -1; return -1;
} }
if (reopen_shmem () < 0) fhs->sun_path (sun_path ());
{ fhs->peer_sun_path (peer_sun_path ());
__seterrno ();
fhs->close ();
return -1;
}
fhs->set_sun_path (get_sun_path ());
fhs->set_peer_sun_path (get_peer_sun_path ());
fhs->connect_wait_thr = NULL; fhs->connect_wait_thr = NULL;
fhs->cwt_termination_evt = NULL; fhs->cwt_termination_evt = NULL;
fhs->cwt_param = NULL; fhs->cwt_param = NULL;
@ -1250,12 +1356,13 @@ fhandler_socket_unix::socket (int af, int type, int protocol, int flags)
return -1; return -1;
rmem (262144); rmem (262144);
wmem (262144); wmem (262144);
set_addr_family (af); set_addr_family (AF_UNIX);
set_socket_type (type); set_socket_type (type);
if (flags & SOCK_NONBLOCK) if (flags & SOCK_NONBLOCK)
set_nonblocking (true); set_nonblocking (true);
if (flags & SOCK_CLOEXEC) if (flags & SOCK_CLOEXEC)
set_close_on_exec (true); set_close_on_exec (true);
init_cred ();
set_io_handle (NULL); set_io_handle (NULL);
set_unique_id (); set_unique_id ();
set_ino (get_unique_id ()); set_ino (get_unique_id ());
@ -1281,46 +1388,36 @@ fhandler_socket_unix::socketpair (int af, int type, int protocol, int flags,
return -1; return -1;
} }
if (create_shmem () < 0)
return -1;
if (fh->create_shmem () < 0)
goto fh_shmem_failed;
/* socket() on both sockets */ /* socket() on both sockets */
rmem (262144); rmem (262144);
fh->rmem (262144); fh->rmem (262144);
wmem (262144); wmem (262144);
fh->wmem (262144); fh->wmem (262144);
set_addr_family (af); set_addr_family (AF_UNIX);
fh->set_addr_family (af); fh->set_addr_family (AF_UNIX);
set_socket_type (type); set_socket_type (type);
fh->set_socket_type (type); fh->set_socket_type (type);
set_cred ();
fh->set_cred ();
set_unique_id (); set_unique_id ();
set_ino (get_unique_id ()); set_ino (get_unique_id ());
/* bind/listen 1st socket */ /* bind/listen 1st socket */
gen_pipe_name (); gen_pipe_name ();
pipe = create_pipe (true); pipe = create_pipe (true);
if (!pipe) if (!pipe)
return -1; goto create_pipe_failed;
set_io_handle (pipe); set_io_handle (pipe);
set_sun_path (&sun); sun_path (&sun);
fh->set_peer_sun_path (&sun); fh->peer_sun_path (&sun);
if (create_shmem () < 0)
{
NtClose (pipe);
return -1;
}
binding_state (bound);
connect_state (listener); connect_state (listener);
/* connect 2nd socket */ /* connect 2nd socket, even for DGRAM. There's no difference as far
if (type != SOCK_DGRAM as socketpairs are concerned. */
&& fh->open_pipe (pc.get_nt_native_path (), false) < 0) if (fh->open_pipe (pc.get_nt_native_path (), false) < 0)
{ goto fh_open_pipe_failed;
NtClose (shmem_handle);
NtClose (pipe);
return -1;
}
if (fh->create_shmem () < 0)
{
NtClose (fh->get_handle ());
NtClose (shmem_handle);
NtClose (pipe);
}
fh->connect_state (connected); fh->connect_state (connected);
if (flags & SOCK_NONBLOCK) if (flags & SOCK_NONBLOCK)
{ {
@ -1333,6 +1430,16 @@ fhandler_socket_unix::socketpair (int af, int type, int protocol, int flags,
fh->set_close_on_exec (true); fh->set_close_on_exec (true);
} }
return 0; return 0;
fh_open_pipe_failed:
NtClose (pipe);
create_pipe_failed:
NtUnmapViewOfSection (GetCurrentProcess (), fh->shmem);
NtClose (fh->shmem_handle);
fh_shmem_failed:
NtUnmapViewOfSection (GetCurrentProcess (), shmem);
NtClose (shmem_handle);
return -1;
} }
/* Bind creates the backing file, generates the pipe name and sets /* Bind creates the backing file, generates the pipe name and sets
@ -1385,10 +1492,14 @@ fhandler_socket_unix::bind (const struct sockaddr *name, int namelen)
binding_state (unbound); binding_state (unbound);
return -1; return -1;
} }
set_sun_path (&sun); state_lock ();
/* If we're already connected, send name to peer. */ sun_path (&sun);
/* If we're already connected, send socket info to peer. In this case
send_sock_info calls state_unlock */
if (connect_state () == connected) if (connect_state () == connected)
send_my_name (); send_sock_info (true);
else
state_unlock ();
binding_state (bound); binding_state (bound);
return 0; return 0;
} }
@ -1426,6 +1537,9 @@ fhandler_socket_unix::listen (int backlog)
return -1; return -1;
} }
set_io_handle (pipe); set_io_handle (pipe);
state_lock ();
set_cred ();
state_unlock ();
connect_state (listener); connect_state (listener);
conn_unlock (); conn_unlock ();
return 0; return 0;
@ -1473,7 +1587,7 @@ fhandler_socket_unix::accept4 (struct sockaddr *peer, int *len, int flags)
if (sock->create_shmem () < 0) if (sock->create_shmem () < 0)
goto create_shmem_failed; goto create_shmem_failed;
sock->set_addr_family (get_addr_family ()); sock->set_addr_family (AF_UNIX);
sock->set_socket_type (get_socket_type ()); sock->set_socket_type (get_socket_type ());
if (flags & SOCK_NONBLOCK) if (flags & SOCK_NONBLOCK)
sock->set_nonblocking (true); sock->set_nonblocking (true);
@ -1486,15 +1600,20 @@ fhandler_socket_unix::accept4 (struct sockaddr *peer, int *len, int flags)
sock->binding_state (binding_state ()); sock->binding_state (binding_state ());
sock->set_io_handle (accepted); sock->set_io_handle (accepted);
sock->set_sun_path (get_sun_path ()); sock->sun_path (sun_path ());
error = sock->recv_peer_name (); sock->sock_cred (sock_cred ());
/* Send this socket info to connecting socket. */
sock->send_sock_info (false);
/* Fetch the packet sent by send_sock_info called by
connecting peer. */
error = sock->recv_peer_info ();
if (error == 0) if (error == 0)
{ {
__try __try
{ {
if (peer) if (peer)
{ {
sun_name_t *sun = sock->get_peer_sun_path (); sun_name_t *sun = sock->peer_sun_path ();
if (sun) if (sun)
{ {
memcpy (peer, &sun->un, memcpy (peer, &sun->un,
@ -1591,14 +1710,14 @@ fhandler_socket_unix::connect (const struct sockaddr *name, int namelen)
connect_state (unconnected); connect_state (unconnected);
return -1; return -1;
} }
set_peer_sun_path (&sun); peer_sun_path (&sun);
if (get_socket_type () != SOCK_DGRAM) if (get_socket_type () != SOCK_DGRAM)
{ {
if (connect_pipe (&pipe_name) < 0) if (connect_pipe (&pipe_name) < 0)
{ {
if (get_errno () != EINPROGRESS) if (get_errno () != EINPROGRESS)
{ {
set_peer_sun_path (NULL); peer_sun_path (NULL);
connect_state (connect_failed); connect_state (connect_failed);
} }
return -1; return -1;
@ -1611,36 +1730,60 @@ fhandler_socket_unix::connect (const struct sockaddr *name, int namelen)
int int
fhandler_socket_unix::getsockname (struct sockaddr *name, int *namelen) fhandler_socket_unix::getsockname (struct sockaddr *name, int *namelen)
{ {
sun_name_t sun; sun_name_t *sun = sun_path ();
if (get_sun_path ()) memcpy (name, sun, MIN (*namelen, sun->un_len));
memcpy (&sun, &get_sun_path ()->un, get_sun_path ()->un_len); *namelen = sun->un_len;
else
sun.un_len = 0;
memcpy (name, &sun, MIN (*namelen, sun.un_len));
*namelen = sun.un_len;
return 0; return 0;
} }
int int
fhandler_socket_unix::getpeername (struct sockaddr *name, int *namelen) fhandler_socket_unix::getpeername (struct sockaddr *name, int *namelen)
{ {
sun_name_t sun; sun_name_t *sun = peer_sun_path ();
memcpy (name, sun, MIN (*namelen, sun->un_len));
if (get_peer_sun_path ()) *namelen = sun->un_len;
memcpy (&sun, &get_peer_sun_path ()->un, get_peer_sun_path ()->un_len);
else
sun.un_len = 0;
memcpy (name, &sun, MIN (*namelen, sun.un_len));
*namelen = sun.un_len;
return 0; return 0;
} }
int int
fhandler_socket_unix::shutdown (int how) fhandler_socket_unix::shutdown (int how)
{ {
set_errno (EAFNOSUPPORT); NTSTATUS status = STATUS_SUCCESS;
return -1; IO_STATUS_BLOCK io;
if (how < SHUT_RD || how > SHUT_RDWR)
{
set_errno (EINVAL);
return -1;
}
/* Convert SHUT_RD/SHUT_WR/SHUT_RDWR to _SHUT_RECV/_SHUT_SEND bits. */
++how;
state_lock ();
int old_shutdown_mask = saw_shutdown ();
int new_shutdown_mask = old_shutdown_mask | how;
if (new_shutdown_mask != old_shutdown_mask)
saw_shutdown (new_shutdown_mask);
state_unlock ();
if (new_shutdown_mask != old_shutdown_mask)
{
/* Send shutdown info to peer. Note that it's not necessarily fatal
if the info isn't sent here. The info will be reproduced by any
followup package sent to the peer. */
af_unix_pkt_hdr_t packet (true, (shut_state) new_shutdown_mask, 0, 0, 0);
io_lock ();
set_pipe_non_blocking (true);
status = NtWriteFile (get_handle (), NULL, NULL, NULL, &io, &packet,
packet.pckt_len, NULL, NULL);
set_pipe_non_blocking (is_nonblocking ());
io_unlock ();
}
if (!NT_SUCCESS (status))
{
debug_printf ("Couldn't send shutdown info: NtWriteFile: %y", status);
return -1;
}
return 0;
} }
int int
@ -1652,24 +1795,25 @@ fhandler_socket_unix::close ()
{ {
if (evt) if (evt)
SetEvent (evt); SetEvent (evt);
WaitForSingleObject (thr, INFINITE); NtWaitForSingleObject (thr, FALSE, NULL);
CloseHandle (thr); NtClose (thr);
} }
if (evt) if (evt)
NtClose (evt); NtClose (evt);
PVOID param = InterlockedExchangePointer (&cwt_param, NULL); PVOID param = InterlockedExchangePointer (&cwt_param, NULL);
if (param) if (param)
cfree (param); cfree (param);
HANDLE hdl = InterlockedExchangePointer (&get_handle (), NULL);
if (hdl)
NtClose (hdl);
if (backing_file_handle && backing_file_handle != INVALID_HANDLE_VALUE)
NtClose (backing_file_handle);
HANDLE shm = InterlockedExchangePointer (&shmem_handle, NULL); HANDLE shm = InterlockedExchangePointer (&shmem_handle, NULL);
if (shm) if (shm)
NtClose (shm); NtClose (shm);
param = InterlockedExchangePointer ((PVOID *) &shmem, NULL); param = InterlockedExchangePointer ((PVOID *) &shmem, NULL);
if (param) if (param)
NtUnmapViewOfSection (GetCurrentProcess (), param); NtUnmapViewOfSection (GetCurrentProcess (), param);
if (get_handle ())
NtClose (get_handle ());
if (backing_file_handle && backing_file_handle != INVALID_HANDLE_VALUE)
NtClose (backing_file_handle);
return 0; return 0;
} }
@ -1683,7 +1827,6 @@ fhandler_socket_unix::getpeereid (pid_t *pid, uid_t *euid, gid_t *egid)
set_errno (EINVAL); set_errno (EINVAL);
return -1; return -1;
} }
conn_lock ();
if (connect_state () != connected) if (connect_state () != connected)
set_errno (ENOTCONN); set_errno (ENOTCONN);
else else
@ -1691,19 +1834,19 @@ fhandler_socket_unix::getpeereid (pid_t *pid, uid_t *euid, gid_t *egid)
__try __try
{ {
state_lock (); state_lock ();
struct ucred *pcred = peer_cred ();
if (pid) if (pid)
*pid = peer_cred.pid; *pid = pcred->pid;
if (euid) if (euid)
*euid = peer_cred.uid; *euid = pcred->uid;
if (egid) if (egid)
*egid = peer_cred.gid; *egid = pcred->gid;
state_unlock (); state_unlock ();
ret = 0; ret = 0;
} }
__except (EFAULT) {} __except (EFAULT) {}
__endtry __endtry
} }
conn_unlock ();
return ret; return ret;
} }
@ -1844,17 +1987,60 @@ fhandler_socket_unix::setsockopt (int level, int optname, const void *optval,
switch (optname) switch (optname)
{ {
case SO_PASSCRED: case SO_PASSCRED:
if (optlen < (socklen_t) sizeof (int))
{
set_errno (EINVAL);
return -1;
}
bool val;
val = !!*(int *) optval;
/* Using bind_lock here to make sure the autobind below is
covered. This is the only place to set so_passcred anyway. */
bind_lock ();
if (val && binding_state () == unbound)
{
sun_name_t sun;
binding_state (bind_pending);
backing_file_handle = autobind (&sun);
if (!backing_file_handle)
{
binding_state (unbound);
bind_unlock ();
return -1;
}
sun_path (&sun);
binding_state (bound);
}
so_passcred (val);
bind_unlock ();
break; break;
case SO_REUSEADDR: case SO_REUSEADDR:
reuseaddr (*(int *) optval); if (optlen < (socklen_t) sizeof (int))
{
set_errno (EINVAL);
return -1;
}
reuseaddr (!!*(int *) optval);
break; break;
case SO_RCVBUF: case SO_RCVBUF:
if (optlen < (socklen_t) sizeof (int))
{
set_errno (EINVAL);
return -1;
}
rmem (*(int *) optval); rmem (*(int *) optval);
break; break;
case SO_SNDBUF: case SO_SNDBUF:
if (optlen < (socklen_t) sizeof (int))
{
set_errno (EINVAL);
return -1;
}
wmem (*(int *) optval); wmem (*(int *) optval);
break; break;
@ -1900,6 +2086,12 @@ fhandler_socket_unix::getsockopt (int level, int optname, const void *optval,
{ {
case SO_ERROR: case SO_ERROR:
{ {
if (*optlen < (socklen_t) sizeof (int))
{
set_errno (EINVAL);
return -1;
}
int *e = (int *) optval; int *e = (int *) optval;
LONG err; LONG err;
@ -1909,7 +2101,17 @@ fhandler_socket_unix::getsockopt (int level, int optname, const void *optval,
} }
case SO_PASSCRED: case SO_PASSCRED:
break; {
if (*optlen < (socklen_t) sizeof (int))
{
set_errno (EINVAL);
return -1;
}
int *e = (int *) optval;
*e = so_passcred ();
break;
}
case SO_PEERCRED: case SO_PEERCRED:
{ {
@ -1977,6 +2179,11 @@ fhandler_socket_unix::getsockopt (int level, int optname, const void *optval,
case SO_TYPE: case SO_TYPE:
{ {
if (*optlen < (socklen_t) sizeof (int))
{
set_errno (EINVAL);
return -1;
}
unsigned int *type = (unsigned int *) optval; unsigned int *type = (unsigned int *) optval;
*type = get_socket_type (); *type = get_socket_type ();
*optlen = (socklen_t) sizeof *type; *optlen = (socklen_t) sizeof *type;
@ -1987,6 +2194,11 @@ fhandler_socket_unix::getsockopt (int level, int optname, const void *optval,
case SO_LINGER: case SO_LINGER:
{ {
if (*optlen < (socklen_t) sizeof (struct linger))
{
set_errno (EINVAL);
return -1;
}
struct linger *linger = (struct linger *) optval; struct linger *linger = (struct linger *) optval;
memset (linger, 0, sizeof *linger); memset (linger, 0, sizeof *linger);
*optlen = (socklen_t) sizeof *linger; *optlen = (socklen_t) sizeof *linger;
@ -1995,6 +2207,11 @@ fhandler_socket_unix::getsockopt (int level, int optname, const void *optval,
default: default:
{ {
if (*optlen < (socklen_t) sizeof (int))
{
set_errno (EINVAL);
return -1;
}
unsigned int *val = (unsigned int *) optval; unsigned int *val = (unsigned int *) optval;
*val = 0; *val = 0;
*optlen = (socklen_t) sizeof *val; *optlen = (socklen_t) sizeof *val;
@ -2083,9 +2300,9 @@ fhandler_socket_unix::fstat (struct stat *buf)
{ {
int ret = 0; int ret = 0;
if (!get_sun_path () if (sun_path ()
|| get_sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t) && (sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
|| get_sun_path ()->un.sun_path[0] == '\0') || sun_path ()->un.sun_path[0] == '\0'))
return fhandler_socket::fstat (buf); return fhandler_socket::fstat (buf);
ret = fhandler_base::fstat_fs (buf); ret = fhandler_base::fstat_fs (buf);
if (!ret) if (!ret)
@ -2099,9 +2316,9 @@ fhandler_socket_unix::fstat (struct stat *buf)
int __reg2 int __reg2
fhandler_socket_unix::fstatvfs (struct statvfs *sfs) fhandler_socket_unix::fstatvfs (struct statvfs *sfs)
{ {
if (!get_sun_path () if (sun_path ()
|| get_sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t) && (sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
|| get_sun_path ()->un.sun_path[0] == '\0') || sun_path ()->un.sun_path[0] == '\0'))
return fhandler_socket::fstatvfs (sfs); return fhandler_socket::fstatvfs (sfs);
fhandler_disk_file fh (pc); fhandler_disk_file fh (pc);
fh.get_device () = FH_FS; fh.get_device () = FH_FS;
@ -2111,9 +2328,9 @@ fhandler_socket_unix::fstatvfs (struct statvfs *sfs)
int int
fhandler_socket_unix::fchmod (mode_t newmode) fhandler_socket_unix::fchmod (mode_t newmode)
{ {
if (!get_sun_path () if (sun_path ()
|| get_sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t) && (sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
|| get_sun_path ()->un.sun_path[0] == '\0') || sun_path ()->un.sun_path[0] == '\0'))
return fhandler_socket::fchmod (newmode); return fhandler_socket::fchmod (newmode);
fhandler_disk_file fh (pc); fhandler_disk_file fh (pc);
fh.get_device () = FH_FS; fh.get_device () = FH_FS;
@ -2130,9 +2347,9 @@ fhandler_socket_unix::fchmod (mode_t newmode)
int int
fhandler_socket_unix::fchown (uid_t uid, gid_t gid) fhandler_socket_unix::fchown (uid_t uid, gid_t gid)
{ {
if (!get_sun_path () if (sun_path ()
|| get_sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t) && (sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
|| get_sun_path ()->un.sun_path[0] == '\0') || sun_path ()->un.sun_path[0] == '\0'))
return fhandler_socket::fchown (uid, gid); return fhandler_socket::fchown (uid, gid);
fhandler_disk_file fh (pc); fhandler_disk_file fh (pc);
return fh.fchown (uid, gid); return fh.fchown (uid, gid);
@ -2141,9 +2358,9 @@ fhandler_socket_unix::fchown (uid_t uid, gid_t gid)
int int
fhandler_socket_unix::facl (int cmd, int nentries, aclent_t *aclbufp) fhandler_socket_unix::facl (int cmd, int nentries, aclent_t *aclbufp)
{ {
if (!get_sun_path () if (sun_path ()
|| get_sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t) && (sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
|| get_sun_path ()->un.sun_path[0] == '\0') || sun_path ()->un.sun_path[0] == '\0'))
return fhandler_socket::facl (cmd, nentries, aclbufp); return fhandler_socket::facl (cmd, nentries, aclbufp);
fhandler_disk_file fh (pc); fhandler_disk_file fh (pc);
return fh.facl (cmd, nentries, aclbufp); return fh.facl (cmd, nentries, aclbufp);
@ -2152,9 +2369,9 @@ fhandler_socket_unix::facl (int cmd, int nentries, aclent_t *aclbufp)
int int
fhandler_socket_unix::link (const char *newpath) fhandler_socket_unix::link (const char *newpath)
{ {
if (!get_sun_path () if (sun_path ()
|| get_sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t) && (sun_path ()->un_len <= (socklen_t) sizeof (sa_family_t)
|| get_sun_path ()->un.sun_path[0] == '\0') || sun_path ()->un.sun_path[0] == '\0'))
return fhandler_socket::link (newpath); return fhandler_socket::link (newpath);
fhandler_disk_file fh (pc); fhandler_disk_file fh (pc);
return fh.link (newpath); return fh.link (newpath);