diff options
author | Linus Torvalds <torvalds@cc.helsinki.fi> | 1993-03-22 21:03:45 +0000 |
---|---|---|
committer | Nicolas Pitre <nico@cam.org> | 2007-08-19 14:19:14 -0400 |
commit | 3ccb23223d9cbee8e7946aa23f44a84ceff88d3c (patch) | |
tree | 35552324421d578052a6bc9267cae9a49aed3008 | |
parent | eeb7df9e7d9a3a48dc39743a13e0ed1cc0d5cb41 (diff) | |
download | archive-3ccb23223d9cbee8e7946aa23f44a84ceff88d3c.tar.gz |
ALPHA-diffs for 0.99 patchlevel 7
I don't generally announce ALPHA-diffs to quite this large an audience,
but I'll be partying^H^H^H^H^H^H^H^Hunavailable for the rest of the
week, and it's unlikely that I will be able to check mails or the
newsgroups until the start of April. As a result, I'm putting up my
latest kernel version for ftp as it fixes some things in 0.99.7.
The ALPHA-diffs can be found on nic.funet.fi: in the directory
pub/OS/Linux/PEOPLE/Linus. If you dislike patching, you can get the
full sources in "linux-0.99.7A.tar.z", or just get the diff file
"ALPHA-diff.z".
Changes in this release:
- the new kernel now detects the lock-up condition at startup if you
have a faulty 386/387 coupling, and will use software floating point
in that case.
- the Xia filesystem is updated to the latest version
- the DOS filesystem is updated to the latest version
- the XT disk driver is included: I haven't been able to test it, but
at least it won't bother anybody if you don't configure it in..
- the latest serial diffs are in
- minor ultrastor fixes
- some changes to the keyboard and line printer drivers: I hope the
keyboard lockups that some people have reported would be gone with
this release.
- some fixes to arp.c
I'll be interested in success/failure reports, although I won't be able
to answer them for some time (and I might miss some of the posts on
c.o.l).
Linus
55 files changed, 1815 insertions, 627 deletions
@@ -24,11 +24,11 @@ endif # # ROOT_DEV specifies the default root-device when making the image. -# This can be either FLOPPY, /dev/xxxx or empty, in which case the -# default of FLOPPY is used by 'build'. +# This can be either FLOPPY, CURRENT, /dev/xxxx or empty, in which case +# the default of FLOPPY is used by 'build'. # -ROOT_DEV = /dev/hdb1 +ROOT_DEV = CURRENT # # uncomment the correct keyboard: @@ -45,20 +45,20 @@ ROOT_DEV = /dev/hdb1 # 0x10 - dieresis (umlaut) KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0 -# KEYBOARD = -DKBD_FINNISH_LATIN1 -DKBDFLAGS=0x9F +# KEYBOARD = -DKBD_FINNISH_LATIN1 -DKBDFLAGS=0x1F # KEYBOARD = -DKBD_US -DKBDFLAGS=0 # KEYBOARD = -DKBD_GR -DKBDFLAGS=0 -# KEYBOARD = -DKBD_GR_LATIN1 -DKBDFLAGS=0x9F +# KEYBOARD = -DKBD_GR_LATIN1 -DKBDFLAGS=0x1F # KEYBOARD = -DKBD_FR -DKBDFLAGS=0 -# KEYBOARD = -DKBD_FR_LATIN1 -DKBDFLAGS=0x9F +# KEYBOARD = -DKBD_FR_LATIN1 -DKBDFLAGS=0x1F # KEYBOARD = -DKBD_UK -DKBDFLAGS=0 # KEYBOARD = -DKBD_DK -DKBDFLAGS=0 -# KEYBOARD = -DKBD_DK_LATIN1 -DKBDFLAGS=0x9F +# KEYBOARD = -DKBD_DK_LATIN1 -DKBDFLAGS=0x1F # KEYBOARD = -DKBD_DVORAK -DKBDFLAGS=0 # KEYBOARD = -DKBD_SG -DKBDFLAGS=0 -# KEYBOARD = -DKBD_SG_LATIN1 -DKBDFLAGS=0x9F +# KEYBOARD = -DKBD_SG_LATIN1 -DKBDFLAGS=0x1F # KEYBOARD = -DKBD_SF -DKBDFLAGS=0 -# KEYBOARD = -DKBD_SF_LATIN1 -DKBDFLAGS=0x9F +# KEYBOARD = -DKBD_SF_LATIN1 -DKBDFLAGS=0x1F # KEYBOARD = -DKBD_NO -DKBDFLAGS=0 # @@ -139,7 +139,7 @@ tools/./version.h: tools/version.h tools/version.h: $(CONFIGURE) Makefile @./makever.sh - @echo \#define UTS_RELEASE \"0.99.pl7-`cat .version`\" > tools/version.h + @echo \#define UTS_RELEASE \"0.99.pl7A-`cat .version`\" > tools/version.h @echo \#define UTS_VERSION \"`date +%D`\" >> tools/version.h @echo \#define LINUX_COMPILE_TIME \"`date +%T`\" >> tools/version.h @echo \#define LINUX_COMPILE_BY \"`whoami`\" >> tools/version.h @@ -232,9 +232,9 @@ clean: mrproper: clean rm -f include/linux/autoconf.h tools/version.h rm -f .version .config* - rm -f `find . -name .depend -print` + rm -f .depend `find . -name .depend -print` -backup: clean +backup: mrproper cd .. && tar cf - linux | gzip -9 > backup.z sync @@ -4,6 +4,8 @@ Kernel math emulation CONFIG_MATH_EMULATION y/n n Normal harddisk support CONFIG_BLK_DEV_HD y/n y +XT harddisk support +CONFIG_BLK_DEV_XD y/n n TCP/IP CONFIG_TCPIP y/n y Kernel profiling support diff --git a/fs/buffer.c b/fs/buffer.c index fac233b..fce0733 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/locks.h> +#include <linux/errno.h> #include <asm/system.h> #include <asm/io.h> @@ -102,6 +103,11 @@ int sys_sync(void) return 0; } +int sys_fsync(int fd) +{ + return -ENOSYS; +} + void invalidate_buffers(dev_t dev) { int i; @@ -358,7 +364,7 @@ repeat: if (bh->b_count || bh->b_size != size) goto repeat; if (bh->b_dirt) { - sync_buffers(bh->b_dev); + sync_buffers(0); goto repeat; } /* NOTE!! While we slept waiting for this block, somebody else might */ @@ -460,7 +460,7 @@ restart_interp: } i = inode->i_mode; if (IS_NOSUID(inode) && (((i & S_ISUID) && inode->i_uid != current-> - euid) || ((i & S_ISGID) && inode->i_gid != current->egid)) && + euid) || ((i & S_ISGID) && !in_group_p(inode->i_gid))) && !suser()) { retval = -EPERM; goto exec_error2; diff --git a/fs/ext/symlink.c b/fs/ext/symlink.c index 8d46ce1..94d148e 100644 --- a/fs/ext/symlink.c +++ b/fs/ext/symlink.c @@ -48,7 +48,6 @@ static int ext_follow_link(struct inode * dir, struct inode * inode, int flag, int mode, struct inode ** res_inode) { int error; - unsigned short fs; struct buffer_head * bh; *res_inode = NULL; diff --git a/fs/minix/namei.c b/fs/minix/namei.c index a902655..e7e9702 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -376,6 +376,7 @@ static int empty_dir(struct inode * inode) return 1; info = &inode->i_sb->u.minix_sb; block = 0; + bh = NULL; offset = 2*info->s_dirsize; if (inode->i_size & (info->s_dirsize-1)) goto bad_dir; diff --git a/fs/msdos/dir.c b/fs/msdos/dir.c index 12a5a45..90415ce 100644 --- a/fs/msdos/dir.c +++ b/fs/msdos/dir.c @@ -1,19 +1,19 @@ /* * linux/fs/msdos/dir.c * - * Written 1992 by Werner Almesberger + * Written 1992,1993 by Werner Almesberger * * MS-DOS directory handling functions */ #include <asm/segment.h> -#include <linux/sched.h> #include <linux/fs.h> #include <linux/msdos_fs.h> #include <linux/errno.h> #include <linux/stat.h> + static int msdos_dir_read(struct inode * inode,struct file * filp, char * buf,int count) { return -EISDIR; @@ -22,6 +22,7 @@ static int msdos_dir_read(struct inode * inode,struct file * filp, char * buf,in static int msdos_readdir(struct inode *inode,struct file *filp, struct dirent *dirent,int count); + static struct file_operations msdos_dir_operations = { NULL, /* lseek - default */ msdos_dir_read, /* read */ @@ -78,8 +79,7 @@ static int msdos_readdir(struct inode *inode,struct file *filp, if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1)) return -ENOENT; bh = NULL; while ((ino = msdos_get_entry(inode,&filp->f_pos,&bh,&de)) > -1) { - if (de->name[0] && ((unsigned char *) (de->name))[0] != - DELETED_FLAG && !(de->attr & ATTR_VOLUME)) { + if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) { for (i = last = 0; i < 8; i++) { if (!(c = de->name[i])) break; if (c >= 'A' && c <= 'Z') c += 32; diff --git a/fs/msdos/fat.c b/fs/msdos/fat.c index 8e766c1..a1c0fa7 100644 --- a/fs/msdos/fat.c +++ b/fs/msdos/fat.c @@ -1,7 +1,7 @@ /* * linux/fs/msdos/fat.c * - * Written 1992 by Werner Almesberger + * Written 1992,1993 by Werner Almesberger */ #include <linux/msdos_fs.h> @@ -22,6 +22,7 @@ int fat_access(struct super_block *sb,int this,int new_value) void *data,*data2,*c_data,*c_data2; int first,last,next,copy; + if ((unsigned) (this-2) >= MSDOS_SB(sb)->clusters) return 0; if (MSDOS_SB(sb)->fat_bits == 16) first = last = this*2; else { first = this*3/2; @@ -46,9 +47,9 @@ int fat_access(struct super_block *sb,int this,int new_value) } if (MSDOS_SB(sb)->fat_bits == 16) { p_first = p_last = NULL; /* GCC needs that stuff */ - next = ((unsigned short *) data)[(first & (SECTOR_SIZE-1)) - >> 1]; - if (next >= 0xfff8) next = -1; + next = CF_LE_W(((unsigned short *) data)[(first & + (SECTOR_SIZE-1)) >> 1]); + if (next >= 0xfff7) next = -1; } else { p_first = &((unsigned char *) data)[first & (SECTOR_SIZE-1)]; @@ -56,12 +57,12 @@ int fat_access(struct super_block *sb,int this,int new_value) (SECTOR_SIZE-1)]; if (this & 1) next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff; else next = (*p_first+(*p_last << 8)) & 0xfff; - if (next >= 0xff8) next = -1; + if (next >= 0xff7) next = -1; } if (new_value != -1) { if (MSDOS_SB(sb)->fat_bits == 16) ((unsigned short *) data)[(first & (SECTOR_SIZE-1)) >> - 1] = new_value; + 1] = CT_LE_W(new_value); else { if (this & 1) { *p_first = (*p_first & 0xf) | (new_value << 4); @@ -167,8 +168,11 @@ printk("cache add: <%d,%d> %d (%d)\n",inode->i_dev,inode->i_ino,f_clu,d_clu); for (walk = fat_cache; walk->next; walk = (last = walk)->next) if (inode->i_dev == walk->device && walk->ino == inode->i_ino && walk->file_cluster == f_clu) { - if (walk->disk_cluster != d_clu) - panic("FAT cache corruption"); + if (walk->disk_cluster != d_clu) { + printk("FAT cache corruption"); + cache_inval_inode(inode); + return; + } /* update LRU */ if (last == NULL) return; last->next = walk->next; @@ -226,10 +230,7 @@ int get_cluster(struct inode *inode,int cluster) if ((this = fat_access(inode->i_sb,this,-1)) == -1) return 0; if (!this) return 0; } - if (!(MSDOS_I(inode)->i_busy && inode->i_nlink)) - cache_add(inode,cluster,this); - /* don't add clusters of moved files, because we can't invalidate them - when this inode is returned. */ + cache_add(inode,cluster,this); return this; } @@ -278,8 +279,10 @@ int fat_free(struct inode *inode,int skip) } lock_fat(inode->i_sb); while (this != -1) { - if (!(this = fat_access(inode->i_sb,this,0))) - panic("fat_free: deleting beyond EOF"); + if (!(this = fat_access(inode->i_sb,this,0))) { + fs_panic(inode->i_sb,"fat_free: deleting beyond EOF"); + break; + } if (MSDOS_SB(inode->i_sb)->free_clusters != -1) MSDOS_SB(inode->i_sb)->free_clusters++; inode->i_blocks -= MSDOS_SB(inode->i_sb)->cluster_size; diff --git a/fs/msdos/file.c b/fs/msdos/file.c index ec446fb..afddf81 100644 --- a/fs/msdos/file.c +++ b/fs/msdos/file.c @@ -1,7 +1,7 @@ /* * linux/fs/msdos/file.c * - * Written 1992 by Werner Almesberger + * Written 1992,1993 by Werner Almesberger * * MS-DOS regular file handling primitives */ diff --git a/fs/msdos/inode.c b/fs/msdos/inode.c index bd5b7d8..c19df60 100644 --- a/fs/msdos/inode.c +++ b/fs/msdos/inode.c @@ -1,7 +1,7 @@ /* * linux/fs/msdos/inode.c * - * Written 1992 by Werner Almesberger + * Written 1992,1993 by Werner Almesberger */ #include <linux/msdos_fs.h> @@ -15,22 +15,28 @@ #include <asm/segment.h> + void msdos_put_inode(struct inode *inode) { struct inode *depend; + struct super_block *sb; - if (inode->i_nlink) + if (inode->i_nlink) { + if (MSDOS_I(inode)->i_busy) cache_inval_inode(inode); return; + } inode->i_size = 0; msdos_truncate(inode); depend = MSDOS_I(inode)->i_depend; + sb = inode->i_sb; clear_inode(inode); if (depend) { if (MSDOS_I(depend)->i_old != inode) { - printk("Invalid link (0x%X): expected 0x%X, got " - "0x%X\n",(int) depend,(int) inode,(int) - MSDOS_I(depend)->i_old); - panic("That's fatal"); + printk("Invalid link (0x%X): expected 0x%X, got 0x%X\n", + (int) depend,(int) inode,(int) MSDOS_I(depend)-> + i_old); + fs_panic(sb,"..."); + return; } MSDOS_I(depend)->i_old = NULL; iput(depend); @@ -50,7 +56,7 @@ void msdos_put_super(struct super_block *sb) static struct super_operations msdos_sops = { msdos_read_inode, - NULL, + msdos_notify_change, msdos_write_inode, msdos_put_inode, msdos_put_super, @@ -59,7 +65,8 @@ static struct super_operations msdos_sops = { }; -static int parse_options(char *options,char *check,char *conversion,uid_t *uid, gid_t *gid, int *umask) +static int parse_options(char *options,char *check,char *conversion,uid_t *uid, + gid_t *gid,int *umask,int *debug,int *fat) { char *this,*value; @@ -68,6 +75,7 @@ static int parse_options(char *options,char *check,char *conversion,uid_t *uid, *uid = current->uid; *gid = current->gid; *umask = current->umask; + *debug = *fat = 0; if (!options) return 1; for (this = strtok(options,","); this; this = strtok(NULL,",")) { if ((value = strchr(this,'=')) != NULL) @@ -109,6 +117,17 @@ static int parse_options(char *options,char *check,char *conversion,uid_t *uid, if (*value) return 0; } + else if (!strcmp(this,"debug")) { + if (value) return 0; + *debug = 1; + } + else if (!strcmp(this,"fat")) { + if (!value || !*value) + return 0; + *fat = simple_strtoul(value,&value,0); + if (*value || (*fat != 12 && *fat != 16)) + return 0; + } else return 0; } return 1; @@ -121,13 +140,15 @@ struct super_block *msdos_read_super(struct super_block *s,void *data) { struct buffer_head *bh; struct msdos_boot_sector *b; - int data_sectors; + int data_sectors,logical_sector_size,sector_mult; + int debug,error,fat; char check,conversion; uid_t uid; gid_t gid; int umask; - if (!parse_options((char *) data,&check,&conversion,&uid,&gid,&umask)) { + if (!parse_options((char *) data,&check,&conversion,&uid,&gid,&umask, + &debug,&fat)) { s->s_dev = 0; return NULL; } @@ -142,29 +163,64 @@ struct super_block *msdos_read_super(struct super_block *s,void *data) } b = (struct msdos_boot_sector *) bh->b_data; s->s_blocksize = 1024; /* we cannot handle anything else yet */ - MSDOS_SB(s)->cluster_size = b->cluster_size; + +/* + * The DOS3 partition size limit is *not* 32M as many people think. + * Instead, it is 64K sectors (with the usual sector size being + * 512 bytes, leading to a 32M limit). + * + * DOS 3 partition managers got around this problem by faking a + * larger sector size, ie treating multiple physical sectors as + * a single logical sector. + * + * We can accommodate this scheme by adjusting our cluster size, + * fat_start, and data_start by an appropriate value. + * + * (by Drew Eckhardt) + */ + +#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0) + /* don't divide by zero */ + + logical_sector_size = CF_LE_W(*(unsigned short *) &b->sector_size); + sector_mult = logical_sector_size >> SECTOR_BITS; + MSDOS_SB(s)->cluster_size = b->cluster_size*sector_mult; MSDOS_SB(s)->fats = b->fats; - MSDOS_SB(s)->fat_start = b->reserved; - MSDOS_SB(s)->fat_length = b->fat_length; - MSDOS_SB(s)->dir_start = b->reserved+b->fats*b->fat_length; - MSDOS_SB(s)->dir_entries = *((unsigned short *) &b->dir_entries); - MSDOS_SB(s)->data_start = MSDOS_SB(s)->dir_start+((MSDOS_SB(s)-> - dir_entries << 5) >> 9); - data_sectors = (*((unsigned short *) &b->sectors) ? *((unsigned short *) - &b->sectors) : b->total_sect)-MSDOS_SB(s)->data_start; - MSDOS_SB(s)->clusters = b->cluster_size ? data_sectors/b->cluster_size : - 0; - MSDOS_SB(s)->fat_bits = MSDOS_SB(s)->clusters > MSDOS_FAT12 ? 16 : 12; - brelse(bh); -printk("[MS-DOS FS Rel. alpha.8, FAT %d, check=%c, conv=%c, uid=%d, gid=%d, umask=%03o]\n", - MSDOS_SB(s)->fat_bits,check,conversion,uid,gid,umask); -printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d,se=%d,ts=%d]\n", - b->media,MSDOS_SB(s)->cluster_size,MSDOS_SB(s)->fats,MSDOS_SB(s)->fat_start, - MSDOS_SB(s)->fat_length,MSDOS_SB(s)->dir_start,MSDOS_SB(s)->dir_entries, - MSDOS_SB(s)->data_start,*(unsigned short *) &b->sectors,b->total_sect); - if (!MSDOS_SB(s)->fats || (MSDOS_SB(s)->dir_entries & (MSDOS_DPS-1)) + MSDOS_SB(s)->fat_start = CF_LE_W(b->reserved)*sector_mult; + MSDOS_SB(s)->fat_length = CF_LE_W(b->fat_length)*sector_mult; + MSDOS_SB(s)->dir_start = (CF_LE_W(b->reserved)+b->fats*CF_LE_W( + b->fat_length))*sector_mult; + MSDOS_SB(s)->dir_entries = CF_LE_W(*((unsigned short *) &b->dir_entries + )); + MSDOS_SB(s)->data_start = MSDOS_SB(s)->dir_start+ROUND_TO_MULTIPLE(( + MSDOS_SB(s)->dir_entries << MSDOS_DIR_BITS) >> SECTOR_BITS, + sector_mult); + data_sectors = (CF_LE_W(*((unsigned short *) &b->sectors)) ? + CF_LE_W(*((unsigned short *) &b->sectors)) : + CF_LE_L(b->total_sect))*sector_mult-MSDOS_SB(s)->data_start; + MSDOS_SB(s)->clusters = b->cluster_size ? data_sectors/b->cluster_size/ + sector_mult : 0; + MSDOS_SB(s)->fat_bits = fat ? fat : MSDOS_SB(s)->clusters > MSDOS_FAT12 + ? 16 : 12; + error = !MSDOS_SB(s)->fats || (MSDOS_SB(s)->dir_entries & (MSDOS_DPS-1)) || !b->cluster_size || MSDOS_SB(s)->clusters+2 > MSDOS_SB(s)-> - fat_length*SECTOR_SIZE*8/MSDOS_SB(s)->fat_bits) { + fat_length*SECTOR_SIZE*8/MSDOS_SB(s)->fat_bits || !sector_mult || + (logical_sector_size & (SECTOR_SIZE-1)) || !b->secs_track || + !b->heads; + brelse(bh); + if (error || debug) { + printk("[MS-DOS FS Rel. alpha.10,FAT %d,check=%c,conv=%c," + "uid=%d,gid=%d,umask=%03o%s]\n",MSDOS_SB(s)->fat_bits,check, + conversion,uid,gid,umask,MSDOS_CAN_BMAP(MSDOS_SB(s)) ? ", + bmap" : ""); + printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d," + "se=%d,ts=%d,ls=%d]\n",b->media,MSDOS_SB(s)->cluster_size, + MSDOS_SB(s)->fats,MSDOS_SB(s)->fat_start,MSDOS_SB(s)-> + fat_length,MSDOS_SB(s)->dir_start,MSDOS_SB(s)->dir_entries, + MSDOS_SB(s)->data_start,CF_LE_W(*(unsigned short *) &b-> + sectors),b->total_sect,logical_sector_size); + } + if (error) { s->s_dev = 0; printk("Unsupported FS parameters\n"); return NULL; @@ -180,6 +236,7 @@ printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d,se=%d,ts=%d]\n", MSDOS_SB(s)->free_clusters = -1; /* don't know yet */ MSDOS_SB(s)->fat_wait = NULL; MSDOS_SB(s)->fat_lock = 0; + MSDOS_SB(s)->prev_free = 0; if (!(s->s_mounted = iget(s,MSDOS_ROOT_INO))) { s->s_dev = 0; printk("get root inode failed\n"); @@ -260,16 +317,18 @@ void msdos_read_inode(struct inode *inode) inode->i_mtime = inode->i_atime = inode->i_ctime = 0; return; } - if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS, BLOCK_SIZE))) - panic("unable to read i-node block"); + if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS, + BLOCK_SIZE))) { + printk("dev = 0x%04X, ino = %d\n",inode->i_dev,inode->i_ino); + panic("msdos_read_inode: unable to read i-node block"); + } raw_entry = &((struct msdos_dir_entry *) (bh->b_data)) [inode->i_ino & (MSDOS_DPB-1)]; - if ((raw_entry->attr & ATTR_DIR) && *raw_entry->name && *(unsigned char *) - raw_entry->name != DELETED_FLAG) { + if ((raw_entry->attr & ATTR_DIR) && !IS_FREE(raw_entry->name)) { inode->i_mode = MSDOS_MKMODE(raw_entry->attr,0777 & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFDIR; inode->i_op = &msdos_dir_inode_operations; - MSDOS_I(inode)->i_start = raw_entry->start; + MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start); inode->i_nlink = msdos_subdirs(inode); /* includes .., compensating for "self" */ #ifdef DEBUG @@ -279,7 +338,7 @@ void msdos_read_inode(struct inode *inode) } #endif inode->i_size = 0; - if ((this = raw_entry->start) != 0) + if ((this = CF_LE_W(raw_entry->start)) != 0) while (this != -1) { inode->i_size += SECTOR_SIZE*MSDOS_SB(inode-> i_sb)->cluster_size; @@ -289,14 +348,15 @@ void msdos_read_inode(struct inode *inode) } } else { - inode->i_mode = MSDOS_MKMODE(raw_entry->attr,0666 & - ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IFREG; + inode->i_mode = MSDOS_MKMODE(raw_entry->attr,(IS_NOEXEC(inode) + ? 0666 : 0777) & ~MSDOS_SB(inode->i_sb)->fs_umask) | + S_IFREG; inode->i_op = MSDOS_CAN_BMAP(MSDOS_SB(inode->i_sb)) ? &msdos_file_inode_operations : &msdos_file_inode_operations_no_bmap; - MSDOS_I(inode)->i_start = raw_entry->start; + MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start); inode->i_nlink = 1; - inode->i_size = raw_entry->size; + inode->i_size = CF_LE_L(raw_entry->size); } MSDOS_I(inode)->i_binary = is_binary(MSDOS_SB(inode->i_sb)->conversion, raw_entry->ext); @@ -306,7 +366,7 @@ void msdos_read_inode(struct inode *inode) inode->i_blocks = (inode->i_size+inode->i_blksize-1)/ inode->i_blksize*MSDOS_SB(inode->i_sb)->cluster_size; inode->i_mtime = inode->i_atime = inode->i_ctime = - date_dos2unix(raw_entry->time,raw_entry->date); + date_dos2unix(CF_LE_W(raw_entry->time),CF_LE_W(raw_entry->date)); brelse(bh); } @@ -318,8 +378,11 @@ void msdos_write_inode(struct inode *inode) inode->i_dirt = 0; if (inode->i_ino == MSDOS_ROOT_INO || !inode->i_nlink) return; - if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS, BLOCK_SIZE))) - panic("unable to read i-node block"); + if (!(bh = bread(inode->i_dev,inode->i_ino >> MSDOS_DPB_BITS, + BLOCK_SIZE))) { + printk("dev = 0x%04X, ino = %d\n",inode->i_dev,inode->i_ino); + panic("msdos_write_inode: unable to read i-node block"); + } raw_entry = &((struct msdos_dir_entry *) (bh->b_data)) [inode->i_ino & (MSDOS_DPB-1)]; if (S_ISDIR(inode->i_mode)) { @@ -328,12 +391,41 @@ void msdos_write_inode(struct inode *inode) } else { raw_entry->attr = ATTR_NONE; - raw_entry->size = inode->i_size; + raw_entry->size = CT_LE_L(inode->i_size); } raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) | MSDOS_I(inode)->i_attrs; - raw_entry->start = MSDOS_I(inode)->i_start; + raw_entry->start = CT_LE_L(MSDOS_I(inode)->i_start); date_unix2dos(inode->i_mtime,&raw_entry->time,&raw_entry->date); + raw_entry->time = CT_LE_W(raw_entry->time); + raw_entry->date = CT_LE_W(raw_entry->date); bh->b_dirt = 1; brelse(bh); } + + +int msdos_notify_change(int flags,struct inode *inode) +{ + int error; + + error = 0; + if ((flags & NOTIFY_UIDGID) && (inode->i_uid != MSDOS_SB(inode->i_sb)-> + fs_uid || inode->i_gid != MSDOS_SB(inode->i_sb)->fs_gid)) { + inode->i_uid = MSDOS_SB(inode->i_sb)->fs_uid; + inode->i_gid = MSDOS_SB(inode->i_sb)->fs_gid; + error = -EPERM; + } + if (!(flags & NOTIFY_MODE)) + return error; + if (inode->i_mode & ~MSDOS_VALID_MODE) { + inode->i_mode &= MSDOS_VALID_MODE; + error = -EPERM; + } + if (IS_NOEXEC(inode) && !S_ISDIR(inode->i_mode)) + inode->i_mode &= S_IFMT | 0666; + else inode->i_mode |= 0111; + inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU + & ~MSDOS_SB(inode->i_sb)->fs_umask) | S_IRUSR) >> 6)*0111)) & + ~MSDOS_SB(inode->i_sb)->fs_umask; + return error; +} diff --git a/fs/msdos/misc.c b/fs/msdos/misc.c index e75f0f5..6f1aef5 100644 --- a/fs/msdos/misc.c +++ b/fs/msdos/misc.c @@ -1,9 +1,10 @@ /* * linux/fs/msdos/misc.c * - * Written 1992 by Werner Almesberger + * Written 1992,1993 by Werner Almesberger */ +#include <linux/fs.h> #include <linux/msdos_fs.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -11,14 +12,38 @@ #include <linux/string.h> #include <linux/stat.h> +/* Well-known binary file extensions */ + static char bin_extensions[] = - "EXECOMAPPSYSOVLOBJLIB" /* program code */ - "ARCZIPLHALZHZOOTARZ ARJTZ " /* common archivers */ - "GIFBMPTIFGL JPGPCX" /* graphics */ - "TFMVF GF PK PXLDVI"; /* TeX */ + "EXECOMAPPSYSOVLOBJLIB" /* program code */ + "ARCZIPLHALZHZOOTARZ ARJ" /* common archivers */ + "TZ TAZTZPTPZ" /* abbreviations of tar.Z and tar.zip */ + "GIFBMPTIFGL JPGPCX" /* graphics */ + "TFMVF GF PK PXLDVI"; /* TeX */ -/* Select binary/text conversion */ +/* + * fs_panic reports a severe file system problem and sets the file system + * read-only. The file system can be made writable again by remounting it. + */ + +void fs_panic(struct super_block *s,char *msg) +{ + int not_ro; + + not_ro = !(s->s_flags & MS_RDONLY); + if (not_ro) s->s_flags |= MS_RDONLY; + printk("Filesystem panic (dev 0x%04X, mounted on 0x%04X:%d)\n %s\n", + s->s_dev,s->s_covered->i_dev,s->s_covered->i_ino,msg); + if (not_ro) + printk(" File system has been set read-only\n"); +} + + +/* + * is_binary selects optional text conversion based on the conversion mode and + * the extension part of the file name. + */ int is_binary(char conversion,char *extension) { @@ -76,35 +101,34 @@ void unlock_fat(struct super_block *sb) } +/* + * msdos_add_cluster tries to allocate a new cluster and adds it to the file + * represented by inode. The cluster is zero-initialized. + */ + int msdos_add_cluster(struct inode *inode) { - static struct wait_queue *wait = NULL; - static int lock = 0; - static int previous = 0; /* works best if one FS is being used */ int count,this,limit,last,current,sector; void *data; struct buffer_head *bh; if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC; if (!MSDOS_SB(inode->i_sb)->free_clusters) return -ENOSPC; - while (lock) sleep_on(&wait); - lock = 1; lock_fat(inode->i_sb); limit = MSDOS_SB(inode->i_sb)->clusters; this = limit; /* to keep GCC happy */ for (count = 0; count < limit; count++) { - this = ((count+previous) % limit)+2; + this = ((count+MSDOS_SB(inode->i_sb)->prev_free) % limit)+2; if (fat_access(inode->i_sb,this,-1) == 0) break; } #ifdef DEBUG printk("free cluster: %d\n",this); #endif - previous = (count+previous+1) % limit; + MSDOS_SB(inode->i_sb)->prev_free = (count+MSDOS_SB(inode->i_sb)-> + prev_free+1) % limit; if (count >= limit) { MSDOS_SB(inode->i_sb)->free_clusters = 0; unlock_fat(inode->i_sb); - lock = 0; - wake_up(&wait); return -ENOSPC; } fat_access(inode->i_sb,this,MSDOS_SB(inode->i_sb)->fat_bits == 12 ? @@ -112,8 +136,6 @@ printk("free cluster: %d\n",this); if (MSDOS_SB(inode->i_sb)->free_clusters != -1) MSDOS_SB(inode->i_sb)->free_clusters--; unlock_fat(inode->i_sb); - lock = 0; - wake_up(&wait); #ifdef DEBUG printk("set to %x\n",fat_access(inode->i_sb,this,-1)); #endif @@ -122,8 +144,10 @@ printk("set to %x\n",fat_access(inode->i_sb,this,-1)); cache_lookup(inode,0x7fffffff,&last,¤t); while (current && current != -1) if (!(current = fat_access(inode->i_sb, - last = current,-1))) - panic("File without EOF"); + last = current,-1))) { + fs_panic(inode->i_sb,"File without EOF"); + return -ENOSPC; + } } #ifdef DEBUG printk("last = %d\n",last); @@ -145,7 +169,8 @@ printk("zeroing sector %d\n",sector); #endif if (current < MSDOS_SB(inode->i_sb)->cluster_size-1 && !(sector & 1)) { - if (!(bh = getblk(inode->i_dev,sector >> 1, BLOCK_SIZE))) + if (!(bh = getblk(inode->i_dev,sector >> 1, + BLOCK_SIZE))) printk("getblk failed\n"); else { memset(bh->b_data,0,BLOCK_SIZE); @@ -154,7 +179,8 @@ printk("zeroing sector %d\n",sector); current++; } else { - if (!(bh = msdos_sread(inode->i_dev,sector,&data))) + if (!(bh = msdos_sread(inode->i_dev,sector, + &data))) printk("msdos_sread failed\n"); else memset(data,0,SECTOR_SIZE); } @@ -165,8 +191,11 @@ printk("zeroing sector %d\n",sector); } inode->i_blocks += MSDOS_SB(inode->i_sb)->cluster_size; if (S_ISDIR(inode->i_mode)) { - if (inode->i_size & (SECTOR_SIZE-1)) - panic("Odd directory size"); + if (inode->i_size & (SECTOR_SIZE-1)) { + fs_panic(inode->i_sb,"Odd directory size"); + inode->i_size = (inode->i_size+SECTOR_SIZE) & + ~(SECTOR_SIZE-1); + } inode->i_size += SECTOR_SIZE*MSDOS_SB(inode->i_sb)-> cluster_size; #ifdef DEBUG @@ -246,7 +275,7 @@ int msdos_get_entry(struct inode *dir,int *pos,struct buffer_head **bh, if ((sector = msdos_smap(dir,offset >> SECTOR_BITS)) == -1) return -1; if (!sector) - return -1; /* FAT error ... */ + return -1; /* beyond EOF */ *pos += sizeof(struct msdos_dir_entry); if (*bh) brelse(*bh); @@ -262,101 +291,119 @@ int msdos_get_entry(struct inode *dir,int *pos,struct buffer_head **bh, } -/* Scans a directory for a given file (name points to its formatted name) or - for an empty directory slot (name is NULL). Returns the inode number. */ - -int msdos_scan(struct inode *dir,char *name,struct buffer_head **res_bh, - struct msdos_dir_entry **res_de,int *ino) -{ - int pos; - struct msdos_dir_entry *de; - struct inode *inode; - - pos = 0; - *res_bh = NULL; - while ((*ino = msdos_get_entry(dir,&pos,res_bh,&de)) > -1) { - if (name) { - if (de->name[0] && ((unsigned char *) (de->name))[0] - != DELETED_FLAG && !(de->attr & ATTR_VOLUME) && - !strncmp(de->name,name,MSDOS_NAME)) break; - } - else if (!de->name[0] || ((unsigned char *) (de->name))[0] == - DELETED_FLAG) { - if (!(inode = iget(dir->i_sb,*ino))) break; - if (!MSDOS_I(inode)->i_busy) { - iput(inode); - break; - } - /* skip deleted files that haven't been closed yet */ - iput(inode); - } - } - if (*ino == -1) { - if (*res_bh) brelse(*res_bh); - *res_bh = NULL; - return name ? -ENOENT : -ENOSPC; - } - *res_de = de; - return 0; -} - - -/* Now an ugly part: this set of directory scan routines works on clusters - rather than on inodes and sectors. They are necessary to locate the '..' - directory "inode". raw_found operates in three modes: if name is non-NULL, - the directory is scanned for an entry with that name. If ino is non-NULL, - the directory is scanned for an entry whose data starts at *number. If name - and ino are NULL, the directory entries are counted in *number. */ +/* + * Now an ugly part: this set of directory scan routines works on clusters + * rather than on inodes and sectors. They are necessary to locate the '..' + * directory "inode". raw_scan_sector operates in four modes: + * + * name number ino action + * -------- -------- -------- ------------------------------------------------- + * non-NULL - X Find an entry with that name + * NULL non-NULL non-NULL Find an entry whose data starts at *number + * NULL non-NULL NULL Count subdirectories in *number. (*) + * NULL NULL non-NULL Find an empty entry + * + * (*) The return code should be ignored. It DOES NOT indicate success or + * failure. *number has to be initialized to zero. + * + * - = not used, X = a value is returned unless NULL + * + * If res_bh is non-NULL, the buffer is not deallocated but returned to the + * caller on success. res_de is set accordingly. + * + * If cont is non-zero, raw_found continues with the entry after the one + * res_bh/res_de point to. + */ -static int raw_found(struct super_block *sb,int sector,char *name,int *number, - int *ino) +#define RSS_NAME /* search for name */ \ + done = !strncmp(data[entry].name,name,MSDOS_NAME) && \ + !(data[entry].attr & ATTR_VOLUME); + +#define RSS_START /* search for start cluster */ \ + done = !IS_FREE(data[entry].name) && CF_LE_W(data[entry].start) == *number; + +#define RSS_FREE /* search for free entry */ \ + { \ + done = IS_FREE(data[entry].name); \ + if (done) { \ + inode = iget(sb,sector*MSDOS_DPS+entry); \ + if (inode) { \ + /* Directory slots of busy deleted files aren't available yet. */ \ + done = !MSDOS_I(inode)->i_busy; \ + iput(inode); \ + } \ + } \ + } + +#define RSS_COUNT /* count subdirectories */ \ + { \ + done = 0; \ + if (!IS_FREE(data[entry].name) && (data[entry].attr & ATTR_DIR)) \ + (*number)++; \ + } + +static int raw_scan_sector(struct super_block *sb,int sector,char *name, + int *number,int *ino,struct buffer_head **res_bh, + struct msdos_dir_entry **res_de) { struct buffer_head *bh; struct msdos_dir_entry *data; + struct inode *inode; int entry,start,done; if (!(bh = msdos_sread(sb->s_dev,sector,(void **) &data))) return -EIO; for (entry = 0; entry < MSDOS_DPS; entry++) { - if (name) done = !strncmp(data[entry].name,name,MSDOS_NAME); + if (name) RSS_NAME else { - if (ino) - done = *(unsigned char *) data[entry].name != - DELETED_FLAG && data[entry].start == - *number; + if (!ino) RSS_COUNT else { - done = 0; - if (*data[entry].name && *(unsigned char *) - data[entry].name != DELETED_FLAG && - (data[entry].attr & ATTR_DIR)) (*number)++; + if (number) RSS_START + else RSS_FREE } } if (done) { if (ino) *ino = sector*MSDOS_DPS+entry; - start = data[entry].start; - brelse(bh); + start = CF_LE_W(data[entry].start); + if (!res_bh) brelse(bh); + else { + *res_bh = bh; + *res_de = &data[entry]; + } return start; } } brelse(bh); - return -1; + return -ENOENT; } -static int raw_scan_root(struct super_block *sb,char *name,int *number,int *ino) +/* + * raw_scan_root performs raw_scan_sector on the root directory until the + * requested entry is found or the end of the directory is reached. + */ + +static int raw_scan_root(struct super_block *sb,char *name,int *number,int *ino, + struct buffer_head **res_bh,struct msdos_dir_entry **res_de) { int count,cluster; for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) { - if ((cluster = raw_found(sb,MSDOS_SB(sb)->dir_start+count,name, - number,ino)) >= 0) return cluster; + if ((cluster = raw_scan_sector(sb,MSDOS_SB(sb)->dir_start+count, + name,number,ino,res_bh,res_de)) >= 0) return cluster; } return -ENOENT; } +/* + * raw_scan_nonroot performs raw_scan_sector on a non-root directory until the + * requested entry is found or the end of the directory is reached. + */ + static int raw_scan_nonroot(struct super_block *sb,int start,char *name, - int *number,int *ino) + int *number,int *ino,struct buffer_head **res_bh,struct msdos_dir_entry + **res_de) { int count,cluster; @@ -365,11 +412,15 @@ static int raw_scan_nonroot(struct super_block *sb,int start,char *name, #endif do { for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) { - if ((cluster = raw_found(sb,(start-2)*MSDOS_SB(sb)-> - cluster_size+MSDOS_SB(sb)->data_start+count,name, - number,ino)) >= 0) return cluster; + if ((cluster = raw_scan_sector(sb,(start-2)* + MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start+ + count,name,number,ino,res_bh,res_de)) >= 0) + return cluster; + } + if (!(start = fat_access(sb,start,-1))) { + fs_panic(sb,"FAT error"); + break; } - if (!(start = fat_access(sb,start,-1))) panic("FAT error"); #ifdef DEBUG printk("next start: %d\n",start); #endif @@ -379,34 +430,50 @@ static int raw_scan_nonroot(struct super_block *sb,int start,char *name, } -static int raw_scan(struct super_block *sb,int start,char *name,int number, - int *ino) +/* + * raw_scan performs raw_scan_sector on any sector. + * + * NOTE: raw_scan must not be used on a directory that is is the process of + * being created. + */ + +static int raw_scan(struct super_block *sb,int start,char *name,int *number, + int *ino,struct buffer_head **res_bh,struct msdos_dir_entry **res_de) { - if (start) return raw_scan_nonroot(sb,start,name,&number,ino); - else return raw_scan_root(sb,name,&number,ino); + if (start) + return raw_scan_nonroot(sb,start,name,number,ino,res_bh,res_de); + else return raw_scan_root(sb,name,number,ino,res_bh,res_de); } +/* + * msdos_parent_ino returns the inode number of the parent directory of dir. + * File creation has to be deferred while msdos_parent_ino is running to + * prevent renames. + */ + int msdos_parent_ino(struct inode *dir,int locked) { + static int zero = 0; int error,current,prev,this; if (!S_ISDIR(dir->i_mode)) panic("Non-directory fed to m_p_i"); if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino; if (!locked) lock_creation(); /* prevent renames */ - if ((current = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,0, - NULL)) < 0) { + if ((current = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT, + &zero,NULL,NULL,NULL)) < 0) { if (!locked) unlock_creation(); return current; } if (!current) this = MSDOS_ROOT_INO; else { - if ((prev = raw_scan(dir->i_sb,current,MSDOS_DOTDOT,0,NULL)) < - 0) { + if ((prev = raw_scan(dir->i_sb,current,MSDOS_DOTDOT,&zero,NULL, + NULL,NULL)) < 0) { if (!locked) unlock_creation(); return prev; } - if ((error = raw_scan(dir->i_sb,prev,NULL,current,&this)) < 0) { + if ((error = raw_scan(dir->i_sb,prev,NULL,¤t,&this,NULL, + NULL)) < 0) { if (!locked) unlock_creation(); return error; } @@ -416,17 +483,41 @@ int msdos_parent_ino(struct inode *dir,int locked) } +/* + * msdos_subdirs counts the number of sub-directories of dir. It can be run + * on directories being created. + */ + int msdos_subdirs(struct inode *dir) { int count; count = 0; if (dir->i_ino == MSDOS_ROOT_INO) - (void) raw_scan_root(dir->i_sb,NULL,&count,NULL); + (void) raw_scan_root(dir->i_sb,NULL,&count,NULL,NULL,NULL); else { if (!MSDOS_I(dir)->i_start) return 0; /* in mkdir */ else (void) raw_scan_nonroot(dir->i_sb,MSDOS_I(dir)->i_start, - NULL,&count,NULL); + NULL,&count,NULL,NULL,NULL); } return count; } + + +/* + * Scans a directory for a given file (name points to its formatted name) or + * for an empty directory slot (name is NULL). Returns an error code or zero. + */ + +int msdos_scan(struct inode *dir,char *name,struct buffer_head **res_bh, + struct msdos_dir_entry **res_de,int *ino) +{ + int res; + + if (name) + res = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,name,NULL,ino, + res_bh,res_de); + else res = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,NULL,NULL,ino, + res_bh,res_de); + return res < 0 ? res : 0; +} diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 79988a4..66b9ec7 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -1,7 +1,7 @@ /* * linux/fs/msdos/namei.c * - * Written 1992 by Werner Almesberger + * Written 1992,1993 by Werner Almesberger */ #include <asm/segment.h> @@ -25,35 +25,33 @@ static char *reserved_names[] = { /* Characters that are undesirable in an MS-DOS file name */ static char bad_chars[] = "*?<>|\""; -static char bad_if_strict[] = "+=,;"; +static char bad_if_strict[] = "+=,; "; /* Formats an MS-DOS file name. Rejects invalid names. */ -static int msdos_format_name(char conv,const char *name,int len,char *res) +static int msdos_format_name(char conv,const char *name,int len,char *res, + int dot_dirs) { char *walk,**reserved; char c; int space; - if (*(unsigned char *)name == DELETED_FLAG) return -EINVAL; - if (name[0] == '.' && (len == 1 || (len == 2 && - name[1] == '.'))) { + if (IS_FREE(name)) return -EINVAL; + if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) { + if (!dot_dirs) return -EEXIST; memset(res+1,' ',10); while (len--) *res++ = '.'; return 0; } - space = 0; /* to make GCC happy */ + space = 1; /* disallow names starting with a dot */ c = 0; for (walk = res; len && walk-res < 8; walk++) { - c = *(name++); + c = *name++; len--; if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL; - if (c >= 'A' && c <= 'Z') { - if (conv == 's') return -EINVAL; - c += 32; - } + if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL; if (c < ' ' || c == ':' || c == '\\') return -EINVAL; if (c == '.') break; space = c == ' '; @@ -61,26 +59,22 @@ static int msdos_format_name(char conv,const char *name,int len,char *res) } if (space) return -EINVAL; if (conv == 's' && len && c != '.') { - c = *(name++); + c = *name++; len--; if (c != '.') return -EINVAL; } - while (c != '.' && len--) c = *(name++); - if (walk == res) return -EINVAL; + while (c != '.' && len--) c = *name++; if (c == '.') { while (walk-res < 8) *walk++ = ' '; while (len > 0 && walk-res < MSDOS_NAME) { - c = *(name++); + c = *name++; len--; if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL; if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL; if (c < ' ' || c == ':' || c == '\\' || c == '.') return -EINVAL; - if (c >= 'A' && c <= 'Z') { - if (conv == 's') return -EINVAL; - c += 32; - } + if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL; space = c == ' '; *walk++ = c >= 'a' && c <= 'z' ? c-32 : c; } @@ -103,7 +97,7 @@ static int msdos_find(struct inode *dir,const char *name,int len, int res; if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len, - msdos_name)) < 0) return res; + msdos_name,1)) < 0) return res; return msdos_scan(dir,msdos_name,bh,de,ino); } @@ -126,8 +120,7 @@ int msdos_lookup(struct inode *dir,const char *name,int len, *result = dir; return 0; } - if (len == 2 && name[0] == '.' && name[1] == '.') - { + if (len == 2 && name[0] == '.' && name[1] == '.') { ino = msdos_parent_ino(dir,0); iput(dir); if (ino < 0) return ino; @@ -152,8 +145,11 @@ int msdos_lookup(struct inode *dir,const char *name,int len, while (MSDOS_I(*result)->i_old) { next = MSDOS_I(*result)->i_old; iput(*result); - if (!(*result = iget(next->i_sb,next->i_ino))) - panic("msdos_lookup: Can't happen"); + if (!(*result = iget(next->i_sb,next->i_ino))) { + fs_panic(dir->i_sb,"msdos_lookup: Can't happen"); + iput(dir); + return -ENOENT; + } } iput(dir); return 0; @@ -170,6 +166,7 @@ static int msdos_create_entry(struct inode *dir,char *name,int is_dir, int res,ino; if ((res = msdos_scan(dir,NULL,&bh,&de,&ino)) < 0) { + if (res != -ENOENT) return res; if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC; if ((res = msdos_add_cluster(dir)) < 0) return res; if ((res = msdos_scan(dir,NULL,&bh,&de,&ino)) < 0) return res; @@ -201,7 +198,7 @@ int msdos_create(struct inode *dir,const char *name,int len,int mode, if (!dir) return -ENOENT; if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len, - msdos_name)) < 0) { + msdos_name,0)) < 0) { iput(dir); return res; } @@ -226,7 +223,7 @@ static void dump_fat(struct super_block *sb,int start) printk("["); while (start) { printk("%d ",start); - start = fat_access(sb,start,-1); + start = fat_access(sb,start,-1); if (!start) { printk("ERROR"); break; @@ -248,7 +245,7 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode) int ino,res; if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len, - msdos_name)) < 0) { + msdos_name,0)) < 0) { iput(dir); return res; } @@ -289,7 +286,8 @@ int msdos_mkdir(struct inode *dir,const char *name,int len,int mode) return 0; mkdir_error: iput(inode); - if (msdos_rmdir(dir,name,len) < 0) panic("rmdir in mkdir failed"); + if (msdos_rmdir(dir,name,len) < 0) + fs_panic(dir->i_sb,"rmdir in mkdir failed"); unlock_creation(); return res; } @@ -304,9 +302,9 @@ int msdos_rmdir(struct inode *dir,const char *name,int len) bh = NULL; inode = NULL; - res = -EINVAL; - if (name[0] == '.' && (len == 1 || (len == 2 && - name[1] == '.'))) goto rmdir_done; + res = -EPERM; + if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) + goto rmdir_done; if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) goto rmdir_done; res = -ENOENT; if (!(inode = iget(dir->i_sb,ino))) goto rmdir_done; @@ -320,8 +318,7 @@ int msdos_rmdir(struct inode *dir,const char *name,int len) pos = 0; dbh = NULL; while (msdos_get_entry(inode,&pos,&dbh,&dde) > -1) - if (dde->name[0] && ((unsigned char *) dde->name)[0] != - DELETED_FLAG && strncmp(dde->name,MSDOS_DOT, + if (!IS_FREE(dde->name) && strncmp(dde->name,MSDOS_DOT, MSDOS_NAME) && strncmp(dde->name,MSDOS_DOTDOT, MSDOS_NAME)) goto rmdir_done; if (dbh) brelse(dbh); @@ -439,10 +436,13 @@ static int rename_diff_dir(struct inode *old_dir,char *old_name, if (!(walk = iget(new_dir->i_sb,ino))) return -EIO; } iput(walk); - if ((error = msdos_scan(new_dir,NULL,&free_bh,&free_de,&free_ino)) < 0) - return error; - exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino) - >= 0; + while ((error = msdos_scan(new_dir,NULL,&free_bh,&free_de,&free_ino)) < + 0) { + if (error != -ENOENT) return error; + error = msdos_add_cluster(new_dir); + if (error) return error; + } + exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino) >= 0; if (!(old_inode = iget(old_dir->i_sb,old_ino))) { brelse(free_bh); if (exists) brelse(new_bh); @@ -536,9 +536,9 @@ int msdos_rename(struct inode *old_dir,const char *old_name,int old_len, int old_ino,error; if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->name_check, - old_name,old_len,old_msdos_name)) < 0) goto rename_done; + old_name,old_len,old_msdos_name,1)) < 0) goto rename_done; if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->name_check, - new_name,new_len,new_msdos_name)) < 0) goto rename_done; + new_name,new_len,new_msdos_name,0)) < 0) goto rename_done; if ((error = msdos_scan(old_dir,old_msdos_name,&old_bh,&old_de, &old_ino)) < 0) goto rename_done; lock_creation(); diff --git a/fs/xiafs/bitmap.c b/fs/xiafs/bitmap.c index c85cc1e..03660d3 100644 --- a/fs/xiafs/bitmap.c +++ b/fs/xiafs/bitmap.c @@ -330,7 +330,8 @@ struct inode * xiafs_new_inode(struct inode * dir) inode->i_ino = tmp; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_op = NULL; - inode->i_blocks = inode->i_blksize = 0; + inode->i_blocks = 0; + inode->i_blksize = XIAFS_ZSIZE(inode->i_sb); return inode; } diff --git a/fs/xiafs/dir.c b/fs/xiafs/dir.c index d39947c..ebd41f9 100644 --- a/fs/xiafs/dir.c +++ b/fs/xiafs/dir.c @@ -101,8 +101,10 @@ static int xiafs_readdir(struct inode * inode, put_fs_long(de->d_ino,&dirent->d_ino); put_fs_word(i,&dirent->d_reclen); brelse(bh); - inode->i_atime=CURRENT_TIME; - inode->i_dirt=1; + if (!IS_RDONLY (inode)) { + inode->i_atime=CURRENT_TIME; + inode->i_dirt=1; + } return i; } de = (struct xiafs_direct *) (offset + bh->b_data); @@ -113,9 +115,9 @@ static int xiafs_readdir(struct inode * inode, return 0; } } - inode->i_atime=CURRENT_TIME; + if (!IS_RDONLY (inode)) { + inode->i_atime=CURRENT_TIME; + inode->i_dirt=1; + } return 0; } - - - diff --git a/fs/xiafs/file.c b/fs/xiafs/file.c index 4ccd327..4a0f34d 100644 --- a/fs/xiafs/file.c +++ b/fs/xiafs/file.c @@ -175,9 +175,10 @@ xiafs_file_read(struct inode * inode, struct file * filp, char * buf, int count) if (!read) return -EIO; filp->f_reada = 1; - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; - + if (!IS_RDONLY (inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } return read; } @@ -239,7 +240,7 @@ xiafs_file_write(struct inode * inode, struct file * filp, char * buf, int count bh->b_dirt = 1; brelse(bh); } - inode->i_mtime = CURRENT_TIME; + inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; filp->f_pos = pos; inode->i_dirt = 1; diff --git a/fs/xiafs/inode.c b/fs/xiafs/inode.c index 823cc76..4d6ac37 100644 --- a/fs/xiafs/inode.c +++ b/fs/xiafs/inode.c @@ -176,8 +176,10 @@ int xiafs_bmap(struct inode * inode,int zone) printk("XIA-FS: zone > big (%s %d)\n", WHERE_ERR); return 0; } - inode->i_atime = CURRENT_TIME; - inode->i_dirt = 1; + if (!IS_RDONLY (inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } if (zone < 8) return inode->u.xiafs_i.i_zone[zone]; zone -= 8; @@ -236,6 +238,7 @@ repeat: goto repeat; } *lp = tmp; + inode->i_blocks+=2 << XIAFS_ZSHIFT(inode->i_sb); return result; } @@ -279,17 +282,18 @@ repeat: } result = getblk(bh->b_dev, tmp, XIAFS_ZSIZE(inode->i_sb)); if (*lp) { - xiafs_free_zone(inode->i_sb,tmp); + xiafs_free_zone(inode->i_sb, tmp); brelse(result); goto repeat; } *lp = tmp; + inode->i_blocks+=2 << XIAFS_ZSHIFT(inode->i_sb); bh->b_dirt = 1; brelse(bh); return result; } -struct buffer_head * xiafs_getblk(struct inode * inode, int zone, int create) +struct buffer_head * xiafs_getblk(struct inode * inode, int zone, int create) { struct buffer_head * bh; u_long prev_addr=0; @@ -368,15 +372,16 @@ void xiafs_read_inode(struct inode * inode) inode->i_mtime = raw_inode->i_mtime; inode->i_atime = raw_inode->i_atime; inode->i_ctime = raw_inode->i_ctime; - inode->i_blocks = 0; - inode->i_blksize = 0; /* let vfs estimate */ - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) + inode->i_blksize = XIAFS_ZSIZE(inode->i_sb); + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { + inode->i_blocks=0; inode->i_rdev = raw_inode->i_zone[0]; - else { + } else { + XIAFS_GET_BLOCKS(raw_inode, inode->i_blocks); for (zone = 0; zone < 8; zone++) - inode->u.xiafs_i.i_zone[zone] = raw_inode->i_zone[zone]; - inode->u.xiafs_i.i_ind_zone = raw_inode->i_ind_zone; - inode->u.xiafs_i.i_dind_zone = raw_inode->i_dind_zone; + inode->u.xiafs_i.i_zone[zone] = raw_inode->i_zone[zone] & 0xffffff; + inode->u.xiafs_i.i_ind_zone = raw_inode->i_ind_zone & 0xffffff; + inode->u.xiafs_i.i_dind_zone = raw_inode->i_dind_zone & 0xffffff; } brelse(bh); if (S_ISREG(inode->i_mode)) @@ -406,6 +411,12 @@ void xiafs_write_inode(struct inode * inode) int zone; ino_t ino; + if (IS_RDONLY (inode)) { + printk("XIA-FS: write_inode on a read-only filesystem (%s %d)\n", WHERE_ERR); + inode->i_dirt = 0; + return; + } + ino = inode->i_ino; if (!ino || ino > inode->i_sb->u.xiafs_sb.s_ninodes) { printk("XIA-FS: bad inode number (%s %d)\n", WHERE_ERR); @@ -427,20 +438,20 @@ void xiafs_write_inode(struct inode * inode) raw_inode->i_gid = inode->i_gid; raw_inode->i_nlinks = inode->i_nlink; raw_inode->i_size = inode->i_size; - if (inode->i_ctime < inode->i_mtime) - inode->i_ctime=inode->i_mtime; - if (inode->i_atime < inode->i_ctime) - inode->i_atime=inode->i_ctime; raw_inode->i_atime = inode->i_atime; raw_inode->i_ctime = inode->i_ctime; raw_inode->i_mtime = inode->i_mtime; if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) raw_inode->i_zone[0] = inode->i_rdev; else { + XIAFS_PUT_BLOCKS(raw_inode, inode->i_blocks); for (zone = 0; zone < 8; zone++) - raw_inode->i_zone[zone] = inode->u.xiafs_i.i_zone[zone]; - raw_inode->i_ind_zone = inode->u.xiafs_i.i_ind_zone; - raw_inode->i_dind_zone = inode->u.xiafs_i.i_dind_zone; + raw_inode->i_zone[zone] = (raw_inode->i_zone[zone] & 0xff000000) + | (inode->u.xiafs_i.i_zone[zone] & 0xffffff); + raw_inode->i_ind_zone = (raw_inode->i_ind_zone & 0xff000000) + | (inode->u.xiafs_i.i_ind_zone & 0xffffff); + raw_inode->i_dind_zone = (raw_inode->i_dind_zone & 0xff000000) + | (inode->u.xiafs_i.i_dind_zone & 0xffffff); } inode->i_dirt=0; bh->b_dirt=1; diff --git a/fs/xiafs/namei.c b/fs/xiafs/namei.c index 49703ee..faaf466 100644 --- a/fs/xiafs/namei.c +++ b/fs/xiafs/namei.c @@ -212,7 +212,7 @@ static struct buffer_head * xiafs_add_entry(struct inode * dir, } } if (!de->d_ino && RNDUP4(namelen)+8 <= de->d_rec_len) { - dir->i_mtime = CURRENT_TIME; + dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_dirt = 1; memcpy(de->d_name, name, namelen); de->d_name[namelen]=0; @@ -316,7 +316,7 @@ int xiafs_mknod(struct inode *dir, const char *name, int len, int mode, int rdev } if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = rdev; - inode->i_mtime = inode->i_atime = CURRENT_TIME; + inode->i_atime = inode->i_ctime = inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; bh = xiafs_add_entry(dir, name, len, &de, NULL); if (!bh) { @@ -357,7 +357,7 @@ int xiafs_mkdir(struct inode * dir, const char * name, int len, int mode) } inode->i_op = &xiafs_dir_inode_operations; inode->i_size = XIAFS_ZSIZE(dir->i_sb); - inode->i_mtime = inode->i_atime = CURRENT_TIME; + inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; dir_block = xiafs_bread(inode,0,1); if (!dir_block) { iput(dir); @@ -524,7 +524,7 @@ int xiafs_rmdir(struct inode * dir, const char * name, int len) inode->i_nlink=0; inode->i_dirt=1; dir->i_nlink--; - dir->i_mtime = CURRENT_TIME; + dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_dirt=1; retval = 0; end_rmdir: @@ -569,10 +569,9 @@ repeat: } xiafs_rm_entry(de, de_pre); bh->b_dirt = 1; - dir->i_mtime = CURRENT_TIME; + dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->i_dirt = 1; inode->i_nlink--; - inode->i_ctime = CURRENT_TIME; inode->i_dirt = 1; retval = 0; end_unlink: @@ -668,7 +667,7 @@ int xiafs_link(struct inode * oldinode, struct inode * dir, brelse(bh); iput(dir); oldinode->i_nlink++; - oldinode->i_ctime = CURRENT_TIME; + oldinode->i_atime = oldinode->i_ctime = CURRENT_TIME; oldinode->i_dirt = 1; iput(oldinode); return 0; diff --git a/fs/xiafs/symlink.c b/fs/xiafs/symlink.c index cd68698..e3e963b 100644 --- a/fs/xiafs/symlink.c +++ b/fs/xiafs/symlink.c @@ -57,8 +57,10 @@ static int xiafs_readlink(struct inode * inode, char * buffer, int buflen) if (buflen > BLOCK_SIZE) buflen = BLOCK_SIZE; bh = xiafs_bread(inode, 0, 0); - inode->i_atime=CURRENT_TIME; - inode->i_dirt=1; + if (!IS_RDONLY (inode)) { + inode->i_atime=CURRENT_TIME; + inode->i_dirt=1; + } iput(inode); if (!bh) return 0; @@ -90,8 +92,10 @@ static int xiafs_follow_link(struct inode * dir, struct inode * inode, *res_inode = inode; return 0; } - inode->i_atime=CURRENT_TIME; - inode->i_dirt=1; + if (!IS_RDONLY (inode)) { + inode->i_atime=CURRENT_TIME; + inode->i_dirt=1; + } if (current->link_count > 5) { iput(inode); iput(dir); diff --git a/fs/xiafs/truncate.c b/fs/xiafs/truncate.c index e6a0d63..7b95b37 100644 --- a/fs/xiafs/truncate.c +++ b/fs/xiafs/truncate.c @@ -60,6 +60,7 @@ repeat: else { *lp = 0; inode->i_dirt = 1; + inode->i_blocks-=2 << XIAFS_ZSHIFT(inode->i_sb); xiafs_free_zone(inode->i_sb, tmp); } brelse(bh); @@ -105,6 +106,7 @@ repeat: else { *indp = 0; ind_bh->b_dirt = 1; + inode->i_blocks-= 2 << XIAFS_ZSHIFT(inode->i_sb); xiafs_free_zone(inode->i_sb, tmp); } brelse(bh); @@ -117,6 +119,7 @@ repeat: else { tmp = *lp; *lp = 0; + inode->i_blocks-= 2 << XIAFS_ZSHIFT(inode->i_sb); xiafs_free_zone(inode->i_sb, tmp); } } @@ -166,6 +169,7 @@ repeat: tmp = *lp; *lp = 0; inode->i_dirt = 1; + inode->i_blocks-=2 << XIAFS_ZSHIFT(inode->i_sb); xiafs_free_zone(inode->i_sb, tmp); } } @@ -189,7 +193,7 @@ void xiafs_truncate(struct inode * inode) current->counter = 0; schedule(); } - inode->i_mtime = CURRENT_TIME; + inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; inode->i_dirt = 1; } diff --git a/fs/xiafs/xiafs_mac.h b/fs/xiafs/xiafs_mac.h index f3cfdae..2c3509e 100644 --- a/fs/xiafs/xiafs_mac.h +++ b/fs/xiafs/xiafs_mac.h @@ -17,4 +17,16 @@ extern char internal_error_message[]; #define XIAFS_BITS_PER_Z_BITS(sp) (BLOCK_SIZE_BITS + 3 + XIAFS_ZSHIFT(sp)) #define XIAFS_INODES_PER_Z(sp) (_XIAFS_INODES_PER_BLOCK << XIAFS_ZSHIFT(sp)) +/* Use the most significant bytes of zone pointers to store block counter. */ +/* This is ugly, but it works. Note, We have another 7 bytes for "expension". */ +#define XIAFS_GET_BLOCKS(row_ip, blocks) \ + blocks=((((row_ip)->i_zone[0] >> 24) & 0xff )|\ + (((row_ip)->i_zone[1] >> 16) & 0xff00 )|\ + (((row_ip)->i_zone[2] >> 8) & 0xff0000 ) ) + +/* XIAFS_PUT_BLOCKS should be called before saving zone pointers */ +#define XIAFS_PUT_BLOCKS(row_ip, blocks) \ + (row_ip)->i_zone[2]=((blocks)<< 8) & 0xff000000;\ + (row_ip)->i_zone[1]=((blocks)<<16) & 0xff000000;\ + (row_ip)->i_zone[0]=((blocks)<<24) & 0xff000000 diff --git a/include/linux/mm.h b/include/linux/mm.h index e69c24d..16ebe8d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -95,7 +95,6 @@ extern int copy_page_tables(struct task_struct * new); extern int unmap_page_range(unsigned long from, unsigned long size); extern int remap_page_range(unsigned long from, unsigned long to, unsigned long size, int mask); extern int zeromap_page_range(unsigned long from, unsigned long size, int mask); -extern void write_verify(unsigned long address); extern void do_wp_page(unsigned long error_code, unsigned long address, struct task_struct *tsk, unsigned long user_esp); diff --git a/include/linux/msdos_fs.h b/include/linux/msdos_fs.h index 12b9711..cb87458 100644 --- a/include/linux/msdos_fs.h +++ b/include/linux/msdos_fs.h @@ -6,6 +6,8 @@ */ #include <linux/fs.h> +#include <linux/stat.h> +#include <linux/fd.h> #define MSDOS_ROOT_INO 1 /* == MINIX_ROOT_INO */ #define SECTOR_SIZE 512 /* sector size (bytes) */ @@ -32,6 +34,11 @@ /* attribute bits that are copied "as is" */ #define DELETED_FLAG 0xe5 /* marks file as deleted when in name[0] */ +#define IS_FREE(n) (!*(n) || *(unsigned char *) (n) == DELETED_FLAG || \ + *(unsigned char *) (n) == FD_FILL_BYTE) + +#define MSDOS_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO) + /* valid file mode bits */ #define MSDOS_SB(s) (&((s)->u.msdos_sb)) #define MSDOS_I(i) (&((i)->u.msdos_i)) @@ -40,10 +47,26 @@ #define MSDOS_DOT ". " /* ".", padded to MSDOS_NAME chars */ #define MSDOS_DOTDOT ".. " /* "..", padded to MSDOS_NAME chars */ -#define MSDOS_FAT12 4086 /* maximum number of clusters in a 12 bit FAT */ +#define MSDOS_FAT12 4078 /* maximum number of clusters in a 12 bit FAT */ + +/* + * Conversion from and to little-endian byte order. (no-op on i386/i486) + * + * Naming: Ca_b_c, where a: F = from, T = to, b: LE = little-endian, BE = big- + * endian, c: W = word (16 bits), L = longword (32 bits) + */ + +#define CF_LE_W(v) (v) +#define CF_LE_L(v) (v) +#define CT_LE_W(v) (v) +#define CT_LE_L(v) (v) + struct msdos_boot_sector { - char ignored[13]; + char ignored[3]; /* Boot strap short or near jump */ + char system_id[8]; /* Name - can be used to special case + partition manager volumes */ + unsigned char sector_size[2];/* bytes per logical sector */ unsigned char cluster_size; /* sectors/cluster */ unsigned short reserved; /* reserved sectors */ unsigned char fats; /* number of FATs */ @@ -51,8 +74,8 @@ struct msdos_boot_sector { unsigned char sectors[2]; /* number of sectors */ unsigned char media; /* media code (unused) */ unsigned short fat_length; /* sectors/FAT */ - unsigned short secs_track; /* sectors per track (unused) */ - unsigned short heads; /* number of heads (unused) */ + unsigned short secs_track; /* sectors per track */ + unsigned short heads; /* number of heads */ unsigned long hidden; /* hidden sectors (unused) */ unsigned long total_sect; /* number of sectors (if sectors == 0) */ }; @@ -80,11 +103,11 @@ struct fat_cache { /* Convert attribute bits and a mask to the UNIX mode. */ -#define MSDOS_MKMODE(a,m) (m & (a & ATTR_RO ? 0444 : 0777)) +#define MSDOS_MKMODE(a,m) (m & (a & ATTR_RO ? 0555 : 0777)) /* Convert the UNIX mode to MS-DOS attribute bits. */ -#define MSDOS_MKATTR(m) (!(m & 0200) ? ATTR_RO : ATTR_NONE) +#define MSDOS_MKATTR(m) ((m & 0200) ? ATTR_NONE : ATTR_RO) static inline struct buffer_head *msdos_sread(int dev,int sector,void **start) @@ -100,6 +123,7 @@ static inline struct buffer_head *msdos_sread(int dev,int sector,void **start) /* misc.c */ +extern void fs_panic(struct super_block *s,char *msg); extern int is_binary(char conversion,char *extension); extern void lock_creation(void); extern void unlock_creation(void); @@ -149,6 +173,7 @@ extern void msdos_statfs(struct super_block *sb,struct statfs *buf); extern int msdos_bmap(struct inode *inode,int block); extern void msdos_read_inode(struct inode *inode); extern void msdos_write_inode(struct inode *inode); +extern int msdos_notify_change(int flags,struct inode *inode); /* dir.c */ diff --git a/include/linux/msdos_fs_i.h b/include/linux/msdos_fs_i.h index 4dacb18..21eb999 100644 --- a/include/linux/msdos_fs_i.h +++ b/include/linux/msdos_fs_i.h @@ -2,8 +2,9 @@ #define _MSDOS_FS_I /* - * msdos file system inode data in memory + * MS-DOS file system inode data in memory */ + struct msdos_inode_info { int i_start; /* first cluster or 0 */ int i_attrs; /* unused attribute bits */ diff --git a/include/linux/msdos_fs_sb.h b/include/linux/msdos_fs_sb.h index 3ed1273..025d250 100644 --- a/include/linux/msdos_fs_sb.h +++ b/include/linux/msdos_fs_sb.h @@ -1,6 +1,10 @@ #ifndef _MSDOS_FS_SB #define _MSDOS_FS_SB +/* + * MS-DOS file system in-core superblock data + */ + struct msdos_sb_info { unsigned short cluster_size; /* sectors/cluster */ unsigned char fats,fat_bits; /* number of FATs, FAT bits (12 or 16) */ @@ -15,6 +19,7 @@ struct msdos_sb_info { unsigned char conversion; /* b = binary, t = text, a = auto */ struct wait_queue *fat_wait; int fat_lock; + int prev_free; /* previously returned free cluster number */ int free_clusters; /* -1 if undefined */ }; diff --git a/include/linux/sched.h b/include/linux/sched.h index 460dd72..d853c4f 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -143,16 +143,16 @@ struct tss_struct { struct task_struct { /* these are hardcoded - don't touch */ - long state; /* -1 unrunnable, 0 runnable, >0 stopped */ + long state; /* -1 unrunnable, 0 runnable, >0 stopped */ long counter; long priority; - long signal; + unsigned long signal; + unsigned long blocked; /* bitmap of masked signals */ + unsigned long flags; /* per process flags, defined below */ +/* various fields */ struct sigaction sigaction[32]; - long blocked; /* bitmap of masked signals */ unsigned long saved_kernel_stack; unsigned long kernel_stack_page; - unsigned int flags; /* per process flags, defined below */ -/* various fields */ int exit_code; int dumpable:1; int swappable:1; @@ -220,9 +220,9 @@ struct task_struct { * your own risk!. Base=0, limit=0x1fffff (=2MB) */ #define INIT_TASK \ -/* state etc */ { 0,15,15, \ -/* signals */ 0,{{ 0, },},0,0,0, \ -/* flags */ 0, \ +/* state etc */ { 0,15,15,0,0,0, \ +/* signals */ {{ 0, },}, \ +/* stack */ 0,0, \ /* ec,brk... */ 0,0,0,0,0,0,0,0, \ /* argv.. */ 0,0,0,0, \ /* pid etc.. */ 0,0,0,0, \ @@ -275,7 +275,7 @@ extern void interruptible_sleep_on(struct wait_queue ** p); extern void wake_up(struct wait_queue ** p); extern void wake_up_interruptible(struct wait_queue ** p); -extern int send_sig(long sig,struct task_struct * p,int priv); +extern int send_sig(unsigned long sig,struct task_struct * p,int priv); extern int in_group_p(gid_t grp); extern int request_irq(unsigned int irq,void (*handler)(int)); diff --git a/include/linux/sys.h b/include/linux/sys.h index 45ae421..d6c15ea 100644 --- a/include/linux/sys.h +++ b/include/linux/sys.h @@ -119,6 +119,8 @@ extern int sys_vm86(); extern int sys_wait4(); extern int sys_swapoff(); extern int sys_sysinfo(); +extern int sys_ipc(); +extern int sys_fsync(); fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read, sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link, @@ -141,7 +143,7 @@ sys_ftruncate, sys_fchmod, sys_fchown, sys_getpriority, sys_setpriority, sys_profil, sys_statfs, sys_fstatfs, sys_ioperm, sys_socketcall, sys_syslog, sys_setitimer, sys_getitimer, sys_newstat, sys_newlstat, sys_newfstat, sys_newuname, sys_iopl, sys_vhangup, sys_idle, sys_vm86, -sys_wait4, sys_swapoff, sys_sysinfo }; +sys_wait4, sys_swapoff, sys_sysinfo, sys_ipc, sys_fsync }; /* So we don't have to do any more manual updating.... */ int NR_syscalls = sizeof(sys_call_table)/sizeof(fn_ptr); diff --git a/include/linux/timer.h b/include/linux/timer.h index 03ee1d3..868b2a3 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -22,6 +22,8 @@ * SCSI_TIMER scsi.c timeout timer * * NET_TIMER tcp/ip timeout timer + * + * MISC_TIMER reserved for special uses like the 387 timeouts etc */ #define BLANK_TIMER 0 @@ -33,6 +35,7 @@ #define SCSI_TIMER 18 #define NET_TIMER 19 #define SOUND_TIMER 20 +#define MISC_TIMER 21 struct timer_struct { unsigned long expires; diff --git a/include/linux/tty.h b/include/linux/tty.h index 4f7eaf0..eb18e64 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -99,6 +99,7 @@ struct serial_struct { #define ASYNC_HUP_NOTIFY 0x0001 /* Notify blocked open on hangups */ #define ASYNC_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */ #define ASYNC_SAK 0x0004 /* Secure Attention Key (Orange book) */ +#define ASYNC_SKIP_TEST 0x0008 /* Skip UART test on bootup */ #define ASYNC_SPD_MASK 0x0030 #define ASYNC_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */ diff --git a/include/linux/xd.h b/include/linux/xd.h new file mode 100644 index 0000000..3ff0844 --- /dev/null +++ b/include/linux/xd.h @@ -0,0 +1,143 @@ +#ifndef _LINUX_XD_H +#define _LINUX_XD_H + +/* + * This file contains the definitions for the IO ports and errors etc. for XT hard disk controllers (at least the DTC 5150X). + * + * Author: Pat Mackinlay, smackinla@cc.curtin.edu.au + * Date: 29/09/92 + * + * Revised: 01/01/93, ... + * + * Ref: DTC 5150X Controller Specification (thanks to Kevin Fowler, kevinf@agora.rain.com) + * Also thanks to: Salvador Abreu, Dave Thaler, Risto Kankkunen and Wim Van Dorst. + */ + +/* XT hard disk controller registers */ +#define XD_DATA (xd_iobase + 0x00) /* data RW register */ +#define XD_RESET (xd_iobase + 0x01) /* reset WO register */ +#define XD_STATUS (xd_iobase + 0x01) /* status RO register */ +#define XD_SELECT (xd_iobase + 0x02) /* select WO register */ +#define XD_JUMPER (xd_iobase + 0x02) /* jumper RO register */ +#define XD_CONTROL (xd_iobase + 0x03) /* DMAE/INTE WO register */ +#define XD_RESERVED (xd_iobase + 0x03) /* reserved */ + +/* XT hard disk controller commands (incomplete list) */ +#define CMD_TESTREADY 0x00 /* test drive ready */ +#define CMD_RECALIBRATE 0x01 /* recalibrate drive */ +#define CMD_SENSE 0x03 /* request sense */ +#define CMD_FORMATDRV 0x04 /* format drive */ +#define CMD_VERIFY 0x05 /* read verify */ +#define CMD_FORMATTRK 0x06 /* format track */ +#define CMD_FORMATBAD 0x07 /* format bad track */ +#define CMD_READ 0x08 /* read */ +#define CMD_WRITE 0x0A /* write */ +#define CMD_SEEK 0x0B /* seek */ + +/* Controller specific commands */ +#define CMD_DTCSETPARAM 0x0C /* set drive parameters (DTC 5150X only?) */ +#define CMD_DTCGETECC 0x0D /* get ecc error length (DTC 5150X only?) */ +#define CMD_DTCREADBUF 0x0E /* read sector buffer (DTC 5150X only?) */ +#define CMD_DTCWRITEBUF 0x0F /* write sector buffer (DTC 5150X only?) */ +#define CMD_DTCREMAPTRK 0x11 /* assign alternate track (DTC 5150X only?) */ +#define CMD_DTCGETPARAM 0xFB /* get drive parameters (DTC 5150X only?) */ +#define CMD_DTCSETSTEP 0xFC /* set step rate (DTC 5150X only?) */ +#define CMD_DTCSETGEOM 0xFE /* set geometry data (DTC 5150X only?) */ +#define CMD_DTCGETGEOM 0xFF /* get geometry data (DTC 5150X only?) */ +#define CMD_ST11GETGEOM 0xF8 /* get geometry data (Seagate ST11R/M only?) */ +#define CMD_WDSETPARAM 0x0C /* set drive parameters (WD 1004A27X only?) */ + +/* Bits for command status byte */ +#define CSB_ERROR 0x02 /* error */ +#define CSB_LUN 0x20 /* logical Unit Number */ + +/* XT hard disk controller status bits */ +#define STAT_READY 0x01 /* controller is ready */ +#define STAT_INPUT 0x02 /* data flowing from controller to host */ +#define STAT_COMMAND 0x04 /* controller in command phase */ +#define STAT_SELECT 0x08 /* controller is selected */ +#define STAT_REQUEST 0x10 /* controller requesting data */ +#define STAT_INTERRUPT 0x20 /* controller requesting interrupt */ + +/* XT hard disk controller control bits */ +#define PIO_MODE 0x00 /* control bits to set for PIO */ +#define DMA_MODE 0x03 /* control bits to set for DMA & interrupt */ + +#define XD_MAXDRIVES 2 /* maximum 2 drives */ +#define XD_TIMEOUT 100 /* 1 second timeout */ +#define XD_RETRIES 4 /* maximum 4 retries */ + +#undef DEBUG /* define for debugging output */ +#undef XD_OVERRIDE /* define to override auto-detection */ + +#ifdef DEBUG + #define DEBUG_STARTUP /* debug driver initialisation */ + #define DEBUG_OVERRIDE /* debug override geometry detection */ + #define DEBUG_READWRITE /* debug each read/write command */ + #define DEBUG_OTHER /* debug misc. interrupt/DMA stuff */ + #define DEBUG_COMMAND /* debug each controller command */ +#endif DEBUG + +/* this structure defines the XT drives and their types */ +typedef struct { + u_char heads; + u_short cylinders; + u_char sectors; + u_char control; +} XD_INFO; + +#define HDIO_GETGEO 0x0301 /* get drive geometry */ + +/* this structure is returned to the HDIO_GETGEO ioctl */ +typedef struct { + u_char heads; + u_char sectors; + u_short cylinders; + u_long start; +} XD_GEOMETRY; + +/* this structure defines a ROM BIOS signature */ +typedef struct { + u_long offset; + u_char *string; + void (*init_controller)(u_char *address); + void (*init_drive)(u_char drive); + u_char *name; +} XD_SIGNATURE; + +extern void resetup_one_dev (struct gendisk *dev,unsigned int drive); + +u_long xd_init(u_long mem_start,u_long mem_end); +static u_char xd_detect (u_char *controller,u_char **address); +static u_char xd_initdrives (void (*init_drive)(u_char drive)); +static void xd_geninit (void); + +static int xd_open (struct inode *inode,struct file *file); +static void do_xd_request (void); +static int xd_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned int arg); +static void xd_release (struct inode *inode,struct file *file); +static int xd_reread_partitions (int dev); +static int xd_readwrite (u_char operation,u_char drive,u_char *buffer,u_int block,u_int count); +static void xd_recalibrate (u_char drive); + +static void xd_interrupt_handler (int unused); +static u_char xd_setup_dma (u_char opcode,u_char *buffer,u_int count); +static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control); +static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout); +static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout); + +/* card specific setup and geometry gathering code */ +#ifndef XD_OVERRIDE +static void xd_dtc5150x_init_controller (u_char *address); +static void xd_dtc5150x_init_drive (u_char drive); +static void xd_wd1004a27x_init_controller (u_char *address); +static void xd_wd1004a27x_init_drive (u_char drive); +static void xd_seagate11_init_controller (u_char *address); +static void xd_seagate11_init_drive (u_char drive); +static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc); +#endif XD_OVERRIDE + +static void xd_override_init_controller (u_char *address); +static void xd_override_init_drive (u_char drive); + +#endif _LINUX_XD_H diff --git a/init/main.c b/init/main.c index 72b03d4..09d7d44 100644 --- a/init/main.c +++ b/init/main.c @@ -18,6 +18,7 @@ #include <linux/head.h> #include <linux/unistd.h> #include <linux/string.h> +#include <linux/timer.h> extern unsigned long * prof_buffer; extern unsigned long prof_len; @@ -203,6 +204,17 @@ static void parse_options(char *line) envp_init[envs+1] = NULL; } +static void copro_timeout(void) +{ +#ifdef CONFIG_MATH_EMULATION + printk(" Trying to use software floating point\n"); + hard_math = 0; + __asm__("movl %%cr0,%%eax ; xorl $6,%%eax ; movl %%eax,%%cr0":::"ax"); +#else + printk(" No software floating point - tough cookies\n"); +#endif +} + void start_kernel(void) { /* @@ -266,13 +278,19 @@ void start_kernel(void) if (hard_math) { unsigned short control_word; - printk("Checking for 387 error mechanism ..."); + timer_table[MISC_TIMER].expires = jiffies+100; + timer_table[MISC_TIMER].fn = copro_timeout; + timer_active |= 1<<MISC_TIMER; + printk("You have a bad 386/387 coupling."); __asm__("fninit ; fnstcw %0 ; fwait":"=m" (*&control_word)); control_word &= 0xffc0; __asm__("fldcw %0 ; fwait"::"m" (*&control_word)); outb_p(inb_p(0x21) | (1 << 2), 0x21); __asm__("fldz ; fld1 ; fdiv %st,%st(1) ; fwait"); - printk(" ok, using %s.\n",ignore_irq13?"exception 16":"irq13"); + timer_active &= ~(1<<MISC_TIMER); + if (hard_math) + printk("\rMath coprocessor using %s error reporting.\n", + ignore_irq13?"exception 16":"irq13"); } move_to_user_mode(); if (!fork()) /* we count on this going ok */ diff --git a/kernel/blk_drv/Makefile b/kernel/blk_drv/Makefile index 618a623..621e6f7 100644 --- a/kernel/blk_drv/Makefile +++ b/kernel/blk_drv/Makefile @@ -18,7 +18,7 @@ SUBDIRS = scsi -OBJS = hd.o ll_rw_blk.o floppy.o ramdisk.o genhd.o +OBJS = xd.o hd.o ll_rw_blk.o floppy.o ramdisk.o genhd.o all: blk_drv.a scsisubdirs @@ -38,6 +38,9 @@ dep: $(CPP) -M *.c > .depend for i in $(SUBDIRS); do (cd $$i && $(MAKE) dep); done +xd.o: + $(CC) $(CFLAGS) -fno-omit-frame-pointer $(RAMDISK) -c $< + dummy: # diff --git a/kernel/blk_drv/blk.h b/kernel/blk_drv/blk.h index e054c49..e771184 100644 --- a/kernel/blk_drv/blk.h +++ b/kernel/blk_drv/blk.h @@ -78,6 +78,8 @@ extern void rd_load(void); extern long rd_init(long mem_start, int length); extern int ramdisk_size; +extern unsigned long xd_init(unsigned long mem_start, unsigned long mem_end); + #define RO_IOCTLS(dev,where) \ case BLKROSET: if (!suser()) return -EPERM; \ set_device_ro((dev),get_fs_long((long *) (where))); return 0; \ @@ -147,6 +149,14 @@ extern int ramdisk_size; #define DEVICE_ON(device) #define DEVICE_OFF(device) +#elif (MAJOR_NR == 13) +/* xt hard disk */ +#define DEVICE_NAME "xt disk" +#define DEVICE_REQUEST do_xd_request +#define DEVICE_NR(device) (MINOR(device) >> 6) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + #else /* unknown blk device */ #error "unknown blk device" diff --git a/kernel/blk_drv/ll_rw_blk.c b/kernel/blk_drv/ll_rw_blk.c index 583d501..8deb1e2 100644 --- a/kernel/blk_drv/ll_rw_blk.c +++ b/kernel/blk_drv/ll_rw_blk.c @@ -386,6 +386,9 @@ long blk_dev_init(long mem_start, long mem_end) #ifdef CONFIG_BLK_DEV_HD mem_start = hd_init(mem_start,mem_end); #endif +#ifdef CONFIG_BLK_DEV_XD + mem_start = xd_init(mem_start,mem_end); +#endif if (ramdisk_size) mem_start += rd_init(mem_start, ramdisk_size*1024); return mem_start; diff --git a/kernel/blk_drv/scsi/ultrastor.c b/kernel/blk_drv/scsi/ultrastor.c index f02dd4b..c41968a 100644 --- a/kernel/blk_drv/scsi/ultrastor.c +++ b/kernel/blk_drv/scsi/ultrastor.c @@ -91,8 +91,8 @@ struct config { unsigned char interrupt: 4; unsigned char dma_channel: 3; unsigned char bios_drive_number: 1; - unsigned char heads: 6; - unsigned char sectors: 6; + unsigned char heads; + unsigned char sectors; unsigned char ha_scsi_id: 3; unsigned char subversion: 4; }; diff --git a/kernel/blk_drv/xd.c b/kernel/blk_drv/xd.c new file mode 100644 index 0000000..daf8a7a --- /dev/null +++ b/kernel/blk_drv/xd.c @@ -0,0 +1,619 @@ +/* + * This file contains the driver for an XT hard disk controller (at least the DTC 5150X) for Linux. + * + * Author: Pat Mackinlay, smackinla@cc.curtin.edu.au + * Date: 29/09/92 + * + * Revised: 01/01/93, ... + * + * Ref: DTC 5150X Controller Specification (thanks to Kevin Fowler, kevinf@agora.rain.com) + * Also thanks to: Salvador Abreu, Dave Thaler, Risto Kankkunen and Wim Van Dorst. + */ + +#include <linux/config.h> +#ifdef CONFIG_BLK_DEV_XD + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/genhd.h> +#include <linux/xd.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/segment.h> +#include <asm/dma.h> + +#define MAJOR_NR 13 +#include "blk.h" + +XD_INFO xd_info[XD_MAXDRIVES]; + +/* If you try this driver and find that your card is not detected by the driver at bootup, you need to add your BIOS + signature and details to the following list of signatures. A BIOS signature is a string embedded into the first + few bytes of your controller's on-board ROM BIOS. To find out what yours is, use something like MS-DOS's DEBUG + command. Run DEBUG, and then you can examine your BIOS signature with: + + d xxxx:0000 + + where xxxx is the segment of your controller (like C800 or D000 or something). On the ASCII dump at the right, you should + be able to see a string mentioning the manufacturer's copyright etc. Add this string into the table below. The parameters + in the table are, in order: + + offset ; this is the offset (in bytes) from the start of your ROM where the signature starts + signature ; this is the actual text of the signature + xd_?_init_controller ; this is the controller init routine used by your controller + xd_?_init_drive ; this is the drive init routine used by your controller + + The controllers directly supported at the moment are: DTC 5150x, WD 1004A27X, ST11M/R and override. If your controller is + made by the same manufacturer as one of these, try using the same init routines as they do. If that doesn't work, your + best bet is to use the "override" routines. These routines use a "portable" method of getting the disk's geometry, and + may work with your card. If none of these seem to work, try sending me some email and I'll see what I can do <grin>. */ + +static XD_SIGNATURE xd_sigs[] = { + { 0x0000,"Override geometry handler",xd_override_init_controller,xd_override_init_drive,"n unknown" }, /* Pat Mackinlay, smackinla@cc.curtin.edu.au (pat@gu.uwa.edu.au) */ +#ifndef XD_OVERRIDE + { 0x000B,"CXD23A Not an IBM ROM (C)Copyright Data Technology Corp 12/03/88",xd_dtc5150x_init_controller,xd_dtc5150x_init_drive," DTC 5150X" }, /* Pat Mackinlay, smackinla@cc.curtin.edu.au (pat@gu.uwa.edu.au) */ + { 0x0008,"07/15/86 (C) Copyright 1986 Western Digital Corp",xd_wd1004a27x_init_controller,xd_wd1004a27x_init_drive," Western Digital 1002AWX1" }, /* Ian Justman, citrus!ianj@csusac.ecs.csus.edu */ + { 0x0008,"06/24/88 (C) Copyright 1988 Western Digital Corp",xd_wd1004a27x_init_controller,xd_wd1004a27x_init_drive," Western Digital 1004A27X" }, /* Dave Thaler, thalerd@engin.umich.edu */ + { 0x0015,"SEAGATE ST11 BIOS REVISION",xd_seagate11_init_controller,xd_seagate11_init_drive," Seagate ST11M/R" }, /* Salvador Abreu, spa@fct.unl.pt */ + { 0x0010,"ST11R BIOS",xd_seagate11_init_controller,xd_seagate11_init_drive," Seagate ST11M/R" }, /* Risto Kankkunen, risto.kankkunen@cs.helsinki.fi */ +#endif XD_OVERRIDE +}; +#ifndef XD_OVERRIDE +static u_char *xd_bases[] = { (u_char *) 0xC8000,(u_char *) 0xCA000,(u_char *) 0xD0000,(u_char *) 0xD8000,(u_char *) 0xE0000 }; +#endif XD_OVERRIDE + +static struct hd_struct xd[XD_MAXDRIVES << 6]; +static int xd_sizes[XD_MAXDRIVES << 6],xd_access[XD_MAXDRIVES] = { 0,0 }; +static struct gendisk xd_gendisk = { MAJOR_NR,"xd",6,1 << 6,XD_MAXDRIVES,xd_geninit,xd,xd_sizes,0,(void *) xd_info,NULL }; +static struct file_operations xd_fops = { NULL,block_read,block_write,NULL,NULL,xd_ioctl,NULL,xd_open,xd_release }; + +static struct wait_queue *xd_wait_exclusive = NULL,*xd_wait_int = NULL,*xd_wait_open = NULL; +static u_char xd_valid[XD_MAXDRIVES] = { 0,0 }; +static u_char xd_busy = 0,xd_drives = 0; +static u_char xd_irq,xd_dma,xd_maxsectors; +static u_short xd_iobase; + +/* xd_init: grab the IRQ and DMA channel and initialise the drives */ +u_long xd_init (u_long mem_start,u_long mem_end) +{ + u_char i,controller,*address; + + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + if (register_blkdev(MAJOR_NR,"xd",&xd_fops)) { + printk("xd_init: unable to get major number %d\n",MAJOR_NR); + return (mem_start); + } + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ + xd_gendisk.next = gendisk_head; + gendisk_head = &xd_gendisk; + + if (xd_detect(&controller,&address)) { + + printk("xd_init: detected a%s controller (type %d) at address %p\n",xd_sigs[controller].name,controller,address); + xd_sigs[controller].init_controller(address); + xd_drives = xd_initdrives(xd_sigs[controller].init_drive); + + printk("xd_init: detected %d hard drive%s (using IRQ%d & DMA%d)\n",xd_drives,xd_drives == 1 ? "" : "s",xd_irq,xd_dma); + for (i = 0; i < xd_drives; i++) + printk("xd_init: drive %d geometry - heads = %d, cylinders = %d, sectors = %d\n",i,xd_info[i].heads,xd_info[i].cylinders,xd_info[i].sectors); + + if (!request_irq(xd_irq,xd_interrupt_handler)) { + if (request_dma(xd_dma)) { + printk("xd_init: unable to get DMA%d\n",xd_dma); + free_irq(xd_irq); + } + } + else + printk("xd_init: unable to get IRQ%d\n",xd_irq); + } + return mem_start; +} + +/* xd_detect: scan the possible BIOS ROM locations for the signature strings */ +static u_char xd_detect (u_char *controller,u_char **address) +{ +#ifndef XD_OVERRIDE + u_char i,j,found = 0; + + for (i = 0; i < (sizeof(xd_bases) / sizeof(xd_bases[0])) && !found; i++) + for (j = 1; j < (sizeof(xd_sigs) / sizeof(xd_sigs[0])) && !found; j++) + if (!memcmp((u_char *) (xd_bases[i] + xd_sigs[j].offset),xd_sigs[j].string,strlen(xd_sigs[j].string))) { + *controller = j; + *address = xd_bases[i]; + found++; + } + return (found); +#else + *controller = 0; + *address = NULL; + return (1); +#endif XD_OVERRIDE +} + +/* xd_geninit: set up the "raw" device entries in the table */ +static void xd_geninit (void) +{ + u_char i; + + for (i = 0; i < xd_drives; i++) { + xd[i << 6].nr_sects = xd_info[i].heads * xd_info[i].cylinders * xd_info[i].sectors; + xd_valid[i] = 1; + } + + xd_gendisk.nr_real = xd_drives; +} + +/* xd_open: open a device */ +static int xd_open (struct inode *inode,struct file *file) +{ + int target = DEVICE_NR(MINOR(inode->i_rdev)); + + while (!xd_valid[target]) + sleep_on(&xd_wait_open); + + xd_access[target]++; + + return (0); +} + +/* do_xd_request: handle an incoming request */ +static void do_xd_request (void) +{ + u_int block,count,retry; + int code; + + sti(); + while (code = 0, CURRENT) { + INIT_REQUEST; /* do some checking on the request structure */ + + if (CURRENT_DEV < xd_drives && CURRENT->sector + CURRENT->nr_sectors <= xd[MINOR(CURRENT->dev)].nr_sects) { + block = CURRENT->sector + xd[MINOR(CURRENT->dev)].start_sect; + count = CURRENT->nr_sectors; + + switch (CURRENT->cmd) { + case READ: + case WRITE: for (retry = 0; (retry < XD_RETRIES) && !code; retry++) + code = xd_readwrite(CURRENT->cmd,CURRENT_DEV,CURRENT->buffer,block,count); + break; + default: printk("do_xd_request: unknown request\n"); break; + } + } + end_request(code); /* wrap up, 0 = fail, 1 = success */ + } +} + +/* xd_ioctl: handle device ioctl's */ +static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_int arg) +{ + XD_GEOMETRY *geometry = (XD_GEOMETRY *) arg; + int dev = DEVICE_NR(MINOR(inode->i_rdev)),err; + + if (inode && (dev < xd_drives)) + switch (cmd) { + case HDIO_GETGEO: if (arg) { + if ((err = verify_area(VERIFY_WRITE,geometry,sizeof(*geometry)))) + return (err); + put_fs_byte(xd_info[dev].heads,(char *) &geometry->heads); + put_fs_byte(xd_info[dev].sectors,(char *) &geometry->sectors); + put_fs_word(xd_info[dev].cylinders,(short *) &geometry->cylinders); + put_fs_long(xd[MINOR(inode->i_rdev)].start_sect,(long *) &geometry->start); + + return (0); + } + break; + case BLKGETSIZE: if (arg) { + if ((err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long)))) + return (err); + put_fs_long(xd[MINOR(inode->i_rdev)].nr_sects,(long *) arg); + + return (0); + } + break; + case BLKRRPART: return (xd_reread_partitions(inode->i_rdev)); + RO_IOCTLS(inode->i_rdev,arg); + } + return (-EINVAL); +} + +/* xd_release: release the device */ +static void xd_release (struct inode *inode, struct file *file) +{ + sync_dev(inode->i_rdev); + xd_access[DEVICE_NR(MINOR(inode->i_rdev))]--; +} + +/* xd_reread_partitions: rereads the partition table from a drive */ + +/* xd_reread_partitions: rereads the partition table from a drive */ +static int xd_reread_partitions(int dev) +{ + int target = DEVICE_NR(MINOR(dev)),start = target << xd_gendisk.minor_shift,partition; + + cli(); xd_valid[target] = (xd_access[target] != 1); sti(); + if (xd_valid[target]) + return (-EBUSY); + + for (partition = xd_gendisk.max_p - 1; partition >= 0; partition--) { + sync_dev(MAJOR_NR << 8 | start | partition); + invalidate_inodes(MAJOR_NR << 8 | start | partition); + invalidate_buffers(MAJOR_NR << 8 | start | partition); + xd_gendisk.part[start + partition].start_sect = 0; + xd_gendisk.part[start + partition].nr_sects = 0; + }; + + xd_gendisk.part[start].nr_sects = xd_info[target].heads * xd_info[target].cylinders * xd_info[target].sectors; + resetup_one_dev(&xd_gendisk,target); + + xd_valid[target] = 1; + wake_up(&xd_wait_open); + + return (0); +} + +/* xd_readwrite: handle a read/write request */ +static int xd_readwrite (u_char operation,u_char drive,u_char *buffer,u_int block,u_int count) +{ + u_char cmdblk[6],sense[4]; + u_short track,cylinder; + u_char head,sector,control,mode,temp; + +#ifdef DEBUG_READWRITE + printk("xd_readwrite: operation = %s, drive = %d, buffer = 0x%X, block = %d, count = %d\n",operation == READ ? "read" : "write",drive,buffer,block,count); +#endif DEBUG_READWRITE + + control = xd_info[drive].control; + while (count) { + temp = count < xd_maxsectors ? count : xd_maxsectors; + + track = block / xd_info[drive].sectors; + head = track % xd_info[drive].heads; + cylinder = track / xd_info[drive].heads; + sector = block % xd_info[drive].sectors; + +#ifdef DEBUG_READWRITE + printk("xd_readwrite: drive = %d, head = %d, cylinder = %d, sector = %d, count = %d\n",drive,head,cylinder,sector,temp); +#endif DEBUG_READWRITE + + if (xd_busy) /* get exclusive access to the controller */ + sleep_on(&xd_wait_exclusive); + xd_busy = 1; + + mode = xd_setup_dma(operation == READ ? DMA_MODE_READ : DMA_MODE_WRITE,buffer,temp * 0x200); + xd_build(cmdblk,operation == READ ? CMD_READ : CMD_WRITE,drive,head,cylinder,sector,temp & 0xFF,control); + + switch (xd_command(cmdblk,mode,buffer,buffer,sense,XD_TIMEOUT)) { + case 1: printk("xd_readwrite: timeout, recalibrating drive\n"); xd_recalibrate(drive); xd_busy = 0; wake_up(&xd_wait_exclusive); return (0); + case 2: switch ((sense[0] & 0x30) >> 4) { + case 0: printk("xd_readwrite: drive error, code = 0x%X",sense[0] & 0x0F); break; + case 1: printk("xd_readwrite: controller error, code = 0x%X",sense[0] & 0x0F); break; + case 2: printk("xd_readwrite: command error, code = 0x%X",sense[0] & 0x0F); break; + case 3: printk("xd_readwrite: miscellaneous error, code = 0x%X",sense[0] & 0x0F); break; + } + if (sense[0] & 0x80) + printk(" - drive = %d, head = %d, cylinder = %d, sector = %d\n",sense[1] & 0xE0,sense[1] & 0x1F,((sense[2] & 0xC0) << 2) | sense[3],sense[2] & 0x3F); + else + printk(" - no valid disk address\n"); + xd_busy = 0; wake_up(&xd_wait_exclusive); + return (0); + } + count -= temp, buffer += temp * 0x200, block += temp; + + xd_busy = 0; wake_up(&xd_wait_exclusive); + } + return (1); +} + +/* xd_recalibrate: recalibrate a given drive and reset controller if necessary */ +static void xd_recalibrate (u_char drive) +{ + u_char cmdblk[6]; + + xd_build(cmdblk,CMD_RECALIBRATE,drive,0,0,0,0,0); + if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 8)) + printk("xd_recalibrate: warning! error recalibrating, controller may be unstable\n"); +} + +/* xd_interrupt_handler: interrupt service routine */ +static void xd_interrupt_handler (int unused) +{ + if (inb(XD_STATUS) & STAT_INTERRUPT) { /* check if it was our device */ +#ifdef DEBUG_OTHER + printk("xd_interrupt_handler: interrupt detected\n"); +#endif DEBUG_OTHER + outb(0,XD_CONTROL); /* acknowledge interrupt */ + wake_up(&xd_wait_int); /* and wake up sleeping processes */ + } + else + printk("xd_interrupt_handler: unexpected interrupt\n"); +} + +/* xd_dma: set up the DMA controller for a data transfer */ +static u_char xd_setup_dma (u_char mode,u_char *buffer,u_int count) +{ + if (buffer < ((u_char *) 0x1000000 - count)) { /* transfer to address < 16M? */ + if (((u_int) buffer & 0xFFFF0000) != ((u_int) buffer + count) & 0xFFFF0000) { +#ifdef DEBUG_OTHER + printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n"); +#endif DEBUG_OTHER + return (PIO_MODE); + } + disable_dma(xd_dma); + clear_dma_ff(xd_dma); + set_dma_mode(xd_dma,mode); + set_dma_addr(xd_dma,(u_int) buffer); + set_dma_count(xd_dma,count); + + return (DMA_MODE); /* use DMA and INT */ + } +#ifdef DEBUG_OTHER + printk("xd_setup_dma: using PIO, cannot DMA above 16 meg\n"); +#endif DEBUG_OTHER + return (PIO_MODE); +} + +/* xd_build: put stuff into an array in a format suitable for the controller */ +static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control) +{ + cmdblk[0] = command; + cmdblk[1] = ((drive & 0x07) << 5) | (head & 0x1F); + cmdblk[2] = ((cylinder & 0x300) >> 2) | (sector & 0x3F); + cmdblk[3] = cylinder & 0xFF; + cmdblk[4] = count; + cmdblk[5] = control; + + return (cmdblk); +} + +/* xd_waitport: waits until port & mask == flags or a timeout occurs. return 1 for a timeout */ +static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout) +{ + u_long expiry = jiffies + timeout; + + while (((inb(port) & mask) != flags) && (jiffies < expiry)) + ; + + return (jiffies >= expiry); +} + +/* xd_command: handle all data transfers necessary for a single command */ +static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout) +{ + u_char cmdblk[6],csb,complete = 0; + +#ifdef DEBUG_COMMAND + printk("xd_command: command = 0x%X, mode = 0x%X, indata = 0x%X, outdata = 0x%X, sense = 0x%X\n",command,mode,indata,outdata,sense); +#endif DEBUG_COMMAND + + outb(0,XD_SELECT); + outb(mode,XD_CONTROL); + + if (xd_waitport(XD_STATUS,STAT_SELECT,STAT_SELECT,timeout)) + return (1); + + while (!complete) { + if (xd_waitport(XD_STATUS,STAT_READY,STAT_READY,timeout)) + return (1); + switch (inb(XD_STATUS) & (STAT_COMMAND | STAT_INPUT)) { + case 0: if (mode == DMA_MODE) { + enable_dma(xd_dma); + sleep_on(&xd_wait_int); + disable_dma(xd_dma); + } + else + outb(outdata ? *outdata++ : 0,XD_DATA); + break; + case STAT_INPUT: if (mode == DMA_MODE) { + enable_dma(xd_dma); + sleep_on(&xd_wait_int); + disable_dma(xd_dma); + } + else + if (indata) + *indata++ = inb(XD_DATA); + else + inb(XD_DATA); + break; + case STAT_COMMAND: outb(command ? *command++ : 0,XD_DATA); break; + case STAT_COMMAND + | STAT_INPUT: complete = 1; break; + } + } + csb = inb(XD_DATA); + + if (xd_waitport(XD_STATUS,0,STAT_SELECT,timeout)) /* wait until deselected */ + return (1); + + if (csb & CSB_ERROR) { /* read sense data if error */ + xd_build(cmdblk,CMD_SENSE,(csb & CSB_LUN) >> 5,0,0,0,0,0); + if (xd_command(cmdblk,0,sense,0,0,XD_TIMEOUT)) + printk("xd_command: warning! sense command failed!\n"); + } + +#ifdef DEBUG_COMMAND + printk("xd_command: completed with csb = 0x%X\n",csb); +#endif DEBUG_COMMAND + + return (csb & CSB_ERROR); +} + +static u_char xd_initdrives (void (*init_drive)(u_char drive)) +{ + u_char cmdblk[6],i,count = 0; + + for (i = 0; i < XD_MAXDRIVES; i++) { + xd_build(cmdblk,CMD_TESTREADY,i,0,0,0,0,0); + if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) { + init_drive(count); + count++; + } + } + return (count); +} + +#ifndef XD_OVERRIDE +static void xd_dtc5150x_init_controller (u_char *address) +{ + switch ((u_long) address) { + case 0xC8000: xd_iobase = 0x320; break; + case 0xCA000: xd_iobase = 0x324; break; + } + xd_irq = 5; /* the IRQ _can_ be changed on this card, but requires a hardware mod */ + xd_dma = 3; + xd_maxsectors = 0x01; /* my card seems to have trouble doing multi-block transfers? */ + + outb(0,XD_RESET); /* reset the controller */ +} + +static void xd_dtc5150x_init_drive (u_char drive) +{ + u_char cmdblk[6],buf[64]; + + xd_build(cmdblk,CMD_DTCGETGEOM,drive,0,0,0,0,0); + if (!xd_command(cmdblk,PIO_MODE,buf,0,0,XD_TIMEOUT * 2)) { + xd_info[drive].heads = buf[0x0A]; /* heads */ + xd_info[drive].cylinders = ((u_short *) (buf))[0x04]; /* cylinders */ + xd_info[drive].sectors = 17; /* sectors */ +#if 0 + xd_info[drive].rwrite = ((u_short *) (buf + 1))[0x05]; /* reduced write */ + xd_info[drive].precomp = ((u_short *) (buf + 1))[0x06]; /* write precomp */ + xd_info[drive].ecc = buf[0x0F]; /* ecc length */ +#endif 0 + xd_info[drive].control = 0; /* control byte */ + + xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf + 1))[0x05],((u_short *) (buf + 1))[0x06],buf[0x0F]); + xd_build(cmdblk,CMD_DTCSETSTEP,drive,0,0,0,0,7); + if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) + printk("xd_dtc5150x_init_drive: error setting step rate for drive %d\n",drive); + } + else + printk("xd_dtc5150x_init_drive: error reading geometry for drive %d\n",drive); +} + +static void xd_wd1004a27x_init_controller (u_char *address) +{ + switch ((u_long) address) { + case 0xC8000: xd_iobase = 0x320; break; + case 0xCA000: xd_iobase = 0x324; break; + case 0xD0000: xd_iobase = 0x328; break; + case 0xD8000: xd_iobase = 0x32C; break; + } + xd_irq = 5; /* don't know how to auto-detect this yet */ + xd_dma = 3; + xd_maxsectors = 0x01; /* this one doesn't wrap properly either... */ + + outb(0,XD_RESET); /* reset the controller */ +} + +static void xd_wd1004a27x_init_drive (u_char drive) +{ + u_char cmdblk[6],buf[0x200]; + + xd_build(cmdblk,CMD_READ,drive,0,0,0,1,0); + if (!xd_command(cmdblk,PIO_MODE,buf,0,0,XD_TIMEOUT * 2)) { + xd_info[drive].heads = buf[0x1AF]; /* heads */ + xd_info[drive].cylinders = ((u_short *) (buf + 1))[0xD6]; /* cylinders */ + xd_info[drive].sectors = 17; /* sectors */ +#if 0 + xd_info[drive].rwrite = ((u_short *) (buf))[0xD8]; /* reduced write */ + xd_info[drive].wprecomp = ((u_short *) (buf))[0xDA]; /* write precomp */ + xd_info[drive].ecc = buf[0x1B4]; /* ecc length */ +#endif 0 + xd_info[drive].control = buf[0x1B5]; /* control byte */ + + xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf))[0xD8],((u_short *) (buf))[0xDA],buf[0x1B4]); + } + else + printk("xd_wd1004a27x_init_drive: error reading geometry for drive %d\n",drive); +} + +static void xd_seagate11_init_controller (u_char *address) +{ + switch ((u_long) address) { + case 0xC8000: xd_iobase = 0x320; break; + case 0xD0000: xd_iobase = 0x324; break; + case 0xD8000: xd_iobase = 0x328; break; + case 0xE0000: xd_iobase = 0x32C; break; + } + xd_irq = 5; /* the IRQ and DMA channel are fixed on the Seagate controllers */ + xd_dma = 3; + xd_maxsectors = 0x40; + + outb(0,XD_RESET); /* reset the controller */ +} + +static void xd_seagate11_init_drive (u_char drive) +{ + u_char cmdblk[6],buf[0x200]; + + xd_build(cmdblk,CMD_ST11GETGEOM,drive,0,0,0,1,0); + if (!xd_command(cmdblk,PIO_MODE,buf,0,0,XD_TIMEOUT * 2)) { + xd_info[drive].heads = buf[0x04]; /* heads */ + xd_info[drive].cylinders = (buf[0x02] << 8) | buf[0x03]; /* cylinders */ + xd_info[drive].sectors = buf[0x05]; /* sectors */ + xd_info[drive].control = 0; /* control byte */ + } + else + printk("xd_seagate11_init_drive: error reading geometry from drive %d\n",drive); +} +#endif XD_OVERRIDE + +/* xd_override_init_controller: sets appropriate values for unknown controllers. */ +static void xd_override_init_controller (u_char *address) +{ + xd_iobase = 0x320; /* standard setting */ + xd_irq = 5; /* ditto */ + xd_dma = 3; /* ditto */ + xd_maxsectors = 0x01; + + outb(0,XD_RESET); /* reset the controller */ +} + +/* xd_override_init_drive: this finds disk geometry in a "binary search" style, narrowing in on the "correct" number of heads + etc. by trying values until it gets the highest successful value. Idea courtesy Salvador Abreu (spa@fct.unl.pt). */ +static void xd_override_init_drive (u_char drive) +{ + u_short min[] = { 0,0,0 },max[] = { 16,1024,64 },test[] = { 0,0,0 }; + u_char cmdblk[6],i; + + for (i = 0; i < 3; i++) { + while (min[i] != max[i] - 1) { + test[i] = (min[i] + max[i]) / 2; + xd_build(cmdblk,CMD_SEEK,drive,(u_char) test[0],(u_short) test[1],(u_char) test[2],0,0); + if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) + min[i] = test[i]; + else + max[i] = test[i]; + } + test[i] = min[i]; + } + xd_info[drive].heads = (u_char) min[0] + 1; + xd_info[drive].cylinders = (u_short) min[1] + 1; + xd_info[drive].sectors = (u_char) min[2] + 1; + xd_info[drive].control = 0; +} + +#ifndef XD_OVERRIDE +/* xd_setparam: set the drive characteristics */ +static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc) +{ + u_char cmdblk[14]; + + xd_build(cmdblk,command,drive,0,0,0,0,0); + cmdblk[6] = (u_char) (cylinders >> 8) & 0x03; + cmdblk[7] = (u_char) (cylinders & 0xFF); + cmdblk[8] = heads & 0x1F; + cmdblk[9] = (u_char) (rwrite >> 8) & 0x03; + cmdblk[10] = (u_char) (rwrite & 0xFF); + cmdblk[11] = (u_char) (wprecomp >> 8) & 0x03; + cmdblk[12] = (u_char) (wprecomp & 0xFF); + cmdblk[13] = ecc; + + if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2)) + printk("xd_setparam: error setting characteristics for drive %d\n",drive); +} +#endif XD_OVERRIDE + +#endif CONFIG_BLK_DEV_XD diff --git a/kernel/chr_drv/console.c b/kernel/chr_drv/console.c index a0aa4dd..32bec47 100644 --- a/kernel/chr_drv/console.c +++ b/kernel/chr_drv/console.c @@ -54,6 +54,7 @@ #define NPAR 16 extern void vt_init(void); +extern void register_console(void (*proc)(const char *)); unsigned long video_num_columns; /* Number of text columns */ unsigned long video_num_lines; /* Number of test lines */ @@ -1244,6 +1245,7 @@ long con_init(long kmem_start) long base; int orig_x = ORIG_X; int orig_y = ORIG_Y; + void console_print(const char * b); vc_scrmembuf = (unsigned short *) kmem_start; video_num_columns = ORIG_VIDEO_COLS; @@ -1334,6 +1336,7 @@ long con_init(long kmem_start) display_desc, video_num_columns,video_num_lines, NR_CONSOLES); + register_console(console_print); return kmem_start; } diff --git a/kernel/chr_drv/keyboard.c b/kernel/chr_drv/keyboard.c index 803491f..30d4e83 100644 --- a/kernel/chr_drv/keyboard.c +++ b/kernel/chr_drv/keyboard.c @@ -40,6 +40,7 @@ extern void do_keyboard_interrupt(void); extern void ctrl_alt_del(void); extern void change_console(unsigned int new_console); +extern void fake_keyboard_interrupt(void); unsigned long kbd_flags = 0; unsigned long kbd_dead_keys = 0; @@ -75,23 +76,6 @@ static inline void kb_wait(void) break; } -/* - * send_cmd() sends a command byte to the keyboard. - */ -static inline void send_cmd(unsigned char c) -{ - kb_wait(); - outb(c,0x64); -} - -static inline unsigned char get_scancode(void) -{ - kb_wait(); - if (inb_p(0x64) & 0x01) - return inb(0x60); - return 0; -} - static void keyboard_interrupt(int int_pt_regs) { static unsigned char rep = 0xff; @@ -102,8 +86,11 @@ static void keyboard_interrupt(int int_pt_regs) if (!kbd_dead_keys) kbd_prev_dead_keys = 0; kbd_dead_keys = 0; - send_cmd(0xAD); - scancode = get_scancode(); + kb_wait(); + if (!(inb_p(0x64) & 0x01)) + goto end_kbd_intr; + scancode = inb(0x60); + mark_bh(KEYBOARD_BH); if (scancode == 0xfa) { acknowledge = 1; goto end_kbd_intr; @@ -145,8 +132,6 @@ static void keyboard_interrupt(int int_pt_regs) key_table[scancode](scancode); rep = scancode; end_kbd_intr: - send_cmd(0xAE); - mark_bh(KEYBOARD_BH); } static void put_queue(int ch) @@ -1116,13 +1101,15 @@ unsigned int handle_diacr(unsigned int ch) { static unsigned char diacr_table[] = {'`', 180, '^', '~', 168, 0}; /* Must end with 0 */ + static unsigned char ret_diacr[] = + {'`', '\'', '^', '~', '"' }; /* Must not end with 0 */ int i; for(i=0; diacr_table[i]; i++) if (ch==diacr_table[i] && ((1<<i)&kbd->kbd_flags)) { if (diacr == i) { diacr=-1; - return ch; /* pressed twice */ + return ret_diacr[i]; /* pressed twice */ } else { diacr=i; /* key is dead */ return 0; @@ -1131,7 +1118,7 @@ unsigned int handle_diacr(unsigned int ch) if (diacr == -1) return ch; else if (ch == ' ') { - ch=diacr_table[diacr]; + ch=ret_diacr[diacr]; diacr=-1; return ch; } else if (ch<64 || ch>122) { @@ -1366,6 +1353,10 @@ static void kbd_bh(void * unused) want_console = -1; } do_keyboard_interrupt(); + cli(); + if (inb_p(0x64) & 0x01) + fake_keyboard_interrupt(); + sti(); } long no_idt[2] = {0, 0}; @@ -1475,6 +1466,6 @@ unsigned long kbd_init(unsigned long kmem_start) } bh_base[KEYBOARD_BH].routine = kbd_bh; request_irq(KEYBOARD_IRQ,keyboard_interrupt); - keyboard_interrupt(0); + mark_bh(KEYBOARD_BH); return kmem_start; } diff --git a/kernel/chr_drv/lp.c b/kernel/chr_drv/lp.c index eb8c00b..679634f 100644 --- a/kernel/chr_drv/lp.c +++ b/kernel/chr_drv/lp.c @@ -299,6 +299,8 @@ static int lp_open(struct inode * inode, struct file * file) sa.sa_restorer = NULL; ret = irqaction(irq, &sa); if (ret) { + kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE); + lp_table[minor].lp_buffer = NULL; printk("lp%d unable to use interrupt %d, error %d\n", irq, ret); return ret; } @@ -317,6 +319,7 @@ static void lp_release(struct inode * inode, struct file * file) if ((irq = LP_IRQ(minor))) { free_irq(irq); kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE); + lp_table[minor].lp_buffer = NULL; } LP_F(minor) &= ~LP_BUSY; @@ -353,28 +356,48 @@ static int lp_ioctl(struct inode *inode, struct file *file, case LPSETIRQ: { int ret; int oldirq; + int newirq = arg; + struct lp_struct *lp = &lp_table[minor]; struct sigaction sa; if (!suser()) return -EPERM; - if ((oldirq = LP_IRQ(minor))) { + oldirq = LP_IRQ(minor); + + /* Allocate buffer now if we are going to need it */ + if (!oldirq && newirq) { + if (!(lp->lp_buffer = kmalloc(LP_BUFFER_SIZE, GFP_KERNEL))) + return -ENOMEM; + } + + if (oldirq) { free_irq(oldirq); } - if (arg) { + if (newirq) { /* Install new irq */ sa.sa_handler = lp_interrupt; sa.sa_flags = SA_INTERRUPT; sa.sa_mask = 0; sa.sa_restorer = NULL; - if ((ret = irqaction(arg, &sa))) { - if (oldirq) + if ((ret = irqaction(newirq, &sa))) { + if (oldirq) { /* restore old irq */ irqaction(oldirq, &sa); + } else { + /* We don't need the buffer */ + kfree_s(lp->lp_buffer, LP_BUFFER_SIZE); + lp->lp_buffer = NULL; + } return ret; } } - LP_IRQ(minor) = arg; + if (oldirq && !newirq) { + /* We don't need the buffer */ + kfree_s(lp->lp_buffer, LP_BUFFER_SIZE); + lp->lp_buffer = NULL; + } + LP_IRQ(minor) = newirq; lp_reset(minor); break; } @@ -405,8 +428,10 @@ long lp_init(long kmem_start) unsigned int testvalue = 0; int count = 0; - if (register_chrdev(6,"lp",&lp_fops)) + if (register_chrdev(6,"lp",&lp_fops)) { printk("unable to get major 6 for line printer\n"); + return kmem_start; + } /* take on all known port values */ for (offset = 0; offset < LP_NO; offset++) { /* write to port & read back to check */ diff --git a/kernel/chr_drv/serial.c b/kernel/chr_drv/serial.c index b5926d7..0637af9 100644 --- a/kernel/chr_drv/serial.c +++ b/kernel/chr_drv/serial.c @@ -51,7 +51,7 @@ * port. * */ - + #define WAKEUP_CHARS (3*TTY_BUF_SIZE/4) /* @@ -78,58 +78,59 @@ static int IRQ_timeout[16]; #define BASE_BAUD ( 1843200 / 16 ) struct async_struct rs_table[] = { - { BASE_BAUD, 0x3F8, 4, 0, }, - { BASE_BAUD, 0x2F8, 3, 0, }, - { BASE_BAUD, 0x3E8, 4, 0, }, - { BASE_BAUD, 0x2E8, 3, 0, }, + /* UART CLK PORT IRQ FLAGS */ + { BASE_BAUD, 0x3F8, 4, ASYNC_SKIP_TEST, }, /* ttyS0 */ + { BASE_BAUD, 0x2F8, 3, ASYNC_SKIP_TEST, }, /* ttyS1 */ + { BASE_BAUD, 0x3E8, 4, ASYNC_SKIP_TEST, }, /* ttyS2 */ + { BASE_BAUD, 0x2E8, 3, ASYNC_SKIP_TEST, }, /* ttyS3 */ #ifdef CONFIG_AST_FOURPORT - { BASE_BAUD, 0x1A0, 2, ASYNC_FOURPORT }, - { BASE_BAUD, 0x1A8, 2, ASYNC_FOURPORT }, - { BASE_BAUD, 0x1B0, 2, ASYNC_FOURPORT }, - { BASE_BAUD, 0x1B8, 2, ASYNC_FOURPORT }, - - { BASE_BAUD, 0x2A0, 5, ASYNC_FOURPORT }, - { BASE_BAUD, 0x2A8, 5, ASYNC_FOURPORT }, - { BASE_BAUD, 0x2B0, 5, ASYNC_FOURPORT }, - { BASE_BAUD, 0x2B8, 5, ASYNC_FOURPORT }, + { BASE_BAUD, 0x1A0, 9, ASYNC_FOURPORT }, /* ttyS4 */ + { BASE_BAUD, 0x1A8, 9, ASYNC_FOURPORT }, /* ttyS5 */ + { BASE_BAUD, 0x1B0, 9, ASYNC_FOURPORT }, /* ttyS6 */ + { BASE_BAUD, 0x1B8, 9, ASYNC_FOURPORT }, /* ttyS7 */ + + { BASE_BAUD, 0x2A0, 5, ASYNC_FOURPORT }, /* ttyS8 */ + { BASE_BAUD, 0x2A8, 5, ASYNC_FOURPORT }, /* ttyS9 */ + { BASE_BAUD, 0x2B0, 5, ASYNC_FOURPORT }, /* ttyS10 */ + { BASE_BAUD, 0x2B8, 5, ASYNC_FOURPORT }, /* ttyS11 */ #else /* CONFIG_AST_FOURPORT */ - { BASE_BAUD, 0x000, 0 }, - { BASE_BAUD, 0x000, 0 }, - { BASE_BAUD, 0x000, 0 }, - { BASE_BAUD, 0x000, 0 }, - - { BASE_BAUD, 0x000, 0 }, - { BASE_BAUD, 0x000, 0 }, - { BASE_BAUD, 0x000, 0 }, - { BASE_BAUD, 0x000, 0 }, + { BASE_BAUD, 0x000, 0 }, /* ttyS4 */ + { BASE_BAUD, 0x000, 0 }, /* ttyS5 */ + { BASE_BAUD, 0x000, 0 }, /* ttyS6 */ + { BASE_BAUD, 0x000, 0 }, /* ttyS7 */ + + { BASE_BAUD, 0x000, 0 }, /* ttyS8 */ + { BASE_BAUD, 0x000, 0 }, /* ttyS9 */ + { BASE_BAUD, 0x000, 0 }, /* ttyS10 */ + { BASE_BAUD, 0x000, 0 }, /* ttyS11 */ #endif /* CONFIG_AST_FOURPORT */ #ifdef CONFIG_ACCENT_ASYNC - { BASE_BAUD, 0x330, 4, 0 }, - { BASE_BAUD, 0x338, 4, 0 }, + { BASE_BAUD, 0x330, 4, 0 }, /* ttyS12 */ + { BASE_BAUD, 0x338, 4, 0 }, /* ttyS13 */ #else /* CONFIG_ACCENT_ASYNC */ - { BASE_BAUD, 0x000, 0 }, - { BASE_BAUD, 0x000, 0 }, + { BASE_BAUD, 0x000, 0 }, /* ttyS12 */ + { BASE_BAUD, 0x000, 0 }, /* ttyS13 */ #endif /* CONFIG_ACCENT_ASYNC */ - { BASE_BAUD, 0x000, 0 }, - { BASE_BAUD, 0x000, 0 }, - - { BASE_BAUD, 0x100, 4, 0 }, - { BASE_BAUD, 0x108, 4, 0 }, - { BASE_BAUD, 0x110, 4, 0 }, - { BASE_BAUD, 0x118, 4, 0 }, - { BASE_BAUD, 0x120, 4, 0 }, - { BASE_BAUD, 0x128, 4, 0 }, - { BASE_BAUD, 0x130, 4, 0 }, - { BASE_BAUD, 0x138, 4, 0 }, - { BASE_BAUD, 0x140, 4, 0 }, - { BASE_BAUD, 0x148, 4, 0 }, - { BASE_BAUD, 0x150, 4, 0 }, - { BASE_BAUD, 0x158, 4, 0 }, - { BASE_BAUD, 0x160, 4, 0 }, - { BASE_BAUD, 0x168, 4, 0 }, - { BASE_BAUD, 0x170, 4, 0 }, - { BASE_BAUD, 0x178, 4, 0 }, + { BASE_BAUD, 0x000, 0 }, /* ttyS14 (spare; user configurable) */ + { BASE_BAUD, 0x000, 0 }, /* ttyS15 (spare; user configurable) */ + + { BASE_BAUD, 0x100, 12, 0 }, /* ttyS16 */ + { BASE_BAUD, 0x108, 12, 0 }, /* ttyS17 */ + { BASE_BAUD, 0x110, 12, 0 }, /* ttyS18 */ + { BASE_BAUD, 0x118, 12, 0 }, /* ttyS19 */ + { BASE_BAUD, 0x120, 12, 0 }, /* ttyS20 */ + { BASE_BAUD, 0x128, 12, 0 }, /* ttyS21 */ + { BASE_BAUD, 0x130, 12, 0 }, /* ttyS22 */ + { BASE_BAUD, 0x138, 12, 0 }, /* ttyS23 */ + { BASE_BAUD, 0x140, 12, 0 }, /* ttyS24 */ + { BASE_BAUD, 0x148, 12, 0 }, /* ttyS25 */ + { BASE_BAUD, 0x150, 12, 0 }, /* ttyS26 */ + { BASE_BAUD, 0x158, 12, 0 }, /* ttyS27 */ + { BASE_BAUD, 0x160, 12, 0 }, /* ttyS28 */ + { BASE_BAUD, 0x168, 12, 0 }, /* ttyS29 */ + { BASE_BAUD, 0x170, 12, 0 }, /* ttyS30 */ + { BASE_BAUD, 0x178, 12, 0 }, /* ttyS31 */ }; #define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct)) @@ -275,8 +276,7 @@ static inline void transmit_chars(struct async_struct *info, int *done_work) (*done_work)++; } -static inline void check_modem_status(struct async_struct *info, - int *done_work) +static inline int check_modem_status(struct async_struct *info) { int status; @@ -292,11 +292,12 @@ static inline void check_modem_status(struct async_struct *info, if (info->tty->stopped) { if (status & UART_MSR_CTS) { info->tty->stopped = 0; - done_work++; + return 1; } } else info->tty->stopped = !(status & UART_MSR_CTS); } + return 0; } static inline void figure_RS_timer(void) @@ -334,17 +335,18 @@ static void rs_interrupt(int irq) if (!pass_number || !(serial_inp(info, UART_IIR) & UART_IIR_NO_INT)) { done = 0; - status = serial_inp(info, UART_LSR); if (status & UART_LSR_DR) { receive_chars(info, &status); done_work++; } + recheck_write: if ((status & UART_LSR_THRE) && !info->tty->stopped) { transmit_chars(info, &done_work); } - check_modem_status(info, &done_work); + if (check_modem_status(info)) + goto recheck_write; } info = info->next_port; @@ -365,7 +367,6 @@ static void rs_interrupt(int irq) figure_RS_timer(); } -#ifdef CONFIG_AUTO_IRQ /* * This is the serial driver's interrupt routine while we are probing * for submarines. @@ -379,7 +380,6 @@ static void rs_probe(int irq) rs_triggered |= 1 << irq; return; } -#endif /* * ------------------------------------------------------------------- @@ -1288,7 +1288,7 @@ int rs_open(struct tty_struct *tty, struct file * filp) */ static void show_serial_version(void) { - printk("Serial driver version 3.93 with"); + printk("Serial driver version 3.94 with"); #ifdef CONFIG_AST_FOURPORT printk(" AST_FOURPORT"); #define SERIAL_OPT @@ -1309,7 +1309,6 @@ static void show_serial_version(void) #undef SERIAL_OPT } -#ifdef CONFIG_AUTO_IRQ /* * This routine is called by init(); it attempts to determine which * interrupt a serial port is configured to use. It is not @@ -1361,13 +1360,12 @@ static int get_auto_irq(struct async_struct *info) outb_p(save_ICP, ICP); return(rs_irq_triggered); } -#endif /* * This routine is called by rs_init() to initialize a specific serial * port. If CONFIG_AUTO_IRQ is defined, it will attempt to figure out * which IRQ the serial port is on by calling get_auto_irq(). (See - * above). a + * above). * * It also determines what type of UART ship this serial port is * using: 8250, 16450, 16550, 16550A. The important question is @@ -1378,15 +1376,54 @@ static void init(struct async_struct * info) { unsigned char status1, status2, scratch, scratch2; unsigned port = info->port; -#ifdef CONFIG_AUTO_IRQ int retries; -#endif if (!port) return; -#ifdef CONFIG_AUTO_IRQ - scratch2 = 0; + /* + * Do a simple existence test first; if we fail this, there's + * no point trying anything else. + */ + scratch = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, 0); + scratch2 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, scratch); + if (scratch2) + return; /* We failed; there's nothing here */ + + /* + * Check to see if a UART is really there. Certain broken + * internal modems based on the Rockwell chipset fail this + * test, because they apparently don't implement the loopback + * test mode. So this test is skipped on the COM 1 through + * COM 4 ports. This *should* be safe, since no board + * manufactucturer would be stupid enough to design a board + * that conflicts with COM 1-4 --- we hope! + */ + if (!(info->flags & ASYNC_SKIP_TEST)) { + scratch = serial_inp(info, UART_MCR); + serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch); + scratch2 = serial_inp(info, UART_MSR); + serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(info, UART_MSR) & 0xF0; + serial_outp(info, UART_MCR, scratch); + serial_outp(info, UART_MSR, scratch2); + if (status1 != 0x90) { + info->type = PORT_UNKNOWN; + return; + } + } + + /* + * Here's where we do the automatic IRQ detection. We always + * try to do this test; CONFIG_AUTO_IRQ merely determins + * whether or not we pay attention to the results. If + * CONFIG_AUTO_IRQ is off, then we merely print a warning + * message if the default IRQ does not match the results made + * by the automatic IRQ detection system. + */ + scratch = scratch2 = 0; for (retries = 0; retries < 5; retries++) { if (!scratch) scratch = get_auto_irq(info); @@ -1398,28 +1435,22 @@ static void init(struct async_struct * info) scratch = scratch2 = 0; } } - if (scratch && (scratch == scratch2)) + if (scratch && (scratch == scratch2)) { +#ifdef CONFIG_AUTO_IRQ info->irq = scratch; - else { - info->type = PORT_UNKNOWN; - return; - } -#else /* CONFIG_AUTO_IRQ */ - /* - * Check to see if a UART is really there. - */ - scratch = serial_inp(info, UART_MCR); - serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch); - scratch2 = serial_inp(info, UART_MSR); - serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); - status1 = serial_inp(info, UART_MSR) & 0xF0; - serial_outp(info, UART_MCR, scratch); - serial_outp(info, UART_MSR, scratch2); - if (status1 != 0x90) { +#else + if (info->irq != scratch) + printk("Warning: auto IRQ detection for tty%d found IRQ %d, not %d.\n", + info->line, scratch, info->irq); +#endif + } else { +#ifdef CONFIG_AUTO_IRQ info->type = PORT_UNKNOWN; return; +#else + printk("Warning: auto IRQ detection for tty%d failed; using default IRQ.\n", info->line); +#endif } -#endif /* CONFIG_AUTO_IRQ */ outb_p(UART_FCR_ENABLE_FIFO, UART_FCR + port); scratch = inb(UART_IIR + port) >> 6; @@ -1459,7 +1490,6 @@ long rs_init(long kmem_start) { int i; struct async_struct * info; -#ifdef CONFIG_AUTO_IRQ int irq_lines = 0; struct sigaction sa; unsigned long timeout; @@ -1469,28 +1499,26 @@ long rs_init(long kmem_start) */ sti(); - rs_triggered = 0; sa.sa_handler = rs_probe; sa.sa_flags = (SA_INTERRUPT); sa.sa_mask = 0; sa.sa_restorer = NULL; -#endif + memset(&rs_event, 0, sizeof(rs_event)); bh_base[SERIAL_BH].routine = do_softint; timer_table[RS_TIMER].fn = rs_timer; timer_table[RS_TIMER].expires = 0; IRQ_active = 0; + rs_triggered = 0; for (i = 0; i < 16; i++) { IRQ_ports[i] = 0; IRQ_timeout[i] = 0; -#ifdef CONFIG_AUTO_IRQ if (!irqaction(i, &sa)) irq_lines |= 1 << i; -#endif } -#ifdef CONFIG_AUTO_IRQ - timeout = jiffies+5; + + timeout = jiffies+10; while (timeout >= jiffies) ; for (i = 0; i < 16; i++) { @@ -1501,7 +1529,7 @@ long rs_init(long kmem_start) free_irq(i); } } -#endif + show_serial_version(); for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) { info->line = i; @@ -1540,7 +1568,6 @@ long rs_init(long kmem_start) break; } } -#ifdef CONFIG_AUTO_IRQ /* * Turn interrupts back off, since they were off when we * started this. See start_kernel() in init/main.c. @@ -1550,7 +1577,6 @@ long rs_init(long kmem_start) if (irq_lines & (1 << i)) free_irq(i); } -#endif return kmem_start; } diff --git a/kernel/chr_drv/tty_io.c b/kernel/chr_drv/tty_io.c index 5ffd395..aed9428 100644 --- a/kernel/chr_drv/tty_io.c +++ b/kernel/chr_drv/tty_io.c @@ -1060,6 +1060,7 @@ static void release_dev(int dev, struct file * filp) { struct tty_struct *tty, *o_tty; struct termios *tp, *o_tp; + struct task_struct **p; tty = tty_table[dev]; tp = tty_termios[dev]; @@ -1107,6 +1108,15 @@ static void release_dev(int dev, struct file * filp) if (tty->count) return; + /* + * Make sure there aren't any processes that still think this + * tty is their controlling tty. + */ + for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { + if ((*p) && (*p)->tty == tty->line) + (*p)->tty = -1; + } + if (ldiscs[tty->disc].close != NULL) ldiscs[tty->disc].close(tty); diff --git a/kernel/exit.c b/kernel/exit.c index bd3020f..4a1e02a 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -20,9 +20,9 @@ int sys_close(int fd); int getrusage(struct task_struct *, int, struct rusage *); -int send_sig(long sig,struct task_struct * p,int priv) +int send_sig(unsigned long sig,struct task_struct * p,int priv) { - if (!p || (sig < 0) || (sig > 32)) + if (!p || sig > 32) return -EINVAL; if (!priv && ((sig != SIGCONT) || (current->session != p->session)) && (current->euid != p->euid) && (current->uid != p->uid) && !suser()) diff --git a/kernel/fork.c b/kernel/fork.c index ddaaef7..8bf0008 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -6,7 +6,7 @@ /* * 'fork.c' contains the help-routines for the 'fork' system call - * (see also system_call.s), and some misc functions ('verify_area'). + * (see also system_call.s). * Fork is rather simple, once you get the hang of it, but the memory * management can be a bitch. See 'mm/mm.c': 'copy_page_tables()' */ @@ -24,27 +24,6 @@ long last_pid=0; -int verify_area(int type, void * addr, unsigned long size) -{ - unsigned long start; - - start = (unsigned long) addr; - if (start >= TASK_SIZE) - return -EFAULT; - if (size > TASK_SIZE - start) - return -EFAULT; - if (type == VERIFY_READ) - return 0; - size += start & 0xfff; - size >>= 12; - start &= 0xfffff000; - do { - write_verify(start); - start += 4096; - } while (size--); - return 0; -} - static int find_empty_process(void) { int i, task_nr; diff --git a/kernel/irq.c b/kernel/irq.c index 9d503ae..2e5d29f 100644 --- a/kernel/irq.c +++ b/kernel/irq.c @@ -125,6 +125,11 @@ static void (*bad_interrupt[16])(void) = { bad_IRQ14_interrupt, bad_IRQ15_interrupt }; +void fake_keyboard_interrupt(void) +{ + IRQ1_interrupt(); +} + /* * Initial irq handlers. */ diff --git a/kernel/printk.c b/kernel/printk.c index 0f99367..8503ce6 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -2,6 +2,13 @@ * linux/kernel/printk.c * * Copyright (C) 1991, 1992 Linus Torvalds + * + * Modified to make sys_syslog() more flexible: added commands to + * return the last 4k of kernel messages, regardless of whether + * they've been read or not. Added option to suppress kernel printk's + * to the console. Added hook for sending the console messages + * elsewhere, in preparation for a serial line console (someday). + * Ted Ts'o, 2/11/93. */ #include <stdarg.h> @@ -18,46 +25,48 @@ static char buf[1024]; extern int vsprintf(char * buf, const char * fmt, va_list args); extern void console_print(const char *); -static unsigned long log_page = 0; +static void (*console_print_proc)(const char *) = 0; +static char log_buf[4096]; static unsigned long log_start = 0; +static unsigned long logged_chars = 0; unsigned long log_size = 0; +int log_to_console = 1; struct wait_queue * log_wait = NULL; +/* + * Commands to sys_syslog: + * + * 0 -- Close the log. Currently a NOP. + * 1 -- Open and reset log. + * 2 -- Read from the log. + * 3 -- Read up to the last 4k of messages in the ring buffer. + * 4 -- Read and clear last 4k of messages in the ring buffer + * 5 -- Clear ring buffer. + * 6 -- Disable printk's to console + * 7 -- Enable printk's to console + */ int sys_syslog(int type, char * buf, int len) { - unsigned long i; + unsigned long i, j, count; + int do_clear = 0; char c; - if (!suser()) + if ((type != 3) && !suser()) return -EPERM; switch (type) { - case 0: - i = log_page; - log_page = 0; - free_page(i); - wake_up_interruptible(&log_wait); + case 0: /* Close log */ return 0; - case 1: - i = get_free_page(GFP_KERNEL); - if (log_page) { - free_page(i); - return 0; - } else if ((log_page = i) != 0) { - log_start = log_size = 0; - return 0; - } - return -ENOMEM; - case 2: + case 1: /* Open and reset log */ + log_start += log_size; + log_size = 0; + return 0; + case 2: /* Read from log */ if (!buf || len < 0) return -EINVAL; if (!len) return 0; - i = verify_area(VERIFY_WRITE, buf, len); - if (i) - return i; + verify_area(VERIFY_WRITE,buf,len); while (!log_size) { - if (!log_page) - return -EIO; if (current->signal & ~current->blocked) return -ERESTARTSYS; cli(); @@ -67,7 +76,7 @@ int sys_syslog(int type, char * buf, int len) } i = 0; while (log_size && i < len) { - c = *((char *) log_page+log_start); + c = *((char *) log_buf+log_start); log_start++; log_size--; log_start &= 4095; @@ -76,6 +85,36 @@ int sys_syslog(int type, char * buf, int len) i++; } return i; + case 4: /* Read/clear last 4k of kernel messages */ + do_clear = 1; + case 3: /* Read last 4k of kernel messages */ + if (!buf || len < 0) + return -EINVAL; + if (!len) + return 0; + verify_area(VERIFY_WRITE,buf,len); + count = len; + if (count > 4096) + count = 4096; + if (count > logged_chars) + count = logged_chars; + j = log_start + log_size - count; + for (i = 0; i < count; i++) { + c = *((char *) log_buf + (j++ & 4095)); + put_fs_byte(c, buf++); + } + if (do_clear) + logged_chars = 0; + return i; + case 5: /* Clear ring buffer */ + logged_chars = 0; + return 0; + case 6: /* Disable logging to console */ + log_to_console = 0; + return 0; + case 7: /* Enable logging to console */ + log_to_console = 1; + return 0; } return -EINVAL; } @@ -85,21 +124,47 @@ int printk(const char *fmt, ...) { va_list args; int i,j; - char * p; va_start(args, fmt); i=vsprintf(buf,fmt,args); va_end(args); - for (j = 0; j < i && log_page ; j++) { - p = (char *) log_page + (4095 & (log_start+log_size)); - *p = buf[j]; + for (j = 0; j < i ; j++) { + log_buf[(log_start+log_size) & 4095] = buf[j]; if (log_size < 4096) log_size++; else log_start++; + logged_chars++; } - if (log_page) - wake_up_interruptible(&log_wait); - console_print(buf); + wake_up_interruptible(&log_wait); + if (log_to_console && console_print_proc) + (*console_print_proc)(buf); return i; } + +/* + * The console driver calls this routine during kernel initialization + * to register the console printing procedure with printk() and to + * print any messages that were printed by the kernel before the + * console priver was initialized. + */ +void register_console(void (*proc)(const char *)) +{ + int i,j; + int p = log_start; + char buf[16]; + + console_print_proc = proc; + + for (i=0,j=0; i < log_size; i++) { + buf[j++] = log_buf[p]; + p++; p &= 4095; + if (j < sizeof(buf)-1) + continue; + buf[j] = 0; + (*proc)(buf); + j = 0; + } + buf[j] = 0; + (*proc)(buf); +} diff --git a/kernel/sched.c b/kernel/sched.c index ad207e2..992a3fb 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -159,8 +159,10 @@ int sys_pause(void) if (sa->sa_handler == SIG_IGN || (sa->sa_handler == SIG_DFL && (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH))) current->blocked |= mask; - current->state = TASK_INTERRUPTIBLE; - schedule(); + do { + current->state = TASK_INTERRUPTIBLE; + schedule(); + } while (!(current->signal & ~current->blocked)); /* if a suspending signal interrupted us we must restart */ if (!(current->signal & ~current->blocked & ~(_S(SIGSTOP) | _S(SIGTSTP) | _S(SIGTTIN) | _S(SIGTTOU)))) { diff --git a/kernel/signal.c b/kernel/signal.c index ffa74e2..f90b7a2 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -170,87 +170,104 @@ extern int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. */ -int do_signal(long signr,struct pt_regs * regs) +void do_signal(struct pt_regs * regs) { + unsigned long signr; unsigned long sa_handler; long old_eip = regs->eip; - struct sigaction * sa = current->sigaction + signr - 1; + struct sigaction * sa; int longs; unsigned long * tmp_esp; - sa_handler = (unsigned long) sa->sa_handler; - if ((regs->orig_eax >= 0) && - ((regs->eax == -ERESTARTSYS) || (regs->eax == -ERESTARTNOINTR))) { - if ((sa_handler > 1) && (regs->eax == -ERESTARTSYS) && - (sa->sa_flags & SA_INTERRUPT)) - regs->eax = -EINTR; - else { - regs->eax = regs->orig_eax; - regs->eip = old_eip -= 2; - } + if (regs->orig_eax >= 0 && regs->eax == -ERESTARTNOINTR) { + regs->eax = regs->orig_eax; + regs->eip = old_eip -= 2; } - if (sa_handler==1) { + signr = current->signal & ~current->blocked; + do { + __asm__("bsf %2,%1\n\t" + "btrl %1,%0" + :"=m" (current->signal),"=r" (signr) + :"1" (signr)); + sa = current->sigaction + signr; + signr++; + sa_handler = (unsigned long) sa->sa_handler; + if (sa_handler==1) { /* check for SIGCHLD: it's special */ - if (signr == SIGCHLD) - while (sys_waitpid(-1,NULL,WNOHANG) > 0) - /* nothing */; - return(1); /* Ignore, see if there are more signals... */ - } - if (!sa_handler) { - if (current->pid == 1) - return 1; - switch (signr) { - case SIGCONT: - case SIGCHLD: - case SIGWINCH: - return(1); /* Ignore, ... */ + if (signr == SIGCHLD) + while (sys_waitpid(-1,NULL,WNOHANG) > 0) + /* nothing */; + continue; + } + if (!sa_handler) { + if (current->pid == 1) + continue; + switch (signr) { + case SIGCONT: + case SIGCHLD: + case SIGWINCH: + continue; - case SIGSTOP: - case SIGTSTP: - case SIGTTIN: - case SIGTTOU: - current->state = TASK_STOPPED; - current->exit_code = signr; - if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & - SA_NOCLDSTOP)) - send_sig(SIGCHLD, current->p_pptr, 1); - return(1); /* Reschedule another event */ + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & + SA_NOCLDSTOP)) + send_sig(SIGCHLD, current->p_pptr, 1); + schedule(); + continue; - case SIGQUIT: - case SIGILL: - case SIGTRAP: - case SIGIOT: - case SIGFPE: - case SIGSEGV: - if (core_dump(signr,regs)) - signr |= 0x80; - /* fall through */ - default: - current->signal |= _S(signr & 0x7f); - do_exit(signr); + case SIGQUIT: + case SIGILL: + case SIGTRAP: + case SIGIOT: + case SIGFPE: + case SIGSEGV: + if (core_dump(signr,regs)) + signr |= 0x80; + /* fall through */ + default: + current->signal |= _S(signr & 0x7f); + do_exit(signr); + } } - } - /* - * OK, we're invoking a handler - */ - if (sa->sa_flags & SA_ONESHOT) - sa->sa_handler = NULL; - regs->eip = sa_handler; - longs = (sa->sa_flags & SA_NOMASK)?(7*4):(8*4); - regs->esp -= longs; - tmp_esp = (unsigned long *) regs->esp; - verify_area(VERIFY_WRITE,tmp_esp,longs); - put_fs_long((long) sa->sa_restorer,tmp_esp++); - put_fs_long(signr,tmp_esp++); - if (!(sa->sa_flags & SA_NOMASK)) - put_fs_long(current->blocked,tmp_esp++); - put_fs_long(regs->eax,tmp_esp++); - put_fs_long(regs->ecx,tmp_esp++); - put_fs_long(regs->edx,tmp_esp++); - put_fs_long(regs->eflags,tmp_esp++); - put_fs_long(old_eip,tmp_esp++); - current->blocked |= sa->sa_mask; + /* + * OK, we're invoking a handler + */ + if (regs->orig_eax >= 0 && regs->eax == -ERESTARTSYS) { + if (sa->sa_flags & SA_INTERRUPT) + regs->eax = -EINTR; + else { + regs->eax = regs->orig_eax; + regs->eip = old_eip -= 2; + } + } + if (sa->sa_flags & SA_ONESHOT) + sa->sa_handler = NULL; + regs->eip = sa_handler; + longs = (sa->sa_flags & SA_NOMASK)?(7*4):(8*4); + regs->esp -= longs; + tmp_esp = (unsigned long *) regs->esp; + verify_area(VERIFY_WRITE,tmp_esp,longs); + put_fs_long((long) sa->sa_restorer,tmp_esp++); + put_fs_long(signr,tmp_esp++); + if (!(sa->sa_flags & SA_NOMASK)) + put_fs_long(current->blocked,tmp_esp++); + put_fs_long(regs->eax,tmp_esp++); + put_fs_long(regs->ecx,tmp_esp++); + put_fs_long(regs->edx,tmp_esp++); + put_fs_long(regs->eflags,tmp_esp++); + put_fs_long(old_eip,tmp_esp++); + current->blocked |= sa->sa_mask; /* force a supervisor-mode page-in of the signal handler to reduce races */ - __asm__("testb $0,%%fs:%0"::"m" (*(char *) sa_handler)); - return(0); /* Continue, execute handler */ + __asm__("testb $0,%%fs:%0"::"m" (*(char *) sa_handler)); + return; + } while ((signr = current->signal & ~current->blocked)); + if (regs->orig_eax >= 0 && regs->eax == -ERESTARTSYS) { + regs->eax = regs->orig_eax; + regs->eip = old_eip -= 2; + } } diff --git a/kernel/sys.c b/kernel/sys.c index a7433b4..0777f8e 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -130,7 +130,12 @@ int sys_prof(void) return -ENOSYS; } -unsigned long save_v86_state(int signr,struct vm86_regs * regs) +int sys_ipc(void) +{ + return -ENOSYS; +} + +unsigned long save_v86_state(struct vm86_regs * regs) { unsigned long stack; diff --git a/kernel/sys_call.S b/kernel/sys_call.S index 1c911dd..9b10c82 100644 --- a/kernel/sys_call.S +++ b/kernel/sys_call.S @@ -65,23 +65,12 @@ VM_MASK = 0x00020000 /* * these are offsets into the task-struct. */ -state = 0 -counter = 4 -priority = 8 +state = 0 +counter = 4 +priority = 8 signal = 12 -sigaction = 16 # MUST be 16 (=len of sigaction) -blocked = (33*16) -saved_kernel_stack = ((33*16)+4) -kernel_stack_page = ((33*16)+8) -flags = ((33*16)+12) - -/* - * offsets within sigaction - */ -sa_handler = 0 -sa_mask = 4 -sa_flags = 8 -sa_restorer = 12 +blocked = 16 +flags = 20 ENOSYS = 38 @@ -113,6 +102,21 @@ ENOSYS = 38 movl $0x17,%edx; \ mov %dx,%fs +#define RESTORE_ALL \ + popl %ebx; \ + popl %ecx; \ + popl %edx; \ + popl %esi; \ + popl %edi; \ + popl %ebp; \ + popl %eax; \ + pop %ds; \ + pop %es; \ + pop %fs; \ + pop %gs; \ + addl $4,%esp; \ + iret + .align 4 reschedule: pushl $ret_from_sys_call @@ -151,16 +155,16 @@ _system_call: ret_from_sys_call: movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are testl $VM_MASK,%eax # different then - jne 4f + jne 1f cmpw $0x0f,CS(%esp) # was old code segment supervisor ? jne 2f cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ? jne 2f -4: sti # slow interrupts get here with interrupts disabled +1: sti # slow interrupts get here with interrupts disabled orl $IF_MASK,%eax # these just try to make sure andl $~NT_MASK,%eax # the program doesn't do anything movl %eax,EFLAGS(%esp) # stupid -1: cmpl $0,_need_resched + cmpl $0,_need_resched jne reschedule movl _current,%eax cmpl _task,%eax # task[0] cannot have signals @@ -169,44 +173,28 @@ ret_from_sys_call: jne reschedule cmpl $0,counter(%eax) # counter je reschedule - movl signal(%eax),%ebx movl blocked(%eax),%ecx notl %ecx - andl %ebx,%ecx - je 2f # XXX - branch is almost always taken - bsfl %ecx,%ecx - btrl %ecx,signal(%eax) # change atomically (%ebx is stale) - jnc 2f # bit became clear (can't happen?) - incl %ecx + andl signal(%eax),%ecx + jne signal_return +2: RESTORE_ALL +.align 4 +signal_return: movl %esp,%ebx - testl $VM_MASK,EFLAGS(%esp) - je 3f pushl %ebx - pushl %ecx + testl $VM_MASK,EFLAGS(%ebx) + jne v86_signal_return + call _do_signal + popl %ebx + RESTORE_ALL +.align 4 +v86_signal_return: call _save_v86_state - popl %ecx - movl %eax,%ebx movl %eax,%esp -3: pushl %ebx - pushl %ecx + pushl %eax call _do_signal - popl %ecx popl %ebx - testl %eax, %eax - jne 1b # see if we need to switch tasks, or do more signals -2: popl %ebx - popl %ecx - popl %edx - popl %esi - popl %edi - popl %ebp - popl %eax - pop %ds - pop %es - pop %fs - pop %gs - addl $4,%esp # skip the orig_eax - iret + RESTORE_ALL .align 4 _sys_execve: diff --git a/mm/memory.c b/mm/memory.c index 899869c..64655cc 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -563,10 +563,28 @@ void do_wp_page(unsigned long error_code, unsigned long address, invalidate(); } -void write_verify(unsigned long address) +int verify_area(int type, void * addr, unsigned long size) { - if (address < TASK_SIZE) - do_wp_page(1,address,current,0); + unsigned long start; + + start = (unsigned long) addr; + if (start >= TASK_SIZE) + return -EFAULT; + if (size > TASK_SIZE - start) + return -EFAULT; + if (type == VERIFY_READ || !size) + return 0; + if (!size) + return 0; + size--; + size += start & 0xfff; + size >>= 12; + start &= 0xfffff000; + do { + do_wp_page(1,start,current,0); + start += 4096; + } while (size--); + return 0; } static void get_empty_page(struct task_struct * tsk, unsigned long address) @@ -662,7 +662,7 @@ int sys_swapon(const char * specialfile) p->flags = 0; return -ENOMEM; } - read_swap_page(type < 1,tmp); + read_swap_page(SWP_ENTRY(type,0),tmp); if (strncmp("SWAP-SPACE",tmp+4086,10)) { printk("Unable to find swap-space signature\n"); free_page((long) tmp); diff --git a/net/tcp/arp.c b/net/tcp/arp.c index cbb9cdb..f1243b7 100644 --- a/net/tcp/arp.c +++ b/net/tcp/arp.c @@ -317,40 +317,24 @@ arp_destroy(unsigned long paddr) { unsigned long hash; struct arp_table *apt; - struct arp_table *lapt; + struct arp_table **lapt; PRINTK (("arp_destroy (paddr=%X)\n",paddr)); /* we don't want to destroy are own arp */ if (my_ip_addr(paddr)) return; hash = net32(paddr) & (ARP_TABLE_SIZE - 1); cli(); /* can't be interrupted. */ - /* make sure there is something there. */ - if (arp_table[hash] == NULL) return; - - /* check the first one. */ - if (arp_table[hash]->ip == paddr) - { - apt = (struct arp_table *)arp_table[hash]; - arp_table[hash] = arp_table[hash]->next; - arp_free (apt, sizeof (*apt)); - sti(); - return; - } - - /* now deal with it any where else in the chain. */ - lapt = (struct arp_table *)arp_table[hash]; - for (apt = (struct arp_table *)arp_table[hash]->next; - apt != NULL; - apt = (struct arp_table *)apt->next) - { - if (apt->ip == paddr) - { - lapt->next = apt->next; - arp_free (apt, sizeof (*apt)); - sti(); - return; - } - } + lapt = (struct arp_table **) &arp_table[hash]; + while ((apt = *lapt) != NULL) { + if (apt->ip == paddr) + { + *lapt = (struct arp_table *) apt->next; + arp_free(apt, sizeof(*apt)); + sti(); + return; + } + lapt = (struct arp_table **) &apt->next; + } sti(); } @@ -410,7 +394,7 @@ arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) tbl->last_used = timer_seq; } - if (!my_ip_addr(*arp_targetp(arp))) + if (my_ip_addr(*arp_targetp(arp)) != IS_MYADDR) { kfree_skb (skb, FREE_READ); return (0); diff --git a/net/tcp/ip.c b/net/tcp/ip.c index 2aa7a44..e517a6b 100644 --- a/net/tcp/ip.c +++ b/net/tcp/ip.c @@ -192,7 +192,7 @@ int ip_addr_match (unsigned long addr1, unsigned long addr2) { int i; - if (addr1 == addr2) return (1); + if (addr1 == addr2) return (IS_MYADDR); for (i = 0; i < 4; i++, addr1 >>= 8, addr2 >>= 8) { if ((addr1 & 0xff) != (addr2 & 0xff)) @@ -203,20 +203,22 @@ ip_addr_match (unsigned long addr1, unsigned long addr2) { return (0); } - return (1); + return (IS_BROADCAST); } } - return (1); + return (IS_MYADDR); } int my_ip_addr(unsigned long addr) { int i; + int result; for (i = 0; i < MAX_IP_ADDRES; i++) { if (ip_addr[i] == 0) return (0); - if (ip_addr_match (addr, ip_addr[i])) return (1); + result = ip_addr_match (addr, ip_addr[i]); + if (result) return result; } return (0); } diff --git a/net/tcp/ip.h b/net/tcp/ip.h index 4d543f9..744efe5 100644 --- a/net/tcp/ip.h +++ b/net/tcp/ip.h @@ -156,6 +156,12 @@ extern int ip_ads; #define MY_IP_ADDR ip_addr[0] int my_ip_addr(unsigned long); +/* + * returned by my_ip_addr.. + */ +#define IS_MYADDR 1 +#define IS_BROADCAST 2 + #include "eth.h" void diff --git a/tools/build.c b/tools/build.c index d8cddfd..a15ee3b 100644 --- a/tools/build.c +++ b/tools/build.c @@ -94,7 +94,14 @@ int main(int argc, char ** argv) if ((argc < 4) || (argc > 5)) usage(); if (argc > 4) { - if (strcmp(argv[4], "FLOPPY")) { + if (!strcmp(argv[4], "CURRENT")) { + if (stat("/", &sb)) { + perror("/"); + die("Couldn't stat /"); + } + major_root = major(sb.st_dev); + minor_root = minor(sb.st_dev); + } else if (strcmp(argv[4], "FLOPPY")) { if (stat(argv[4], &sb)) { perror(argv[4]); die("Couldn't stat root device."); diff --git a/tools/version.h b/tools/version.h deleted file mode 100644 index dd045d4..0000000 --- a/tools/version.h +++ /dev/null @@ -1,5 +0,0 @@ -#define UTS_RELEASE "0.99.pl1-46" -#define UTS_VERSION "12/20/92" -#define LINUX_COMPILE_TIME "14:31:20" -#define LINUX_COMPILE_BY "root" -#define LINUX_COMPILE_HOST "home" |