PFS: rewrite, restyle

- remove the buffer pool, inode bitmap, and inode hash table, and
  simplify the code accordingly;
- use theoretically slightly more optimal buffer management;
- put the entire source in one file, instead of having many files
  with one or two functions each;
- convert the code to KNF style.

Change-Id: Ib8f6f0bd99fbc6eb9098fba718e71b8e560783d9
This commit is contained in:
David van Moolenbroek 2014-08-24 10:20:59 +00:00
parent f859061eaf
commit 30d9b70391
19 changed files with 438 additions and 1050 deletions

View file

@ -1,7 +1,6 @@
# Makefile for Pipe File System (PFS) # Makefile for Pipe File System (PFS)
PROG= pfs PROG= pfs
SRCS= open.c table.c inode.c main.c super.c link.c \ SRCS= pfs.c
buffer.c read.c misc.c mount.c stadir.c
DPADD+= ${LIBFSDRIVER} ${LIBSYS} DPADD+= ${LIBFSDRIVER} ${LIBSYS}
LDADD+= -lfsdriver -lsys LDADD+= -lfsdriver -lsys

View file

@ -1,26 +0,0 @@
#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

View file

@ -1,98 +0,0 @@
#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);
}

View file

@ -1,22 +0,0 @@
#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

View file

@ -1,33 +0,0 @@
#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

View file

@ -1,20 +0,0 @@
#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

View file

@ -1,325 +0,0 @@
/* 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 */
}

View file

@ -1,37 +0,0 @@
#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

View file

@ -1,42 +0,0 @@
#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);
}

View file

@ -1,75 +0,0 @@
#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();
}

View file

@ -1,17 +0,0 @@
#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;
}

View file

@ -1,35 +0,0 @@
#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 */
}

View file

@ -1,42 +0,0 @@
#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);
}

437
minix/fs/pfs/pfs.c Normal file
View file

@ -0,0 +1,437 @@
/* 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;
}

View file

@ -1,56 +0,0 @@
#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

View file

@ -1,85 +0,0 @@
#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);
}

View file

@ -1,42 +0,0 @@
#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);
}

View file

@ -1,72 +0,0 @@
/* 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. */
}

View file

@ -1,21 +0,0 @@
/* 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
};