Logo Search packages:      
Sourcecode: linux version File versions  Download package

timod.c

/* $Id: timod.c,v 1.19 2002/02/08 03:57:14 davem Exp $
 * timod.c: timod emulation.
 *
 * Copyright (C) 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)
 *
 * Streams & timod emulation based on code
 * Copyright (C) 1995, 1996 Mike Jagdis (jaggy@purplet.demon.co.uk)
 *
 */
 
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/netdevice.h>
#include <linux/poll.h>

#include <net/sock.h>

#include <asm/uaccess.h>
#include <asm/termios.h>

#include "conv.h"
#include "socksys.h"

asmlinkage int solaris_ioctl(unsigned int fd, unsigned int cmd, u32 arg);

static DEFINE_SPINLOCK(timod_pagelock);
static char * page = NULL ;

#ifndef DEBUG_SOLARIS_KMALLOC

#define mykmalloc kmalloc
#define mykfree kfree

#else

void * mykmalloc(size_t s, gfp_t gfp)
{
      static char * page;
      static size_t free;
      void * r;
      s = ((s + 63) & ~63);
      if( s > PAGE_SIZE ) {
            SOLD("too big size, calling real kmalloc");
            return kmalloc(s, gfp);
      }
      if( s > free ) {
            /* we are wasting memory, but we don't care */
            page = (char *)__get_free_page(gfp);
            free = PAGE_SIZE;
      }
      r = page;
      page += s;
      free -= s;
      return r;
}

void mykfree(void *p)
{
}

#endif

#ifndef DEBUG_SOLARIS

#define BUF_SIZE  PAGE_SIZE
#define PUT_MAGIC(a,m)
#define SCHECK_MAGIC(a,m)
#define BUF_OFFSET      0
#define MKCTL_TRAILER   0

#else

#define BUF_SIZE  (PAGE_SIZE-2*sizeof(u64))
#define BUFPAGE_MAGIC   0xBADC0DEDDEADBABEL
#define MKCTL_MAGIC     0xDEADBABEBADC0DEDL
#define PUT_MAGIC(a,m)  do{(*(u64*)(a))=(m);}while(0)
#define SCHECK_MAGIC(a,m)     do{if((*(u64*)(a))!=(m))printk("%s,%u,%s(): magic %08x at %p corrupted!\n",\
                        __FILE__,__LINE__,__FUNCTION__,(m),(a));}while(0)
#define BUF_OFFSET      sizeof(u64)
#define MKCTL_TRAILER   sizeof(u64)

#endif

static char *getpage( void )
{
      char *r;
      SOLD("getting page");
      spin_lock(&timod_pagelock);
      if (page) {
            r = page;
            page = NULL;
            spin_unlock(&timod_pagelock);
            SOLD("got cached");
            return r + BUF_OFFSET;
      }
      spin_unlock(&timod_pagelock);
      SOLD("getting new");
      r = (char *)__get_free_page(GFP_KERNEL);
      PUT_MAGIC(r,BUFPAGE_MAGIC);
      PUT_MAGIC(r+PAGE_SIZE-sizeof(u64),BUFPAGE_MAGIC);
      return r + BUF_OFFSET;
}

static void putpage(char *p)
{
      SOLD("putting page");
      p = p - BUF_OFFSET;
      SCHECK_MAGIC(p,BUFPAGE_MAGIC);
      SCHECK_MAGIC(p+PAGE_SIZE-sizeof(u64),BUFPAGE_MAGIC);
      spin_lock(&timod_pagelock);
      if (page) {
            spin_unlock(&timod_pagelock);
            free_page((unsigned long)p);
            SOLD("freed it");
      } else {
            page = p;
            spin_unlock(&timod_pagelock);
            SOLD("cached it");
      }
}

static struct T_primsg *timod_mkctl(int size)
{
      struct T_primsg *it;

      SOLD("creating primsg");
      it = (struct T_primsg *)mykmalloc(size+sizeof(*it)-sizeof(s32)+2*MKCTL_TRAILER, GFP_KERNEL);
      if (it) {
            SOLD("got it");
            it->pri = MSG_HIPRI;
            it->length = size;
            PUT_MAGIC((char*)((u64)(((char *)&it->type)+size+7)&~7),MKCTL_MAGIC);
      }
      return it;
}

static void timod_wake_socket(unsigned int fd)
{
      struct socket *sock;
      struct fdtable *fdt;

      SOLD("wakeing socket");
      fdt = files_fdtable(current->files);
      sock = SOCKET_I(fdt->fd[fd]->f_path.dentry->d_inode);
      wake_up_interruptible(&sock->wait);
      read_lock(&sock->sk->sk_callback_lock);
      if (sock->fasync_list && !test_bit(SOCK_ASYNC_WAITDATA, &sock->flags))
            __kill_fasync(sock->fasync_list, SIGIO, POLL_IN);
      read_unlock(&sock->sk->sk_callback_lock);
      SOLD("done");
}

static void timod_queue(unsigned int fd, struct T_primsg *it)
{
      struct sol_socket_struct *sock;
      struct fdtable *fdt;

      SOLD("queuing primsg");
      fdt = files_fdtable(current->files);
      sock = (struct sol_socket_struct *)fdt->fd[fd]->private_data;
      it->next = sock->pfirst;
      sock->pfirst = it;
      if (!sock->plast)
            sock->plast = it;
      timod_wake_socket(fd);
      SOLD("done");
}

static void timod_queue_end(unsigned int fd, struct T_primsg *it)
{
      struct sol_socket_struct *sock;
      struct fdtable *fdt;

      SOLD("queuing primsg at end");
      fdt = files_fdtable(current->files);
      sock = (struct sol_socket_struct *)fdt->fd[fd]->private_data;
      it->next = NULL;
      if (sock->plast)
            sock->plast->next = it;
      else
            sock->pfirst = it;
      sock->plast = it;
      SOLD("done");
}

static void timod_error(unsigned int fd, int prim, int terr, int uerr)
{
      struct T_primsg *it;
      
      SOLD("making error");
      it = timod_mkctl(sizeof(struct T_error_ack));
      if (it) {
            struct T_error_ack *err = (struct T_error_ack *)&it->type;
            
            SOLD("got it");
            err->PRIM_type = T_ERROR_ACK;
            err->ERROR_prim = prim;
            err->TLI_error = terr;
            err->UNIX_error = uerr; /* FIXME: convert this */
            timod_queue(fd, it);
      }
      SOLD("done");
}

static void timod_ok(unsigned int fd, int prim)
{
      struct T_primsg *it;
      struct T_ok_ack *ok;
      
      SOLD("creating ok ack");
      it = timod_mkctl(sizeof(*ok));
      if (it) {
            SOLD("got it");
            ok = (struct T_ok_ack *)&it->type;
            ok->PRIM_type = T_OK_ACK;
            ok->CORRECT_prim = prim;
            timod_queue(fd, it);
      }
      SOLD("done");
}

static int timod_optmgmt(unsigned int fd, int flag, char __user *opt_buf, int opt_len, int do_ret)
{
      int error, failed;
      int ret_space, ret_len;
      long args[5];
      char *ret_pos,*ret_buf;
      int (*sys_socketcall)(int, unsigned long *) =
            (int (*)(int, unsigned long *))SYS(socketcall);
      mm_segment_t old_fs = get_fs();

      SOLD("entry");
      SOLDD(("fd %u flg %u buf %p len %u doret %u",fd,flag,opt_buf,opt_len,do_ret));
      if (!do_ret && (!opt_buf || opt_len <= 0))
            return 0;
      SOLD("getting page");
      ret_pos = ret_buf = getpage();
      ret_space = BUF_SIZE;
      ret_len = 0;
      
      error = failed = 0;
      SOLD("looping");
      while(opt_len >= sizeof(struct opthdr)) {
            struct opthdr *opt;
            int orig_opt_len; 
            SOLD("loop start");
            opt = (struct opthdr *)ret_pos; 
            if (ret_space < sizeof(struct opthdr)) {
                  failed = TSYSERR;
                  break;
            }
            SOLD("getting opthdr");
            if (copy_from_user(opt, opt_buf, sizeof(struct opthdr)) ||
                  opt->len > opt_len) {
                  failed = TBADOPT;
                  break;
            }
            SOLD("got opthdr");
            if (flag == T_NEGOTIATE) {
                  char *buf;
                  
                  SOLD("handling T_NEGOTIATE");
                  buf = ret_pos + sizeof(struct opthdr);
                  if (ret_space < opt->len + sizeof(struct opthdr) ||
                        copy_from_user(buf, opt_buf+sizeof(struct opthdr), opt->len)) {
                        failed = TSYSERR;
                        break;
                  }
                  SOLD("got optdata");
                  args[0] = fd;
                  args[1] = opt->level;
                  args[2] = opt->name;
                  args[3] = (long)buf;
                  args[4] = opt->len;
                  SOLD("calling SETSOCKOPT");
                  set_fs(KERNEL_DS);
                  error = sys_socketcall(SYS_SETSOCKOPT, args);
                  set_fs(old_fs);
                  if (error) {
                        failed = TBADOPT;
                        break;
                  }
                  SOLD("SETSOCKOPT ok");
            }
            orig_opt_len = opt->len;
            opt->len = ret_space - sizeof(struct opthdr);
            if (opt->len < 0) {
                  failed = TSYSERR;
                  break;
            }
            args[0] = fd;
            args[1] = opt->level;
            args[2] = opt->name;
            args[3] = (long)(ret_pos+sizeof(struct opthdr));
            args[4] = (long)&opt->len;
            SOLD("calling GETSOCKOPT");
            set_fs(KERNEL_DS);
            error = sys_socketcall(SYS_GETSOCKOPT, args);
            set_fs(old_fs);
            if (error) {
                  failed = TBADOPT;
                  break;
            }
            SOLD("GETSOCKOPT ok");
            ret_space -= sizeof(struct opthdr) + opt->len;
            ret_len += sizeof(struct opthdr) + opt->len;
            ret_pos += sizeof(struct opthdr) + opt->len;
            opt_len -= sizeof(struct opthdr) + orig_opt_len;
            opt_buf += sizeof(struct opthdr) + orig_opt_len;
            SOLD("loop end");
      }
      SOLD("loop done");
      if (do_ret) {
            SOLD("generating ret msg");
            if (failed)
                  timod_error(fd, T_OPTMGMT_REQ, failed, -error);
            else {
                  struct T_primsg *it;
                  it = timod_mkctl(sizeof(struct T_optmgmt_ack) + ret_len);
                  if (it) {
                        struct T_optmgmt_ack *ack =
                              (struct T_optmgmt_ack *)&it->type;
                        SOLD("got primsg");
                        ack->PRIM_type = T_OPTMGMT_ACK;
                        ack->OPT_length = ret_len;
                        ack->OPT_offset = sizeof(struct T_optmgmt_ack);
                        ack->MGMT_flags = (failed ? T_FAILURE : flag);
                        memcpy(((char*)ack)+sizeof(struct T_optmgmt_ack),
                              ret_buf, ret_len);
                        timod_queue(fd, it);
                  }
            }
      }
      SOLDD(("put_page %p\n", ret_buf));
      putpage(ret_buf);
      SOLD("done");     
      return 0;
}

int timod_putmsg(unsigned int fd, char __user *ctl_buf, int ctl_len,
                  char __user *data_buf, int data_len, int flags)
{
      int ret, error, terror;
      char *buf;
      struct file *filp;
      struct inode *ino;
      struct fdtable *fdt;
      struct sol_socket_struct *sock;
      mm_segment_t old_fs = get_fs();
      long args[6];
      int (*sys_socketcall)(int, unsigned long __user *) =
            (int (*)(int, unsigned long __user *))SYS(socketcall);
      int (*sys_sendto)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int) =
            (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int))SYS(sendto);

      fdt = files_fdtable(current->files);
      filp = fdt->fd[fd];
      ino = filp->f_path.dentry->d_inode;
      sock = (struct sol_socket_struct *)filp->private_data;
      SOLD("entry");
      if (get_user(ret, (int __user *)A(ctl_buf)))
            return -EFAULT;
      switch (ret) {
      case T_BIND_REQ:
      {
            struct T_bind_req req;
            
            SOLDD(("bind %016lx(%016lx)\n", sock, filp));
            SOLD("T_BIND_REQ");
            if (sock->state != TS_UNBND) {
                  timod_error(fd, T_BIND_REQ, TOUTSTATE, 0);
                  return 0;
            }
            SOLD("state ok");
            if (copy_from_user(&req, ctl_buf, sizeof(req))) {
                  timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT);
                  return 0;
            }
            SOLD("got ctl req");
            if (req.ADDR_offset && req.ADDR_length) {
                  if (req.ADDR_length > BUF_SIZE) {
                        timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT);
                        return 0;
                  }
                  SOLD("req size ok");
                  buf = getpage();
                  if (copy_from_user(buf, ctl_buf + req.ADDR_offset, req.ADDR_length)) {
                        timod_error(fd, T_BIND_REQ, TSYSERR, EFAULT);
                        putpage(buf);
                        return 0;
                  }
                  SOLD("got ctl data");
                  args[0] = fd;
                  args[1] = (long)buf;
                  args[2] = req.ADDR_length;
                  SOLD("calling BIND");
                  set_fs(KERNEL_DS);
                  error = sys_socketcall(SYS_BIND, args);
                  set_fs(old_fs);
                  putpage(buf);
                  SOLD("BIND returned");
            } else 
                  error = 0;
            if (!error) {
                  struct T_primsg *it;
                  if (req.CONIND_number) {
                        args[0] = fd;
                        args[1] = req.CONIND_number;
                        SOLD("calling LISTEN");
                        set_fs(KERNEL_DS);
                        error = sys_socketcall(SYS_LISTEN, args);
                        set_fs(old_fs);
                        SOLD("LISTEN done");
                  }
                  it = timod_mkctl(sizeof(struct T_bind_ack)+sizeof(struct sockaddr));
                  if (it) {
                        struct T_bind_ack *ack;

                        ack = (struct T_bind_ack *)&it->type;
                        ack->PRIM_type = T_BIND_ACK;
                        ack->ADDR_offset = sizeof(*ack);
                        ack->ADDR_length = sizeof(struct sockaddr);
                        ack->CONIND_number = req.CONIND_number;
                        args[0] = fd;
                        args[1] = (long)(ack+sizeof(*ack));
                        args[2] = (long)&ack->ADDR_length;
                        set_fs(KERNEL_DS);
                        sys_socketcall(SYS_GETSOCKNAME,args);
                        set_fs(old_fs);
                        sock->state = TS_IDLE;
                        timod_ok(fd, T_BIND_REQ);
                        timod_queue_end(fd, it);
                        SOLD("BIND done");
                        return 0;
                  }
            }
            SOLD("some error");
            switch (error) {
                  case -EINVAL:
                        terror = TOUTSTATE;
                        error = 0;
                        break;
                  case -EACCES:
                        terror = TACCES;
                        error = 0;
                        break;
                  case -EADDRNOTAVAIL:
                  case -EADDRINUSE:
                        terror = TNOADDR;
                        error = 0;
                        break;
                  default:
                        terror = TSYSERR;
                        break;
            }
            timod_error(fd, T_BIND_REQ, terror, -error);
            SOLD("BIND done");
            return 0;
      }
      case T_CONN_REQ:
      {
            struct T_conn_req req;
            unsigned short oldflags;
            struct T_primsg *it;
            SOLD("T_CONN_REQ");
            if (sock->state != TS_UNBND && sock->state != TS_IDLE) {
                  timod_error(fd, T_CONN_REQ, TOUTSTATE, 0);
                  return 0;
            }
            SOLD("state ok");
            if (copy_from_user(&req, ctl_buf, sizeof(req))) {
                  timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT);
                  return 0;
            }
            SOLD("got ctl req");
            if (ctl_len > BUF_SIZE) {
                  timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT);
                  return 0;
            }
            SOLD("req size ok");
            buf = getpage();
            if (copy_from_user(buf, ctl_buf, ctl_len)) {
                  timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT);
                  putpage(buf);
                  return 0;
            }
#ifdef DEBUG_SOLARIS          
            {
                  char * ptr = buf;
                  int len = ctl_len;
                  printk("returned data (%d bytes): ",len);
                  while( len-- ) {
                        if (!(len & 7))
                              printk(" ");
                        printk("%02x",(unsigned char)*ptr++);
                  }
                  printk("\n");
            }
#endif
            SOLD("got ctl data");
            args[0] = fd;
            args[1] = (long)buf+req.DEST_offset;
            args[2] = req.DEST_length;
            oldflags = filp->f_flags;
            filp->f_flags &= ~O_NONBLOCK;
            SOLD("calling CONNECT");
            set_fs(KERNEL_DS);
            error = sys_socketcall(SYS_CONNECT, args);
            set_fs(old_fs);
            filp->f_flags = oldflags;
            SOLD("CONNECT done");
            if (!error) {
                  struct T_conn_con *con;
                  SOLD("no error");
                  it = timod_mkctl(ctl_len);
                  if (!it) {
                        putpage(buf);
                        return -ENOMEM;
                  }
                  con = (struct T_conn_con *)&it->type;
#ifdef DEBUG_SOLARIS                
                  {
                        char * ptr = buf;
                        int len = ctl_len;
                        printk("returned data (%d bytes): ",len);
                        while( len-- ) {
                              if (!(len & 7))
                                    printk(" ");
                              printk("%02x",(unsigned char)*ptr++);
                        }
                        printk("\n");
                  }
#endif
                  memcpy(con, buf, ctl_len);
                  SOLD("copied ctl_buf");
                  con->PRIM_type = T_CONN_CON;
                  sock->state = TS_DATA_XFER;
            } else {
                  struct T_discon_ind *dis;
                  SOLD("some error");
                  it = timod_mkctl(sizeof(*dis));
                  if (!it) {
                        putpage(buf);
                        return -ENOMEM;
                  }
                  SOLD("got primsg");
                  dis = (struct T_discon_ind *)&it->type;
                  dis->PRIM_type = T_DISCON_IND;
                  dis->DISCON_reason = -error;  /* FIXME: convert this as in iABI_errors() */
                  dis->SEQ_number = 0;
            }
            putpage(buf);
            timod_ok(fd, T_CONN_REQ);
            it->pri = 0;
            timod_queue_end(fd, it);
            SOLD("CONNECT done");
            return 0;
      }
      case T_OPTMGMT_REQ:
      {
            struct T_optmgmt_req req;
            SOLD("OPTMGMT_REQ");
            if (copy_from_user(&req, ctl_buf, sizeof(req)))
                  return -EFAULT;
            SOLD("got req");
            return timod_optmgmt(fd, req.MGMT_flags,
                        req.OPT_offset > 0 ? ctl_buf + req.OPT_offset : NULL,
                        req.OPT_length, 1);
      }
      case T_UNITDATA_REQ:
      {
            struct T_unitdata_req req;
            
            int err;
            SOLD("T_UNITDATA_REQ");
            if (sock->state != TS_IDLE && sock->state != TS_DATA_XFER) {
                  timod_error(fd, T_CONN_REQ, TOUTSTATE, 0);
                  return 0;
            }
            SOLD("state ok");
            if (copy_from_user(&req, ctl_buf, sizeof(req))) {
                  timod_error(fd, T_CONN_REQ, TSYSERR, EFAULT);
                  return 0;
            }
            SOLD("got ctl req");
#ifdef DEBUG_SOLARIS          
            {
                  char * ptr = ctl_buf+req.DEST_offset;
                  int len = req.DEST_length;
                  printk("socket address (%d bytes): ",len);
                  while( len-- ) {
                        char c;
                        if (get_user(c,ptr))
                              printk("??");
                        else
                              printk("%02x",(unsigned char)c);
                        ptr++;
                  }
                  printk("\n");
            }
#endif            
            err = sys_sendto(fd, data_buf, data_len, 0, req.DEST_length > 0 ? (struct sockaddr __user *)(ctl_buf+req.DEST_offset) : NULL, req.DEST_length);
            if (err == data_len)
                  return 0;
            if(err >= 0) {
                  printk("timod: sendto failed to send all the data\n");
                  return 0;
            }
            timod_error(fd, T_CONN_REQ, TSYSERR, -err);
            return 0;
      }
      default:
            printk(KERN_INFO "timod_putmsg: unsupported command %u.\n", ret);
            break;
      }
      return -EINVAL;
}

int timod_getmsg(unsigned int fd, char __user *ctl_buf, int ctl_maxlen, s32 __user *ctl_len,
                  char __user *data_buf, int data_maxlen, s32 __user *data_len, int *flags_p)
{
      int error;
      int oldflags;
      struct file *filp;
      struct inode *ino;
      struct fdtable *fdt;
      struct sol_socket_struct *sock;
      struct T_unitdata_ind udi;
      mm_segment_t old_fs = get_fs();
      long args[6];
      char __user *tmpbuf;
      int tmplen;
      int (*sys_socketcall)(int, unsigned long __user *) =
            (int (*)(int, unsigned long __user *))SYS(socketcall);
      int (*sys_recvfrom)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *);
      
      SOLD("entry");
      SOLDD(("%u %p %d %p %p %d %p %d\n", fd, ctl_buf, ctl_maxlen, ctl_len, data_buf, data_maxlen, data_len, *flags_p));
      fdt = files_fdtable(current->files);
      filp = fdt->fd[fd];
      ino = filp->f_path.dentry->d_inode;
      sock = (struct sol_socket_struct *)filp->private_data;
      SOLDD(("%p %p\n", sock->pfirst, sock->pfirst ? sock->pfirst->next : NULL));
      if ( ctl_maxlen > 0 && !sock->pfirst && SOCKET_I(ino)->type == SOCK_STREAM
            && sock->state == TS_IDLE) {
            SOLD("calling LISTEN");
            args[0] = fd;
            args[1] = -1;
            set_fs(KERNEL_DS);
            sys_socketcall(SYS_LISTEN, args);
            set_fs(old_fs);
            SOLD("LISTEN done");
      }
      if (!(filp->f_flags & O_NONBLOCK)) {
            struct poll_wqueues wait_table;
            poll_table *wait;

            poll_initwait(&wait_table);
            wait = &wait_table.pt;
            for(;;) {
                  SOLD("loop");
                  set_current_state(TASK_INTERRUPTIBLE);
                  /* ! ( l<0 || ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ 
                  /* ( ! l<0 && ! ( l>=0 && ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ 
                  /* ( l>=0 && ( ! l>=0 || ! ( ! pfirst || (flags == HIPRI && pri != HIPRI) ) ) ) */ 
                  /* ( l>=0 && ( l<0 || ( pfirst && ! (flags == HIPRI && pri != HIPRI) ) ) ) */ 
                  /* ( l>=0 && ( l<0 || ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) ) */ 
                  /* ( l>=0 && ( pfirst && (flags != HIPRI || pri == HIPRI) ) ) */ 
                  if (ctl_maxlen >= 0 && sock->pfirst && (*flags_p != MSG_HIPRI || sock->pfirst->pri == MSG_HIPRI))
                        break;
                  SOLD("cond 1 passed");
                  if (
                  #if 1
                        *flags_p != MSG_HIPRI &&
                  #endif
                        ((filp->f_op->poll(filp, wait) & POLLIN) ||
                        (filp->f_op->poll(filp, NULL) & POLLIN) ||
                        signal_pending(current))
                  ) {
                        break;
                  }
                  if( *flags_p == MSG_HIPRI ) {
                        SOLD("avoiding lockup");
                        break ;
                  }
                  if(wait_table.error) {
                        SOLD("wait-table error");
                        poll_freewait(&wait_table);
                        return wait_table.error;
                  }
                  SOLD("scheduling");
                  schedule();
            }
            SOLD("loop done");
            current->state = TASK_RUNNING;
            poll_freewait(&wait_table);
            if (signal_pending(current)) {
                  SOLD("signal pending");
                  return -EINTR;
            }
      }
      if (ctl_maxlen >= 0 && sock->pfirst) {
            struct T_primsg *it = sock->pfirst;
            int l = min_t(int, ctl_maxlen, it->length);
            SCHECK_MAGIC((char*)((u64)(((char *)&it->type)+sock->offset+it->length+7)&~7),MKCTL_MAGIC);
            SOLD("purting ctl data");
            if(copy_to_user(ctl_buf,
                  (char*)&it->type + sock->offset, l))
                  return -EFAULT;
            SOLD("pur it");
            if(put_user(l, ctl_len))
                  return -EFAULT;
            SOLD("set ctl_len");
            *flags_p = it->pri;
            it->length -= l;
            if (it->length) {
                  SOLD("more ctl");
                  sock->offset += l;
                  return MORECTL;
            } else {
                  SOLD("removing message");
                  sock->pfirst = it->next;
                  if (!sock->pfirst)
                        sock->plast = NULL;
                  SOLDD(("getmsg kfree %016lx->%016lx\n", it, sock->pfirst));
                  mykfree(it);
                  sock->offset = 0;
                  SOLD("ctl done");
                  return 0;
            }
      }
      *flags_p = 0;
      if (ctl_maxlen >= 0) {
            SOLD("ACCEPT perhaps?");
            if (SOCKET_I(ino)->type == SOCK_STREAM && sock->state == TS_IDLE) {
                  struct T_conn_ind ind;
                  char *buf = getpage();
                  int len = BUF_SIZE;

                  SOLD("trying ACCEPT");
                  if (put_user(ctl_maxlen - sizeof(ind), ctl_len))
                        return -EFAULT;
                  args[0] = fd;
                  args[1] = (long)buf;
                  args[2] = (long)&len;
                  oldflags = filp->f_flags;
                  filp->f_flags |= O_NONBLOCK;
                  SOLD("calling ACCEPT");
                  set_fs(KERNEL_DS);
                  error = sys_socketcall(SYS_ACCEPT, args);
                  set_fs(old_fs);
                  filp->f_flags = oldflags;
                  if (error < 0) {
                        SOLD("some error");
                        putpage(buf);
                        return error;
                  }
                  if (error) {
                        SOLD("connect");
                        putpage(buf);
                        if (sizeof(ind) > ctl_maxlen) {
                              SOLD("generating CONN_IND");
                              ind.PRIM_type = T_CONN_IND;
                              ind.SRC_length = len;
                              ind.SRC_offset = sizeof(ind);
                              ind.OPT_length = ind.OPT_offset = 0;
                              ind.SEQ_number = error;
                              if(copy_to_user(ctl_buf, &ind, sizeof(ind))||
                                 put_user(sizeof(ind)+ind.SRC_length,ctl_len))
                                    return -EFAULT;
                              SOLD("CONN_IND created");
                        }
                        if (data_maxlen >= 0)
                              put_user(0, data_len);
                        SOLD("CONN_IND done");
                        return 0;
                  }
                  if (len>ctl_maxlen) {
                        SOLD("data don't fit");
                        putpage(buf);
                        return -EFAULT;         /* XXX - is this ok ? */
                  }
                  if(copy_to_user(ctl_buf,buf,len) || put_user(len,ctl_len)){
                        SOLD("can't copy data");
                        putpage(buf);
                        return -EFAULT;
                  }
                  SOLD("ACCEPT done");
                  putpage(buf);
            }
      }
      SOLD("checking data req");
      if (data_maxlen <= 0) {
            if (data_maxlen == 0)
                  put_user(0, data_len);
            if (ctl_maxlen >= 0)
                  put_user(0, ctl_len);
            return -EAGAIN;
      }
      SOLD("wants data");
      if (ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) {
            SOLD("udi fits");
            tmpbuf = ctl_buf + sizeof(udi);
            tmplen = ctl_maxlen - sizeof(udi);
      } else {
            SOLD("udi does not fit");
            tmpbuf = NULL;
            tmplen = 0;
      }
      if (put_user(tmplen, ctl_len))
            return -EFAULT;
      SOLD("set ctl_len");
      oldflags = filp->f_flags;
      filp->f_flags |= O_NONBLOCK;
      SOLD("calling recvfrom");
      sys_recvfrom = (int (*)(int, void __user *, size_t, unsigned, struct sockaddr __user *, int __user *))SYS(recvfrom);
      error = sys_recvfrom(fd, data_buf, data_maxlen, 0, (struct sockaddr __user *)tmpbuf, ctl_len);
      filp->f_flags = oldflags;
      if (error < 0)
            return error;
      SOLD("error >= 0" ) ;
      if (error && ctl_maxlen > sizeof(udi) && sock->state == TS_IDLE) {
            SOLD("generating udi");
            udi.PRIM_type = T_UNITDATA_IND;
            if (get_user(udi.SRC_length, ctl_len))
                  return -EFAULT;
            udi.SRC_offset = sizeof(udi);
            udi.OPT_length = udi.OPT_offset = 0;
            if (copy_to_user(ctl_buf, &udi, sizeof(udi)) ||
                put_user(sizeof(udi)+udi.SRC_length, ctl_len))
                  return -EFAULT;
            SOLD("udi done");
      } else {
            if (put_user(0, ctl_len))
                  return -EFAULT;
      }
      put_user(error, data_len);
      SOLD("done");
      return 0;
}

asmlinkage int solaris_getmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3)
{
      struct file *filp;
      struct inode *ino;
      struct strbuf __user *ctlptr;
      struct strbuf __user *datptr;
      struct strbuf ctl, dat;
      int __user *flgptr;
      int flags;
      int error = -EBADF;
      struct fdtable *fdt;

      SOLD("entry");
      lock_kernel();
      if(fd >= NR_OPEN) goto out;

      fdt = files_fdtable(current->files);
      filp = fdt->fd[fd];
      if(!filp) goto out;

      ino = filp->f_path.dentry->d_inode;
      if (!ino || !S_ISSOCK(ino->i_mode))
            goto out;

      ctlptr = (struct strbuf __user *)A(arg1);
      datptr = (struct strbuf __user *)A(arg2);
      flgptr = (int __user *)A(arg3);

      error = -EFAULT;

      if (ctlptr) {
            if (copy_from_user(&ctl,ctlptr,sizeof(struct strbuf)) || 
                put_user(-1,&ctlptr->len))
                  goto out;
      } else
            ctl.maxlen = -1;

      if (datptr) {
            if (copy_from_user(&dat,datptr,sizeof(struct strbuf)) || 
                put_user(-1,&datptr->len))
                  goto out;
      } else
            dat.maxlen = -1;

      if (get_user(flags,flgptr))
            goto out;

      switch (flags) {
      case 0:
      case MSG_HIPRI:
      case MSG_ANY:
      case MSG_BAND:
            break;
      default:
            error = -EINVAL;
            goto out;
      }

      error = timod_getmsg(fd,A(ctl.buf),ctl.maxlen,&ctlptr->len,
                        A(dat.buf),dat.maxlen,&datptr->len,&flags);

      if (!error && put_user(flags,flgptr))
            error = -EFAULT;
out:
      unlock_kernel();
      SOLD("done");
      return error;
}

asmlinkage int solaris_putmsg(unsigned int fd, u32 arg1, u32 arg2, u32 arg3)
{
      struct file *filp;
      struct inode *ino;
      struct strbuf __user *ctlptr;
      struct strbuf __user *datptr;
      struct strbuf ctl, dat;
      int flags = (int) arg3;
      int error = -EBADF;
      struct fdtable *fdt;

      SOLD("entry");
      lock_kernel();
      if(fd >= NR_OPEN) goto out;

      fdt = files_fdtable(current->files);
      filp = fdt->fd[fd];
      if(!filp) goto out;

      ino = filp->f_path.dentry->d_inode;
      if (!ino) goto out;

      if (!S_ISSOCK(ino->i_mode) &&
            (imajor(ino) != 30 || iminor(ino) != 1))
            goto out;

      ctlptr = A(arg1);
      datptr = A(arg2);

      error = -EFAULT;

      if (ctlptr) {
            if (copy_from_user(&ctl,ctlptr,sizeof(ctl)))
                  goto out;
            if (ctl.len < 0 && flags) {
                  error = -EINVAL;
                  goto out;
            }
      } else {
            ctl.len = 0;
            ctl.buf = 0;
      }

      if (datptr) {
            if (copy_from_user(&dat,datptr,sizeof(dat)))
                  goto out;
      } else {
            dat.len = 0;
            dat.buf = 0;
      }

      error = timod_putmsg(fd,A(ctl.buf),ctl.len,
                        A(dat.buf),dat.len,flags);
out:
      unlock_kernel();
      SOLD("done");
      return error;
}

Generated by  Doxygen 1.6.0   Back to index