View of xos/fs/pipe.c


XOS | Parent Directory | View | Download

/* Copyright (C) 2008  Emmanuel Varoquaux
 
   This file is part of XOS.
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
#include "file_struct.h"
#include "file_table.h"
 
#include <stat_struct.h>
#include <kmalloc.h>
#include <current.h>
#include <verify_area.h>
#include <time.h>
#include <condition.h>
#include <segment.h>
#include <errno.h>
#include <consts.h>
#include <enums.h>
#include <config.h>
 
/* Operations */
 
static int pipe_read(struct file_struct *pipe, unsigned long pos, char *fs_buf, unsigned int count)
{
  int n;
 
  if (!count)
    return 0;
  if (!verify_area(fs_buf, count, PF_WRITE))
    return -EFAULT;
 
  pos &= PIPE_BUFFER_SIZE - 1;
  n = 0;
  while (!pipe->size) {
    if (!pipe->write_count)
      return 0;
    if (!condition_wait_interruptible(&pipe->condition))
      return -ERESTARTSYS;
  }
  do {
    put_fs_byte(pipe->buffer[pos], fs_buf++);
    pos = (pos + 1) & (PIPE_BUFFER_SIZE - 1);
    pipe->size--;
    n++;
    count--;
  }
  while (count && pipe->size);
  condition_signal_all(&pipe->condition);
  return n;
}
 
static int pipe_write(struct file_struct *pipe, unsigned long pos, const char *fs_buf, unsigned int count)
{
  int n;
 
  if (!count)
    return 0;
  if (!verify_area(fs_buf, count, PF_READ))
    return -EFAULT;
 
  pos &= PIPE_BUFFER_SIZE - 1;
  n = 0;
  if (!pipe->read_count) {
    signal_self(SIGPIPE);
    return -EPIPE;
  }
  do {
    while (pipe->size == PIPE_BUFFER_SIZE) {
      if (!pipe->read_count) {
        signal_self(SIGPIPE);
        return n ? : -EPIPE;
      }
      if (!condition_wait_interruptible(&pipe->condition))
        if (!n)
          return -ERESTARTSYS;
    }
    do {
      pipe->buffer[pos] = get_fs_byte(fs_buf++);
      pos = (pos + 1) & (PIPE_BUFFER_SIZE - 1);
      pipe->size++;
      n++;
      count--;
    }
    while (count && pipe->size < PIPE_BUFFER_SIZE);
    pipe->mtime = get_time();
    condition_signal_all(&pipe->condition);
  }
  while (count);
  return n;
}
 
static long pipe_seek(const struct file_struct *pipe, unsigned long pos, long offset, int whence)
{
  return -ESPIPE;
}
 
static int pipe_stat(const struct file_struct *pipe, struct stat_struct *fs_buf)
{
  struct stat_struct statbuf;
 
  if (!verify_area(fs_buf, sizeof (struct stat_struct), PF_WRITE))
    return -EFAULT;
 
  statbuf.dev = DEV_NONE;
  statbuf.id = 0;
  statbuf.type = FT_PIPE;
  statbuf.rdev = DEV_NONE;
  statbuf.size = 0;
  statbuf.mtime = pipe->mtime;
  memcpy_tofs(fs_buf, &statbuf, sizeof (struct stat_struct));
  return 0;
}
 
static int pipe_ioctl(const struct file_struct *pipe, int request, void *fs_arg)
{
  return -ENOTTY;
}
 
static const struct file_operations_struct pipe_operations = {
  .read = pipe_read,
  .write = pipe_write,
  .seek = pipe_seek,
  .stat = pipe_stat,
  .ioctl = pipe_ioctl
};
 
/* Initialisation / destruction */
 
static int init_pipe(struct file_struct *pipe)
{
  pipe->file_operations = &pipe_operations;
  pipe->read_count = 0;
  pipe->write_count = 0;
  if (!(pipe->buffer = kmalloc(PIPE_BUFFER_SIZE)))
    return -ENOMEM;
  pipe->size = 0;
  pipe->mtime = get_time();
  condition_init(&pipe->condition);
  return 0;
}
 
static void destroy_pipe(struct file_struct *pipe)
{
  kfree(pipe->buffer, PIPE_BUFFER_SIZE);
}
 
static void free_pipe(struct file_struct *pipe)
{
  destroy_pipe(pipe);
  free_file(pipe);
}
 
/* Fonctions exportees */
 
/* Alloue un tube. */
int alloc_pipe(struct file_struct **pipe)
{
  int retval;
 
  if (!(*pipe = alloc_file())) {
    retval = -ENOMEM;
    goto error;
  }
 
  if ((retval = init_pipe(*pipe)) < 0)
    goto error_free_pipe;
  return 0;
 
 error_free_pipe:
  free_file(*pipe);
 error:
  return retval;
}
 
/* Incremente le nombre de references en lecture sur le tube. */
void pipe_hold_read(struct file_struct *pipe)
{
  pipe->read_count++;
}
 
/* Incremente le nombre de references en ecriture sur le tube. */
void pipe_hold_write(struct file_struct *pipe)
{
  pipe->write_count++;
}
 
/* Decremente le nombre de references en lecture sur le tube.
 * Le fichier est libere s'il n'est plus reference. */
void pipe_release_read(struct file_struct *pipe)
{
  pipe->read_count--;
 
  if (!pipe->read_count && !pipe->write_count) /* liberable ? */
    free_pipe(pipe);
  else
    condition_signal_all(&pipe->condition);
}
 
/* Decremente le nombre de references en ecriture sur le tube.
 * Le fichier est libere s'il n'est plus reference. */
void pipe_release_write(struct file_struct *pipe)
{
  pipe->write_count--;
 
  if (!pipe->read_count && !pipe->write_count) /* liberable ? */
    free_pipe(pipe);
  else
    condition_signal_all(&pipe->condition);
}