示例#1
0
static ssize_t
interceptor_ipm_proc_entry_fop_read(struct file *file,
				    char __user *buf,
				    size_t len,
				    loff_t *pos)
{
  SshInterceptor interceptor = file->private_data;
  SshInterceptorIpmMsg msg = NULL;
  ssize_t msg_len;

  write_lock(&interceptor->ipm_proc_entry.lock);
  
  /* Allow only one read at a time. */
  if (interceptor->ipm_proc_entry.read_active)
    {
      write_unlock(&interceptor->ipm_proc_entry.lock);
      SSH_LINUX_PROCFS_DEBUG("interceptor_ipm_proc_entry_fop_read: -EBUSY\n");
      return -EBUSY;
    }

  interceptor->ipm_proc_entry.read_active = TRUE;
  write_unlock(&interceptor->ipm_proc_entry.lock);

  /* Continue from the partial message. */
  if (interceptor->ipm_proc_entry.send_msg != NULL)
    msg = interceptor->ipm_proc_entry.send_msg;
  
  
 retry:  
  if (msg == NULL)
    {
      /* Get the next message from send queue. */
      local_bh_disable();
      write_lock(&interceptor->ipm.lock);
      
      /* Take next message from send queue. */
      if (interceptor->ipm.send_queue != NULL)
	{
	  msg = interceptor->ipm.send_queue;
	  
	  interceptor->ipm.send_queue = msg->next;
	  if (msg->next)
	    msg->next->prev = NULL;
	  msg->next = NULL;
	  
	  if (msg == interceptor->ipm.send_queue_tail)
	    interceptor->ipm.send_queue_tail = msg->next;

          if (msg->reliable == 0)
            {
              SSH_ASSERT(interceptor->ipm.send_queue_num_unreliable > 0);
              interceptor->ipm.send_queue_num_unreliable--;
            }
	}
      
      write_unlock(&interceptor->ipm.lock);
      local_bh_enable();
    }
  
  if (msg == NULL)
    {
      /* Non-blocking mode, fail read. */
      if (file->f_flags & O_NONBLOCK)
	{
	  write_lock(&interceptor->ipm_proc_entry.lock);
	  interceptor->ipm_proc_entry.read_active = FALSE;
	  write_unlock(&interceptor->ipm_proc_entry.lock);
	  
	  return -EAGAIN;
	}
      
      /* Blocking mode, sleep until a message or a signal arrives. */
      interruptible_sleep_on(&interceptor->ipm_proc_entry.wait_queue);
      
      if (signal_pending(current))
	{
	  SSH_LINUX_PROCFS_DEBUG("interceptor_ipm_proc_entry_fop_read: "
				 "-ERESTARTSYS\n");

	  write_lock(&interceptor->ipm_proc_entry.lock);
	  interceptor->ipm_proc_entry.read_active = FALSE;
	  write_unlock(&interceptor->ipm_proc_entry.lock);
	  
	  return -ERESTARTSYS;
	}

      goto retry;
    }
  
  interceptor->ipm_proc_entry.send_msg = msg;

  /* Copy message to userspace. */
  msg_len = msg->len - msg->offset;
  if (len < msg_len)
    msg_len = len;
  
  if (copy_to_user(buf, msg->buf + msg->offset, msg_len))
    {
      SSH_LINUX_PROCFS_WARN("interceptor_ipm_proc_entry_fop_read: "
			    "copy_to_user failed, dropping message\n");

      interceptor->ipm_proc_entry.send_msg = NULL;
      write_lock(&interceptor->ipm_proc_entry.lock);
      interceptor->ipm_proc_entry.read_active = FALSE;
      write_unlock(&interceptor->ipm_proc_entry.lock);

      interceptor_ipm_message_free(interceptor, msg);
      return -EFAULT;
    }
  
  msg->offset += msg_len;
  
  /* Whole message was sent. */
  if (msg->offset >= msg->len)
    {
      interceptor->ipm_proc_entry.send_msg = NULL;
      write_lock(&interceptor->ipm_proc_entry.lock);
      interceptor->ipm_proc_entry.read_active = FALSE;
      write_unlock(&interceptor->ipm_proc_entry.lock);

      interceptor_ipm_message_free(interceptor, msg);
    }
  else
    {
      write_lock(&interceptor->ipm_proc_entry.lock);
      interceptor->ipm_proc_entry.read_active = FALSE;
      write_unlock(&interceptor->ipm_proc_entry.lock);
    }

  return msg_len;
}
示例#2
0
static ssize_t
interceptor_ipm_proc_entry_fop_write(struct file *file,
				     const char __user *buf,
				     size_t len,
				     loff_t *pos)
{
  SshInterceptor interceptor = file->private_data;
  size_t total_len, write_len, recv_len, consumed, msg_len;
  char *user_buf, *recv_buf;

  /* Limit the maximum write length to avoid running in softirq
     context for long periods of time. */
  if (len > SSH_LINUX_PROCFS_IPM_WRITE_MAX_LENGTH)
    write_len = SSH_LINUX_PROCFS_IPM_WRITE_MAX_LENGTH;
  else
    write_len = len;
  
  /* Refuse to receive any data if send queue is getting full.
     Note this here checks if the IPM message freelist is empty,
     which indicates that all IPM messages are in the send queue.
     
     Allowing a new write in such condition could cause a number
     of reply IPM messages to be queued for sending and this would
     cause either unreliable IPM messages to be discarded from the
     send queue or an emergency mallocation of a reliable IPM message.
     
     A better way to solve this problem is to refuse this write
     operation and force the application to read messages from the send
     queue before allowing another write. */
  local_bh_disable();
  read_lock(&interceptor->ipm.lock);
  if (interceptor->ipm.msg_freelist == NULL
      && interceptor->ipm.msg_allocated >= SSH_LINUX_MAX_IPM_MESSAGES)
    write_len = 0;
  read_unlock(&interceptor->ipm.lock);
  local_bh_enable();
  
  if (write_len == 0)
    return -EAGAIN;

  /* Check if there is another write going on. */
 retry:
  write_lock(&interceptor->ipm_proc_entry.lock);
  
  if (interceptor->ipm_proc_entry.write_active)
    {
      write_unlock(&interceptor->ipm_proc_entry.lock);

      /* Non-blocking mode, fail write. */
      if (file->f_flags & O_NONBLOCK)
	return -EAGAIN;

      /* Blocking mode, wait until other writes are done. */
      interruptible_sleep_on(&interceptor->ipm_proc_entry.wait_queue);
      
      if (signal_pending(current))
	{
	  SSH_LINUX_PROCFS_DEBUG("interceptor_ipm_proc_entry_fop_write: "
				 "-ERESTARTSYS\n");
	  return -ERESTARTSYS;
	}
      
      goto retry;
    }
  
  interceptor->ipm_proc_entry.write_active = TRUE;
  write_unlock(&interceptor->ipm_proc_entry.lock);

  /* Receive data. */
  total_len = 0;
  user_buf = (char *) buf;
  while (user_buf < (buf + write_len))
    {
      /* Copy data from user to receive buffer up to the maximum
         allowed write size. */
      user_buf = (char *) (buf + total_len);

      recv_buf = (interceptor->ipm_proc_entry.recv_buf 
		  + interceptor->ipm_proc_entry.recv_len);
      recv_len = (interceptor->ipm_proc_entry.recv_buf_size
		  - interceptor->ipm_proc_entry.recv_len);

      /* Break out of the loop if receive buffer is full. */
      if (recv_len == 0)
	break;

      if (recv_len > (write_len - total_len))
	recv_len = (write_len - total_len);
      
      if (copy_from_user(recv_buf, user_buf, recv_len))
	{
	  SSH_LINUX_PROCFS_WARN("interceptor_ipm_proc_entry_fop_write: "
				"copy_from_user failed, dropping message\n");
	  
	  write_lock(&interceptor->ipm_proc_entry.lock);
	  interceptor->ipm_proc_entry.write_active = FALSE;	  
	  write_unlock(&interceptor->ipm_proc_entry.lock);
	  
	  wake_up_interruptible(&interceptor->ipm_proc_entry.wait_queue);
	  return -EFAULT;
	}
  
      total_len += recv_len;
      interceptor->ipm_proc_entry.recv_len += recv_len;

      /* Parse ipm messages. */
      consumed = 0;
      while (consumed < interceptor->ipm_proc_entry.recv_len)
	{
	  msg_len = 
	    ssh_interceptor_receive_from_ipm(interceptor->ipm_proc_entry.
					     recv_buf + consumed,
					     interceptor->ipm_proc_entry.
					     recv_len - consumed);

	  /* Need more data. */
	  if (msg_len == 0)
	    break;

	  /* Else continue parsing ipm messages. */
	  consumed += msg_len;
	}

      /* Move unparsed data to beginning of receive buffer. */
      if (consumed > 0)
	{
	  SSH_ASSERT(consumed <= interceptor->ipm_proc_entry.recv_len);

	  if (consumed < interceptor->ipm_proc_entry.recv_len)
	    memmove(interceptor->ipm_proc_entry.recv_buf,
		    interceptor->ipm_proc_entry.recv_buf + consumed,
		    interceptor->ipm_proc_entry.recv_len - consumed);
	  
	  interceptor->ipm_proc_entry.recv_len -= consumed;
	}

      /* Continue receiving data from user. */
    }

  write_lock(&interceptor->ipm_proc_entry.lock);
  interceptor->ipm_proc_entry.write_active = FALSE;	  
  write_unlock(&interceptor->ipm_proc_entry.lock);

  if (total_len == 0)
    {
      SSH_LINUX_PROCFS_WARN("interceptor_ipm_proc_entry_fop_write: "
                            "Out of receive buffer space\n");
      return -ENOMEM;
    }
  
  SSH_ASSERT(total_len <= write_len);
  return total_len;
}
示例#3
0
static ssize_t
interceptor_ipm_proc_entry_fop_write(struct file *file,
				     const char __user *buf,
				     size_t len,
				     loff_t *pos)
{
  SshInterceptor interceptor = file->private_data;
  size_t total_len, recv_len, consumed, msg_len;
  char *user_buf, *recv_buf;

  /* Check if there is another write going on. */
 retry:
  write_lock(&interceptor->ipm_proc_entry.lock);
  
  if (interceptor->ipm_proc_entry.write_active)
    {
      write_unlock(&interceptor->ipm_proc_entry.lock);

      /* Non-blocking mode, fail write. */
      if (file->f_flags & O_NONBLOCK)
	return -EAGAIN;

      /* Blocking mode, wait until other writes are done. */
      interruptible_sleep_on(&interceptor->ipm_proc_entry.wait_queue);
      
      if (signal_pending(current))
	{
	  SSH_LINUX_PROCFS_DEBUG("interceptor_ipm_proc_entry_fop_write: "
				 "-ERESTARTSYS\n");
	  return -ERESTARTSYS;
	}
      
      goto retry;
    }
  
  interceptor->ipm_proc_entry.write_active = TRUE;
  write_unlock(&interceptor->ipm_proc_entry.lock);

  /* Receive data. */
  total_len = 0;
  user_buf = (char *) buf;
  while (user_buf < (buf + len))
    {
      /* Copy data from user to receive buffer. */
      user_buf = (char *) (buf + total_len);

      recv_buf = (interceptor->ipm_proc_entry.recv_buf 
		  + interceptor->ipm_proc_entry.recv_offset);      
      recv_len = (interceptor->ipm_proc_entry.recv_buf_size
		  - interceptor->ipm_proc_entry.recv_offset);

      /* Break out of the loop if receive buffer is full. */
      if (recv_len == 0)
	break;

      if (recv_len > (len - total_len))
	recv_len = (len - total_len);
      
      if (copy_from_user(recv_buf, user_buf, recv_len))
	{
	  SSH_LINUX_PROCFS_WARN("interceptor_ipm_proc_entry_fop_write: "
				"copy_from_user failed, dropping message\n");
	  
	  write_lock(&interceptor->ipm_proc_entry.lock);
	  interceptor->ipm_proc_entry.write_active = FALSE;	  
	  write_unlock(&interceptor->ipm_proc_entry.lock);
	  
	  wake_up_interruptible(&interceptor->ipm_proc_entry.wait_queue);
	  return -EFAULT;
	}
  
      total_len += recv_len;
      interceptor->ipm_proc_entry.recv_len += recv_len;

      /* Parse ipm messages. */
      consumed = 0;
      while (consumed < interceptor->ipm_proc_entry.recv_len)
	{
	  msg_len = 
	    ssh_interceptor_receive_from_ipm(interceptor->ipm_proc_entry.
					     recv_buf + consumed,
					     interceptor->ipm_proc_entry.
					     recv_len - consumed);

	  consumed += msg_len;

	  /* Need more data. */
	  if (msg_len == 0)
	    break;

	  /* Else continue parsing ipm messages. */
	}

      /* Move unparsed data to beginning of receive buffer. */
      if (consumed > 0)
	{
	  SSH_ASSERT(consumed <= interceptor->ipm_proc_entry.recv_len);

	  if (consumed < interceptor->ipm_proc_entry.recv_len)
	    memmove(interceptor->ipm_proc_entry.recv_buf,
		    interceptor->ipm_proc_entry.recv_buf + consumed,
		    interceptor->ipm_proc_entry.recv_len - consumed);
	  
	  interceptor->ipm_proc_entry.recv_offset = 
	    interceptor->ipm_proc_entry.recv_len - consumed;
	  interceptor->ipm_proc_entry.recv_len -= consumed;
	}

      /* Continue receiving data from user. */
    }

  write_lock(&interceptor->ipm_proc_entry.lock);
  interceptor->ipm_proc_entry.write_active = FALSE;	  
  write_unlock(&interceptor->ipm_proc_entry.lock);

  if (total_len == 0)
    {
      SSH_LINUX_PROCFS_WARN("interceptor_ipm_proc_entry_fop_write: "
                            "Out of receive buffer space\n");
      return -ENOMEM;
    }
  
  return total_len;
}