--- /dev/null
+# Makefile for Pipe File System (PFS)
+SERVER = pfs
+NR_BUFS=256
+BS=4096
+
+# directories
+u = /usr
+i = $u/include
+s = $i/sys
+h = $i/minix
+
+# programs, flags, etc.
+CC = exec cc
+CFLAGS = -I$i $(EXTRA_OPTS) $(CPROFILE) -DNR_BUFS=$(NR_BUFS)
+LDFLAGS = -i
+LIBS = -lsys
+
+OBJ = open.o table.o inode.o main.o super.o link.o \
+ buffer.o read.o misc.o utility.o stadir.o
+
+# build local binary
+install all build: $(SERVER)
+$(SERVER): $(OBJ)
+ $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS)
+
+# clean up local files
+clean:
+ rm -f $(SERVER) *.o *.bak
+
+depend:
+ mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend
+
+# Include generated dependencies.
+include .depend
+
+
--- /dev/null
+/* 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. */
+
+#define NIL_BUF ((struct buf *) 0) /* indicates absence of a buffer */
+
+#define BUFHASH(b) ((b) % NR_BUFS)
+
+EXTERN struct buf *front; /* points to least recently used free block */
+EXTERN struct buf *rear; /* points to most recently used free block */
+EXTERN int bufs_in_use; /* # bufs currently in use (not on free list)*/
+
--- /dev/null
+#include "fs.h"
+#include "buf.h"
+#include "inode.h"
+#include <sys/types.h>
+#include <stdlib.h>
+#include <alloca.h>
+#include <string.h>
+
+
+/*===========================================================================*
+ * buf_pool *
+ *===========================================================================*/
+PUBLIC void buf_pool(void)
+{
+/* Initialize the buffer pool. */
+
+ front = NIL_BUF;
+ rear = NIL_BUF;
+}
+
+
+
+/*===========================================================================*
+ * get_block *
+ *===========================================================================*/
+PUBLIC struct buf *get_block(dev, inum)
+Dev_t dev;
+ino_t inum;
+{
+ struct buf *bp;
+
+ bp = front;
+ while(bp != NIL_BUF) {
+ 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 *
+ *===========================================================================*/
+PUBLIC struct buf *new_block(dev, inum)
+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(NIL_BUF);
+ }
+ 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 == NIL_BUF) { /* Empty list? */
+ front = bp;
+ bp->b_prev = NIL_BUF;
+ } else {
+ rear->b_next = bp;
+ bp->b_prev = rear;
+ }
+ bp->b_next = NIL_BUF;
+ rear = bp;
+
+ return(bp);
+}
+
+
+/*===========================================================================*
+ * put_block *
+ *===========================================================================*/
+PUBLIC void put_block(dev, inum)
+dev_t dev;
+ino_t inum;
+{
+ struct buf *bp;
+
+ bp = get_block(dev, inum);
+ if (bp == NIL_BUF) 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 == NIL_BUF)
+ front = bp->b_next;
+ else
+ bp->b_prev->b_next = bp->b_next;
+
+ if (bp->b_next == NIL_BUF)
+ 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
+/* Tables sizes */
+#define V1_NR_DZONES 7 /* # direct zone numbers in a V1 inode */
+#define V1_NR_TZONES 9 /* total # zone numbers in a V1 inode */
+#define V2_NR_DZONES 7 /* # direct zone numbers in a V2 inode */
+#define V2_NR_TZONES 10 /* total # zone numbers in a V2 inode */
+
+#define NR_INODES 256 /* # slots in "in core" inode table */
+#define GETDENTS_BUFSIZ 257
+
+#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 INODE_MAP_SIZE INODE_HASH_LOG2
+
+
+/* The type of sizeof may be (unsigned) long. Use the following macro for
+ * taking the sizes of small objects so that there are no surprises like
+ * (small) long constants being passed to routines expecting an int.
+ */
+#define usizeof(t) ((unsigned) sizeof(t))
+
+/* File system types. */
+#define SUPER_MAGIC 0x137F /* magic number contained in super-block */
+#define SUPER_REV 0x7F13 /* magic # when 68000 disk read on PC or vv */
+#define SUPER_V2 0x2468 /* magic # for V2 file systems */
+#define SUPER_V2_REV 0x6824 /* V2 magic written on PC, read on 68K or vv */
+#define SUPER_V3 0x4d5a /* magic # for V3 file systems */
+
+#define V1 1 /* version number of V1 file systems */
+#define V2 2 /* version number of V2 file systems */
+#define V3 3 /* version number of V3 file systems */
+
+/* Miscellaneous constants */
+#define SU_UID ((uid_t) 0) /* super_user's uid_t */
+#define SERVERS_UID ((uid_t) 11) /* who may do FSSIGNON */
+#define SYS_UID ((uid_t) 0) /* uid_t for processes MM and INIT */
+#define SYS_GID ((gid_t) 0) /* gid_t for processes MM and INIT */
+#define NORMAL 0 /* forces get_block to do disk read */
+#define NO_READ 1 /* prevents get_block from doing disk read */
+#define PREFETCH 2 /* tells get_block not to read or mark dev */
+
+#define NO_BIT ((bit_t) 0) /* returned by alloc_bit() to signal failure */
+
+/* write_map() args */
+#define WMAP_FREE (1 << 0)
+
+#define IGN_PERM 0
+#define CHK_PERM 1
+
+#define CLEAN 0 /* disk and memory copies identical */
+#define DIRTY 1 /* disk and memory copies differ */
+#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 BYTE_SWAP 0 /* tells conv2/conv4 to swap bytes */
+
+#define END_OF_FILE (-104) /* eof detected */
+
+#define ROOT_INODE 1 /* inode number for root directory */
+#define BOOT_BLOCK ((block_t) 0) /* block number of boot block */
+#define SUPER_BLOCK_BYTES (1024) /* bytes offset */
+#define START_BLOCK 2 /* first block of FS (not counting SB) */
+
+#define DIR_ENTRY_SIZE usizeof (struct direct) /* # bytes/dir entry */
+#define NR_DIR_ENTRIES(b) ((b)/DIR_ENTRY_SIZE) /* # dir entries/blk */
+#define SUPER_SIZE usizeof (struct super_block) /* super_block size */
+#define PIPE_SIZE(b) (V1_NR_DZONES*(b)) /* pipe size in bytes */
+
+#define FS_BITMAP_CHUNKS(b) ((b)/usizeof (bitchunk_t))/* # map chunks/blk */
+#define FS_BITCHUNK_BITS (usizeof(bitchunk_t) * CHAR_BIT)
+#define FS_BITS_PER_BLOCK(b) (FS_BITMAP_CHUNKS(b) * FS_BITCHUNK_BITS)
+
+/* Derived sizes pertaining to the V1 file system. */
+#define V1_ZONE_NUM_SIZE usizeof (zone1_t) /* # bytes in V1 zone */
+#define V1_INODE_SIZE usizeof (d1_inode) /* bytes in V1 dsk ino */
+
+/* # zones/indir block */
+#define V1_INDIRECTS (_STATIC_BLOCK_SIZE/V1_ZONE_NUM_SIZE)
+
+/* # V1 dsk inodes/blk */
+#define V1_INODES_PER_BLOCK (_STATIC_BLOCK_SIZE/V1_INODE_SIZE)
+
+/* Derived sizes pertaining to the V2 file system. */
+#define V2_ZONE_NUM_SIZE usizeof (zone_t) /* # bytes in V2 zone */
+#define V2_INODE_SIZE usizeof (d2_inode) /* bytes in V2 dsk ino */
+#define V2_INDIRECTS(b) ((b)/V2_ZONE_NUM_SIZE) /* # zones/indir block */
+#define V2_INODES_PER_BLOCK(b) ((b)/V2_INODE_SIZE)/* # V2 dsk inodes/blk */
+
+#define PFS_MIN(a,b) pfs_min_f(__FILE__,__LINE__,(a), (b))
+#define PFS_NUL(str,l,m) pfs_nul_f(__FILE__,__LINE__,(str), (l), (m))
+
+/* Args to dev_bio/dev_io */
+#define PFS_DEV_READ 10001
+#define PFS_DEV_WRITE 10002
+#define PFS_DEV_SCATTER 10003
+#define PFS_DEV_GATHER 10004
+
--- /dev/null
+
+/* Driver endpoints for major devices. Only the block devices
+ * are mapped here, it's a subset of the mapping in the VFS */
+
+EXTERN struct driver_endpoints {
+ endpoint_t driver_e;
+} driver_endpoints[NR_DEVICES];
+
+
--- /dev/null
+/* This is the master header for pfs. It includes some other files
+ * and defines the principal constants.
+ */
+#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */
+#define _MINIX 1 /* tell headers to include MINIX stuff */
+#define _SYSTEM 1 /* tell headers that this is the kernel */
+
+#define VERBOSE 0 /* show messages during initialization? */
+
+/* The following are so basic, all the *.c files get them automatically. */
+#include <minix/config.h> /* MUST be first */
+#include <ansi.h> /* MUST be second */
+#include <sys/types.h>
+#include <minix/const.h>
+#include <minix/type.h>
+#include <minix/dmap.h>
+#include <limits.h>
+#include <errno.h>
+#include <minix/syslib.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <minix/sysutil.h>
+
+#include "const.h"
+#include "proto.h"
+#include "glo.h"
--- /dev/null
+/* EXTERN should be extern except for the table file */
+#ifdef _TABLE
+#undef EXTERN
+#define EXTERN
+#endif
+
+#include <minix/vfsif.h>
+
+/* The following variables are used for returning results to the caller. */
+EXTERN int err_code; /* temporary storage for error number */
+
+EXTERN int cch[NR_INODES];
+
+extern _PROTOTYPE (int (*fs_call_vec[]), (void) ); /* fs call table */
+
+EXTERN message fs_m_in;
+EXTERN message fs_m_out;
+
+EXTERN uid_t caller_uid;
+EXTERN gid_t caller_gid;
+EXTERN int req_nr;
+EXTERN int SELF_E;
+EXTERN int exitsignaled;
+EXTERN int busy;
+
+/* Inode map. */
+EXTERN bitchunk_t inodemap[FS_BITMAP_CHUNKS(NR_INODES)];
--- /dev/null
+
+#define _SYSTEM 1 /* get OK and negative error codes */
+#define _MINIX 1 /* tell headers to include MINIX stuff */
+
+#define VERBOSE 0 /* display diagnostics */
+
+#include <ansi.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <minix/callnr.h>
+#include <minix/config.h>
+#include <minix/type.h>
+#include <minix/const.h>
+#include <minix/com.h>
+#include <minix/syslib.h>
+#include <minix/sysutil.h>
+#include <minix/keymap.h>
+#include <minix/bitmap.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include "proto.h"
+
--- /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
+ * dup_inode: indicate that someone else is using an inode table entry
+ * find_inode: retrieve pointer to inode in inode cache
+ *
+ */
+
+#include "fs.h"
+#include "buf.h"
+#include "inode.h"
+#include <minix/vfsif.h>
+
+FORWARD _PROTOTYPE( int addhash_inode, (struct inode *node) );
+FORWARD _PROTOTYPE( int unhash_inode, (struct inode *node) );
+
+
+/*===========================================================================*
+ * fs_putnode *
+ *===========================================================================*/
+PUBLIC int fs_putnode()
+{
+/* Find the inode specified by the request message and decrease its counter.*/
+
+ struct inode *rip;
+ int count;
+ dev_t dev;
+ ino_t inum;
+
+ rip = find_inode(fs_m_in.REQ_INODE_NR);
+
+ if(!rip) {
+ printf("%s:%d put_inode: inode #%d dev: %d not found\n", __FILE__,
+ __LINE__, fs_m_in.REQ_INODE_NR, fs_m_in.REQ_DEV);
+ panic(__FILE__, "fs_putnode failed", NO_NUM);
+ }
+
+ count = fs_m_in.REQ_COUNT;
+ if (count <= 0) {
+ printf("%s:%d put_inode: bad value for count: %d\n", __FILE__,
+ __LINE__, count);
+ panic(__FILE__, "fs_putnode failed", NO_NUM);
+ } else if(count > rip->i_count) {
+ printf("%s:%d put_inode: count too high: %d > %d\n", __FILE__,
+ __LINE__, count, rip->i_count);
+ panic(__FILE__, "fs_putnode failed", NO_NUM);
+ }
+
+ /* Decrease reference counter, but keep one reference; it will be consumed by
+ * put_inode(). */
+ rip->i_count -= count - 1;
+ dev = rip->i_dev;
+ inum = rip->i_num;
+ put_inode(rip);
+ if (rip->i_count == 0) put_block(dev, inum);
+ return(OK);
+}
+
+
+/*===========================================================================*
+ * init_inode_cache *
+ *===========================================================================*/
+PUBLIC 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[NR_INODES]; ++rip) {
+ rip->i_num = 0;
+ 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 *
+ *===========================================================================*/
+PRIVATE int addhash_inode(struct inode *node)
+{
+ int hashi = node->i_num & INODE_HASH_MASK;
+
+ /* insert into hash table */
+ LIST_INSERT_HEAD(&hash_inodes[hashi], node, i_hash);
+ return(OK);
+}
+
+
+/*===========================================================================*
+ * unhash_inode *
+ *===========================================================================*/
+PRIVATE int unhash_inode(struct inode *node)
+{
+ /* remove from hash table */
+ LIST_REMOVE(node, i_hash);
+ return(OK);
+}
+
+
+/*===========================================================================*
+ * get_inode *
+ *===========================================================================*/
+PUBLIC struct inode *get_inode(dev, numb)
+dev_t dev; /* device on which inode resides */
+int numb; /* inode number (ANSI: may not be unshort) */
+{
+/* 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, *xp;
+ int hashi;
+
+ hashi = 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(NIL_INODE);
+ }
+ rip = TAILQ_FIRST(&unused_inodes);
+
+ /* If not free unhash it */
+ if (rip->i_num != 0) 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 *
+ *===========================================================================*/
+PUBLIC struct inode *find_inode(numb)
+int numb; /* inode number (ANSI: may not be unshort) */
+{
+/* Find the inode specified by the inode and device number.
+ */
+ struct inode *rip;
+ int hashi;
+
+ hashi = 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(NIL_INODE);
+}
+
+
+/*===========================================================================*
+ * put_inode *
+ *===========================================================================*/
+PUBLIC void put_inode(rip)
+register 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 == NIL_INODE) return; /* checking here is easier than in caller */
+
+ if (rip->i_count < 1)
+ panic(__FILE__, "put_inode: i_count already below 1", rip->i_count);
+
+ if (--rip->i_count == 0) { /* i_count == 0 means no one is using it now */
+ if (rip->i_nlinks == 0) {
+ /* i_nlinks == 0 means 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, 0);
+ }
+
+ if (rip->i_nlinks == 0) {
+ /* free, put at the front of the LRU list */
+ unhash_inode(rip);
+ rip->i_num = 0;
+ 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 *
+ *===========================================================================*/
+PUBLIC struct inode *alloc_inode(dev_t dev, mode_t bits)
+{
+/* Allocate a free inode on 'dev', and return a pointer to it. */
+
+ register struct inode *rip;
+ int major, minor;
+ bit_t b;
+ ino_t i_num;
+
+ b = alloc_bit();
+ if (b == NO_BIT) {
+ err_code = ENOSPC;
+ printf("PipeFS is out of inodes\n");
+ return(NIL_INODE);
+ }
+ i_num = (ino_t) b;
+
+
+ /* Try to acquire a slot in the inode table. */
+ if ((rip = get_inode(dev, i_num)) == NIL_INODE) {
+ /* 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 = 0; /* initial no links */
+ rip->i_uid = caller_uid; /* file's uid is owner's */
+ rip->i_gid = caller_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 *
+ *===========================================================================*/
+PUBLIC void wipe_inode(rip)
+register 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.
+ */
+
+ register int i;
+
+ rip->i_size = 0;
+ rip->i_update = ATIME | CTIME | MTIME; /* update all times later */
+}
+
+
+/*===========================================================================*
+ * free_inode *
+ *===========================================================================*/
+PUBLIC 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 >= NR_INODES) return;
+ b = rip->i_num;
+ free_bit(b);
+}
+
+
+/*===========================================================================*
+ * dup_inode *
+ *===========================================================================*/
+PUBLIC void dup_inode(ip)
+struct inode *ip; /* The inode to be duplicated. */
+{
+/* This routine is a simplified form of get_inode() for the case where
+ * the inode pointer is already known.
+ */
+
+ ip->i_count++;
+}
+
+
+/*===========================================================================*
+ * update_times *
+ *===========================================================================*/
+PUBLIC void update_times(rip)
+register 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();
+ 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
+/* 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 */
+ off_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)*/
+
+ /* The following items are not present on the disk. */
+ dev_t i_dev; /* which 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[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];
+
+#define NIL_INODE (struct inode *) 0 /* indicates absence of inode slot */
+
--- /dev/null
+#include "fs.h"
+#include <sys/stat.h>
+#include <string.h>
+#include <minix/com.h>
+#include <minix/callnr.h>
+#include "buf.h"
+#include "inode.h"
+#include <minix/vfsif.h>
+
+/*===========================================================================*
+ * fs_ftrunc *
+ *===========================================================================*/
+PUBLIC int fs_ftrunc(void)
+{
+ struct inode *rip;
+ off_t start, end;
+ int r;
+ ino_t inumb;
+
+ inumb = fs_m_in.REQ_INODE_NR;
+
+ if( (rip = find_inode(inumb)) == NIL_INODE) return(EINVAL);
+
+ start = fs_m_in.REQ_TRC_START_LO;
+ end = fs_m_in.REQ_TRC_END_LO;
+
+ return truncate_inode(rip, start);
+}
+
+
+/*===========================================================================*
+ * truncate_inode *
+ *===========================================================================*/
+PUBLIC 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.
+ */
+ int scale;
+
+ /* 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"
+#include <assert.h>
+#include <minix/dmap.h>
+#include <minix/endpoint.h>
+#include <minix/vfsif.h>
+#include "buf.h"
+#include "inode.h"
+#include "drivers.h"
+
+FORWARD _PROTOTYPE(void init_server, (void) );
+FORWARD _PROTOTYPE(void get_work, (message *m_in) );
+
+/*===========================================================================*
+ * main *
+ *===========================================================================*/
+PUBLIC int main(int argc, char *argv[])
+{
+/* This is the main routine of this service. The main loop consists of
+ * three major activities: getting new work, processing the work, and
+ * sending the reply. The loop never terminates, unless a panic occurs.
+ */
+ int error, ind;
+ message m;
+
+ /* Initialize the server, then go to work. */
+ init_server();
+
+ while(!exitsignaled || busy) {
+ endpoint_t src;
+
+ /* Wait for request message. */
+ get_work(&fs_m_in);
+
+ src = fs_m_in.m_source;
+ error = OK;
+ caller_uid = -1; /* To trap errors */
+ caller_gid = -1;
+
+ if (src == PM_PROC_NR) continue; /* Exit signal */
+ assert(src == VFS_PROC_NR); /* Otherwise this must be VFS talking */
+ req_nr = fs_m_in.m_type;
+ if (req_nr < VFS_BASE) {
+ fs_m_in.m_type += VFS_BASE;
+ req_nr = fs_m_in.m_type;
+ }
+ ind = req_nr - VFS_BASE;
+
+ if (ind < 0 || ind >= NREQS) {
+ printf("mfs: bad request %d\n", req_nr);
+ printf("ind = %d\n", ind);
+ error = EINVAL;
+ } else {
+ error = (*fs_call_vec[ind])();
+ }
+
+ fs_m_out.m_type = error;
+ reply(src, &fs_m_out);
+
+ }
+ return(OK);
+}
+
+
+/*===========================================================================*
+ * init_server *
+ *===========================================================================*/
+PRIVATE void init_server(void)
+{
+ int i;
+
+ /* Initialize main loop parameters. */
+ exitsignaled = 0; /* No exit request seen yet. */
+ busy = 0; /* Server is not 'busy' (i.e., inodes in use). */
+
+ /* Init inode table */
+ for (i = 0; i < NR_INODES; ++i) {
+ inode[i].i_count = 0;
+ cch[i] = 0;
+ }
+
+ init_inode_cache();
+
+ /* Init driver mapping */
+ for (i = 0; i < NR_DEVICES; ++i)
+ driver_endpoints[i].driver_e = NONE;
+
+ SELF_E = getprocnr();
+ buf_pool();
+}
+
+
+/*===========================================================================*
+ * get_work *
+ *===========================================================================*/
+PRIVATE void get_work(m_in)
+message *m_in; /* pointer to message */
+{
+ int r, srcok = 0;
+ endpoint_t src;
+
+ do {
+ if ((r = receive(ANY, m_in)) != OK) /* wait for message */
+ panic("PFS","receive failed", r);
+ src = fs_m_in.m_source;
+
+ if (src != VFS_PROC_NR) {
+ if(src == PM_PROC_NR) {
+ if(is_notify(fs_m_in.m_type)) {
+ exitsignaled = 1; /* Normal exit request. */
+ srcok = 1;
+ } else
+ printf("PFS: unexpected message from PM\n");
+ } else
+ printf("PFS: unexpected source %d\n", src);
+ } else if(src == VFS_PROC_NR) {
+ srcok = 1; /* Normal FS request. */
+ } else
+ printf("PFS: unexpected source %d\n", src);
+ } while(!srcok);
+
+ assert( src == VFS_PROC_NR ||
+ (src == PM_PROC_NR && is_notify(fs_m_in.m_type))
+ );
+}
+
+
+/*===========================================================================*
+ * reply *
+ *===========================================================================*/
+PUBLIC void reply(who, m_out)
+int who;
+message *m_out; /* report result */
+{
+ if (OK != send(who, m_out)) /* send the message */
+ printf("PFS(%d) was unable to send reply\n", SELF_E);
+}
+
--- /dev/null
+#include "fs.h"
+#include <fcntl.h>
+#include <minix/vfsif.h>
+#include "buf.h"
+#include "inode.h"
+
+
+/*===========================================================================*
+ * fs_sync *
+ *===========================================================================*/
+PUBLIC int fs_sync()
+{
+/* Perform the sync() system call. No-op on this FS. */
+
+ return(OK); /* sync() can't fail */
+}
+
+
--- /dev/null
+#include "fs.h"
+#include <sys/stat.h>
+#include <unistd.h>
+#include <minix/callnr.h>
+#include <minix/com.h>
+#include "buf.h"
+#include "inode.h"
+#include <minix/vfsif.h>
+
+
+/*===========================================================================*
+ * fs_newnode *
+ *===========================================================================*/
+PUBLIC int fs_newnode()
+{
+ register int r = OK;
+ mode_t bits;
+ struct inode *rip;
+ dev_t dev;
+
+ caller_uid = fs_m_in.REQ_UID;
+ caller_gid = fs_m_in.REQ_GID;
+ bits = fs_m_in.REQ_MODE;
+ dev = fs_m_in.REQ_DEV;
+
+ /* Try to allocate the inode */
+ if( (rip = alloc_inode(dev, bits) ) == NIL_INODE) return(err_code);
+
+ if (bits & S_IFMT != S_IFIFO) {
+ r = EIO; /* We only support pipes */
+ } else if ((get_block(dev, rip->i_num)) == NIL_BUF)
+ r = EIO;
+
+ if (r != OK) {
+ free_inode(rip);
+ } else {
+ /* Fill in the fields of the response message */
+ fs_m_out.RES_INODE_NR = rip->i_num;
+ fs_m_out.RES_MODE = rip->i_mode;
+ fs_m_out.RES_FILE_SIZE_LO = rip->i_size;
+ fs_m_out.RES_UID = rip->i_uid;
+ fs_m_out.RES_GID = rip->i_gid;
+ fs_m_out.RES_DEV = dev;
+ }
+
+ return(r);
+}
+
--- /dev/null
+/* Function prototypes. */
+
+/* Structs used in prototypes must be declared as such first. */
+struct buf;
+struct filp;
+struct inode;
+
+/* buffer.c */
+_PROTOTYPE( struct buf *get_block, (Dev_t dev, ino_t inum) );
+_PROTOTYPE( struct buf *new_block, (Dev_t dev, ino_t inum) );
+_PROTOTYPE( void put_block, (Dev_t dev, ino_t inum) );
+
+/* cache.c */
+_PROTOTYPE( void buf_pool, (void) );
+
+/* inode.c */
+_PROTOTYPE( struct inode *alloc_inode, (dev_t dev, mode_t mode) );
+_PROTOTYPE( void dup_inode, (struct inode *ip) );
+_PROTOTYPE( struct inode *find_inode, (int numb) );
+_PROTOTYPE( void free_inode, (struct inode *rip) );
+_PROTOTYPE( int fs_putnode, (void) );
+_PROTOTYPE( void init_inode_cache, (void) );
+_PROTOTYPE( struct inode *get_inode, (Dev_t dev, int numb) );
+_PROTOTYPE( void put_inode, (struct inode *rip) );
+_PROTOTYPE( void update_times, (struct inode *rip) );
+_PROTOTYPE( void wipe_inode, (struct inode *rip) );
+
+/* link.c */
+_PROTOTYPE( int fs_ftrunc, (void) );
+_PROTOTYPE( int truncate_inode, (struct inode *rip, off_t newsize) );
+
+
+/* main.c */
+_PROTOTYPE( void reply, (int who, message *m_out) );
+
+/* misc.c */
+_PROTOTYPE( int fs_sync, (void) );
+
+/* open.c */
+_PROTOTYPE( int fs_newnode, (void) );
+
+/* read.c */
+_PROTOTYPE( int fs_readwrite, (void) );
+_PROTOTYPE( block_t read_map, (struct inode *rip, off_t pos) );
+_PROTOTYPE( int read_write, (int rw_flag) );
+
+/* utility.c */
+_PROTOTYPE( time_t clock_time, (void) );
+_PROTOTYPE( int no_sys, (void) );
+
+/* stadir.c */
+_PROTOTYPE( int fs_stat, (void) );
+
+/* super.c */
+_PROTOTYPE( bit_t alloc_bit, (void) );
+_PROTOTYPE( void free_bit, (bit_t bit_returned) );
--- /dev/null
+#include "fs.h"
+#include "buf.h"
+#include <minix/com.h>
+#include <string.h>
+#include <minix/u64.h>
+#include "inode.h"
+
+
+/*===========================================================================*
+ * fs_readwrite *
+ *===========================================================================*/
+PUBLIC int fs_readwrite(void)
+{
+ int r, rw_flag;
+ block_t b;
+ struct buf *bp;
+ cp_grant_id_t gid;
+ off_t position, f_size;
+ unsigned int nrbytes, cum_io;
+ mode_t mode_word;
+ struct inode *rip;
+ ino_t inumb;
+
+ r = OK;
+ cum_io = 0;
+ inumb = fs_m_in.REQ_INODE_NR;
+ rw_flag = (fs_m_in.m_type == REQ_READ ? READING : WRITING);
+#if 0
+ printf("PFS: going to %s inode %d\n", (rw_flag == READING? "read from": "write to"), inumb);
+#endif
+
+ /* Find the inode referred */
+ if ((rip = find_inode(inumb)) == NIL_INODE) return(EINVAL);
+
+ mode_word = rip->i_mode & I_TYPE;
+ if (mode_word != I_NAMED_PIPE) return(EIO);
+ f_size = rip->i_size;
+
+ /* Get the values from the request message */
+ rw_flag = (fs_m_in.m_type == REQ_READ ? READING : WRITING);
+ gid = fs_m_in.REQ_GRANT;
+ position = fs_m_in.REQ_SEEK_POS_LO;
+ nrbytes = (unsigned) fs_m_in.REQ_NBYTES;
+
+ if (rw_flag == WRITING) {
+ /* Check in advance to see if file will grow too big. */
+ if (position > PIPE_BUF - nrbytes) return(EFBIG);
+ }
+
+ /* Mark inode in use */
+ if ((get_inode(rip->i_dev, rip->i_num)) == NIL_INODE) return(err_code);
+ if ((bp = get_block(rip->i_dev, rip->i_num)) == NIL_BUF) return(err_code);
+
+ if (rw_flag == READING) {
+ /* Copy a chunk from the block buffer to user space. */
+ r = sys_safecopyto(FS_PROC_NR, gid, 0,
+ (vir_bytes) (bp->b_data+position), (phys_bytes) nrbytes, D);
+ } else {
+ /* Copy a chunk from user space to the block buffer. */
+ r = sys_safecopyfrom(FS_PROC_NR, gid, 0,
+ (vir_bytes) (bp->b_data+position), (phys_bytes) nrbytes, D);
+ }
+
+ if (r == OK) {
+ position += nrbytes; /* Update position */
+ cum_io += nrbytes;
+ }
+
+ fs_m_out.RES_SEEK_POS_LO = position; /* It might change later and the VFS
+ has to know this value */
+
+ /* On write, update file size and access time. */
+ if (rw_flag == WRITING) {
+ if (position > f_size) rip->i_size = position;
+ } else {
+ if(position >= rip->i_size) {
+ /* All data in the pipe is read, so reset pipe pointers */
+ rip->i_size = 0; /* no data left */
+ position = 0; /* reset reader(s) */
+ }
+ }
+
+ bp->b_bytes = position;
+ if (rw_flag == READING) rip->i_update |= ATIME;
+ if (rw_flag == WRITING) rip->i_update |= CTIME | MTIME;
+ fs_m_out.RES_NBYTES = cum_io;
+ put_inode(rip);
+ put_block(rip->i_dev, rip->i_num);
+
+ return(r);
+}
+
--- /dev/null
+#include "fs.h"
+#include "inode.h"
+#include <sys/stat.h>
+
+FORWARD _PROTOTYPE( int stat_inode, (struct inode *rip, int who_e,
+ cp_grant_id_t gid) );
+
+
+/*===========================================================================*
+ * stat_inode *
+ *===========================================================================*/
+PRIVATE int stat_inode(rip, who_e, gid)
+register struct inode *rip; /* pointer to inode to stat */
+int who_e; /* Caller endpoint */
+cp_grant_id_t gid; /* grant for the stat buf */
+{
+/* Common code for stat and fstat system calls. */
+
+ struct stat statbuf;
+ int r, s;
+
+ /* Update the atime, ctime, and mtime fields in the inode, if need be. */
+ if (rip->i_update) update_times(rip);
+
+ 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 = rip->i_gid;
+ statbuf.st_rdev = (dev_t) 0;
+ statbuf.st_size = rip->i_size;
+ 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;
+
+ /* Copy the struct to user space. */
+ r = sys_safecopyto(who_e, gid, 0, (vir_bytes) &statbuf,
+ (phys_bytes) sizeof(statbuf), D);
+
+ return(r);
+}
+
+
+/*===========================================================================*
+ * fs_stat *
+ *===========================================================================*/
+PUBLIC int fs_stat()
+{
+ register int r; /* return value */
+ register struct inode *rip; /* target inode */
+
+ if( (rip = find_inode(fs_m_in.REQ_INODE_NR)) == NIL_INODE) return(EINVAL);
+ get_inode(rip->i_dev, rip->i_num); /* mark inode in use */
+ r = stat_inode(rip, fs_m_in.m_source, fs_m_in.REQ_GRANT);
+ put_inode(rip); /* release the inode */
+ return(r);
+}
+
--- /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"
+#include <string.h>
+#include <minix/com.h>
+#include <minix/u64.h>
+#include "buf.h"
+#include "inode.h"
+#include "const.h"
+
+
+/*===========================================================================*
+ * alloc_bit *
+ *===========================================================================*/
+PUBLIC bit_t alloc_bit(void)
+{
+/* Allocate a bit from a bit map and return its bit number. */
+ bitchunk_t *wptr, *wlim;
+ bit_t b;
+ int i, bcount;
+
+ bcount = FS_BITMAP_CHUNKS(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 = (wptr - &inodemap[0]) * FS_BITCHUNK_BITS + i;
+
+ /* Don't allocate bits beyond end of map. */
+ if (b >= 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 *
+ *===========================================================================*/
+PUBLIC 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 = bit_returned / FS_BITCHUNK_BITS;
+ bit = bit_returned % FS_BITCHUNK_BITS;
+
+ /* Unset bit */
+ k = &inodemap[word];
+ mask = 1 << bit;
+ *k &= ~mask;
+
+ busy--; /* One inode less in use. */
+}
+
+
--- /dev/null
+
+/* This file contains the table used to map system call numbers onto the
+ * routines that perform them.
+ */
+
+#define _TABLE
+
+#include "fs.h"
+#include <minix/callnr.h>
+#include <minix/com.h>
+#include "inode.h"
+#include "buf.h"
+#include "drivers.h"
+
+PUBLIC _PROTOTYPE (int (*fs_call_vec[]), (void) ) = {
+ no_sys, /* 0 not used */
+ no_sys, /* 1 */
+ fs_putnode, /* 2 */
+ no_sys, /* 3 */
+ fs_ftrunc, /* 4 */
+ no_sys, /* 5 */
+ no_sys, /* 6 */
+ no_sys, /* 7 */
+ fs_stat, /* 8 */
+ no_sys, /* 9 */
+ no_sys, /* 10 */
+ no_sys, /* 11 */
+ no_sys, /* 12 */
+ no_sys, /* 13 */
+ no_sys, /* 14 */
+ no_sys, /* 15 */
+ fs_sync, /* 16 */
+ no_sys, /* 17 */
+ no_sys, /* 18 */
+ fs_readwrite, /* 19 */
+ fs_readwrite, /* 20 */
+ no_sys, /* 21 */
+ no_sys, /* 22 */
+ no_sys, /* 23 */
+ no_sys, /* 24 */
+ no_sys, /* 25 */
+ no_sys, /* 26 */
+ no_sys, /* 27 */
+ no_sys, /* 28 */
+ fs_newnode, /* 29 */
+ no_sys, /* 30 */
+ no_sys, /* 31 */
+};
+
--- /dev/null
+
+struct buf {
+ /* Data portion of the buffer. */
+ union fsdata_u *bp;
+
+ /* 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 */
+ struct buf *b_hash; /* used to link bufs on hash chains */
+ block_t b_blocknr; /* block number of its (minor) device */
+ dev_t b_dev; /* major | minor device where block resides */
+ char b_dirt; /* CLEAN or DIRTY */
+ char b_count; /* number of users of this buffer */
+ int b_bytes; /* Number of bytes allocated in bp */
+};
+
--- /dev/null
+#include "fs.h"
+
+
+/*===========================================================================*
+ * no_sys *
+ *===========================================================================*/
+PUBLIC int no_sys()
+{
+/* Somebody has used an illegal system call number */
+ printf("no_sys: invalid call %d\n", req_nr);
+ return(EINVAL);
+}
+
+
+/*===========================================================================*
+ * clock_time *
+ *===========================================================================*/
+PUBLIC time_t clock_time()
+{
+/* This routine returns the time in seconds since 1.1.1970. MINIX is an
+ * astrophysically naive system that assumes the earth rotates at a constant
+ * rate and that such things as leap seconds do not exist.
+ */
+
+ int r;
+ clock_t uptime, boottime;
+
+ if ((r = getuptime2(&uptime,&boottime)) != OK)
+ panic(__FILE__,"clock_time: getuptme2 failed", r);
+
+ return( (time_t) (boottime + (uptime/sys_hz())));
+}
+