Goto sanos source index

//
// pipefs.c
//
// Pipe Filesystem
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os/krnl.h>

int pipefs_mount(struct fs *fs, char *opts);
int pipefs_close(struct file *filp);
int pipefs_fsync(struct file *filp);
int pipefs_read(struct file *filp, void *data, size_t size, off64_t pos);
int pipefs_write(struct file *filp, void *data, size_t size, off64_t pos);
int pipefs_ioctl(struct file *filp, int cmd, void *data, size_t size);
off64_t pipefs_lseek(struct file *filp, off64_t offset, int origin);
int pipefs_fstat(struct file *filp, struct stat64 *buffer);

struct fsops pipefsops = {
  FSOP_READ | FSOP_WRITE,

  NULL,
  NULL,

  NULL,
  pipefs_mount,
  NULL,

  NULL,

  NULL,
  pipefs_close,
  NULL,
  pipefs_fsync,

  pipefs_read,
  pipefs_write,
  pipefs_ioctl,

  NULL,
  pipefs_lseek,
  NULL,

  NULL,
  NULL,

  pipefs_fstat,
  NULL,

  NULL,

  NULL,
  NULL,

  NULL,
  NULL,

  NULL,
  NULL,
  NULL,

  NULL,
  NULL
};

struct pipereq {
  struct pipereq *next;
  struct pipe *pipe;
  struct thread *thread;
  char *buffer;
  size_t size;
  int rc;
};

struct pipe {
  struct file *filp;
  struct pipe *peer;
  struct pipereq *waithead;
  struct pipereq *waittail;
};

struct fs *pipefs;

static void release_all_waiters(struct pipe *pipe, int retcode) {
  struct pipereq *req = pipe->waithead;
  struct pipereq *next;

  while (req) {
    next = req->next;
    req->rc = retcode;
    mark_thread_ready(req->thread, 1, 2);
    req = next;
  }

  pipe->waithead = pipe->waittail = NULL;
}

static void cancel_request(struct pipe *pipe, struct pipereq *req) {
  if (pipe->waithead == req) {
    pipe->waithead = req->next;
    if (pipe->waittail == req) pipe->waittail = NULL;
  } else if (pipe->waithead) {
    struct pipereq *r = pipe->waithead;
    while (r->next) {
      if (r->next == req) {
        r->next = req->next;
        if (pipe->waittail == req) pipe->waittail = r;
        return;
      }
      r = r->next;
    }
  }
}

void init_pipefs() {
  register_filesystem("pipefs", &pipefsops);
  mount("pipefs", "", "", NULL, &pipefs);
}

int pipe(struct file **readpipe, struct file **writepipe) {
  struct file *rd;
  struct file *wr;
  struct pipe *rdp;
  struct pipe *wrp;

  if (!pipefs) return -ENOENT;

  rd = newfile(pipefs, NULL, O_RDONLY, S_IREAD);
  wr = newfile(pipefs, NULL, O_WRONLY, S_IWRITE);
  rdp = kmalloc(sizeof(struct pipe));
  wrp = kmalloc(sizeof(struct pipe));
  if (!rd || !wr || !rdp || !wrp) {
    kfree(rd);
    kfree(wr);
    kfree(rdp);
    kfree(wrp);
    return -EMFILE;
  }

  rd->data = rdp;
  rdp->filp = rd;
  rdp->peer = wrp;
  rdp->waithead = rdp->waittail = NULL;

  wr->data = wrp;
  wrp->filp = wr;
  wrp->peer = rdp;
  wrp->waithead = wrp->waittail = NULL;

  *readpipe = rd;
  *writepipe = wr;

  return 0;
}

int pipefs_mount(struct fs *fs, char *opts) {
  // Only allow one instance of pipefs
  if (pipefs) return -EPERM;
  return 0;
}

int pipefs_close(struct file *filp) {
  struct pipe *pipe = (struct pipe *) filp->data;

  set_io_event(&filp->iob, IOEVT_CLOSE);
  release_all_waiters(pipe, -EINTR);

  if (pipe->peer) {
    if (filp->flags & O_WRONLY) {
      set_io_event(&pipe->peer->filp->iob, IOEVT_READ);
      release_all_waiters(pipe->peer, 0);
    } else {
      set_io_event(&pipe->peer->filp->iob, IOEVT_WRITE);
      release_all_waiters(pipe->peer, -EPIPE);
    }
    pipe->peer->peer = NULL;
    pipe->peer = NULL;
  }

  kfree(pipe);

  return 0;
}

int pipefs_fsync(struct file *filp) {
  return 0;
}

int pipefs_read(struct file *filp, void *data, size_t size, off64_t pos) {
  struct pipe *pipe = (struct pipe *) filp->data;
  char *p;
  size_t left;
  size_t count;

  if (size == 0) return 0;
  if (!pipe->peer) return 0;

  p = (char *) data;
  left = size;
  count = 0;
  while (pipe->peer->waithead && left > 0) {
    struct pipereq *req = pipe->peer->waithead;
    size_t bytes = req->size;
    
    if (bytes > left) bytes = left;

    memcpy(p, req->buffer, bytes);
    req->buffer += bytes;
    req->size -= bytes;

    p += bytes;
    left -= bytes;
    count += bytes;

    if (req->size == 0) {
      pipe->peer->waithead = req->next;
      if (pipe->peer->waithead == NULL) pipe->peer->waittail = NULL;
      req->rc = 0;
      mark_thread_ready(req->thread, 1, 2);
    }
  }

  if (pipe->peer->waithead == NULL) clear_io_event(&filp->iob, IOEVT_READ);

  if (count == 0) {
    struct pipereq req;
    int rc;

    set_io_event(&pipe->peer->filp->iob, IOEVT_WRITE);
    if (filp->flags & O_NONBLOCK) return -EAGAIN;

    req.pipe = pipe;
    req.thread = self();
    req.buffer = p;
    req.size = left;
    req.rc = -EINTR;
    req.next = NULL;

    if (pipe->waittail) { 
      pipe->waittail->next = &req;
    } else {
      pipe->waittail = &req;
    }

    if (!pipe->waithead) pipe->waithead = &req;

    rc = enter_alertable_wait(THREAD_WAIT_PIPE);
    if (rc < 0) {
      cancel_request(pipe, &req);
      req.rc = rc;
    }

    if (req.rc < 0) return req.rc;
    count += req.rc;
  }

  return count;
}

int pipefs_write(struct file *filp, void *data, size_t size, off64_t pos) {
  struct pipe *pipe = (struct pipe *) filp->data;
  char *p;
  size_t left;

  if (size == 0) return 0;
  if (!pipe->peer) return -EPIPE;

  p = (char *) data;
  left = size;
  while (pipe->peer->waithead && left > 0) {
    struct pipereq *req = pipe->peer->waithead;
    size_t bytes = req->size;
    
    if (bytes > left) bytes = left;

    memcpy(req->buffer, p, bytes);
    req->rc = bytes;

    p += bytes;
    left -= bytes;

    pipe->peer->waithead = req->next;
    if (pipe->peer->waithead == NULL) pipe->peer->waittail = NULL;
    mark_thread_ready(req->thread, 1, 2);
  }

  if (pipe->peer->waithead == NULL) clear_io_event(&filp->iob, IOEVT_WRITE);

  if (left > 0) {
    struct pipereq req;
    int rc;

    set_io_event(&pipe->peer->filp->iob, IOEVT_READ);
    if (filp->flags & O_NONBLOCK) return size - left;

    req.pipe = pipe;
    req.thread = self();
    req.buffer = p;
    req.size = left;
    req.rc = -EINTR;
    req.next = NULL;

    if (pipe->waittail) { 
      pipe->waittail->next = &req;
    } else {
      pipe->waittail = &req;
    }

    if (!pipe->waithead) pipe->waithead = &req;

    rc = enter_alertable_wait(THREAD_WAIT_PIPE);
    if (rc < 0) {
      cancel_request(pipe, &req);
      req.rc = rc;
    }

    if (req.rc < 0) return req.rc;
  }

  return size;
}

int pipefs_ioctl(struct file *filp, int cmd, void *data, size_t size) {
  if (cmd == FIONBIO) return 0;
  return -ENOSYS;
}

off64_t pipefs_lseek(struct file *filp, off64_t offset, int origin) {
  return -ESPIPE;
}

int pipefs_fstat(struct file *filp, struct stat64 *buffer) {
  // TODO: implement timestamps on create, read and write
  return -ENOSYS;
}