diff options
| author | Andrew Lee <alee14498@protonmail.com> | 2021-08-15 00:16:45 -0400 |
|---|---|---|
| committer | Andrew Lee <alee14498@protonmail.com> | 2021-08-15 00:16:45 -0400 |
| commit | 723428bebe3105ad3c3406e416402d1831b482c4 (patch) | |
| tree | ff990e306163515973746ddfb261f29ba8765441 /linux/fs | |
| download | linux-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/Makefile | 95 | ||||
| -rw-r--r-- | linux/fs/bitmap.c | 158 | ||||
| -rw-r--r-- | linux/fs/block_dev.c | 86 | ||||
| -rw-r--r-- | linux/fs/buffer.c | 254 | ||||
| -rw-r--r-- | linux/fs/char_dev.c | 50 | ||||
| -rw-r--r-- | linux/fs/exec.c | 306 | ||||
| -rw-r--r-- | linux/fs/fcntl.c | 69 | ||||
| -rw-r--r-- | linux/fs/file_dev.c | 84 | ||||
| -rw-r--r-- | linux/fs/file_table.c | 3 | ||||
| -rw-r--r-- | linux/fs/inode.c | 288 | ||||
| -rw-r--r-- | linux/fs/ioctl.c | 40 | ||||
| -rw-r--r-- | linux/fs/namei.c | 678 | ||||
| -rw-r--r-- | linux/fs/open.c | 188 | ||||
| -rw-r--r-- | linux/fs/pipe.c | 92 | ||||
| -rw-r--r-- | linux/fs/read_write.c | 97 | ||||
| -rw-r--r-- | linux/fs/stat.c | 51 | ||||
| -rw-r--r-- | linux/fs/super.c | 102 | ||||
| -rw-r--r-- | linux/fs/truncate.c | 59 | ||||
| -rw-r--r-- | linux/fs/tty_ioctl.c | 166 |
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 *) ×->actime); + modtime = get_fs_long((unsigned long *) ×->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; + } +} |
