fs
bitmap.c
/* 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;
}
block_dev.c
#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);
}
buffer.c
/*
- ‘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;
}
char_dev.c
#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);
}
exec.c
#include <errno.h>
#include <sys/stat.h>
#include <a.out.h>
#include <linux/fs.h>
#include <