aboutsummaryrefslogtreecommitdiff
path: root/linux/fs
diff options
context:
space:
mode:
authorAndrew Lee <alee14498@protonmail.com>2021-08-15 00:16:45 -0400
committerAndrew Lee <alee14498@protonmail.com>2021-08-15 00:16:45 -0400
commit723428bebe3105ad3c3406e416402d1831b482c4 (patch)
treeff990e306163515973746ddfb261f29ba8765441 /linux/fs
downloadlinux-0.01-distro-723428bebe3105ad3c3406e416402d1831b482c4.tar.gz
linux-0.01-distro-723428bebe3105ad3c3406e416402d1831b482c4.tar.bz2
linux-0.01-distro-723428bebe3105ad3c3406e416402d1831b482c4.zip
Inital commit
Diffstat (limited to 'linux/fs')
-rw-r--r--linux/fs/Makefile95
-rw-r--r--linux/fs/bitmap.c158
-rw-r--r--linux/fs/block_dev.c86
-rw-r--r--linux/fs/buffer.c254
-rw-r--r--linux/fs/char_dev.c50
-rw-r--r--linux/fs/exec.c306
-rw-r--r--linux/fs/fcntl.c69
-rw-r--r--linux/fs/file_dev.c84
-rw-r--r--linux/fs/file_table.c3
-rw-r--r--linux/fs/inode.c288
-rw-r--r--linux/fs/ioctl.c40
-rw-r--r--linux/fs/namei.c678
-rw-r--r--linux/fs/open.c188
-rw-r--r--linux/fs/pipe.c92
-rw-r--r--linux/fs/read_write.c97
-rw-r--r--linux/fs/stat.c51
-rw-r--r--linux/fs/super.c102
-rw-r--r--linux/fs/truncate.c59
-rw-r--r--linux/fs/tty_ioctl.c166
19 files changed, 2866 insertions, 0 deletions
diff --git a/linux/fs/Makefile b/linux/fs/Makefile
new file mode 100644
index 0000000..24e7e4c
--- /dev/null
+++ b/linux/fs/Makefile
@@ -0,0 +1,95 @@
+AR =gar
+AS =gas
+CC =gcc
+LD =gld
+CFLAGS =-Wall -O -fstrength-reduce -fcombine-regs -fomit-frame-pointer \
+ -mstring-insns -nostdinc -I../include
+CPP =gcc -E -nostdinc -I../include
+
+.c.s:
+ $(CC) $(CFLAGS) \
+ -S -o $*.s $<
+.c.o:
+ $(CC) $(CFLAGS) \
+ -c -o $*.o $<
+.s.o:
+ $(AS) -o $*.o $<
+
+OBJS= open.o read_write.o inode.o file_table.o buffer.o super.o \
+ block_dev.o char_dev.o file_dev.o stat.o exec.o pipe.o namei.o \
+ bitmap.o fcntl.o ioctl.o tty_ioctl.o truncate.o
+
+fs.o: $(OBJS)
+ $(LD) -r -o fs.o $(OBJS)
+
+clean:
+ rm -f core *.o *.a tmp_make
+ for i in *.c;do rm -f `basename $$i .c`.s;done
+
+dep:
+ sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
+ (for i in *.c;do $(CPP) -M $$i;done) >> tmp_make
+ cp tmp_make Makefile
+
+### Dependencies:
+bitmap.o : bitmap.c ../include/string.h ../include/linux/sched.h \
+ ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+ ../include/linux/mm.h ../include/linux/kernel.h
+block_dev.o : block_dev.c ../include/errno.h ../include/linux/fs.h \
+ ../include/sys/types.h ../include/linux/kernel.h ../include/asm/segment.h
+buffer.o : buffer.c ../include/linux/config.h ../include/linux/sched.h \
+ ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+ ../include/linux/mm.h ../include/linux/kernel.h ../include/asm/system.h
+char_dev.o : char_dev.c ../include/errno.h ../include/linux/sched.h \
+ ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+ ../include/linux/mm.h ../include/linux/kernel.h
+exec.o : exec.c ../include/errno.h ../include/sys/stat.h \
+ ../include/sys/types.h ../include/a.out.h ../include/linux/fs.h \
+ ../include/linux/sched.h ../include/linux/head.h ../include/linux/mm.h \
+ ../include/linux/kernel.h ../include/asm/segment.h
+fcntl.o : fcntl.c ../include/string.h ../include/errno.h \
+ ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
+ ../include/sys/types.h ../include/linux/mm.h ../include/linux/kernel.h \
+ ../include/asm/segment.h ../include/fcntl.h ../include/sys/stat.h
+file_dev.o : file_dev.c ../include/errno.h ../include/fcntl.h \
+ ../include/sys/types.h ../include/linux/sched.h ../include/linux/head.h \
+ ../include/linux/fs.h ../include/linux/mm.h ../include/linux/kernel.h \
+ ../include/asm/segment.h
+file_table.o : file_table.c ../include/linux/fs.h ../include/sys/types.h
+inode.o : inode.c ../include/string.h ../include/linux/sched.h \
+ ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+ ../include/linux/mm.h ../include/linux/kernel.h ../include/asm/system.h
+ioctl.o : ioctl.c ../include/string.h ../include/errno.h \
+ ../include/sys/stat.h ../include/sys/types.h ../include/linux/sched.h \
+ ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h
+namei.o : namei.c ../include/linux/sched.h ../include/linux/head.h \
+ ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
+ ../include/linux/kernel.h ../include/asm/segment.h ../include/string.h \
+ ../include/fcntl.h ../include/errno.h ../include/const.h \
+ ../include/sys/stat.h
+open.o : open.c ../include/string.h ../include/errno.h ../include/fcntl.h \
+ ../include/sys/types.h ../include/utime.h ../include/sys/stat.h \
+ ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
+ ../include/linux/mm.h ../include/linux/tty.h ../include/termios.h \
+ ../include/linux/kernel.h ../include/asm/segment.h
+pipe.o : pipe.c ../include/signal.h ../include/sys/types.h \
+ ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
+ ../include/linux/mm.h ../include/asm/segment.h
+read_write.o : read_write.c ../include/sys/stat.h ../include/sys/types.h \
+ ../include/errno.h ../include/linux/kernel.h ../include/linux/sched.h \
+ ../include/linux/head.h ../include/linux/fs.h ../include/linux/mm.h \
+ ../include/asm/segment.h
+stat.o : stat.c ../include/errno.h ../include/sys/stat.h \
+ ../include/sys/types.h ../include/linux/fs.h ../include/linux/sched.h \
+ ../include/linux/head.h ../include/linux/mm.h ../include/linux/kernel.h \
+ ../include/asm/segment.h
+super.o : super.c ../include/linux/config.h ../include/linux/sched.h \
+ ../include/linux/head.h ../include/linux/fs.h ../include/sys/types.h \
+ ../include/linux/mm.h ../include/linux/kernel.h
+truncate.o : truncate.c ../include/linux/sched.h ../include/linux/head.h \
+ ../include/linux/fs.h ../include/sys/types.h ../include/linux/mm.h \
+ ../include/sys/stat.h
+tty_ioctl.o : tty_ioctl.c ../include/errno.h ../include/termios.h \
+ ../include/linux/sched.h ../include/linux/head.h ../include/linux/fs.h \
+ ../include/sys/types.h ../include/linux/mm.h ../include/linux/kernel.h \
+ ../include/linux/tty.h ../include/asm/segment.h ../include/asm/system.h
diff --git a/linux/fs/bitmap.c b/linux/fs/bitmap.c
new file mode 100644
index 0000000..49bb81b
--- /dev/null
+++ b/linux/fs/bitmap.c
@@ -0,0 +1,158 @@
+/* bitmap.c contains the code that handles the inode and block bitmaps */
+#include <string.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+
+#define clear_block(addr) \
+__asm__("cld\n\t" \
+ "rep\n\t" \
+ "stosl" \
+ ::"a" (0),"c" (BLOCK_SIZE/4),"D" ((long) (addr)):"cx","di")
+
+#define set_bit(nr,addr) ({\
+register int res __asm__("ax"); \
+__asm__("btsl %2,%3\n\tsetb %%al":"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
+res;})
+
+#define clear_bit(nr,addr) ({\
+register int res __asm__("ax"); \
+__asm__("btrl %2,%3\n\tsetnb %%al":"=a" (res):"0" (0),"r" (nr),"m" (*(addr))); \
+res;})
+
+#define find_first_zero(addr) ({ \
+int __res; \
+__asm__("cld\n" \
+ "1:\tlodsl\n\t" \
+ "notl %%eax\n\t" \
+ "bsfl %%eax,%%edx\n\t" \
+ "je 2f\n\t" \
+ "addl %%edx,%%ecx\n\t" \
+ "jmp 3f\n" \
+ "2:\taddl $32,%%ecx\n\t" \
+ "cmpl $8192,%%ecx\n\t" \
+ "jl 1b\n" \
+ "3:" \
+ :"=c" (__res):"c" (0),"S" (addr):"ax","dx","si"); \
+__res;})
+
+void free_block(int dev, int block)
+{
+ struct super_block * sb;
+ struct buffer_head * bh;
+
+ if (!(sb = get_super(dev)))
+ panic("trying to free block on nonexistent device");
+ if (block < sb->s_firstdatazone || block >= sb->s_nzones)
+ panic("trying to free block not in datazone");
+ bh = get_hash_table(dev,block);
+ if (bh) {
+ if (bh->b_count != 1) {
+ printk("trying to free block (%04x:%d), count=%d\n",
+ dev,block,bh->b_count);
+ return;
+ }
+ bh->b_dirt=0;
+ bh->b_uptodate=0;
+ brelse(bh);
+ }
+ block -= sb->s_firstdatazone - 1 ;
+ if (clear_bit(block&8191,sb->s_zmap[block/8192]->b_data)) {
+ printk("block (%04x:%d) ",dev,block+sb->s_firstdatazone-1);
+ panic("free_block: bit already cleared");
+ }
+ sb->s_zmap[block/8192]->b_dirt = 1;
+}
+
+int new_block(int dev)
+{
+ struct buffer_head * bh;
+ struct super_block * sb;
+ int i,j;
+
+ if (!(sb = get_super(dev)))
+ panic("trying to get new block from nonexistant device");
+ j = 8192;
+ for (i=0 ; i<8 ; i++)
+ if (bh=sb->s_zmap[i])
+ if ((j=find_first_zero(bh->b_data))<8192)
+ break;
+ if (i>=8 || !bh || j>=8192)
+ return 0;
+ if (set_bit(j,bh->b_data))
+ panic("new_block: bit already set");
+ bh->b_dirt = 1;
+ j += i*8192 + sb->s_firstdatazone-1;
+ if (j >= sb->s_nzones)
+ return 0;
+ if (!(bh=getblk(dev,j)))
+ panic("new_block: cannot get block");
+ if (bh->b_count != 1)
+ panic("new block: count is != 1");
+ clear_block(bh->b_data);
+ bh->b_uptodate = 1;
+ bh->b_dirt = 1;
+ brelse(bh);
+ return j;
+}
+
+void free_inode(struct m_inode * inode)
+{
+ struct super_block * sb;
+ struct buffer_head * bh;
+
+ if (!inode)
+ return;
+ if (!inode->i_dev) {
+ memset(inode,0,sizeof(*inode));
+ return;
+ }
+ if (inode->i_count>1) {
+ printk("trying to free inode with count=%d\n",inode->i_count);
+ panic("free_inode");
+ }
+ if (inode->i_nlinks)
+ panic("trying to free inode with links");
+ if (!(sb = get_super(inode->i_dev)))
+ panic("trying to free inode on nonexistent device");
+ if (inode->i_num < 1 || inode->i_num > sb->s_ninodes)
+ panic("trying to free inode 0 or nonexistant inode");
+ if (!(bh=sb->s_imap[inode->i_num>>13]))
+ panic("nonexistent imap in superblock");
+ if (clear_bit(inode->i_num&8191,bh->b_data))
+ panic("free_inode: bit already cleared");
+ bh->b_dirt = 1;
+ memset(inode,0,sizeof(*inode));
+}
+
+struct m_inode * new_inode(int dev)
+{
+ struct m_inode * inode;
+ struct super_block * sb;
+ struct buffer_head * bh;
+ int i,j;
+
+ if (!(inode=get_empty_inode()))
+ return NULL;
+ if (!(sb = get_super(dev)))
+ panic("new_inode with unknown device");
+ j = 8192;
+ for (i=0 ; i<8 ; i++)
+ if (bh=sb->s_imap[i])
+ if ((j=find_first_zero(bh->b_data))<8192)
+ break;
+ if (!bh || j >= 8192 || j+i*8192 > sb->s_ninodes) {
+ iput(inode);
+ return NULL;
+ }
+ if (set_bit(j,bh->b_data))
+ panic("new_inode: bit already set");
+ bh->b_dirt = 1;
+ inode->i_count=1;
+ inode->i_nlinks=1;
+ inode->i_dev=dev;
+ inode->i_dirt=1;
+ inode->i_num = j + i*8192;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ return inode;
+}
diff --git a/linux/fs/block_dev.c b/linux/fs/block_dev.c
new file mode 100644
index 0000000..7bbae6a
--- /dev/null
+++ b/linux/fs/block_dev.c
@@ -0,0 +1,86 @@
+#include <errno.h>
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+#define NR_BLK_DEV ((sizeof (rd_blk))/(sizeof (rd_blk[0])))
+
+int block_write(int dev, long * pos, char * buf, int count)
+{
+ int block = *pos / BLOCK_SIZE;
+ int offset = *pos % BLOCK_SIZE;
+ int chars;
+ int written = 0;
+ struct buffer_head * bh;
+ register char * p;
+
+ while (count>0) {
+ bh = bread(dev,block);
+ if (!bh)
+ return written?written:-EIO;
+ chars = (count<BLOCK_SIZE) ? count : BLOCK_SIZE;
+ p = offset + bh->b_data;
+ offset = 0;
+ block++;
+ *pos += chars;
+ written += chars;
+ count -= chars;
+ while (chars-->0)
+ *(p++) = get_fs_byte(buf++);
+ bh->b_dirt = 1;
+ brelse(bh);
+ }
+ return written;
+}
+
+int block_read(int dev, unsigned long * pos, char * buf, int count)
+{
+ int block = *pos / BLOCK_SIZE;
+ int offset = *pos % BLOCK_SIZE;
+ int chars;
+ int read = 0;
+ struct buffer_head * bh;
+ register char * p;
+
+ while (count>0) {
+ bh = bread(dev,block);
+ if (!bh)
+ return read?read:-EIO;
+ chars = (count<BLOCK_SIZE) ? count : BLOCK_SIZE;
+ p = offset + bh->b_data;
+ offset = 0;
+ block++;
+ *pos += chars;
+ read += chars;
+ count -= chars;
+ while (chars-->0)
+ put_fs_byte(*(p++),buf++);
+ bh->b_dirt = 1;
+ brelse(bh);
+ }
+ return read;
+}
+
+extern void rw_hd(int rw, struct buffer_head * bh);
+
+typedef void (*blk_fn)(int rw, struct buffer_head * bh);
+
+static blk_fn rd_blk[]={
+ NULL, /* nodev */
+ NULL, /* dev mem */
+ NULL, /* dev fd */
+ rw_hd, /* dev hd */
+ NULL, /* dev ttyx */
+ NULL, /* dev tty */
+ NULL}; /* dev lp */
+
+void ll_rw_block(int rw, struct buffer_head * bh)
+{
+ blk_fn blk_addr;
+ unsigned int major;
+
+ if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV || !(blk_addr=rd_blk[major]))
+ panic("Trying to read nonexistent block-device");
+ blk_addr(rw, bh);
+}
diff --git a/linux/fs/buffer.c b/linux/fs/buffer.c
new file mode 100644
index 0000000..312ab17
--- /dev/null
+++ b/linux/fs/buffer.c
@@ -0,0 +1,254 @@
+/*
+ * 'buffer.c' implements the buffer-cache functions. Race-conditions have
+ * been avoided by NEVER letting a interrupt change a buffer (except for the
+ * data, of course), but instead letting the caller do it. NOTE! As interrupts
+ * can wake up a caller, some cli-sti sequences are needed to check for
+ * sleep-on-calls. These should be extremely quick, though (I hope).
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+
+#if (BUFFER_END & 0xfff)
+#error "Bad BUFFER_END value"
+#endif
+
+#if (BUFFER_END > 0xA0000 && BUFFER_END <= 0x100000)
+#error "Bad BUFFER_END value"
+#endif
+
+extern int end;
+struct buffer_head * start_buffer = (struct buffer_head *) &end;
+struct buffer_head * hash_table[NR_HASH];
+static struct buffer_head * free_list;
+static struct task_struct * buffer_wait = NULL;
+int NR_BUFFERS = 0;
+
+static inline void wait_on_buffer(struct buffer_head * bh)
+{
+ cli();
+ while (bh->b_lock)
+ sleep_on(&bh->b_wait);
+ sti();
+}
+
+int sys_sync(void)
+{
+ int i;
+ struct buffer_head * bh;
+
+ sync_inodes(); /* write out inodes into buffers */
+ bh = start_buffer;
+ for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
+ wait_on_buffer(bh);
+ if (bh->b_dirt)
+ ll_rw_block(WRITE,bh);
+ }
+ return 0;
+}
+
+static int sync_dev(int dev)
+{
+ int i;
+ struct buffer_head * bh;
+
+ bh = start_buffer;
+ for (i=0 ; i<NR_BUFFERS ; i++,bh++) {
+ if (bh->b_dev != dev)
+ continue;
+ wait_on_buffer(bh);
+ if (bh->b_dirt)
+ ll_rw_block(WRITE,bh);
+ }
+ return 0;
+}
+
+#define _hashfn(dev,block) (((unsigned)(dev^block))%NR_HASH)
+#define hash(dev,block) hash_table[_hashfn(dev,block)]
+
+static inline void remove_from_queues(struct buffer_head * bh)
+{
+/* remove from hash-queue */
+ if (bh->b_next)
+ bh->b_next->b_prev = bh->b_prev;
+ if (bh->b_prev)
+ bh->b_prev->b_next = bh->b_next;
+ if (hash(bh->b_dev,bh->b_blocknr) == bh)
+ hash(bh->b_dev,bh->b_blocknr) = bh->b_next;
+/* remove from free list */
+ if (!(bh->b_prev_free) || !(bh->b_next_free))
+ panic("Free block list corrupted");
+ bh->b_prev_free->b_next_free = bh->b_next_free;
+ bh->b_next_free->b_prev_free = bh->b_prev_free;
+ if (free_list == bh)
+ free_list = bh->b_next_free;
+}
+
+static inline void insert_into_queues(struct buffer_head * bh)
+{
+/* put at end of free list */
+ bh->b_next_free = free_list;
+ bh->b_prev_free = free_list->b_prev_free;
+ free_list->b_prev_free->b_next_free = bh;
+ free_list->b_prev_free = bh;
+/* put the buffer in new hash-queue if it has a device */
+ bh->b_prev = NULL;
+ bh->b_next = NULL;
+ if (!bh->b_dev)
+ return;
+ bh->b_next = hash(bh->b_dev,bh->b_blocknr);
+ hash(bh->b_dev,bh->b_blocknr) = bh;
+ bh->b_next->b_prev = bh;
+}
+
+static struct buffer_head * find_buffer(int dev, int block)
+{
+ struct buffer_head * tmp;
+
+ for (tmp = hash(dev,block) ; tmp != NULL ; tmp = tmp->b_next)
+ if (tmp->b_dev==dev && tmp->b_blocknr==block)
+ return tmp;
+ return NULL;
+}
+
+/*
+ * Why like this, I hear you say... The reason is race-conditions.
+ * As we don't lock buffers (unless we are readint them, that is),
+ * something might happen to it while we sleep (ie a read-error
+ * will force it bad). This shouldn't really happen currently, but
+ * the code is ready.
+ */
+struct buffer_head * get_hash_table(int dev, int block)
+{
+ struct buffer_head * bh;
+
+repeat:
+ if (!(bh=find_buffer(dev,block)))
+ return NULL;
+ bh->b_count++;
+ wait_on_buffer(bh);
+ if (bh->b_dev != dev || bh->b_blocknr != block) {
+ brelse(bh);
+ goto repeat;
+ }
+ return bh;
+}
+
+/*
+ * Ok, this is getblk, and it isn't very clear, again to hinder
+ * race-conditions. Most of the code is seldom used, (ie repeating),
+ * so it should be much more efficient than it looks.
+ */
+struct buffer_head * getblk(int dev,int block)
+{
+ struct buffer_head * tmp;
+
+repeat:
+ if (tmp=get_hash_table(dev,block))
+ return tmp;
+ tmp = free_list;
+ do {
+ if (!tmp->b_count) {
+ wait_on_buffer(tmp); /* we still have to wait */
+ if (!tmp->b_count) /* on it, it might be dirty */
+ break;
+ }
+ tmp = tmp->b_next_free;
+ } while (tmp != free_list || (tmp=NULL));
+ /* Kids, don't try THIS at home ^^^^^. Magic */
+ if (!tmp) {
+ printk("Sleeping on free buffer ..");
+ sleep_on(&buffer_wait);
+ printk("ok\n");
+ goto repeat;
+ }
+ tmp->b_count++;
+ remove_from_queues(tmp);
+/*
+ * Now, when we know nobody can get to this node (as it's removed from the
+ * free list), we write it out. We can sleep here without fear of race-
+ * conditions.
+ */
+ if (tmp->b_dirt)
+ sync_dev(tmp->b_dev);
+/* update buffer contents */
+ tmp->b_dev=dev;
+ tmp->b_blocknr=block;
+ tmp->b_dirt=0;
+ tmp->b_uptodate=0;
+/* NOTE!! While we possibly slept in sync_dev(), somebody else might have
+ * added "this" block already, so check for that. Thank God for goto's.
+ */
+ if (find_buffer(dev,block)) {
+ tmp->b_dev=0; /* ok, someone else has beaten us */
+ tmp->b_blocknr=0; /* to it - free this block and */
+ tmp->b_count=0; /* try again */
+ insert_into_queues(tmp);
+ goto repeat;
+ }
+/* and then insert into correct position */
+ insert_into_queues(tmp);
+ return tmp;
+}
+
+void brelse(struct buffer_head * buf)
+{
+ if (!buf)
+ return;
+ wait_on_buffer(buf);
+ if (!(buf->b_count--))
+ panic("Trying to free free buffer");
+ wake_up(&buffer_wait);
+}
+
+/*
+ * bread() reads a specified block and returns the buffer that contains
+ * it. It returns NULL if the block was unreadable.
+ */
+struct buffer_head * bread(int dev,int block)
+{
+ struct buffer_head * bh;
+
+ if (!(bh=getblk(dev,block)))
+ panic("bread: getblk returned NULL\n");
+ if (bh->b_uptodate)
+ return bh;
+ ll_rw_block(READ,bh);
+ if (bh->b_uptodate)
+ return bh;
+ brelse(bh);
+ return (NULL);
+}
+
+void buffer_init(void)
+{
+ struct buffer_head * h = start_buffer;
+ void * b = (void *) BUFFER_END;
+ int i;
+
+ while ( (b -= BLOCK_SIZE) >= ((void *) (h+1)) ) {
+ h->b_dev = 0;
+ h->b_dirt = 0;
+ h->b_count = 0;
+ h->b_lock = 0;
+ h->b_uptodate = 0;
+ h->b_wait = NULL;
+ h->b_next = NULL;
+ h->b_prev = NULL;
+ h->b_data = (char *) b;
+ h->b_prev_free = h-1;
+ h->b_next_free = h+1;
+ h++;
+ NR_BUFFERS++;
+ if (b == (void *) 0x100000)
+ b = (void *) 0xA0000;
+ }
+ h--;
+ free_list = start_buffer;
+ free_list->b_prev_free = h;
+ h->b_next_free = free_list;
+ for (i=0;i<NR_HASH;i++)
+ hash_table[i]=NULL;
+}
diff --git a/linux/fs/char_dev.c b/linux/fs/char_dev.c
new file mode 100644
index 0000000..e974242
--- /dev/null
+++ b/linux/fs/char_dev.c
@@ -0,0 +1,50 @@
+#include <errno.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+
+extern int tty_read(unsigned minor,char * buf,int count);
+extern int tty_write(unsigned minor,char * buf,int count);
+
+static int rw_ttyx(int rw,unsigned minor,char * buf,int count);
+static int rw_tty(int rw,unsigned minor,char * buf,int count);
+
+typedef (*crw_ptr)(int rw,unsigned minor,char * buf,int count);
+
+#define NRDEVS ((sizeof (crw_table))/(sizeof (crw_ptr)))
+
+static crw_ptr crw_table[]={
+ NULL, /* nodev */
+ NULL, /* /dev/mem */
+ NULL, /* /dev/fd */
+ NULL, /* /dev/hd */
+ rw_ttyx, /* /dev/ttyx */
+ rw_tty, /* /dev/tty */
+ NULL, /* /dev/lp */
+ NULL}; /* unnamed pipes */
+
+static int rw_ttyx(int rw,unsigned minor,char * buf,int count)
+{
+ return ((rw==READ)?tty_read(minor,buf,count):
+ tty_write(minor,buf,count));
+}
+
+static int rw_tty(int rw,unsigned minor,char * buf,int count)
+{
+ if (current->tty<0)
+ return -EPERM;
+ return rw_ttyx(rw,current->tty,buf,count);
+}
+
+int rw_char(int rw,int dev, char * buf, int count)
+{
+ crw_ptr call_addr;
+
+ if (MAJOR(dev)>=NRDEVS)
+ panic("rw_char: dev>NRDEV");
+ if (!(call_addr=crw_table[MAJOR(dev)])) {
+ printk("dev: %04x\n",dev);
+ panic("Trying to r/w from/to nonexistent character device");
+ }
+ return call_addr(rw,MINOR(dev),buf,count);
+}
diff --git a/linux/fs/exec.c b/linux/fs/exec.c
new file mode 100644
index 0000000..e9d5058
--- /dev/null
+++ b/linux/fs/exec.c
@@ -0,0 +1,306 @@
+#include <errno.h>
+#include <sys/stat.h>
+#include <a.out.h>
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <asm/segment.h>
+
+extern int sys_exit(int exit_code);
+extern int sys_close(int fd);
+
+/*
+ * MAX_ARG_PAGES defines the number of pages allocated for arguments
+ * and envelope for the new program. 32 should suffice, this gives
+ * a maximum env+arg of 128kB !
+ */
+#define MAX_ARG_PAGES 32
+
+#define cp_block(from,to) \
+__asm__("pushl $0x10\n\t" \
+ "pushl $0x17\n\t" \
+ "pop %%es\n\t" \
+ "cld\n\t" \
+ "rep\n\t" \
+ "movsl\n\t" \
+ "pop %%es" \
+ ::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \
+ :"cx","di","si")
+
+/*
+ * read_head() reads blocks 1-6 (not 0). Block 0 has already been
+ * read for header information.
+ */
+int read_head(struct m_inode * inode,int blocks)
+{
+ struct buffer_head * bh;
+ int count;
+
+ if (blocks>6)
+ blocks=6;
+ for(count = 0 ; count<blocks ; count++) {
+ if (!inode->i_zone[count+1])
+ continue;
+ if (!(bh=bread(inode->i_dev,inode->i_zone[count+1])))
+ return -1;
+ cp_block(bh->b_data,count*BLOCK_SIZE);
+ brelse(bh);
+ }
+ return 0;
+}
+
+int read_ind(int dev,int ind,long size,unsigned long offset)
+{
+ struct buffer_head * ih, * bh;
+ unsigned short * table,block;
+
+ if (size<=0)
+ panic("size<=0 in read_ind");
+ if (size>512*BLOCK_SIZE)
+ size=512*BLOCK_SIZE;
+ if (!ind)
+ return 0;
+ if (!(ih=bread(dev,ind)))
+ return -1;
+ table = (unsigned short *) ih->b_data;
+ while (size>0) {
+ if (block=*(table++))
+ if (!(bh=bread(dev,block))) {
+ brelse(ih);
+ return -1;
+ } else {
+ cp_block(bh->b_data,offset);
+ brelse(bh);
+ }
+ size -= BLOCK_SIZE;
+ offset += BLOCK_SIZE;
+ }
+ brelse(ih);
+ return 0;
+}
+
+/*
+ * read_area() reads an area into %fs:mem.
+ */
+int read_area(struct m_inode * inode,long size)
+{
+ struct buffer_head * dind;
+ unsigned short * table;
+ int i,count;
+
+ if ((i=read_head(inode,(size+BLOCK_SIZE-1)/BLOCK_SIZE)) ||
+ (size -= BLOCK_SIZE*6)<=0)
+ return i;
+ if ((i=read_ind(inode->i_dev,inode->i_zone[7],size,BLOCK_SIZE*6)) ||
+ (size -= BLOCK_SIZE*512)<=0)
+ return i;
+ if (!(i=inode->i_zone[8]))
+ return 0;
+ if (!(dind = bread(inode->i_dev,i)))
+ return -1;
+ table = (unsigned short *) dind->b_data;
+ for(count=0 ; count<512 ; count++)
+ if ((i=read_ind(inode->i_dev,*(table++),size,
+ BLOCK_SIZE*(518+count))) || (size -= BLOCK_SIZE*512)<=0)
+ return i;
+ panic("Impossibly long executable");
+}
+
+/*
+ * create_tables() parses the env- and arg-strings in new user
+ * memory and creates the pointer tables from them, and puts their
+ * addresses on the "stack", returning the new stack pointer value.
+ */
+static unsigned long * create_tables(char * p,int argc,int envc)
+{
+ unsigned long *argv,*envp;
+ unsigned long * sp;
+
+ sp = (unsigned long *) (0xfffffffc & (unsigned long) p);
+ sp -= envc+1;
+ envp = sp;
+ sp -= argc+1;
+ argv = sp;
+ put_fs_long((unsigned long)envp,--sp);
+ put_fs_long((unsigned long)argv,--sp);
+ put_fs_long((unsigned long)argc,--sp);
+ while (argc-->0) {
+ put_fs_long((unsigned long) p,argv++);
+ while (get_fs_byte(p++)) /* nothing */ ;
+ }
+ put_fs_long(0,argv);
+ while (envc-->0) {
+ put_fs_long((unsigned long) p,envp++);
+ while (get_fs_byte(p++)) /* nothing */ ;
+ }
+ put_fs_long(0,envp);
+ return sp;
+}
+
+/*
+ * count() counts the number of arguments/envelopes
+ */
+static int count(char ** argv)
+{
+ int i=0;
+ char ** tmp;
+
+ if (tmp = argv)
+ while (get_fs_long((unsigned long *) (tmp++)))
+ i++;
+
+ return i;
+}
+
+/*
+ * 'copy_string()' copies argument/envelope strings from user
+ * memory to free pages in kernel mem. These are in a format ready
+ * to be put directly into the top of new user memory.
+ */
+static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
+ unsigned long p)
+{
+ int len,i;
+ char *tmp;
+
+ while (argc-- > 0) {
+ if (!(tmp = (char *)get_fs_long(((unsigned long *) argv)+argc)))
+ panic("argc is wrong");
+ len=0; /* remember zero-padding */
+ do {
+ len++;
+ } while (get_fs_byte(tmp++));
+ if (p-len < 0) /* this shouldn't happen - 128kB */
+ return 0;
+ i = ((unsigned) (p-len)) >> 12;
+ while (i<MAX_ARG_PAGES && !page[i]) {
+ if (!(page[i]=get_free_page()))
+ return 0;
+ i++;
+ }
+ do {
+ --p;
+ if (!page[p/PAGE_SIZE])
+ panic("nonexistent page in exec.c");
+ ((char *) page[p/PAGE_SIZE])[p%PAGE_SIZE] =
+ get_fs_byte(--tmp);
+ } while (--len);
+ }
+ return p;
+}
+
+static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
+{
+ unsigned long code_limit,data_limit,code_base,data_base;
+ int i;
+
+ code_limit = text_size+PAGE_SIZE -1;
+ code_limit &= 0xFFFFF000;
+ data_limit = 0x4000000;
+ code_base = get_base(current->ldt[1]);
+ data_base = code_base;
+ set_base(current->ldt[1],code_base);
+ set_limit(current->ldt[1],code_limit);
+ set_base(current->ldt[2],data_base);
+ set_limit(current->ldt[2],data_limit);
+/* make sure fs points to the NEW data segment */
+ __asm__("pushl $0x17\n\tpop %%fs"::);
+ data_base += data_limit;
+ for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
+ data_base -= PAGE_SIZE;
+ if (page[i])
+ put_page(page[i],data_base);
+ }
+ return data_limit;
+}
+
+/*
+ * 'do_execve()' executes a new program.
+ */
+int do_execve(unsigned long * eip,long tmp,char * filename,
+ char ** argv, char ** envp)
+{
+ struct m_inode * inode;
+ struct buffer_head * bh;
+ struct exec ex;
+ unsigned long page[MAX_ARG_PAGES];
+ int i,argc,envc;
+ unsigned long p;
+
+ if ((0xffff & eip[1]) != 0x000f)
+ panic("execve called from supervisor mode");
+ for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
+ page[i]=0;
+ if (!(inode=namei(filename))) /* get executables inode */
+ return -ENOENT;
+ if (!S_ISREG(inode->i_mode)) { /* must be regular file */
+ iput(inode);
+ return -EACCES;
+ }
+ i = inode->i_mode;
+ if (current->uid && current->euid) {
+ if (current->euid == inode->i_uid)
+ i >>= 6;
+ else if (current->egid == inode->i_gid)
+ i >>= 3;
+ } else if (i & 0111)
+ i=1;
+ if (!(i & 1)) {
+ iput(inode);
+ return -ENOEXEC;
+ }
+ if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) {
+ iput(inode);
+ return -EACCES;
+ }
+ ex = *((struct exec *) bh->b_data); /* read exec-header */
+ brelse(bh);
+ if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||
+ ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||
+ inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
+ iput(inode);
+ return -ENOEXEC;
+ }
+ if (N_TXTOFF(ex) != BLOCK_SIZE)
+ panic("N_TXTOFF != BLOCK_SIZE. See a.out.h.");
+ argc = count(argv);
+ envc = count(envp);
+ p = copy_strings(envc,envp,page,PAGE_SIZE*MAX_ARG_PAGES-4);
+ p = copy_strings(argc,argv,page,p);
+ if (!p) {
+ for (i=0 ; i<MAX_ARG_PAGES ; i++)
+ free_page(page[i]);
+ iput(inode);
+ return -1;
+ }
+/* OK, This is the point of no return */
+ for (i=0 ; i<32 ; i++)
+ current->sig_fn[i] = NULL;
+ for (i=0 ; i<NR_OPEN ; i++)
+ if ((current->close_on_exec>>i)&1)
+ sys_close(i);
+ current->close_on_exec = 0;
+ free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
+ free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
+ if (last_task_used_math == current)
+ last_task_used_math = NULL;
+ current->used_math = 0;
+ p += change_ldt(ex.a_text,page)-MAX_ARG_PAGES*PAGE_SIZE;
+ p = (unsigned long) create_tables((char *)p,argc,envc);
+ current->brk = ex.a_bss +
+ (current->end_data = ex.a_data +
+ (current->end_code = ex.a_text));
+ current->start_stack = p & 0xfffff000;
+ i = read_area(inode,ex.a_text+ex.a_data);
+ iput(inode);
+ if (i<0)
+ sys_exit(-1);
+ i = ex.a_text+ex.a_data;
+ while (i&0xfff)
+ put_fs_byte(0,(char *) (i++));
+ eip[0] = ex.a_entry; /* eip, magic happens :-) */
+ eip[3] = p; /* stack pointer */
+ return 0;
+}
diff --git a/linux/fs/fcntl.c b/linux/fs/fcntl.c
new file mode 100644
index 0000000..169f376
--- /dev/null
+++ b/linux/fs/fcntl.c
@@ -0,0 +1,69 @@
+#include <string.h>
+#include <errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+extern int sys_close(int fd);
+
+static int dupfd(unsigned int fd, unsigned int arg)
+{
+ if (fd >= NR_OPEN || !current->filp[fd])
+ return -EBADF;
+ if (arg >= NR_OPEN)
+ return -EINVAL;
+ while (arg < NR_OPEN)
+ if (current->filp[arg])
+ arg++;
+ else
+ break;
+ if (arg >= NR_OPEN)
+ return -EMFILE;
+ current->close_on_exec &= ~(1<<arg);
+ (current->filp[arg] = current->filp[fd])->f_count++;
+ return arg;
+}
+
+int sys_dup2(unsigned int oldfd, unsigned int newfd)
+{
+ sys_close(newfd);
+ return dupfd(oldfd,newfd);
+}
+
+int sys_dup(unsigned int fildes)
+{
+ return dupfd(fildes,0);
+}
+
+int sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ struct file * filp;
+
+ if (fd >= NR_OPEN || !(filp = current->filp[fd]))
+ return -EBADF;
+ switch (cmd) {
+ case F_DUPFD:
+ return dupfd(fd,arg);
+ case F_GETFD:
+ return (current->close_on_exec>>fd)&1;
+ case F_SETFD:
+ if (arg&1)
+ current->close_on_exec |= (1<<fd);
+ else
+ current->close_on_exec &= ~(1<<fd);
+ return 0;
+ case F_GETFL:
+ return filp->f_flags;
+ case F_SETFL:
+ filp->f_flags &= ~(O_APPEND | O_NONBLOCK);
+ filp->f_flags |= arg & (O_APPEND | O_NONBLOCK);
+ return 0;
+ case F_GETLK: case F_SETLK: case F_SETLKW:
+ return -1;
+ default:
+ return -1;
+ }
+}
diff --git a/linux/fs/file_dev.c b/linux/fs/file_dev.c
new file mode 100644
index 0000000..da5d014
--- /dev/null
+++ b/linux/fs/file_dev.c
@@ -0,0 +1,84 @@
+#include <errno.h>
+#include <fcntl.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+int file_read(struct m_inode * inode, struct file * filp, char * buf, int count)
+{
+ int left,chars,nr;
+ struct buffer_head * bh;
+
+ if ((left=count)<=0)
+ return 0;
+ while (left) {
+ if (nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE)) {
+ if (!(bh=bread(inode->i_dev,nr)))
+ break;
+ } else
+ bh = NULL;
+ nr = filp->f_pos % BLOCK_SIZE;
+ chars = MIN( BLOCK_SIZE-nr , left );
+ filp->f_pos += chars;
+ left -= chars;
+ if (bh) {
+ char * p = nr + bh->b_data;
+ while (chars-->0)
+ put_fs_byte(*(p++),buf++);
+ brelse(bh);
+ } else {
+ while (chars-->0)
+ put_fs_byte(0,buf++);
+ }
+ }
+ inode->i_atime = CURRENT_TIME;
+ return (count-left)?(count-left):-ERROR;
+}
+
+int file_write(struct m_inode * inode, struct file * filp, char * buf, int count)
+{
+ off_t pos;
+ int block,c;
+ struct buffer_head * bh;
+ char * p;
+ int i=0;
+
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+ if (filp->f_flags & O_APPEND)
+ pos = inode->i_size;
+ else
+ pos = filp->f_pos;
+ while (i<count) {
+ if (!(block = create_block(inode,pos/BLOCK_SIZE)))
+ break;
+ if (!(bh=bread(inode->i_dev,block)))
+ break;
+ c = pos % BLOCK_SIZE;
+ p = c + bh->b_data;
+ bh->b_dirt = 1;
+ c = BLOCK_SIZE-c;
+ if (c > count-i) c = count-i;
+ pos += c;
+ if (pos > inode->i_size) {
+ inode->i_size = pos;
+ inode->i_dirt = 1;
+ }
+ i += c;
+ while (c-->0)
+ *(p++) = get_fs_byte(buf++);
+ brelse(bh);
+ }
+ inode->i_mtime = CURRENT_TIME;
+ if (!(filp->f_flags & O_APPEND)) {
+ filp->f_pos = pos;
+ inode->i_ctime = CURRENT_TIME;
+ }
+ return (i?i:-1);
+}
diff --git a/linux/fs/file_table.c b/linux/fs/file_table.c
new file mode 100644
index 0000000..ab7cebf
--- /dev/null
+++ b/linux/fs/file_table.c
@@ -0,0 +1,3 @@
+#include <linux/fs.h>
+
+struct file file_table[NR_FILE];
diff --git a/linux/fs/inode.c b/linux/fs/inode.c
new file mode 100644
index 0000000..d06ff90
--- /dev/null
+++ b/linux/fs/inode.c
@@ -0,0 +1,288 @@
+#include <string.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <asm/system.h>
+
+struct m_inode inode_table[NR_INODE]={{0,},};
+
+static void read_inode(struct m_inode * inode);
+static void write_inode(struct m_inode * inode);
+
+static inline void wait_on_inode(struct m_inode * inode)
+{
+ cli();
+ while (inode->i_lock)
+ sleep_on(&inode->i_wait);
+ sti();
+}
+
+static inline void lock_inode(struct m_inode * inode)
+{
+ cli();
+ while (inode->i_lock)
+ sleep_on(&inode->i_wait);
+ inode->i_lock=1;
+ sti();
+}
+
+static inline void unlock_inode(struct m_inode * inode)
+{
+ inode->i_lock=0;
+ wake_up(&inode->i_wait);
+}
+
+void sync_inodes(void)
+{
+ int i;
+ struct m_inode * inode;
+
+ inode = 0+inode_table;
+ for(i=0 ; i<NR_INODE ; i++,inode++) {
+ wait_on_inode(inode);
+ if (inode->i_dirt && !inode->i_pipe)
+ write_inode(inode);
+ }
+}
+
+static int _bmap(struct m_inode * inode,int block,int create)
+{
+ struct buffer_head * bh;
+ int i;
+
+ if (block<0)
+ panic("_bmap: block<0");
+ if (block >= 7+512+512*512)
+ panic("_bmap: block>big");
+ if (block<7) {
+ if (create && !inode->i_zone[block])
+ if (inode->i_zone[block]=new_block(inode->i_dev)) {
+ inode->i_ctime=CURRENT_TIME;
+ inode->i_dirt=1;
+ }
+ return inode->i_zone[block];
+ }
+ block -= 7;
+ if (block<512) {
+ if (create && !inode->i_zone[7])
+ if (inode->i_zone[7]=new_block(inode->i_dev)) {
+ inode->i_dirt=1;
+ inode->i_ctime=CURRENT_TIME;
+ }
+ if (!inode->i_zone[7])
+ return 0;
+ if (!(bh = bread(inode->i_dev,inode->i_zone[7])))
+ return 0;
+ i = ((unsigned short *) (bh->b_data))[block];
+ if (create && !i)
+ if (i=new_block(inode->i_dev)) {
+ ((unsigned short *) (bh->b_data))[block]=i;
+ bh->b_dirt=1;
+ }
+ brelse(bh);
+ return i;
+ }
+ block -= 512;
+ if (create && !inode->i_zone[8])
+ if (inode->i_zone[8]=new_block(inode->i_dev)) {
+ inode->i_dirt=1;
+ inode->i_ctime=CURRENT_TIME;
+ }
+ if (!inode->i_zone[8])
+ return 0;
+ if (!(bh=bread(inode->i_dev,inode->i_zone[8])))
+ return 0;
+ i = ((unsigned short *)bh->b_data)[block>>9];
+ if (create && !i)
+ if (i=new_block(inode->i_dev)) {
+ ((unsigned short *) (bh->b_data))[block>>9]=i;
+ bh->b_dirt=1;
+ }
+ brelse(bh);
+ if (!i)
+ return 0;
+ if (!(bh=bread(inode->i_dev,i)))
+ return 0;
+ i = ((unsigned short *)bh->b_data)[block&511];
+ if (create && !i)
+ if (i=new_block(inode->i_dev)) {
+ ((unsigned short *) (bh->b_data))[block&511]=i;
+ bh->b_dirt=1;
+ }
+ brelse(bh);
+ return i;
+}
+
+int bmap(struct m_inode * inode,int block)
+{
+ return _bmap(inode,block,0);
+}
+
+int create_block(struct m_inode * inode, int block)
+{
+ return _bmap(inode,block,1);
+}
+
+void iput(struct m_inode * inode)
+{
+ if (!inode)
+ return;
+ wait_on_inode(inode);
+ if (!inode->i_count)
+ panic("iput: trying to free free inode");
+ if (inode->i_pipe) {
+ wake_up(&inode->i_wait);
+ if (--inode->i_count)
+ return;
+ free_page(inode->i_size);
+ inode->i_count=0;
+ inode->i_dirt=0;
+ inode->i_pipe=0;
+ return;
+ }
+ if (!inode->i_dev || inode->i_count>1) {
+ inode->i_count--;
+ return;
+ }
+repeat:
+ if (!inode->i_nlinks) {
+ truncate(inode);
+ free_inode(inode);
+ return;
+ }
+ if (inode->i_dirt) {
+ write_inode(inode); /* we can sleep - so do again */
+ wait_on_inode(inode);
+ goto repeat;
+ }
+ inode->i_count--;
+ return;
+}
+
+static volatile int last_allocated_inode = 0;
+
+struct m_inode * get_empty_inode(void)
+{
+ struct m_inode * inode;
+ int inr;
+
+ while (1) {
+ inode = NULL;
+ inr = last_allocated_inode;
+ do {
+ if (!inode_table[inr].i_count) {
+ inode = inr + inode_table;
+ break;
+ }
+ inr++;
+ if (inr>=NR_INODE)
+ inr=0;
+ } while (inr != last_allocated_inode);
+ if (!inode) {
+ for (inr=0 ; inr<NR_INODE ; inr++)
+ printk("%04x: %6d\t",inode_table[inr].i_dev,
+ inode_table[inr].i_num);
+ panic("No free inodes in mem");
+ }
+ last_allocated_inode = inr;
+ wait_on_inode(inode);
+ while (inode->i_dirt) {
+ write_inode(inode);
+ wait_on_inode(inode);
+ }
+ if (!inode->i_count)
+ break;
+ }
+ memset(inode,0,sizeof(*inode));
+ inode->i_count = 1;
+ return inode;
+}
+
+struct m_inode * get_pipe_inode(void)
+{
+ struct m_inode * inode;
+
+ if (!(inode = get_empty_inode()))
+ return NULL;
+ if (!(inode->i_size=get_free_page())) {
+ inode->i_count = 0;
+ return NULL;
+ }
+ inode->i_count = 2; /* sum of readers/writers */
+ PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;
+ inode->i_pipe = 1;
+ return inode;
+}
+
+struct m_inode * iget(int dev,int nr)
+{
+ struct m_inode * inode, * empty;
+
+ if (!dev)
+ panic("iget with dev==0");
+ empty = get_empty_inode();
+ inode = inode_table;
+ while (inode < NR_INODE+inode_table) {
+ if (inode->i_dev != dev || inode->i_num != nr) {
+ inode++;
+ continue;
+ }
+ wait_on_inode(inode);
+ if (inode->i_dev != dev || inode->i_num != nr) {
+ inode = inode_table;
+ continue;
+ }
+ inode->i_count++;
+ if (empty)
+ iput(empty);
+ return inode;
+ }
+ if (!empty)
+ return (NULL);
+ inode=empty;
+ inode->i_dev = dev;
+ inode->i_num = nr;
+ read_inode(inode);
+ return inode;
+}
+
+static void read_inode(struct m_inode * inode)
+{
+ struct super_block * sb;
+ struct buffer_head * bh;
+ int block;
+
+ lock_inode(inode);
+ sb=get_super(inode->i_dev);
+ block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
+ (inode->i_num-1)/INODES_PER_BLOCK;
+ if (!(bh=bread(inode->i_dev,block)))
+ panic("unable to read i-node block");
+ *(struct d_inode *)inode =
+ ((struct d_inode *)bh->b_data)
+ [(inode->i_num-1)%INODES_PER_BLOCK];
+ brelse(bh);
+ unlock_inode(inode);
+}
+
+static void write_inode(struct m_inode * inode)
+{
+ struct super_block * sb;
+ struct buffer_head * bh;
+ int block;
+
+ lock_inode(inode);
+ sb=get_super(inode->i_dev);
+ block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
+ (inode->i_num-1)/INODES_PER_BLOCK;
+ if (!(bh=bread(inode->i_dev,block)))
+ panic("unable to read i-node block");
+ ((struct d_inode *)bh->b_data)
+ [(inode->i_num-1)%INODES_PER_BLOCK] =
+ *(struct d_inode *)inode;
+ bh->b_dirt=1;
+ inode->i_dirt=0;
+ brelse(bh);
+ unlock_inode(inode);
+}
diff --git a/linux/fs/ioctl.c b/linux/fs/ioctl.c
new file mode 100644
index 0000000..131561b
--- /dev/null
+++ b/linux/fs/ioctl.c
@@ -0,0 +1,40 @@
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <linux/sched.h>
+
+extern int tty_ioctl(int dev, int cmd, int arg);
+
+typedef int (*ioctl_ptr)(int dev,int cmd,int arg);
+
+#define NRDEVS ((sizeof (ioctl_table))/(sizeof (ioctl_ptr)))
+
+static ioctl_ptr ioctl_table[]={
+ NULL, /* nodev */
+ NULL, /* /dev/mem */
+ NULL, /* /dev/fd */
+ NULL, /* /dev/hd */
+ tty_ioctl, /* /dev/ttyx */
+ tty_ioctl, /* /dev/tty */
+ NULL, /* /dev/lp */
+ NULL}; /* named pipes */
+
+
+int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ struct file * filp;
+ int dev,mode;
+
+ if (fd >= NR_OPEN || !(filp = current->filp[fd]))
+ return -EBADF;
+ mode=filp->f_inode->i_mode;
+ if (!S_ISCHR(mode) && !S_ISBLK(mode))
+ return -EINVAL;
+ dev = filp->f_inode->i_zone[0];
+ if (MAJOR(dev) >= NRDEVS)
+ panic("unknown device for ioctl");
+ if (!ioctl_table[MAJOR(dev)])
+ return -ENOTTY;
+ return ioctl_table[MAJOR(dev)](dev,cmd,arg);
+}
diff --git a/linux/fs/namei.c b/linux/fs/namei.c
new file mode 100644
index 0000000..600737b
--- /dev/null
+++ b/linux/fs/namei.c
@@ -0,0 +1,678 @@
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <const.h>
+#include <sys/stat.h>
+
+#define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE])
+
+/*
+ * comment out this line if you want names > NAME_LEN chars to be
+ * truncated. Else they will be disallowed.
+ */
+/* #define NO_TRUNCATE */
+
+#define MAY_EXEC 1
+#define MAY_WRITE 2
+#define MAY_READ 4
+
+/*
+ * permission()
+ *
+ * is used to check for read/write/execute permissions on a file.
+ * I don't know if we should look at just the euid or both euid and
+ * uid, but that should be easily changed.
+ */
+static int permission(struct m_inode * inode,int mask)
+{
+ int mode = inode->i_mode;
+
+/* special case: not even root can read/write a deleted file */
+ if (inode->i_dev && !inode->i_nlinks)
+ return 0;
+ if (!(current->uid && current->euid))
+ mode=0777;
+ else if (current->uid==inode->i_uid || current->euid==inode->i_uid)
+ mode >>= 6;
+ else if (current->gid==inode->i_gid || current->egid==inode->i_gid)
+ mode >>= 3;
+ return mode & mask & 0007;
+}
+
+/*
+ * ok, we cannot use strncmp, as the name is not in our data space.
+ * Thus we'll have to use match. No big problem. Match also makes
+ * some sanity tests.
+ *
+ * NOTE! unlike strncmp, match returns 1 for success, 0 for failure.
+ */
+static int match(int len,const char * name,struct dir_entry * de)
+{
+ register int same __asm__("ax");
+
+ if (!de || !de->inode || len > NAME_LEN)
+ return 0;
+ if (len < NAME_LEN && de->name[len])
+ return 0;
+ __asm__("cld\n\t"
+ "fs ; repe ; cmpsb\n\t"
+ "setz %%al"
+ :"=a" (same)
+ :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
+ :"cx","di","si");
+ return same;
+}
+
+/*
+ * find_entry()
+ *
+ * finds and entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+ * itself (as a parameter - res_dir). It does NOT read the inode of the
+ * entry - you'll have to do that yourself if you want to.
+ */
+static struct buffer_head * find_entry(struct m_inode * dir,
+ const char * name, int namelen, struct dir_entry ** res_dir)
+{
+ int entries;
+ int block,i;
+ struct buffer_head * bh;
+ struct dir_entry * de;
+
+#ifdef NO_TRUNCATE
+ if (namelen > NAME_LEN)
+ return NULL;
+#else
+ if (namelen > NAME_LEN)
+ namelen = NAME_LEN;
+#endif
+ entries = dir->i_size / (sizeof (struct dir_entry));
+ *res_dir = NULL;
+ if (!namelen)
+ return NULL;
+ if (!(block = dir->i_zone[0]))
+ return NULL;
+ if (!(bh = bread(dir->i_dev,block)))
+ return NULL;
+ i = 0;
+ de = (struct dir_entry *) bh->b_data;
+ while (i < entries) {
+ if ((char *)de >= BLOCK_SIZE+bh->b_data) {
+ brelse(bh);
+ bh = NULL;
+ if (!(block = bmap(dir,i/DIR_ENTRIES_PER_BLOCK)) ||
+ !(bh = bread(dir->i_dev,block))) {
+ i += DIR_ENTRIES_PER_BLOCK;
+ continue;
+ }
+ de = (struct dir_entry *) bh->b_data;
+ }
+ if (match(namelen,name,de)) {
+ *res_dir = de;
+ return bh;
+ }
+ de++;
+ i++;
+ }
+ brelse(bh);
+ return NULL;
+}
+
+/*
+ * add_entry()
+ *
+ * adds a file entry to the specified directory, using the same
+ * semantics as find_entry(). It returns NULL if it failed.
+ *
+ * NOTE!! The inode part of 'de' is left at 0 - which means you
+ * may not sleep between calling this and putting something into
+ * the entry, as someone else might have used it while you slept.
+ */
+static struct buffer_head * add_entry(struct m_inode * dir,
+ const char * name, int namelen, struct dir_entry ** res_dir)
+{
+ int block,i;
+ struct buffer_head * bh;
+ struct dir_entry * de;
+
+ *res_dir = NULL;
+#ifdef NO_TRUNCATE
+ if (namelen > NAME_LEN)
+ return NULL;
+#else
+ if (namelen > NAME_LEN)
+ namelen = NAME_LEN;
+#endif
+ if (!namelen)
+ return NULL;
+ if (!(block = dir->i_zone[0]))
+ return NULL;
+ if (!(bh = bread(dir->i_dev,block)))
+ return NULL;
+ i = 0;
+ de = (struct dir_entry *) bh->b_data;
+ while (1) {
+ if ((char *)de >= BLOCK_SIZE+bh->b_data) {
+ brelse(bh);
+ bh = NULL;
+ block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK);
+ if (!block)
+ return NULL;
+ if (!(bh = bread(dir->i_dev,block))) {
+ i += DIR_ENTRIES_PER_BLOCK;
+ continue;
+ }
+ de = (struct dir_entry *) bh->b_data;
+ }
+ if (i*sizeof(struct dir_entry) >= dir->i_size) {
+ de->inode=0;
+ dir->i_size = (i+1)*sizeof(struct dir_entry);
+ dir->i_dirt = 1;
+ dir->i_ctime = CURRENT_TIME;
+ }
+ if (!de->inode) {
+ dir->i_mtime = CURRENT_TIME;
+ for (i=0; i < NAME_LEN ; i++)
+ de->name[i]=(i<namelen)?get_fs_byte(name+i):0;
+ bh->b_dirt = 1;
+ *res_dir = de;
+ return bh;
+ }
+ de++;
+ i++;
+ }
+ brelse(bh);
+ return NULL;
+}
+
+/*
+ * get_dir()
+ *
+ * Getdir traverses the pathname until it hits the topmost directory.
+ * It returns NULL on failure.
+ */
+static struct m_inode * get_dir(const char * pathname)
+{
+ char c;
+ const char * thisname;
+ struct m_inode * inode;
+ struct buffer_head * bh;
+ int namelen,inr,idev;
+ struct dir_entry * de;
+
+ if (!current->root || !current->root->i_count)
+ panic("No root inode");
+ if (!current->pwd || !current->pwd->i_count)
+ panic("No cwd inode");
+ if ((c=get_fs_byte(pathname))=='/') {
+ inode = current->root;
+ pathname++;
+ } else if (c)
+ inode = current->pwd;
+ else
+ return NULL; /* empty name is bad */
+ inode->i_count++;
+ while (1) {
+ thisname = pathname;
+ if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {
+ iput(inode);
+ return NULL;
+ }
+ for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++)
+ /* nothing */ ;
+ if (!c)
+ return inode;
+ if (!(bh = find_entry(inode,thisname,namelen,&de))) {
+ iput(inode);
+ return NULL;
+ }
+ inr = de->inode;
+ idev = inode->i_dev;
+ brelse(bh);
+ iput(inode);
+ if (!(inode = iget(idev,inr)))
+ return NULL;
+ }
+}
+
+/*
+ * dir_namei()
+ *
+ * dir_namei() returns the inode of the directory of the
+ * specified name, and the name within that directory.
+ */
+static struct m_inode * dir_namei(const char * pathname,
+ int * namelen, const char ** name)
+{
+ char c;
+ const char * basename;
+ struct m_inode * dir;
+
+ if (!(dir = get_dir(pathname)))
+ return NULL;
+ basename = pathname;
+ while (c=get_fs_byte(pathname++))
+ if (c=='/')
+ basename=pathname;
+ *namelen = pathname-basename-1;
+ *name = basename;
+ return dir;
+}
+
+/*
+ * namei()
+ *
+ * is used by most simple commands to get the inode of a specified name.
+ * Open, link etc use their own routines, but this is enough for things
+ * like 'chmod' etc.
+ */
+struct m_inode * namei(const char * pathname)
+{
+ const char * basename;
+ int inr,dev,namelen;
+ struct m_inode * dir;
+ struct buffer_head * bh;
+ struct dir_entry * de;
+
+ if (!(dir = dir_namei(pathname,&namelen,&basename)))
+ return NULL;
+ if (!namelen) /* special case: '/usr/' etc */
+ return dir;
+ bh = find_entry(dir,basename,namelen,&de);
+ if (!bh) {
+ iput(dir);
+ return NULL;
+ }
+ inr = de->inode;
+ dev = dir->i_dev;
+ brelse(bh);
+ iput(dir);
+ dir=iget(dev,inr);
+ if (dir) {
+ dir->i_atime=CURRENT_TIME;
+ dir->i_dirt=1;
+ }
+ return dir;
+}
+
+/*
+ * open_namei()
+ *
+ * namei for open - this is in fact almost the whole open-routine.
+ */
+int open_namei(const char * pathname, int flag, int mode,
+ struct m_inode ** res_inode)
+{
+ const char * basename;
+ int inr,dev,namelen;
+ struct m_inode * dir, *inode;
+ struct buffer_head * bh;
+ struct dir_entry * de;
+
+ if ((flag & O_TRUNC) && !(flag & O_ACCMODE))
+ flag |= O_WRONLY;
+ mode &= 0777 & ~current->umask;
+ mode |= I_REGULAR;
+ if (!(dir = dir_namei(pathname,&namelen,&basename)))
+ return -ENOENT;
+ if (!namelen) { /* special case: '/usr/' etc */
+ if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {
+ *res_inode=dir;
+ return 0;
+ }
+ iput(dir);
+ return -EISDIR;
+ }
+ bh = find_entry(dir,basename,namelen,&de);
+ if (!bh) {
+ if (!(flag & O_CREAT)) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!permission(dir,MAY_WRITE)) {
+ iput(dir);
+ return -EACCES;
+ }
+ inode = new_inode(dir->i_dev);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_mode = mode;
+ inode->i_dirt = 1;
+ bh = add_entry(dir,basename,namelen,&de);
+ if (!bh) {
+ inode->i_nlinks--;
+ iput(inode);
+ iput(dir);
+ return -ENOSPC;
+ }
+ de->inode = inode->i_num;
+ bh->b_dirt = 1;
+ brelse(bh);
+ iput(dir);
+ *res_inode = inode;
+ return 0;
+ }
+ inr = de->inode;
+ dev = dir->i_dev;
+ brelse(bh);
+ iput(dir);
+ if (flag & O_EXCL)
+ return -EEXIST;
+ if (!(inode=iget(dev,inr)))
+ return -EACCES;
+ if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) ||
+ permission(inode,ACC_MODE(flag))!=ACC_MODE(flag)) {
+ iput(inode);
+ return -EPERM;
+ }
+ inode->i_atime = CURRENT_TIME;
+ if (flag & O_TRUNC)
+ truncate(inode);
+ *res_inode = inode;
+ return 0;
+}
+
+int sys_mkdir(const char * pathname, int mode)
+{
+ const char * basename;
+ int namelen;
+ struct m_inode * dir, * inode;
+ struct buffer_head * bh, *dir_block;
+ struct dir_entry * de;
+
+ if (current->euid && current->uid)
+ return -EPERM;
+ if (!(dir = dir_namei(pathname,&namelen,&basename)))
+ return -ENOENT;
+ if (!namelen) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!permission(dir,MAY_WRITE)) {
+ iput(dir);
+ return -EPERM;
+ }
+ bh = find_entry(dir,basename,namelen,&de);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ inode = new_inode(dir->i_dev);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_size = 32;
+ inode->i_dirt = 1;
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
+ if (!(inode->i_zone[0]=new_block(inode->i_dev))) {
+ iput(dir);
+ inode->i_nlinks--;
+ iput(inode);
+ return -ENOSPC;
+ }
+ inode->i_dirt = 1;
+ if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) {
+ iput(dir);
+ free_block(inode->i_dev,inode->i_zone[0]);
+ inode->i_nlinks--;
+ iput(inode);
+ return -ERROR;
+ }
+ de = (struct dir_entry *) dir_block->b_data;
+ de->inode=inode->i_num;
+ strcpy(de->name,".");
+ de++;
+ de->inode = dir->i_num;
+ strcpy(de->name,"..");
+ inode->i_nlinks = 2;
+ dir_block->b_dirt = 1;
+ brelse(dir_block);
+ inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask);
+ inode->i_dirt = 1;
+ bh = add_entry(dir,basename,namelen,&de);
+ if (!bh) {
+ iput(dir);
+ free_block(inode->i_dev,inode->i_zone[0]);
+ inode->i_nlinks=0;
+ iput(inode);
+ return -ENOSPC;
+ }
+ de->inode = inode->i_num;
+ bh->b_dirt = 1;
+ dir->i_nlinks++;
+ dir->i_dirt = 1;
+ iput(dir);
+ iput(inode);
+ brelse(bh);
+ return 0;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+static int empty_dir(struct m_inode * inode)
+{
+ int nr,block;
+ int len;
+ struct buffer_head * bh;
+ struct dir_entry * de;
+
+ len = inode->i_size / sizeof (struct dir_entry);
+ if (len<2 || !inode->i_zone[0] ||
+ !(bh=bread(inode->i_dev,inode->i_zone[0]))) {
+ printk("warning - bad directory on dev %04x\n",inode->i_dev);
+ return 0;
+ }
+ de = (struct dir_entry *) bh->b_data;
+ if (de[0].inode != inode->i_num || !de[1].inode ||
+ strcmp(".",de[0].name) || strcmp("..",de[1].name)) {
+ printk("warning - bad directory on dev %04x\n",inode->i_dev);
+ return 0;
+ }
+ nr = 2;
+ de += 2;
+ while (nr<len) {
+ if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) {
+ brelse(bh);
+ block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK);
+ if (!block) {
+ nr += DIR_ENTRIES_PER_BLOCK;
+ continue;
+ }
+ if (!(bh=bread(inode->i_dev,block)))
+ return 0;
+ de = (struct dir_entry *) bh->b_data;
+ }
+ if (de->inode) {
+ brelse(bh);
+ return 0;
+ }
+ de++;
+ nr++;
+ }
+ brelse(bh);
+ return 1;
+}
+
+int sys_rmdir(const char * name)
+{
+ const char * basename;
+ int namelen;
+ struct m_inode * dir, * inode;
+ struct buffer_head * bh;
+ struct dir_entry * de;
+
+ if (current->euid && current->uid)
+ return -EPERM;
+ if (!(dir = dir_namei(name,&namelen,&basename)))
+ return -ENOENT;
+ if (!namelen) {
+ iput(dir);
+ return -ENOENT;
+ }
+ bh = find_entry(dir,basename,namelen,&de);
+ if (!bh) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!permission(dir,MAY_WRITE)) {
+ iput(dir);
+ brelse(bh);
+ return -EPERM;
+ }
+ if (!(inode = iget(dir->i_dev, de->inode))) {
+ iput(dir);
+ brelse(bh);
+ return -EPERM;
+ }
+ if (inode == dir) { /* we may not delete ".", but "../dir" is ok */
+ iput(inode);
+ iput(dir);
+ brelse(bh);
+ return -EPERM;
+ }
+ if (!S_ISDIR(inode->i_mode)) {
+ iput(inode);
+ iput(dir);
+ brelse(bh);
+ return -ENOTDIR;
+ }
+ if (!empty_dir(inode)) {
+ iput(inode);
+ iput(dir);
+ brelse(bh);
+ return -ENOTEMPTY;
+ }
+ if (inode->i_nlinks != 2)
+ printk("empty directory has nlink!=2 (%d)",inode->i_nlinks);
+ de->inode = 0;
+ bh->b_dirt = 1;
+ brelse(bh);
+ inode->i_nlinks=0;
+ inode->i_dirt=1;
+ dir->i_nlinks--;
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt=1;
+ iput(dir);
+ iput(inode);
+ return 0;
+}
+
+int sys_unlink(const char * name)
+{
+ const char * basename;
+ int namelen;
+ struct m_inode * dir, * inode;
+ struct buffer_head * bh;
+ struct dir_entry * de;
+
+ if (!(dir = dir_namei(name,&namelen,&basename)))
+ return -ENOENT;
+ if (!namelen) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!permission(dir,MAY_WRITE)) {
+ iput(dir);
+ return -EPERM;
+ }
+ bh = find_entry(dir,basename,namelen,&de);
+ if (!bh) {
+ iput(dir);
+ return -ENOENT;
+ }
+ inode = iget(dir->i_dev, de->inode);
+ if (!inode) {
+ printk("iget failed in delete (%04x:%d)",dir->i_dev,de->inode);
+ iput(dir);
+ brelse(bh);
+ return -ENOENT;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ iput(inode);
+ iput(dir);
+ brelse(bh);
+ return -EPERM;
+ }
+ if (!inode->i_nlinks) {
+ printk("Deleting nonexistent file (%04x:%d), %d\n",
+ inode->i_dev,inode->i_num,inode->i_nlinks);
+ inode->i_nlinks=1;
+ }
+ de->inode = 0;
+ bh->b_dirt = 1;
+ brelse(bh);
+ inode->i_nlinks--;
+ inode->i_dirt = 1;
+ inode->i_ctime = CURRENT_TIME;
+ iput(inode);
+ iput(dir);
+ return 0;
+}
+
+int sys_link(const char * oldname, const char * newname)
+{
+ struct dir_entry * de;
+ struct m_inode * oldinode, * dir;
+ struct buffer_head * bh;
+ const char * basename;
+ int namelen;
+
+ oldinode=namei(oldname);
+ if (!oldinode)
+ return -ENOENT;
+ if (!S_ISREG(oldinode->i_mode)) {
+ iput(oldinode);
+ return -EPERM;
+ }
+ dir = dir_namei(newname,&namelen,&basename);
+ if (!dir) {
+ iput(oldinode);
+ return -EACCES;
+ }
+ if (!namelen) {
+ iput(oldinode);
+ iput(dir);
+ return -EPERM;
+ }
+ if (dir->i_dev != oldinode->i_dev) {
+ iput(dir);
+ iput(oldinode);
+ return -EXDEV;
+ }
+ if (!permission(dir,MAY_WRITE)) {
+ iput(dir);
+ iput(oldinode);
+ return -EACCES;
+ }
+ bh = find_entry(dir,basename,namelen,&de);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ iput(oldinode);
+ return -EEXIST;
+ }
+ bh = add_entry(dir,basename,namelen,&de);
+ if (!bh) {
+ iput(dir);
+ iput(oldinode);
+ return -ENOSPC;
+ }
+ de->inode = oldinode->i_num;
+ bh->b_dirt = 1;
+ brelse(bh);
+ iput(dir);
+ oldinode->i_nlinks++;
+ oldinode->i_ctime = CURRENT_TIME;
+ oldinode->i_dirt = 1;
+ iput(oldinode);
+ return 0;
+}
diff --git a/linux/fs/open.c b/linux/fs/open.c
new file mode 100644
index 0000000..6918690
--- /dev/null
+++ b/linux/fs/open.c
@@ -0,0 +1,188 @@
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <utime.h>
+#include <sys/stat.h>
+
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+int sys_utime(char * filename, struct utimbuf * times)
+{
+ struct m_inode * inode;
+ long actime,modtime;
+
+ if (!(inode=namei(filename)))
+ return -ENOENT;
+ if (times) {
+ actime = get_fs_long((unsigned long *) &times->actime);
+ modtime = get_fs_long((unsigned long *) &times->modtime);
+ } else
+ actime = modtime = CURRENT_TIME;
+ inode->i_atime = actime;
+ inode->i_mtime = modtime;
+ inode->i_dirt = 1;
+ iput(inode);
+ return 0;
+}
+
+int sys_access(const char * filename,int mode)
+{
+ struct m_inode * inode;
+ int res;
+
+ mode &= 0007;
+ if (!(inode=namei(filename)))
+ return -EACCES;
+ res = inode->i_mode & 0777;
+ iput(inode);
+ if (!(current->euid && current->uid))
+ if (res & 0111)
+ res = 0777;
+ else
+ res = 0666;
+ if (current->euid == inode->i_uid)
+ res >>= 6;
+ else if (current->egid == inode->i_gid)
+ res >>= 6;
+ if ((res & 0007 & mode) == mode)
+ return 0;
+ return -EACCES;
+}
+
+int sys_chdir(const char * filename)
+{
+ struct m_inode * inode;
+
+ if (!(inode = namei(filename)))
+ return -ENOENT;
+ if (!S_ISDIR(inode->i_mode)) {
+ iput(inode);
+ return -ENOTDIR;
+ }
+ iput(current->pwd);
+ current->pwd = inode;
+ return (0);
+}
+
+int sys_chroot(const char * filename)
+{
+ struct m_inode * inode;
+
+ if (!(inode=namei(filename)))
+ return -ENOENT;
+ if (!S_ISDIR(inode->i_mode)) {
+ iput(inode);
+ return -ENOTDIR;
+ }
+ iput(current->root);
+ current->root = inode;
+ return (0);
+}
+
+int sys_chmod(const char * filename,int mode)
+{
+ struct m_inode * inode;
+
+ if (!(inode=namei(filename)))
+ return -ENOENT;
+ if (current->uid && current->euid)
+ if (current->uid!=inode->i_uid && current->euid!=inode->i_uid) {
+ iput(inode);
+ return -EACCES;
+ } else
+ mode = (mode & 0777) | (inode->i_mode & 07000);
+ inode->i_mode = (mode & 07777) | (inode->i_mode & ~07777);
+ inode->i_dirt = 1;
+ iput(inode);
+ return 0;
+}
+
+int sys_chown(const char * filename,int uid,int gid)
+{
+ struct m_inode * inode;
+
+ if (!(inode=namei(filename)))
+ return -ENOENT;
+ if (current->uid && current->euid) {
+ iput(inode);
+ return -EACCES;
+ }
+ inode->i_uid=uid;
+ inode->i_gid=gid;
+ inode->i_dirt=1;
+ iput(inode);
+ return 0;
+}
+
+int sys_open(const char * filename,int flag,int mode)
+{
+ struct m_inode * inode;
+ struct file * f;
+ int i,fd;
+
+ mode &= 0777 & ~current->umask;
+ for(fd=0 ; fd<NR_OPEN ; fd++)
+ if (!current->filp[fd])
+ break;
+ if (fd>=NR_OPEN)
+ return -EINVAL;
+ current->close_on_exec &= ~(1<<fd);
+ f=0+file_table;
+ for (i=0 ; i<NR_FILE ; i++,f++)
+ if (!f->f_count) break;
+ if (i>=NR_FILE)
+ return -EINVAL;
+ (current->filp[fd]=f)->f_count++;
+ if ((i=open_namei(filename,flag,mode,&inode))<0) {
+ current->filp[fd]=NULL;
+ f->f_count=0;
+ return i;
+ }
+/* ttys are somewhat special (ttyxx major==4, tty major==5) */
+ if (S_ISCHR(inode->i_mode))
+ if (MAJOR(inode->i_zone[0])==4) {
+ if (current->leader && current->tty<0) {
+ current->tty = MINOR(inode->i_zone[0]);
+ tty_table[current->tty].pgrp = current->pgrp;
+ }
+ } else if (MAJOR(inode->i_zone[0])==5)
+ if (current->tty<0) {
+ iput(inode);
+ current->filp[fd]=NULL;
+ f->f_count=0;
+ return -EPERM;
+ }
+ f->f_mode = inode->i_mode;
+ f->f_flags = flag;
+ f->f_count = 1;
+ f->f_inode = inode;
+ f->f_pos = 0;
+ return (fd);
+}
+
+int sys_creat(const char * pathname, int mode)
+{
+ return sys_open(pathname, O_CREAT | O_TRUNC, mode);
+}
+
+int sys_close(unsigned int fd)
+{
+ struct file * filp;
+
+ if (fd >= NR_OPEN)
+ return -EINVAL;
+ current->close_on_exec &= ~(1<<fd);
+ if (!(filp = current->filp[fd]))
+ return -EINVAL;
+ current->filp[fd] = NULL;
+ if (filp->f_count == 0)
+ panic("Close: file count is 0");
+ if (--filp->f_count)
+ return (0);
+ iput(filp->f_inode);
+ return (0);
+}
diff --git a/linux/fs/pipe.c b/linux/fs/pipe.c
new file mode 100644
index 0000000..7e03e13
--- /dev/null
+++ b/linux/fs/pipe.c
@@ -0,0 +1,92 @@
+#include <signal.h>
+
+#include <linux/sched.h>
+#include <linux/mm.h> /* for get_free_page */
+#include <asm/segment.h>
+
+int read_pipe(struct m_inode * inode, char * buf, int count)
+{
+ char * b=buf;
+
+ while (PIPE_EMPTY(*inode)) {
+ wake_up(&inode->i_wait);
+ if (inode->i_count != 2) /* are there any writers left? */
+ return 0;
+ sleep_on(&inode->i_wait);
+ }
+ while (count>0 && !(PIPE_EMPTY(*inode))) {
+ count --;
+ put_fs_byte(((char *)inode->i_size)[PIPE_TAIL(*inode)],b++);
+ INC_PIPE( PIPE_TAIL(*inode) );
+ }
+ wake_up(&inode->i_wait);
+ return b-buf;
+}
+
+int write_pipe(struct m_inode * inode, char * buf, int count)
+{
+ char * b=buf;
+
+ wake_up(&inode->i_wait);
+ if (inode->i_count != 2) { /* no readers */
+ current->signal |= (1<<(SIGPIPE-1));
+ return -1;
+ }
+ while (count-->0) {
+ while (PIPE_FULL(*inode)) {
+ wake_up(&inode->i_wait);
+ if (inode->i_count != 2) {
+ current->signal |= (1<<(SIGPIPE-1));
+ return b-buf;
+ }
+ sleep_on(&inode->i_wait);
+ }
+ ((char *)inode->i_size)[PIPE_HEAD(*inode)] = get_fs_byte(b++);
+ INC_PIPE( PIPE_HEAD(*inode) );
+ wake_up(&inode->i_wait);
+ }
+ wake_up(&inode->i_wait);
+ return b-buf;
+}
+
+int sys_pipe(unsigned long * fildes)
+{
+ struct m_inode * inode;
+ struct file * f[2];
+ int fd[2];
+ int i,j;
+
+ j=0;
+ for(i=0;j<2 && i<NR_FILE;i++)
+ if (!file_table[i].f_count)
+ (f[j++]=i+file_table)->f_count++;
+ if (j==1)
+ f[0]->f_count=0;
+ if (j<2)
+ return -1;
+ j=0;
+ for(i=0;j<2 && i<NR_OPEN;i++)
+ if (!current->filp[i]) {
+ current->filp[ fd[j]=i ] = f[j];
+ j++;
+ }
+ if (j==1)
+ current->filp[fd[0]]=NULL;
+ if (j<2) {
+ f[0]->f_count=f[1]->f_count=0;
+ return -1;
+ }
+ if (!(inode=get_pipe_inode())) {
+ current->filp[fd[0]] =
+ current->filp[fd[1]] = NULL;
+ f[0]->f_count = f[1]->f_count = 0;
+ return -1;
+ }
+ f[0]->f_inode = f[1]->f_inode = inode;
+ f[0]->f_pos = f[1]->f_pos = 0;
+ f[0]->f_mode = 1; /* read */
+ f[1]->f_mode = 2; /* write */
+ put_fs_long(fd[0],0+fildes);
+ put_fs_long(fd[1],1+fildes);
+ return 0;
+}
diff --git a/linux/fs/read_write.c b/linux/fs/read_write.c
new file mode 100644
index 0000000..93faac2
--- /dev/null
+++ b/linux/fs/read_write.c
@@ -0,0 +1,97 @@
+#include <sys/stat.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+
+extern int rw_char(int rw,int dev, char * buf, int count);
+extern int read_pipe(struct m_inode * inode, char * buf, int count);
+extern int write_pipe(struct m_inode * inode, char * buf, int count);
+extern int block_read(int dev, off_t * pos, char * buf, int count);
+extern int block_write(int dev, off_t * pos, char * buf, int count);
+extern int file_read(struct m_inode * inode, struct file * filp,
+ char * buf, int count);
+extern int file_write(struct m_inode * inode, struct file * filp,
+ char * buf, int count);
+
+int sys_lseek(unsigned int fd,off_t offset, int origin)
+{
+ struct file * file;
+ int tmp;
+
+ if (fd >= NR_OPEN || !(file=current->filp[fd]) || !(file->f_inode)
+ || !IS_BLOCKDEV(MAJOR(file->f_inode->i_dev)))
+ return -EBADF;
+ if (file->f_inode->i_pipe)
+ return -ESPIPE;
+ switch (origin) {
+ case 0:
+ if (offset<0) return -EINVAL;
+ file->f_pos=offset;
+ break;
+ case 1:
+ if (file->f_pos+offset<0) return -EINVAL;
+ file->f_pos += offset;
+ break;
+ case 2:
+ if ((tmp=file->f_inode->i_size+offset) < 0)
+ return -EINVAL;
+ file->f_pos = tmp;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return file->f_pos;
+}
+
+int sys_read(unsigned int fd,char * buf,int count)
+{
+ struct file * file;
+ struct m_inode * inode;
+
+ if (fd>=NR_OPEN || count<0 || !(file=current->filp[fd]))
+ return -EINVAL;
+ if (!count)
+ return 0;
+ verify_area(buf,count);
+ inode = file->f_inode;
+ if (inode->i_pipe)
+ return (file->f_mode&1)?read_pipe(inode,buf,count):-1;
+ if (S_ISCHR(inode->i_mode))
+ return rw_char(READ,inode->i_zone[0],buf,count);
+ if (S_ISBLK(inode->i_mode))
+ return block_read(inode->i_zone[0],&file->f_pos,buf,count);
+ if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {
+ if (count+file->f_pos > inode->i_size)
+ count = inode->i_size - file->f_pos;
+ if (count<=0)
+ return 0;
+ return file_read(inode,file,buf,count);
+ }
+ printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode);
+ return -EINVAL;
+}
+
+int sys_write(unsigned int fd,char * buf,int count)
+{
+ struct file * file;
+ struct m_inode * inode;
+
+ if (fd>=NR_OPEN || count <0 || !(file=current->filp[fd]))
+ return -EINVAL;
+ if (!count)
+ return 0;
+ inode=file->f_inode;
+ if (inode->i_pipe)
+ return (file->f_mode&2)?write_pipe(inode,buf,count):-1;
+ if (S_ISCHR(inode->i_mode))
+ return rw_char(WRITE,inode->i_zone[0],buf,count);
+ if (S_ISBLK(inode->i_mode))
+ return block_write(inode->i_zone[0],&file->f_pos,buf,count);
+ if (S_ISREG(inode->i_mode))
+ return file_write(inode,file,buf,count);
+ printk("(Write)inode->i_mode=%06o\n\r",inode->i_mode);
+ return -EINVAL;
+}
diff --git a/linux/fs/stat.c b/linux/fs/stat.c
new file mode 100644
index 0000000..4bec71d
--- /dev/null
+++ b/linux/fs/stat.c
@@ -0,0 +1,51 @@
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+
+static int cp_stat(struct m_inode * inode, struct stat * statbuf)
+{
+ struct stat tmp;
+ int i;
+
+ verify_area(statbuf,sizeof (* statbuf));
+ tmp.st_dev = inode->i_dev;
+ tmp.st_ino = inode->i_num;
+ tmp.st_mode = inode->i_mode;
+ tmp.st_nlink = inode->i_nlinks;
+ tmp.st_uid = inode->i_uid;
+ tmp.st_gid = inode->i_gid;
+ tmp.st_rdev = inode->i_zone[0];
+ tmp.st_size = inode->i_size;
+ tmp.st_atime = inode->i_atime;
+ tmp.st_mtime = inode->i_mtime;
+ tmp.st_ctime = inode->i_ctime;
+ for (i=0 ; i<sizeof (tmp) ; i++)
+ put_fs_byte(((char *) &tmp)[i],&((char *) statbuf)[i]);
+ return (0);
+}
+
+int sys_stat(char * filename, struct stat * statbuf)
+{
+ int i;
+ struct m_inode * inode;
+
+ if (!(inode=namei(filename)))
+ return -ENOENT;
+ i=cp_stat(inode,statbuf);
+ iput(inode);
+ return i;
+}
+
+int sys_fstat(unsigned int fd, struct stat * statbuf)
+{
+ struct file * f;
+ struct m_inode * inode;
+
+ if (fd >= NR_OPEN || !(f=current->filp[fd]) || !(inode=f->f_inode))
+ return -ENOENT;
+ return cp_stat(inode,statbuf);
+}
diff --git a/linux/fs/super.c b/linux/fs/super.c
new file mode 100644
index 0000000..d832289
--- /dev/null
+++ b/linux/fs/super.c
@@ -0,0 +1,102 @@
+/*
+ * super.c contains code to handle the super-block tables.
+ */
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+
+/* set_bit uses setb, as gas doesn't recognize setc */
+#define set_bit(bitnr,addr) ({ \
+register int __res __asm__("ax"); \
+__asm__("bt %2,%3;setb %%al":"=a" (__res):"a" (0),"r" (bitnr),"m" (*(addr))); \
+__res; })
+
+struct super_block super_block[NR_SUPER];
+
+struct super_block * do_mount(int dev)
+{
+ struct super_block * p;
+ struct buffer_head * bh;
+ int i,block;
+
+ for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++ )
+ if (!(p->s_dev))
+ break;
+ p->s_dev = -1; /* mark it in use */
+ if (p >= &super_block[NR_SUPER])
+ return NULL;
+ if (!(bh = bread(dev,1)))
+ return NULL;
+ *p = *((struct super_block *) bh->b_data);
+ brelse(bh);
+ if (p->s_magic != SUPER_MAGIC) {
+ p->s_dev = 0;
+ return NULL;
+ }
+ for (i=0;i<I_MAP_SLOTS;i++)
+ p->s_imap[i] = NULL;
+ for (i=0;i<Z_MAP_SLOTS;i++)
+ p->s_zmap[i] = NULL;
+ block=2;
+ for (i=0 ; i < p->s_imap_blocks ; i++)
+ if (p->s_imap[i]=bread(dev,block))
+ block++;
+ else
+ break;
+ for (i=0 ; i < p->s_zmap_blocks ; i++)
+ if (p->s_zmap[i]=bread(dev,block))
+ block++;
+ else
+ break;
+ if (block != 2+p->s_imap_blocks+p->s_zmap_blocks) {
+ for(i=0;i<I_MAP_SLOTS;i++)
+ brelse(p->s_imap[i]);
+ for(i=0;i<Z_MAP_SLOTS;i++)
+ brelse(p->s_zmap[i]);
+ p->s_dev=0;
+ return NULL;
+ }
+ p->s_imap[0]->b_data[0] |= 1;
+ p->s_zmap[0]->b_data[0] |= 1;
+ p->s_dev = dev;
+ p->s_isup = NULL;
+ p->s_imount = NULL;
+ p->s_time = 0;
+ p->s_rd_only = 0;
+ p->s_dirt = 0;
+ return p;
+}
+
+void mount_root(void)
+{
+ int i,free;
+ struct super_block * p;
+ struct m_inode * mi;
+
+ if (32 != sizeof (struct d_inode))
+ panic("bad i-node size");
+ for(i=0;i<NR_FILE;i++)
+ file_table[i].f_count=0;
+ for(p = &super_block[0] ; p < &super_block[NR_SUPER] ; p++)
+ p->s_dev = 0;
+ if (!(p=do_mount(ROOT_DEV)))
+ panic("Unable to mount root");
+ if (!(mi=iget(ROOT_DEV,1)))
+ panic("Unable to read root i-node");
+ mi->i_count += 3 ; /* NOTE! it is logically used 4 times, not 1 */
+ p->s_isup = p->s_imount = mi;
+ current->pwd = mi;
+ current->root = mi;
+ free=0;
+ i=p->s_nzones;
+ while (-- i >= 0)
+ if (!set_bit(i&8191,p->s_zmap[i>>13]->b_data))
+ free++;
+ printk("%d/%d free blocks\n\r",free,p->s_nzones);
+ free=0;
+ i=p->s_ninodes+1;
+ while (-- i >= 0)
+ if (!set_bit(i&8191,p->s_imap[i>>13]->b_data))
+ free++;
+ printk("%d/%d free inodes\n\r",free,p->s_ninodes);
+}
diff --git a/linux/fs/truncate.c b/linux/fs/truncate.c
new file mode 100644
index 0000000..fd82db8
--- /dev/null
+++ b/linux/fs/truncate.c
@@ -0,0 +1,59 @@
+#include <linux/sched.h>
+
+#include <sys/stat.h>
+
+static void free_ind(int dev,int block)
+{
+ struct buffer_head * bh;
+ unsigned short * p;
+ int i;
+
+ if (!block)
+ return;
+ if (bh=bread(dev,block)) {
+ p = (unsigned short *) bh->b_data;
+ for (i=0;i<512;i++,p++)
+ if (*p)
+ free_block(dev,*p);
+ brelse(bh);
+ }
+ free_block(dev,block);
+}
+
+static void free_dind(int dev,int block)
+{
+ struct buffer_head * bh;
+ unsigned short * p;
+ int i;
+
+ if (!block)
+ return;
+ if (bh=bread(dev,block)) {
+ p = (unsigned short *) bh->b_data;
+ for (i=0;i<512;i++,p++)
+ if (*p)
+ free_ind(dev,*p);
+ brelse(bh);
+ }
+ free_block(dev,block);
+}
+
+void truncate(struct m_inode * inode)
+{
+ int i;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
+ return;
+ for (i=0;i<7;i++)
+ if (inode->i_zone[i]) {
+ free_block(inode->i_dev,inode->i_zone[i]);
+ inode->i_zone[i]=0;
+ }
+ free_ind(inode->i_dev,inode->i_zone[7]);
+ free_dind(inode->i_dev,inode->i_zone[8]);
+ inode->i_zone[7] = inode->i_zone[8] = 0;
+ inode->i_size = 0;
+ inode->i_dirt = 1;
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+}
+
diff --git a/linux/fs/tty_ioctl.c b/linux/fs/tty_ioctl.c
new file mode 100644
index 0000000..b4d9bf7
--- /dev/null
+++ b/linux/fs/tty_ioctl.c
@@ -0,0 +1,166 @@
+#include <errno.h>
+#include <termios.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+static void flush(struct tty_queue * queue)
+{
+ cli();
+ queue->head = queue->tail;
+ sti();
+}
+
+static void wait_until_sent(struct tty_struct * tty)
+{
+ /* do nothing - not implemented */
+}
+
+static void send_break(struct tty_struct * tty)
+{
+ /* do nothing - not implemented */
+}
+
+static int get_termios(struct tty_struct * tty, struct termios * termios)
+{
+ int i;
+
+ verify_area(termios, sizeof (*termios));
+ for (i=0 ; i< (sizeof (*termios)) ; i++)
+ put_fs_byte( ((char *)&tty->termios)[i] , i+(char *)termios );
+ return 0;
+}
+
+static int set_termios(struct tty_struct * tty, struct termios * termios)
+{
+ int i;
+
+ for (i=0 ; i< (sizeof (*termios)) ; i++)
+ ((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios);
+ return 0;
+}
+
+static int get_termio(struct tty_struct * tty, struct termio * termio)
+{
+ int i;
+ struct termio tmp_termio;
+
+ verify_area(termio, sizeof (*termio));
+ tmp_termio.c_iflag = tty->termios.c_iflag;
+ tmp_termio.c_oflag = tty->termios.c_oflag;
+ tmp_termio.c_cflag = tty->termios.c_cflag;
+ tmp_termio.c_lflag = tty->termios.c_lflag;
+ tmp_termio.c_line = tty->termios.c_line;
+ for(i=0 ; i < NCC ; i++)
+ tmp_termio.c_cc[i] = tty->termios.c_cc[i];
+ for (i=0 ; i< (sizeof (*termio)) ; i++)
+ put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio );
+ return 0;
+}
+
+static int set_termio(struct tty_struct * tty, struct termio * termio)
+{
+ int i;
+ struct termio tmp_termio;
+
+ for (i=0 ; i< (sizeof (*termio)) ; i++)
+ ((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio);
+ *(unsigned short *)&tty->termios.c_iflag = tmp_termio.c_iflag;
+ *(unsigned short *)&tty->termios.c_oflag = tmp_termio.c_oflag;
+ *(unsigned short *)&tty->termios.c_cflag = tmp_termio.c_cflag;
+ *(unsigned short *)&tty->termios.c_lflag = tmp_termio.c_lflag;
+ tty->termios.c_line = tmp_termio.c_line;
+ for(i=0 ; i < NCC ; i++)
+ tty->termios.c_cc[i] = tmp_termio.c_cc[i];
+ return 0;
+}
+
+int tty_ioctl(int dev, int cmd, int arg)
+{
+ struct tty_struct * tty;
+ if (MAJOR(dev) == 5) {
+ dev=current->tty;
+ if (dev<0)
+ panic("tty_ioctl: dev<0");
+ } else
+ dev=MINOR(dev);
+ tty = dev + tty_table;
+ switch (cmd) {
+ case TCGETS:
+ return get_termios(tty,(struct termios *) arg);
+ case TCSETSF:
+ flush(&tty->read_q); /* fallthrough */
+ case TCSETSW:
+ wait_until_sent(tty); /* fallthrough */
+ case TCSETS:
+ return set_termios(tty,(struct termios *) arg);
+ case TCGETA:
+ return get_termio(tty,(struct termio *) arg);
+ case TCSETAF:
+ flush(&tty->read_q); /* fallthrough */
+ case TCSETAW:
+ wait_until_sent(tty); /* fallthrough */
+ case TCSETA:
+ return set_termio(tty,(struct termio *) arg);
+ case TCSBRK:
+ if (!arg) {
+ wait_until_sent(tty);
+ send_break(tty);
+ }
+ return 0;
+ case TCXONC:
+ return -EINVAL; /* not implemented */
+ case TCFLSH:
+ if (arg==0)
+ flush(&tty->read_q);
+ else if (arg==1)
+ flush(&tty->write_q);
+ else if (arg==2) {
+ flush(&tty->read_q);
+ flush(&tty->write_q);
+ } else
+ return -EINVAL;
+ return 0;
+ case TIOCEXCL:
+ return -EINVAL; /* not implemented */
+ case TIOCNXCL:
+ return -EINVAL; /* not implemented */
+ case TIOCSCTTY:
+ return -EINVAL; /* set controlling term NI */
+ case TIOCGPGRP:
+ verify_area((void *) arg,4);
+ put_fs_long(tty->pgrp,(unsigned long *) arg);
+ return 0;
+ case TIOCSPGRP:
+ tty->pgrp=get_fs_long((unsigned long *) arg);
+ return 0;
+ case TIOCOUTQ:
+ verify_area((void *) arg,4);
+ put_fs_long(CHARS(tty->write_q),(unsigned long *) arg);
+ return 0;
+ case TIOCSTI:
+ return -EINVAL; /* not implemented */
+ case TIOCGWINSZ:
+ return -EINVAL; /* not implemented */
+ case TIOCSWINSZ:
+ return -EINVAL; /* not implemented */
+ case TIOCMGET:
+ return -EINVAL; /* not implemented */
+ case TIOCMBIS:
+ return -EINVAL; /* not implemented */
+ case TIOCMBIC:
+ return -EINVAL; /* not implemented */
+ case TIOCMSET:
+ return -EINVAL; /* not implemented */
+ case TIOCGSOFTCAR:
+ return -EINVAL; /* not implemented */
+ case TIOCSSOFTCAR:
+ return -EINVAL; /* not implemented */
+ default:
+ return -EINVAL;
+ }
+}