blob: 05cbd4204ae7563594ff58a2885333b706aa5bbf [file] [log] [blame]
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/socket.h>
#include <linux/un.h>
#include <linux/fcntl.h>
#include <linux/termios.h>
#include <linux/mm.h>
#include <linux/config.h>
#include <asm/system.h>
#include <asm/segment.h>
#include "kern_sock.h"
static struct unix_proto_data {
int refcnt; /* cnt of reference 0=free */
struct socket *socket; /* socket we're bound to */
int protocol;
struct sockaddr_un sockaddr_un;
short sockaddr_len; /* >0 if name bound */
char *buf;
int bp_head, bp_tail;
struct inode *inode;
struct unix_proto_data *peerupd;
} unix_datas[NSOCKETS];
#define last_unix_data (unix_datas + NSOCKETS - 1)
#define UN_DATA(SOCK) ((struct unix_proto_data *)(SOCK)->data)
#define UN_PATH_OFFSET ((unsigned long)((struct sockaddr_un *)0)->sun_path)
/*
* buffer size must be power of 2. buffer mgmt inspired by pipe code.
* note that buffer contents can wraparound, and we can write one byte less
* than full size to discern full vs empty.
*/
#define BUF_SIZE PAGE_SIZE
#define UN_BUF_AVAIL(UPD) (((UPD)->bp_head - (UPD)->bp_tail) & (BUF_SIZE-1))
#define UN_BUF_SPACE(UPD) ((BUF_SIZE-1) - UN_BUF_AVAIL(UPD))
static int unix_proto_init(void);
static int unix_proto_create(struct socket *sock, int protocol);
static int unix_proto_dup(struct socket *newsock, struct socket *oldsock);
static int unix_proto_release(struct socket *sock, struct socket *peer);
static int unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
int sockaddr_len);
static int unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
int sockaddr_len, int flags);
static int unix_proto_socketpair(struct socket *sock1, struct socket *sock2);
static int unix_proto_accept(struct socket *sock, struct socket *newsock,
int flags);
static int unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr,
int *usockaddr_len, int peer);
static int unix_proto_read(struct socket *sock, char *ubuf, int size,
int nonblock);
static int unix_proto_write(struct socket *sock, char *ubuf, int size,
int nonblock);
static int unix_proto_select(struct socket *sock, int sel_type, select_table * wait);
static int unix_proto_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg);
static int unix_proto_listen(struct socket *sock, int backlog);
static int unix_proto_send (struct socket *sock, void *buff, int len,
int nonblock, unsigned flags);
static int unix_proto_recv (struct socket *sock, void *buff, int len,
int nonblock, unsigned flags);
static int unix_proto_sendto (struct socket *sock, void *buff, int len,
int nonblock, unsigned flags,
struct sockaddr *addr, int addr_len);
static int unix_proto_recvfrom (struct socket *sock, void *buff, int len,
int nonblock, unsigned flags,
struct sockaddr *addr, int *addr_len);
static int unix_proto_shutdown (struct socket *sock, int how);
static int unix_proto_setsockopt (struct socket *sock, int level, int optname,
char *optval, int optlen);
static int unix_proto_getsockopt (struct socket *sock, int level, int optname,
char *optval, int *optlen);
struct proto_ops unix_proto_ops = {
unix_proto_init,
unix_proto_create,
unix_proto_dup,
unix_proto_release,
unix_proto_bind,
unix_proto_connect,
unix_proto_socketpair,
unix_proto_accept,
unix_proto_getname,
unix_proto_read,
unix_proto_write,
unix_proto_select,
unix_proto_ioctl,
unix_proto_listen,
unix_proto_send,
unix_proto_recv,
unix_proto_sendto,
unix_proto_recvfrom,
unix_proto_shutdown,
unix_proto_setsockopt,
unix_proto_getsockopt,
NULL, /* unix_proto_fcntl. */
};
static inline int
min (int a, int b)
{
if (a < b) return (a);
return (b);
}
#ifdef SOCK_DEBUG
void
sockaddr_un_printk(struct sockaddr_un *sockun, int sockaddr_len)
{
char buf[sizeof(sockun->sun_path) + 1];
sockaddr_len -= UN_PATH_OFFSET;
if (sockun->sun_family != AF_UNIX)
printk("sockaddr_un: <BAD FAMILY: %d>\n", sockun->sun_family);
else if (sockaddr_len <= 0 || sockaddr_len >= sizeof(buf))
printk("sockaddr_un: <BAD LENGTH: %d>\n", sockaddr_len);
else {
memcpy(buf, sockun->sun_path, sockaddr_len);
buf[sockaddr_len] = '\0';
printk("sockaddr_un: '%s'[%d]\n", buf,
sockaddr_len + UN_PATH_OFFSET);
}
}
#endif /* SOCK_DEBUG */
/* don't have to do anything. */
static int
unix_proto_listen (struct socket *sock, int backlog)
{
return (0);
}
static int
unix_proto_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
return (-EOPNOTSUPP);
}
static int
unix_proto_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
return (-EOPNOTSUPP);
}
static int
unix_proto_sendto(struct socket *sock, void *buff, int len, int nonblock,
unsigned flags, struct sockaddr *addr, int addr_len)
{
return (-EOPNOTSUPP);
}
static int
unix_proto_recvfrom(struct socket *sock, void *buff, int len, int nonblock,
unsigned flags, struct sockaddr *addr, int *addr_len)
{
return (-EOPNOTSUPP);
}
static int
unix_proto_shutdown (struct socket *sock, int how)
{
return (-EOPNOTSUPP);
}
static int
unix_proto_send(struct socket *sock, void *buff, int len, int nonblock,
unsigned flags)
{
/* this error needs to be checked. */
if (flags != 0)
return (-EINVAL);
return (unix_proto_write (sock, buff, len, nonblock));
}
static int
unix_proto_recv(struct socket *sock, void *buff, int len, int nonblock,
unsigned flags)
{
/* this error needs to be checked. */
if (flags != 0)
return (-EINVAL);
return (unix_proto_read (sock, buff, len, nonblock));
}
static struct unix_proto_data *
unix_data_lookup(struct sockaddr_un *sockun, int sockaddr_len,
struct inode *inode)
{
struct unix_proto_data *upd;
for (upd = unix_datas; upd <= last_unix_data; ++upd) {
if (upd->refcnt && upd->socket &&
upd->socket->state == SS_UNCONNECTED &&
upd->sockaddr_un.sun_family == sockun->sun_family &&
upd->inode == inode)
return upd;
}
return NULL;
}
static struct unix_proto_data *
unix_data_alloc(void)
{
struct unix_proto_data *upd;
cli();
for (upd = unix_datas; upd <= last_unix_data; ++upd) {
if (!upd->refcnt) {
upd->refcnt = 1;
sti();
upd->socket = NULL;
upd->sockaddr_len = 0;
upd->buf = NULL;
upd->bp_head = upd->bp_tail = 0;
upd->inode = NULL;
upd->peerupd = NULL;
return upd;
}
}
sti();
return NULL;
}
static inline void
unix_data_ref(struct unix_proto_data *upd)
{
++upd->refcnt;
PRINTK(("unix_data_ref: refing data 0x%x (%d)\n", upd, upd->refcnt));
}
static void
unix_data_deref(struct unix_proto_data *upd)
{
if (upd->refcnt == 1) {
PRINTK(("unix_data_deref: releasing data 0x%x\n", upd));
if (upd->buf) {
free_page((unsigned long)upd->buf);
upd->buf = NULL;
upd->bp_head = upd->bp_tail = 0;
}
}
--upd->refcnt;
}
/*
* upon a create, we allocate an empty protocol data, and grab a page to
* buffer writes
*/
static int
unix_proto_create(struct socket *sock, int protocol)
{
struct unix_proto_data *upd;
PRINTK(("unix_proto_create: socket 0x%x, proto %d\n", sock, protocol));
if (protocol != 0) {
PRINTK(("unix_proto_create: protocol != 0\n"));
return -EINVAL;
}
if (!(upd = unix_data_alloc())) {
printk("unix_proto_create: can't allocate buffer\n");
return -ENOMEM;
}
if (!(upd->buf = (char *)get_free_page(GFP_USER))) {
printk("unix_proto_create: can't get page!\n");
unix_data_deref(upd);
return -ENOMEM;
}
upd->protocol = protocol;
upd->socket = sock;
UN_DATA(sock) = upd;
PRINTK(("unix_proto_create: allocated data 0x%x\n", upd));
return 0;
}
static int
unix_proto_dup(struct socket *newsock, struct socket *oldsock)
{
struct unix_proto_data *upd = UN_DATA(oldsock);
return unix_proto_create(newsock, upd->protocol);
}
static int
unix_proto_release(struct socket *sock, struct socket *peer)
{
struct unix_proto_data *upd = UN_DATA(sock);
PRINTK(("unix_proto_release: socket 0x%x, unix_data 0x%x\n",
sock, upd));
if (!upd)
return 0;
if (upd->socket != sock) {
printk("unix_proto_release: socket link mismatch!\n");
return -EINVAL;
}
if (upd->inode) {
PRINTK(("unix_proto_release: releasing inode 0x%x\n",
upd->inode));
iput(upd->inode);
upd->inode = NULL;
}
UN_DATA(sock) = NULL;
upd->socket = NULL;
if (upd->peerupd)
unix_data_deref(upd->peerupd);
unix_data_deref(upd);
return 0;
}
/*
* bind a name to a socket. this is where much of the work is done. we
* allocate a fresh page for the buffer, grab the appropriate inode and
* set things up.
*
* XXX what should we do if an address is already bound? here we return
* EINVAL, but it may be necessary to re-bind. i think thats what bsd does
* in the case of datagram sockets
*/
static int
unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
int sockaddr_len)
{
struct unix_proto_data *upd = UN_DATA(sock);
char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1];
int i;
PRINTK(("unix_proto_bind: socket 0x%x, len=%d\n", sock,
sockaddr_len));
if (sockaddr_len <= UN_PATH_OFFSET ||
sockaddr_len > sizeof(struct sockaddr_un)) {
PRINTK(("unix_proto_bind: bad length %d\n", sockaddr_len));
return -EINVAL;
}
if (upd->sockaddr_len || upd->inode) {
printk("unix_proto_bind: already bound!\n");
return -EINVAL;
}
verify_area(VERIFY_WRITE,umyaddr, sockaddr_len);
memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len);
if (upd->sockaddr_un.sun_family != AF_UNIX) {
PRINTK(("unix_proto_bind: family is %d, not AF_UNIX (%d)\n",
upd->sockaddr_un.sun_family, AF_UNIX));
return -EINVAL;
}
memcpy(fname, upd->sockaddr_un.sun_path, sockaddr_len-UN_PATH_OFFSET);
fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
i = do_mknod(fname, S_IFSOCK | 0777, 0);
if (i == 0)
i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL);
if (i < 0) {
printk("unix_proto_bind: can't open socket %s\n", fname);
return i;
}
upd->sockaddr_len = sockaddr_len; /* now its legal */
PRINTK(("unix_proto_bind: bound socket address: "));
#ifdef SOCK_DEBUG
sockaddr_un_printk(&upd->sockaddr_un, upd->sockaddr_len);
#endif
PRINTK(("to inode 0x%x\n", upd->inode));
return 0;
}
/*
* perform a connection. we can only connect to unix sockets (i can't for
* the life of me find an application where that wouldn't be the case!)
*/
static int
unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
int sockaddr_len, int flags)
{
int i;
struct unix_proto_data *serv_upd;
struct sockaddr_un sockun;
char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1];
struct inode *inode;
PRINTK(("unix_proto_connect: socket 0x%x, servlen=%d\n", sock,
sockaddr_len));
if (sockaddr_len <= UN_PATH_OFFSET ||
sockaddr_len > sizeof(struct sockaddr_un)) {
PRINTK(("unix_proto_connect: bad length %d\n", sockaddr_len));
return -EINVAL;
}
if (sock->state == SS_CONNECTING)
return (-EINPROGRESS);
if (sock->state == SS_CONNECTED)
return (-EISCONN);
verify_area(VERIFY_WRITE,uservaddr, sockaddr_len);
memcpy_fromfs(&sockun, uservaddr, sockaddr_len);
if (sockun.sun_family != AF_UNIX) {
PRINTK(("unix_proto_connect: family is %d, not AF_UNIX (%d)\n",
sockun.sun_family, AF_UNIX));
return -EINVAL;
}
/*
* try to open the name in the filesystem - this is how we
* identify ourselves and our server. Note that we don't
* hold onto the inode that long, just enough to find our
* server. When we're connected, we mooch off the server.
*/
memcpy(fname, sockun.sun_path, sockaddr_len-UN_PATH_OFFSET);
fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
i = open_namei(fname, 0, S_IFSOCK, &inode, NULL);
if (i < 0) {
PRINTK(("unix_proto_connect: can't open socket %s\n", fname));
return i;
}
serv_upd = unix_data_lookup(&sockun, sockaddr_len, inode);
iput(inode);
if (!serv_upd) {
PRINTK(("unix_proto_connect: can't locate peer %s at inode 0x%x\n",
fname, inode));
return -EINVAL;
}
if ((i = sock_awaitconn(sock, serv_upd->socket)) < 0) {
PRINTK(("unix_proto_connect: can't await connection\n"));
return i;
}
unix_data_ref(UN_DATA(sock->conn));
UN_DATA(sock)->peerupd = UN_DATA(sock->conn); /* ref server */
return 0;
}
/*
* to do a socketpair, we just connect the two datas, easy! since we
* always wait on the socket inode, they're no contention for a wait area,
* and deadlock prevention in the case of a process writing to itself is,
* ignored, in true unix fashion!
*/
static int
unix_proto_socketpair(struct socket *sock1, struct socket *sock2)
{
struct unix_proto_data *upd1 = UN_DATA(sock1), *upd2 = UN_DATA(sock2);
unix_data_ref(upd1);
unix_data_ref(upd2);
upd1->peerupd = upd2;
upd2->peerupd = upd1;
return 0;
}
/*
* on accept, we ref the peer's data for safe writes
*/
static int
unix_proto_accept(struct socket *sock, struct socket *newsock, int flags)
{
struct socket *clientsock;
PRINTK(("unix_proto_accept: socket 0x%x accepted via socket 0x%x\n",
sock, newsock));
/*
* if there aren't any sockets awaiting connection, then wait for
* one, unless nonblocking
*/
while (!(clientsock = sock->iconn)) {
if (flags & O_NONBLOCK)
return -EAGAIN;
interruptible_sleep_on(sock->wait);
if (current->signal & ~current->blocked) {
PRINTK(("sys_accept: sleep was interrupted\n"));
return -ERESTARTSYS;
}
}
/*
* great. finish the connection relative to server and client,
* wake up the client and return the new fd to the server
*/
sock->iconn = clientsock->next;
clientsock->next = NULL;
newsock->conn = clientsock;
clientsock->conn = newsock;
clientsock->state = SS_CONNECTED;
newsock->state = SS_CONNECTED;
wake_up(clientsock->wait);
unix_data_ref (UN_DATA(newsock->conn));
UN_DATA(newsock)->peerupd = UN_DATA(newsock->conn);
UN_DATA(newsock)->sockaddr_un = UN_DATA(sock)->sockaddr_un;
UN_DATA(newsock)->sockaddr_len = UN_DATA(sock)->sockaddr_len;
UN_DATA(newsock->conn)->sockaddr_un = UN_DATA(sock)->sockaddr_un;
UN_DATA(newsock->conn)->sockaddr_len = UN_DATA(sock)->sockaddr_len;
return 0;
}
/*
* gets the current name or the name of the connected socket.
*/
static int
unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr,
int *usockaddr_len, int peer)
{
struct unix_proto_data *upd;
int len;
PRINTK(("unix_proto_getname: socket 0x%x for %s\n", sock,
peer ? "peer" : "self"));
if (peer) {
if (sock->state != SS_CONNECTED) {
PRINTK(("unix_proto_getname: socket not connected\n"));
return -EINVAL;
}
upd = UN_DATA(sock->conn);
}
else
upd = UN_DATA(sock);
verify_area(VERIFY_WRITE,usockaddr_len, sizeof(*usockaddr_len));
if ((len = get_fs_long(usockaddr_len)) <= 0)
return -EINVAL;
if (len > upd->sockaddr_len)
len = upd->sockaddr_len;
if (len) {
verify_area(VERIFY_WRITE,usockaddr, len);
memcpy_tofs(usockaddr, &upd->sockaddr_un, len);
}
put_fs_long(len, usockaddr_len);
return 0;
}
/*
* we read from our own buf.
*/
static int
unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock)
{
struct unix_proto_data *upd;
int todo, avail;
if ((todo = size) <= 0)
return 0;
upd = UN_DATA(sock);
while (!(avail = UN_BUF_AVAIL(upd))) {
if (sock->state != SS_CONNECTED) {
PRINTK(("unix_proto_read: socket not connected\n"));
return (sock->state == SS_DISCONNECTING) ? 0 : -EINVAL;
}
PRINTK(("unix_proto_read: no data available...\n"));
if (nonblock)
return -EAGAIN;
interruptible_sleep_on(sock->wait);
if (current->signal & ~current->blocked) {
PRINTK(("unix_proto_read: interrupted\n"));
return -ERESTARTSYS;
}
}
/*
* copy from the read buffer into the user's buffer, watching for
* wraparound. then we wake up the writer
*/
do {
int part, cando;
if (avail <= 0) {
PRINTK(("unix_proto_read: AVAIL IS NEGATIVE!!!\n"));
send_sig(SIGKILL,current,1);
return -EINTR;
}
if ((cando = todo) > avail)
cando = avail;
if (cando > (part = BUF_SIZE - upd->bp_tail))
cando = part;
PRINTK(("unix_proto_read: avail=%d, todo=%d, cando=%d\n",
avail, todo, cando));
verify_area(VERIFY_WRITE,ubuf, cando);
memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando);
upd->bp_tail = (upd->bp_tail + cando) & (BUF_SIZE-1);
ubuf += cando;
todo -= cando;
if (sock->state == SS_CONNECTED)
wake_up(sock->conn->wait);
avail = UN_BUF_AVAIL(upd);
} while (todo && avail);
return size - todo;
}
/*
* we write to our peer's buf. when we connected we ref'd this peer so we
* are safe that the buffer remains, even after the peer has disconnected,
* which we check other ways.
*/
static int
unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock)
{
struct unix_proto_data *pupd;
int todo, space;
if ((todo = size) <= 0)
return 0;
if (sock->state != SS_CONNECTED) {
PRINTK(("unix_proto_write: socket not connected\n"));
if (sock->state == SS_DISCONNECTING) {
send_sig(SIGPIPE,current,1);
return -EINTR;
}
return -EINVAL;
}
pupd = UN_DATA(sock)->peerupd; /* safer than sock->conn */
while (!(space = UN_BUF_SPACE(pupd))) {
PRINTK(("unix_proto_write: no space left...\n"));
if (nonblock)
return -EAGAIN;
interruptible_sleep_on(sock->wait);
if (current->signal & ~current->blocked) {
PRINTK(("unix_proto_write: interrupted\n"));
return -ERESTARTSYS;
}
if (sock->state == SS_DISCONNECTING) {
PRINTK(("unix_proto_write: disconnected (SIGPIPE)\n"));
send_sig(SIGPIPE,current,1);
return -EINTR;
}
}
/*
* copy from the user's buffer to the write buffer, watching for
* wraparound. then we wake up the reader
*/
do {
int part, cando;
if (space <= 0) {
PRINTK(("unix_proto_write: SPACE IS NEGATIVE!!!\n"));
send_sig(SIGKILL,current,1);
return -EINTR;
}
/*
* we may become disconnected inside this loop, so watch
* for it (peerupd is safe until we close)
*/
if (sock->state == SS_DISCONNECTING) {
send_sig(SIGPIPE,current,1);
return -EINTR;
}
if ((cando = todo) > space)
cando = space;
if (cando > (part = BUF_SIZE - pupd->bp_head))
cando = part;
PRINTK(("unix_proto_write: space=%d, todo=%d, cando=%d\n",
space, todo, cando));
verify_area(VERIFY_WRITE,ubuf, cando);
memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando);
pupd->bp_head = (pupd->bp_head + cando) & (BUF_SIZE-1);
ubuf += cando;
todo -= cando;
if (sock->state == SS_CONNECTED)
wake_up(sock->conn->wait);
space = UN_BUF_SPACE(pupd);
} while (todo && space);
return size - todo;
}
static int
unix_proto_select(struct socket *sock, int sel_type, select_table * wait)
{
struct unix_proto_data *upd, *peerupd;
/*
* handle server sockets specially
*/
if (sock->flags & SO_ACCEPTCON) {
if (sel_type == SEL_IN) {
PRINTK(("sock_select: %sconnections pending\n",
sock->iconn ? "" : "no "));
if (sock->iconn)
return 1;
select_wait(sock->wait, wait);
return sock->iconn ? 1 : 0;
}
PRINTK(("sock_select: nothing else for server socket\n"));
select_wait(sock->wait, wait);
return 0;
}
if (sel_type == SEL_IN) {
upd = UN_DATA(sock);
PRINTK(("unix_proto_select: there is%s data available\n",
UN_BUF_AVAIL(upd) ? "" : " no"));
if (UN_BUF_AVAIL(upd)) /* even if disconnected */
return 1;
else if (sock->state != SS_CONNECTED) {
PRINTK(("unix_proto_select: socket not connected (read EOF)\n"));
return 1;
}
select_wait(sock->wait,wait);
return 0;
}
if (sel_type == SEL_OUT) {
if (sock->state != SS_CONNECTED) {
PRINTK(("unix_proto_select: socket not connected (write EOF)\n"));
return 1;
}
peerupd = UN_DATA(sock->conn);
PRINTK(("unix_proto_select: there is%s space available\n",
UN_BUF_SPACE(peerupd) ? "" : " no"));
if (UN_BUF_SPACE(peerupd) > 0)
return 1;
select_wait(sock->wait,wait);
return 0;
}
/* SEL_EX */
PRINTK(("unix_proto_select: there are no exceptions here?!\n"));
return 0;
}
static int
unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct unix_proto_data *upd, *peerupd;
upd = UN_DATA(sock);
peerupd = (sock->state == SS_CONNECTED) ? UN_DATA(sock->conn) : NULL;
switch (cmd) {
case TIOCINQ:
if (sock->flags & SO_ACCEPTCON)
return -EINVAL;
verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
if (UN_BUF_AVAIL(upd) || peerupd)
put_fs_long(UN_BUF_AVAIL(upd), (unsigned long *)arg);
else
put_fs_long(1, (unsigned long *)arg); /* read EOF */
break;
case TIOCOUTQ:
if (sock->flags & SO_ACCEPTCON)
return -EINVAL;
verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
if (peerupd)
put_fs_long(UN_BUF_SPACE(peerupd),
(unsigned long *)arg);
else
put_fs_long(0, (unsigned long *)arg);
break;
default:
return -EINVAL;
}
return 0;
}
static int
unix_proto_init(void)
{
struct unix_proto_data *upd;
PRINTK(("unix_proto_init: initializing...\n"));
for (upd = unix_datas; upd <= last_unix_data; ++upd)
upd->refcnt = 0;
return 0;
}