# Makefile for Pipe File System (PFS)
PROG= pfs
-SRCS= open.c table.c inode.c main.c super.c link.c \
- buffer.c read.c misc.c mount.c stadir.c
+SRCS= pfs.c
DPADD+= ${LIBFSDRIVER} ${LIBSYS}
LDADD+= -lfsdriver -lsys
+++ /dev/null
-#ifndef __PFS_BUF_H__
-#define __PFS_BUF_H__
-
-/* Buffer (block) cache.
- */
-
-struct buf {
- /* Data portion of the buffer. */
- char b_data[PIPE_BUF]; /* ordinary user data */
-
- /* Header portion of the buffer. */
- struct buf *b_next; /* used to link all free bufs in a chain */
- struct buf *b_prev; /* used to link all free bufs the other way */
- ino_t b_num; /* inode number on minor device */
- dev_t b_dev; /* major | minor device where block resides */
- int b_bytes; /* Number of bytes allocated in bp */
- int b_count; /* Number of users of this buffer */
-};
-
-/* A block is free if b_dev == NO_DEV. */
-
-
-EXTERN struct buf *front; /* points to least recently used free block */
-EXTERN struct buf *rear; /* points to most recently used free block */
-
-#endif
+++ /dev/null
-#include "fs.h"
-
-static struct buf *new_block(dev_t dev, ino_t inum);
-
-/*===========================================================================*
- * buf_pool *
- *===========================================================================*/
-void buf_pool(void)
-{
-/* Initialize the buffer pool. */
-
- front = NULL;
- rear = NULL;
-}
-
-
-
-/*===========================================================================*
- * get_block *
- *===========================================================================*/
-struct buf *get_block(dev_t dev, ino_t inum)
-{
- struct buf *bp = front;
-
- while(bp != NULL) {
- if (bp->b_dev == dev && bp->b_num == inum) {
- bp->b_count++;
- return(bp);
- }
- bp = bp->b_next;
- }
-
- /* Buffer was not found. Try to allocate a new one */
- return new_block(dev, inum);
-}
-
-
-/*===========================================================================*
- * new_block *
- *===========================================================================*/
-static struct buf *new_block(dev_t dev, ino_t inum)
-{
-/* Allocate a new buffer and add it to the double linked buffer list */
- struct buf *bp;
-
- bp = malloc(sizeof(struct buf));
- if (bp == NULL) {
- err_code = ENOSPC;
- return(NULL);
- }
- bp->b_num = inum;
- bp->b_dev = dev;
- bp->b_bytes = 0;
- bp->b_count = 1;
- memset(bp->b_data, 0 , PIPE_BUF);
-
- /* Add at the end of the buffer */
- if (front == NULL) { /* Empty list? */
- front = bp;
- bp->b_prev = NULL;
- } else {
- rear->b_next = bp;
- bp->b_prev = rear;
- }
- bp->b_next = NULL;
- rear = bp;
-
- return(bp);
-}
-
-
-/*===========================================================================*
- * put_block *
- *===========================================================================*/
-void put_block(dev_t dev, ino_t inum)
-{
- struct buf *bp;
-
- bp = get_block(dev, inum);
- if (bp == NULL) return; /* We didn't find the block. Nothing to put. */
-
- bp->b_count--; /* Compensate for above 'get_block'. */
- if (--bp->b_count > 0) return;
-
- /* Cut bp out of the loop */
- if (bp->b_prev == NULL)
- front = bp->b_next;
- else
- bp->b_prev->b_next = bp->b_next;
-
- if (bp->b_next == NULL)
- rear = bp->b_prev;
- else
- bp->b_next->b_prev = bp->b_prev;
-
- /* Buffer administration is done. Now it's safe to free up bp. */
- free(bp);
-}
+++ /dev/null
-#ifndef __PFS_CONST_H__
-#define __PFS_CONST_H__
-
-#define PFS_NR_INODES 512 /* # slots in "in core" inode table */
-
-#define INODE_HASH_LOG2 7 /* 2 based logarithm of the inode hash size */
-#define INODE_HASH_SIZE ((unsigned long)1<<INODE_HASH_LOG2)
-#define INODE_HASH_MASK (((unsigned long)1<<INODE_HASH_LOG2)-1)
-
-#define NO_BIT ((bit_t) 0) /* returned by alloc_bit() to signal failure */
-
-#define ATIME 002 /* set if atime field needs updating */
-#define CTIME 004 /* set if ctime field needs updating */
-#define MTIME 010 /* set if mtime field needs updating */
-
-#define FS_BITMAP_CHUNKS(b) ((b)/sizeof (bitchunk_t))/* # map chunks/blk */
-#define FS_BITCHUNK_BITS (sizeof(bitchunk_t) * CHAR_BIT)
-#define FS_BITS_PER_BLOCK(b) (FS_BITMAP_CHUNKS(b) * FS_BITCHUNK_BITS)
-
-#define FS_CALL_VEC_SIZE 31
-
-#endif
+++ /dev/null
-#ifndef __PFS_FS_H__
-#define __PFS_FS_H__
-
-/* This is the master header for fs. It includes some other files
- * and defines the principal constants.
- */
-#define _SYSTEM 1 /* tell headers that this is the kernel */
-
-/* The following are so basic, all the *.c files get them automatically. */
-#include <minix/config.h> /* MUST be first */
-#include <sys/types.h>
-#include <minix/const.h>
-#include <minix/type.h>
-
-#include <lib.h>
-#include <minix/syslib.h>
-#include <minix/sysutil.h>
-
-#include <minix/fsdriver.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <assert.h>
-
-#include "const.h"
-#include "proto.h"
-#include "glo.h"
-#include "buf.h"
-#include "inode.h"
-
-#endif
+++ /dev/null
-#ifndef __PFS_GLO_H__
-#define __PFS_GLO_H__
-
-/* EXTERN should be extern except for the table file */
-#ifdef _TABLE
-#undef EXTERN
-#define EXTERN
-#endif
-
-/* The following variables are used for returning results to the caller. */
-EXTERN int err_code; /* temporary storage for error number */
-
-extern struct fsdriver pfs_table;
-
-EXTERN int busy;
-
-/* Inode map. */
-EXTERN bitchunk_t inodemap[FS_BITMAP_CHUNKS(PFS_NR_INODES)];
-
-#endif
+++ /dev/null
-/* This file manages the inode table. There are procedures to allocate and
- * deallocate inodes, acquire, erase, and release them, and read and write
- * them from the disk.
- *
- * The entry points into this file are
- * get_inode: search inode table for a given inode; if not there,
- * read it
- * put_inode: indicate that an inode is no longer needed in memory
- * alloc_inode: allocate a new, unused inode
- * wipe_inode: erase some fields of a newly allocated inode
- * free_inode: mark an inode as available for a new file
- * update_times: update atime, ctime, and mtime
- * find_inode: retrieve pointer to inode in inode cache
- *
- */
-
-#include "fs.h"
-
-static void addhash_inode(struct inode * const node);
-static void unhash_inode(struct inode * const node);
-
-
-/*===========================================================================*
- * fs_putnode *
- *===========================================================================*/
-int fs_putnode(ino_t ino_nr, unsigned int count)
-{
-/* Find the inode specified by the request message and decrease its counter.*/
- struct inode *rip;
- dev_t dev;
-
- rip = find_inode(ino_nr);
-
- if(!rip) {
- printf("%s:%d put_inode: inode #%llu not found\n", __FILE__,
- __LINE__, ino_nr);
- panic("fs_putnode failed");
- }
-
- if (count > rip->i_count) {
- printf("%s:%d put_inode: count too high: %d > %d\n", __FILE__,
- __LINE__, count, rip->i_count);
- panic("fs_putnode failed");
- }
-
- /* Decrease reference counter, but keep one reference; it will be consumed by
- * put_inode(). */
- rip->i_count -= count - 1;
- dev = rip->i_dev;
- put_inode(rip);
- if (rip->i_count == 0) put_block(dev, ino_nr);
- return(OK);
-}
-
-
-/*===========================================================================*
- * init_inode_cache *
- *===========================================================================*/
-void init_inode_cache()
-{
- struct inode *rip;
- struct inodelist *rlp;
-
- /* init free/unused list */
- TAILQ_INIT(&unused_inodes);
-
- /* init hash lists */
- for (rlp = &hash_inodes[0]; rlp < &hash_inodes[INODE_HASH_SIZE]; ++rlp)
- LIST_INIT(rlp);
-
- /* add free inodes to unused/free list */
- for (rip = &inode[0]; rip < &inode[PFS_NR_INODES]; ++rip) {
- rip->i_num = NO_ENTRY;
- TAILQ_INSERT_HEAD(&unused_inodes, rip, i_unused);
- }
-
- /* Reserve the first inode (bit 0) to prevent it from being allocated later*/
- if (alloc_bit() != NO_BIT) printf("PFS could not reserve NO_BIT\n");
- busy = 0; /* This bit does not make the server 'in use/busy'. */
-}
-
-
-/*===========================================================================*
- * addhash_inode *
- *===========================================================================*/
-static void addhash_inode(struct inode * const node)
-{
- int hashi = (int) (node->i_num & INODE_HASH_MASK);
-
- /* insert into hash table */
- LIST_INSERT_HEAD(&hash_inodes[hashi], node, i_hash);
-}
-
-
-/*===========================================================================*
- * unhash_inode *
- *===========================================================================*/
-static void unhash_inode(struct inode * const node)
-{
- /* remove from hash table */
- LIST_REMOVE(node, i_hash);
-}
-
-
-/*===========================================================================*
- * get_inode *
- *===========================================================================*/
-struct inode *get_inode(
- dev_t dev, /* device on which inode resides */
- ino_t numb /* inode number */
-)
-{
-/* Find the inode in the hash table. If it is not there, get a free inode
- * load it from the disk if it's necessary and put on the hash list
- */
- register struct inode *rip;
- int hashi;
-
- hashi = (int) (numb & INODE_HASH_MASK);
-
- /* Search inode in the hash table */
- LIST_FOREACH(rip, &hash_inodes[hashi], i_hash) {
- if (rip->i_num == numb && rip->i_dev == dev) {
- /* If unused, remove it from the unused/free list */
- if (rip->i_count == 0) {
- TAILQ_REMOVE(&unused_inodes, rip, i_unused);
- }
- ++rip->i_count;
-
- return(rip);
- }
- }
-
- /* Inode is not on the hash, get a free one */
- if (TAILQ_EMPTY(&unused_inodes)) {
- err_code = ENFILE;
- return(NULL);
- }
- rip = TAILQ_FIRST(&unused_inodes);
-
- /* If not free unhash it */
- if (rip->i_num != NO_ENTRY) unhash_inode(rip);
-
- /* Inode is not unused any more */
- TAILQ_REMOVE(&unused_inodes, rip, i_unused);
-
- /* Load the inode. */
- rip->i_dev = dev;
- rip->i_num = numb;
- rip->i_count = 1;
- rip->i_update = 0; /* all the times are initially up-to-date */
-
- /* Add to hash */
- addhash_inode(rip);
-
-
- return(rip);
-}
-
-
-/*===========================================================================*
- * find_inode *
- *===========================================================================*/
-struct inode *find_inode(ino_t numb /* inode number */)
-{
-/* Find the inode specified by the inode and device number.
- */
- struct inode *rip;
- int hashi;
-
- hashi = (int) (numb & INODE_HASH_MASK);
-
- /* Search inode in the hash table */
- LIST_FOREACH(rip, &hash_inodes[hashi], i_hash) {
- if (rip->i_count > 0 && rip->i_num == numb) {
- return(rip);
- }
- }
-
- return(NULL);
-}
-
-
-/*===========================================================================*
- * put_inode *
- *===========================================================================*/
-void put_inode(rip)
-struct inode *rip; /* pointer to inode to be released */
-{
-/* The caller is no longer using this inode. If no one else is using it either
- * write it back to the disk immediately. If it has no links, truncate it and
- * return it to the pool of available inodes.
- */
-
- if (rip == NULL) return; /* checking here is easier than in caller */
-
- if (rip->i_count < 1)
- panic("put_inode: i_count already below 1: %d", rip->i_count);
-
- if (--rip->i_count == 0) { /* i_count == 0 means no one is using it now */
- if (rip->i_nlinks == NO_LINK) { /* Are there links to this file? */
- /* no links, free the inode. */
- truncate_inode(rip, 0); /* return all the disk blocks */
- rip->i_mode = I_NOT_ALLOC; /* clear I_TYPE field */
- free_inode(rip);
- } else {
- truncate_inode(rip, (off_t) 0);
- }
-
- if (rip->i_nlinks == NO_LINK) {
- /* free, put at the front of the LRU list */
- unhash_inode(rip);
- rip->i_num = NO_ENTRY;
- rip->i_dev = NO_DEV;
- rip->i_rdev = NO_DEV;
- TAILQ_INSERT_HEAD(&unused_inodes, rip, i_unused);
- } else {
- /* unused, put at the back of the LRU (cache it) */
- TAILQ_INSERT_TAIL(&unused_inodes, rip, i_unused);
- }
- }
-}
-
-
-/*===========================================================================*
- * alloc_inode *
- *===========================================================================*/
-struct inode *alloc_inode(dev_t dev, mode_t bits, uid_t uid, gid_t gid)
-{
-/* Allocate a free inode on 'dev', and return a pointer to it. */
-
- register struct inode *rip;
- bit_t b;
- ino_t i_num;
- int print_oos_msg = 1;
-
- b = alloc_bit();
- if (b == NO_BIT) {
- err_code = ENOSPC;
- if (print_oos_msg)
- printf("PipeFS is out of inodes\n");
- print_oos_msg = 0; /* Don't repeat message */
- return(NULL);
- }
- i_num = (ino_t) b;
- print_oos_msg = 1;
-
-
- /* Try to acquire a slot in the inode table. */
- if ((rip = get_inode(dev, i_num)) == NULL) {
- /* No inode table slots available. Free the inode if just allocated.*/
- if (dev == NO_DEV) free_bit(b);
- } else {
- /* An inode slot is available. */
-
- rip->i_mode = bits; /* set up RWX bits */
- rip->i_nlinks = NO_LINK; /* initial no links */
- rip->i_uid = uid; /* set file user id */
- rip->i_gid = gid; /* ditto group id */
-
- /* Fields not cleared already are cleared in wipe_inode(). They have
- * been put there because truncate() needs to clear the same fields if
- * the file happens to be open while being truncated. It saves space
- * not to repeat the code twice.
- */
- wipe_inode(rip);
- }
-
- return(rip);
-}
-
-
-/*===========================================================================*
- * wipe_inode *
- *===========================================================================*/
-void wipe_inode(rip)
-struct inode *rip; /* the inode to be erased */
-{
-/* Erase some fields in the inode. This function is called from alloc_inode()
- * when a new inode is to be allocated, and from truncate(), when an existing
- * inode is to be truncated.
- */
-
- rip->i_size = 0;
- rip->i_update = ATIME | CTIME | MTIME; /* update all times later */
-}
-
-
-/*===========================================================================*
- * free_inode *
- *===========================================================================*/
-void free_inode(rip)
-struct inode *rip;
-{
-/* Return an inode to the pool of unallocated inodes. */
-
- bit_t b;
-
- if (rip->i_num <= 0 || rip->i_num >= PFS_NR_INODES) return;
- b = (bit_t) rip->i_num;
- free_bit(b);
-}
-
-
-/*===========================================================================*
- * update_times *
- *===========================================================================*/
-void update_times(rip)
-struct inode *rip; /* pointer to inode to be read/written */
-{
-/* Various system calls are required by the standard to update atime, ctime,
- * or mtime. Since updating a time requires sending a message to the clock
- * task--an expensive business--the times are marked for update by setting
- * bits in i_update. When a stat, fstat, or sync is done, or an inode is
- * released, update_times() may be called to actually fill in the times.
- */
-
- time_t cur_time;
-
- cur_time = clock_time(NULL);
- if (rip->i_update & ATIME) rip->i_atime = cur_time;
- if (rip->i_update & CTIME) rip->i_ctime = cur_time;
- if (rip->i_update & MTIME) rip->i_mtime = cur_time;
- rip->i_update = 0; /* they are all up-to-date now */
-}
+++ /dev/null
-#ifndef __PFS_INODE_H__
-#define __PFS_INODE_H__
-
-/* Inode table. This table holds inodes that are currently in use.
- */
-
-#include <sys/queue.h>
-
-EXTERN struct inode {
- mode_t i_mode; /* file type, protection, etc. */
- nlink_t i_nlinks; /* how many links to this file */
- uid_t i_uid; /* user id of the file's owner */
- gid_t i_gid; /* group number */
- size_t i_size; /* current file size in bytes */
- time_t i_atime; /* time of last access (V2 only) */
- time_t i_mtime; /* when was file data last changed */
- time_t i_ctime; /* when was inode itself changed (V2 only)*/
-
- dev_t i_dev; /* which device is the inode on */
- dev_t i_rdev; /* which special device is the inode on */
- ino_t i_num; /* inode number on its (minor) device */
- int i_count; /* # times inode used; 0 means slot is free */
- char i_update; /* the ATIME, CTIME, and MTIME bits are here */
-
- LIST_ENTRY(inode) i_hash; /* hash list */
- TAILQ_ENTRY(inode) i_unused; /* free and unused list */
-
-
-} inode[PFS_NR_INODES];
-
-/* list of unused/free inodes */
-EXTERN TAILQ_HEAD(unused_inodes_t, inode) unused_inodes;
-
-/* inode hashtable */
-EXTERN LIST_HEAD(inodelist, inode) hash_inodes[INODE_HASH_SIZE];
-
-#endif
+++ /dev/null
-#include "fs.h"
-
-/*===========================================================================*
- * fs_trunc *
- *===========================================================================*/
-int fs_trunc(ino_t ino_nr, off_t start, off_t end)
-{
- struct inode *rip;
-
- if( (rip = find_inode(ino_nr)) == NULL) return(EINVAL);
-
- if (end != 0) return(EINVAL); /* creating holes is not supported */
-
- return truncate_inode(rip, start);
-}
-
-
-/*===========================================================================*
- * truncate_inode *
- *===========================================================================*/
-int truncate_inode(rip, newsize)
-register struct inode *rip; /* pointer to inode to be truncated */
-off_t newsize; /* inode must become this size */
-{
-/* Set inode to a certain size, freeing any zones no longer referenced
- * and updating the size in the inode. If the inode is extended, the
- * extra space is a hole that reads as zeroes.
- *
- * Nothing special has to happen to file pointers if inode is opened in
- * O_APPEND mode, as this is different per fd and is checked when
- * writing is done.
- */
-
- /* Pipes can shrink, so adjust size to make sure all zones are removed. */
- if(newsize != 0) return(EINVAL); /* Only truncate pipes to 0. */
- rip->i_size = newsize;
-
- /* Next correct the inode size. */
- wipe_inode(rip); /* Pipes can only be truncated to 0. */
-
- return(OK);
-}
+++ /dev/null
-#include "fs.h"
-
-/* SEF functions and variables. */
-static void sef_local_startup(void);
-static int sef_cb_init_fresh(int type, sef_init_info_t *info);
-static void sef_cb_signal_handler(int signo);
-
-/*===========================================================================*
- * main *
- *===========================================================================*/
-int main(int argc, char *argv[])
-{
-/* This is the main routine of this service. */
-
- /* SEF local startup. */
- env_setargs(argc, argv);
- sef_local_startup();
-
- /* The fsdriver library does the actual work here. */
- fsdriver_task(&pfs_table);
-
- return(OK);
-}
-
-/*===========================================================================*
- * sef_local_startup *
- *===========================================================================*/
-static void sef_local_startup()
-{
- /* Register init callbacks. */
- sef_setcb_init_fresh(sef_cb_init_fresh);
- sef_setcb_init_restart(sef_cb_init_fail);
-
- /* No live update support for now. */
-
- /* Register signal callbacks. */
- sef_setcb_signal_handler(sef_cb_signal_handler);
-
- /* Let SEF perform startup. */
- sef_startup();
-}
-
-/*===========================================================================*
- * sef_cb_init_fresh *
- *===========================================================================*/
-static int sef_cb_init_fresh(int type, sef_init_info_t *info)
-{
-/* Initialize the pipe file server. */
- int i;
- struct passwd *pw;
-
- /* Initialize main loop parameters. */
- busy = 0; /* Server is not 'busy' (i.e., inodes in use). */
-
- /* Init inode table */
- for (i = 0; i < PFS_NR_INODES; ++i) {
- inode[i].i_count = 0;
- }
-
- init_inode_cache();
- buf_pool();
-
- return(OK);
-}
-
-/*===========================================================================*
- * sef_cb_signal_handler *
- *===========================================================================*/
-static void sef_cb_signal_handler(int signo)
-{
- /* Only check for termination signal, ignore anything else. */
- if (signo != SIGTERM) return;
-
- fsdriver_terminate();
-}
+++ /dev/null
-#include "fs.h"
-
-
-/*===========================================================================*
- * fs_chmod *
- *===========================================================================*/
-int fs_chmod(ino_t ino_nr, mode_t *mode)
-{
- struct inode *rip; /* target inode */
-
- if( (rip = find_inode(ino_nr)) == NULL) return(EINVAL);
-
- rip->i_mode = (rip->i_mode & ~ALL_MODES) | (*mode & ALL_MODES);
-
- *mode = rip->i_mode; /* return new mode */
- return OK;
-}
+++ /dev/null
-#include "fs.h"
-#include "glo.h"
-
-
-/*===========================================================================*
- * fs_mount *
- *===========================================================================*/
-int fs_mount(dev_t __unused dev, unsigned int __unused flags,
- struct fsdriver_node *node, unsigned int *res_flags)
-{
-/* Mount Pipe File Server. */
-
- /* This function does not do much. PFS has no root node, and VFS will ignore
- * the returned node details anyway. The whole idea is to provide symmetry
- * with other file systems, thus keeping libfsdriver simple and free of
- * special cases. Everything else (e.g., mounting PFS in VFS) is already an
- * exception anyway.
- */
- memset(node, 0, sizeof(*node));
- *res_flags = 0;
-
- return(OK);
-}
-
-
-/*===========================================================================*
- * fs_unmount *
- *===========================================================================*/
-void fs_unmount(void)
-{
-/* Unmount Pipe File Server. */
-
- if (busy)
- printf("PFS: unmounting while busy!\n"); /* nothing we can do anyway */
-}
+++ /dev/null
-#include "fs.h"
-
-
-/*===========================================================================*
- * fs_newnode *
- *===========================================================================*/
-int fs_newnode(mode_t mode, uid_t uid, gid_t gid, dev_t dev,
- struct fsdriver_node *node)
-{
- register int r = OK;
- struct inode *rip;
-
- /* Try to allocate the inode */
- if( (rip = alloc_inode(dev, mode, uid, gid) ) == NULL) return(err_code);
-
- switch (mode & S_IFMT) {
- case S_IFBLK:
- case S_IFCHR:
- rip->i_rdev = dev; /* Major/minor dev numbers */
- break;
- case S_IFIFO:
- if ((get_block(dev, rip->i_num)) == NULL)
- r = EIO;
- break;
- default:
- r = EIO; /* Unsupported file type */
- }
-
- if (r != OK) {
- free_inode(rip);
- } else {
- /* Fill in the fields of the response message */
- node->fn_ino_nr = rip->i_num;
- node->fn_mode = rip->i_mode;
- node->fn_size = rip->i_size;
- node->fn_uid = rip->i_uid;
- node->fn_gid = rip->i_gid;
- node->fn_dev = dev;
- }
-
- return(r);
-}
--- /dev/null
+/* PFS - Pipe File Server */
+
+#include <minix/drivers.h>
+#include <minix/fsdriver.h>
+#include <minix/vfsif.h>
+#include <assert.h>
+
+/*
+ * The following constant defines the number of inodes in PFS, which is
+ * therefore the maximum number of open pipes and cloned devices that can be
+ * used in the entire system. If anything, it should be kept somewhat in sync
+ * with VFS's maximum number of inodes. In the future, inodes could be
+ * allocated dynamically, but this will require extra infrastructure.
+ */
+#define PFS_NR_INODES 512 /* maximum number of inodes in PFS */
+
+/* The following bits can be combined in the inode's i_update field. */
+#define ATIME 0x1 /* update access time later */
+#define MTIME 0x2 /* update modification time later */
+#define CTIME 0x4 /* update change time later */
+
+static struct inode {
+ ino_t i_num; /* inode number */
+
+ mode_t i_mode; /* file mode and permissions */
+ uid_t i_uid; /* user ID of the file's owner */
+ gid_t i_gid; /* group ID of the file's owner */
+ size_t i_size; /* current file size in bytes */
+ dev_t i_rdev; /* device number for device nodes */
+ time_t i_atime; /* file access time */
+ time_t i_mtime; /* file modification time */
+ time_t i_ctime; /* file change time */
+
+ char *i_data; /* data buffer, for pipes only */
+ size_t i_start; /* start of data into data buffer */
+
+ unsigned char i_update; /* which file times to update? */
+ unsigned char i_free; /* sanity check: is the inode free? */
+
+ LIST_ENTRY(inode) i_next; /* next element in free list */
+} inode[PFS_NR_INODES];
+
+static LIST_HEAD(, inode) free_inodes; /* list of free inodes */
+
+/*
+ * Mount the pipe file server.
+ */
+static int
+pfs_mount(dev_t __unused dev, unsigned int __unused flags,
+ struct fsdriver_node * node, unsigned int * res_flags)
+{
+ struct inode *rip;
+ unsigned int i;
+
+ LIST_INIT(&free_inodes); /* initialize the free list */
+
+ /*
+ * Initialize the inode table. We walk backwards so that the lowest
+ * inode numbers end up being used first. Silly? Sure, but aesthetics
+ * are worth something, too..
+ */
+ for (i = PFS_NR_INODES; i > 0; i--) {
+ rip = &inode[i - 1];
+
+ /* Inode number 0 is reserved. See also pfs_findnode. */
+ rip->i_num = i;
+ rip->i_free = TRUE;
+
+ LIST_INSERT_HEAD(&free_inodes, rip, i_next);
+ }
+
+ /*
+ * PFS has no root node, and VFS will ignore the returned node details
+ * anyway. The whole idea is to provide symmetry with other file
+ * systems, thus keeping libfsdriver simple and free of special cases.
+ */
+ memset(node, 0, sizeof(*node));
+ *res_flags = RES_64BIT;
+
+ return OK;
+}
+
+/*
+ * Unmount the pipe file server.
+ */
+static void
+pfs_unmount(void)
+{
+ unsigned int i;
+
+ /* Warn about in-use inodes. There's nothing else we can do. */
+ for (i = 0; i < PFS_NR_INODES; i++)
+ if (inode[i].i_free == FALSE)
+ break;
+
+ if (i < PFS_NR_INODES)
+ printf("PFS: unmounting while busy!\n");
+}
+
+/*
+ * Find the node with the corresponding inode number. It must be in use.
+ */
+static struct inode *
+pfs_findnode(ino_t ino_nr)
+{
+ struct inode *rip;
+
+ /* Inode numbers are 1-based, because inode number 0 is reserved. */
+ if (ino_nr < 1 || ino_nr > PFS_NR_INODES)
+ return NULL;
+
+ rip = &inode[ino_nr - 1];
+ assert(rip->i_num == ino_nr);
+
+ if (rip->i_free == TRUE)
+ return NULL;
+
+ return rip;
+}
+
+/*
+ * Create a new, unlinked node. It must be either a pipe or a device file.
+ */
+static int
+pfs_newnode(mode_t mode, uid_t uid, gid_t gid, dev_t dev,
+ struct fsdriver_node * node)
+{
+ struct inode *rip;
+ char *data;
+ int isfifo, isdev;
+
+ /* Check the file type. Do we support it at all? */
+ isfifo = S_ISFIFO(mode);
+ isdev = S_ISBLK(mode) || S_ISCHR(mode);
+
+ if (!isfifo && !isdev)
+ return EINVAL; /* this means VFS is misbehaving.. */
+
+ /* Is there a free inode? */
+ if (LIST_EMPTY(&free_inodes))
+ return ENFILE;
+
+ /* For pipes, we need a buffer. Try to allocate one. */
+ data = NULL;
+ if (isfifo && (data = malloc(PIPE_BUF)) == NULL)
+ return ENOSPC;
+
+ /* Nothing can go wrong now. Take an inode off the free list. */
+ rip = LIST_FIRST(&free_inodes);
+ LIST_REMOVE(rip, i_next);
+
+ assert(rip->i_free == TRUE);
+ rip->i_free = FALSE; /* this is for sanity checks only */
+
+ /* Initialize the inode's fields. */
+ rip->i_mode = mode;
+ rip->i_uid = uid;
+ rip->i_gid = gid;
+ rip->i_size = 0;
+ rip->i_update = ATIME | MTIME | CTIME;
+ if (isdev)
+ rip->i_rdev = dev;
+ else
+ rip->i_rdev = NO_DEV;
+ rip->i_data = data;
+ rip->i_start = 0;
+
+ /* Fill in the fields of the response message. */
+ node->fn_ino_nr = rip->i_num;
+ node->fn_mode = rip->i_mode;
+ node->fn_size = rip->i_size;
+ node->fn_uid = rip->i_uid;
+ node->fn_gid = rip->i_gid;
+ node->fn_dev = rip->i_rdev;
+
+ return OK;
+}
+
+/*
+ * Close a node.
+ */
+static int
+pfs_putnode(ino_t ino_nr, unsigned int count)
+{
+ struct inode *rip;
+
+ if ((rip = pfs_findnode(ino_nr)) == NULL)
+ return EINVAL;
+
+ /*
+ * Since the new-node call is the only way to open an inode, and there
+ * is no way to increase the use count of an already-opened inode, we
+ * can safely assume that the reference count will only ever be one.
+ * That also means we are always freeing up the target inode here.
+ */
+ if (count != 1)
+ return EINVAL;
+
+ /* For pipes, free the inode data buffer. */
+ if (rip->i_data != NULL)
+ free(rip->i_data);
+
+ /* Return the inode to the free list. */
+ rip->i_free = TRUE;
+
+ LIST_INSERT_HEAD(&free_inodes, rip, i_next);
+
+ return OK;
+}
+
+/*
+ * Read from a pipe.
+ */
+static ssize_t
+pfs_read(ino_t ino_nr, struct fsdriver_data * data, size_t bytes,
+ off_t __unused pos, int __unused call)
+{
+ struct inode *rip;
+ int r;
+
+ /* The target node must be a pipe. */
+ if ((rip = pfs_findnode(ino_nr)) == NULL || !S_ISFIFO(rip->i_mode))
+ return EINVAL;
+
+ /* We can't read beyond the maximum file position. */
+ if (bytes > PIPE_BUF)
+ return EFBIG;
+
+ /* Limit the request to how much is in the pipe. */
+ if (bytes > rip->i_size)
+ bytes = rip->i_size;
+
+ /* Copy the data to user space. */
+ if ((r = fsdriver_copyout(data, 0, rip->i_data + rip->i_start,
+ bytes)) != OK)
+ return r;
+
+ /* Update file size and access time. */
+ rip->i_size -= bytes;
+ rip->i_start += bytes;
+ rip->i_update |= ATIME;
+
+ /* Return the number of bytes transferred. */
+ return bytes;
+}
+
+/*
+ * Write to a pipe.
+ */
+static ssize_t
+pfs_write(ino_t ino_nr, struct fsdriver_data * data, size_t bytes,
+ off_t __unused pos, int __unused call)
+{
+ struct inode *rip;
+ int r;
+
+ /* The target node must be a pipe. */
+ if ((rip = pfs_findnode(ino_nr)) == NULL || !S_ISFIFO(rip->i_mode))
+ return EINVAL;
+
+ /* Check in advance to see if file will grow too big. */
+ if (rip->i_size + bytes > PIPE_BUF)
+ return EFBIG;
+
+ /*
+ * Move any previously remaining data to the front of the buffer.
+ * Doing so upon writes rather than reads saves on memory moves when
+ * there are many small reads. Not using the buffer circularly saves
+ * on kernel calls.
+ */
+ if (rip->i_start > 0) {
+ if (rip->i_size > 0)
+ memmove(rip->i_data, rip->i_data + rip->i_start,
+ rip->i_size);
+
+ rip->i_start = 0;
+ }
+
+ /* Copy the data from user space. */
+ r = fsdriver_copyin(data, 0, rip->i_data + rip->i_size, bytes);
+ if (r != OK)
+ return r;
+
+ /* Update file size and times. */
+ rip->i_size += bytes;
+ rip->i_update |= CTIME | MTIME;
+
+ /* Return the number of bytes transferred. */
+ return bytes;
+}
+
+/*
+ * Truncate a pipe.
+ */
+static int
+pfs_trunc(ino_t ino_nr, off_t start_pos, off_t end_pos)
+{
+ struct inode *rip;
+
+ /* The target node must be a pipe. */
+ if ((rip = pfs_findnode(ino_nr)) == NULL || !S_ISFIFO(rip->i_mode))
+ return EINVAL;
+
+ /* We only support full truncation of pipes. */
+ if (start_pos != 0 || end_pos != 0)
+ return EINVAL;
+
+ /* Update file size and times. */
+ rip->i_size = 0;
+ rip->i_update |= CTIME | MTIME;
+
+ return OK;
+}
+
+/*
+ * Return node status.
+ */
+static int
+pfs_stat(ino_t ino_nr, struct stat * statbuf)
+{
+ struct inode *rip;
+ time_t now;
+
+ if ((rip = pfs_findnode(ino_nr)) == NULL)
+ return EINVAL;
+
+ /* Update the time fields in the inode, if need be. */
+ if (rip->i_update != 0) {
+ now = clock_time(NULL);
+
+ if (rip->i_update & ATIME) rip->i_atime = now;
+ if (rip->i_update & MTIME) rip->i_mtime = now;
+ if (rip->i_update & CTIME) rip->i_ctime = now;
+
+ rip->i_update = 0;
+ }
+
+ /* Fill the stat buffer. */
+ statbuf->st_dev = rip->i_rdev; /* workaround for old socketpair bug */
+ statbuf->st_ino = rip->i_num;
+ statbuf->st_mode = rip->i_mode;
+ statbuf->st_nlink = 0;
+ statbuf->st_uid = rip->i_uid;
+ statbuf->st_gid = rip->i_gid;
+ statbuf->st_rdev = rip->i_rdev;
+ statbuf->st_size = rip->i_size;
+ statbuf->st_atime = rip->i_atime;
+ statbuf->st_mtime = rip->i_mtime;
+ statbuf->st_ctime = rip->i_ctime;
+ statbuf->st_blksize = PIPE_BUF;
+ statbuf->st_blocks = howmany(rip->i_size, S_BLKSIZE);
+
+ return OK;
+}
+
+/*
+ * Change node permissions.
+ */
+static int
+pfs_chmod(ino_t ino_nr, mode_t * mode)
+{
+ struct inode *rip;
+
+ if ((rip = pfs_findnode(ino_nr)) == NULL)
+ return EINVAL;
+
+ /* Update file mode and times. */
+ rip->i_mode = (rip->i_mode & ~ALLPERMS) | (*mode & ALLPERMS);
+ rip->i_update |= MTIME | CTIME;
+
+ *mode = rip->i_mode;
+ return OK;
+}
+
+/*
+ * Process a signal.
+ */
+static void
+pfs_signal(int signo)
+{
+
+ /* Only check for termination signal, ignore anything else. */
+ if (signo != SIGTERM) return;
+
+ fsdriver_terminate();
+}
+
+/*
+ * Perform SEF initialization.
+ */
+static void
+pfs_startup(void)
+{
+
+ /* Register initialization callbacks. */
+ sef_setcb_init_fresh(sef_cb_init_null);
+ sef_setcb_init_restart(sef_cb_init_fail);
+
+ /* No live update support for now. */
+
+ /* Register signal callbacks. */
+ sef_setcb_signal_handler(pfs_signal);
+
+ /* Let SEF perform startup. */
+ sef_startup();
+}
+
+/*
+ * Function call table for the fsdriver library.
+ */
+static struct fsdriver pfs_table = {
+ .fdr_mount = pfs_mount,
+ .fdr_unmount = pfs_unmount,
+ .fdr_newnode = pfs_newnode,
+ .fdr_putnode = pfs_putnode,
+ .fdr_read = pfs_read,
+ .fdr_write = pfs_write,
+ .fdr_trunc = pfs_trunc,
+ .fdr_stat = pfs_stat,
+ .fdr_chmod = pfs_chmod
+};
+
+/*
+ * The main routine of this service.
+ */
+int
+main(void)
+{
+
+ /* Local startup. */
+ pfs_startup();
+
+ /* The fsdriver library does the actual work here. */
+ fsdriver_task(&pfs_table);
+
+ return EXIT_SUCCESS;
+}
+++ /dev/null
-#ifndef __PFS_PROTO_H__
-#define __PFS_PROTO_H__
-
-/* Function prototypes. */
-
-/* Structs used in prototypes must be declared as such first. */
-struct buf;
-struct inode;
-
-/* buffer.c */
-struct buf *get_block(dev_t dev, ino_t inum);
-void put_block(dev_t dev, ino_t inum);
-void buf_pool(void);
-
-/* inode.c */
-struct inode *alloc_inode(dev_t dev, mode_t mode, uid_t uid, gid_t gid);
-void dup_inode(struct inode *ip);
-struct inode *find_inode(ino_t numb);
-void free_inode(struct inode *rip);
-int fs_putnode(ino_t ino_nr, unsigned int count);
-void init_inode_cache(void);
-struct inode *get_inode(dev_t dev, ino_t numb);
-void put_inode(struct inode *rip);
-void update_times(struct inode *rip);
-void wipe_inode(struct inode *rip);
-
-/* link.c */
-int fs_trunc(ino_t ino_nr, off_t start, off_t end);
-int truncate_inode(struct inode *rip, off_t newsize);
-
-/* misc.c */
-int fs_chmod(ino_t ino_nr, mode_t *mode);
-
-/* mount.c */
-int fs_mount(dev_t dev, unsigned int flags, struct fsdriver_node *node,
- unsigned int *res_flags);
-void fs_unmount(void);
-
-/* open.c */
-int fs_newnode(mode_t mode, uid_t uid, gid_t gid, dev_t dev,
- struct fsdriver_node *node);
-
-/* read.c */
-ssize_t fs_read(ino_t ino_nr, struct fsdriver_data *data, size_t bytes,
- off_t pos, int call);
-ssize_t fs_write(ino_t ino_nr, struct fsdriver_data *data, size_t bytes,
- off_t pos, int call);
-
-/* stadir.c */
-int fs_stat(ino_t ino_nr, struct stat *statbuf);
-
-/* super.c */
-bit_t alloc_bit(void);
-void free_bit(bit_t bit_returned);
-
-#endif
+++ /dev/null
-#include "fs.h"
-
-
-/*===========================================================================*
- * fs_read *
- *===========================================================================*/
-ssize_t fs_read(ino_t ino_nr, struct fsdriver_data *data, size_t bytes,
- off_t __unused pos, int call)
-{
- int r;
- struct buf *bp;
- struct inode *rip;
-
- /* Find the inode referred */
- if ((rip = find_inode(ino_nr)) == NULL) return(EINVAL);
-
- if (!S_ISFIFO(rip->i_mode)) return(EIO);
-
- /* We can't read or write beyond the max file position */
- if (bytes > PIPE_BUF) return(EFBIG);
-
- if (bytes > rip->i_size) {
- /* There aren't that many bytes to read */
- bytes = rip->i_size;
- }
-
- /* Copy a chunk from the block buffer to user space. */
- if ((bp = get_block(rip->i_dev, rip->i_num)) == NULL) return(err_code);
-
- r = fsdriver_copyout(data, 0, bp->b_data, bytes);
-
- if (r == OK && rip->i_size > bytes) {
- /* Move any remaining data to the front of the buffer. */
- /* FIXME: see if this really is the optimal strategy. */
- memmove(bp->b_data, bp->b_data + bytes, rip->i_size - bytes);
- }
-
- put_block(rip->i_dev, rip->i_num);
-
- if (r != OK)
- return r;
-
- /* Update file size and access time. */
- rip->i_size -= bytes;
- rip->i_update |= ATIME;
-
- return(bytes);
-}
-
-
-/*===========================================================================*
- * fs_write *
- *===========================================================================*/
-ssize_t fs_write(ino_t ino_nr, struct fsdriver_data *data, size_t bytes,
- off_t __unused pos, int __unused call)
-{
- int r;
- struct buf *bp;
- struct inode *rip;
-
- /* Find the inode referred */
- if ((rip = find_inode(ino_nr)) == NULL) return(EINVAL);
-
- if (!S_ISFIFO(rip->i_mode)) return(EIO);
-
- /* Check in advance to see if file will grow too big. */
- if (rip->i_size + bytes > PIPE_BUF)
- return(EFBIG);
-
- /* Copy the data from user space to the block buffer. */
- if ((bp = get_block(rip->i_dev, rip->i_num)) == NULL) return(err_code);
-
- r = fsdriver_copyin(data, 0, bp->b_data + rip->i_size, bytes);
-
- put_block(rip->i_dev, rip->i_num);
-
- if (r != OK)
- return r;
-
- /* Update file size and file times. */
- rip->i_size += bytes;
- rip->i_update |= CTIME | MTIME;
-
- return(bytes);
-}
+++ /dev/null
-#include "fs.h"
-
-
-/*===========================================================================*
- * fs_stat *
- *===========================================================================*/
-int fs_stat(ino_t ino_nr, struct stat *statbuf)
-{
- struct inode *rip;
- mode_t type;
- u32_t blocks; /* The unit of this is 512 */
- int s;
-
- if ((rip = find_inode(ino_nr)) == NULL) return(EINVAL);
-
- type = rip->i_mode & I_TYPE;
- s = (type == I_CHAR_SPECIAL || type == I_BLOCK_SPECIAL);
-
- /* Update the atime, ctime, and mtime fields in the inode, if need be. */
- if (rip->i_update) update_times(rip);
-
- blocks = rip->i_size / S_BLKSIZE;
- if (rip->i_size % S_BLKSIZE != 0)
- blocks += 1;
-
- statbuf->st_dev = rip->i_dev;
- statbuf->st_ino = rip->i_num;
- statbuf->st_mode = rip->i_mode;
- statbuf->st_nlink = rip->i_nlinks;
- statbuf->st_uid = rip->i_uid;
- statbuf->st_gid = (short int) rip->i_gid;
- statbuf->st_rdev = (s ? rip->i_rdev : NO_DEV);
- statbuf->st_size = rip->i_size;
- if (!s) statbuf->st_mode &= ~I_REGULAR;/* wipe out I_REGULAR bit for pipes */
- statbuf->st_atime = rip->i_atime;
- statbuf->st_mtime = rip->i_mtime;
- statbuf->st_ctime = rip->i_ctime;
- statbuf->st_blksize = PIPE_BUF;
- statbuf->st_blocks = blocks;
-
- return(OK);
-}
+++ /dev/null
-/* This file manages the super block table and the related data structures,
- * namely, the bit maps that keep track of which zones and which inodes are
- * allocated and which are free. When a new inode or zone is needed, the
- * appropriate bit map is searched for a free entry.
- *
- * The entry points into this file are
- * alloc_bit: somebody wants to allocate a zone or inode; find one
- * free_bit: indicate that a zone or inode is available for allocation
- */
-
-#include "fs.h"
-
-
-/*===========================================================================*
- * alloc_bit *
- *===========================================================================*/
-bit_t alloc_bit(void)
-{
-/* Allocate a bit from a bit map and return its bit number. */
- bitchunk_t *wptr, *wlim;
- bit_t b;
- unsigned int i, bcount;
-
- bcount = FS_BITMAP_CHUNKS(PFS_NR_INODES); /* Inode map has this many chunks. */
- wlim = &inodemap[bcount]; /* Point to last chunk in inodemap. */
-
- for (wptr = &inodemap[0]; wptr < wlim; wptr++) {
- /* Does this word contain a free bit? */
- if (*wptr == (bitchunk_t) ~0) continue; /* No. Go to next word */
-
- /* Find and allocate the free bit. */
- for (i = 0; (*wptr & (1 << i)) != 0; ++i) {}
-
- /* Get inode number */
- b = (bit_t) ((wptr - &inodemap[0]) * FS_BITCHUNK_BITS + i);
-
- /* Don't allocate bits beyond end of map. */
- if (b >= PFS_NR_INODES) break;
-
- /* Allocate and return bit number. */
- *wptr |= 1 << i;
-
- /* Mark server 'busy' */
- busy++;
- return(b);
- }
-
- return(NO_BIT); /* no bit could be allocated */
-}
-
-
-/*===========================================================================*
- * free_bit *
- *===========================================================================*/
-void free_bit(bit_returned)
-bit_t bit_returned; /* number of bit to insert into the inode map*/
-{
- bitchunk_t *k, mask;
- bit_t bit;
- unsigned word;
-
- /* Get word offset and bit within offset */
- word = (unsigned) (bit_returned / (bit_t) FS_BITCHUNK_BITS);
- bit = bit_returned % (bit_t) FS_BITCHUNK_BITS;
-
- /* Unset bit */
- k = &inodemap[word];
- mask = (unsigned) 1 << bit;
- *k &= ~mask;
-
- busy--; /* One inode less in use. */
-}
+++ /dev/null
-
-/* This file contains the table used to map VFS/FS call numbers onto the
- * routines that perform them.
- */
-
-#define _TABLE
-
-#include "fs.h"
-
-/* File System Handlers (pfs) */
-struct fsdriver pfs_table = {
- .fdr_mount = fs_mount,
- .fdr_unmount = fs_unmount,
- .fdr_newnode = fs_newnode,
- .fdr_putnode = fs_putnode,
- .fdr_read = fs_read,
- .fdr_write = fs_write,
- .fdr_trunc = fs_trunc,
- .fdr_stat = fs_stat,
- .fdr_chmod = fs_chmod
-};