aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@cc.helsinki.fi>1993-03-13 19:37:57 +0000
committerNicolas Pitre <nico@cam.org>2007-08-19 14:19:13 -0400
commiteeb7df9e7d9a3a48dc39743a13e0ed1cc0d5cb41 (patch)
tree64a9699b70d1d26ec9fce28c108d2cd07bccf379
parent56cac397826c13fb61390c9ca3fa5354a7b80ca5 (diff)
downloadarchive-eeb7df9e7d9a3a48dc39743a13e0ed1cc0d5cb41.tar.gz
Linux 0.99 patchlevel 7 available..v0.99-pl7
It has been two weeks since the last release, so it's high time you should once more enjoy the pleasures of patching up your kernel to a higher version number if you are into those kinds of perversions. Linux 0.99pl7 is available as both full source and diffs against pl6 on nic.funet.fi: pub/OS/Linux/PEOPLE/Linus, and it will probably show up on the other major sites within days. As of pl7, I'm trying out a new format: both the full distribution and the diffs are now compressed with gzip as it is now available at most machines. Also, the diffs are no longer context diffs: they use the smaller unified diff format. At least the stock SunOS 'patch' binary seems not to understand them at all, but GNU patch has no problems, and unified diffs are a bit smaller (not that it matters much after gzip has done its deed on them). As to the changes in pl7: they are many and varied, and hopefully all to the better (-"Dream on Linus" -"Shut up"). Short list follows, hope I haven't forgotten anything major. - ext2fs is in: note that this is version 0.2c and that if you are currently using an older version there are some changes. Small filesystems (< 256MB) should reportedly be automatically converted, bigger filesystems need some assistance. Ext2fs written by Remy Card. - xiafs is also in: again, the final version uses a slightly different layout to support exact file block counts, so if you use the xiafs, you should make sure you have the latest fs-tools. Xiafs written by Frank Xia. - updated Ultrastor SCSI driver with scatter/gather by Scott Taylor. It should be much faster, as well as support the Ultrastor-34F. - major changes in the memory manager. Yours truly got carried away, and finally cleaned up the mm layer due to pmacdona wanting mmap() on /dev/zero. This means that the IPC patches won't go in, and need updating. Krishna? - more big changes: I rewrote most of the VFS filename-handling. Filenames are copied into kernel space before being used, which cleaned things up somewhat, as well as simplifying some race- condition handling. As a result, I was also able to easily expand the minix fs to cover the "linux" fs that some people have been using (same layout, but with 30-character names). - updated the printer driver: Nigel Gamble. It is now able to use interrupts, although the default behaviour is still to poll. - serial driver updates by tytso (but no SLIP yet) - various minor patches for POSIX compliace: Bruce Evans, Rick Sladkey and me. - other minor patches all over the place: scsi, tcpip etc. All in all, the patches are almost half a megabyte even as unified diffs: getting the full sources might be easier than patching it all up. As always, some of the patches are actually tested by me, some aren't (and just because I wrote some of them doesn't mean I actually *tested* them: I have no idea if mmap() works on /dev/zero, although it should). I have neither a printer nor an Ultrastor controller, and I haven't got the diskspace to test out the new filesystems, so I can only hope they work "as advertized". If you have problems, I want to hear about them, so keep the reports coming, and try to pinpoint the problem as well as you can ("when I do *this* it happens every time.."). Linus
-rw-r--r--Makefile34
-rw-r--r--boot/bootsect.S10
-rw-r--r--boot/head.S6
-rw-r--r--config.in6
-rw-r--r--fs/Makefile9
-rw-r--r--fs/buffer.c123
-rw-r--r--fs/exec.c29
-rw-r--r--fs/ext/namei.c12
-rw-r--r--fs/ext/symlink.c3
-rw-r--r--fs/ext2/Makefile34
-rw-r--r--fs/ext2/balloc.c390
-rw-r--r--fs/ext2/bitmap.c23
-rw-r--r--fs/ext2/dcache.c336
-rw-r--r--fs/ext2/dir.c121
-rw-r--r--fs/ext2/file.c266
-rw-r--r--fs/ext2/ialloc.c465
-rw-r--r--fs/ext2/inode.c683
-rw-r--r--fs/ext2/namei.c918
-rw-r--r--fs/ext2/symlink.c124
-rw-r--r--fs/ext2/truncate.c283
-rw-r--r--fs/fcntl.c13
-rw-r--r--fs/fifo.c55
-rw-r--r--fs/filesystems.c12
-rw-r--r--fs/inode.c40
-rw-r--r--fs/ioctl.c13
-rw-r--r--fs/isofs/inode.c2
-rw-r--r--fs/isofs/namei.c6
-rw-r--r--fs/isofs/rock.c19
-rw-r--r--fs/isofs/symlink.c4
-rw-r--r--fs/locks.c26
-rw-r--r--fs/minix/dir.c15
-rw-r--r--fs/minix/file.c3
-rw-r--r--fs/minix/inode.c8
-rw-r--r--fs/minix/namei.c223
-rw-r--r--fs/minix/symlink.c4
-rw-r--r--fs/minix/truncate.c4
-rw-r--r--fs/msdos/namei.c24
-rw-r--r--fs/namei.c227
-rw-r--r--fs/nfs/dir.c89
-rw-r--r--fs/nfs/proc.c20
-rw-r--r--fs/nfs/sock.c11
-rw-r--r--fs/nfs/symlink.c4
-rw-r--r--fs/open.c68
-rw-r--r--fs/pipe.c16
-rw-r--r--fs/proc/array.c6
-rw-r--r--fs/proc/base.c2
-rw-r--r--fs/proc/fd.c6
-rw-r--r--fs/proc/inode.c3
-rw-r--r--fs/proc/root.c2
-rw-r--r--fs/read_write.c19
-rw-r--r--fs/select.c28
-rw-r--r--fs/stat.c28
-rw-r--r--fs/super.c29
-rw-r--r--fs/xiafs/Makefile37
-rw-r--r--fs/xiafs/bitmap.c388
-rw-r--r--fs/xiafs/dir.c121
-rw-r--r--fs/xiafs/file.c251
-rw-r--r--fs/xiafs/inode.c450
-rw-r--r--fs/xiafs/namei.c855
-rw-r--r--fs/xiafs/symlink.c114
-rw-r--r--fs/xiafs/truncate.c197
-rw-r--r--fs/xiafs/xiafs_mac.h20
-rw-r--r--include/asm/irq.h2
-rw-r--r--include/asm/segment.h4
-rw-r--r--include/linux/ext2_fs.h278
-rw-r--r--include/linux/ext2_fs_i.h20
-rw-r--r--include/linux/ext2_fs_sb.h37
-rw-r--r--include/linux/ext_fs.h3
-rw-r--r--include/linux/fcntl.h1
-rw-r--r--include/linux/fs.h15
-rw-r--r--include/linux/iso_fs.h4
-rw-r--r--include/linux/kernel.h6
-rw-r--r--include/linux/lp.h25
-rw-r--r--include/linux/minix_fs.h15
-rw-r--r--include/linux/minix_fs_sb.h2
-rw-r--r--include/linux/mm.h63
-rw-r--r--include/linux/nfs_fs.h30
-rw-r--r--include/linux/pipe_fs_i.h4
-rw-r--r--include/linux/serial.h10
-rw-r--r--include/linux/sock_ioctl.h5
-rw-r--r--include/linux/termios.h3
-rw-r--r--include/linux/tty.h28
-rw-r--r--include/linux/types.h6
-rw-r--r--include/linux/unistd.h2
-rw-r--r--include/linux/vfs.h3
-rw-r--r--include/linux/xia_fs.h116
-rw-r--r--include/linux/xia_fs_i.h19
-rw-r--r--include/linux/xia_fs_sb.h36
-rw-r--r--init/main.c4
-rw-r--r--kernel/FPU-emu/Reg_constant.c111
-rw-r--r--kernel/FPU-emu/errors.c2
-rw-r--r--kernel/FPU-emu/fpu_aux.c6
-rw-r--r--kernel/FPU-emu/fpu_emu.h2
-rw-r--r--kernel/FPU-emu/fpu_entry.c4
-rw-r--r--kernel/FPU-emu/fpu_etc.c8
-rw-r--r--kernel/FPU-emu/fpu_trig.c36
-rw-r--r--kernel/FPU-emu/load_store.c4
-rw-r--r--kernel/FPU-emu/reg_constant.c16
-rw-r--r--kernel/FPU-emu/reg_ld_str.c28
-rw-r--r--kernel/blk_drv/blk.h8
-rw-r--r--kernel/blk_drv/floppy.c6
-rw-r--r--kernel/blk_drv/hd.c47
-rw-r--r--kernel/blk_drv/ll_rw_blk.c8
-rw-r--r--kernel/blk_drv/scsi/aha1542.c9
-rw-r--r--kernel/blk_drv/scsi/hosts.c4
-rw-r--r--kernel/blk_drv/scsi/scsi.c20
-rw-r--r--kernel/blk_drv/scsi/scsi.h4
-rw-r--r--kernel/blk_drv/scsi/scsi_ioctl.c13
-rw-r--r--kernel/blk_drv/scsi/sd.c2
-rw-r--r--kernel/blk_drv/scsi/sd_ioctl.c10
-rw-r--r--kernel/blk_drv/scsi/seagate.c2
-rw-r--r--kernel/blk_drv/scsi/sr.c2
-rw-r--r--kernel/blk_drv/scsi/st.c12
-rw-r--r--kernel/blk_drv/scsi/ultrastor.c320
-rw-r--r--kernel/blk_drv/scsi/ultrastor.h46
-rw-r--r--kernel/chr_drv/console.c4
-rw-r--r--kernel/chr_drv/lp.c268
-rw-r--r--kernel/chr_drv/mem.c29
-rw-r--r--kernel/chr_drv/psaux.c4
-rw-r--r--kernel/chr_drv/pty.c5
-rw-r--r--kernel/chr_drv/serial.c987
-rw-r--r--kernel/chr_drv/sound/Makefile40
-rw-r--r--kernel/chr_drv/tty_io.c96
-rw-r--r--kernel/chr_drv/tty_ioctl.c48
-rw-r--r--kernel/chr_drv/vt.c44
-rw-r--r--kernel/exit.c13
-rw-r--r--kernel/fork.c16
-rw-r--r--kernel/info.c5
-rw-r--r--kernel/irq.c2
-rw-r--r--kernel/itimer.c27
-rw-r--r--kernel/panic.c4
-rw-r--r--kernel/printk.c4
-rw-r--r--kernel/ptrace.c13
-rw-r--r--kernel/sched.c34
-rw-r--r--kernel/signal.c116
-rw-r--r--kernel/sys.c110
-rw-r--r--kernel/traps.c6
-rw-r--r--mm/memory.c304
-rw-r--r--mm/mmap.c99
-rw-r--r--mm/swap.c178
-rw-r--r--net/socket.c32
-rw-r--r--net/tcp/arp.c4
-rw-r--r--net/tcp/ip.c2
-rw-r--r--net/tcp/loopback.c1
-rw-r--r--net/tcp/packet.c10
-rw-r--r--net/tcp/raw.c10
-rw-r--r--net/tcp/sock.c17
-rw-r--r--net/tcp/tcp.c23
-rw-r--r--net/tcp/udp.c18
-rw-r--r--net/unix.c24
-rw-r--r--tools/build.c9
-rw-r--r--zBoot/misc.c4
152 files changed, 9645 insertions, 1773 deletions
diff --git a/Makefile b/Makefile
index 6aa6b99..f985dc4 100644
--- a/Makefile
+++ b/Makefile
@@ -70,23 +70,11 @@ KEYBOARD = -DKBD_FINNISH -DKBDFLAGS=0
SVGA_MODE= -DSVGA_MODE=1
-#
-# Edit the SOUND_SUPPORT line to suit your setup if you have configured
-# the sound driver to be in the kernel (not really there yet).
-#
-# The DSP_BUFFSIZE defines size of the DMA buffer used for PCM voice I/O.
-# You should use one of the values 4096 (SB), 16384 (SB Pro), 32768 (PAS+)
-# or 65536 (PAS16). The SBC_IRQ defines the IRQ line used by SoundBlaster and
-# the PAS_IRQ is the IRQ number for ProAudioSpectrum.
-#
-
-SOUND_SUPPORT = -DKERNEL_SOUNDCARD -DDSP_BUFFSIZE=16384 -DSBC_IRQ=7 -DPAS_IRQ=5
-
#
# standard CFLAGS
#
-CFLAGS = -Wall -O6 -fomit-frame-pointer
+CFLAGS = -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer
ifdef CONFIG_M486
CFLAGS := $(CFLAGS) -m486
@@ -139,6 +127,10 @@ lilo: $(CONFIGURE) Image
config:
sh Configure < config.in
mv .config~ .config
+ $(MAKE) soundconf
+
+soundconf:
+ cd kernel/chr_drv/sound;$(MAKE) config
linuxsubdirs: dummy
@for i in $(SUBDIRS); do (cd $$i && echo $$i && $(MAKE)) || exit; done
@@ -147,7 +139,7 @@ tools/./version.h: tools/version.h
tools/version.h: $(CONFIGURE) Makefile
@./makever.sh
- @echo \#define UTS_RELEASE \"0.99.pl6-`cat .version`\" > tools/version.h
+ @echo \#define UTS_RELEASE \"0.99.pl7-`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
@@ -201,10 +193,7 @@ zBoot/zSystem: zBoot/*.c zBoot/*.S tools/zSystem
cd zBoot;$(MAKE)
zImage: $(CONFIGURE) boot/bootsect boot/setup zBoot/zSystem tools/build
- cp zBoot/zSystem system.tmp
- $(STRIP) system.tmp
- tools/build boot/bootsect boot/setup system.tmp $(ROOT_DEV) > zImage
- rm system.tmp
+ tools/build boot/bootsect boot/setup zBoot/zSystem $(ROOT_DEV) > zImage
sync
zdisk: zImage
@@ -240,14 +229,21 @@ clean:
rm -f init/*.o tools/system tools/build boot/*.o tools/*.o
for i in zBoot $(SUBDIRS); do (cd $$i && $(MAKE) clean); done
+mrproper: clean
+ rm -f include/linux/autoconf.h tools/version.h
+ rm -f .version .config*
+ rm -f `find . -name .depend -print`
+
backup: clean
- cd .. && tar cf - linux | compress - > backup.Z
+ cd .. && tar cf - linux | gzip -9 > backup.z
sync
depend dep:
+ touch tools/version.h
for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done > .depend~
for i in tools/*.c;do echo -n "tools/";$(CPP) -M $$i;done >> .depend~
for i in $(SUBDIRS); do (cd $$i && $(MAKE) dep) || exit; done
+ rm -f tools/version.h
mv .depend~ .depend
ifdef CONFIGURATION
diff --git a/boot/bootsect.S b/boot/bootsect.S
index 075c4be..7655cff 100644
--- a/boot/bootsect.S
+++ b/boot/bootsect.S
@@ -35,7 +35,6 @@ BOOTSEG = 0x07C0 ! original address of boot-sector
INITSEG = DEF_INITSEG ! we move boot here - out of the way
SETUPSEG = DEF_SETUPSEG ! setup starts here
SYSSEG = DEF_SYSSEG ! system loaded at 0x10000 (65536).
-ENDSEG = SYSSEG + SYSSIZE ! where to stop loading
! ROOT_DEV & SWAP_DEV are now written by "build".
ROOT_DEV = 0
@@ -256,8 +255,9 @@ die: jne die ! es must be at 64kB boundary
xor bx,bx ! bx is starting address within segment
rp_read:
mov ax,es
- cmp ax,#ENDSEG ! have we loaded all yet?
- jb ok1_read
+ sub ax,#SYSSEG
+ cmp ax,syssize ! have we loaded all yet?
+ jbe ok1_read
ret
ok1_read:
seg cs
@@ -429,7 +429,9 @@ msg1:
.byte 13,10
.ascii "Loading"
-.org 502
+.org 500
+syssize:
+ .word SYSSIZE
swap_dev:
.word SWAP_DEV
ram_size:
diff --git a/boot/head.S b/boot/head.S
index 8cf5c4a..ab2f994 100644
--- a/boot/head.S
+++ b/boot/head.S
@@ -16,6 +16,7 @@
.globl _swapper_pg_dir,_pg0
.globl _empty_bad_page
.globl _empty_bad_page_table
+.globl _empty_zero_page
.globl _tmp_floppy_area,_floppy_track_buffer
#include <linux/tasks.h>
@@ -206,6 +207,9 @@ _empty_bad_page:
_empty_bad_page_table:
.org 0x5000
+_empty_zero_page:
+
+.org 0x6000
/*
* tmp_floppy_area is used by the floppy-driver when DMA cannot
* reach to a buffer-block. It needs to be aligned, so that it isn't
@@ -224,7 +228,7 @@ _floppy_track_buffer:
/* This is the default interrupt "handler" :-) */
int_msg:
- .asciz "Unknown interrupt\n\r"
+ .asciz "Unknown interrupt\n"
.align 2
ignore_int:
cld
diff --git a/config.in b/config.in
index d48ff79..1ea8c9d 100644
--- a/config.in
+++ b/config.in
@@ -48,6 +48,10 @@ Standard (minix) fs support
CONFIG_MINIX_FS y/n y
Extended fs support
CONFIG_EXT_FS y/n n
+Second extended fs support
+CONFIG_EXT2_FS y/n n
+xiafs filesystem support
+CONFIG_XIA_FS y/n n
msdos fs support
CONFIG_MSDOS_FS y/n y
/proc filesystem support
@@ -75,5 +79,5 @@ MicroSoft busmouse support
CONFIG_MS_BUSMOUSE y/n n
ATIXL busmouse support
CONFIG_ATIXL_BUSMOUSE y/n n
-Soundcard support (not really there yet)
+Soundcard support (distributed separately)
CONFIG_SOUND y/n n
diff --git a/fs/Makefile b/fs/Makefile
index 8a32ef8..4f3f559 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -7,7 +7,7 @@
#
# Note 2! The CFLAGS definitions are now in the main makefile...
-SUBDIRS = minix ext msdos proc isofs nfs
+SUBDIRS = minix ext ext2 msdos proc isofs nfs xiafs
ifdef CONFIG_MINIX_FS
FS_SUBDIRS := $(FS_SUBDIRS) minix
@@ -15,6 +15,9 @@ endif
ifdef CONFIG_EXT_FS
FS_SUBDIRS := $(FS_SUBDIRS) ext
endif
+ifdef CONFIG_EXT2_FS
+FS_SUBDIRS := $(FS_SUBDIRS) ext2
+endif
ifdef CONFIG_MSDOS_FS
FS_SUBDIRS := $(FS_SUBDIRS) msdos
endif
@@ -27,6 +30,10 @@ endif
ifdef CONFIG_NFS_FS
FS_SUBDIRS := $(FS_SUBDIRS) nfs
endif
+ifdef CONFIG_XIA_FS
+FS_SUBDIRS := $(FS_SUBDIRS) xiafs
+endif
+
.c.s:
$(CC) $(CFLAGS) -S $<
diff --git a/fs/buffer.c b/fs/buffer.c
index d8bfb60..fac233b 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -78,6 +78,8 @@ static void sync_buffers(dev_t dev)
bh = free_list;
for (i = nr_buffers*2 ; i-- > 0 ; bh = bh->b_next_free) {
+ if (dev && bh->b_dev != dev)
+ continue;
if (bh->b_lock)
continue;
if (!bh->b_dirt)
@@ -145,14 +147,12 @@ void check_disk_change(dev_t dev)
#if defined(CONFIG_BLK_DEV_SD) && defined(CONFIG_SCSI)
case 8: /* Removable scsi disk */
i = check_scsidisk_media_change(dev, 0);
- if (i) printk("Flushing buffers and inodes for SCSI disk\n");
break;
#endif
#if defined(CONFIG_BLK_DEV_SR) && defined(CONFIG_SCSI)
case 11: /* CDROM */
i = check_cdrom_media_change(dev, 0);
- if (i) printk("Flushing buffers and inodes for CDROM\n");
break;
#endif
@@ -162,6 +162,8 @@ void check_disk_change(dev_t dev)
if (!i) return;
+ printk("VFS: Disk change detected on device %d/%d\n",
+ MAJOR(dev), MINOR(dev));
for (i=0 ; i<NR_SUPER ; i++)
if (super_block[i].s_dev == dev)
put_super(super_block[i].s_dev);
@@ -193,7 +195,7 @@ static inline void remove_from_hash_queue(struct buffer_head * bh)
static inline void remove_from_free_list(struct buffer_head * bh)
{
if (!(bh->b_prev_free) || !(bh->b_next_free))
- panic("Free block list corrupted");
+ panic("VFS: Free block list corrupted");
bh->b_prev_free->b_next_free = bh->b_next_free;
bh->b_next_free->b_prev_free = bh->b_prev_free;
if (free_list == bh)
@@ -263,7 +265,8 @@ static struct buffer_head * find_buffer(dev_t dev, int block, int size)
if (tmp->b_size == size)
return tmp;
else {
- printk("wrong block-size on device %04x\n",dev);
+ printk("VFS: Wrong blocksize on device %d/%d\n",
+ MAJOR(dev), MINOR(dev));
return NULL;
}
return NULL;
@@ -328,6 +331,8 @@ repeat:
for (tmp = free_list; buffers-- > 0 ; tmp = tmp->b_next_free) {
if (tmp->b_count || tmp->b_size != size)
continue;
+ if (mem_map[MAP_NR((unsigned long) tmp->b_data)] != 1)
+ continue;
if (!bh || BADNESS(tmp)<BADNESS(bh)) {
bh = tmp;
if (!BADNESS(tmp))
@@ -383,7 +388,7 @@ void brelse(struct buffer_head * buf)
wake_up(&buffer_wait);
return;
}
- printk("Trying to free free buffer\n");
+ printk("VFS: brelse: Trying to free free buffer\n");
}
/*
@@ -395,7 +400,8 @@ struct buffer_head * bread(dev_t dev, int block, int size)
struct buffer_head * bh;
if (!(bh = getblk(dev, block, size))) {
- printk("bread: getblk returned NULL\n");
+ printk("VFS: bread: READ error on device %d/%d\n",
+ MAJOR(dev), MINOR(dev));
return NULL;
}
if (bh->b_uptodate)
@@ -408,46 +414,6 @@ struct buffer_head * bread(dev_t dev, int block, int size)
return NULL;
}
-#define COPYBLK(from,to) \
-__asm__("cld\n\t" \
- "rep\n\t" \
- "movsl\n\t" \
- ::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \
- :"cx","di","si")
-
-/*
- * bread_page reads four buffers into memory at the desired address. It's
- * a function of its own, as there is some speed to be got by reading them
- * all at the same time, not waiting for one to be read, and then another
- * etc.
- */
-void bread_page(unsigned long address, dev_t dev, int b[4])
-{
- struct buffer_head * bh[4];
- struct buffer_head * bhr[4];
- int bhnum = 0;
- int i;
-
- for (i=0 ; i<4 ; i++)
- if (b[i]) {
- bh[i] = getblk(dev, b[i], 1024);
- if (bh[i] && !bh[i]->b_uptodate)
- bhr[bhnum++] = bh[i];
- } else
- bh[i] = NULL;
-
- if (bhnum)
- ll_rw_block(READ, bhnum, bhr);
-
- for (i=0 ; i<4 ; i++,address += BLOCK_SIZE)
- if (bh[i]) {
- wait_on_buffer(bh[i]);
- if (bh[i]->b_uptodate)
- COPYBLK((unsigned long) bh[i]->b_data,address);
- brelse(bh[i]);
- }
-}
-
/*
* Ok, breada can be used as bread, but additionally to mark other
* blocks for reading as well. End the argument list with a negative
@@ -460,7 +426,8 @@ struct buffer_head * breada(dev_t dev,int first, ...)
va_start(args,first);
if (!(bh = getblk(dev, first, 1024))) {
- printk("breada: getblk returned NULL\n");
+ printk("VFS: breada: READ error on device %d/%d\n",
+ MAJOR(dev), MINOR(dev));
return NULL;
}
if (!bh->b_uptodate)
@@ -528,6 +495,58 @@ static struct buffer_head * get_unused_buffer_head(void)
return bh;
}
+static inline unsigned long try_to_share_buffers(unsigned long address, dev_t dev, int b[], int size)
+{
+ return 0;
+}
+
+#define COPYBLK(from,to) \
+__asm__ __volatile__("rep ; movsl" \
+ ::"c" (BLOCK_SIZE/4),"S" (from),"D" (to) \
+ :"cx","di","si")
+
+/*
+ * bread_page reads four buffers into memory at the desired address. It's
+ * a function of its own, as there is some speed to be got by reading them
+ * all at the same time, not waiting for one to be read, and then another
+ * etc. This also allows us to optimize memory usage by sharing code pages
+ * and filesystem buffers.. This is not yet implemented.
+ */
+unsigned long bread_page(unsigned long address, dev_t dev, int b[], int size, int prot)
+{
+ struct buffer_head * bh[4];
+ struct buffer_head * bhr[4];
+ unsigned long where;
+ int bhnum = 0;
+ int i;
+
+ if (!(prot & PAGE_RW)) {
+ where = try_to_share_buffers(address,dev,b,size);
+ if (where)
+ return where;
+ }
+ for (i=0 ; i<4 ; i++) {
+ bh[i] = NULL;
+ if (b[i]) {
+ bh[i] = getblk(dev, b[i], size);
+ if (bh[i] && !bh[i]->b_uptodate)
+ bhr[bhnum++] = bh[i];
+ }
+ }
+ if (bhnum)
+ ll_rw_block(READ, bhnum, bhr);
+ where = address;
+ for (i=0 ; i<4 ; i++,address += BLOCK_SIZE) {
+ if (bh[i]) {
+ wait_on_buffer(bh[i]);
+ if (bh[i]->b_uptodate)
+ COPYBLK((unsigned long) bh[i]->b_data,address);
+ brelse(bh[i]);
+ }
+ }
+ return where;
+}
+
/*
* Try to increase the number of buffers available: the size argument
* is used to determine what kind of buffers we want. Currently only
@@ -541,7 +560,7 @@ void grow_buffers(int size)
struct buffer_head *bh, *tmp;
if ((size & 511) || (size > 4096)) {
- printk("grow_buffers: size = %d\n",size);
+ printk("VFS: grow_buffers: size = %d\n",size);
return;
}
page = get_free_page(GFP_BUFFER);
@@ -601,6 +620,10 @@ static int try_to_free(struct buffer_head * bh)
unsigned long page;
struct buffer_head * tmp, * p;
+ page = (unsigned long) bh->b_data;
+ page &= 0xfffff000;
+ if (mem_map[MAP_NR(page)] != 1)
+ return 0;
tmp = bh;
do {
if (!tmp)
@@ -609,8 +632,6 @@ static int try_to_free(struct buffer_head * bh)
return 0;
tmp = tmp->b_this_page;
} while (tmp != bh);
- page = (unsigned long) bh->b_data;
- page &= 0xfffff000;
tmp = bh;
do {
p = tmp;
@@ -678,6 +699,6 @@ void buffer_init(void)
free_list = 0;
grow_buffers(BLOCK_SIZE);
if (!free_list)
- panic("Unable to initialize buffer free list!");
+ panic("VFS: Unable to initialize buffer free list!");
return;
}
diff --git a/fs/exec.c b/fs/exec.c
index 6ff1047..15a3e11 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -184,6 +184,7 @@ int sys_uselib(const char * library)
struct inode * inode;
struct buffer_head * bh;
struct exec ex;
+ unsigned long offset;
int error;
if (!library || get_limit(0x17) != TASK_SIZE)
@@ -211,18 +212,21 @@ int sys_uselib(const char * library)
}
ex = *(struct exec *) bh->b_data;
brelse(bh);
- if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||
- ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||
+ if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize ||
+ ex.a_drsize || ex.a_entry & 0xfff ||
inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
iput(inode);
return -ENOEXEC;
}
current->libraries[libnum].library = inode;
current->libraries[libnum].start = ex.a_entry;
- current->libraries[libnum].length = (ex.a_data+ex.a_text+0xfff) & 0xfffff000;
- current->libraries[libnum].bss = (ex.a_bss+0xfff) & 0xfffff000;
+ offset = (ex.a_data + ex.a_text + 0xfff) & 0xfffff000;
+ current->libraries[libnum].length = offset;
+ current->libraries[libnum].bss = ex.a_bss;
+ offset += ex.a_entry;
+ zeromap_page_range(offset, ex.a_bss, PAGE_COPY);
#if 0
- printk("Loaded library %d at %08x, length %08x\n",
+ printk("VFS: Loaded library %d at %08x, length %08x\n",
libnum,
current->libraries[libnum].start,
current->libraries[libnum].length);
@@ -315,7 +319,7 @@ static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
if (from_kmem == 1)
set_fs(new_fs);
if (!(tmp = (char *)get_fs_long(((unsigned long *)argv)+argc)))
- panic("argc is wrong");
+ panic("VFS: argc is wrong");
if (from_kmem == 1)
set_fs(old_fs);
len=0; /* remember zero-padding */
@@ -432,7 +436,7 @@ int do_execve(unsigned long * eip,long tmp,char * filename,
int ch;
if ((0xffff & eip[1]) != 0x000f)
- panic("execve called from supervisor mode");
+ panic("VFS: execve called from supervisor mode");
for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
page[i]=0;
retval = namei(filename,&inode); /* get executable inode */
@@ -551,24 +555,22 @@ restart_interp:
}
/*
* OK, now restart the process with the interpreter's inode.
+ * Note that we use open_namei() as the name is now in kernel
+ * space, and we don't need to copy it.
*/
- old_fs = get_fs();
- set_fs(get_ds());
- retval = namei(interp,&inode);
- set_fs(old_fs);
+ retval = open_namei(interp,0,0,&inode,NULL);
if (retval)
goto exec_error1;
goto restart_interp;
}
if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC) ||
ex.a_trsize || ex.a_drsize ||
- ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||
inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
retval = -ENOEXEC;
goto exec_error2;
}
if (N_TXTOFF(ex) != BLOCK_SIZE && N_MAGIC(ex) != OMAGIC) {
- printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename);
+ printk("VFS: N_TXTOFF != BLOCK_SIZE. See a.out.h.");
retval = -ENOEXEC;
goto exec_error2;
}
@@ -635,6 +637,7 @@ restart_interp:
iput(inode);
} else
current->executable = inode;
+ zeromap_page_range((ex.a_text + ex.a_data + 0xfff) & 0xfffff000,ex.a_bss, PAGE_COPY);
eip[0] = ex.a_entry; /* eip, magic happens :-) */
eip[3] = p; /* stack pointer */
if (current->flags & PF_PTRACED)
diff --git a/fs/ext/namei.c b/fs/ext/namei.c
index 1421b54..62045f1 100644
--- a/fs/ext/namei.c
+++ b/fs/ext/namei.c
@@ -65,7 +65,7 @@ static int ext_match(int len,const char * name,struct ext_dir_entry * de)
if (len < EXT_NAME_LEN && len != de->name_len)
return 0;
__asm__("cld\n\t"
- "fs ; repe ; cmpsb\n\t"
+ "repe ; cmpsb\n\t"
"setz %%al"
:"=a" (same)
:"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
@@ -291,7 +291,7 @@ printk ("ext_add_entry : creating next block\n");
dir->i_mtime = CURRENT_TIME;
de->name_len = namelen;
for (i=0; i < namelen ; i++)
- de->name[i]=get_fs_byte(name+i);
+ de->name[i] = name[i];
bh->b_dirt = 1;
*res_dir = de;
return bh;
@@ -612,6 +612,8 @@ int ext_unlink(struct inode * dir, const char * name, int len)
inode->i_nlink--;
inode->i_dirt = 1;
inode->i_ctime = CURRENT_TIME;
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt = 1;
retval = 0;
end_unlink:
brelse(bh);
@@ -643,7 +645,7 @@ int ext_symlink(struct inode * dir, const char * name, int len, const char * sym
return -ENOSPC;
}
i = 0;
- while (i < 1023 && (c=get_fs_byte(symname++)))
+ while (i < 1023 && (c = *(symname++)))
name_block->b_data[i++] = c;
name_block->b_data[i] = 0;
name_block->b_dirt = 1;
@@ -716,12 +718,9 @@ int ext_link(struct inode * oldinode, struct inode * dir, const char * name, int
static int subdir(struct inode * new, struct inode * old)
{
- unsigned short fs;
int ino;
int result;
- __asm__("mov %%fs,%0":"=r" (fs));
- __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
new->i_count++;
result = 0;
for (;;) {
@@ -738,7 +737,6 @@ static int subdir(struct inode * new, struct inode * old)
break;
}
iput(new);
- __asm__("mov %0,%%fs"::"r" (fs));
return result;
}
diff --git a/fs/ext/symlink.c b/fs/ext/symlink.c
index 27a5f97..8d46ce1 100644
--- a/fs/ext/symlink.c
+++ b/fs/ext/symlink.c
@@ -76,12 +76,9 @@ static int ext_follow_link(struct inode * dir, struct inode * inode,
return -EIO;
}
iput(inode);
- __asm__("mov %%fs,%0":"=r" (fs));
- __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
current->link_count++;
error = open_namei(bh->b_data,flag,mode,res_inode,dir);
current->link_count--;
- __asm__("mov %0,%%fs"::"r" (fs));
brelse(bh);
return error;
}
diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile
new file mode 100644
index 0000000..d4abbd3
--- /dev/null
+++ b/fs/ext2/Makefile
@@ -0,0 +1,34 @@
+#
+# Makefile for the linux ext2-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+
+OBJS= balloc.o bitmap.o dcache.o dir.o file.o ialloc.o inode.o \
+ namei.o symlink.o truncate.o
+
+ext2.o: $(OBJS)
+ $(LD) -r -o ext2.o $(OBJS)
+
+clean:
+ rm -f core *.s *.o *.a
+
+dep:
+ $(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
new file mode 100644
index 0000000..6ea0c62
--- /dev/null
+++ b/fs/ext2/balloc.c
@@ -0,0 +1,390 @@
+/*
+ * linux/fs/ext2/balloc.c
+ *
+ * Copyright (C) 1992, 1993 Remy Card (card@masi.ibp.fr)
+ *
+ */
+
+/* balloc.c contains the blocks allocation and deallocation routines */
+
+/*
+
+ The free blocks are managed by bitmaps. A file system contains several
+ blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap
+ block for inodes, N blocks for the inode table and data blocks.
+
+ The file system contains group descriptors which are located after the
+ super block. Each descriptor contains the number of the bitmap block and
+ the free blocks count in the block. The descriptors are loaded in memory
+ when a file system is mounted (see ext2_read_super).
+
+*/
+
+#include <linux/sched.h>
+#include <linux/ext2_fs.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+#define clear_block(addr,size) \
+ __asm__("cld\n\t" \
+ "rep\n\t" \
+ "stosl" \
+ : \
+ :"a" (0), "c" (size/4), "D" ((long) (addr)) \
+ :"cx", "di")
+
+#define set_bit(nr,addr) ( \
+{ \
+ char res; \
+ __asm__ __volatile__("btsl %1,%2\n\tsetb %0" \
+ :"=q" (res) \
+ :"r" (nr),"m" (*(addr))); \
+ res; \
+} \
+)
+
+#define clear_bit(nr,addr) ( \
+{ \
+ char res; \
+ __asm__ __volatile__("btrl %1,%2\n\tsetnb %0" \
+ :"=q" (res) \
+ :"r" (nr),"m" (*(addr))); \
+ res; \
+} \
+)
+
+#define find_first_zero(addr,size) ( \
+{ \
+ int __res; \
+ __asm__("cld\n" \
+ "1:\tlodsl\n\t" \
+ "notl %%eax\n\t" \
+ "bsfl %%eax,%%edx\n\t" \
+ "jne 2f\n\t" \
+ "addl $32,%%ecx\n\t" \
+ "cmpl %%ebx,%%ecx\n\t" \
+ "jl 1b\n\t" \
+ "xorl %%edx,%%edx\n" \
+ "2:\taddl %%edx,%%ecx" \
+ :"=c" (__res):"0" (0), "S" (addr), "b" (size) \
+ :"ax", "bx", "dx", "si"); \
+ __res; \
+} \
+)
+
+static void read_block_bitmap (struct super_block * sb,
+ unsigned int block_group,
+ unsigned long bitmap_nr)
+{
+ unsigned long group_desc;
+ unsigned long desc;
+ struct ext2_group_desc * gdp;
+ struct buffer_head * bh;
+
+ group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
+ desc = block_group % EXT2_DESC_PER_BLOCK(sb);
+ if (!sb->u.ext2_sb.s_group_desc[group_desc]) {
+ printk ("block_group = %d,group_desc = %d,desc = %d\n",
+ block_group, group_desc, desc);
+ panic ("read_block_bitmap: Group descriptor not loaded");
+ }
+ gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
+ bh = bread (sb->s_dev, gdp[desc].bg_block_bitmap, sb->s_blocksize);
+ if (!bh) {
+ printk ("block_group = %d,group_desc = %d,desc = %d,block_bitmap = %d\n",
+ block_group, group_desc, desc, gdp[desc].bg_block_bitmap);
+ panic ("read_block_bitmap: Cannot read block bitmap");
+ }
+ sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group;
+ sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh;
+}
+
+/*
+ * load_block_bitmap loads the block bitmap for a blocks group
+ *
+ * It maintains a cache for the last bitmaps loaded. This cache is managed
+ * with a LRU algorithm.
+ *
+ * Notes:
+ * 1/ There is one cache per mounted file system.
+ * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
+ * this function reads the bitmap without maintaining a LRU cache.
+ */
+static int load_block_bitmap (struct super_block * sb,
+ unsigned int block_group)
+{
+ int i, j;
+ unsigned long block_bitmap_number;
+ struct buffer_head * block_bitmap;
+
+ if (block_group >= sb->u.ext2_sb.s_groups_count) {
+ printk ("block_group = %d, groups_count = %d\n",
+ block_group, sb->u.ext2_sb.s_groups_count);
+ panic ("load_block_bitmap: block_group >= groups_count");
+ }
+ if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 &&
+ sb->u.ext2_sb.s_block_bitmap_number[0] == block_group)
+ return 0;
+
+ if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) {
+ if (sb->u.ext2_sb.s_block_bitmap[block_group]) {
+ if (sb->u.ext2_sb.s_block_bitmap_number[block_group] != block_group)
+ panic ("load_block_bitmap: block_group != block_bitmap_number");
+ else
+ return block_group;
+ } else {
+ read_block_bitmap (sb, block_group, block_group);
+ return block_group;
+ }
+ }
+
+ for (i = 0; i < sb->u.ext2_sb.s_loaded_block_bitmaps &&
+ sb->u.ext2_sb.s_block_bitmap_number[i] != block_group; i++)
+ ;
+ if (i < sb->u.ext2_sb.s_loaded_block_bitmaps &&
+ sb->u.ext2_sb.s_block_bitmap_number[i] == block_group) {
+ block_bitmap_number = sb->u.ext2_sb.s_block_bitmap_number[i];
+ block_bitmap = sb->u.ext2_sb.s_block_bitmap[i];
+ for (j = i; j > 0; j--) {
+ sb->u.ext2_sb.s_block_bitmap_number[j] =
+ sb->u.ext2_sb.s_block_bitmap_number[j - 1];
+ sb->u.ext2_sb.s_block_bitmap[j] =
+ sb->u.ext2_sb.s_block_bitmap[j - 1];
+ }
+ sb->u.ext2_sb.s_block_bitmap_number[0] = block_bitmap_number;
+ sb->u.ext2_sb.s_block_bitmap[0] = block_bitmap;
+ } else {
+ if (sb->u.ext2_sb.s_loaded_block_bitmaps < EXT2_MAX_GROUP_LOADED)
+ sb->u.ext2_sb.s_loaded_block_bitmaps++;
+ else
+ brelse (sb->u.ext2_sb.s_block_bitmap[EXT2_MAX_GROUP_LOADED - 1]);
+ for (j = sb->u.ext2_sb.s_loaded_block_bitmaps - 1; j > 0; j--) {
+ sb->u.ext2_sb.s_block_bitmap_number[j] =
+ sb->u.ext2_sb.s_block_bitmap_number[j - 1];
+ sb->u.ext2_sb.s_block_bitmap[j] =
+ sb->u.ext2_sb.s_block_bitmap[j - 1];
+ }
+ read_block_bitmap (sb, block_group, 0);
+ }
+ return 0;
+}
+
+void ext2_free_block (struct super_block * sb, unsigned long block)
+{
+ struct buffer_head * bh;
+ struct buffer_head * bh2;
+ unsigned long block_group;
+ unsigned long bit;
+ unsigned long group_desc;
+ unsigned long desc;
+ int bitmap_nr;
+ struct ext2_group_desc * gdp;
+ struct ext2_super_block * es;
+
+ if (!sb) {
+ printk ("ext2_free_block: nonexistant device");
+ return;
+ }
+ lock_super (sb);
+ if (block < sb->u.ext2_sb.s_first_data_block ||
+ block >= sb->u.ext2_sb.s_blocks_count) {
+ printk ("ext2_free_block: block not in datazone\n");
+ unlock_super (sb);
+ return;
+ }
+ es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_free_block: freeing block %d\n", block);
+#endif
+ bh = get_hash_table (sb->s_dev, block, sb->s_blocksize);
+ if (bh)
+ bh->b_dirt = 0;
+ brelse (bh);
+ block_group = (block - sb->u.ext2_sb.s_first_data_block) /
+ EXT2_BLOCKS_PER_GROUP(sb);
+ bit = (block - sb->u.ext2_sb.s_first_data_block) %
+ EXT2_BLOCKS_PER_GROUP(sb);
+ bitmap_nr = load_block_bitmap (sb, block_group);
+ bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
+ if (!bh) {
+ printk ("block_group = %d\n", block_group);
+ panic ("ext2_free_block: Unable to load group bitmap");
+ }
+ if (clear_bit (bit, bh->b_data))
+ printk ("ext2_free_block (%04x:%d): bit already cleared\n",
+ sb->s_dev, block);
+ else {
+ group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
+ desc = block_group % EXT2_DESC_PER_BLOCK(sb);
+ bh2 = sb->u.ext2_sb.s_group_desc[group_desc];
+ if (!bh2) {
+ printk ("group_desc = %d\n", group_desc);
+ panic ("ext2_free_block: Group descriptor not loaded");
+ }
+ gdp = (struct ext2_group_desc *) bh2->b_data;
+ gdp[desc].bg_free_blocks_count ++;
+ bh2->b_dirt = 1;
+ }
+ bh->b_dirt = 1;
+ es->s_free_blocks_count ++;
+ sb->u.ext2_sb.s_sbh->b_dirt = 1;
+ sb->s_dirt = 1;
+ unlock_super (sb);
+ return;
+}
+
+/*
+ * ext2_new_block does not use a very clever allocation algorithm yet
+ *
+ * Currently, the group descriptors are scanned until a free block is found
+ */
+int ext2_new_block (struct super_block * sb, unsigned long block_group)
+{
+ struct buffer_head * bh;
+ int i, j;
+ unsigned long group_desc;
+ unsigned long desc;
+ int bitmap_nr;
+ struct ext2_group_desc * gdp;
+ struct ext2_super_block * es;
+
+ if (!sb) {
+ printk ("ext2_new_block: nonexistant device");
+ return 0;
+ }
+ lock_super (sb);
+ es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ if (es->s_free_blocks_count <= es->s_r_blocks_count && !suser()) {
+ unlock_super (sb);
+ return 0;
+ }
+
+repeat:
+ group_desc = 0;
+ desc = 0;
+ gdp = NULL;
+ for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+ if (!gdp) {
+ if (!sb->u.ext2_sb.s_group_desc[group_desc])
+ panic ("ext2_new_block: Descriptor not loaded");
+ gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
+ }
+ if (gdp[desc].bg_free_blocks_count > 0)
+ break;
+ desc ++;
+ if (desc == EXT2_DESC_PER_BLOCK(sb)) {
+ group_desc ++;
+ desc = 0;
+ gdp = NULL;
+ }
+ }
+ if (i >= sb->u.ext2_sb.s_groups_count) {
+ unlock_super (sb);
+ return 0;
+ }
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_new_block: using block group %d(%d,%d,%d)\n",
+ i, group_desc, desc, gdp[desc].bg_free_blocks_count);
+#endif
+ bitmap_nr = load_block_bitmap (sb, i);
+ bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
+ if (!bh) {
+ printk ("block_group = %d\n", i);
+ panic ("ext2_new_block: Unable to load group bitmap");
+ }
+ if ((j = find_first_zero (bh->b_data, EXT2_BLOCKS_PER_GROUP(sb))) <
+ EXT2_BLOCKS_PER_GROUP(sb)) {
+ if (set_bit (j, bh->b_data)) {
+ printk ("ext2_new_block: bit already set\n");
+ goto repeat;
+ }
+ bh->b_dirt = 1;
+ } else
+ goto repeat;
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_new_block: found bit %d\n", j);
+#endif
+ j += i * EXT2_BLOCKS_PER_GROUP(sb) +
+ sb->u.ext2_sb.s_first_data_block;
+ if (j >= sb->u.ext2_sb.s_blocks_count) {
+ printk ("block_group = %d,block=%d\n", i, j);
+ printk ("ext2_new_block: block >= blocks count");
+ return 0;
+ }
+ if (!(bh = getblk (sb->s_dev, j, sb->s_blocksize))) {
+ printk ("ext2_new_block: cannot get block");
+ unlock_super (sb);
+ return 0;
+ }
+ clear_block (bh->b_data, sb->s_blocksize);
+ bh->b_uptodate = 1;
+ bh->b_dirt = 1;
+ brelse (bh);
+#ifdef EXT2FS_DEBUG
+ printk("ext2_new_block: allocating block %d\n", j);
+#endif
+ gdp[desc].bg_free_blocks_count --;
+ sb->u.ext2_sb.s_group_desc[group_desc]->b_dirt = 1;
+ es->s_free_blocks_count --;
+ sb->u.ext2_sb.s_sbh->b_dirt = 1;
+ sb->s_dirt = 1;
+ unlock_super (sb);
+ return j;
+}
+
+unsigned long ext2_count_free_blocks (struct super_block *sb)
+{
+ struct ext2_super_block * es;
+#ifdef EXT2FS_DEBUG
+ unsigned long desc_count, bitmap_count, x;
+ unsigned long group_desc;
+ unsigned long desc;
+ int bitmap_nr;
+ struct ext2_group_desc * gdp;
+ int i;
+
+ lock_super (sb);
+ es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ desc_count = 0;
+ bitmap_count = 0;
+ group_desc = 0;
+ desc = 0;
+ gdp = NULL;
+ for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+ if (!gdp) {
+ if (!sb->u.ext2_sb.s_group_desc[group_desc]) {
+ printk ("ext2_count_free_block: Descriptor not loaded\n");
+ break;
+ }
+ gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
+ }
+ desc_count += gdp[desc].bg_free_blocks_count;
+ bitmap_nr = load_block_bitmap (sb, i);
+ if (sb->u.ext2_sb.s_block_bitmap[bitmap_nr])
+ x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr],
+ sb->s_blocksize);
+ else {
+ x = 0;
+ printk ("Cannot load bitmap for group %d\n", i);
+ }
+ printk ("group %d: stored = %d, counted = %d\n",
+ i, gdp[desc].bg_free_blocks_count, x);
+ bitmap_count += x;
+ desc ++;
+ if (desc == EXT2_DESC_PER_BLOCK(sb)) {
+ group_desc ++;
+ desc = 0;
+ gdp = NULL;
+ }
+ }
+ printk("ext2_count_free_blocks: stored = %d, computed = %d, %d\n",
+ es->s_free_blocks_count, desc_count, bitmap_count);
+ unlock_super (sb);
+ return bitmap_count;
+#else
+ es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ return es->s_free_blocks_count;
+#endif
+}
diff --git a/fs/ext2/bitmap.c b/fs/ext2/bitmap.c
new file mode 100644
index 0000000..fcaa960
--- /dev/null
+++ b/fs/ext2/bitmap.c
@@ -0,0 +1,23 @@
+/*
+ * linux/fs/ext2/bitmap.c
+ *
+ * Copyright (C) 1992, 1993 Remy Card (card@masi.ibp.fr)
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+
+static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
+
+unsigned long ext2_count_free (struct buffer_head * map, unsigned numchars)
+{
+ unsigned i;
+ unsigned long sum = 0;
+
+ if (!map)
+ return (0);
+ for (i = 0; i < numchars; i++)
+ sum += nibblemap[map->b_data[i] & 0xf] +
+ nibblemap[(map->b_data[i] >> 4) & 0xf];
+ return (sum);
+}
diff --git a/fs/ext2/dcache.c b/fs/ext2/dcache.c
new file mode 100644
index 0000000..e5fec8b
--- /dev/null
+++ b/fs/ext2/dcache.c
@@ -0,0 +1,336 @@
+/*
+ * linux/fs/ext2/dcache.c
+ *
+ * Copyright (C) 1992, 1993 Remy Card (card@masi.ibp.fr)
+ *
+ */
+
+/*
+ * dcache.c contains the code that handles the directory cache used by
+ * lookup() and readdir()
+ */
+
+#include <asm/segment.h>
+
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#ifndef DONT_USE_DCACHE
+
+struct dir_cache_entry {
+ unsigned short dev;
+ unsigned long dir;
+ unsigned long ino;
+ char name[EXT2_NAME_LEN];
+ int len;
+ struct dir_cache_entry *queue_prev;
+ struct dir_cache_entry *queue_next;
+ struct dir_cache_entry *prev;
+ struct dir_cache_entry *next;
+};
+
+static struct dir_cache_entry *first = NULL;
+static struct dir_cache_entry *last = NULL;
+static struct dir_cache_entry *first_free = NULL;
+static int cache_initialized = 0;
+#ifdef EXT2FS_DEBUG_CACHE
+static int hits = 0;
+static int misses = 0;
+#endif
+
+#define CACHE_SIZE 128
+
+static struct dir_cache_entry dcache[CACHE_SIZE];
+
+#define HASH_QUEUES 16
+
+static struct dir_cache_entry *queue_head[HASH_QUEUES];
+static struct dir_cache_entry *queue_tail[HASH_QUEUES];
+
+#define hash(dev,dir) ((dev ^ dir) % HASH_QUEUES)
+
+/*
+ * Initialize the cache
+ */
+static void init_cache (void)
+{
+ int i;
+
+ dcache[0].prev = NULL;
+ dcache[0].next = &dcache[1];
+ dcache[0].queue_next = dcache[0].queue_prev = NULL;
+ for (i = 1; i < CACHE_SIZE - 1; i++) {
+ dcache[i].prev = &dcache[i - 1];
+ dcache[i].next = &dcache[i + 1];
+ dcache[i].queue_next = dcache[i].queue_prev = NULL;
+ }
+ dcache[i].prev = &dcache[i - 1];
+ dcache[i].next = NULL;
+ dcache[i].queue_next = dcache[i].queue_prev = NULL;
+ first_free = &dcache[0];
+ for (i = 0; i < HASH_QUEUES; i++)
+ queue_tail[i] = queue_head[i] = NULL;
+ cache_initialized = 1;
+}
+
+/*
+ * Find a name in the cache
+ */
+static struct dir_cache_entry *find_name (int queue, unsigned short dev,
+ unsigned long dir, const char *name,
+ int len)
+{
+ struct dir_cache_entry *p;
+
+ for (p = queue_head[queue]; p!= NULL && (p->dev != dev ||
+ p->dir != dir || p->len != len || strncmp (name, p->name, p->len));
+ p = p->queue_next)
+ ;
+ return p;
+}
+
+#ifdef EXT2FS_DEBUG_CACHE
+/*
+ * List the cache entries for debugging
+ */
+static void show_cache (const char *func_name)
+{
+ struct dir_cache_entry *p;
+
+ printk ("%s: cache status\n", func_name);
+ for (p = first; p != NULL; p = p->next)
+ printk ("dev:%04x, dir=%4d, name=%s\n",
+ p->dev, p->dir, p->name);
+}
+#endif
+
+/*
+ * Add an entry at the beginning of the cache
+ */
+static void add_to_cache (struct dir_cache_entry *p)
+{
+ p->prev = NULL;
+ p->next = first;
+ if (first)
+ first->prev = p;
+ if (!last)
+ last = p;
+ first = p;
+}
+
+/*
+ * Add an entry at the beginning of a queue
+ */
+static void add_to_queue (int queue, struct dir_cache_entry *p)
+{
+ p->queue_prev = NULL;
+ p->queue_next = queue_head[queue];
+ if (queue_head[queue])
+ queue_head[queue]->queue_prev = p;
+ if (!queue_tail[queue])
+ queue_tail[queue] = p;
+ queue_head[queue] = p;
+}
+
+/*
+ * Remove an entry from the cache
+ */
+static void remove_from_cache (struct dir_cache_entry *p)
+{
+ if (p->prev)
+ p->prev->next = p->next;
+ else
+ first = p->next;
+ if (p->next)
+ p->next->prev = p->prev;
+ else
+ last = p->prev;
+}
+
+/*
+ * Remove an entry from a queue
+ */
+static void remove_from_queue (int queue, struct dir_cache_entry *p)
+{
+ if (p->queue_prev)
+ p->queue_prev->queue_next = p->queue_next;
+ else
+ queue_head[queue] = p->queue_next;
+ if (p->queue_next)
+ p->queue_next->queue_prev = p->queue_prev;
+ else
+ queue_tail[queue] = p->queue_prev;
+}
+
+/*
+ * Invalidate all cache entries on a device (called by put_super() when
+ * a file system is unmounted)
+ */
+void ext2_dcache_invalidate (unsigned short dev)
+{
+ struct dir_cache_entry *p;
+ struct dir_cache_entry *p2;
+
+ if (!cache_initialized)
+ init_cache ();
+ for (p = first; p != NULL; p = p2) {
+ p2 = p->next;
+ if (p->dev == dev) {
+ remove_from_cache (p);
+ remove_from_queue (hash (p->dev, p->dir), p);
+ p->next = first_free;
+ first_free = p;
+ }
+ }
+#ifdef EXT2FS_DEBUG_CACHE
+ show_cache ("dcache_invalidate");
+#endif
+}
+
+/*
+ * Lookup a directory entry in the cache
+ *
+ * Note: the name is in the caller's address space
+ */
+unsigned long ext2_dcache_lookup (unsigned short dev, unsigned long dir,
+ const char *name, int len)
+{
+ char our_name[EXT2_NAME_LEN];
+ int queue;
+ struct dir_cache_entry *p;
+
+ if (!cache_initialized)
+ init_cache ();
+ if (len > EXT2_NAME_LEN)
+ len = EXT2_NAME_LEN;
+ memcpy (our_name, (char *) name, len);
+ our_name[len] = '\0';
+#ifdef EXT2FS_DEBUG_CACHE
+ printk ("dcache_lookup (%04x, %d, %s, %d)\n", dev, dir, our_name, len);
+#endif
+ queue = hash(dev, dir);
+ if ((p = find_name (queue, dev, dir, our_name, len))) {
+ if (p != first) {
+ remove_from_cache (p);
+ add_to_cache (p);
+ }
+ if (p != queue_head[queue]) {
+ remove_from_queue (queue, p);
+ add_to_queue (queue, p);
+ }
+#ifdef EXT2FS_DEBUG_CACHE
+ hits ++;
+ printk ("dcache_lookup: %s,hit,inode=%d,hits=%d,misses=%d\n",
+ our_name, p->ino, hits, misses);
+ show_cache ("dcache_lookup");
+#endif
+ return p->ino;
+ } else {
+#ifdef EXT2FS_DEBUG_CACHE
+ misses ++;
+ printk ("dcache_lookup: %s,miss,hits=%d,misses=%d\n",
+ our_name, hits, misses);
+ show_cache ("dcache_lookup");
+#endif
+ return 0;
+ }
+}
+
+/*
+ * Add a directory entry to the cache
+ *
+ * This function is called by ext2_lookup(), ext2_readdir()
+ * and the functions which create directory entries
+ *
+ * Note: the name is in the kernel address space
+ */
+void ext2_dcache_add (unsigned short dev, unsigned long dir, const char *name,
+ int len, int ino)
+{
+ struct dir_cache_entry *p;
+ int queue;
+
+ if (!cache_initialized)
+ init_cache ();
+#ifdef EXT2FS_DEBUG_CACHE
+ printk ("dcache_add (%04x, %d, %s, %d, %d)\n",
+ dev, dir, name, len, ino);
+#endif
+ if (len > EXT2_NAME_LEN)
+ len = EXT2_NAME_LEN;
+ queue = hash(dev, dir);
+ if ((p = find_name (queue, dev, dir, name, len))) {
+ p->dir = dir;
+ p->ino = ino;
+ if (p != first) {
+ remove_from_cache (p);
+ add_to_cache (p);
+ }
+ if (p != queue_head[queue]) {
+ remove_from_queue (queue, p);
+ add_to_queue (queue, p);
+ }
+ } else {
+ if (first_free) {
+ p = first_free;
+ first_free = p->next;
+ } else {
+ if (!last)
+ panic ("dcache_add: last == NULL\n");
+ else {
+ p = last;
+ last = p->prev;
+ if (last)
+ last->next = NULL;
+ remove_from_queue (hash (p->dev, p->dir), p);
+ }
+ }
+ p->dev = dev;
+ p->dir = dir;
+ p->ino = ino;
+ strncpy (p->name, name, len);
+ p->len = len;
+ p->name[len] = '\0';
+ add_to_cache (p);
+ add_to_queue (queue, p);
+ }
+#ifdef EXT2FS_DEBUG_CACHE
+ show_cache ("dcache_add");
+#endif
+}
+
+/*
+ * Remove a directory from the cache
+ *
+ * This function is called by the functions which remove directory entries
+ *
+ * Note: the name is in the kernel address space
+ */
+void ext2_dcache_remove (unsigned short dev, unsigned long dir,
+ const char *name, int len)
+{
+ struct dir_cache_entry *p;
+ int queue;
+
+ if (!cache_initialized)
+ init_cache ();
+#ifdef EXT2FS_DEBUG_CACHE
+ printk ("dcache_remove (%04x, %d, %s, %d)\n", dev, dir, name, len);
+#endif
+ if (len > EXT2_NAME_LEN)
+ len = EXT2_NAME_LEN;
+ queue = hash(dev, dir);
+ if ((p = find_name (queue, dev, dir, name, len))) {
+ remove_from_cache (p);
+ remove_from_queue (queue, p);
+ p->next = first_free;
+ first_free = p;
+ }
+#ifdef EXT2FS_DEBUG_CACHE
+ show_cache ("dcache_remove");
+#endif
+}
+
+#endif
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
new file mode 100644
index 0000000..0c7f9c1
--- /dev/null
+++ b/fs/ext2/dir.c
@@ -0,0 +1,121 @@
+/*
+ * linux/fs/ext2/dir.c
+ *
+ * Copyright (C) 1992, 1993 Remy Card (card@masi.ibp.fr)
+ *
+ * from
+ *
+ * linux/fs/minix/dir.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext2 directory handling functions
+ */
+
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+
+#if 0
+static int ext2_dir_read (struct inode * inode, struct file * filp,
+ char * buf, int count)
+{
+ return -EISDIR;
+}
+#endif
+
+/* static */ int ext2_file_read (struct inode *, struct file *, char *, int);
+static int ext2_readdir (struct inode *, struct file *, struct dirent *, int);
+
+static struct file_operations ext2_dir_operations = {
+ NULL, /* lseek - default */
+ ext2_file_read, /* read */
+ NULL, /* write - bad */
+ ext2_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* fsync */
+};
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations ext2_dir_inode_operations = {
+ &ext2_dir_operations, /* default directory file-ops */
+ ext2_create, /* create */
+ ext2_lookup, /* lookup */
+ ext2_link, /* link */
+ ext2_unlink, /* unlink */
+ ext2_symlink, /* symlink */
+ ext2_mkdir, /* mkdir */
+ ext2_rmdir, /* rmdir */
+ ext2_mknod, /* mknod */
+ ext2_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ ext2_truncate, /* truncate */
+ NULL /* permission */
+};
+
+static int ext2_readdir (struct inode * inode, struct file * filp,
+ struct dirent * dirent, int count)
+{
+ unsigned int offset, i;
+ struct buffer_head * bh;
+ struct ext2_dir_entry * de;
+ struct super_block * sb;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+ sb = inode->i_sb;
+ while (filp->f_pos < inode->i_size) {
+ offset = filp->f_pos & (sb->s_blocksize - 1);
+ bh = ext2_bread (inode, (filp->f_pos) >> EXT2_BLOCK_SIZE_BITS(sb), 0);
+ if (!bh) {
+ filp->f_pos += sb->s_blocksize - offset;
+ continue;
+ }
+ de = (struct ext2_dir_entry *) (offset + bh->b_data);
+ while (offset < sb->s_blocksize && filp->f_pos < inode->i_size) {
+ if (de->rec_len < EXT2_DIR_REC_LEN(1) ||
+ de->rec_len % 4 != 0 ||
+ de->rec_len < EXT2_DIR_REC_LEN(de->name_len)) {
+ printk ("ext2_readdir: bad directory entry (dev %04x, dir %d)\n",
+ inode->i_dev, inode->i_ino);
+ printk ("offset=%d, inode=%d, rec_len=%d, name_len=%d\n",
+ offset, de->inode, de->rec_len, de->name_len);
+ brelse (bh);
+ return 0;
+ }
+ offset += de->rec_len;
+ filp->f_pos += de->rec_len;
+ if (de->inode) {
+ memcpy_tofs (dirent->d_name, de->name,
+ de->name_len);
+ put_fs_long (de->inode, &dirent->d_ino);
+ put_fs_byte (0, de->name_len + dirent->d_name);
+ put_fs_word (de->name_len, &dirent->d_reclen);
+#ifndef DONT_USE_DCACHE
+ ext2_dcache_add (inode->i_dev, inode->i_ino,
+ de->name, de->name_len,
+ de->inode);
+#endif
+ i = de->name_len;
+ brelse (bh);
+ return i;
+ }
+ de = (struct ext2_dir_entry *) ((char *) de +
+ de->rec_len);
+ }
+ brelse (bh);
+ }
+ return 0;
+}
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
new file mode 100644
index 0000000..e2a3f1c
--- /dev/null
+++ b/fs/ext2/file.c
@@ -0,0 +1,266 @@
+/*
+ * linux/fs/ext2/file.c
+ *
+ * Copyright (C) 1992, 1993 Remy Card (card@masi.ibp.fr)
+ *
+ * from
+ *
+ * linux/fs/minix/file.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext2 fs regular file handling primitives
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/ext2_fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+
+#define NBUF 16
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+
+/* static */ int ext2_file_read (struct inode *, struct file *, char *, int);
+static int ext2_file_write (struct inode *, struct file *, char *, int);
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the ext2 filesystem.
+ */
+static struct file_operations ext2_file_operations = {
+ NULL, /* lseek - default */
+ ext2_file_read, /* read */
+ ext2_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ NULL /* fsync */
+};
+
+struct inode_operations ext2_file_inode_operations = {
+ &ext2_file_operations,/* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ ext2_bmap, /* bmap */
+ ext2_truncate, /* truncate */
+ NULL /* permission */
+};
+
+/* static */ int ext2_file_read (struct inode * inode, struct file * filp,
+ char * buf, int count)
+{
+ int read, left, chars;
+ int block, blocks, offset;
+ int bhrequest, uptodate;
+ struct buffer_head ** bhb, ** bhe;
+ struct buffer_head * bhreq[NBUF];
+ struct buffer_head * buflist[NBUF];
+ struct super_block * sb;
+ unsigned int size;
+
+ if (!inode) {
+ printk ("ext2_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ sb = inode->i_sb;
+ if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) {
+ printk ("ext2_file_read: mode = %07o\n", inode->i_mode);
+ return -EINVAL;
+ }
+ offset = filp->f_pos;
+ size = inode->i_size;
+ if (offset > size)
+ left = 0;
+ else
+ left = size - offset;
+ if (left > count)
+ left = count;
+ if (left <= 0)
+ return 0;
+ read = 0;
+ block = offset >> EXT2_BLOCK_SIZE_BITS(sb);
+ offset &= (sb->s_blocksize - 1);
+ size = (size + sb->s_blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(sb);
+ blocks = (left + offset + sb->s_blocksize - 1) >> EXT2_BLOCK_SIZE_BITS(sb);
+ bhb = bhe = buflist;
+ if (filp->f_reada) {
+ blocks += read_ahead[MAJOR(inode->i_dev)] / (BLOCK_SIZE >> 9);
+ if (block + blocks > size)
+ blocks = size - block;
+ }
+
+ /* We do this in a two stage process. We first try and request
+ as many blocks as we can, then we wait for the first one to
+ complete, and then we try and wrap up as many as are actually
+ done. This routine is rather generic, in that it can be used
+ in a filesystem by substituting the appropriate function in
+ for getblk
+
+ This routine is optimized to make maximum use of the various
+ buffers and caches. */
+
+ do {
+ bhrequest = 0;
+ uptodate = 1;
+ while (blocks) {
+ --blocks;
+ *bhb = ext2_getblk (inode, block++, 0);
+ if (*bhb && !(*bhb)->b_uptodate) {
+ uptodate = 0;
+ bhreq[bhrequest++] = *bhb;
+ }
+
+ if (++bhb == &buflist[NBUF])
+ bhb = buflist;
+
+ /* If the block we have on hand is uptodate, go ahead
+ and complete processing */
+ if (uptodate)
+ break;
+
+ if (bhb == bhe)
+ break;
+ }
+
+ /* Now request them all */
+ if (bhrequest)
+ ll_rw_block (READ, bhrequest, bhreq);
+
+ do { /* Finish off all I/O that has actually completed */
+ if (*bhe) {
+ wait_on_buffer (*bhe);
+ if (!(*bhe)->b_uptodate) { /* read error? */
+ left = 0;
+ break;
+ }
+ }
+ if (left < sb->s_blocksize - offset)
+ chars = left;
+ else
+ chars = sb->s_blocksize - offset;
+ filp->f_pos += chars;
+ left -= chars;
+ read += chars;
+ if (*bhe) {
+ memcpy_tofs (buf, offset + (*bhe)->b_data,
+ chars);
+ brelse (*bhe);
+ buf += chars;
+ } else {
+ while (chars-- > 0)
+ put_fs_byte (0, buf++);
+ }
+ offset = 0;
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ } while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
+ } while (left > 0);
+
+ /* Release the read-ahead blocks */
+ while (bhe != bhb) {
+ brelse (*bhe);
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ }
+ if (!read)
+ return -EIO;
+ filp->f_reada = 1;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+ return read;
+}
+
+static int ext2_file_write (struct inode * inode, struct file * filp,
+ char * buf, int count)
+{
+ off_t pos;
+ int written, c;
+ struct buffer_head * bh;
+ char * p;
+ struct super_block * sb;
+
+ if (!inode) {
+ printk("ext2_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ sb = inode->i_sb;
+ if (!S_ISREG(inode->i_mode)) {
+ printk ("ext2_file_write: mode = %07o\n", inode->i_mode);
+ return -EINVAL;
+ }
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+ if (filp->f_flags & O_APPEND)
+ pos = inode->i_size;
+ else
+ pos = filp->f_pos;
+ written = 0;
+ while (written < count) {
+ bh = ext2_getblk (inode, pos / sb->s_blocksize, 1);
+ if (!bh) {
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_file_write: ext2_getblk returned NULL\n");
+#endif
+ if (!written)
+ written = -ENOSPC;
+ break;
+ }
+ c = sb->s_blocksize - (pos % sb->s_blocksize);
+ if (c > count-written)
+ c = count - written;
+ if (c != sb->s_blocksize && !bh->b_uptodate) {
+ ll_rw_block (READ, 1, &bh);
+ wait_on_buffer (bh);
+ if (!bh->b_uptodate) {
+ brelse (bh);
+ if (!written)
+ written = -EIO;
+ break;
+ }
+ }
+ p = (pos % sb->s_blocksize) + bh->b_data;
+ pos += c;
+ if (pos > inode->i_size) {
+ inode->i_size = pos;
+ inode->i_dirt = 1;
+ }
+ written += c;
+ memcpy_fromfs (p, buf, c);
+ buf += c;
+ bh->b_uptodate = 1;
+ bh->b_dirt = 1;
+ brelse (bh);
+ }
+ inode->i_mtime = CURRENT_TIME;
+ inode->i_ctime = CURRENT_TIME;
+ filp->f_pos = pos;
+ inode->i_dirt = 1;
+ return written;
+}
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
new file mode 100644
index 0000000..017e5ee
--- /dev/null
+++ b/fs/ext2/ialloc.c
@@ -0,0 +1,465 @@
+/*
+ * linux/fs/ext2/ialloc.c
+ *
+ * Copyright (C) 1992, 1993 Remy Card (card@masi.ibp.fr)
+ *
+ */
+
+/* ialloc.c contains the inodes allocation and deallocation routines */
+
+/*
+
+ The free inodes are managed by bitmaps. A file system contains several
+ blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap
+ block for inodes, N blocks for the inode table and data blocks.
+
+ The file system contains group descriptors which are located after the
+ super block. Each descriptor contains the number of the bitmap block and
+ the free blocks count in the block. The descriptors are loaded in memory
+ when a file system is mounted (see ext2_read_super).
+
+*/
+
+#include <linux/sched.h>
+#include <linux/ext2_fs.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+#define set_bit(nr,addr) ( \
+{ \
+ char res; \
+ __asm__ __volatile__("btsl %1,%2\n\tsetb %0" \
+ :"=q" (res) \
+ :"r" (nr),"m" (*(addr))); \
+ res; \
+} \
+)
+
+#define clear_bit(nr,addr) ( \
+{ \
+ char res; \
+ __asm__ __volatile__("btrl %1,%2\n\tsetnb %0" \
+ :"=q" (res) \
+ :"r" (nr),"m" (*(addr))); \
+ res; \
+} \
+)
+
+
+#define find_first_zero(addr,size) ( \
+{ \
+ int __res; \
+ __asm__("cld\n" \
+ "1:\tlodsl\n\t" \
+ "notl %%eax\n\t" \
+ "bsfl %%eax,%%edx\n\t" \
+ "jne 2f\n\t" \
+ "addl $32,%%ecx\n\t" \
+ "cmpl %%ebx,%%ecx\n\t" \
+ "jl 1b\n\t" \
+ "xorl %%edx,%%edx\n" \
+ "2:\taddl %%edx,%%ecx" \
+ :"=c" (__res):"0" (0), "S" (addr), "b" (size) \
+ :"ax", "bx", "dx", "si"); \
+ __res; \
+} \
+)
+
+static void read_inode_bitmap (struct super_block * sb,
+ unsigned long block_group,
+ unsigned int bitmap_nr)
+{
+ unsigned long group_desc;
+ unsigned long desc;
+ struct ext2_group_desc * gdp;
+ struct buffer_head * bh;
+
+ group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
+ desc = block_group % EXT2_DESC_PER_BLOCK(sb);
+ if (!sb->u.ext2_sb.s_group_desc[group_desc]) {
+ printk ("block_group = %d,group_desc = %d,desc = %d\n",
+ block_group, group_desc, desc);
+ panic ("read_inode_bitmap: Group descriptor not loaded");
+ }
+ gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
+ bh = bread (sb->s_dev, gdp[desc].bg_inode_bitmap, sb->s_blocksize);
+ if (!bh) {
+ printk ("block_group = %d,group_desc = %d,desc = %d,inode_bitmap = %d\n",
+ block_group, group_desc, desc, gdp[desc].bg_inode_bitmap);
+ panic ("read_inode_bitmap: Cannot read inode bitmap");
+ }
+ sb->u.ext2_sb.s_inode_bitmap_number[bitmap_nr] = block_group;
+ sb->u.ext2_sb.s_inode_bitmap[bitmap_nr] = bh;
+}
+
+/*
+ * load_inode_bitmap loads the inode bitmap for a blocks group
+ *
+ * It maintains a cache for the last bitmaps loaded. This cache is managed
+ * with a LRU algorithm.
+ *
+ * Notes:
+ * 1/ There is one cache per mounted file system.
+ * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
+ * this function reads the bitmap without maintaining a LRU cache.
+ */
+static int load_inode_bitmap (struct super_block * sb,
+ unsigned int block_group)
+{
+ int i, j;
+ unsigned long inode_bitmap_number;
+ struct buffer_head * inode_bitmap;
+
+ if (block_group >= sb->u.ext2_sb.s_groups_count) {
+ printk ("block_group = %d, groups_count = %d\n",
+ block_group, sb->u.ext2_sb.s_groups_count);
+ panic ("load_inode_bitmap: block_group >= groups_count");
+ }
+ if (sb->u.ext2_sb.s_loaded_inode_bitmaps > 0 &&
+ sb->u.ext2_sb.s_inode_bitmap_number[0] == block_group)
+ return 0;
+ if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) {
+ if (sb->u.ext2_sb.s_inode_bitmap[block_group]) {
+ if (sb->u.ext2_sb.s_inode_bitmap_number[block_group] != block_group)
+ panic ("load_inode_bitmap: block_group != inode_bitmap_number");
+ else
+ return block_group;
+ } else {
+ read_inode_bitmap (sb, block_group, block_group);
+ return block_group;
+ }
+ }
+
+ for (i = 0; i < sb->u.ext2_sb.s_loaded_inode_bitmaps &&
+ sb->u.ext2_sb.s_inode_bitmap_number[i] != block_group;
+ i++)
+ ;
+ if (i < sb->u.ext2_sb.s_loaded_inode_bitmaps &&
+ sb->u.ext2_sb.s_inode_bitmap_number[i] == block_group) {
+ inode_bitmap_number = sb->u.ext2_sb.s_inode_bitmap_number[i];
+ inode_bitmap = sb->u.ext2_sb.s_inode_bitmap[i];
+ for (j = i; j > 0; j--) {
+ sb->u.ext2_sb.s_inode_bitmap_number[j] =
+ sb->u.ext2_sb.s_inode_bitmap_number[j - 1];
+ sb->u.ext2_sb.s_inode_bitmap[j] =
+ sb->u.ext2_sb.s_inode_bitmap[j - 1];
+ }
+ sb->u.ext2_sb.s_inode_bitmap_number[0] = inode_bitmap_number;
+ sb->u.ext2_sb.s_inode_bitmap[0] = inode_bitmap;
+ } else {
+ if (sb->u.ext2_sb.s_loaded_inode_bitmaps < EXT2_MAX_GROUP_LOADED)
+ sb->u.ext2_sb.s_loaded_inode_bitmaps++;
+ else
+ brelse (sb->u.ext2_sb.s_inode_bitmap[EXT2_MAX_GROUP_LOADED - 1]);
+ for (j = sb->u.ext2_sb.s_loaded_inode_bitmaps - 1; j > 0; j--) {
+ sb->u.ext2_sb.s_inode_bitmap_number[j] =
+ sb->u.ext2_sb.s_inode_bitmap_number[j - 1];
+ sb->u.ext2_sb.s_inode_bitmap[j] =
+ sb->u.ext2_sb.s_inode_bitmap[j - 1];
+ }
+ read_inode_bitmap (sb, block_group, 0);
+ }
+ return 0;
+}
+
+/*
+ * This function sets the deletion time for the inode
+ *
+ * This may be used one day by an 'undelete' program
+ */
+static void set_inode_dtime (struct inode * inode,
+ struct ext2_group_desc *gdp, unsigned long desc)
+{
+ unsigned long inode_block;
+ struct buffer_head * bh;
+ struct ext2_inode * raw_inode;
+
+ inode_block = gdp[desc].bg_inode_table + (((inode->i_ino - 1) %
+ EXT2_INODES_PER_GROUP(inode->i_sb)) /
+ EXT2_INODES_PER_BLOCK(inode->i_sb));
+ bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize);
+ if (!bh) {
+ printk ("inode=%d, inode_block=%d\n", inode->i_ino, inode_block);
+ panic ("set_inode_dtime: Cannot load inode table block");
+ }
+ raw_inode = ((struct ext2_inode *) bh->b_data) +
+ (((inode->i_ino - 1) %
+ EXT2_INODES_PER_GROUP(inode->i_sb)) %
+ EXT2_INODES_PER_BLOCK(inode->i_sb));
+ raw_inode->i_dtime = CURRENT_TIME;
+ bh->b_dirt = 1;
+ brelse (bh);
+}
+
+void ext2_free_inode (struct inode * inode)
+{
+ struct super_block * sb;
+ struct buffer_head * bh;
+ struct buffer_head * bh2;
+ unsigned long block_group;
+ unsigned long bit;
+ unsigned long group_desc;
+ unsigned long desc;
+ int bitmap_nr;
+ struct ext2_group_desc * gdp;
+ struct ext2_super_block * es;
+
+ if (!inode)
+ return;
+ if (!inode->i_dev) {
+ printk ("ext2_free_inode: inode has no device\n");
+ return;
+ }
+ if (inode->i_count > 1) {
+ printk ("ext2_free_inode: inode has count=%d\n",
+ inode->i_count);
+ return;
+ }
+ if (inode->i_nlink) {
+ printk ("ext2_free_inode: inode has nlink=%d\n",
+ inode->i_nlink);
+ return;
+ }
+ if (!inode->i_sb) {
+ printk("ext2_free_inode: inode on nonexistent device\n");
+ return;
+ }
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_free_inode: freeing inode %d\n", inode->i_ino);
+#endif
+ sb = inode->i_sb;
+ lock_super (sb);
+ if (inode->i_ino < 1 || inode->i_ino > sb->u.ext2_sb.s_inodes_count) {
+ printk("free_inode: inode 0 or nonexistent inode\n");
+ unlock_super (sb);
+ return;
+ }
+ es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(sb);
+ bit = (inode->i_ino - 1) % EXT2_INODES_PER_GROUP(sb);
+ bitmap_nr = load_inode_bitmap (sb, block_group);
+ bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
+ if (!bh) {
+ printk ("block_group = %d\n", block_group);
+ panic ("ext2_free_inode: Unable to load bitmap");
+ }
+ if (clear_bit (bit, bh->b_data))
+ printk ("ext2_free_inode (%04x:%d): bit already cleared\n",
+ sb->s_dev, inode->i_ino);
+ else {
+ group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
+ desc = block_group % EXT2_DESC_PER_BLOCK(sb);
+ bh2 = sb->u.ext2_sb.s_group_desc[group_desc];
+ if (!bh2) {
+ printk ("group_desc = %d\n", group_desc);
+ panic ("ext2_free_inode: Group descriptor not loaded");
+ }
+ gdp = (struct ext2_group_desc *) bh2->b_data;
+ gdp[desc].bg_free_inodes_count ++;
+ if (S_ISDIR(inode->i_mode))
+ gdp[desc].bg_used_dirs_count --;
+ bh2->b_dirt = 1;
+ set_inode_dtime (inode, gdp, desc);
+ }
+ bh->b_dirt = 1;
+ es->s_free_inodes_count ++;
+ sb->u.ext2_sb.s_sbh->b_dirt = 1;
+ sb->s_dirt = 1;
+ unlock_super (sb);
+ clear_inode (inode);
+}
+
+/*
+ * This function increments the inode version number
+ *
+ * This may be used one day by the NFS server
+ */
+static void inc_inode_version (struct inode * inode,
+ struct ext2_group_desc *gdp,
+ unsigned long desc)
+{
+ unsigned long inode_block;
+ struct buffer_head * bh;
+ struct ext2_inode * raw_inode;
+
+ inode_block = gdp[desc].bg_inode_table + (((inode->i_ino - 1) %
+ EXT2_INODES_PER_GROUP(inode->i_sb)) /
+ EXT2_INODES_PER_BLOCK(inode->i_sb));
+ bh = bread (inode->i_sb->s_dev, inode_block, inode->i_sb->s_blocksize);
+ if (!bh) {
+ printk ("inode=%d, inode_block=%d\n",
+ inode->i_ino, inode_block);
+ printk ("inc_inode_version: Cannot load inode table block");
+ inode->u.ext2_i.i_version = 1;
+ return;
+ }
+ raw_inode = ((struct ext2_inode *) bh->b_data) +
+ (((inode->i_ino - 1) %
+ EXT2_INODES_PER_GROUP(inode->i_sb)) %
+ EXT2_INODES_PER_BLOCK(inode->i_sb));
+ raw_inode->i_version++;
+ inode->u.ext2_i.i_version = raw_inode->i_version;
+ bh->b_dirt = 1;
+ brelse (bh);
+}
+
+/*
+ * ext2_new_inode does not use a very clever algorithm yet
+ *
+ * Currently, the group descriptors are scanned until a free block is found
+ */
+struct inode * ext2_new_inode (const struct inode * dir, int mode)
+{
+ struct super_block * sb;
+ struct buffer_head * bh;
+ int i, j;
+ struct inode * inode;
+ unsigned long group_desc;
+ unsigned long desc;
+ int bitmap_nr;
+ struct ext2_group_desc * gdp;
+ struct ext2_super_block * es;
+
+ if (!dir || !(inode = get_empty_inode ()))
+ return NULL;
+ sb = dir->i_sb;
+ inode->i_sb = sb;
+ inode->i_flags = sb->s_flags;
+ lock_super (sb);
+ es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+repeat:
+ group_desc = 0;
+ desc = 0;
+ gdp = NULL;
+ for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+ if (!gdp) {
+ if (!sb->u.ext2_sb.s_group_desc[group_desc])
+ panic ("ext2_new_inode: Descriptor not loaded");
+ gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
+ }
+ if (gdp[desc].bg_free_inodes_count > 0)
+ break;
+ desc ++;
+ if (desc == EXT2_DESC_PER_BLOCK(sb)) {
+ group_desc ++;
+ desc = 0;
+ gdp = NULL;
+ }
+ }
+ if (i >= sb->u.ext2_sb.s_groups_count) {
+ unlock_super (sb);
+ return NULL;
+ }
+ bitmap_nr = load_inode_bitmap (sb, i);
+ bh = sb->u.ext2_sb.s_inode_bitmap[bitmap_nr];
+ if (!bh) {
+ printk ("block_group = %d\n", i);
+ panic ("ext2_new_inode: Unable to load group inode bitmap");
+ }
+ if ((j = find_first_zero (bh->b_data, EXT2_INODES_PER_GROUP(sb))) <
+ EXT2_INODES_PER_GROUP(sb)) {
+ if (set_bit (j, bh->b_data)) {
+ printk ("ext2_new_inode: bit already set\n");
+ goto repeat;
+ }
+ bh->b_dirt = 1;
+ } else
+ goto repeat;
+ j += i * EXT2_INODES_PER_GROUP(sb) + 1;
+ if (j > sb->u.ext2_sb.s_inodes_count) {
+ printk ("block_group = %d,inode=%d\n", i, j);
+ printk ("ext2_new_inode: inode > inodes count");
+ return NULL;
+ }
+ gdp[desc].bg_free_inodes_count --;
+ if (S_ISDIR(mode))
+ gdp[desc].bg_used_dirs_count ++;
+ sb->u.ext2_sb.s_group_desc[group_desc]->b_dirt = 1;
+ es->s_free_inodes_count --;
+ sb->u.ext2_sb.s_sbh->b_dirt = 1;
+ sb->s_dirt = 1;
+ inode->i_sb = sb;
+ inode->i_count = 1;
+ inode->i_nlink = 1;
+ inode->i_dev = sb->s_dev;
+ inode->i_uid = current->euid;
+ inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->egid;
+ inode->i_dirt = 1;
+ inode->i_ino = j;
+ inode->i_blksize = sb->s_blocksize;
+ inode->i_blocks = 0;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->u.ext2_i.i_flags = 0;
+ inode->u.ext2_i.i_faddr = 0;
+ inode->u.ext2_i.i_frag = 0;
+ inode->u.ext2_i.i_fsize = 0;
+ inode->u.ext2_i.i_file_acl = 0;
+ inode->u.ext2_i.i_dir_acl = 0;
+ inode->u.ext2_i.i_dtime = 0;
+ inode->u.ext2_i.i_block_group = i;
+ inode->i_op = NULL;
+ inc_inode_version (inode, gdp, desc);
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_new_inode : allocating inode %d\n", inode->i_ino);
+#endif
+ unlock_super (sb);
+ return inode;
+}
+
+unsigned long ext2_count_free_inodes (struct super_block *sb)
+{
+ struct ext2_super_block * es;
+#ifdef EXT2FS_DEBUG
+ unsigned long desc_count, bitmap_count, x;
+ unsigned long group_desc;
+ unsigned long desc;
+ int bitmap_nr;
+ struct ext2_group_desc * gdp;
+ int i;
+
+ lock_super (sb);
+ es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ desc_count = 0;
+ bitmap_count = 0;
+ group_desc = 0;
+ desc = 0;
+ gdp = NULL;
+ for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+ if (!gdp) {
+ if (!sb->u.ext2_sb.s_group_desc[group_desc]) {
+ printk ("ext2_count_free_inodes: Descriptor not loaded\n");
+ break;
+ }
+ gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
+ }
+ desc_count += gdp[desc].bg_free_inodes_count;
+ bitmap_nr = load_inode_bitmap (sb, i);
+ if (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr])
+ x = ext2_count_free (sb->u.ext2_sb.s_inode_bitmap[bitmap_nr],
+ EXT2_INODES_PER_GROUP(sb) / 8);
+ else {
+ x = 0;
+ printk ("Cannot load inode bitmap for group %d (bitmap = %d)\n",
+ i, bitmap_nr);
+ }
+ printk ("group %d: stored = %d, counted = %d\n",
+ i, gdp[desc].bg_free_inodes_count, x);
+ bitmap_count += x;
+ desc ++;
+ if (desc == EXT2_DESC_PER_BLOCK(sb)) {
+ group_desc ++;
+ desc = 0;
+ gdp = NULL;
+ }
+ }
+ printk("ext2_count_free_inodes: stored = %d, computed = %d, %d\n",
+ es->s_free_inodes_count, desc_count, bitmap_count);
+ unlock_super (sb);
+ return desc_count;
+#else
+ es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ return es->s_free_inodes_count;
+#endif
+}
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
new file mode 100644
index 0000000..7086c1d
--- /dev/null
+++ b/fs/ext2/inode.c
@@ -0,0 +1,683 @@
+/*
+ * linux/fs/ext2/inode.c
+ *
+ * Copyright (C) 1992, 1993 Remy Card (card@masi.ibp.fr)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/ext2_fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+void ext2_put_inode (struct inode * inode)
+{
+ if (inode->i_nlink)
+ return;
+ inode->i_size = 0;
+ if (inode->i_blocks)
+ ext2_truncate (inode);
+ ext2_free_inode (inode);
+}
+
+void ext2_put_super (struct super_block * sb)
+{
+ struct ext2_super_block * es;
+ int i;
+
+ lock_super (sb);
+ es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+ es->s_valid = 1;
+ sb->u.ext2_sb.s_sbh->b_dirt = 1;
+#ifndef DONT_USE_DCACHE
+ ext2_dcache_invalidate (sb->s_dev);
+#endif
+ sb->s_dev = 0;
+ for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
+ if (sb->u.ext2_sb.s_group_desc[i])
+ brelse (sb->u.ext2_sb.s_group_desc[i]);
+ for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
+ if (sb->u.ext2_sb.s_inode_bitmap[i])
+ brelse (sb->u.ext2_sb.s_inode_bitmap[i]);
+ for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
+ if (sb->u.ext2_sb.s_block_bitmap[i])
+ brelse (sb->u.ext2_sb.s_block_bitmap[i]);
+ unlock_super (sb);
+ return;
+}
+
+static struct super_operations ext2_sops = {
+ ext2_read_inode,
+ NULL,
+ ext2_write_inode,
+ ext2_put_inode,
+ ext2_put_super,
+ ext2_write_super,
+ ext2_statfs
+};
+
+static int convert_pre_02b_fs (struct super_block * sb,
+ struct buffer_head * bh)
+{
+ struct ext2_super_block * es;
+ struct ext2_old_group_desc old_group_desc [BLOCK_SIZE / sizeof (struct ext2_old_group_desc)];
+ struct ext2_group_desc * gdp;
+ struct buffer_head * bh2;
+ int groups_count;
+ int i;
+
+ es = (struct ext2_super_block *) bh->b_data;
+ bh2 = bread (sb->s_dev, 2, BLOCK_SIZE);
+ if (!bh2) {
+ printk ("Cannot read descriptor blocks while converting !\n");
+ return 0;
+ }
+ memcpy (old_group_desc, bh2->b_data, BLOCK_SIZE);
+ groups_count = (sb->u.ext2_sb.s_blocks_count -
+ sb->u.ext2_sb.s_first_data_block +
+ (EXT2_BLOCK_SIZE(sb) * 8) - 1) /
+ (EXT2_BLOCK_SIZE(sb) * 8);
+ memset (bh2->b_data, 0, BLOCK_SIZE);
+ gdp = (struct ext2_group_desc *) bh2->b_data;
+ for (i = 0; i < groups_count; i++) {
+ gdp[i].bg_block_bitmap = old_group_desc[i].bg_block_bitmap;
+ gdp[i].bg_inode_bitmap = old_group_desc[i].bg_inode_bitmap;
+ gdp[i].bg_inode_table = old_group_desc[i].bg_inode_table;
+ gdp[i].bg_free_blocks_count = old_group_desc[i].bg_free_blocks_count;
+ gdp[i].bg_free_inodes_count = old_group_desc[i].bg_free_inodes_count;
+ }
+ bh2->b_dirt = 1;
+ brelse (bh2);
+ es->s_magic = EXT2_SUPER_MAGIC;
+ bh->b_dirt = 1;
+ sb->s_magic = EXT2_SUPER_MAGIC;
+ return 1;
+}
+
+struct super_block * ext2_read_super (struct super_block * s, void * data)
+{
+ struct buffer_head * bh;
+ struct ext2_super_block * es;
+ int dev = s->s_dev;
+ int bh_count;
+ int i, j;
+ int fs_converted = 0;
+
+ lock_super (s);
+ if (!(bh = bread (dev, 1, BLOCK_SIZE))) {
+ s->s_dev = 0;
+ unlock_super (s);
+ printk ("EXT2-fs: unable to read superblock\n");
+ return NULL;
+ }
+ es = (struct ext2_super_block *) bh->b_data;
+ s->s_blocksize = EXT2_MIN_BLOCK_SIZE << es->s_log_block_size;
+ s->u.ext2_sb.s_inodes_count = es->s_inodes_count;
+ s->u.ext2_sb.s_blocks_count = es->s_blocks_count;
+ s->u.ext2_sb.s_r_blocks_count = es->s_r_blocks_count;
+ s->u.ext2_sb.s_first_data_block = es->s_first_data_block;
+ s->u.ext2_sb.s_log_block_size = es->s_log_block_size;
+ s->u.ext2_sb.s_log_frag_size = es->s_log_frag_size;
+ s->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
+ es->s_log_frag_size;
+ s->u.ext2_sb.s_frags_per_block = s->s_blocksize /
+ s->u.ext2_sb.s_frag_size;
+ s->u.ext2_sb.s_blocks_per_group = es->s_blocks_per_group;
+ s->u.ext2_sb.s_frags_per_group = es->s_frags_per_group;
+ s->u.ext2_sb.s_inodes_per_group = es->s_inodes_per_group;
+ s->u.ext2_sb.s_inodes_per_block = s->s_blocksize /
+ sizeof (struct ext2_inode);
+ s->u.ext2_sb.s_desc_per_block = s->s_blocksize /
+ sizeof (struct ext2_group_desc);
+ s->s_magic = es->s_magic;
+ s->u.ext2_sb.s_sbh = bh;
+ s->u.ext2_sb.s_rename_lock = 0;
+ s->u.ext2_sb.s_rename_wait = NULL;
+ if (s->s_magic == EXT2_OLD_SUPER_MAGIC) {
+ if (es->s_blocks_count > 262144) {
+ /* fs > 256 MB can't be converted */
+ s->s_dev = 0;
+ unlock_super (s);
+ brelse (bh);
+ printk ("EXT2-fs: trying to mount a pre-0.2b file"
+ "system which cannot be converted\n");
+ return NULL;
+ }
+ printk ("EXT2-fs: mounting a pre 0.2b file system, "
+ "will try to convert the structure\n");
+ if (s->s_flags & MS_RDONLY == 0) {
+ s->s_dev = 0;
+ unlock_super (s);
+ brelse (bh);
+ printk ("EXT2-fs: cannot convert a read-only fs\n");
+ return NULL;
+ }
+ if (!convert_pre_02b_fs (s, bh)) {
+ s->s_dev = 0;
+ unlock_super (s);
+ brelse (bh);
+ printk ("EXT2-fs: conversion failed !!!\n");
+ return NULL;
+ }
+ printk ("EXT2-fs: conversion succeeded !!!\n");
+ fs_converted = 1;
+ }
+ if (s->s_magic != EXT2_SUPER_MAGIC) {
+ s->s_dev = 0;
+ unlock_super (s);
+ brelse (bh);
+ printk ("EXT2-fs: magic match failed\n");
+ return NULL;
+ }
+ if (s->s_blocksize != s->u.ext2_sb.s_frag_size) {
+ s->s_dev = 0;
+ unlock_super (s);
+ brelse (bh);
+ printk ("EXT2-fs: fragsize != blocksize (not supported yet)\n");
+ return NULL;
+ }
+ if (!es->s_valid)
+ printk ("EXT2-fs warning: mounting non valid file system\n");
+ s->u.ext2_sb.s_groups_count = (s->u.ext2_sb.s_blocks_count -
+ s->u.ext2_sb.s_first_data_block +
+ (EXT2_BLOCK_SIZE(s) * 8) - 1) /
+ (EXT2_BLOCK_SIZE(s) * 8);
+ for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
+ s->u.ext2_sb.s_group_desc[i] = NULL;
+ bh_count = (s->u.ext2_sb.s_groups_count +
+ EXT2_DESC_PER_BLOCK(s) - 1) /
+ EXT2_DESC_PER_BLOCK(s);
+ for (i = 0; i < bh_count; i++) {
+ s->u.ext2_sb.s_group_desc[i] = bread (dev, i + 2, s->s_blocksize);
+ if (!s->u.ext2_sb.s_group_desc[i]) {
+ s->s_dev = 0;
+ unlock_super (s);
+ for (j = 0; j < i; j++)
+ brelse (s->u.ext2_sb.s_group_desc[i]);
+ brelse (bh);
+ printk ("ext2_read_super: unable to read group descriptors\n");
+ return NULL;
+ }
+ }
+ for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
+ s->u.ext2_sb.s_inode_bitmap_number[i] = 0;
+ s->u.ext2_sb.s_inode_bitmap[i] = NULL;
+ s->u.ext2_sb.s_block_bitmap_number[i] = 0;
+ s->u.ext2_sb.s_block_bitmap[i] = NULL;
+ }
+ s->u.ext2_sb.s_loaded_inode_bitmaps = 0;
+ s->u.ext2_sb.s_loaded_block_bitmaps = 0;
+ unlock_super (s);
+ /* set up enough so that it can read an inode */
+ s->s_dev = dev;
+ s->s_op = &ext2_sops;
+ if (!(s->s_mounted = iget(s, EXT2_ROOT_INO))) {
+ s->s_dev = 0;
+ for (i = 0; i < EXT2_MAX_GROUP_DESC; i++)
+ if (s->u.ext2_sb.s_group_desc[i])
+ brelse (s->u.ext2_sb.s_group_desc[i]);
+ brelse (bh);
+ printk ("EXT2-fs: get root inode failed\n");
+ return NULL;
+ }
+ if ((s->s_flags & MS_RDONLY) == 0) {
+ es->s_valid = 0;
+ es->s_mtime = CURRENT_TIME;
+ bh->b_dirt = 1;
+ s->s_dirt = 1;
+ }
+ if (fs_converted) {
+ for (i = 0; i < bh_count; i++)
+ s->u.ext2_sb.s_group_desc[i]->b_dirt = 1;
+ s->s_dirt = 1;
+ }
+ printk ("[EXT II FS %s, bs=%d, fs=%d, gc=%d, bpg=%d, ipg=%d]\n",
+ EXT2FS_VERSION, s->s_blocksize, s->u.ext2_sb.s_frag_size,
+ s->u.ext2_sb.s_groups_count,
+ EXT2_BLOCKS_PER_GROUP(s), EXT2_INODES_PER_GROUP(s));
+ return s;
+}
+
+/*
+ * In the second extended file system, it is not necessary to
+ * write the super block since we use a mapping of the
+ * disk super block in a buffer.
+ *
+ * However, this function is still used to set the fs valid
+ * flags to 0. We need to set this flag to 0 since the fs
+ * may have been checked while mounted and e2fsck may have
+ * set s_valid to 1 after some corrections.
+ *
+ * Note that this function also writes backups of the super block and
+ * of the group descriptors in each group.
+ */
+void ext2_write_super (struct super_block * sb)
+{
+ struct ext2_super_block * es;
+ struct buffer_head * bh;
+ unsigned long block;
+ unsigned long bh_count;
+ int i, j;
+
+ if ((sb->s_flags & MS_RDONLY) == 0) {
+ es = (struct ext2_super_block *) sb->u.ext2_sb.s_sbh->b_data;
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_write_super: setting valid to 0\n");
+#endif
+ es->s_valid = 0;
+ es->s_wtime = CURRENT_TIME;
+ sb->u.ext2_sb.s_sbh->b_dirt = 1;
+ bh_count = (sb->u.ext2_sb.s_groups_count +
+ EXT2_DESC_PER_BLOCK(sb) - 1) /
+ EXT2_DESC_PER_BLOCK(sb);
+ for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+ block = sb->u.ext2_sb.s_first_data_block +
+ i * sb->u.ext2_sb.s_blocks_per_group;
+ if (!(bh = bread (sb->s_dev, block, BLOCK_SIZE)))
+ printk ("ext2_write_super: Unable to read backup super block for group %d\n", i);
+ else {
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_write_super: writing super block backup in group %d at block %d\n", i, block);
+#endif
+ memcpy (bh->b_data, es, BLOCK_SIZE);
+ bh ->b_dirt = 1;
+ brelse (bh);
+ }
+ for (j = 0; j < bh_count; j++) {
+ block ++;
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_write_super: writing descriptors (block %d) backup in group %d at block %d\n", j, i, block);
+#endif
+ if (!(bh = bread (sb->s_dev, block, sb->s_blocksize)))
+ printk ("ext2_write_super: Unable to read backup descriptor for group %d\n", i);
+ else {
+ memcpy (bh->b_data, sb->u.ext2_sb.s_group_desc[j]->b_data, sb->s_blocksize);
+ bh ->b_dirt = 1;
+ brelse (bh);
+ }
+ }
+ }
+ }
+ sb->s_dirt = 0;
+}
+
+void ext2_statfs (struct super_block * sb, struct statfs * buf)
+{
+ long tmp;
+
+ put_fs_long (EXT2_SUPER_MAGIC, &buf->f_type);
+ put_fs_long (sb->s_blocksize, &buf->f_bsize);
+ put_fs_long (sb->u.ext2_sb.s_blocks_count << sb->u.ext2_sb.s_log_block_size,
+ &buf->f_blocks);
+ tmp = ext2_count_free_blocks (sb);
+ put_fs_long (tmp, &buf->f_bfree);
+ put_fs_long (tmp - sb->u.ext2_sb.s_r_blocks_count, &buf->f_bavail);
+ put_fs_long (sb->u.ext2_sb.s_inodes_count, &buf->f_files);
+ put_fs_long (ext2_count_free_inodes(sb), &buf->f_ffree);
+ put_fs_long (EXT2_NAME_LEN, &buf->f_namelen);
+ /* Don't know what value to put in buf->f_fsid */
+}
+
+#define inode_bmap(inode, nr) ((inode)->u.ext2_i.i_data[(nr)])
+
+static int block_bmap (struct buffer_head * bh, int nr)
+{
+ int tmp;
+
+ if (!bh)
+ return 0;
+ tmp = ((unsigned long *) bh->b_data)[nr];
+ brelse (bh);
+ return tmp;
+}
+
+int ext2_bmap (struct inode * inode, int block)
+{
+ int i;
+ int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+
+ if (block < 0) {
+ printk("ext2_bmap: block < 0");
+ return 0;
+ }
+ if (block >= EXT2_NDIR_BLOCKS + addr_per_block +
+ addr_per_block * addr_per_block +
+ addr_per_block * addr_per_block * addr_per_block) {
+ printk ("ext2_bmap: block > big");
+ return 0;
+ }
+ if (block < EXT2_NDIR_BLOCKS)
+ return inode_bmap (inode, block);
+ block -= EXT2_NDIR_BLOCKS;
+ if (block < addr_per_block) {
+ i = inode_bmap (inode, EXT2_IND_BLOCK);
+ if (!i)
+ return 0;
+ return block_bmap (bread (inode->i_dev, i,
+ inode->i_sb->s_blocksize), block);
+ }
+ block -= addr_per_block;
+ if (block < addr_per_block * addr_per_block) {
+ i = inode_bmap (inode, EXT2_DIND_BLOCK);
+ if (!i)
+ return 0;
+ i = block_bmap (bread (inode->i_dev, i,
+ inode->i_sb->s_blocksize),
+ block / addr_per_block);
+ if (!i)
+ return 0;
+ return block_bmap (bread (inode->i_dev, i,
+ inode->i_sb->s_blocksize),
+ block & (addr_per_block - 1));
+ }
+ block -= addr_per_block * addr_per_block;
+ i = inode_bmap (inode, EXT2_TIND_BLOCK);
+ if (!i)
+ return 0;
+ i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
+ block / (addr_per_block * addr_per_block));
+ if (!i)
+ return 0;
+ i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
+ (block / addr_per_block) & (addr_per_block - 1));
+ if (!i)
+ return 0;
+ return block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize),
+ block & (addr_per_block - 1));
+}
+
+static struct buffer_head * inode_getblk (struct inode * inode, int nr,
+ int create)
+{
+ int tmp;
+ unsigned long * p;
+ struct buffer_head * result;
+ int blocks = inode->i_sb->s_blocksize / 512;
+
+ p = inode->u.ext2_i.i_data + nr;
+repeat:
+ tmp = *p;
+ if (tmp) {
+ result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+ if (tmp == *p)
+ return result;
+ brelse (result);
+ goto repeat;
+ }
+ if (!create)
+ return NULL;
+ tmp = ext2_new_block (inode->i_sb, inode->u.ext2_i.i_block_group);
+ if (!tmp)
+ return NULL;
+ result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+ if (*p) {
+ ext2_free_block (inode->i_sb, tmp);
+ brelse (result);
+ goto repeat;
+ }
+ *p = tmp;
+ inode->i_ctime = CURRENT_TIME;
+ inode->i_blocks += blocks;
+ inode->i_dirt = 1;
+ return result;
+}
+
+static struct buffer_head * block_getblk (struct inode * inode,
+ struct buffer_head * bh, int nr,
+ int create, int blocksize)
+{
+ int tmp;
+ unsigned long * p;
+ struct buffer_head * result;
+ int blocks = inode->i_sb->s_blocksize / 512;
+
+ if (!bh)
+ return NULL;
+ if (!bh->b_uptodate) {
+ ll_rw_block (READ, 1, &bh);
+ wait_on_buffer (bh);
+ if (!bh->b_uptodate) {
+ brelse (bh);
+ return NULL;
+ }
+ }
+ p = nr + (unsigned long *) bh->b_data;
+repeat:
+ tmp = *p;
+ if (tmp) {
+ result = getblk (bh->b_dev, tmp, blocksize);
+ if (tmp == *p) {
+ brelse (bh);
+ return result;
+ }
+ brelse (result);
+ goto repeat;
+ }
+ if (!create) {
+ brelse (bh);
+ return NULL;
+ }
+ tmp = ext2_new_block (inode->i_sb, inode->u.ext2_i.i_block_group);
+ if (!tmp) {
+#ifdef EXT2FS_DEBUG
+ printk ("inode_getblk: ext2_new_block returned 0\n");
+#endif
+ brelse (bh);
+ return NULL;
+ }
+ result = getblk (bh->b_dev, tmp, blocksize);
+ if (*p) {
+ ext2_free_block (inode->i_sb, tmp);
+ brelse (result);
+ goto repeat;
+ }
+ *p = tmp;
+ bh->b_dirt = 1;
+ inode->i_blocks += blocks;
+ inode->i_dirt = 1;
+ brelse (bh);
+ return result;
+}
+
+struct buffer_head * ext2_getblk (struct inode * inode, int block,
+ int create)
+{
+ struct buffer_head * bh;
+ unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+
+ if (block < 0) {
+ printk ("ext2_getblk: block < 0\n");
+ return NULL;
+ }
+ if (block > EXT2_NDIR_BLOCKS + addr_per_block +
+ addr_per_block * addr_per_block +
+ addr_per_block * addr_per_block * addr_per_block) {
+ printk ("ext2_getblk: block > big\n");
+ return NULL;
+ }
+ if (block < EXT2_NDIR_BLOCKS)
+ return inode_getblk (inode, block, create);
+ block -= EXT2_NDIR_BLOCKS;
+ if (block < addr_per_block) {
+ bh = inode_getblk (inode, EXT2_IND_BLOCK, create);
+ return block_getblk (inode, bh, block, create,
+ inode->i_sb->s_blocksize);
+ }
+ block -= addr_per_block;
+ if (block < addr_per_block * addr_per_block) {
+ bh = inode_getblk (inode, EXT2_DIND_BLOCK, create);
+ bh = block_getblk (inode, bh, block / addr_per_block, create,
+ inode->i_sb->s_blocksize);
+ return block_getblk (inode, bh, block & (addr_per_block - 1),
+ create, inode->i_sb->s_blocksize);
+ }
+ block -= addr_per_block * addr_per_block;
+ bh = inode_getblk (inode, EXT2_IND_BLOCK, create);
+ bh = block_getblk (inode, bh, block / (addr_per_block * addr_per_block),
+ create, inode->i_sb->s_blocksize);
+ bh = block_getblk (inode, bh, (block / addr_per_block) & (addr_per_block - 1),
+ create, inode->i_sb->s_blocksize);
+ return block_getblk (inode, bh, block & (addr_per_block - 1), create,
+ inode->i_sb->s_blocksize);
+}
+
+struct buffer_head * ext2_bread (struct inode * inode, int block, int create)
+{
+ struct buffer_head * bh;
+
+ bh = ext2_getblk (inode, block, create);
+ if (!bh || bh->b_uptodate)
+ return bh;
+ ll_rw_block (READ, 1, &bh);
+ wait_on_buffer (bh);
+ if (bh->b_uptodate)
+ return bh;
+ brelse (bh);
+ return NULL;
+}
+
+void ext2_read_inode (struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct ext2_inode * raw_inode;
+ unsigned long block_group;
+ unsigned long group_desc;
+ unsigned long desc;
+ unsigned long block;
+ struct ext2_group_desc * gdp;
+
+ if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino < EXT2_FIRST_INO) ||
+ inode->i_ino > inode->i_sb->u.ext2_sb.s_inodes_count) {
+ printk ("ext2_read_inode: bad inode number of dev %0x04: %d\n",
+ inode->i_dev, inode->i_ino);
+ return;
+ }
+ block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
+ if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count)
+ panic ("ext2_read_inode: group >= groups count");
+ group_desc = block_group / EXT2_DESC_PER_BLOCK(inode->i_sb);
+ desc = block_group % EXT2_DESC_PER_BLOCK(inode->i_sb);
+ bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
+ if (!bh)
+ panic ("ext2_read_inode: Descriptor not loaded");
+ gdp = (struct ext2_group_desc *) bh->b_data;
+ block = gdp[desc].bg_inode_table +
+ (((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb))
+ / EXT2_INODES_PER_BLOCK(inode->i_sb));
+ if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
+ panic ("ext2_read_inode: unable to read i-node block");
+ raw_inode = ((struct ext2_inode *) bh->b_data) +
+ (inode->i_ino - 1) % EXT2_INODES_PER_BLOCK(inode->i_sb);
+ inode->i_mode = raw_inode->i_mode;
+ inode->i_uid = raw_inode->i_uid;
+ inode->i_gid = raw_inode->i_gid;
+ inode->i_nlink = raw_inode->i_links_count;
+ inode->i_size = raw_inode->i_size;
+ inode->i_atime = raw_inode->i_atime;
+ inode->i_ctime = raw_inode->i_ctime;
+ inode->i_mtime = raw_inode->i_mtime;
+ inode->u.ext2_i.i_dtime = raw_inode->i_dtime;
+ inode->i_blksize = inode->i_sb->s_blocksize;
+ inode->i_blocks = raw_inode->i_blocks;
+ inode->u.ext2_i.i_flags = raw_inode->i_flags;
+ inode->u.ext2_i.i_faddr = raw_inode->i_faddr;
+ inode->u.ext2_i.i_frag = raw_inode->i_frag;
+ inode->u.ext2_i.i_fsize = raw_inode->i_fsize;
+ inode->u.ext2_i.i_file_acl = raw_inode->i_file_acl;
+ inode->u.ext2_i.i_dir_acl = raw_inode->i_dir_acl;
+ inode->u.ext2_i.i_version = raw_inode->i_version;
+ inode->u.ext2_i.i_block_group = block_group;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ inode->i_rdev = raw_inode->i_block[0];
+ else for (block = 0; block < EXT2_N_BLOCKS; block++)
+ inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
+ brelse (bh);
+ inode->i_op = NULL;
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &ext2_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &ext2_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &ext2_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode)) {
+ inode->i_op = &fifo_inode_operations;
+ inode->i_pipe = 1;
+ PIPE_BASE(*inode) = NULL;
+ PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;
+ PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL;
+ PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0;
+ }
+}
+
+void ext2_write_inode (struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct ext2_inode * raw_inode;
+ unsigned long block_group;
+ unsigned long group_desc;
+ unsigned long desc;
+ unsigned long block;
+ struct ext2_group_desc * gdp;
+
+ if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino < EXT2_FIRST_INO) ||
+ inode->i_ino > inode->i_sb->u.ext2_sb.s_inodes_count) {
+ printk ("ext2_write_inode: bad inode number of dev %0x04: %d\n",
+ inode->i_dev, inode->i_ino);
+ return;
+ }
+ block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
+ if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count)
+ panic ("ext2_write_inode: group >= groups count");
+ group_desc = block_group / EXT2_DESC_PER_BLOCK(inode->i_sb);
+ desc = block_group % EXT2_DESC_PER_BLOCK(inode->i_sb);
+ bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
+ if (!bh)
+ panic ("ext2_write_inode: Descriptor not loaded");
+ gdp = (struct ext2_group_desc *) bh->b_data;
+ block = gdp[desc].bg_inode_table +
+ (((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb))
+ / EXT2_INODES_PER_BLOCK(inode->i_sb));
+ if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize)))
+ panic ("ext2_write_inode: unable to read i-node block");
+ raw_inode = ((struct ext2_inode *)bh->b_data) +
+ (inode->i_ino - 1) % EXT2_INODES_PER_BLOCK(inode->i_sb);
+ raw_inode->i_mode = inode->i_mode;
+ raw_inode->i_uid = inode->i_uid;
+ raw_inode->i_gid = inode->i_gid;
+ raw_inode->i_links_count = inode->i_nlink;
+ raw_inode->i_size = inode->i_size;
+ raw_inode->i_atime = inode->i_atime;
+ raw_inode->i_ctime = inode->i_ctime;
+ raw_inode->i_mtime = inode->i_mtime;
+ raw_inode->i_dtime = inode->u.ext2_i.i_dtime;
+ raw_inode->i_blocks = inode->i_blocks;
+ raw_inode->i_flags = inode->u.ext2_i.i_flags;
+ raw_inode->i_faddr = inode->u.ext2_i.i_faddr;
+ raw_inode->i_frag = inode->u.ext2_i.i_frag;
+ raw_inode->i_fsize = inode->u.ext2_i.i_fsize;
+ raw_inode->i_file_acl = inode->u.ext2_i.i_file_acl;
+ raw_inode->i_dir_acl = inode->u.ext2_i.i_dir_acl;
+ raw_inode->i_version = inode->u.ext2_i.i_version;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ raw_inode->i_block[0] = inode->i_rdev;
+ else for (block = 0; block < EXT2_N_BLOCKS; block++)
+ raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
+ bh->b_dirt = 1;
+ inode->i_dirt = 0;
+ brelse (bh);
+}
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
new file mode 100644
index 0000000..2088888
--- /dev/null
+++ b/fs/ext2/namei.c
@@ -0,0 +1,918 @@
+/*
+ * linux/fs/ext2/namei.c
+ *
+ * Copyright (C) 1992, 1993 Remy Card (card@masi.ibp.fr)
+ *
+ * from
+ *
+ * linux/fs/minix/namei.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/ext2_fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+
+#include <asm/segment.h>
+
+/*
+ * comment out this line if you want names > EXT2_NAME_LEN chars to be
+ * truncated. Else they will be disallowed.
+ */
+/* #define NO_TRUNCATE */
+
+/*
+ * ok, we cannot use strncmp, as the name is not in our data space.
+ * Thus we'll have to use ext2_match. No big problem. ext2_match also makes
+ * some sanity tests.
+ *
+ * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.
+ */
+static int ext2_match (int len, const char * name,
+ struct ext2_dir_entry * de)
+{
+ register int same __asm__("ax");
+
+ if (!de || !de->inode || len > EXT2_NAME_LEN)
+ return 0;
+ /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
+ if (!len && (de->name[0] == '.') && (de->name[1] == '\0'))
+ return 1;
+ if (len < EXT2_NAME_LEN && len != de->name_len)
+ return 0;
+ __asm__("cld\n\t"
+ "repe ; cmpsb\n\t"
+ "setz %%al"
+ :"=a" (same)
+ :"0" (0), "S" ((long) name), "D" ((long) de->name), "c" (len)
+ :"cx", "di", "si");
+ return same;
+}
+
+/*
+ * ext2_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+ * itself (as a parameter - res_dir). It does NOT read the inode of the
+ * entry - you'll have to do that yourself if you want to.
+ *
+ * In the ext2 file system, this function also returns a pointer on the
+ * previous directory entry because functions which remove directory
+ * entries need it
+ */
+static struct buffer_head * ext2_find_entry (struct inode * dir,
+ const char * name, int namelen,
+ struct ext2_dir_entry ** res_dir,
+ struct ext2_dir_entry ** prev_dir)
+{
+ long offset;
+ struct buffer_head * bh;
+ struct ext2_dir_entry * de;
+ struct super_block * sb;
+
+ *res_dir = NULL;
+ if (!dir)
+ return NULL;
+ sb = dir->i_sb;
+#ifdef NO_TRUNCATE
+ if (namelen > EXT2_NAME_LEN)
+ return NULL;
+#else
+ if (namelen > EXT2_NAME_LEN)
+ namelen = EXT2_NAME_LEN;
+#endif
+ bh = ext2_bread (dir, 0, 0);
+ if (!bh)
+ return NULL;
+ if (prev_dir)
+ *prev_dir = NULL;
+ offset = 0;
+ de = (struct ext2_dir_entry *) bh->b_data;
+ while (offset < dir->i_size) {
+ if ((char *)de >= sb->s_blocksize + bh->b_data) {
+ brelse (bh);
+ bh = NULL;
+ bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 0);
+ if (!bh)
+ continue;
+ de = (struct ext2_dir_entry *) bh->b_data;
+ if (prev_dir)
+ *prev_dir = NULL;
+ }
+ if (de->rec_len < EXT2_DIR_REC_LEN(1) ||
+ de->rec_len % 4 != 0 ||
+ de->rec_len < EXT2_DIR_REC_LEN(de->name_len)) {
+ printk ("ext2_find_entry: bad directory entry (dev %04x, dir %d)\n",
+ dir->i_dev, dir->i_ino);
+ printk ("offset=%d, inode=%d, rec_len=%d, name_len=%d\n",
+ offset, de->inode, de->rec_len, de->name_len);
+ brelse (bh);
+ return NULL;
+ }
+ if (ext2_match (namelen, name, de)) {
+ *res_dir = de;
+ return bh;
+ }
+ offset += de->rec_len;
+ if (prev_dir)
+ *prev_dir = de;
+ de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
+ }
+ brelse (bh);
+ return NULL;
+}
+
+int ext2_lookup (struct inode * dir, const char * name, int len,
+ struct inode ** result)
+{
+ int ino;
+ struct ext2_dir_entry * de;
+ struct buffer_head * bh;
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+ if (!S_ISDIR(dir->i_mode)) {
+ iput (dir);
+ return -ENOENT;
+ }
+#ifndef DONT_USE_DCACHE
+ if (!(ino = ext2_dcache_lookup (dir->i_dev, dir->i_ino, name, len))) {
+#endif
+ if (!(bh = ext2_find_entry (dir, name, len, &de, NULL))) {
+ iput (dir);
+ return -ENOENT;
+ }
+ ino = de->inode;
+#ifndef DONT_USE_DCACHE
+ ext2_dcache_add (dir->i_dev, dir->i_ino, de->name,
+ de->name_len, ino);
+#endif
+ brelse (bh);
+#ifndef DONT_USE_DCACHE
+ }
+#endif
+ if (!(*result = iget (dir->i_sb, ino))) {
+ iput (dir);
+ return -EACCES;
+ }
+ iput (dir);
+ return 0;
+}
+
+/*
+ * ext2_add_entry()
+ *
+ * adds a file entry to the specified directory, using the same
+ * semantics as ext2_find_entry(). It returns NULL if it failed.
+ *
+ * NOTE!! The inode part of 'de' is left at 0 - which means you
+ * may not sleep between calling this and putting something into
+ * the entry, as someone else might have used it while you slept.
+ */
+static struct buffer_head * ext2_add_entry (struct inode * dir,
+ const char * name, int namelen,
+ struct ext2_dir_entry ** res_dir)
+{
+ int i;
+ long offset;
+ unsigned short rec_len;
+ struct buffer_head * bh;
+ struct ext2_dir_entry * de, * de1;
+ struct super_block * sb;
+
+ *res_dir = NULL;
+ if (!dir)
+ return NULL;
+ sb = dir->i_sb;
+#ifdef NO_TRUNCATE
+ if (namelen > EXT2_NAME_LEN)
+ return NULL;
+#else
+ if (namelen > EXT2_NAME_LEN)
+ namelen = EXT2_NAME_LEN;
+#endif
+ if (!namelen)
+ return NULL;
+ bh = ext2_bread (dir, 0, 0);
+ if (!bh)
+ return NULL;
+ rec_len = EXT2_DIR_REC_LEN(namelen);
+ offset = 0;
+ de = (struct ext2_dir_entry *) bh->b_data;
+ while (1) {
+ if ((char *)de >= sb->s_blocksize + bh->b_data) {
+ brelse (bh);
+ bh = NULL;
+ bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 0);
+ if (!bh) {
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_add_entry: creating next block\n");
+#endif
+ bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1);
+ if (!bh)
+ return NULL;
+ de = (struct ext2_dir_entry *) bh->b_data;
+ de->inode = 0;
+ de->rec_len = sb->s_blocksize;
+ dir->i_size += sb->s_blocksize;
+ dir->i_dirt = 1;
+ } else {
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_add_entry: skipping to next block\n");
+#endif
+ de = (struct ext2_dir_entry *) bh->b_data;
+ }
+ }
+ if (de->rec_len < EXT2_DIR_REC_LEN(1) ||
+ de->rec_len % 4 != 0 ||
+ de->rec_len < EXT2_DIR_REC_LEN(de->name_len)) {
+ printk ("ext2_add_entry: bad directory entry (dev %04x, dir %d)\n",
+ dir->i_dev, dir->i_ino);
+ printk ("offset=%d, inode=%d, rec_len=%d, name_len=%d\n",
+ offset, de->inode, de->rec_len, de->name_len);
+ brelse (bh);
+ return NULL;
+ }
+ if ((!de->inode && de->rec_len >= rec_len) ||
+ (de->rec_len >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) {
+ offset += de->rec_len;
+ if (de->inode) {
+ de1 = (struct ext2_dir_entry *) ((char *) de +
+ EXT2_DIR_REC_LEN(de->name_len));
+ de1->rec_len = de->rec_len -
+ EXT2_DIR_REC_LEN(de->name_len);
+ de->rec_len = EXT2_DIR_REC_LEN(de->name_len);
+ de = de1;
+ }
+ de->inode = 0;
+ de->name_len = namelen;
+ for (i = 0; i < namelen ; i++)
+ de->name[i] = name [i];
+ dir->i_mtime = CURRENT_TIME;
+ bh->b_dirt = 1;
+ *res_dir = de;
+ return bh;
+ }
+ offset += de->rec_len;
+ de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
+ }
+ brelse (bh);
+ return NULL;
+}
+
+int ext2_create (struct inode * dir,const char * name, int len, int mode,
+ struct inode ** result)
+{
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct ext2_dir_entry * de;
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+ inode = ext2_new_inode (dir, mode);
+ if (!inode) {
+ iput (dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &ext2_file_inode_operations;
+ inode->i_mode = mode;
+ inode->i_dirt = 1;
+ bh = ext2_add_entry (dir, name, len, &de);
+ if (!bh) {
+ inode->i_nlink --;
+ inode->i_dirt = 1;
+ iput (inode);
+ iput (dir);
+ return -ENOSPC;
+ }
+ de->inode = inode->i_ino;
+#ifndef DONT_USE_DCACHE
+ ext2_dcache_add (dir->i_dev, dir->i_ino, de->name, de->name_len,
+ de->inode);
+#endif
+ bh->b_dirt = 1;
+ brelse (bh);
+ iput (dir);
+ *result = inode;
+ return 0;
+}
+
+int ext2_mknod (struct inode * dir, const char * name, int len, int mode,
+ int rdev)
+{
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct ext2_dir_entry * de;
+
+ if (!dir)
+ return -ENOENT;
+ bh = ext2_find_entry (dir, name, len, &de, NULL);
+ if (bh) {
+ brelse (bh);
+ iput (dir);
+ return -EEXIST;
+ }
+ inode = ext2_new_inode (dir, mode);
+ if (!inode) {
+ iput (dir);
+ return -ENOSPC;
+ }
+ inode->i_uid = current->euid;
+ inode->i_mode = mode;
+ inode->i_op = NULL;
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &ext2_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &ext2_dir_inode_operations;
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ }
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &ext2_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode)) {
+ inode->i_op = &fifo_inode_operations;
+ inode->i_pipe = 1;
+ PIPE_BASE(*inode) = NULL;
+ PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;
+ PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL;
+ PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0;
+ }
+ if (S_ISBLK(mode) || S_ISCHR(mode))
+ inode->i_rdev = rdev;
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ bh = ext2_add_entry (dir, name, len, &de);
+ if (!bh) {
+ inode->i_nlink --;
+ inode->i_dirt = 1;
+ iput (inode);
+ iput (dir);
+ return -ENOSPC;
+ }
+ de->inode = inode->i_ino;
+#ifndef DONT_USE_DCACHE
+ ext2_dcache_add (dir->i_dev, dir->i_ino, de->name, de->name_len,
+ de->inode);
+#endif
+ bh->b_dirt = 1;
+ brelse (bh);
+ iput (dir);
+ iput (inode);
+ return 0;
+}
+
+int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
+{
+ struct inode * inode;
+ struct buffer_head * bh, * dir_block;
+ struct ext2_dir_entry * de;
+
+ bh = ext2_find_entry (dir, name, len, &de, NULL);
+ if (bh) {
+ brelse (bh);
+ iput (dir);
+ return -EEXIST;
+ }
+ inode = ext2_new_inode (dir, S_IFDIR);
+ if (!inode) {
+ iput (dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &ext2_dir_inode_operations;
+ inode->i_size = inode->i_sb->s_blocksize;
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
+ dir_block = ext2_bread (inode, 0, 1);
+ if (!dir_block) {
+ iput (dir);
+ inode->i_nlink --;
+ inode->i_dirt = 1;
+ iput (inode);
+ return -ENOSPC;
+ }
+ inode->i_blocks = inode->i_sb->s_blocksize / 512;
+ de = (struct ext2_dir_entry *) dir_block->b_data;
+ de->inode = inode->i_ino;
+ de->name_len = 1;
+ de->rec_len = EXT2_DIR_REC_LEN(de->name_len);
+ strcpy (de->name, ".");
+ de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
+ de->inode = dir->i_ino;
+ de->rec_len = inode->i_sb->s_blocksize - EXT2_DIR_REC_LEN(1);
+ de->name_len = 2;
+ strcpy (de->name, "..");
+ inode->i_nlink = 2;
+ dir_block->b_dirt = 1;
+ brelse (dir_block);
+ inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask);
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ inode->i_dirt = 1;
+ bh = ext2_add_entry (dir, name, len, &de);
+ if (!bh) {
+ iput (dir);
+ inode->i_nlink = 0;
+ iput (inode);
+ return -ENOSPC;
+ }
+ de->inode = inode->i_ino;
+#ifndef DONT_USE_DCACHE
+ ext2_dcache_add (dir->i_dev, dir->i_ino, de->name, de->name_len,
+ de->inode);
+#endif
+ bh->b_dirt = 1;
+ dir->i_nlink ++;
+ dir->i_dirt = 1;
+ iput (dir);
+ iput (inode);
+ brelse (bh);
+ return 0;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+static int empty_dir (struct inode * inode)
+{
+ unsigned long offset;
+ struct buffer_head * bh;
+ struct ext2_dir_entry * de, * de1;
+ struct super_block * sb;
+
+ sb = inode->i_sb;
+ if (inode->i_size < EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) ||
+ !(bh = ext2_bread (inode, 0, 0))) {
+ printk ("warning - bad directory (dev %04x, dir %d)\n",
+ inode->i_dev, inode->i_ino);
+ return 1;
+ }
+ de = (struct ext2_dir_entry *) bh->b_data;
+ de1 = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
+ if (de->inode != inode->i_ino || !de1->inode ||
+ strcmp (".", de->name) || strcmp ("..", de1->name)) {
+ printk ("warning - bad directory (dev %04x, dir %d)\n",
+ inode->i_dev, inode->i_ino);
+ return 1;
+ }
+ offset = de->rec_len + de1->rec_len;
+ de = (struct ext2_dir_entry *) ((char *) de1 + de1->rec_len);
+ while (offset < inode->i_size ) {
+ if ((void *) de >= (void *) (bh->b_data + sb->s_blocksize)) {
+ brelse (bh);
+ bh = ext2_bread (inode, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1);
+ if (!bh) {
+ offset += sb->s_blocksize;
+ continue;
+ }
+ de = (struct ext2_dir_entry *) bh->b_data;
+ }
+ if (de->rec_len < EXT2_DIR_REC_LEN(1) ||
+ de->rec_len % 4 != 0 ||
+ de->rec_len < EXT2_DIR_REC_LEN(de->name_len)) {
+ printk ("empty_dir: bad directory entry (dev %04x, dir %d)\n",
+ inode->i_dev, inode->i_ino);
+ printk ("offset=%d, inode=%d, rec_len=%d, name_len=%d\n",
+ offset, de->inode, de->rec_len, de->name_len);
+ brelse (bh);
+ return 1;
+ }
+ if (de->inode) {
+ brelse (bh);
+ return 0;
+ }
+ offset += de->rec_len;
+ de = (struct ext2_dir_entry *) ((char *) de + de->rec_len);
+ }
+ brelse (bh);
+ return 1;
+}
+
+int ext2_rmdir (struct inode * dir, const char * name, int len)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct ext2_dir_entry * de, * pde;
+
+ inode = NULL;
+ bh = ext2_find_entry (dir, name, len, &de, &pde);
+ retval = -ENOENT;
+ if (!bh)
+ goto end_rmdir;
+ retval = -EPERM;
+ if (!(inode = iget (dir->i_sb, de->inode)))
+ goto end_rmdir;
+ if ((dir->i_mode & S_ISVTX) && current->euid &&
+ inode->i_uid != current->euid)
+ goto end_rmdir;
+ if (inode->i_dev != dir->i_dev)
+ goto end_rmdir;
+ if (inode == dir) /* we may not delete ".", but "../dir" is ok */
+ goto end_rmdir;
+ if (!S_ISDIR(inode->i_mode)) {
+ retval = -ENOTDIR;
+ goto end_rmdir;
+ }
+ if (!empty_dir (inode)) {
+ retval = -ENOTEMPTY;
+ goto end_rmdir;
+ }
+ if (inode->i_count > 1) {
+ retval = -EBUSY;
+ goto end_rmdir;
+ }
+ if (inode->i_nlink != 2)
+ printk ("empty directory has nlink!=2 (%d)\n", inode->i_nlink);
+#ifndef DONT_USE_DCACHE
+ ext2_dcache_remove (dir->i_dev, dir->i_ino, de->name, de->name_len);
+#endif
+ if (pde)
+ pde->rec_len += de->rec_len;
+ else
+ de->inode = 0;
+ bh->b_dirt = 1;
+ inode->i_nlink = 0;
+ inode->i_dirt = 1;
+ dir->i_nlink --;
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt = 1;
+ retval = 0;
+end_rmdir:
+ iput (dir);
+ iput (inode);
+ brelse (bh);
+ return retval;
+}
+
+int ext2_unlink (struct inode * dir, const char * name, int len)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct ext2_dir_entry * de, * pde;
+
+ retval = -ENOENT;
+ inode = NULL;
+ bh = ext2_find_entry (dir, name, len, &de, &pde);
+ if (!bh)
+ goto end_unlink;
+ if (!(inode = iget (dir->i_sb, de->inode)))
+ goto end_unlink;
+ retval = -EPERM;
+ if ((dir->i_mode & S_ISVTX) && !suser() &&
+ current->euid != inode->i_uid &&
+ current->euid != dir->i_uid)
+ goto end_unlink;
+ if (S_ISDIR(inode->i_mode))
+ goto end_unlink;
+ if (!inode->i_nlink) {
+ printk ("Deleting nonexistent file (%04x:%d), %d\n",
+ inode->i_dev, inode->i_ino, inode->i_nlink);
+ inode->i_nlink = 1;
+ }
+#ifndef DONT_USE_DCACHE
+ ext2_dcache_remove (dir->i_dev, dir->i_ino, de->name, de->name_len);
+#endif
+ if (pde)
+ pde->rec_len += de->rec_len;
+ else
+ de->inode = 0;
+ bh->b_dirt = 1;
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt = 1;
+ inode->i_nlink --;
+ inode->i_dirt = 1;
+ inode->i_ctime = CURRENT_TIME;
+ retval = 0;
+end_unlink:
+ brelse (bh);
+ iput (inode);
+ iput (dir);
+ return retval;
+}
+
+int ext2_symlink (struct inode * dir, const char * name, int len,
+ const char * symname)
+{
+ struct ext2_dir_entry * de;
+ struct inode * inode = NULL;
+ struct buffer_head * bh = NULL, * name_block = NULL;
+ char * link;
+ int i;
+ int l;
+ char c;
+
+ if (!(inode = ext2_new_inode (dir, S_IFLNK))) {
+ iput (dir);
+ return -ENOSPC;
+ }
+ inode->i_mode = S_IFLNK | 0777;
+ inode->i_op = &ext2_symlink_inode_operations;
+ for (l = 0; l < inode->i_sb->s_blocksize - 1 &&
+ symname [l]; l++)
+ ;
+ if (l >= EXT2_N_BLOCKS * sizeof (unsigned long)) {
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_symlink: l=%d, normal symlink\n", l);
+#endif
+ name_block = ext2_bread (inode, 0, 1);
+ if (!name_block) {
+ iput (dir);
+ inode->i_nlink --;
+ inode->i_dirt = 1;
+ iput (inode);
+ return -ENOSPC;
+ }
+ link = name_block->b_data;
+ } else {
+ link = (char *) inode->u.ext2_i.i_data;
+#ifdef EXT2FS_DEBUG
+ printk ("ext2_symlink: l=%d, fast symlink\n", l);
+#endif
+ }
+ i = 0;
+ while (i < inode->i_sb->s_blocksize - 1 && (c = *(symname ++)))
+ link[i++] = c;
+ link[i] = 0;
+ if (name_block) {
+ name_block->b_dirt = 1;
+ brelse (name_block);
+ }
+ inode->i_size = i;
+ inode->i_dirt = 1;
+ bh = ext2_find_entry (dir, name, len, &de, NULL);
+ if (bh) {
+ inode->i_nlink --;
+ inode->i_dirt = 1;
+ iput (inode);
+ brelse (bh);
+ iput (dir);
+ return -EEXIST;
+ }
+ bh = ext2_add_entry (dir, name, len, &de);
+ if (!bh) {
+ inode->i_nlink --;
+ inode->i_dirt = 1;
+ iput (inode);
+ iput (dir);
+ return -ENOSPC;
+ }
+ de->inode = inode->i_ino;
+#ifndef DONT_USE_DCACHE
+ ext2_dcache_add (dir->i_dev, dir->i_ino, de->name, de->name_len,
+ de->inode);
+#endif
+ bh->b_dirt = 1;
+ brelse (bh);
+ iput (dir);
+ iput (inode);
+ return 0;
+}
+
+int ext2_link (struct inode * oldinode, struct inode * dir,
+ const char * name, int len)
+{
+ struct ext2_dir_entry * de;
+ struct buffer_head * bh;
+
+ if (S_ISDIR(oldinode->i_mode)) {
+ iput (oldinode);
+ iput (dir);
+ return -EPERM;
+ }
+ if (oldinode->i_nlink > 32000) {
+ iput (oldinode);
+ iput (dir);
+ return -EMLINK;
+ }
+ bh = ext2_find_entry (dir, name, len, &de, NULL);
+ if (bh) {
+ brelse (bh);
+ iput (dir);
+ iput (oldinode);
+ return -EEXIST;
+ }
+ bh = ext2_add_entry (dir, name, len, &de);
+ if (!bh) {
+ iput (dir);
+ iput (oldinode);
+ return -ENOSPC;
+ }
+ de->inode = oldinode->i_ino;
+#ifndef DONT_USE_DCACHE
+ ext2_dcache_add (dir->i_dev, dir->i_ino, de->name, de->name_len,
+ de->inode);
+#endif
+ bh->b_dirt = 1;
+ brelse (bh);
+ iput (dir);
+ oldinode->i_nlink ++;
+ oldinode->i_ctime = CURRENT_TIME;
+ oldinode->i_dirt = 1;
+ iput (oldinode);
+ return 0;
+}
+
+static int subdir (struct inode * new, struct inode * old)
+{
+ int ino;
+ int result;
+
+ new->i_count ++;
+ result = 0;
+ for (;;) {
+ if (new == old) {
+ result = 1;
+ break;
+ }
+ if (new->i_dev != old->i_dev)
+ break;
+ ino = new->i_ino;
+ if (ext2_lookup (new, "..", 2, &new))
+ break;
+ if (new->i_ino == ino)
+ break;
+ }
+ iput (new);
+ return result;
+}
+
+#define PARENT_INO(buffer) \
+ ((struct ext2_dir_entry *) ((char *) buffer + \
+ ((struct ext2_dir_entry *) buffer)->rec_len))->inode
+
+#define PARENT_NAME(buffer) \
+ ((struct ext2_dir_entry *) ((char *) buffer + \
+ ((struct ext2_dir_entry *) buffer)->rec_len))->name
+
+/*
+ * rename uses retrying to avoid race-conditions: at least they should be
+ * minimal.
+ * it tries to allocate all the blocks, then sanity-checks, and if the sanity-
+ * checks fail, it tries to restart itself again. Very practical - no changes
+ * are done until we know everything works ok.. and then all the changes can be
+ * done in one fell swoop when we have claimed all the buffers needed.
+ *
+ * Anybody can rename anything with this: the permission checks are left to the
+ * higher-level routines.
+ */
+static int do_ext2_rename (struct inode * old_dir, const char * old_name,
+ int old_len, struct inode * new_dir,
+ const char * new_name, int new_len)
+{
+ struct inode * old_inode, * new_inode;
+ struct buffer_head * old_bh, * new_bh, * dir_bh;
+ struct ext2_dir_entry * old_de, * new_de, * pde;
+ int retval;
+
+ goto start_up;
+try_again:
+ brelse (old_bh);
+ brelse (new_bh);
+ brelse (dir_bh);
+ iput (old_inode);
+ iput (new_inode);
+ current->counter = 0;
+ schedule ();
+start_up:
+ old_inode = new_inode = NULL;
+ old_bh = new_bh = dir_bh = NULL;
+ old_bh = ext2_find_entry (old_dir, old_name, old_len, &old_de, &pde);
+ retval = -ENOENT;
+ if (!old_bh)
+ goto end_rename;
+ old_inode = iget (old_dir->i_sb, old_de->inode);
+ if (!old_inode)
+ goto end_rename;
+ if ((old_dir->i_mode & S_ISVTX) &&
+ current->euid != old_inode->i_uid &&
+ current->euid != old_dir->i_uid && !suser())
+ goto end_rename;
+ new_bh = ext2_find_entry (new_dir, new_name, new_len, &new_de, NULL);
+ if (new_bh) {
+ new_inode = iget (new_dir->i_sb, new_de->inode);
+ if (!new_inode) {
+ brelse (new_bh);
+ new_bh = NULL;
+ }
+ }
+ if (new_inode == old_inode) {
+ retval = 0;
+ goto end_rename;
+ }
+ if (new_inode && S_ISDIR(new_inode->i_mode)) {
+ retval = -EEXIST;
+ goto end_rename;
+ }
+ retval = -EPERM;
+ if (new_inode && (new_dir->i_mode & S_ISVTX) &&
+ current->euid != new_inode->i_uid &&
+ current->euid != new_dir->i_uid && !suser())
+ goto end_rename;
+ if (S_ISDIR(old_inode->i_mode)) {
+ retval = -EEXIST;
+ if (new_bh)
+ goto end_rename;
+ retval = -EACCES;
+ if (!permission (old_inode, MAY_WRITE))
+ goto end_rename;
+ retval = -EINVAL;
+ if (subdir (new_dir, old_inode))
+ goto end_rename;
+ retval = -EIO;
+ dir_bh = ext2_bread (old_inode, 0, 0);
+ if (!dir_bh)
+ goto end_rename;
+ if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
+ goto end_rename;
+ }
+ if (!new_bh)
+ new_bh = ext2_add_entry (new_dir, new_name, new_len, &new_de);
+ retval = -ENOSPC;
+ if (!new_bh)
+ goto end_rename;
+/* sanity checking before doing the rename - avoid races */
+ if (new_inode && (new_de->inode != new_inode->i_ino))
+ goto try_again;
+ if (new_de->inode && !new_inode)
+ goto try_again;
+ if (old_de->inode != old_inode->i_ino)
+ goto try_again;
+/* ok, that's it */
+ new_de->inode = old_inode->i_ino;
+#ifndef DONT_USE_DCACHE
+ ext2_dcache_remove (old_dir->i_dev, old_dir->i_ino, old_de->name,
+ old_de->name_len);
+ ext2_dcache_add (new_dir->i_dev, new_dir->i_ino, new_de->name,
+ new_de->name_len, new_de->inode);
+#endif
+ if (((char *) new_de) + new_de->rec_len == (char *) old_de)
+ new_de->rec_len += old_de->rec_len;
+ else if (pde)
+ pde->rec_len += old_de->rec_len;
+ else
+ old_de->inode = 0;
+ if (new_inode) {
+ new_inode->i_nlink --;
+ new_inode->i_dirt = 1;
+ }
+ old_bh->b_dirt = 1;
+ new_bh->b_dirt = 1;
+ if (dir_bh) {
+ PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
+ dir_bh->b_dirt = 1;
+ old_dir->i_nlink --;
+ new_dir->i_nlink ++;
+ old_dir->i_dirt = 1;
+ new_dir->i_dirt = 1;
+ }
+ retval = 0;
+end_rename:
+ brelse (dir_bh);
+ brelse (old_bh);
+ brelse (new_bh);
+ iput (old_inode);
+ iput (new_inode);
+ iput (old_dir);
+ iput (new_dir);
+ return retval;
+}
+
+/*
+ * Ok, rename also locks out other renames, as they can change the parent of
+ * a directory, and we don't want any races. Other races are checked for by
+ * "do_rename()", which restarts if there are inconsistencies.
+ *
+ * Note that there is no race between different filesystems: it's only within
+ * the same device that races occur: many renames can happen at once, as long
+ * as they are on different partitions.
+ *
+ * In the second extended file system, we use a lock flag stored in the memory
+ * super-block. This way, we really lock other renames only if they occur
+ * on the same file system
+ */
+int ext2_rename (struct inode * old_dir, const char * old_name, int old_len,
+ struct inode * new_dir, const char * new_name, int new_len)
+{
+ int result;
+
+ while (old_dir->i_sb->u.ext2_sb.s_rename_lock)
+ sleep_on (&old_dir->i_sb->u.ext2_sb.s_rename_wait);
+ old_dir->i_sb->u.ext2_sb.s_rename_lock = 1;
+ result = do_ext2_rename (old_dir, old_name, old_len, new_dir,
+ new_name, new_len);
+ old_dir->i_sb->u.ext2_sb.s_rename_lock = 0;
+ wake_up (&old_dir->i_sb->u.ext2_sb.s_rename_wait);
+ return result;
+}
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
new file mode 100644
index 0000000..64904d9
--- /dev/null
+++ b/fs/ext2/symlink.c
@@ -0,0 +1,124 @@
+/*
+ * linux/fs/ext2/symlink.c
+ *
+ * Copyright (C) 1992, 1993 Remy Card (card@masi.ibp.fr)
+ *
+ * from
+ *
+ * linux/fs/minix/symlink.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext2 symlink handling code
+ */
+
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/stat.h>
+
+static int ext2_readlink (struct inode *, char *, int);
+static int ext2_follow_link (struct inode *, struct inode *, int, int,
+ struct inode **);
+
+/*
+ * symlinks can't do much...
+ */
+struct inode_operations ext2_symlink_inode_operations = {
+ NULL, /* no file-operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ ext2_readlink, /* readlink */
+ ext2_follow_link, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static int ext2_follow_link(struct inode * dir, struct inode * inode,
+ int flag, int mode, struct inode ** res_inode)
+{
+ int error;
+ struct buffer_head * bh = NULL;
+ char * link;
+
+ *res_inode = NULL;
+ if (!dir) {
+ dir = current->root;
+ dir->i_count ++;
+ }
+ if (!inode) {
+ iput (dir);
+ return -ENOENT;
+ }
+ if (!S_ISLNK(inode->i_mode)) {
+ iput (dir);
+ *res_inode = inode;
+ return 0;
+ }
+ if (current->link_count > 5) {
+ iput (dir);
+ iput (inode);
+ return -ELOOP;
+ }
+ if (inode->i_blocks) {
+ if (!(bh = ext2_bread (inode, 0, 0))) {
+ iput (dir);
+ iput (inode);
+ return -EIO;
+ }
+ link = bh->b_data;
+ } else
+ link = (char *) inode->u.ext2_i.i_data;
+ current->link_count ++;
+ error = open_namei (link, flag, mode, res_inode, dir);
+ current->link_count --;
+ iput (inode);
+ if (bh)
+ brelse (bh);
+ return error;
+}
+
+static int ext2_readlink (struct inode * inode, char * buffer, int buflen)
+{
+ struct buffer_head * bh = NULL;
+ char * link;
+ int i;
+ char c;
+
+ if (!S_ISLNK(inode->i_mode)) {
+ iput (inode);
+ return -EINVAL;
+ }
+ if (buflen > inode->i_sb->s_blocksize - 1)
+ buflen = inode->i_sb->s_blocksize - 1;
+ if (inode->i_blocks) {
+ bh = ext2_bread (inode, 0, 0);
+ if (!bh) {
+ iput (inode);
+ return 0;
+ }
+ link = bh->b_data;
+ }
+ else
+ link = (char *) inode->u.ext2_i.i_data;
+ i = 0;
+ while (i < buflen && (c = link[i])) {
+ i ++;
+ put_fs_byte (c, buffer++);
+ }
+ iput (inode);
+ if (bh)
+ brelse (bh);
+ return i;
+}
diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c
new file mode 100644
index 0000000..bc613bc
--- /dev/null
+++ b/fs/ext2/truncate.c
@@ -0,0 +1,283 @@
+/*
+ * linux/fs/ext2/truncate.c
+ *
+ * Copyright (C) 1992, 1993 Remy Card (card@masi.ibp.fr)
+ *
+ * from
+ *
+ * linux/fs/minix/truncate.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/ext2_fs.h>
+#include <linux/tty.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+
+/*
+ * Truncate has the most races in the whole filesystem: coding it is
+ * a pain in the a**. Especially as I don't do any locking...
+ *
+ * The code may look a bit weird, but that's just because I've tried to
+ * handle things like file-size changes in a somewhat graceful manner.
+ * Anyway, truncating a file at the same time somebody else writes to it
+ * is likely to result in pretty weird behaviour...
+ *
+ * The new code handles normal truncates (size = 0) as well as the more
+ * general case (size = XXX). I hope.
+ */
+
+static int trunc_direct (struct inode * inode)
+{
+ int i, tmp;
+ unsigned long * p;
+ struct buffer_head * bh;
+ int retry = 0;
+ int blocks = inode->i_sb->s_blocksize / 512;
+#define DIRECT_BLOCK ((inode->i_size + inode->i_sb->s_blocksize - 1) / \
+ inode->i_sb->s_blocksize)
+ int direct_block = DIRECT_BLOCK;
+
+repeat:
+ for (i = direct_block ; i < EXT2_NDIR_BLOCKS ; i++) {
+ p = inode->u.ext2_i.i_data + i;
+ tmp = *p;
+ if (!tmp)
+ continue;
+ bh = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+ if (i < direct_block) {
+ brelse (bh);
+ goto repeat;
+ }
+ if ((bh && bh->b_count != 1) || tmp != *p) {
+ retry = 1;
+ brelse (bh);
+ continue;
+ }
+ *p = 0;
+ inode->i_blocks -= blocks;
+ inode->i_dirt = 1;
+ brelse (bh);
+ ext2_free_block (inode->i_sb, tmp);
+ }
+ return retry;
+}
+
+static int trunc_indirect (struct inode * inode, int offset, unsigned long * p)
+{
+ int i, tmp;
+ struct buffer_head * bh;
+ struct buffer_head * ind_bh;
+ unsigned long * ind;
+ int retry = 0;
+ int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+ int blocks = inode->i_sb->s_blocksize / 512;
+#define INDIRECT_BLOCK ((int)DIRECT_BLOCK - offset)
+ int indirect_block = INDIRECT_BLOCK;
+
+ tmp = *p;
+ if (!tmp)
+ return 0;
+ ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+ if (tmp != *p) {
+ brelse (ind_bh);
+ return 1;
+ }
+ if (!ind_bh) {
+ *p = 0;
+ return 0;
+ }
+repeat:
+ for (i = indirect_block ; i < addr_per_block ; i++) {
+ if (i < 0)
+ i = 0;
+ if (i < indirect_block)
+ goto repeat;
+ ind = i + (unsigned long *) ind_bh->b_data;
+ tmp = *ind;
+ if (!tmp)
+ continue;
+ bh = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+ if (i < indirect_block) {
+ brelse (bh);
+ goto repeat;
+ }
+ if ((bh && bh->b_count != 1) || tmp != *ind) {
+ retry = 1;
+ brelse (bh);
+ continue;
+ }
+ *ind = 0;
+ ind_bh->b_dirt = 1;
+ brelse (bh);
+ ext2_free_block (inode->i_sb, tmp);
+ inode->i_blocks -= blocks;
+ inode->i_dirt = 1;
+ }
+ ind = (unsigned long *) ind_bh->b_data;
+ for (i = 0; i < addr_per_block; i++)
+ if (*(ind++))
+ break;
+ if (i >= addr_per_block)
+ if (ind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = *p;
+ *p = 0;
+ inode->i_blocks -= blocks;
+ inode->i_dirt = 1;
+ ext2_free_block (inode->i_sb, tmp);
+ }
+ brelse (ind_bh);
+ return retry;
+}
+
+static int trunc_dindirect (struct inode * inode, int offset,
+ unsigned long * p)
+{
+ int i, tmp;
+ struct buffer_head * dind_bh;
+ unsigned long * dind;
+ int retry = 0;
+ int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+ int blocks = inode->i_sb->s_blocksize / 512;
+#define DINDIRECT_BLOCK (((int)DIRECT_BLOCK - offset) / addr_per_block)
+ int dindirect_block = DINDIRECT_BLOCK;
+
+ tmp = *p;
+ if (!tmp)
+ return 0;
+ dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+ if (tmp != *p) {
+ brelse (dind_bh);
+ return 1;
+ }
+ if (!dind_bh) {
+ *p = 0;
+ return 0;
+ }
+repeat:
+ for (i = dindirect_block ; i < addr_per_block ; i ++) {
+ if (i < 0)
+ i = 0;
+ if (i < dindirect_block)
+ goto repeat;
+ dind = i + (unsigned long *) dind_bh->b_data;
+ tmp = *dind;
+ if (!tmp)
+ continue;
+ retry |= trunc_indirect (inode, offset + (i * addr_per_block),
+ dind);
+ dind_bh->b_dirt = 1;
+ }
+ dind = (unsigned long *) dind_bh->b_data;
+ for (i = 0; i < addr_per_block; i++)
+ if (*(dind++))
+ break;
+ if (i >= addr_per_block)
+ if (dind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = *p;
+ *p = 0;
+ inode->i_blocks -= blocks;
+ inode->i_dirt = 1;
+ ext2_free_block (inode->i_sb, tmp);
+ }
+ brelse (dind_bh);
+ return retry;
+}
+
+static int trunc_tindirect (struct inode * inode)
+{
+ int i, tmp;
+ struct buffer_head * tind_bh;
+ unsigned long * tind, * p;
+ int retry = 0;
+ int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+ int blocks = inode->i_sb->s_blocksize / 512;
+#define TINDIRECT_BLOCK (((int)DIRECT_BLOCK - (addr_per_block * addr_per_block + \
+ addr_per_block + EXT2_NDIR_BLOCKS)) / \
+ (addr_per_block * addr_per_block))
+ int tindirect_block = TINDIRECT_BLOCK;
+
+ p = inode->u.ext2_i.i_data + EXT2_TIND_BLOCK;
+ if (!(tmp = *p))
+ return 0;
+ tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
+ if (tmp != *p) {
+ brelse (tind_bh);
+ return 1;
+ }
+ if (!tind_bh) {
+ *p = 0;
+ return 0;
+ }
+repeat:
+ for (i = tindirect_block ; i < addr_per_block ; i ++) {
+ if (i < 0)
+ i = 0;
+ if (i < tindirect_block)
+ goto repeat;
+ tind = i + (unsigned long *) tind_bh->b_data;
+ retry |= trunc_dindirect(inode, EXT2_NDIR_BLOCKS +
+/* addr_per_block + addr_per_block * addr_per_block +
+ (i * (addr_per_block * addr_per_block)), tind); */
+ addr_per_block + (i + 1) * addr_per_block * addr_per_block,
+ tind);
+ tind_bh->b_dirt = 1;
+ }
+ tind = (unsigned long *) tind_bh->b_data;
+ for (i = 0; i < addr_per_block; i++)
+ if (*(tind++))
+ break;
+ if (i >= addr_per_block)
+ if (tind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = *p;
+ *p = 0;
+ inode->i_blocks -= blocks;
+ inode->i_dirt = 1;
+ ext2_free_block (inode->i_sb, tmp);
+ }
+ brelse (tind_bh);
+ return retry;
+}
+
+void ext2_truncate (struct inode * inode)
+{
+ int retry;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return;
+ while (1) {
+ retry = trunc_direct(inode);
+ retry |= trunc_indirect (inode, EXT2_IND_BLOCK,
+ (unsigned long *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK]);
+ retry |= trunc_dindirect (inode, EXT2_IND_BLOCK +
+ EXT2_ADDR_PER_BLOCK(inode->i_sb),
+ (unsigned long *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK]);
+ retry |= trunc_tindirect (inode);
+ if (!retry)
+ break;
+ current->counter = 0;
+ schedule ();
+ }
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+}
+
+/*
+ * Called when a inode is released. Note that this is different
+ * from ext2_open: open gets called at every open, but release
+ * gets called only when /all/ the files are closed.
+ */
+void ext2_release (struct inode * inode, struct file * filp)
+{
+ printk ("ext2_release not implemented\n");
+}
diff --git a/fs/fcntl.c b/fs/fcntl.c
index e9d63d0..52c6d90 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -41,6 +41,19 @@ int sys_dup2(unsigned int oldfd, unsigned int newfd)
return -EBADF;
if (newfd == oldfd)
return newfd;
+ /*
+ * errno's for dup2() are slightly different than for fcntl(F_DUPFD)
+ * for historical reasons.
+ */
+ if (newfd > NR_OPEN) /* historical botch - should have been >= */
+ return -EBADF; /* dupfd() would return -EINVAL */
+#if 1
+ if (newfd == NR_OPEN)
+ return -EBADF; /* dupfd() does return -EINVAL and that may
+ * even be the standard! But that is too
+ * weird for now.
+ */
+#endif
sys_close(newfd);
return dupfd(oldfd,newfd);
}
diff --git a/fs/fifo.c b/fs/fifo.c
index 3ca5fc3..868558b 100644
--- a/fs/fifo.c
+++ b/fs/fifo.c
@@ -27,19 +27,28 @@ static int fifo_open(struct inode * inode,struct file * filp)
* opened, even when there is no process writing the FIFO.
*/
filp->f_op = &read_pipe_fops;
- PIPE_READERS(*inode)++;
- if (!(filp->f_flags & O_NONBLOCK))
+ if (!PIPE_READERS(*inode)++)
+ wake_up(&PIPE_WRITE_WAIT(*inode));
+ if (!(filp->f_flags & O_NONBLOCK) && !PIPE_WRITERS(*inode)) {
+ PIPE_RD_OPENERS(*inode)++;
while (!PIPE_WRITERS(*inode)) {
+#if 0
if (PIPE_HEAD(*inode) != PIPE_TAIL(*inode))
break;
+#endif
if (current->signal & ~current->blocked) {
retval = -ERESTARTSYS;
break;
}
interruptible_sleep_on(&PIPE_READ_WAIT(*inode));
}
- if (retval)
- PIPE_READERS(*inode)--;
+ if (!--PIPE_RD_OPENERS(*inode))
+ wake_up(&PIPE_WRITE_WAIT(*inode));
+ }
+ while (PIPE_WR_OPENERS(*inode))
+ interruptible_sleep_on(&PIPE_READ_WAIT(*inode));
+ if (retval && !--PIPE_READERS(*inode))
+ wake_up(&PIPE_WRITE_WAIT(*inode));
break;
case 2:
@@ -53,16 +62,24 @@ static int fifo_open(struct inode * inode,struct file * filp)
break;
}
filp->f_op = &write_pipe_fops;
- PIPE_WRITERS(*inode)++;
- while (!PIPE_READERS(*inode)) {
- if (current->signal & ~current->blocked) {
- retval = -ERESTARTSYS;
- break;
+ if (!PIPE_WRITERS(*inode)++)
+ wake_up(&PIPE_READ_WAIT(*inode));
+ if (!PIPE_READERS(*inode)) {
+ PIPE_WR_OPENERS(*inode)++;
+ while (!PIPE_READERS(*inode)) {
+ if (current->signal & ~current->blocked) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ interruptible_sleep_on(&PIPE_WRITE_WAIT(*inode));
}
- interruptible_sleep_on(&PIPE_WRITE_WAIT(*inode));
+ if (!--PIPE_WR_OPENERS(*inode))
+ wake_up(&PIPE_READ_WAIT(*inode));
}
- if (retval)
- PIPE_WRITERS(*inode)--;
+ while (PIPE_RD_OPENERS(*inode))
+ interruptible_sleep_on(&PIPE_WRITE_WAIT(*inode));
+ if (retval && !--PIPE_WRITERS(*inode))
+ wake_up(&PIPE_READ_WAIT(*inode));
break;
case 3:
@@ -73,17 +90,19 @@ static int fifo_open(struct inode * inode,struct file * filp)
* the process can at least talk to itself.
*/
filp->f_op = &rdwr_pipe_fops;
- PIPE_WRITERS(*inode) += 1;
- PIPE_READERS(*inode) += 1;
+ if (!PIPE_READERS(*inode)++)
+ wake_up(&PIPE_WRITE_WAIT(*inode));
+ while (PIPE_WR_OPENERS(*inode))
+ interruptible_sleep_on(&PIPE_READ_WAIT(*inode));
+ if (!PIPE_WRITERS(*inode)++)
+ wake_up(&PIPE_READ_WAIT(*inode));
+ while (PIPE_RD_OPENERS(*inode))
+ interruptible_sleep_on(&PIPE_WRITE_WAIT(*inode));
break;
default:
retval = -EINVAL;
}
- if (PIPE_WRITERS(*inode))
- wake_up(&PIPE_READ_WAIT(*inode));
- if (PIPE_READERS(*inode))
- wake_up(&PIPE_WRITE_WAIT(*inode));
if (retval || PIPE_BASE(*inode))
return retval;
page = get_free_page(GFP_KERNEL);
diff --git a/fs/filesystems.c b/fs/filesystems.c
index 3891f53..81f9de3 100644
--- a/fs/filesystems.c
+++ b/fs/filesystems.c
@@ -11,9 +11,15 @@
#ifdef CONFIG_MINIX_FS
#include <linux/minix_fs.h>
#endif
+#ifdef CONFIG_XIA_FS
+#include <linux/xia_fs.h>
+#endif
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#endif
+#ifdef CONFIG_EXT2_FS
+#include <linux/ext2_fs.h>
+#endif
#ifdef CONFIG_EXT_FS
#include <linux/ext_fs.h>
#endif
@@ -31,9 +37,15 @@ struct file_system_type file_systems[] = {
#ifdef CONFIG_MINIX_FS
{minix_read_super, "minix", 1},
#endif
+#ifdef CONFIG_XIA_FS
+ {xiafs_read_super, "xiafs", 1},
+#endif
#ifdef CONFIG_EXT_FS
{ext_read_super, "ext", 1},
#endif
+#ifdef CONFIG_EXT2_FS
+ {ext2_read_super, "ext2", 1},
+#endif
#ifdef CONFIG_MSDOS_FS
{msdos_read_super, "msdos", 1},
#endif
diff --git a/fs/inode.c b/fs/inode.c
index b83c789..9bcab7c 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -164,7 +164,8 @@ void invalidate_inodes(dev_t dev)
wait_on_inode(inode);
if (inode->i_dev == dev) {
if (inode->i_count) {
- printk("inode in use on removed disk\n\r");
+ printk("VFS: inode busy on removed device %d/%d\n",
+ MAJOR(dev), MINOR(dev));
continue;
}
clear_inode(inode);
@@ -191,9 +192,10 @@ void iput(struct inode * inode)
return;
wait_on_inode(inode);
if (!inode->i_count) {
- printk("iput: trying to free free inode\n");
- printk("device %04x, inode %d, mode=%07o\n",inode->i_rdev,
- inode->i_ino,inode->i_mode);
+ printk("VFS: iput: trying to free free inode\n");
+ printk("VFS: device %d/%d, inode %d, mode=0%07o\n",
+ MAJOR(inode->i_rdev), MINOR(inode->i_rdev),
+ inode->i_ino, inode->i_mode);
return;
}
if (inode->i_pipe) {
@@ -242,7 +244,7 @@ repeat:
}
}
if (!inode) {
- printk("No free inodes - contact Linus\n");
+ printk("VFS: No free inodes - contact Linus\n");
sleep_on(&inode_wait);
goto repeat;
}
@@ -275,8 +277,13 @@ struct inode * get_pipe_inode(void)
inode->i_count = 2; /* sum of readers/writers */
PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL;
PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;
+ PIPE_RD_OPENERS(*inode) = PIPE_WR_OPENERS(*inode) = 0;
PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1;
inode->i_pipe = 1;
+ inode->i_mode |= S_IFIFO;
+ inode->i_uid = current->euid;
+ inode->i_gid = current->egid;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
return inode;
}
@@ -285,7 +292,7 @@ struct inode * iget(struct super_block * sb,int nr)
struct inode * inode, * empty;
if (!sb)
- panic("iget with sb==NULL");
+ panic("VFS: iget with sb==NULL");
empty = get_empty_inode();
inode = inode_table;
while (inode < NR_INODE+inode_table) {
@@ -306,21 +313,34 @@ struct inode * iget(struct super_block * sb,int nr)
if (super_block[i].s_covered==inode)
break;
if (i >= NR_SUPER) {
- printk("Mounted inode hasn't got sb\n");
- if (empty)
+ printk("VFS: Mounted inode hasn't got sb\n");
+ if (empty) {
+ if (last_inode > inode_table)
+ --last_inode;
+ else
+ last_inode
+ = inode_table + NR_INODE;
iput(empty);
+ }
return inode;
}
iput(inode);
if (!(inode = super_block[i].s_mounted))
- printk("iget: mounted dev has no rootinode\n");
+ printk("VFS: Mounted device %d/%d has no rootinode\n",
+ MAJOR(inode->i_dev), MINOR(inode->i_dev));
else {
inode->i_count++;
wait_on_inode(inode);
}
}
- if (empty)
+ if (empty) {
+ if (last_inode > inode_table)
+ --last_inode;
+ else
+ last_inode
+ = inode_table + NR_INODE;
iput(empty);
+ }
return inode;
}
if (!empty)
diff --git a/fs/ioctl.c b/fs/ioctl.c
index 7e14598..e97140e 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -15,6 +15,7 @@
static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
+ int error;
int block;
switch (cmd) {
@@ -23,7 +24,9 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
return -EBADF;
if (filp->f_inode->i_op->bmap == NULL)
return -EINVAL;
- verify_area((void *) arg,4);
+ error = verify_area(VERIFY_WRITE,(void *) arg,4);
+ if (error)
+ return error;
block = get_fs_long((long *) arg);
block = filp->f_inode->i_op->bmap(filp->f_inode,block);
put_fs_long(block,(long *) arg);
@@ -31,12 +34,16 @@ static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
case FIGETBSZ:
if (filp->f_inode->i_sb == NULL)
return -EBADF;
- verify_area((void *) arg,4);
+ error = verify_area(VERIFY_WRITE,(void *) arg,4);
+ if (error)
+ return error;
put_fs_long(filp->f_inode->i_sb->s_blocksize,
(long *) arg);
return 0;
case FIONREAD:
- verify_area((void *) arg,4);
+ error = verify_area(VERIFY_WRITE,(void *) arg,4);
+ if (error)
+ return error;
put_fs_long(filp->f_inode->i_size - filp->f_pos,
(long *) arg);
return 0;
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index 233146e..ff86ea2 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -477,8 +477,8 @@ int isofs_lookup_grandparent(struct inode * parent, int extent) {
if (de->name_len[0] == 1 && de->name[0] == 1)
{
- brelse(bh);
parent_dir = find_rock_ridge_relocation(de, parent);
+ brelse(bh);
break;
};
}
diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c
index 1481bf2..4ba4262 100644
--- a/fs/isofs/namei.c
+++ b/fs/isofs/namei.c
@@ -45,7 +45,7 @@ static int isofs_match(int len,const char * name, char * compare, int dlen)
if (dlen != len)
return 0;
__asm__("cld\n\t"
- "fs ; repe ; cmpsb\n\t"
+ "repe ; cmpsb\n\t"
"setz %%al"
:"=a" (same)
:"0" (0),"S" ((long) name),"D" ((long) compare),"c" (len)
@@ -241,8 +241,8 @@ int isofs_lookup(struct inode * dir,const char * name, int len,
/* We need this backlink for the .. entry */
- if (ino_back) (*result)->u.isofs_i.i_backlink = ino_back;
-
+ if (ino_back && !(*result)->i_pipe)
+ (*result)->u.isofs_i.i_backlink = ino_back;
iput(dir);
return 0;
}
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index 09ffa0c..7bc95c6 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -60,14 +60,17 @@
if (offset >= 1024) block++; \
offset &= 1023; \
bh = bread(DEV, block, 1024); \
- memcpy(buffer, bh->b_data, cont_size); \
- brelse(bh); \
- chr = buffer; \
- len = cont_size; \
- cont_extent = 0; \
- cont_size = 0; \
- cont_offset = 0; \
- goto LABEL; \
+ if (bh) { \
+ memcpy(buffer, bh->b_data, cont_size); \
+ brelse(bh); \
+ chr = buffer; \
+ len = cont_size; \
+ cont_extent = 0; \
+ cont_size = 0; \
+ cont_offset = 0; \
+ goto LABEL; \
+ } \
+ printk("Unable to read rock-ridge descriptor block\n"); \
}}
diff --git a/fs/isofs/symlink.c b/fs/isofs/symlink.c
index b9002fe..03adf92 100644
--- a/fs/isofs/symlink.c
+++ b/fs/isofs/symlink.c
@@ -45,7 +45,6 @@ static int isofs_follow_link(struct inode * dir, struct inode * inode,
int flag, int mode, struct inode ** res_inode)
{
int error;
- unsigned short fs;
char * pnt;
if (!dir) {
@@ -62,7 +61,6 @@ static int isofs_follow_link(struct inode * dir, struct inode * inode,
*res_inode = inode;
return 0;
}
- __asm__("mov %%fs,%0":"=r" (fs));
if ((current->link_count > 5) ||
!(pnt = get_rock_ridge_symlink(inode))) {
iput(dir);
@@ -71,11 +69,9 @@ static int isofs_follow_link(struct inode * dir, struct inode * inode,
return -ELOOP;
}
iput(inode);
- __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
current->link_count++;
error = open_namei(pnt,flag,mode,res_inode,dir);
current->link_count--;
- __asm__("mov %0,%%fs"::"r" (fs));
kfree(pnt);
return error;
}
diff --git a/fs/locks.c b/fs/locks.c
index efd9641..fc588a0 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -49,14 +49,17 @@ void fcntl_init_locks(void)
}
int fcntl_getlk(unsigned int fd, struct flock *l)
-{
+{
+ int error;
struct flock flock;
struct file *filp;
struct file_lock *fl,file_lock;
if (fd >= NR_OPEN || !(filp = current->filp[fd]))
return -EBADF;
- verify_area(l, sizeof(*l));
+ error = verify_area(VERIFY_WRITE,l, sizeof(*l));
+ if (error)
+ return error;
memcpy_fromfs(&flock, l, sizeof(flock));
if (flock.l_type == F_UNLCK)
return -EINVAL;
@@ -86,7 +89,8 @@ int fcntl_getlk(unsigned int fd, struct flock *l)
*/
int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
-{
+{
+ int error;
struct file *filp;
struct file_lock *fl,file_lock;
struct flock flock;
@@ -97,7 +101,9 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
if (fd >= NR_OPEN || !(filp = current->filp[fd]))
return -EBADF;
- verify_area(l, sizeof(*l));
+ error = verify_area(VERIFY_WRITE, l, sizeof(*l));
+ if (error)
+ return error;
memcpy_fromfs(&flock, l, sizeof(flock));
if (!copy_flock(filp, &file_lock, &flock))
return -EINVAL;
@@ -145,7 +151,11 @@ repeat:
* FIXME: We need to check for deadlocks here.
*/
if (cmd == F_SETLKW) {
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
interruptible_sleep_on(&fl->fl_wait);
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
goto repeat;
}
return -EAGAIN;
@@ -330,7 +340,7 @@ static int lock_it(struct file *filp, struct file_lock *caller)
fl->fl_end = caller->fl_start - 1;
/* must continue, may be more overlaps */
} else {
- printk("lock_it: program bug: unanticipated overlap\n");
+ printk("VFS: lock_it: program bug: unanticipated overlap\n");
free_lock(filp, caller);
return -ENOLCK;
}
@@ -431,7 +441,7 @@ static struct file_lock *alloc_lock(struct file *filp, struct file_lock *templat
if (file_lock_free_list == NULL)
return NULL; /* no available entry */
if (file_lock_free_list->fl_owner != NULL)
- panic("alloc_lock: broken free list\n");
+ panic("VFS: alloc_lock: broken free list\n");
new = file_lock_free_list; /* remove from free list */
file_lock_free_list = file_lock_free_list->fl_next;
@@ -457,7 +467,7 @@ static void free_lock(struct file *filp, struct file_lock *fl)
struct file_lock **fl_p;
if (fl->fl_owner == NULL) /* sanity check */
- panic("free_lock: broken lock list\n");
+ panic("VFS: free_lock: broken lock list\n");
/*
* We only use a singly linked list to save some memory space
@@ -469,7 +479,7 @@ static void free_lock(struct file *filp, struct file_lock *fl)
break;
}
if (*fl_p == NULL) {
- printk("free_lock: lock is not in file's lock list\n");
+ printk("VFS: free_lock: lock is not in file's lock list\n");
} else {
*fl_p = (*fl_p)->fl_next;
}
diff --git a/fs/minix/dir.c b/fs/minix/dir.c
index b2535ba..8c678e9 100644
--- a/fs/minix/dir.c
+++ b/fs/minix/dir.c
@@ -61,10 +61,12 @@ static int minix_readdir(struct inode * inode, struct file * filp,
char c;
struct buffer_head * bh;
struct minix_dir_entry * de;
+ struct minix_sb_info * info;
- if (!inode || !S_ISDIR(inode->i_mode))
+ if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode))
return -EBADF;
- if (filp->f_pos & (sizeof (struct minix_dir_entry) - 1))
+ info = &inode->i_sb->u.minix_sb;
+ if (filp->f_pos & (info->s_dirsize - 1))
return -EBADF;
while (filp->f_pos < inode->i_size) {
offset = filp->f_pos & 1023;
@@ -73,12 +75,12 @@ static int minix_readdir(struct inode * inode, struct file * filp,
filp->f_pos += 1024-offset;
continue;
}
- de = (struct minix_dir_entry *) (offset + bh->b_data);
while (offset < 1024 && filp->f_pos < inode->i_size) {
- offset += sizeof (struct minix_dir_entry);
- filp->f_pos += sizeof (struct minix_dir_entry);
+ de = (struct minix_dir_entry *) (offset + bh->b_data);
+ offset += info->s_dirsize;
+ filp->f_pos += info->s_dirsize;
if (de->inode) {
- for (i = 0; i < MINIX_NAME_LEN; i++)
+ for (i = 0; i < info->s_namelen; i++)
if ((c = de->name[i]) != 0)
put_fs_byte(c,i+dirent->d_name);
else
@@ -91,7 +93,6 @@ static int minix_readdir(struct inode * inode, struct file * filp,
return i;
}
}
- de++;
}
brelse(bh);
}
diff --git a/fs/minix/file.c b/fs/minix/file.c
index 6f70ac6..e78e89e 100644
--- a/fs/minix/file.c
+++ b/fs/minix/file.c
@@ -241,8 +241,7 @@ static int minix_file_write(struct inode * inode, struct file * filp, char * buf
bh->b_dirt = 1;
brelse(bh);
}
- inode->i_mtime = CURRENT_TIME;
- inode->i_ctime = CURRENT_TIME;
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
filp->f_pos = pos;
inode->i_dirt = 1;
return written;
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
index 76d4bc4..a019314 100644
--- a/fs/minix/inode.c
+++ b/fs/minix/inode.c
@@ -74,7 +74,13 @@ struct super_block *minix_read_super(struct super_block *s,void *data)
s->u.minix_sb.s_max_size = ms->s_max_size;
s->s_magic = ms->s_magic;
brelse(bh);
- if (s->s_magic != MINIX_SUPER_MAGIC) {
+ if (s->s_magic == MINIX_SUPER_MAGIC) {
+ s->u.minix_sb.s_dirsize = 16;
+ s->u.minix_sb.s_namelen = 14;
+ } else if (s->s_magic == MINIX_SUPER_MAGIC2) {
+ s->u.minix_sb.s_dirsize = 32;
+ s->u.minix_sb.s_namelen = 30;
+ } else {
s->s_dev = 0;
unlock_super(s);
printk("MINIX-fs magic match failed\n");
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
index 5af220e..a902655 100644
--- a/fs/minix/namei.c
+++ b/fs/minix/namei.c
@@ -27,19 +27,24 @@
*
* NOTE! unlike strncmp, minix_match returns 1 for success, 0 for failure.
*/
-static int minix_match(int len,const char * name,struct minix_dir_entry * de)
+static int minix_match(int len, const char * name,
+ struct buffer_head * bh, unsigned long * offset,
+ struct minix_sb_info * info)
{
+ struct minix_dir_entry * de;
register int same __asm__("ax");
- if (!de || !de->inode || len > MINIX_NAME_LEN)
+ de = (struct minix_dir_entry *) (bh->b_data + *offset);
+ *offset += info->s_dirsize;
+ if (!de->inode || len > info->s_namelen)
return 0;
/* "" means "." ---> so paths like "/usr/lib//libc.a" work */
if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
return 1;
- if (len < MINIX_NAME_LEN && de->name[len])
+ if (len < info->s_namelen && de->name[len])
return 0;
__asm__("cld\n\t"
- "fs ; repe ; cmpsb\n\t"
+ "repe ; cmpsb\n\t"
"setz %%al"
:"=a" (same)
:"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
@@ -58,44 +63,43 @@ static int minix_match(int len,const char * name,struct minix_dir_entry * de)
static struct buffer_head * minix_find_entry(struct inode * dir,
const char * name, int namelen, struct minix_dir_entry ** res_dir)
{
- int entries, i;
+ unsigned long block, offset;
struct buffer_head * bh;
- struct minix_dir_entry * de;
+ struct minix_sb_info * info;
*res_dir = NULL;
- if (!dir)
+ if (!dir || !dir->i_sb)
return NULL;
+ info = &dir->i_sb->u.minix_sb;
+ if (namelen > info->s_namelen) {
#ifdef NO_TRUNCATE
- if (namelen > MINIX_NAME_LEN)
return NULL;
#else
- if (namelen > MINIX_NAME_LEN)
- namelen = MINIX_NAME_LEN;
+ namelen = info->s_namelen;
#endif
- entries = dir->i_size / (sizeof (struct minix_dir_entry));
- bh = minix_bread(dir,0,0);
- if (!bh)
- return NULL;
- i = 0;
- de = (struct minix_dir_entry *) bh->b_data;
- while (i < entries) {
- if ((char *)de >= BLOCK_SIZE+bh->b_data) {
- brelse(bh);
- bh = minix_bread(dir,i/MINIX_DIR_ENTRIES_PER_BLOCK,0);
+ }
+ bh = NULL;
+ block = offset = 0;
+ while (block*BLOCK_SIZE+offset < dir->i_size) {
+ if (!bh) {
+ bh = minix_bread(dir,block,0);
if (!bh) {
- i += MINIX_DIR_ENTRIES_PER_BLOCK;
+ block++;
continue;
}
- de = (struct minix_dir_entry *) bh->b_data;
}
- if (minix_match(namelen,name,de)) {
- *res_dir = de;
+ *res_dir = (struct minix_dir_entry *) (bh->b_data + offset);
+ if (minix_match(namelen,name,bh,&offset,info))
return bh;
- }
- de++;
- i++;
+ if (offset < bh->b_size)
+ continue;
+ brelse(bh);
+ bh = NULL;
+ offset = 0;
+ block++;
}
brelse(bh);
+ *res_dir = NULL;
return NULL;
}
@@ -136,63 +140,61 @@ int minix_lookup(struct inode * dir,const char * name, int len,
* NOTE!! The inode part of 'de' is left at 0 - which means you
* may not sleep between calling this and putting something into
* the entry, as someone else might have used it while you slept.
- *
- * Arggh. To avoid race-conditions, we copy the name into kernel
- * space before actually writing it into the buffer...
*/
static struct buffer_head * minix_add_entry(struct inode * dir,
const char * name, int namelen, struct minix_dir_entry ** res_dir)
{
int i;
+ unsigned long block, offset;
struct buffer_head * bh;
struct minix_dir_entry * de;
- char name_buffer[MINIX_NAME_LEN];
+ struct minix_sb_info * info;
*res_dir = NULL;
- if (!dir)
+ if (!dir || !dir->i_sb)
return NULL;
- if (namelen > MINIX_NAME_LEN) {
+ info = &dir->i_sb->u.minix_sb;
+ if (namelen > info->s_namelen) {
#ifdef NO_TRUNCATE
return NULL;
#else
- namelen = MINIX_NAME_LEN;
+ namelen = info->s_namelen;
#endif
}
if (!namelen)
return NULL;
- bh = minix_bread(dir,0,0);
- if (!bh)
- return NULL;
- for (i = 0; i < MINIX_NAME_LEN ; i++)
- name_buffer[i] = (i<namelen) ? get_fs_byte(name+i) : 0;
- i = 0;
- de = (struct minix_dir_entry *) bh->b_data;
+ bh = NULL;
+ block = offset = 0;
while (1) {
- if ((char *)de >= BLOCK_SIZE+bh->b_data) {
- brelse(bh);
- bh = minix_bread(dir,i/MINIX_DIR_ENTRIES_PER_BLOCK,1);
+ if (!bh) {
+ bh = minix_bread(dir,block,1);
if (!bh)
return NULL;
- de = (struct minix_dir_entry *) bh->b_data;
}
- if (i*sizeof(struct minix_dir_entry) >= dir->i_size) {
- de->inode=0;
- dir->i_size = (i+1)*sizeof(struct minix_dir_entry);
+ de = (struct minix_dir_entry *) (bh->b_data + offset);
+ offset += info->s_dirsize;
+ if (block*bh->b_size + offset > dir->i_size) {
+ de->inode = 0;
+ dir->i_size = block*bh->b_size + offset;
dir->i_dirt = 1;
dir->i_ctime = CURRENT_TIME;
}
if (!de->inode) {
- dir->i_mtime = CURRENT_TIME;
- memcpy(de->name,name_buffer,MINIX_NAME_LEN);
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ for (i = 0; i < info->s_namelen ; i++)
+ de->name[i] = (i < namelen) ? name[i] : 0;
bh->b_dirt = 1;
*res_dir = de;
- return bh;
+ break;
}
- de++;
- i++;
+ if (offset < bh->b_size)
+ continue;
+ brelse(bh);
+ bh = NULL;
+ offset = 0;
+ block++;
}
- brelse(bh);
- return NULL;
+ return bh;
}
int minix_create(struct inode * dir,const char * name, int len, int mode,
@@ -297,14 +299,20 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode)
struct inode * inode;
struct buffer_head * bh, *dir_block;
struct minix_dir_entry * de;
-
+ struct minix_sb_info * info;
+
+ if (!dir || !dir->i_sb) {
+ iput(dir);
+ return -EINVAL;
+ }
+ info = &dir->i_sb->u.minix_sb;
bh = minix_find_entry(dir,name,len,&de);
if (bh) {
brelse(bh);
iput(dir);
return -EEXIST;
}
- if (dir->i_nlink > 250) {
+ if (dir->i_nlink >= MINIX_LINK_MAX) {
iput(dir);
return -EMLINK;
}
@@ -314,7 +322,7 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode)
return -ENOSPC;
}
inode->i_op = &minix_dir_inode_operations;
- inode->i_size = 2 * sizeof (struct minix_dir_entry);
+ inode->i_size = 2 * info->s_dirsize;
inode->i_mtime = inode->i_atime = CURRENT_TIME;
dir_block = minix_bread(inode,0,1);
if (!dir_block) {
@@ -327,7 +335,7 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode)
de = (struct minix_dir_entry *) dir_block->b_data;
de->inode=inode->i_ino;
strcpy(de->name,".");
- de++;
+ de = (struct minix_dir_entry *) (dir_block->b_data + info->s_dirsize);
de->inode = dir->i_ino;
strcpy(de->name,"..");
inode->i_nlink = 2;
@@ -359,42 +367,56 @@ int minix_mkdir(struct inode * dir, const char * name, int len, int mode)
*/
static int empty_dir(struct inode * inode)
{
- int nr, len;
+ unsigned int block, offset;
struct buffer_head * bh;
struct minix_dir_entry * de;
+ struct minix_sb_info * info;
- len = inode->i_size / sizeof (struct minix_dir_entry);
- if (len<2 || !(bh = minix_bread(inode,0,0))) {
- printk("warning - bad directory on dev %04x\n",inode->i_dev);
+ if (!inode || !inode->i_sb)
return 1;
- }
+ info = &inode->i_sb->u.minix_sb;
+ block = 0;
+ offset = 2*info->s_dirsize;
+ if (inode->i_size & (info->s_dirsize-1))
+ goto bad_dir;
+ if (inode->i_size < offset)
+ goto bad_dir;
+ bh = minix_bread(inode,0,0);
+ if (!bh)
+ goto bad_dir;
de = (struct minix_dir_entry *) bh->b_data;
- if (de[0].inode != inode->i_ino || !de[1].inode ||
- strcmp(".",de[0].name) || strcmp("..",de[1].name)) {
- printk("warning - bad directory on dev %04x\n",inode->i_dev);
- return 1;
- }
- nr = 2;
- de += 2;
- while (nr<len) {
- if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) {
- brelse(bh);
- bh = minix_bread(inode,nr/MINIX_DIR_ENTRIES_PER_BLOCK,0);
+ if (!de->inode || strcmp(de->name,"."))
+ goto bad_dir;
+ de = (struct minix_dir_entry *) (bh->b_data + info->s_dirsize);
+ if (!de->inode || strcmp(de->name,".."))
+ goto bad_dir;
+ while (block*BLOCK_SIZE+offset < inode->i_size) {
+ if (!bh) {
+ bh = minix_bread(inode,block,0);
if (!bh) {
- nr += MINIX_DIR_ENTRIES_PER_BLOCK;
+ block++;
continue;
}
- de = (struct minix_dir_entry *) bh->b_data;
}
+ de = (struct minix_dir_entry *) (bh->b_data + offset);
+ offset += info->s_dirsize;
if (de->inode) {
brelse(bh);
return 0;
}
- de++;
- nr++;
+ if (offset < bh->b_size)
+ continue;
+ brelse(bh);
+ bh = NULL;
+ offset = 0;
+ block++;
}
brelse(bh);
return 1;
+bad_dir:
+ brelse(bh);
+ printk("Bad directory on device %04x\n",inode->i_dev);
+ return 1;
}
int minix_rmdir(struct inode * dir, const char * name, int len)
@@ -520,7 +542,7 @@ int minix_symlink(struct inode * dir, const char * name, int len, const char * s
return -ENOSPC;
}
i = 0;
- while (i < 1023 && (c=get_fs_byte(symname++)))
+ while (i < 1023 && (c=*(symname++)))
name_block->b_data[i++] = c;
name_block->b_data[i] = 0;
name_block->b_dirt = 1;
@@ -562,7 +584,7 @@ int minix_link(struct inode * oldinode, struct inode * dir, const char * name, i
iput(dir);
return -EPERM;
}
- if (oldinode->i_nlink > 126) {
+ if (oldinode->i_nlink >= MINIX_LINK_MAX) {
iput(oldinode);
iput(dir);
return -EMLINK;
@@ -593,12 +615,9 @@ int minix_link(struct inode * oldinode, struct inode * dir, const char * name, i
static int subdir(struct inode * new, struct inode * old)
{
- unsigned short fs;
int ino;
int result;
- __asm__("mov %%fs,%0":"=r" (fs));
- __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
new->i_count++;
result = 0;
for (;;) {
@@ -615,15 +634,11 @@ static int subdir(struct inode * new, struct inode * old)
break;
}
iput(new);
- __asm__("mov %0,%%fs"::"r" (fs));
return result;
}
#define PARENT_INO(buffer) \
-(((struct minix_dir_entry *) (buffer))[1].inode)
-
-#define PARENT_NAME(buffer) \
-(((struct minix_dir_entry *) (buffer))[1].name)
+(((struct minix_dir_entry *) ((buffer)+info->s_dirsize))->inode)
/*
* rename uses retrying to avoid race-conditions: at least they should be minimal.
@@ -641,8 +656,10 @@ static int do_minix_rename(struct inode * old_dir, const char * old_name, int ol
struct inode * old_inode, * new_inode;
struct buffer_head * old_bh, * new_bh, * dir_bh;
struct minix_dir_entry * old_de, * new_de;
+ struct minix_sb_info * info;
int retval;
+ info = &old_dir->i_sb->u.minix_sb;
goto start_up;
try_again:
brelse(old_bh);
@@ -680,8 +697,18 @@ start_up:
goto end_rename;
}
if (new_inode && S_ISDIR(new_inode->i_mode)) {
- retval = -EEXIST;
- goto end_rename;
+ retval = -EISDIR;
+ if (!S_ISDIR(old_inode->i_mode))
+ goto end_rename;
+ retval = -EINVAL;
+ if (subdir(new_dir, old_inode))
+ goto end_rename;
+ retval = -ENOTEMPTY;
+ if (!empty_dir(new_inode))
+ goto end_rename;
+ retval = -EBUSY;
+ if (new_inode->i_count > 1)
+ goto end_rename;
}
retval = -EPERM;
if (new_inode && (new_dir->i_mode & S_ISVTX) &&
@@ -689,9 +716,6 @@ start_up:
current->euid != new_dir->i_uid && !suser())
goto end_rename;
if (S_ISDIR(old_inode->i_mode)) {
- retval = -EEXIST;
- if (new_bh)
- goto end_rename;
retval = -EACCES;
if (!permission(old_inode, MAY_WRITE))
goto end_rename;
@@ -705,7 +729,7 @@ start_up:
if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
goto end_rename;
retval = -EMLINK;
- if (new_dir->i_nlink > 250)
+ if (new_dir->i_nlink >= MINIX_LINK_MAX)
goto end_rename;
}
if (!new_bh)
@@ -733,9 +757,14 @@ start_up:
PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
dir_bh->b_dirt = 1;
old_dir->i_nlink--;
- new_dir->i_nlink++;
old_dir->i_dirt = 1;
- new_dir->i_dirt = 1;
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_dirt = 1;
+ } else {
+ new_dir->i_nlink++;
+ new_dir->i_dirt = 1;
+ }
}
retval = 0;
end_rename:
diff --git a/fs/minix/symlink.c b/fs/minix/symlink.c
index c59e95c..a3310ae 100644
--- a/fs/minix/symlink.c
+++ b/fs/minix/symlink.c
@@ -42,7 +42,6 @@ static int minix_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;
@@ -70,12 +69,9 @@ static int minix_follow_link(struct inode * dir, struct inode * inode,
return -EIO;
}
iput(inode);
- __asm__("mov %%fs,%0":"=r" (fs));
- __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
current->link_count++;
error = open_namei(bh->b_data,flag,mode,res_inode,dir);
current->link_count--;
- __asm__("mov %0,%%fs"::"r" (fs));
brelse(bh);
return error;
}
diff --git a/fs/minix/truncate.c b/fs/minix/truncate.c
index 29f4c6d..0c0b0c6 100644
--- a/fs/minix/truncate.c
+++ b/fs/minix/truncate.c
@@ -37,7 +37,7 @@ repeat:
p = i + inode->u.minix_i.i_data;
if (!(tmp = *p))
continue;
- bh = getblk(inode->i_dev,tmp,BLOCK_SIZE);
+ bh = get_hash_table(inode->i_dev,tmp,BLOCK_SIZE);
if (i < DIRECT_BLOCK) {
brelse(bh);
goto repeat;
@@ -86,7 +86,7 @@ repeat:
tmp = *ind;
if (!tmp)
continue;
- bh = getblk(inode->i_dev,tmp,BLOCK_SIZE);
+ bh = get_hash_table(inode->i_dev,tmp,BLOCK_SIZE);
if (i < INDIRECT_BLOCK) {
brelse(bh);
goto repeat;
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c
index e38a92c..79988a4 100644
--- a/fs/msdos/namei.c
+++ b/fs/msdos/namei.c
@@ -36,9 +36,9 @@ static int msdos_format_name(char conv,const char *name,int len,char *res)
char c;
int space;
- if (get_fs_byte(name) == DELETED_FLAG) return -EINVAL;
- if (get_fs_byte(name) == '.' && (len == 1 || (len == 2 &&
- get_fs_byte(name+1) == '.'))) {
+ if (*(unsigned char *)name == DELETED_FLAG) return -EINVAL;
+ if (name[0] == '.' && (len == 1 || (len == 2 &&
+ name[1] == '.'))) {
memset(res+1,' ',10);
while (len--) *res++ = '.';
return 0;
@@ -46,7 +46,7 @@ static int msdos_format_name(char conv,const char *name,int len,char *res)
space = 0; /* to make GCC happy */
c = 0;
for (walk = res; len && walk-res < 8; walk++) {
- c = get_fs_byte(name++);
+ c = *(name++);
len--;
if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
@@ -61,16 +61,16 @@ static int msdos_format_name(char conv,const char *name,int len,char *res)
}
if (space) return -EINVAL;
if (conv == 's' && len && c != '.') {
- c = get_fs_byte(name++);
+ c = *(name++);
len--;
if (c != '.') return -EINVAL;
}
- while (c != '.' && len--) c = get_fs_byte(name++);
+ while (c != '.' && len--) c = *(name++);
if (walk == res) return -EINVAL;
if (c == '.') {
while (walk-res < 8) *walk++ = ' ';
while (len > 0 && walk-res < MSDOS_NAME) {
- c = get_fs_byte(name++);
+ c = *(name++);
len--;
if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
if (conv == 's' && strchr(bad_if_strict,c))
@@ -122,11 +122,11 @@ int msdos_lookup(struct inode *dir,const char *name,int len,
iput(dir);
return -ENOENT;
}
- if (len == 1 && get_fs_byte(name) == '.') {
+ if (len == 1 && name[0] == '.') {
*result = dir;
return 0;
}
- if (len == 2 && get_fs_byte(name) == '.' && get_fs_byte(name+1) == '.')
+ if (len == 2 && name[0] == '.' && name[1] == '.')
{
ino = msdos_parent_ino(dir,0);
iput(dir);
@@ -139,7 +139,7 @@ int msdos_lookup(struct inode *dir,const char *name,int len,
return res;
}
if (bh) brelse(bh);
-/* printk("lookup: ino=%d\r\n",ino); */
+/* printk("lookup: ino=%d\n",ino); */
if (!(*result = iget(dir->i_sb,ino))) {
iput(dir);
return -EACCES;
@@ -305,8 +305,8 @@ int msdos_rmdir(struct inode *dir,const char *name,int len)
bh = NULL;
inode = NULL;
res = -EINVAL;
- if (get_fs_byte(name) == '.' && (len == 1 || (len == 2 &&
- get_fs_byte(name+1) == '.'))) goto rmdir_done;
+ 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;
diff --git a/fs/namei.c b/fs/namei.c
index 8c6956c..d615e4b 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -26,6 +26,52 @@
/* #define NO_TRUNCATE */
/*
+ * In order to reduce some races, while at the same time doing additional
+ * checking and hopefully speeding things up, we copy filenames to the
+ * kernel data space before using them..
+ *
+ * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
+ */
+int getname(const char * filename, char **result)
+{
+ int error;
+ unsigned long i, page;
+ char * tmp, c;
+
+ i = (unsigned long) filename;
+ if (!i || i >= TASK_SIZE)
+ return -EFAULT;
+ i = TASK_SIZE - i;
+ error = -EFAULT;
+ if (i > PAGE_SIZE) {
+ i = PAGE_SIZE;
+ error = -ENAMETOOLONG;
+ }
+ c = get_fs_byte(filename++);
+ if (!c)
+ return -ENOENT;
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ *result = tmp = (char *) page;
+ while (--i) {
+ *(tmp++) = c;
+ c = get_fs_byte(filename++);
+ if (!c) {
+ *tmp = '\0';
+ return 0;
+ }
+ }
+ free_page(page);
+ return error;
+}
+
+void putname(char * name)
+{
+ free_page((unsigned long) name);
+}
+
+/*
* permission()
*
* is used to check for read/write/execute permissions on a file.
@@ -61,10 +107,11 @@ int lookup(struct inode * dir,const char * name, int len,
struct super_block * sb;
*result = NULL;
- if (len==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') {
- if (dir == current->root)
- len = 1;
- else if ((sb = dir->i_sb) && (dir == sb->s_mounted)) {
+ if (len==2 && name[0] == '.' && name[1] == '.') {
+ if (dir == current->root) {
+ *result = dir;
+ return 0;
+ } else if ((sb = dir->i_sb) && (dir == sb->s_mounted)) {
sb = dir->i_sb;
iput(dir);
dir = sb->s_covered;
@@ -125,7 +172,7 @@ static int dir_namei(const char * pathname, int * namelen, const char ** name,
base = current->pwd;
base->i_count++;
}
- if ((c=get_fs_byte(pathname))=='/') {
+ if ((c = *pathname) == '/') {
iput(base);
base = current->root;
pathname++;
@@ -133,12 +180,14 @@ static int dir_namei(const char * pathname, int * namelen, const char ** name,
}
while (1) {
thisname = pathname;
- for(len=0;(c=get_fs_byte(pathname++))&&(c!='/');len++)
+ for(len=0;(c = *(pathname++))&&(c != '/');len++)
/* nothing */ ;
if (!c)
break;
base->i_count++;
+ ((char *) thisname)[len] = '\0'; /* fake string.. */
error = lookup(base,thisname,len,&inode);
+ ((char *) thisname)[len] = c;
if (error) {
iput(base);
return error;
@@ -186,7 +235,15 @@ static int _namei(const char * pathname, struct inode * base,
int lnamei(const char * pathname, struct inode ** res_inode)
{
- return _namei(pathname,NULL,0,res_inode);
+ int error;
+ char * tmp;
+
+ error = getname(pathname,&tmp);
+ if (!error) {
+ error = _namei(tmp,NULL,0,res_inode);
+ putname(tmp);
+ }
+ return error;
}
/*
@@ -198,7 +255,15 @@ int lnamei(const char * pathname, struct inode ** res_inode)
*/
int namei(const char * pathname, struct inode ** res_inode)
{
- return _namei(pathname,NULL,1,res_inode);
+ int error;
+ char * tmp;
+
+ error = getname(pathname,&tmp);
+ if (!error) {
+ error = _namei(tmp,NULL,1,res_inode);
+ putname(tmp);
+ }
+ return error;
}
/*
@@ -247,7 +312,7 @@ int open_namei(const char * pathname, int flag, int mode,
iput(dir);
return error;
}
- if (!permission(dir,MAY_WRITE)) {
+ if (!permission(dir,MAY_WRITE | MAY_EXEC)) {
iput(dir);
return -EACCES;
}
@@ -261,7 +326,7 @@ int open_namei(const char * pathname, int flag, int mode,
}
return dir->i_op->create(dir,basename,namelen,mode,res_inode);
}
- if (flag & O_EXCL) {
+ if ((flag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
iput(dir);
iput(inode);
return -EEXIST;
@@ -312,6 +377,7 @@ int do_mknod(const char * filename, int mode, dev_t dev)
int namelen, error;
struct inode * dir;
+ mode &= ~current->umask;
error = dir_namei(filename,&namelen,&basename, NULL, &dir);
if (error)
return error;
@@ -323,7 +389,7 @@ int do_mknod(const char * filename, int mode, dev_t dev)
iput(dir);
return -EROFS;
}
- if (!permission(dir,MAY_WRITE)) {
+ if (!permission(dir,MAY_WRITE | MAY_EXEC)) {
iput(dir);
return -EACCES;
}
@@ -336,12 +402,20 @@ int do_mknod(const char * filename, int mode, dev_t dev)
int sys_mknod(const char * filename, int mode, dev_t dev)
{
- if (S_ISFIFO(mode) || suser())
- return do_mknod(filename,mode,dev);
- return -EPERM;
+ int error;
+ char * tmp;
+
+ if (S_ISDIR(mode) || (!S_ISFIFO(mode) && !suser()))
+ return -EPERM;
+ error = getname(filename,&tmp);
+ if (!error) {
+ error = do_mknod(tmp,mode,dev);
+ putname(tmp);
+ }
+ return error;
}
-int sys_mkdir(const char * pathname, int mode)
+static int do_mkdir(const char * pathname, int mode)
{
const char * basename;
int namelen, error;
@@ -358,7 +432,7 @@ int sys_mkdir(const char * pathname, int mode)
iput(dir);
return -EROFS;
}
- if (!permission(dir,MAY_WRITE)) {
+ if (!permission(dir,MAY_WRITE | MAY_EXEC)) {
iput(dir);
return -EACCES;
}
@@ -369,7 +443,20 @@ int sys_mkdir(const char * pathname, int mode)
return dir->i_op->mkdir(dir,basename,namelen,mode);
}
-int sys_rmdir(const char * name)
+int sys_mkdir(const char * pathname, int mode)
+{
+ int error;
+ char * tmp;
+
+ error = getname(pathname,&tmp);
+ if (!error) {
+ error = do_mkdir(tmp,mode);
+ putname(tmp);
+ }
+ return error;
+}
+
+static int do_rmdir(const char * name)
{
const char * basename;
int namelen, error;
@@ -397,7 +484,20 @@ int sys_rmdir(const char * name)
return dir->i_op->rmdir(dir,basename,namelen);
}
-int sys_unlink(const char * name)
+int sys_rmdir(const char * pathname)
+{
+ int error;
+ char * tmp;
+
+ error = getname(pathname,&tmp);
+ if (!error) {
+ error = do_rmdir(tmp);
+ putname(tmp);
+ }
+ return error;
+}
+
+static int do_unlink(const char * name)
{
const char * basename;
int namelen, error;
@@ -425,7 +525,20 @@ int sys_unlink(const char * name)
return dir->i_op->unlink(dir,basename,namelen);
}
-int sys_symlink(const char * oldname, const char * newname)
+int sys_unlink(const char * pathname)
+{
+ int error;
+ char * tmp;
+
+ error = getname(pathname,&tmp);
+ if (!error) {
+ error = do_unlink(tmp);
+ putname(tmp);
+ }
+ return error;
+}
+
+static int do_symlink(const char * oldname, const char * newname)
{
struct inode * dir;
const char * basename;
@@ -442,7 +555,7 @@ int sys_symlink(const char * oldname, const char * newname)
iput(dir);
return -EROFS;
}
- if (!permission(dir,MAY_WRITE)) {
+ if (!permission(dir,MAY_WRITE | MAY_EXEC)) {
iput(dir);
return -EACCES;
}
@@ -453,15 +566,29 @@ int sys_symlink(const char * oldname, const char * newname)
return dir->i_op->symlink(dir,basename,namelen,oldname);
}
-int sys_link(const char * oldname, const char * newname)
+int sys_symlink(const char * oldname, const char * newname)
{
- struct inode * oldinode, * dir;
+ int error;
+ char * old, * new;
+
+ error = getname(oldname,&old);
+ if (!error) {
+ error = getname(newname,&new);
+ if (!error) {
+ error = do_symlink(old,new);
+ putname(new);
+ }
+ putname(old);
+ }
+ return error;
+}
+
+static int do_link(struct inode * oldinode, const char * newname)
+{
+ struct inode * dir;
const char * basename;
int namelen, error;
- error = namei(oldname, &oldinode);
- if (error)
- return error;
error = dir_namei(newname,&namelen,&basename,NULL,&dir);
if (error) {
iput(oldinode);
@@ -482,7 +609,7 @@ int sys_link(const char * oldname, const char * newname)
iput(oldinode);
return -EXDEV;
}
- if (!permission(dir,MAY_WRITE)) {
+ if (!permission(dir,MAY_WRITE | MAY_EXEC)) {
iput(dir);
iput(oldinode);
return -EACCES;
@@ -495,7 +622,24 @@ int sys_link(const char * oldname, const char * newname)
return dir->i_op->link(oldinode, dir, basename, namelen);
}
-int sys_rename(const char * oldname, const char * newname)
+int sys_link(const char * oldname, const char * newname)
+{
+ int error;
+ char * new;
+ struct inode * oldinode;
+
+ error = namei(oldname, &oldinode);
+ if (error)
+ return error;
+ error = getname(newname,&new);
+ if (!error) {
+ error = do_link(oldinode,new);
+ putname(new);
+ }
+ return error;
+}
+
+static int do_rename(const char * oldname, const char * newname)
{
struct inode * old_dir, * new_dir;
const char * old_base, * new_base;
@@ -504,12 +648,12 @@ int sys_rename(const char * oldname, const char * newname)
error = dir_namei(oldname,&old_len,&old_base,NULL,&old_dir);
if (error)
return error;
- if (!permission(old_dir,MAY_WRITE)) {
+ if (!permission(old_dir,MAY_WRITE | MAY_EXEC)) {
iput(old_dir);
return -EACCES;
}
- if (!old_len || (get_fs_byte(old_base) == '.' &&
- (old_len == 1 || (get_fs_byte(old_base+1) == '.' &&
+ if (!old_len || (old_base[0] == '.' &&
+ (old_len == 1 || (old_base[1] == '.' &&
old_len == 2)))) {
iput(old_dir);
return -EPERM;
@@ -519,13 +663,13 @@ int sys_rename(const char * oldname, const char * newname)
iput(old_dir);
return error;
}
- if (!permission(new_dir,MAY_WRITE)) {
+ if (!permission(new_dir,MAY_WRITE | MAY_EXEC)) {
iput(old_dir);
iput(new_dir);
return -EACCES;
}
- if (!new_len || (get_fs_byte(new_base) == '.' &&
- (new_len == 1 || (get_fs_byte(new_base+1) == '.' &&
+ if (!new_len || (new_base[0] == '.' &&
+ (new_len == 1 || (new_base[1] == '.' &&
new_len == 2)))) {
iput(old_dir);
iput(new_dir);
@@ -549,3 +693,20 @@ int sys_rename(const char * oldname, const char * newname)
return old_dir->i_op->rename(old_dir, old_base, old_len,
new_dir, new_base, new_len);
}
+
+int sys_rename(const char * oldname, const char * newname)
+{
+ int error;
+ char * old, * new;
+
+ error = getname(oldname,&old);
+ if (!error) {
+ error = getname(newname,&new);
+ if (!error) {
+ error = do_rename(old,new);
+ putname(new);
+ }
+ putname(old);
+ }
+ return error;
+}
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 0ea79ad..81f3fd8 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -180,7 +180,7 @@ static struct nfs_lookup_cache_entry {
} nfs_lookup_cache[NFS_LOOKUP_CACHE_SIZE];
static struct nfs_lookup_cache_entry *nfs_lookup_cache_index(struct inode *dir,
- char *filename)
+ const char *filename)
{
struct nfs_lookup_cache_entry *entry;
int i;
@@ -194,7 +194,7 @@ static struct nfs_lookup_cache_entry *nfs_lookup_cache_index(struct inode *dir,
return NULL;
}
-static int nfs_lookup_cache_lookup(struct inode *dir, char *filename,
+static int nfs_lookup_cache_lookup(struct inode *dir, const char *filename,
struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
@@ -218,7 +218,7 @@ static int nfs_lookup_cache_lookup(struct inode *dir, char *filename,
return 0;
}
-static void nfs_lookup_cache_add(struct inode *dir, char *filename,
+static void nfs_lookup_cache_add(struct inode *dir, const char *filename,
struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
@@ -244,7 +244,7 @@ static void nfs_lookup_cache_add(struct inode *dir, char *filename,
}
static void nfs_lookup_cache_remove(struct inode *dir, struct inode *inode,
- char *filename)
+ const char *filename)
{
struct nfs_lookup_cache_entry *entry;
int dev;
@@ -286,7 +286,6 @@ static void nfs_lookup_cache_refresh(struct inode *file,
static int nfs_lookup(struct inode *dir, const char *name, int len,
struct inode **result)
{
- char filename[NFS_MAXNAMLEN + 1];
struct nfs_fh fhandle;
struct nfs_fattr fattr;
int error;
@@ -301,20 +300,18 @@ static int nfs_lookup(struct inode *dir, const char *name, int len,
iput(dir);
return -ENAMETOOLONG;
}
- memcpy_fromfs(filename, (char *) name, len);
- filename[len] = '\0';
- if (len == 1 && filename[0] == '.') { /* cheat for "." */
+ if (len == 1 && name[0] == '.') { /* cheat for "." */
*result = dir;
return 0;
}
if ((NFS_SERVER(dir)->flags & NFS_MOUNT_NOAC)
- || !nfs_lookup_cache_lookup(dir, filename, &fhandle, &fattr)) {
+ || !nfs_lookup_cache_lookup(dir, name, &fhandle, &fattr)) {
if ((error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir),
- filename, &fhandle, &fattr))) {
+ name, &fhandle, &fattr))) {
iput(dir);
return error;
}
- nfs_lookup_cache_add(dir, filename, &fhandle, &fattr);
+ nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
}
if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr))) {
iput(dir);
@@ -327,7 +324,6 @@ static int nfs_lookup(struct inode *dir, const char *name, int len,
static int nfs_create(struct inode *dir, const char *name, int len, int mode,
struct inode **result)
{
- char filename[NFS_MAXNAMLEN + 1];
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
@@ -343,13 +339,11 @@ static int nfs_create(struct inode *dir, const char *name, int len, int mode,
iput(dir);
return -ENAMETOOLONG;
}
- memcpy_fromfs(filename, (char *) name, len);
- filename[len] = '\0';
sattr.mode = mode;
sattr.uid = sattr.gid = sattr.size = -1;
sattr.atime.seconds = sattr.mtime.seconds = -1;
if ((error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
- filename, &sattr, &fhandle, &fattr))) {
+ name, &sattr, &fhandle, &fattr))) {
iput(dir);
return error;
}
@@ -357,7 +351,7 @@ static int nfs_create(struct inode *dir, const char *name, int len, int mode,
iput(dir);
return -EACCES;
}
- nfs_lookup_cache_add(dir, filename, &fhandle, &fattr);
+ nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
iput(dir);
return 0;
}
@@ -365,7 +359,6 @@ static int nfs_create(struct inode *dir, const char *name, int len, int mode,
static int nfs_mknod(struct inode *dir, const char *name, int len,
int mode, int rdev)
{
- char filename[NFS_MAXNAMLEN + 1];
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
@@ -380,8 +373,6 @@ static int nfs_mknod(struct inode *dir, const char *name, int len,
iput(dir);
return -ENAMETOOLONG;
}
- memcpy_fromfs(filename, (char *) name, len);
- filename[len] = '\0';
sattr.mode = mode;
sattr.uid = sattr.gid = -1;
if (S_ISCHR(mode) || S_ISBLK(mode))
@@ -390,16 +381,15 @@ static int nfs_mknod(struct inode *dir, const char *name, int len,
sattr.size = -1;
sattr.atime.seconds = sattr.mtime.seconds = -1;
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
- filename, &sattr, &fhandle, &fattr);
+ name, &sattr, &fhandle, &fattr);
if (!error)
- nfs_lookup_cache_add(dir, filename, &fhandle, &fattr);
+ nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
iput(dir);
return error;
}
static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode)
{
- char filename[NFS_MAXNAMLEN + 1];
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
@@ -414,22 +404,19 @@ static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode)
iput(dir);
return -ENAMETOOLONG;
}
- memcpy_fromfs(filename, (char *) name, len);
- filename[len] = '\0';
sattr.mode = mode;
sattr.uid = sattr.gid = sattr.size = -1;
sattr.atime.seconds = sattr.mtime.seconds = -1;
error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
- filename, &sattr, &fhandle, &fattr);
+ name, &sattr, &fhandle, &fattr);
if (!error)
- nfs_lookup_cache_add(dir, filename, &fhandle, &fattr);
+ nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
iput(dir);
return error;
}
static int nfs_rmdir(struct inode *dir, const char *name, int len)
{
- char filename[NFS_MAXNAMLEN + 1];
int error;
if (!dir || !S_ISDIR(dir->i_mode)) {
@@ -441,18 +428,15 @@ static int nfs_rmdir(struct inode *dir, const char *name, int len)
iput(dir);
return -ENAMETOOLONG;
}
- memcpy_fromfs(filename, (char *) name, len);
- filename[len] = '\0';
- error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), filename);
+ error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), name);
if (!error)
- nfs_lookup_cache_remove(dir, NULL, filename);
+ nfs_lookup_cache_remove(dir, NULL, name);
iput(dir);
return error;
}
static int nfs_unlink(struct inode *dir, const char *name, int len)
{
- char filename[NFS_MAXNAMLEN + 1];
int error;
if (!dir || !S_ISDIR(dir->i_mode)) {
@@ -464,11 +448,9 @@ static int nfs_unlink(struct inode *dir, const char *name, int len)
iput(dir);
return -ENAMETOOLONG;
}
- memcpy_fromfs(filename, (char *) name, len);
- filename[len] = '\0';
- error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), filename);
+ error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), name);
if (!error)
- nfs_lookup_cache_remove(dir, NULL, filename);
+ nfs_lookup_cache_remove(dir, NULL, name);
iput(dir);
return error;
}
@@ -476,12 +458,8 @@ static int nfs_unlink(struct inode *dir, const char *name, int len)
static int nfs_symlink(struct inode *dir, const char *name, int len,
const char *symname)
{
- char filename[NFS_MAXNAMLEN + 1];
- char *symfilename;
struct nfs_sattr sattr;
int error;
- int i;
- int c;
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_symlink: inode is NULL or not a directory\n");
@@ -492,23 +470,15 @@ static int nfs_symlink(struct inode *dir, const char *name, int len,
iput(dir);
return -ENAMETOOLONG;
}
- memcpy_fromfs(filename, (char *) name, len);
- filename[len] = '\0';
- symfilename = (char *) kmalloc(NFS_MAXPATHLEN + 1, GFP_KERNEL);
- for (i = 0; i < NFS_MAXPATHLEN && (c = get_fs_byte(symname++)); i++)
- symfilename[i] = c;
- if (i == NFS_MAXPATHLEN && get_fs_byte(symname)) {
- kfree_s(symfilename, NFS_MAXPATHLEN + 1);
+ if (strlen(symname) > NFS_MAXPATHLEN) {
iput(dir);
return -ENAMETOOLONG;
}
- symfilename[i] = '\0';
sattr.mode = S_IFLNK | 0777; /* SunOS 4.1.2 crashes without this! */
sattr.uid = sattr.gid = sattr.size = -1;
sattr.atime.seconds = sattr.mtime.seconds = -1;
error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
- filename, symfilename, &sattr);
- kfree_s(symfilename, NFS_MAXPATHLEN + 1);
+ name, symname, &sattr);
iput(dir);
return error;
}
@@ -516,7 +486,6 @@ static int nfs_symlink(struct inode *dir, const char *name, int len,
static int nfs_link(struct inode *oldinode, struct inode *dir,
const char *name, int len)
{
- char filename[NFS_MAXNAMLEN + 1];
int error;
if (!oldinode) {
@@ -536,10 +505,8 @@ static int nfs_link(struct inode *oldinode, struct inode *dir,
iput(dir);
return -ENAMETOOLONG;
}
- memcpy_fromfs(filename, (char *) name, len);
- filename[len] = '\0';
error = nfs_proc_link(NFS_SERVER(oldinode), NFS_FH(oldinode),
- NFS_FH(dir), filename);
+ NFS_FH(dir), name);
if (!error)
nfs_lookup_cache_remove(dir, oldinode, NULL);
iput(oldinode);
@@ -550,8 +517,6 @@ static int nfs_link(struct inode *oldinode, struct inode *dir,
static int nfs_rename(struct inode *old_dir, const char *old_name, int old_len,
struct inode *new_dir, const char *new_name, int new_len)
{
- char old_filename[NFS_MAXNAMLEN + 1];
- char new_filename[NFS_MAXNAMLEN + 1];
int error;
if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
@@ -571,16 +536,12 @@ static int nfs_rename(struct inode *old_dir, const char *old_name, int old_len,
iput(new_dir);
return -ENAMETOOLONG;
}
- memcpy_fromfs(old_filename, (char *) old_name, old_len);
- old_filename[old_len] = '\0';
- memcpy_fromfs(new_filename, (char *) new_name, new_len);
- new_filename[new_len] = '\0';
error = nfs_proc_rename(NFS_SERVER(old_dir),
- NFS_FH(old_dir), old_filename,
- NFS_FH(new_dir), new_filename);
+ NFS_FH(old_dir), old_name,
+ NFS_FH(new_dir), new_name);
if (!error) {
- nfs_lookup_cache_remove(old_dir, NULL, old_filename);
- nfs_lookup_cache_remove(new_dir, NULL, new_filename);
+ nfs_lookup_cache_remove(old_dir, NULL, old_name);
+ nfs_lookup_cache_remove(new_dir, NULL, new_name);
}
iput(old_dir);
iput(new_dir);
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 522b8aa..e62739e 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -56,7 +56,7 @@ static inline int *xdr_decode_fhandle(int *p, struct nfs_fh *fhandle)
return p;
}
-static inline int *xdr_encode_string(int *p, char *string)
+static inline int *xdr_encode_string(int *p, const char *string)
{
int len, quadlen;
@@ -215,7 +215,7 @@ int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
return -nfs_stat_to_errno(status);
}
-int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, char *name,
+int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir, const char *name,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
int *p, *p0;
@@ -343,7 +343,7 @@ int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle,
}
int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
- char *name, struct nfs_sattr *sattr,
+ const char *name, struct nfs_sattr *sattr,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
int *p, *p0;
@@ -372,7 +372,7 @@ int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
return -nfs_stat_to_errno(status);
}
-int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, char *name)
+int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, const char *name)
{
int *p, *p0;
int status;
@@ -398,8 +398,8 @@ int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir, char *name)
}
int nfs_proc_rename(struct nfs_server *server,
- struct nfs_fh *old_dir, char *old_name,
- struct nfs_fh *new_dir, char *new_name)
+ struct nfs_fh *old_dir, const char *old_name,
+ struct nfs_fh *new_dir, const char *new_name)
{
int *p, *p0;
int status;
@@ -427,7 +427,7 @@ int nfs_proc_rename(struct nfs_server *server,
}
int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
- struct nfs_fh *dir, char *name)
+ struct nfs_fh *dir, const char *name)
{
int *p, *p0;
int status;
@@ -454,7 +454,7 @@ int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
}
int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
- char *name, char *path, struct nfs_sattr *sattr)
+ const char *name, const char *path, struct nfs_sattr *sattr)
{
int *p, *p0;
int status;
@@ -482,7 +482,7 @@ int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
}
int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
- char *name, struct nfs_sattr *sattr,
+ const char *name, struct nfs_sattr *sattr,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
int *p, *p0;
@@ -511,7 +511,7 @@ int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
return -nfs_stat_to_errno(status);
}
-int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, char *name)
+int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
{
int *p, *p0;
int status;
diff --git a/fs/nfs/sock.c b/fs/nfs/sock.c
index 7c8f51e..910719f 100644
--- a/fs/nfs/sock.c
+++ b/fs/nfs/sock.c
@@ -11,6 +11,7 @@
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
+#include <asm/segment.h>
#include <netinet/in.h>
@@ -63,8 +64,8 @@ static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end)
printk("nfs_rpc_call: socki_lookup failed\n");
return -EBADF;
}
- __asm__("mov %%fs,%0":"=r" (fs));
- __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
+ fs = get_fs();
+ set_fs(get_ds());
for (n = 0, timeout = init_timeout; ; n++, timeout <<= 1) {
result = sock->ops->send(sock, (void *) start, len, 0, 0);
if (result < 0) {
@@ -123,8 +124,10 @@ static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end)
NULL, &addrlen);
if (result < 0) {
if (result == -EAGAIN) {
- printk("nfs_rpc_call: bad select ready\n");
goto re_select;
+#if 0
+ printk("nfs_rpc_call: bad select ready\n");
+#endif
}
if (result != -ERESTARTSYS) {
printk("nfs_rpc_call: recv error = %d\n",
@@ -141,7 +144,7 @@ static int do_nfs_rpc_call(struct nfs_server *server, int *start, int *end)
printk("nfs_rpc_call: XID mismatch\n");
#endif
}
- __asm__("mov %0,%%fs"::"r" (fs));
+ set_fs(fs);
return result;
}
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
index 5d08e43..29b7b8e 100644
--- a/fs/nfs/symlink.c
+++ b/fs/nfs/symlink.c
@@ -43,7 +43,6 @@ static int nfs_follow_link(struct inode *dir, struct inode *inode,
int flag, int mode, struct inode **res_inode)
{
int error;
- unsigned short fs;
char *res;
*res_inode = NULL;
@@ -74,13 +73,10 @@ static int nfs_follow_link(struct inode *dir, struct inode *inode,
return error;
}
iput(inode);
- __asm__("mov %%fs,%0":"=r" (fs));
- __asm__("mov %0,%%fs"::"r" ((unsigned short) 0x10));
current->link_count++;
error = open_namei(res, flag, mode, res_inode, dir);
current->link_count--;
kfree_s(res, NFS_MAXPATHLEN + 1);
- __asm__("mov %0,%%fs"::"r" (fs));
return error;
}
diff --git a/fs/open.c b/fs/open.c
index ebd7d9a..c4c3322 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -31,7 +31,9 @@ int sys_statfs(const char * path, struct statfs * buf)
struct inode * inode;
int error;
- verify_area(buf, sizeof(struct statfs));
+ error = verify_area(VERIFY_WRITE, buf, sizeof(struct statfs));
+ if (error)
+ return error;
error = namei(path,&inode);
if (error)
return error;
@@ -48,8 +50,11 @@ int sys_fstatfs(unsigned int fd, struct statfs * buf)
{
struct inode * inode;
struct file * file;
+ int error;
- verify_area(buf, sizeof(struct statfs));
+ error = verify_area(VERIFY_WRITE, buf, sizeof(struct statfs));
+ if (error)
+ return error;
if (fd >= NR_OPEN || !(file = current->filp[fd]))
return -EBADF;
if (!(inode = file->f_inode))
@@ -129,17 +134,17 @@ int sys_utime(char * filename, struct utimbuf * times)
}
actime = get_fs_long((unsigned long *) &times->actime);
modtime = get_fs_long((unsigned long *) &times->modtime);
+ inode->i_ctime = CURRENT_TIME;
} else {
if ((current->euid != inode->i_uid) &&
!permission(inode,MAY_WRITE)) {
iput(inode);
return -EACCES;
}
- actime = modtime = CURRENT_TIME;
+ actime = modtime = inode->i_ctime = CURRENT_TIME;
}
inode->i_atime = actime;
inode->i_mtime = modtime;
- inode->i_ctime = CURRENT_TIME;
inode->i_dirt = 1;
error = notify_change(NOTIFY_TIME, inode);
iput(inode);
@@ -147,34 +152,39 @@ int sys_utime(char * filename, struct utimbuf * times)
}
/*
- * XXX should we use the real or effective uid? BSD uses the real uid,
- * so as to make this call useful to setuid programs.
+ * XXX we should use the real ids for checking _all_ components of the
+ * path. Now we only use them for the final compenent of the path.
*/
int sys_access(const char * filename,int mode)
{
struct inode * inode;
int res, i_mode;
- mode &= 0007;
+ if (mode != (mode & 0007)) /* where's F_OK, X_OK, W_OK, R_OK? */
+ return -EINVAL;
res = namei(filename,&inode);
if (res)
return res;
- i_mode = res = inode->i_mode & 0777;
+ i_mode = inode->i_mode;
+ res = i_mode & 0777;
iput(inode);
if (current->uid == inode->i_uid)
res >>= 6;
else if (in_group_p(inode->i_gid))
res >>= 3;
- if ((res & 0007 & mode) == mode)
+ if ((res & mode) == mode)
return 0;
/*
* XXX we are doing this test last because we really should be
* swapping the effective with the real user id (temporarily),
* and then calling suser() routine. If we do call the
* suser() routine, it needs to be called last.
+ *
+ * XXX nope. suser() is inappropriate and swapping the ids while
+ * decomposing the path would be racy.
*/
if ((!current->uid) &&
- (!(mode & 1) || (i_mode & 0111)))
+ (S_ISDIR(i_mode) || !(mode & 1) || (i_mode & 0111)))
return 0;
return -EACCES;
}
@@ -340,11 +350,12 @@ int sys_chown(const char * filename, uid_t user, gid_t group)
* for the internal routines (ie open_namei()/follow_link() etc). 00 is
* used by symlinks.
*/
-int sys_open(const char * filename,int flag,int mode)
+int sys_open(const char * filename,int flags,int mode)
{
struct inode * inode;
struct file * f;
- int i,fd;
+ char * tmp;
+ int flag,error,fd;
for(fd=0 ; fd<NR_OPEN ; fd++)
if (!current->filp[fd])
@@ -356,31 +367,32 @@ int sys_open(const char * filename,int flag,int mode)
if (!f)
return -ENFILE;
current->filp[fd] = f;
- f->f_flags = flag;
+ f->f_flags = flag = flags;
f->f_mode = (flag+1) & O_ACCMODE;
if (f->f_mode)
flag++;
if (flag & (O_TRUNC | O_CREAT))
flag |= 2;
- i = open_namei(filename,flag,mode,&inode,NULL);
- if (i) {
+ error = getname(filename,&tmp);
+ if (!error) {
+ error = open_namei(tmp,flag,mode,&inode,NULL);
+ putname(tmp);
+ }
+ if (error) {
current->filp[fd]=NULL;
f->f_count--;
- return i;
+ return error;
}
if (flag & O_TRUNC) {
inode->i_size = 0;
if (inode->i_op && inode->i_op->truncate)
inode->i_op->truncate(inode);
- if ((i = notify_change(NOTIFY_SIZE, inode))) {
+ if ((error = notify_change(NOTIFY_SIZE, inode))) {
iput(inode);
current->filp[fd] = NULL;
f->f_count--;
- return i;
+ return error;
}
- }
- if (!IS_RDONLY(inode)) {
- inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
}
f->f_inode = inode;
@@ -390,12 +402,12 @@ int sys_open(const char * filename,int flag,int mode)
if (inode->i_op)
f->f_op = inode->i_op->default_file_ops;
if (f->f_op && f->f_op->open) {
- i = f->f_op->open(inode,f);
- if (i) {
+ error = f->f_op->open(inode,f);
+ if (error) {
iput(inode);
f->f_count--;
current->filp[fd]=NULL;
- return i;
+ return error;
}
}
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
@@ -412,7 +424,7 @@ int close_fp(struct file *filp)
struct inode *inode;
if (filp->f_count == 0) {
- printk("Close: file count is 0\n");
+ printk("VFS: Close: file count is 0\n");
return 0;
}
inode = filp->f_inode;
@@ -435,10 +447,10 @@ int sys_close(unsigned int fd)
struct file * filp;
if (fd >= NR_OPEN)
- return -EINVAL;
+ return -EBADF;
FD_CLR(fd, &current->close_on_exec);
if (!(filp = current->filp[fd]))
- return -EINVAL;
+ return -EBADF;
current->filp[fd] = NULL;
return (close_fp (filp));
}
@@ -457,6 +469,6 @@ int sys_vhangup(void)
if (current->tty < 0)
return 0;
tty = TTY_TABLE(MINOR(current->tty));
- tty_hangup(tty);
+ tty_vhangup(tty);
return 0;
}
diff --git a/fs/pipe.c b/fs/pipe.c
index ac8b9eb..86809e0 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -110,11 +110,14 @@ static int bad_pipe_rw(struct inode * inode, struct file * filp, char * buf, int
static int pipe_ioctl(struct inode *pino, struct file * filp,
unsigned int cmd, unsigned int arg)
{
+ int error;
+
switch (cmd) {
case FIONREAD:
- verify_area((void *) arg,4);
- put_fs_long(PIPE_SIZE(*pino),(unsigned long *) arg);
- return 0;
+ error = verify_area(VERIFY_WRITE, (void *) arg,4);
+ if (!error)
+ put_fs_long(PIPE_SIZE(*pino),(unsigned long *) arg);
+ return error;
default:
return -EINVAL;
}
@@ -216,7 +219,9 @@ int sys_pipe(unsigned long * fildes)
int fd[2];
int i,j;
- verify_area(fildes,8);
+ j = verify_area(VERIFY_WRITE,fildes,8);
+ if (j)
+ return j;
for(j=0 ; j<2 ; j++)
if (!(f[j] = get_empty_filp()))
break;
@@ -246,9 +251,10 @@ int sys_pipe(unsigned long * fildes)
}
f[0]->f_inode = f[1]->f_inode = inode;
f[0]->f_pos = f[1]->f_pos = 0;
- f[0]->f_flags = f[1]->f_flags = 0;
+ f[0]->f_flags = O_RDONLY;
f[0]->f_op = &read_pipe_fops;
f[0]->f_mode = 1; /* read */
+ f[1]->f_flags = O_WRONLY;
f[1]->f_op = &write_pipe_fops;
f[1]->f_mode = 2; /* write */
put_fs_long(fd[0],0+fildes);
diff --git a/fs/proc/array.c b/fs/proc/array.c
index e9e6cf2..460e64b 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -19,8 +19,8 @@
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
-#define KSTK_EIP(stack) (((char *)stack)[1019])
-#define KSTK_ESP(stack) (((char *)stack)[1022])
+#define KSTK_EIP(stack) (((unsigned long *)stack)[1019])
+#define KSTK_ESP(stack) (((unsigned long *)stack)[1022])
#define _SSIZE(stack) (TASK_SIZE - KSTK_ESP(stack))
#define SSIZE(stack) (KSTK_ESP(stack) ? _SSIZE(stack) : 0)
@@ -161,7 +161,7 @@ static unsigned long get_wchan(struct task_struct *p)
unsigned long stack_page;
int count = 0;
- if (!p || p == current)
+ if (!p || p == current || p->state == TASK_RUNNING)
return 0;
ebp = p->tss.ebp;
stack_page = p->kernel_stack_page;
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 51009b6..ac06664 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -79,7 +79,7 @@ int proc_match(int len,const char * name,struct proc_dir_entry * de)
if (de->namelen != len)
return 0;
__asm__("cld\n\t"
- "fs ; repe ; cmpsb\n\t"
+ "repe ; cmpsb\n\t"
"setz %%al"
:"=a" (same)
:"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
diff --git a/fs/proc/fd.c b/fs/proc/fd.c
index 25f7b46..ba4dcb7 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -70,8 +70,8 @@ static int proc_lookupfd(struct inode * dir,const char * name, int len,
iput(dir);
return -ENOENT;
}
- if (!len || (get_fs_byte(name) == '.' && (len == 1 ||
- (get_fs_byte(name+1) == '.' && len == 2)))) {
+ if (!len || (name[0] == '.' && (len == 1 ||
+ (name[1] == '.' && len == 2)))) {
if (len < 2) {
*result = dir;
return 0;
@@ -86,7 +86,7 @@ static int proc_lookupfd(struct inode * dir,const char * name, int len,
iput(dir);
fd = 0;
while (len-- > 0) {
- c = get_fs_byte(name) - '0';
+ c = *name - '0';
name++;
if (c > 9) {
fd = 0xfffff;
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
index 60251a7..e788d41 100644
--- a/fs/proc/inode.c
+++ b/fs/proc/inode.c
@@ -116,8 +116,7 @@ void proc_read_inode(struct inode * inode)
return;
case 3:
inode->i_op = &proc_mem_inode_operations;
- inode->i_mode = S_IFCHR | 0600;
- inode->i_rdev = 0x0101;
+ inode->i_mode = S_IFREG | 0600;
return;
case 4:
case 5:
diff --git a/fs/proc/root.c b/fs/proc/root.c
index aa07cf7..af77c16 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -90,7 +90,7 @@ static int proc_lookuproot(struct inode * dir,const char * name, int len,
} else {
pid = 0;
while (len-- > 0) {
- c = get_fs_byte(name) - '0';
+ c = *name - '0';
name++;
if (c > 9) {
pid = 0;
diff --git a/fs/read_write.c b/fs/read_write.c
index 50f5c9b..1d5d334 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -9,7 +9,6 @@
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/sched.h>
-#include <linux/minix_fs.h>
#include <asm/segment.h>
@@ -19,17 +18,20 @@
*/
int sys_readdir(unsigned int fd, struct dirent * dirent, unsigned int count)
{
+ int error;
struct file * file;
struct inode * inode;
if (fd >= NR_OPEN || !(file = current->filp[fd]) ||
!(inode = file->f_inode))
return -EBADF;
+ error = -ENOTDIR;
if (file->f_op && file->f_op->readdir) {
- verify_area(dirent, sizeof (*dirent));
- return file->f_op->readdir(inode,file,dirent,count);
+ error = verify_area(VERIFY_WRITE, dirent, sizeof (*dirent));
+ if (!error)
+ error = file->f_op->readdir(inode,file,dirent,count);
}
- return -ENOTDIR;
+ return error;
}
int sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
@@ -67,6 +69,7 @@ int sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
int sys_read(unsigned int fd,char * buf,unsigned int count)
{
+ int error;
struct file * file;
struct inode * inode;
@@ -78,12 +81,15 @@ int sys_read(unsigned int fd,char * buf,unsigned int count)
return -EINVAL;
if (!count)
return 0;
- verify_area(buf,count);
+ error = verify_area(VERIFY_WRITE,buf,count);
+ if (error)
+ return error;
return file->f_op->read(inode,file,buf,count);
}
int sys_write(unsigned int fd,char * buf,unsigned int count)
{
+ int error;
struct file * file;
struct inode * inode;
@@ -95,5 +101,8 @@ int sys_write(unsigned int fd,char * buf,unsigned int count)
return -EINVAL;
if (!count)
return 0;
+ error = verify_area(VERIFY_READ,buf,count);
+ if (error)
+ return error;
return file->f_op->write(inode,file,buf,count);
}
diff --git a/fs/select.c b/fs/select.c
index 1bf7f15..19b3553 100644
--- a/fs/select.c
+++ b/fs/select.c
@@ -137,24 +137,33 @@ repeat:
return count;
}
-static void __get_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fdset)
+/*
+ * We do a VERIFY_WRITE here even though we are only reading this time:
+ * we'll write to it eventually..
+ */
+static int __get_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fdset)
{
+ int error;
+
FD_ZERO(fdset);
if (!fs_pointer)
- return;
+ return 0;
+ error = verify_area(VERIFY_WRITE,fs_pointer,sizeof(fd_set));
+ if (error)
+ return error;
while (nr > 0) {
*fdset = get_fs_long(fs_pointer);
fdset++;
fs_pointer++;
nr -= 32;
}
+ return 0;
}
static void __set_fd_set(int nr, unsigned long * fs_pointer, unsigned long * fdset)
{
if (!fs_pointer)
return;
- verify_area(fs_pointer, sizeof(fd_set));
while (nr > 0) {
put_fs_long(*fdset, fs_pointer);
fdset++;
@@ -185,6 +194,9 @@ int sys_select( unsigned long *buffer )
struct timeval *tvp;
unsigned long timeout;
+ i = verify_area(VERIFY_READ, buffer, 20);
+ if (i)
+ return i;
n = get_fs_long(buffer++);
if (n < 0)
return -EINVAL;
@@ -194,11 +206,14 @@ int sys_select( unsigned long *buffer )
outp = (fd_set *) get_fs_long(buffer++);
exp = (fd_set *) get_fs_long(buffer++);
tvp = (struct timeval *) get_fs_long(buffer);
- get_fd_set(n, inp, &in);
- get_fd_set(n, outp, &out);
- get_fd_set(n, exp, &ex);
+ if ((i = get_fd_set(n, inp, &in)) ||
+ (i = get_fd_set(n, outp, &out)) ||
+ (i = get_fd_set(n, exp, &ex))) return i;
timeout = 0xffffffff;
if (tvp) {
+ i = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp));
+ if (i)
+ return i;
timeout = jiffies;
timeout += ROUND_UP(get_fs_long((unsigned long *)&tvp->tv_usec),(1000000/HZ));
timeout += get_fs_long((unsigned long *)&tvp->tv_sec) * HZ;
@@ -213,7 +228,6 @@ int sys_select( unsigned long *buffer )
timeout = 0;
current->timeout = 0;
if (tvp) {
- verify_area(tvp, sizeof(*tvp));
put_fs_long(timeout/HZ, (unsigned long *) &tvp->tv_sec);
timeout %= HZ;
timeout *= (1000000/HZ);
diff --git a/fs/stat.c b/fs/stat.c
index 5583979..abdc27e 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -15,9 +15,8 @@ static void cp_old_stat(struct inode * inode, struct old_stat * statbuf)
{
struct old_stat tmp;
- printk("Warning: %s using old stat() call. Recompile your binary.\n",
+ printk("VFS: Warning: %s using old stat() call. Recompile your binary.\n",
current->comm);
- verify_area(statbuf,sizeof (*statbuf));
tmp.st_dev = inode->i_dev;
tmp.st_ino = inode->i_ino;
tmp.st_mode = inode->i_mode;
@@ -37,7 +36,6 @@ static void cp_new_stat(struct inode * inode, struct new_stat * statbuf)
struct new_stat tmp = {0, };
unsigned int blocks, indirect;
- verify_area(statbuf,sizeof (*statbuf));
tmp.st_dev = inode->i_dev;
tmp.st_ino = inode->i_ino;
tmp.st_mode = inode->i_mode;
@@ -82,6 +80,9 @@ int sys_stat(char * filename, struct old_stat * statbuf)
struct inode * inode;
int error;
+ error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf));
+ if (error)
+ return error;
error = namei(filename,&inode);
if (error)
return error;
@@ -95,6 +96,9 @@ int sys_newstat(char * filename, struct new_stat * statbuf)
struct inode * inode;
int error;
+ error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf));
+ if (error)
+ return error;
error = namei(filename,&inode);
if (error)
return error;
@@ -108,6 +112,9 @@ int sys_lstat(char * filename, struct old_stat * statbuf)
struct inode * inode;
int error;
+ error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf));
+ if (error)
+ return error;
error = lnamei(filename,&inode);
if (error)
return error;
@@ -121,6 +128,9 @@ int sys_newlstat(char * filename, struct new_stat * statbuf)
struct inode * inode;
int error;
+ error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf));
+ if (error)
+ return error;
error = lnamei(filename,&inode);
if (error)
return error;
@@ -133,7 +143,11 @@ int sys_fstat(unsigned int fd, struct old_stat * statbuf)
{
struct file * f;
struct inode * inode;
+ int error;
+ error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf));
+ if (error)
+ return error;
if (fd >= NR_OPEN || !(f=current->filp[fd]) || !(inode=f->f_inode))
return -EBADF;
cp_old_stat(inode,statbuf);
@@ -144,7 +158,11 @@ int sys_newfstat(unsigned int fd, struct new_stat * statbuf)
{
struct file * f;
struct inode * inode;
+ int error;
+ error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf));
+ if (error)
+ return error;
if (fd >= NR_OPEN || !(f=current->filp[fd]) || !(inode=f->f_inode))
return -EBADF;
cp_new_stat(inode,statbuf);
@@ -158,7 +176,9 @@ int sys_readlink(const char * path, char * buf, int bufsiz)
if (bufsiz <= 0)
return -EINVAL;
- verify_area(buf,bufsiz);
+ error = verify_area(VERIFY_WRITE,buf,bufsiz);
+ if (error)
+ return error;
error = lnamei(path,&inode);
if (error)
return error;
diff --git a/fs/super.c b/fs/super.c
index f5dd73c..4820491 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -103,13 +103,15 @@ void put_super(dev_t dev)
struct super_block * sb;
if (dev == ROOT_DEV) {
- printk("root diskette changed: prepare for armageddon\n\r");
+ printk("VFS: Root device %d/%d: prepare for armageddon\n",
+ MAJOR(dev), MINOR(dev));
return;
}
if (!(sb = get_super(dev)))
return;
if (sb->s_covered) {
- printk("Mounted disk changed - tssk, tssk\n\r");
+ printk("VFS: Mounted device %d/%d - tssk, tssk\n",
+ MAJOR(dev), MINOR(dev));
return;
}
if (sb->s_op && sb->s_op->put_super)
@@ -128,7 +130,8 @@ static struct super_block * read_super(dev_t dev,char *name,int flags,void *data
if (s)
return s;
if (!(type = get_fs_type(name))) {
- printk("get fs type failed %s\n",name);
+ printk("VFS: on device %d/%d: get_fs_type(%s) failed\n",
+ MAJOR(dev), MINOR(dev), name);
return NULL;
}
for (s = 0+super_block ;; s++) {
@@ -181,7 +184,8 @@ static void put_unnamed_dev(dev_t dev)
if (!dev)
return;
if (!unnamed_dev_in_use[dev]) {
- printk("put_unnamed_dev: trying to free unused device\n");
+ printk("VFS: put_unnamed_dev: freeing unused device %d/%d\n",
+ MAJOR(dev), MINOR(dev));
return;
}
unnamed_dev_in_use[dev] = 0;
@@ -196,7 +200,8 @@ static int do_umount(dev_t dev)
if (!(sb=get_super(dev)) || !(sb->s_covered))
return -ENOENT;
if (!sb->s_covered->i_mount)
- printk("Mounted inode has i_mount=0\n");
+ printk("VFS: umount(%d/%d): mounted inode has i_mount=0\n",
+ MAJOR(dev), MINOR(dev));
if (!fs_may_umount(dev, sb->s_mounted))
return -EBUSY;
sb->s_covered->i_mount=0;
@@ -366,10 +371,9 @@ int sys_mount(char * dev_name, char * dir_name, char * type,
if (!suser())
return -EPERM;
- if ((new_flags & (MS_MGC_MSK | MS_REMOUNT)) == (MS_MGC_VAL |
- MS_REMOUNT))
- return do_remount(dir_name,new_flags & ~MS_MGC_MSK &
- ~MS_REMOUNT);
+ if ((new_flags & (MS_MGC_MSK | MS_REMOUNT)) == (MS_MGC_VAL | MS_REMOUNT)) {
+ return do_remount(dir_name,new_flags & ~MS_MGC_MSK & ~MS_REMOUNT);
+ }
if (type) {
for (i = 0 ; i < 100 ; i++)
if (!(tmp[i] = get_fs_byte(type++)))
@@ -397,8 +401,7 @@ int sys_mount(char * dev_name, char * dir_name, char * type,
iput(inode);
return -ENXIO;
}
- }
- else {
+ } else {
if (!(dev = get_unnamed_dev()))
return -EMFILE;
inode = NULL;
@@ -443,7 +446,7 @@ void mount_root(void)
memset(super_block, 0, sizeof(super_block));
fcntl_init_locks();
if (MAJOR(ROOT_DEV) == 2) {
- printk("Insert root floppy and press ENTER");
+ printk("VFS: Insert root floppy and press ENTER");
wait_for_keypress();
}
for (fs_type = file_systems; fs_type->read_super; fs_type++) {
@@ -460,5 +463,5 @@ void mount_root(void)
return;
}
}
- panic("Unable to mount root");
+ panic("VFS: Unable to mount root");
}
diff --git a/fs/xiafs/Makefile b/fs/xiafs/Makefile
new file mode 100644
index 0000000..90a8b75
--- /dev/null
+++ b/fs/xiafs/Makefile
@@ -0,0 +1,37 @@
+#
+# Makefile for the XIAFS filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+.s.o:
+ $(AS) -o $*.o $<
+
+OBJS= bitmap.o truncate.o namei.o inode.o \
+ file.o dir.o symlink.o
+
+xiafs.o: $(OBJS)
+ $(LD) -r -o xiafs.o $(OBJS)
+
+clean:
+ rm -f core *.o *.a *.s
+
+veryclean:
+ rm -f core *.o *.a *.s *~ .depend tags
+
+dep:
+ $(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/fs/xiafs/bitmap.c b/fs/xiafs/bitmap.c
new file mode 100644
index 0000000..c85cc1e
--- /dev/null
+++ b/fs/xiafs/bitmap.c
@@ -0,0 +1,388 @@
+/*
+ * linux/fs/xiafs/bitmap.c
+ *
+ * Copyright (C) Q. Frank Xia, 1993.
+ *
+ * Based on Linus' minix/bitmap.c
+ * Copyright (C) Linus Torvalds, 1991, 1992.
+ *
+ * This software may be redistributed per Linux Copyright.
+ */
+
+/* bitmap.c contains the code that handles the inode and block bitmaps */
+
+#include <linux/sched.h>
+#include <linux/locks.h>
+#include <linux/xia_fs.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "xiafs_mac.h"
+
+
+#define clear_bit(nr,addr) ({\
+char res; \
+__asm__ __volatile__("btrl %1,%2\n\tsetnb %0": \
+"=q" (res):"r" (nr),"m" (*(addr))); \
+res;})
+
+char internal_error_message[]="XIA-FS: internal error %s %d\n";
+
+static int find_first_zero(struct buffer_head *bh, int start_bit, int end_bit)
+{
+ /* This routine searches first 0 bit from (start_bit) to (end_bit-1).
+ * If found the bit is set to 1 and the bit # is returned, otherwise,
+ * -1 is returned. Race condition is avoid by using "btsl" and
+ * "goto repeat". ---Frank.
+ */
+
+ int end, i, j, tmp;
+ u_long *bmap;
+ char res;
+
+ bmap=(u_long *)bh->b_data;
+ end = end_bit >> 5;
+
+repeat:
+ i=start_bit >> 5;
+ if ( (tmp=(~bmap[i]) & (0xffffffff << (start_bit & 31))) )
+ goto zone_found;
+ while (++i < end)
+ if (~bmap[i]) {
+ tmp=~bmap[i];
+ goto zone_found;
+ }
+ if ( !(tmp=~bmap[i] & ((1 << (end_bit & 31)) -1)) )
+ return -1;
+zone_found:
+ for (j=0; j < 32; j++)
+ if (tmp & (1 << j))
+ break;
+ __asm__ ("btsl %1,%2\n\tsetb %0": \
+ "=q" (res):"r" (j),"m" (bmap[i]));
+ if (res) {
+ start_bit=j + (i << 5) + 1;
+ goto repeat;
+ }
+ bh->b_dirt=1;
+ return j + (i << 5);
+}
+
+static void clear_buf(struct buffer_head * bh)
+{
+ register int i;
+ register long * lp;
+
+ lp=(long *)bh->b_data;
+ for (i= bh->b_size >> 2; i-- > 0; )
+ *lp++=0;
+}
+
+static void que(struct buffer_head * bmap[], int bznr[], int pos)
+{
+ struct buffer_head * tbh;
+ int tmp;
+ int i;
+
+ tbh=bmap[pos];
+ tmp=bznr[pos];
+ for (i=pos; i > 0; i--) {
+ bmap[i]=bmap[i-1];
+ bznr[i]=bznr[i-1];
+ }
+ bmap[0]=tbh;
+ bznr[0]=tmp;
+}
+
+#define get_imap_zone(sb, bit_nr, not_que) \
+ get__map_zone((sb), (sb)->u.xiafs_sb.s_imap_buf, \
+ (sb)->u.xiafs_sb.s_imap_iznr, \
+ (sb)->u.xiafs_sb.s_imap_cached, 1, \
+ (sb)->u.xiafs_sb.s_imap_zones, _XIAFS_IMAP_SLOTS, \
+ bit_nr, not_que)
+
+#define get_zmap_zone(sb, bit_nr, not_que) \
+ get__map_zone((sb), (sb)->u.xiafs_sb.s_zmap_buf, \
+ (sb)->u.xiafs_sb.s_zmap_zznr, \
+ (sb)->u.xiafs_sb.s_zmap_cached, \
+ 1+(sb)->u.xiafs_sb.s_imap_zones, \
+ (sb)->u.xiafs_sb.s_zmap_zones, _XIAFS_ZMAP_SLOTS, \
+ bit_nr, not_que)
+
+static struct buffer_head *
+get__map_zone(struct super_block *sb, struct buffer_head * bmap_buf[],
+ int bznr[], u_char cache, int first_zone,
+ int bmap_zones, int slots, u_long bit_nr, int * not_que)
+{
+ struct buffer_head * tmp_bh;
+ int z_nr, i;
+
+ z_nr = bit_nr >> XIAFS_BITS_PER_Z_BITS(sb);
+ if (z_nr >= bmap_zones) {
+ printk("XIA-FS: bad inode/zone number (%s %d)\n", WHERE_ERR);
+ return NULL;
+ }
+ if (!cache)
+ return bmap_buf[z_nr];
+ lock_super(sb);
+ for (i=0; i < slots; i++)
+ if (bznr[i]==z_nr)
+ break;
+ if (i < slots) { /* cache hit */
+ if (not_que) {
+ *not_que=i;
+ return bmap_buf[i];
+ } else {
+ que(bmap_buf, bznr, i);
+ return bmap_buf[0];
+ }
+ }
+ tmp_bh=bread(sb->s_dev, z_nr+first_zone, XIAFS_ZSIZE(sb)); /* cache not hit */
+ if (!tmp_bh) {
+ printk("XIA-FS: read bitmap failed (%s %d)\n", WHERE_ERR);
+ unlock_super(sb);
+ return NULL;
+ }
+ brelse(bmap_buf[slots-1]);
+ bmap_buf[slots-1]=tmp_bh;
+ bznr[slots-1]=z_nr;
+ if (not_que)
+ *not_que=slots-1;
+ else
+ que(bmap_buf, bznr, slots-1);
+ return tmp_bh;
+}
+
+#define xiafs_unlock_super(sb, cache) if (cache) unlock_super(sb);
+
+#define get_free_ibit(sb, prev_bit) \
+ get_free__bit(sb, sb->u.xiafs_sb.s_imap_buf, \
+ sb->u.xiafs_sb.s_imap_iznr, \
+ sb->u.xiafs_sb.s_imap_cached, \
+ 1, sb->u.xiafs_sb.s_imap_zones, \
+ _XIAFS_IMAP_SLOTS, prev_bit);
+
+#define get_free_zbit(sb, prev_bit) \
+ get_free__bit(sb, sb->u.xiafs_sb.s_zmap_buf, \
+ sb->u.xiafs_sb.s_zmap_zznr, \
+ sb->u.xiafs_sb.s_zmap_cached, \
+ 1 + sb->u.xiafs_sb.s_imap_zones, \
+ sb->u.xiafs_sb.s_zmap_zones, \
+ _XIAFS_ZMAP_SLOTS, prev_bit);
+
+static u_long
+get_free__bit(struct super_block *sb, struct buffer_head * bmap_buf[],
+ int bznr[], u_char cache, int first_zone, int bmap_zones,
+ int slots, u_long prev_bit)
+{
+ struct buffer_head * bh;
+ int not_done=0;
+ u_long pos, start_bit, end_bit, total_bits;
+ int z_nr, tmp;
+
+ total_bits=bmap_zones << XIAFS_BITS_PER_Z_BITS(sb);
+ if (prev_bit >= total_bits)
+ prev_bit=0;
+ pos=prev_bit+1;
+ end_bit=XIAFS_BITS_PER_Z(sb);
+
+ do {
+ if (pos >= total_bits)
+ pos=0;
+ if (!not_done) { /* first time */
+ not_done=1;
+ start_bit= pos & (end_bit-1);
+ } else
+ start_bit=0;
+ if ( pos < prev_bit && pos+end_bit >= prev_bit) { /* last time */
+ not_done=0;
+ end_bit=prev_bit & (end_bit-1); /* only here end_bit modified */
+ }
+ bh = get__map_zone(sb, bmap_buf, bznr, cache, first_zone,
+ bmap_zones, slots, pos, &z_nr);
+ if (!bh)
+ return 0;
+ tmp=find_first_zero(bh, start_bit, end_bit);
+ if (tmp >= 0)
+ break;
+ xiafs_unlock_super(sb, sb->u.xiafs_sb.s_zmap_cached);
+ pos=(pos & ~(end_bit-1))+end_bit;
+ } while (not_done);
+
+ if (tmp < 0)
+ return 0;
+ if (cache)
+ que(bmap_buf, bznr, z_nr);
+ xiafs_unlock_super(sb, cache);
+ return (pos & ~(XIAFS_BITS_PER_Z(sb)-1))+tmp;
+}
+
+void xiafs_free_zone(struct super_block * sb, int d_addr)
+{
+ struct buffer_head * bh;
+ unsigned int bit, offset;
+
+ if (!sb) {
+ printk(INTERN_ERR);
+ return;
+ }
+ if (d_addr < sb->u.xiafs_sb.s_firstdatazone ||
+ d_addr >= sb->u.xiafs_sb.s_nzones) {
+ printk("XIA-FS: bad zone number (%s %d)\n", WHERE_ERR);
+ return;
+ }
+ bh = get_hash_table(sb->s_dev, d_addr, XIAFS_ZSIZE(sb));
+ if (bh)
+ bh->b_dirt=0;
+ brelse(bh);
+ bit=d_addr - sb->u.xiafs_sb.s_firstdatazone + 1;
+ bh = get_zmap_zone(sb, bit, NULL);
+ if (!bh)
+ return;
+ offset = bit & (XIAFS_BITS_PER_Z(sb) -1);
+ if (clear_bit(offset, bh->b_data))
+ printk("XIA-FS: bit %d (0x%x) already cleared (%s %d)\n", bit, bit, WHERE_ERR);
+ bh->b_dirt = 1;
+ xiafs_unlock_super(sb, sb->u.xiafs_sb.s_zmap_cached);
+}
+
+int xiafs_new_zone(struct super_block * sb, u_long prev_addr)
+{
+ struct buffer_head * bh;
+ int prev_znr, tmp;
+
+ if (!sb) {
+ printk(INTERN_ERR);
+ return 0;
+ }
+ if (prev_addr < sb->u.xiafs_sb.s_firstdatazone ||
+ prev_addr >= sb->u.xiafs_sb.s_nzones) {
+ prev_addr=sb->u.xiafs_sb.s_firstdatazone;
+ }
+ prev_znr=prev_addr-sb->u.xiafs_sb.s_firstdatazone+1;
+ tmp=get_free_zbit(sb, prev_znr);
+ if (!tmp)
+ return 0;
+ tmp += sb->u.xiafs_sb.s_firstdatazone -1;
+ if (!(bh = getblk(sb->s_dev, tmp, XIAFS_ZSIZE(sb)))) {
+ printk("XIA-FS: I/O error (%s %d)\n", WHERE_ERR);
+ return 0;
+ }
+ if (bh->b_count != 1) {
+ printk(INTERN_ERR);
+ return 0;
+ }
+ clear_buf(bh);
+ bh->b_uptodate = 1;
+ bh->b_dirt = 1;
+ brelse(bh);
+ return tmp;
+}
+
+void xiafs_free_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ int tmp;
+
+ if (!inode)
+ return;
+ if (!inode->i_dev || inode->i_count!=1 || inode->i_nlink || !inode->i_sb ||
+ inode->i_ino < 3 || inode->i_ino > inode->i_sb->u.xiafs_sb.s_ninodes) {
+ printk("XIA-FS: bad inode (%s %d)\n", WHERE_ERR);
+ return;
+ }
+ bh = get_imap_zone(inode->i_sb, inode->i_ino, NULL);
+ if (!bh)
+ return;
+ tmp = inode->i_ino & (XIAFS_BITS_PER_Z(inode->i_sb)-1);
+ if (clear_bit(tmp, bh->b_data))
+ printk("XIA-FS: bit %d (0x%x) already cleared (%s %d)\n",
+ inode->i_ino, inode->i_ino, WHERE_ERR);
+ bh->b_dirt = 1;
+ xiafs_unlock_super(inode->i_sb, inode->i_sb->u.xiafs_sb.s_imap_cached);
+ clear_inode(inode);
+}
+
+struct inode * xiafs_new_inode(struct inode * dir)
+{
+ struct super_block * sb;
+ struct inode * inode;
+ ino_t tmp;
+
+ sb = dir->i_sb;
+ if (!dir || !(inode = get_empty_inode()))
+ return NULL;
+ inode->i_sb = sb;
+ inode->i_flags = inode->i_sb->s_flags;
+
+ tmp=get_free_ibit(sb, dir->i_ino);
+ if (!tmp) {
+ iput(inode);
+ return NULL;
+ }
+ inode->i_count = 1;
+ inode->i_nlink = 1;
+ inode->i_dev = sb->s_dev;
+ inode->i_uid = current->euid;
+ inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->egid;
+ inode->i_dirt = 1;
+ 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;
+ return inode;
+}
+
+static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
+
+static u_long count_zone(struct buffer_head * bh)
+{
+ int i, tmp;
+ u_long sum;
+
+ sum=0;
+ for (i=bh->b_size; i-- > 0; ) {
+ tmp=bh->b_data[i];
+ sum += nibblemap[tmp & 0xf] + nibblemap[(tmp & 0xff) >> 4];
+ }
+ return sum;
+}
+
+unsigned long xiafs_count_free_inodes(struct super_block *sb)
+{
+ struct buffer_head * bh;
+ int izones, i, not_que;
+ u_long sum;
+
+ sum=0;
+ izones=sb->u.xiafs_sb.s_imap_zones;
+ for (i=0; i < izones; i++) {
+ bh=get_imap_zone(sb, i << XIAFS_BITS_PER_Z_BITS(sb), &not_que);
+ if (bh) {
+ sum += count_zone(bh);
+ xiafs_unlock_super(sb, sb->u.xiafs_sb.s_imap_cached);
+ }
+ }
+ i=izones << XIAFS_BITS_PER_Z_BITS(sb);
+ return i - sum;
+}
+
+unsigned long xiafs_count_free_zones(struct super_block *sb)
+{
+ struct buffer_head * bh;
+ int zzones, i, not_que;
+ u_long sum;
+
+ sum=0;
+ zzones=sb->u.xiafs_sb.s_zmap_zones;
+ for (i=0; i < zzones; i++) {
+ bh=get_zmap_zone(sb, i << XIAFS_BITS_PER_Z_BITS(sb), &not_que);
+ if (bh) {
+ sum += count_zone(bh);
+ xiafs_unlock_super(sb, sb->u.xiafs_sb.s_zmap_cached);
+ }
+ }
+ i=zzones << XIAFS_BITS_PER_Z_BITS(sb);
+ return i - sum;
+}
diff --git a/fs/xiafs/dir.c b/fs/xiafs/dir.c
new file mode 100644
index 0000000..d39947c
--- /dev/null
+++ b/fs/xiafs/dir.c
@@ -0,0 +1,121 @@
+/*
+ * linux/fs/xiafs/dir.c
+ *
+ * Copyright (C) Q. Frank Xia, 1993.
+ *
+ * Based on Linus' minix/dir.c
+ * Copyright (C) Linus Torvalds, 1991, 1992.
+ *
+ * This software may be redistributed per Linux Copyright.
+ */
+
+#include <asm/segment.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/xia_fs.h>
+#include <linux/stat.h>
+
+#include "xiafs_mac.h"
+
+static int xiafs_dir_read(struct inode *, struct file *, char *, int);
+static int xiafs_readdir(struct inode *, struct file *, struct dirent *, int);
+
+static struct file_operations xiafs_dir_operations = {
+ NULL, /* lseek - default */
+ xiafs_dir_read, /* read */
+ NULL, /* write - bad */
+ xiafs_readdir, /* readdir */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* default fsync */
+};
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations xiafs_dir_inode_operations = {
+ &xiafs_dir_operations, /* default directory file-ops */
+ xiafs_create, /* create */
+ xiafs_lookup, /* lookup */
+ xiafs_link, /* link */
+ xiafs_unlink, /* unlink */
+ xiafs_symlink, /* symlink */
+ xiafs_mkdir, /* mkdir */
+ xiafs_rmdir, /* rmdir */
+ xiafs_mknod, /* mknod */
+ xiafs_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ xiafs_truncate, /* truncate */
+ NULL /* permission */
+};
+
+static int xiafs_dir_read(struct inode * inode,
+ struct file * filp, char * buf, int count)
+{
+ return -EISDIR;
+}
+
+static int xiafs_readdir(struct inode * inode,
+ struct file * filp, struct dirent * dirent, int count)
+{
+ u_int offset, i;
+ struct buffer_head * bh;
+ struct xiafs_direct * de;
+
+ if (!inode || !inode->i_sb || !S_ISDIR(inode->i_mode))
+ return -EBADF;
+ if (inode->i_size & (XIAFS_ZSIZE(inode->i_sb) - 1) )
+ return -EBADF;
+ while (filp->f_pos < inode->i_size) {
+ offset = filp->f_pos & (XIAFS_ZSIZE(inode->i_sb) - 1);
+ bh = xiafs_bread(inode, filp->f_pos >> XIAFS_ZSIZE_BITS(inode->i_sb),0);
+ if (!bh) {
+ filp->f_pos += XIAFS_ZSIZE(inode->i_sb)-offset;
+ continue;
+ }
+ de = (struct xiafs_direct *) (offset + bh->b_data);
+ while (offset < XIAFS_ZSIZE(inode->i_sb) && filp->f_pos < inode->i_size) {
+ if (de->d_ino > inode->i_sb->u.xiafs_sb.s_ninodes ||
+ de->d_rec_len < 12 ||
+ (char *)de+de->d_rec_len > XIAFS_ZSIZE(inode->i_sb)+bh->b_data ||
+ de->d_name_len < 1 || de->d_name_len + 8 > de->d_rec_len ||
+ de->d_name_len > _XIAFS_NAME_LEN ||
+ de->d_name[de->d_name_len] ) {
+ printk("XIA-FS: bad directory entry (%s %d)\n", WHERE_ERR);
+ brelse(bh);
+ return 0;
+ }
+ offset += de->d_rec_len;
+ filp->f_pos += de->d_rec_len;
+ if (de->d_ino) {
+ for (i = 0; i < de->d_name_len ; i++)
+ put_fs_byte(de->d_name[i],i+dirent->d_name);
+ put_fs_byte(0,i+dirent->d_name);
+ 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;
+ return i;
+ }
+ de = (struct xiafs_direct *) (offset + bh->b_data);
+ }
+ brelse(bh);
+ if (offset > XIAFS_ZSIZE(inode->i_sb)) {
+ printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR);
+ return 0;
+ }
+ }
+ inode->i_atime=CURRENT_TIME;
+ return 0;
+}
+
+
+
diff --git a/fs/xiafs/file.c b/fs/xiafs/file.c
new file mode 100644
index 0000000..4ccd327
--- /dev/null
+++ b/fs/xiafs/file.c
@@ -0,0 +1,251 @@
+/*
+ * linux/fs/xiafs/file.c
+ *
+ * Copyright (C) Q. Frank Xia, 1993.
+ *
+ * Based on Linus' minix/file.c
+ * Copyright (C) Linus Torvalds, 1991, 1992.
+ *
+ * This software may be redistributed per Linux Copyright.
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/xia_fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+
+#include "xiafs_mac.h"
+
+#define NBUF 16
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+static int xiafs_file_read(struct inode *, struct file *, char *, int);
+static int xiafs_file_write(struct inode *, struct file *, char *, int);
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the xiafs filesystem.
+ */
+static struct file_operations xiafs_file_operations = {
+ NULL, /* lseek - default */
+ xiafs_file_read, /* read */
+ xiafs_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ NULL, /* no special open is needed */
+ NULL, /* release */
+ NULL /* fsync */
+};
+
+struct inode_operations xiafs_file_inode_operations = {
+ &xiafs_file_operations, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ xiafs_bmap, /* bmap */
+ xiafs_truncate, /* truncate */
+ NULL /* permission */
+};
+
+static int
+xiafs_file_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ int read, left, chars;
+ int zone_nr, zones, f_zones, offset;
+ int bhrequest, uptodate;
+ struct buffer_head ** bhb, ** bhe;
+ struct buffer_head * bhreq[NBUF];
+ struct buffer_head * buflist[NBUF];
+
+ if (!inode) {
+ printk("XIA-FS: inode = NULL (%s %d)\n", WHERE_ERR);
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("XIA-FS: mode != regular (%s %d)\n", WHERE_ERR);
+ return -EINVAL;
+ }
+ offset = filp->f_pos;
+ left = inode->i_size - offset;
+ if (left > count)
+ left = count;
+ if (left <= 0)
+ return 0;
+ read = 0;
+ zone_nr = offset >> XIAFS_ZSIZE_BITS(inode->i_sb);
+ offset &= XIAFS_ZSIZE(inode->i_sb) -1 ;
+ f_zones =(inode->i_size+XIAFS_ZSIZE(inode->i_sb)-1)>>XIAFS_ZSIZE_BITS(inode->i_sb);
+ zones = (left+offset+XIAFS_ZSIZE(inode->i_sb)-1) >> XIAFS_ZSIZE_BITS(inode->i_sb);
+ bhb = bhe = buflist;
+ if (filp->f_reada) {
+ zones += read_ahead[MAJOR(inode->i_dev)] >> (1+XIAFS_ZSHIFT(inode->i_sb));
+ if (zone_nr + zones > f_zones)
+ zones = f_zones - zone_nr;
+ }
+
+ /* We do this in a two stage process. We first try and request
+ as many blocks as we can, then we wait for the first one to
+ complete, and then we try and wrap up as many as are actually
+ done. This routine is rather generic, in that it can be used
+ in a filesystem by substituting the appropriate function in
+ for getblk.
+
+ This routine is optimized to make maximum use of the various
+ buffers and caches. */
+
+ do {
+ bhrequest = 0;
+ uptodate = 1;
+ while (zones--) {
+ *bhb = xiafs_getblk(inode, zone_nr++, 0);
+ if (*bhb && !(*bhb)->b_uptodate) {
+ uptodate = 0;
+ bhreq[bhrequest++] = *bhb;
+ }
+
+ if (++bhb == &buflist[NBUF])
+ bhb = buflist;
+
+ /* If the block we have on hand is uptodate, go ahead
+ and complete processing. */
+ if (uptodate)
+ break;
+ if (bhb == bhe)
+ break;
+ }
+
+ /* Now request them all */
+ if (bhrequest)
+ ll_rw_block(READ, bhrequest, bhreq);
+
+ do { /* Finish off all I/O that has actually completed */
+ if (*bhe) {
+ wait_on_buffer(*bhe);
+ if (!(*bhe)->b_uptodate) { /* read error? */
+ left = 0;
+ break;
+ }
+ }
+ if (left < XIAFS_ZSIZE(inode->i_sb) - offset)
+ chars = left;
+ else
+ chars = XIAFS_ZSIZE(inode->i_sb) - offset;
+ filp->f_pos += chars;
+ left -= chars;
+ read += chars;
+ if (*bhe) {
+ memcpy_tofs(buf,offset+(*bhe)->b_data,chars);
+ brelse(*bhe);
+ buf += chars;
+ } else {
+ while (chars-->0)
+ put_fs_byte(0,buf++);
+ }
+ offset = 0;
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ } while (left > 0 && bhe != bhb && (!*bhe || !(*bhe)->b_lock));
+ } while (left > 0);
+
+/* Release the read-ahead blocks */
+ while (bhe != bhb) {
+ brelse(*bhe);
+ if (++bhe == &buflist[NBUF])
+ bhe = buflist;
+ };
+ if (!read)
+ return -EIO;
+ filp->f_reada = 1;
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+
+ return read;
+}
+
+static int
+xiafs_file_write(struct inode * inode, struct file * filp, char * buf, int count)
+{
+ off_t pos;
+ int written, c;
+ struct buffer_head * bh;
+ char * cp;
+
+ if (!inode) {
+ printk("XIA-FS: inode = NULL (%s %d)\n", WHERE_ERR);
+ return -EINVAL;
+ }
+ if (!S_ISREG(inode->i_mode)) {
+ printk("XIA-FS: mode != regular (%s %d)\n", WHERE_ERR);
+ return -EINVAL;
+ }
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+ if (filp->f_flags & O_APPEND)
+ pos = inode->i_size;
+ else
+ pos = filp->f_pos;
+ written = 0;
+ while (written < count) {
+ bh = xiafs_getblk(inode, pos >> XIAFS_ZSIZE_BITS(inode->i_sb), 1);
+ if (!bh) {
+ if (!written)
+ written = -ENOSPC;
+ break;
+ }
+ c = XIAFS_ZSIZE(inode->i_sb) - (pos & (XIAFS_ZSIZE(inode->i_sb) - 1));
+ if (c > count-written)
+ c = count-written;
+ if (c != XIAFS_ZSIZE(inode->i_sb) && !bh->b_uptodate) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!bh->b_uptodate) {
+ brelse(bh);
+ if (!written)
+ written = -EIO;
+ break;
+ }
+ }
+ cp = (pos & (XIAFS_ZSIZE(inode->i_sb)-1)) + bh->b_data;
+ pos += c;
+ if (pos > inode->i_size) {
+ inode->i_size = pos;
+ inode->i_dirt = 1;
+ }
+ written += c;
+ memcpy_fromfs(cp,buf,c);
+ buf += c;
+ bh->b_uptodate = 1;
+ bh->b_dirt = 1;
+ brelse(bh);
+ }
+ inode->i_mtime = CURRENT_TIME;
+ filp->f_pos = pos;
+ inode->i_dirt = 1;
+
+ return written;
+}
+
+
+
+
diff --git a/fs/xiafs/inode.c b/fs/xiafs/inode.c
new file mode 100644
index 0000000..823cc76
--- /dev/null
+++ b/fs/xiafs/inode.c
@@ -0,0 +1,450 @@
+/*
+ * linux/fs/xiafs/inode.c
+ *
+ * Copyright (C) Q. Frank Xia, 1993.
+ *
+ * Based on Linus' minix/inode.c
+ * Copyright (C) Linus Torvalds, 1991, 1992.
+ *
+ * This software may be redistributed per Linux Copyright.
+ */
+
+#include <linux/sched.h>
+#include <linux/xia_fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#include "xiafs_mac.h"
+
+static u_long random_nr;
+
+void xiafs_put_inode(struct inode *inode)
+{
+ if (inode->i_nlink)
+ return;
+ inode->i_size = 0;
+ xiafs_truncate(inode);
+ xiafs_free_inode(inode);
+}
+
+void xiafs_put_super(struct super_block *sb)
+{
+ int i;
+
+ lock_super(sb);
+ sb->s_dev = 0;
+ for(i = 0 ; i < _XIAFS_IMAP_SLOTS ; i++)
+ brelse(sb->u.xiafs_sb.s_imap_buf[i]);
+ for(i = 0 ; i < _XIAFS_ZMAP_SLOTS ; i++)
+ brelse(sb->u.xiafs_sb.s_zmap_buf[i]);
+ unlock_super(sb);
+}
+
+static struct super_operations xiafs_sops = {
+ xiafs_read_inode,
+ NULL,
+ xiafs_write_inode,
+ xiafs_put_inode,
+ xiafs_put_super,
+ NULL,
+ xiafs_statfs
+};
+
+struct super_block *xiafs_read_super(struct super_block *s, void *data)
+{
+ struct buffer_head *bh;
+ struct xiafs_super_block *sp;
+ int i, z, dev;
+
+ dev=s->s_dev;
+ lock_super(s);
+ if (!(bh = bread(dev, 0, BLOCK_SIZE))) {
+ s->s_dev=0;
+ unlock_super(s);
+ printk("XIA-FS: read super_block failed (%s %d)\n", WHERE_ERR);
+ return NULL;
+ }
+ sp = (struct xiafs_super_block *) bh->b_data;
+ s->s_magic = sp->s_magic;
+ if (s->s_magic != _XIAFS_SUPER_MAGIC) {
+ s->s_dev = 0;
+ unlock_super(s);
+ brelse(bh);
+ printk("XIA-FS: super magic mismatch\n");
+ return NULL;
+ }
+ s->s_blocksize = sp->s_zone_size;
+ s->u.xiafs_sb.s_nzones = sp->s_nzones;
+ s->u.xiafs_sb.s_ninodes = sp->s_ninodes;
+ s->u.xiafs_sb.s_ndatazones = sp->s_ndatazones;
+ s->u.xiafs_sb.s_imap_zones = sp->s_imap_zones;
+ s->u.xiafs_sb.s_zmap_zones = sp->s_zmap_zones;
+ s->u.xiafs_sb.s_firstdatazone = sp->s_firstdatazone;
+ s->u.xiafs_sb.s_zone_shift = sp->s_zone_shift;
+ s->u.xiafs_sb.s_max_size = sp->s_max_size;
+ brelse(bh);
+ for (i=0;i < _XIAFS_IMAP_SLOTS;i++) {
+ s->u.xiafs_sb.s_imap_buf[i] = NULL;
+ s->u.xiafs_sb.s_imap_iznr[i] = -1;
+ }
+ for (i=0;i < _XIAFS_ZMAP_SLOTS;i++) {
+ s->u.xiafs_sb.s_zmap_buf[i] = NULL;
+ s->u.xiafs_sb.s_zmap_zznr[i] = -1;
+ }
+ z=1;
+ if ( s->u.xiafs_sb.s_imap_zones > _XIAFS_IMAP_SLOTS )
+ s->u.xiafs_sb.s_imap_cached=1;
+ else {
+ s->u.xiafs_sb.s_imap_cached=0;
+ for (i=0 ; i < s->u.xiafs_sb.s_imap_zones ; i++) {
+ if (!(s->u.xiafs_sb.s_imap_buf[i]=bread(dev, z++, XIAFS_ZSIZE(s))))
+ goto xiafs_read_super_fail;
+ s->u.xiafs_sb.s_imap_iznr[i]=i;
+ }
+ }
+ if ( s->u.xiafs_sb.s_zmap_zones > _XIAFS_ZMAP_SLOTS )
+ s->u.xiafs_sb.s_zmap_cached=1;
+ else {
+ s->u.xiafs_sb.s_zmap_cached=0;
+ for (i=0 ; i < s->u.xiafs_sb.s_zmap_zones ; i++) {
+ if (!(s->u.xiafs_sb.s_zmap_buf[i]=bread(dev, z++, XIAFS_ZSIZE(s))))
+ goto xiafs_read_super_fail;
+ s->u.xiafs_sb.s_zmap_zznr[i]=i;
+ }
+ }
+ /* set up enough so that it can read an inode */
+ s->s_dev = dev;
+ s->s_op = &xiafs_sops;
+ s->s_mounted = iget(s, _XIAFS_ROOT_INO);
+ if (!s->s_mounted)
+ goto xiafs_read_super_fail;
+ unlock_super(s);
+ random_nr=CURRENT_TIME;
+ return s;
+
+xiafs_read_super_fail:
+ for(i=0; i < _XIAFS_IMAP_SLOTS; i++)
+ brelse(s->u.xiafs_sb.s_imap_buf[i]);
+ for(i=0; i < _XIAFS_ZMAP_SLOTS; i++)
+ brelse(s->u.xiafs_sb.s_zmap_buf[i]);
+ s->s_dev=0;
+ unlock_super(s);
+ printk("XIA-FS: read bitmaps failed (%s %d)\n", WHERE_ERR);
+ return NULL;
+}
+
+void xiafs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ long tmp;
+
+ put_fs_long(_XIAFS_SUPER_MAGIC, &buf->f_type);
+ put_fs_long(XIAFS_ZSIZE(sb), &buf->f_bsize);
+ put_fs_long(sb->u.xiafs_sb.s_ndatazones, &buf->f_blocks);
+ tmp = xiafs_count_free_zones(sb);
+ put_fs_long(tmp, &buf->f_bfree);
+ put_fs_long(tmp, &buf->f_bavail);
+ put_fs_long(sb->u.xiafs_sb.s_ninodes, &buf->f_files);
+ put_fs_long(xiafs_count_free_inodes(sb), &buf->f_ffree);
+ /* don't know what should be put in buf->f_fsid */
+}
+
+static int zone_bmap(struct buffer_head * bh, int nr)
+{
+ int tmp;
+
+ if (!bh)
+ return 0;
+ tmp = ((u_long *) bh->b_data)[nr];
+ brelse(bh);
+ return tmp;
+}
+
+int xiafs_bmap(struct inode * inode,int zone)
+{
+ int i;
+
+ if (zone < 0) {
+ printk("XIA-FS: block < 0 (%s %d)\n", WHERE_ERR);
+ return 0;
+ }
+ if (zone >= 8+(1+XIAFS_ADDRS_PER_Z(inode->i_sb))*XIAFS_ADDRS_PER_Z(inode->i_sb)) {
+ printk("XIA-FS: zone > big (%s %d)\n", WHERE_ERR);
+ return 0;
+ }
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ if (zone < 8)
+ return inode->u.xiafs_i.i_zone[zone];
+ zone -= 8;
+ if (zone < XIAFS_ADDRS_PER_Z(inode->i_sb)) {
+ i = inode->u.xiafs_i.i_ind_zone;
+ if (i)
+ i = zone_bmap(bread(inode->i_dev, i, XIAFS_ZSIZE(inode->i_sb)), zone);
+ return i;
+ }
+ zone -= XIAFS_ADDRS_PER_Z(inode->i_sb);
+ i = inode->u.xiafs_i.i_dind_zone;
+ if (i)
+ i = zone_bmap(bread(inode->i_dev, i, XIAFS_ZSIZE(inode->i_sb)),
+ zone >> XIAFS_ADDRS_PER_Z_BITS(inode->i_sb));
+ if (i)
+ i= zone_bmap(bread(inode->i_dev,i, XIAFS_ZSIZE(inode->i_sb)),
+ zone & (XIAFS_ADDRS_PER_Z(inode->i_sb)-1));
+ return i;
+}
+
+static u_long get_prev_addr(struct inode * inode, int zone)
+{
+ u_long tmp;
+
+ if (zone > 0)
+ while (--zone >= 0) /* only files with holes suffer */
+ if ((tmp=xiafs_bmap(inode, zone)))
+ return tmp;
+ random_nr=(random_nr+23)%inode->i_sb->u.xiafs_sb.s_ndatazones;
+ return random_nr + inode->i_sb->u.xiafs_sb.s_firstdatazone;
+}
+
+static struct buffer_head *
+dt_getblk(struct inode * inode, u_long *lp, int create, u_long prev_addr)
+{
+ int tmp;
+ struct buffer_head * result;
+
+repeat:
+ if ((tmp=*lp)) {
+ result = getblk(inode->i_dev, tmp, XIAFS_ZSIZE(inode->i_sb));
+ if (tmp == *lp)
+ return result;
+ brelse(result);
+ goto repeat;
+ }
+ if (!create)
+ return NULL;
+ tmp = xiafs_new_zone(inode->i_sb, prev_addr);
+ if (!tmp)
+ return NULL;
+ result = getblk(inode->i_dev, tmp, XIAFS_ZSIZE(inode->i_sb));
+ if (*lp) {
+ xiafs_free_zone(inode->i_sb, tmp);
+ brelse(result);
+ goto repeat;
+ }
+ *lp = tmp;
+ return result;
+}
+
+static struct buffer_head *
+indt_getblk(struct inode * inode, struct buffer_head * bh,
+ int nr, int create, u_long prev_addr)
+{
+ int tmp;
+ u_long *lp;
+ struct buffer_head * result;
+
+ if (!bh)
+ return NULL;
+ if (!bh->b_uptodate) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!bh->b_uptodate) {
+ brelse(bh);
+ return NULL;
+ }
+ }
+ lp = nr + (u_long *) bh->b_data;
+repeat:
+ if ((tmp=*lp)) {
+ result = getblk(bh->b_dev, tmp, XIAFS_ZSIZE(inode->i_sb));
+ if (tmp == *lp) {
+ brelse(bh);
+ return result;
+ }
+ brelse(result);
+ goto repeat;
+ }
+ if (!create) {
+ brelse(bh);
+ return NULL;
+ }
+ tmp = xiafs_new_zone(inode->i_sb, prev_addr);
+ if (!tmp) {
+ brelse(bh);
+ return NULL;
+ }
+ result = getblk(bh->b_dev, tmp, XIAFS_ZSIZE(inode->i_sb));
+ if (*lp) {
+ xiafs_free_zone(inode->i_sb,tmp);
+ brelse(result);
+ goto repeat;
+ }
+ *lp = tmp;
+ bh->b_dirt = 1;
+ brelse(bh);
+ return result;
+}
+
+struct buffer_head * xiafs_getblk(struct inode * inode, int zone, int create)
+{
+ struct buffer_head * bh;
+ u_long prev_addr=0;
+
+ if (zone<0) {
+ printk("XIA-FS: zone < 0 (%s %d)\n", WHERE_ERR);
+ return NULL;
+ }
+ if (zone >= 8+(1+XIAFS_ADDRS_PER_Z(inode->i_sb))*XIAFS_ADDRS_PER_Z(inode->i_sb)) {
+ if (!create)
+ printk("XIA-FS: zone > big (%s %d)\n", WHERE_ERR);
+ return NULL;
+ }
+ if (create)
+ prev_addr=get_prev_addr(inode, zone);
+ if (zone < 8)
+ return dt_getblk(inode, zone+inode->u.xiafs_i.i_zone, create, prev_addr);
+ zone -= 8;
+ if (zone < XIAFS_ADDRS_PER_Z(inode->i_sb)) {
+ bh = dt_getblk(inode, &(inode->u.xiafs_i.i_ind_zone), create, prev_addr);
+ bh = indt_getblk(inode, bh, zone, create, prev_addr);
+ return bh;
+ }
+ zone -= XIAFS_ADDRS_PER_Z(inode->i_sb);
+ bh = dt_getblk(inode, &(inode->u.xiafs_i.i_dind_zone), create, prev_addr);
+ bh = indt_getblk(inode, bh, zone>>XIAFS_ADDRS_PER_Z_BITS(inode->i_sb),
+ create, prev_addr);
+ bh = indt_getblk(inode, bh, zone&(XIAFS_ADDRS_PER_Z(inode->i_sb)-1),
+ create, prev_addr);
+ return bh;
+}
+
+struct buffer_head * xiafs_bread(struct inode * inode, int zone, int create)
+{
+ struct buffer_head * bh;
+
+ bh = xiafs_getblk(inode, zone, create);
+ if (!bh || bh->b_uptodate)
+ return bh;
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (bh->b_uptodate)
+ return bh;
+ brelse(bh);
+ return NULL;
+}
+
+void xiafs_read_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct xiafs_inode * raw_inode;
+ int zone;
+ ino_t ino;
+
+ ino = inode->i_ino;
+ inode->i_op = NULL;
+ inode->i_mode=0;
+ if (!ino || ino > inode->i_sb->u.xiafs_sb.s_ninodes) {
+ printk("XIA-FS: bad inode number (%s %d)\n", WHERE_ERR);
+ return;
+ }
+ zone = 1 + inode->i_sb->u.xiafs_sb.s_imap_zones +
+ inode->i_sb->u.xiafs_sb.s_zmap_zones +
+ (ino-1)/ XIAFS_INODES_PER_Z(inode->i_sb);
+ if (!(bh=bread(inode->i_dev, zone, XIAFS_ZSIZE(inode->i_sb)))) {
+ printk("XIA-FS: read i-node zone failed (%s %d)\n", WHERE_ERR);
+ return;
+ }
+ raw_inode = ((struct xiafs_inode *) bh->b_data) +
+ ((ino-1) & (XIAFS_INODES_PER_Z(inode->i_sb) - 1));
+ inode->i_mode = raw_inode->i_mode;
+ inode->i_uid = raw_inode->i_uid;
+ inode->i_gid = raw_inode->i_gid;
+ inode->i_nlink = raw_inode->i_nlinks;
+ inode->i_size = raw_inode->i_size;
+ 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_rdev = raw_inode->i_zone[0];
+ else {
+ 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;
+ }
+ brelse(bh);
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &xiafs_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode))
+ inode->i_op = &xiafs_dir_inode_operations;
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &xiafs_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode)) {
+ inode->i_op = &fifo_inode_operations;
+ inode->i_pipe = 1;
+ PIPE_BASE(*inode) = NULL;
+ PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;
+ PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL;
+ PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0;
+ }
+}
+
+void xiafs_write_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct xiafs_inode * raw_inode;
+ int zone;
+ ino_t ino;
+
+ 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);
+ inode->i_dirt=0;
+ return;
+ }
+ zone = 1 + inode->i_sb->u.xiafs_sb.s_imap_zones +
+ inode->i_sb->u.xiafs_sb.s_zmap_zones +
+ (ino-1) / XIAFS_INODES_PER_Z(inode->i_sb);
+ if (!(bh=bread(inode->i_dev, zone, XIAFS_ZSIZE(inode->i_sb)))) {
+ printk("XIA-FS: read i-node zone failed (%s %d)\n", WHERE_ERR);
+ inode->i_dirt=0;
+ return;
+ }
+ raw_inode = ((struct xiafs_inode *)bh->b_data) +
+ ((ino-1) & (XIAFS_INODES_PER_Z(inode->i_sb) -1));
+ raw_inode->i_mode = inode->i_mode;
+ raw_inode->i_uid = inode->i_uid;
+ 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 {
+ 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;
+ }
+ inode->i_dirt=0;
+ bh->b_dirt=1;
+ brelse(bh);
+}
+
+
diff --git a/fs/xiafs/namei.c b/fs/xiafs/namei.c
new file mode 100644
index 0000000..49703ee
--- /dev/null
+++ b/fs/xiafs/namei.c
@@ -0,0 +1,855 @@
+/*
+ * Linux/fs/xiafs/namei.c
+ *
+ * Copyright (C) Q. Frank Xia, 1993.
+ *
+ * Based on Linus' minix/namei.c
+ * Copyright (C) Linus Torvalds, 1991, 1992.
+ *
+ * This software may be redistributed per Linux Copyright.
+ */
+
+#include <linux/sched.h>
+#include <linux/xia_fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+#include <asm/segment.h>
+
+#include "xiafs_mac.h"
+
+#define RNDUP4(x) ((3+(u_long)(x)) & ~3)
+/*
+ * ok, we cannot use strncmp, as the name is not in our data space.
+ * Thus we'll have to use xiafs_match. No big problem. Match also makes
+ * some sanity tests.
+ *
+ * NOTE! unlike strncmp, xiafs_match returns 1 for success, 0 for failure.
+ */
+static int xiafs_match(int len, const char * name, struct xiafs_direct * dep)
+{
+ int i;
+
+ if (!dep || !dep->d_ino || len > _XIAFS_NAME_LEN)
+ return 0;
+ /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
+ if (!len && (dep->d_name[0]=='.') && (dep->d_name[1]=='\0'))
+ return 1;
+ if (len < _XIAFS_NAME_LEN && dep->d_name[len])
+ return 0;
+ for (i=0; i < len; i++)
+ if (*name++ != dep->d_name[i])
+ break;
+ return (i==len) ? 1 : 0;
+}
+
+/*
+ * xiafs_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+ * itself (as a parameter - res_dir). It does NOT read the inode of the
+ * entry - you'll have to do that yourself if you want to.
+ */
+static struct buffer_head *
+xiafs_find_entry(struct inode * inode, const char * name, int namelen,
+ struct xiafs_direct ** res_dir, struct xiafs_direct ** res_pre)
+{
+ int i, zones, pos;
+ struct buffer_head * bh;
+ struct xiafs_direct * dep, * dep_pre;
+
+ *res_dir = NULL;
+ if (!inode)
+ return NULL;
+ if (namelen > _XIAFS_NAME_LEN)
+ return NULL;
+
+ if (inode->i_size & (XIAFS_ZSIZE(inode->i_sb) - 1)) {
+ printk("XIA-FS: bad dir size (%s %d)\n", WHERE_ERR);
+ return NULL;
+ }
+ zones=inode->i_size >> XIAFS_ZSIZE_BITS(inode->i_sb);
+ for (i=0; i < zones; i++ ) {
+ bh = xiafs_bread(inode, i, 0);
+ if (!bh)
+ continue;
+ dep_pre=dep=(struct xiafs_direct *)bh->b_data;
+ if (!i && (dep->d_rec_len != 12 || !dep->d_ino ||
+ dep->d_name_len != 1 || strcmp(dep->d_name, "."))) {
+ printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR);
+ brelse(bh);
+ return NULL;
+ }
+ pos = 0;
+ while ( pos < XIAFS_ZSIZE(inode->i_sb) ) {
+ if (dep->d_ino > inode->i_sb->u.xiafs_sb.s_ninodes ||
+ dep->d_rec_len < 12 ||
+ dep->d_rec_len+(char *)dep > bh->b_data+XIAFS_ZSIZE(inode->i_sb) ||
+ dep->d_name_len + 8 > dep->d_rec_len || dep->d_name_len <= 0 ||
+ dep->d_name[dep->d_name_len] ) {
+ brelse(bh);
+ return NULL;
+ }
+ if (xiafs_match(namelen, name, dep)) {
+ *res_dir=dep;
+ if (res_pre)
+ *res_pre=dep_pre;
+ return bh;
+ }
+ pos += dep->d_rec_len;
+ dep_pre=dep;
+ dep=(struct xiafs_direct *)(bh->b_data + pos);
+ }
+ brelse(bh);
+ if (pos > XIAFS_ZSIZE(inode->i_sb)) {
+ printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR);
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+int xiafs_lookup(struct inode * dir, const char * name, int len,
+ struct inode ** result)
+{
+ int ino;
+ struct xiafs_direct * dep;
+ struct buffer_head * bh;
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+ if (!S_ISDIR(dir->i_mode)) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!(bh = xiafs_find_entry(dir, name, len, &dep, NULL))) {
+ iput(dir);
+ return -ENOENT;
+ }
+ ino = dep->d_ino;
+ brelse(bh);
+ if (!(*result = iget(dir->i_sb, ino))) {
+ iput(dir);
+ return -EACCES;
+ }
+ iput(dir);
+ return 0;
+}
+
+/*
+ * xiafs_add_entry()
+ *
+ * adds a file entry to the specified directory, using the same
+ * semantics as xiafs_find_entry(). It returns NULL if it failed.
+ *
+ * NOTE!! The inode part of 'de' is left at 0 - which means you
+ * may not sleep between calling this and putting something into
+ * the entry, as someone else might have used it while you slept.
+ */
+static struct buffer_head * xiafs_add_entry(struct inode * dir,
+ const char * name, int namelen, struct xiafs_direct ** res_dir,
+ struct xiafs_direct ** res_pre)
+{
+ int i, pos, offset;
+ struct buffer_head * bh;
+ struct xiafs_direct * de, * de_pre;
+
+ *res_dir = NULL;
+ if (!dir || !namelen || namelen > _XIAFS_NAME_LEN)
+ return NULL;
+
+ if (dir->i_size & (XIAFS_ZSIZE(dir->i_sb) - 1)) {
+ printk("XIA-FS: bad dir size (%s %d)\n", WHERE_ERR);
+ return NULL;
+ }
+ pos=0;
+ for ( ; ; ) {
+ bh = xiafs_bread(dir, pos >> XIAFS_ZSIZE_BITS(dir->i_sb), pos ? 1:0);
+ if (!bh)
+ return NULL;
+ de_pre=de=(struct xiafs_direct *)bh->b_data;
+ if (!pos) {
+ if (de->d_rec_len != 12 || !de->d_ino || de->d_name_len != 1 ||
+ strcmp(de->d_name, ".")) {
+ printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR);
+ brelse(bh);
+ return NULL;
+ }
+ offset = 12;
+ de_pre=de=(struct xiafs_direct *)(bh->b_data+12);
+ } else
+ offset = 0;
+ while (offset < XIAFS_ZSIZE(dir->i_sb)) {
+ if (pos >= dir->i_size) {
+ de->d_ino=0;
+ de->d_name_len=0;
+ de->d_name[0]=0;
+ de->d_rec_len=XIAFS_ZSIZE(dir->i_sb);
+ dir->i_size += XIAFS_ZSIZE(dir->i_sb);
+ dir->i_dirt = 1;
+ } else {
+ if (de->d_ino > dir->i_sb->u.xiafs_sb.s_ninodes ||
+ de->d_rec_len < 12 ||
+ (char *)de+de->d_rec_len > bh->b_data+XIAFS_ZSIZE(dir->i_sb) ||
+ de->d_name_len + 8 > de->d_rec_len ||
+ de->d_name[de->d_name_len]) {
+ printk("XIA-FS: bad directory entry (%s %d)\n", WHERE_ERR);
+ brelse(bh);
+ return NULL;
+ }
+ if (de->d_ino &&
+ RNDUP4(de->d_name_len)+RNDUP4(namelen)+16<=de->d_rec_len) {
+ i=RNDUP4(de->d_name_len)+8;
+ de_pre=de;
+ de=(struct xiafs_direct *)(i+(u_char *)de_pre);
+ de->d_ino=0;
+ de->d_rec_len=de_pre->d_rec_len-i;
+ de_pre->d_rec_len=i;
+ }
+ }
+ if (!de->d_ino && RNDUP4(namelen)+8 <= de->d_rec_len) {
+ dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt = 1;
+ memcpy(de->d_name, name, namelen);
+ de->d_name[namelen]=0;
+ de->d_name_len=namelen;
+ bh->b_dirt = 1;
+ *res_dir = de;
+ if (res_pre)
+ *res_pre = de_pre;
+ return bh;
+ }
+ offset+=de->d_rec_len;
+ de_pre=de;
+ de=(struct xiafs_direct *)(bh->b_data+offset);
+ }
+ brelse(bh);
+ if (offset > XIAFS_ZSIZE(dir->i_sb)) {
+ printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR);
+ return NULL;
+ }
+ pos+=XIAFS_ZSIZE(dir->i_sb);
+ }
+ return NULL;
+}
+
+int xiafs_create(struct inode * dir, const char * name, int len, int mode,
+ struct inode ** result)
+{
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct xiafs_direct * de;
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+ inode = xiafs_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &xiafs_file_inode_operations;
+ inode->i_mode = mode;
+ inode->i_dirt = 1;
+ bh = xiafs_add_entry(dir, name, len, &de, NULL);
+ if (!bh) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ iput(dir);
+ return -ENOSPC;
+ }
+ de->d_ino = inode->i_ino;
+ bh->b_dirt = 1;
+ brelse(bh);
+ iput(dir);
+ *result = inode;
+ return 0;
+}
+
+int xiafs_mknod(struct inode *dir, const char *name, int len, int mode, int rdev)
+{
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct xiafs_direct * de;
+
+ if (!dir)
+ return -ENOENT;
+ bh = xiafs_find_entry(dir,name,len,&de, NULL);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ inode = xiafs_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_uid = current->euid;
+ inode->i_mode = mode;
+ inode->i_op = NULL;
+ if (S_ISREG(inode->i_mode))
+ inode->i_op = &xiafs_file_inode_operations;
+ else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &xiafs_dir_inode_operations;
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ }
+ else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &xiafs_symlink_inode_operations;
+ else if (S_ISCHR(inode->i_mode))
+ inode->i_op = &chrdev_inode_operations;
+ else if (S_ISBLK(inode->i_mode))
+ inode->i_op = &blkdev_inode_operations;
+ else if (S_ISFIFO(inode->i_mode)) {
+ inode->i_op = &fifo_inode_operations;
+ inode->i_pipe = 1;
+ PIPE_BASE(*inode) = NULL;
+ PIPE_HEAD(*inode) = PIPE_TAIL(*inode) = 0;
+ PIPE_READ_WAIT(*inode) = PIPE_WRITE_WAIT(*inode) = NULL;
+ PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0;
+ }
+ if (S_ISBLK(mode) || S_ISCHR(mode))
+ inode->i_rdev = rdev;
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ bh = xiafs_add_entry(dir, name, len, &de, NULL);
+ if (!bh) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ iput(dir);
+ return -ENOSPC;
+ }
+ de->d_ino = inode->i_ino;
+ bh->b_dirt = 1;
+ brelse(bh);
+ iput(dir);
+ iput(inode);
+ return 0;
+}
+
+int xiafs_mkdir(struct inode * dir, const char * name, int len, int mode)
+{
+ struct inode * inode;
+ struct buffer_head * bh, *dir_block;
+ struct xiafs_direct * de;
+
+ bh = xiafs_find_entry(dir,name,len,&de, NULL);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ if (dir->i_nlink > 64000) {
+ iput(dir);
+ return -EMLINK;
+ }
+ inode = xiafs_new_inode(dir);
+ if (!inode) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_op = &xiafs_dir_inode_operations;
+ inode->i_size = XIAFS_ZSIZE(dir->i_sb);
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
+ dir_block = xiafs_bread(inode,0,1);
+ if (!dir_block) {
+ iput(dir);
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ return -ENOSPC;
+ }
+ de = (struct xiafs_direct *) dir_block->b_data;
+ de->d_ino=inode->i_ino;
+ strcpy(de->d_name,".");
+ de->d_name_len=1;
+ de->d_rec_len=12;
+ de =(struct xiafs_direct *)(12 + dir_block->b_data);
+ de->d_ino = dir->i_ino;
+ strcpy(de->d_name,"..");
+ de->d_name_len=2;
+ de->d_rec_len=XIAFS_ZSIZE(dir->i_sb)-12;
+ inode->i_nlink = 2;
+ dir_block->b_dirt = 1;
+ brelse(dir_block);
+ inode->i_mode = S_IFDIR | (mode & 0777 & ~current->umask);
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ inode->i_dirt = 1;
+ bh = xiafs_add_entry(dir, name, len, &de, NULL);
+ if (!bh) {
+ iput(dir);
+ inode->i_nlink=0;
+ iput(inode);
+ return -ENOSPC;
+ }
+ de->d_ino = inode->i_ino;
+ bh->b_dirt = 1;
+ dir->i_nlink++;
+ dir->i_dirt = 1;
+ iput(dir);
+ iput(inode);
+ brelse(bh);
+ return 0;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+static int empty_dir(struct inode * inode)
+{
+ int i, zones, offset;
+ struct buffer_head * bh;
+ struct xiafs_direct * de;
+
+ if (inode->i_size & (XIAFS_ZSIZE(inode->i_sb)-1) ) {
+ printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR);
+ return 1;
+ }
+
+ zones=inode->i_size >> XIAFS_ZSIZE_BITS(inode->i_sb);
+ for (i=0; i < zones; i++) {
+ bh = xiafs_bread(inode, i, 0);
+ if (!i) {
+ if (!bh) {
+ printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR);
+ return 1;
+ }
+ de=(struct xiafs_direct *)bh->b_data;
+ if (de->d_ino != inode->i_ino || strcmp(".", de->d_name) ||
+ de->d_rec_len != 12 ) {
+ printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR);
+ brelse(bh);
+ return 1;
+ }
+ de=(struct xiafs_direct *)(12 + bh->b_data);
+ if (!de->d_ino || strcmp("..", de->d_name)) {
+ printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR);
+ brelse(bh);
+ return 1;
+ }
+ offset=de->d_rec_len+12;
+ }
+ else
+ offset = 0;
+ if (!bh)
+ continue;
+ while (offset < XIAFS_ZSIZE(inode->i_sb)) {
+ de=(struct xiafs_direct *)(bh->b_data+offset);
+ if (de->d_ino > inode->i_sb->u.xiafs_sb.s_ninodes ||
+ de->d_rec_len < 12 ||
+ (char *)de+de->d_rec_len > bh->b_data+XIAFS_ZSIZE(inode->i_sb) ||
+ de->d_name_len + 8 > de->d_rec_len ||
+ de->d_name[de->d_name_len]) {
+ printk("XIA-FS: bad directory (%s %d)\n", WHERE_ERR);
+ brelse(bh);
+ return 1;
+ }
+ if (de->d_ino) {
+ brelse(bh);
+ return 0;
+ }
+ offset+=de->d_rec_len;
+ }
+ brelse(bh);
+ }
+ return 1;
+}
+
+static void xiafs_rm_entry(struct xiafs_direct *de, struct xiafs_direct * de_pre)
+{
+ if (de==de_pre) {
+ de->d_ino=0;
+ return;
+ }
+ while (de_pre->d_rec_len+(u_char *)de_pre < (u_char *)de) {
+ if (de_pre->d_rec_len < 12) {
+ printk("XIA-FS: bad directory entry (%s %d)\n", WHERE_ERR);
+ return;
+ }
+ de_pre=(struct xiafs_direct *)(de_pre->d_rec_len+(u_char *)de_pre);
+ }
+ if (de_pre->d_rec_len+(u_char *)de_pre > (u_char *)de) {
+ printk("XIA-FS: bad directory entry (%s %d)\n", WHERE_ERR);
+ return;
+ }
+ de_pre->d_rec_len+=de->d_rec_len;
+}
+
+int xiafs_rmdir(struct inode * dir, const char * name, int len)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct xiafs_direct * de, * de_pre;
+
+ inode = NULL;
+ bh = xiafs_find_entry(dir, name, len, &de, &de_pre);
+ retval = -ENOENT;
+ if (!bh)
+ goto end_rmdir;
+ retval = -EPERM;
+ if (!(inode = iget(dir->i_sb, de->d_ino)))
+ goto end_rmdir;
+ if ((dir->i_mode & S_ISVTX) && current->euid &&
+ inode->i_uid != current->euid)
+ goto end_rmdir;
+ if (inode->i_dev != dir->i_dev)
+ goto end_rmdir;
+ if (inode == dir) /* we may not delete ".", but "../dir" is ok */
+ goto end_rmdir;
+ if (!S_ISDIR(inode->i_mode)) {
+ retval = -ENOTDIR;
+ goto end_rmdir;
+ }
+ if (!empty_dir(inode)) {
+ retval = -ENOTEMPTY;
+ goto end_rmdir;
+ }
+ if (inode->i_count > 1) {
+ retval = -EBUSY;
+ goto end_rmdir;
+ }
+ if (inode->i_nlink != 2)
+ printk("XIA-FS: empty directory has nlink!=2 (%s %d)\n", WHERE_ERR);
+ xiafs_rm_entry(de, de_pre);
+ bh->b_dirt = 1;
+ inode->i_nlink=0;
+ inode->i_dirt=1;
+ dir->i_nlink--;
+ dir->i_mtime = CURRENT_TIME;
+ dir->i_dirt=1;
+ retval = 0;
+end_rmdir:
+ iput(dir);
+ iput(inode);
+ brelse(bh);
+ return retval;
+}
+
+int xiafs_unlink(struct inode * dir, const char * name, int len)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct xiafs_direct * de, * de_pre;
+
+repeat:
+ retval = -ENOENT;
+ inode = NULL;
+ bh = xiafs_find_entry(dir, name, len, &de, &de_pre);
+ if (!bh)
+ goto end_unlink;
+ if (!(inode = iget(dir->i_sb, de->d_ino)))
+ goto end_unlink;
+ if (de->d_ino != inode->i_ino) {
+ iput(inode);
+ brelse(bh);
+ current->counter = 0;
+ schedule();
+ goto repeat;
+ }
+ retval = -EPERM;
+ if ((dir->i_mode & S_ISVTX) && !suser() &&
+ current->euid != inode->i_uid &&
+ current->euid != dir->i_uid)
+ goto end_unlink;
+ if (S_ISDIR(inode->i_mode))
+ goto end_unlink;
+ if (!inode->i_nlink) {
+ printk("XIA-FS: Deleting nonexistent file (%s %d)\n", WHERE_ERR);
+ inode->i_nlink=1;
+ }
+ xiafs_rm_entry(de, de_pre);
+ bh->b_dirt = 1;
+ 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:
+ brelse(bh);
+ iput(inode);
+ iput(dir);
+ return retval;
+}
+
+int xiafs_symlink(struct inode * dir, const char * name,
+ int len, const char * symname)
+{
+ struct xiafs_direct * de;
+ struct inode * inode = NULL;
+ struct buffer_head * bh = NULL, * name_block = NULL;
+ int i;
+ char c;
+
+ bh = xiafs_find_entry(dir,name,len, &de, NULL);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ return -EEXIST;
+ }
+ if (!(inode = xiafs_new_inode(dir))) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ inode->i_mode = S_IFLNK | 0777;
+ inode->i_op = &xiafs_symlink_inode_operations;
+ name_block = xiafs_bread(inode,0,1);
+ if (!name_block) {
+ iput(dir);
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ return -ENOSPC;
+ }
+ for (i = 0; i < BLOCK_SIZE-1 && (c=*symname++); i++)
+ name_block->b_data[i] = c;
+ name_block->b_data[i] = 0;
+ name_block->b_dirt = 1;
+ brelse(name_block);
+ inode->i_size = i;
+ inode->i_dirt = 1;
+ bh = xiafs_add_entry(dir, name, len, &de, NULL);
+ if (!bh) {
+ inode->i_nlink--;
+ inode->i_dirt = 1;
+ iput(inode);
+ iput(dir);
+ return -ENOSPC;
+ }
+ de->d_ino = inode->i_ino;
+ bh->b_dirt = 1;
+ brelse(bh);
+ iput(dir);
+ iput(inode);
+ return 0;
+}
+
+int xiafs_link(struct inode * oldinode, struct inode * dir,
+ const char * name, int len)
+{
+ struct xiafs_direct * de;
+ struct buffer_head * bh;
+
+ if (S_ISDIR(oldinode->i_mode)) {
+ iput(oldinode);
+ iput(dir);
+ return -EPERM;
+ }
+ if (oldinode->i_nlink > 64000) {
+ iput(oldinode);
+ iput(dir);
+ return -EMLINK;
+ }
+ bh = xiafs_find_entry(dir, name, len, &de, NULL);
+ if (bh) {
+ brelse(bh);
+ iput(dir);
+ iput(oldinode);
+ return -EEXIST;
+ }
+ bh = xiafs_add_entry(dir, name, len, &de, NULL);
+ if (!bh) {
+ iput(dir);
+ iput(oldinode);
+ return -ENOSPC;
+ }
+ de->d_ino = oldinode->i_ino;
+ bh->b_dirt = 1;
+ brelse(bh);
+ iput(dir);
+ oldinode->i_nlink++;
+ oldinode->i_ctime = CURRENT_TIME;
+ oldinode->i_dirt = 1;
+ iput(oldinode);
+ return 0;
+}
+
+static int subdir(struct inode * new, struct inode * old)
+{
+ int ino;
+ int result;
+
+ new->i_count++;
+ result = 0;
+ for (;;) {
+ if (new == old) {
+ result = 1;
+ break;
+ }
+ if (new->i_dev != old->i_dev)
+ break;
+ ino = new->i_ino;
+ if (xiafs_lookup(new,"..",2,&new))
+ break;
+ if (new->i_ino == ino)
+ break;
+ }
+ iput(new);
+ return result;
+}
+
+#define PARENT_INO(buffer) \
+ (((struct xiafs_direct *) ((u_char *)(buffer) + 12))->d_ino)
+
+/*
+ * rename uses retry to avoid race-conditions: at least they should be minimal.
+ * it tries to allocate all the blocks, then sanity-checks, and if the sanity-
+ * checks fail, it tries to restart itself again. Very practical - no changes
+ * are done until we know everything works ok.. and then all the changes can be
+ * done in one fell swoop when we have claimed all the buffers needed.
+ *
+ * Anybody can rename anything with this: the permission checks are left to the
+ * higher-level routines.
+ */
+static int do_xiafs_rename(struct inode * old_dir, const char * old_name,
+ int old_len, struct inode * new_dir,
+ const char * new_name, int new_len)
+{
+ struct inode * old_inode, * new_inode;
+ struct buffer_head * old_bh, * new_bh, * dir_bh;
+ struct xiafs_direct * old_de, * old_de_pre, * new_de, * new_de_pre;
+ int retval;
+
+try_again:
+ old_inode = new_inode = NULL;
+ old_bh = new_bh = dir_bh = NULL;
+ old_bh = xiafs_find_entry(old_dir, old_name, old_len, &old_de, &old_de_pre);
+ retval = -ENOENT;
+ if (!old_bh)
+ goto end_rename;
+ old_inode = iget(old_dir->i_sb, old_de->d_ino);
+ if (!old_inode)
+ goto end_rename;
+ retval = -EPERM;
+ if ((old_dir->i_mode & S_ISVTX) &&
+ current->euid != old_inode->i_uid &&
+ current->euid != old_dir->i_uid && !suser())
+ goto end_rename;
+ new_bh = xiafs_find_entry(new_dir, new_name, new_len, &new_de, NULL);
+ if (new_bh) {
+ new_inode = iget(new_dir->i_sb, new_de->d_ino);
+ if (!new_inode) {
+ brelse(new_bh);
+ new_bh = NULL;
+ }
+ }
+ if (new_inode == old_inode) {
+ retval = 0;
+ goto end_rename;
+ }
+ if (new_inode && S_ISDIR(new_inode->i_mode)) {
+ retval = -EEXIST;
+ goto end_rename;
+ }
+ retval = -EPERM;
+ if (new_inode && (new_dir->i_mode & S_ISVTX) &&
+ current->euid != new_inode->i_uid &&
+ current->euid != new_dir->i_uid && !suser())
+ goto end_rename;
+ if (S_ISDIR(old_inode->i_mode)) {
+ retval = -EEXIST;
+ if (new_bh)
+ goto end_rename;
+ retval = -EACCES;
+ if (!permission(old_inode, MAY_WRITE))
+ goto end_rename;
+ retval = -EINVAL;
+ if (subdir(new_dir, old_inode))
+ goto end_rename;
+ retval = -EIO;
+ dir_bh = xiafs_bread(old_inode,0,0);
+ if (!dir_bh)
+ goto end_rename;
+ if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino)
+ goto end_rename;
+ retval = -EMLINK;
+ if (new_dir->i_nlink > 64000)
+ goto end_rename;
+ }
+ if (!new_bh)
+ new_bh = xiafs_add_entry(new_dir, new_name, new_len, &new_de, &new_de_pre);
+ retval = -ENOSPC;
+ if (!new_bh)
+ goto end_rename;
+ /* sanity checking */
+ if ( (new_inode && (new_de->d_ino != new_inode->i_ino))
+ || (new_de->d_ino && !new_inode)
+ || (old_de->d_ino != old_inode->i_ino)) {
+ xiafs_rm_entry(new_de, new_de_pre);
+ brelse(old_bh);
+ brelse(new_bh);
+ brelse(dir_bh);
+ iput(old_inode);
+ iput(new_inode);
+ current->counter=0;
+ schedule();
+ goto try_again;
+ }
+ xiafs_rm_entry(old_de, old_de_pre);
+ new_de->d_ino = old_inode->i_ino;
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_dirt = 1;
+ }
+ old_bh->b_dirt = 1;
+ new_bh->b_dirt = 1;
+ if (dir_bh) {
+ PARENT_INO(dir_bh->b_data) = new_dir->i_ino;
+ dir_bh->b_dirt = 1;
+ old_dir->i_nlink--;
+ new_dir->i_nlink++;
+ old_dir->i_dirt = 1;
+ new_dir->i_dirt = 1;
+ }
+ retval = 0;
+end_rename:
+ brelse(dir_bh);
+ brelse(old_bh);
+ brelse(new_bh);
+ iput(old_inode);
+ iput(new_inode);
+ iput(old_dir);
+ iput(new_dir);
+ return retval;
+}
+
+/*
+ * Ok, rename also locks out other renames, as they can change the parent of
+ * a directory, and we don't want any races. Other races are checked for by
+ * "do_rename()", which restarts if there are inconsistencies.
+ *
+ * Note that there is no race between different filesystems: it's only within
+ * the same device that races occur: many renames can happen at once, as long
+ * as they are on different partitions.
+ */
+int xiafs_rename(struct inode * old_dir, const char * old_name, int old_len,
+ struct inode * new_dir, const char * new_name, int new_len)
+{
+ static struct wait_queue * wait = NULL;
+ static int lock = 0;
+ int result;
+
+ while (lock)
+ sleep_on(&wait);
+ lock = 1;
+ result = do_xiafs_rename(old_dir, old_name, old_len,
+ new_dir, new_name, new_len);
+ lock = 0;
+ wake_up(&wait);
+ return result;
+}
+
+
+
+
+
diff --git a/fs/xiafs/symlink.c b/fs/xiafs/symlink.c
new file mode 100644
index 0000000..cd68698
--- /dev/null
+++ b/fs/xiafs/symlink.c
@@ -0,0 +1,114 @@
+/*
+ * linux/fs/xiafs/symlink.c
+ *
+ * Copyright (C) Q. Frank Xia, 1993.
+ *
+ * Based on Linus' minix/symlink.c
+ * Copyright (C) Linus Torvalds, 1991, 1992.
+ *
+ * This software may be redistributed per Linux Copyright.
+ */
+
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/xia_fs.h>
+#include <linux/stat.h>
+
+static int
+xiafs_readlink(struct inode *, char *, int);
+
+static int
+xiafs_follow_link(struct inode *, struct inode *, int, int, struct inode **);
+
+/*
+ * symlinks can't do much...
+ */
+struct inode_operations xiafs_symlink_inode_operations = {
+ NULL, /* no file-operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ xiafs_readlink, /* readlink */
+ xiafs_follow_link, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+static int xiafs_readlink(struct inode * inode, char * buffer, int buflen)
+{
+ struct buffer_head * bh;
+ int i;
+ char c;
+
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(inode);
+ return -EINVAL;
+ }
+ if (buflen > BLOCK_SIZE)
+ buflen = BLOCK_SIZE;
+ bh = xiafs_bread(inode, 0, 0);
+ inode->i_atime=CURRENT_TIME;
+ inode->i_dirt=1;
+ iput(inode);
+ if (!bh)
+ return 0;
+ for (i=0; i < buflen && (c=bh->b_data[i]); i++)
+ put_fs_byte(c, buffer++);
+ if (i < buflen-1)
+ put_fs_byte((char)0, buffer);
+ brelse(bh);
+ return i;
+}
+
+static int xiafs_follow_link(struct inode * dir, struct inode * inode,
+ int flag, int mode, struct inode ** res_inode)
+{
+ int error;
+ struct buffer_head * bh;
+
+ *res_inode = NULL;
+ if (!dir) {
+ dir = current->root;
+ dir->i_count++;
+ }
+ if (!inode) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(dir);
+ *res_inode = inode;
+ return 0;
+ }
+ inode->i_atime=CURRENT_TIME;
+ inode->i_dirt=1;
+ if (current->link_count > 5) {
+ iput(inode);
+ iput(dir);
+ return -ELOOP;
+ }
+ if (!(bh = xiafs_bread(inode, 0, 0))) {
+ iput(inode);
+ iput(dir);
+ return -EIO;
+ }
+ iput(inode);
+ current->link_count++;
+ error = open_namei(bh->b_data,flag,mode,res_inode,dir);
+ current->link_count--;
+ brelse(bh);
+ return error;
+}
+
+
+
diff --git a/fs/xiafs/truncate.c b/fs/xiafs/truncate.c
new file mode 100644
index 0000000..e6a0d63
--- /dev/null
+++ b/fs/xiafs/truncate.c
@@ -0,0 +1,197 @@
+/*
+ * linux/fs/xiafs/truncate.c
+ *
+ * Copyright (C) Q. Frank Xia, 1993.
+ *
+ * Based on Linus' minix/truncate.c
+ * Copyright (C) Linus Torvalds, 1991, 1992.
+ *
+ * This software may be redistributed per Linux Copyright.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/xia_fs.h>
+#include <linux/tty.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+
+#include "xiafs_mac.h"
+
+/*
+ * Linus' comment:
+ *
+ * Truncate has the most races in the whole filesystem: coding it is
+ * a pain in the a**. Especially as I don't do any locking...
+ *
+ * The code may look a bit weird, but that's just because I've tried to
+ * handle things like file-size changes in a somewhat graceful manner.
+ * Anyway, truncating a file at the same time somebody else writes to it
+ * is likely to result in pretty weird behaviour...
+ *
+ * The new code handles normal truncates (size = 0) as well as the more
+ * general case (size = XXX). I hope.
+ */
+
+#define DT_ZONE ((inode->i_size + XIAFS_ZSIZE(inode->i_sb) - 1) \
+ >> XIAFS_ZSIZE_BITS(inode->i_sb) )
+
+static int trunc_direct(struct inode * inode)
+{
+ u_long * lp;
+ struct buffer_head * bh;
+ int i, tmp;
+ int retry = 0;
+
+repeat:
+ for (i = DT_ZONE ; i < 8 ; i++) {
+ if (i < DT_ZONE)
+ goto repeat;
+ lp=i + inode->u.xiafs_i.i_zone;
+ if (!(tmp = *lp))
+ continue;
+ bh = getblk(inode->i_dev, tmp, XIAFS_ZSIZE(inode->i_sb));
+ if (i < DT_ZONE) {
+ brelse(bh);
+ goto repeat;
+ }
+ if ((bh && bh->b_count != 1) || tmp != *lp)
+ retry = 1;
+ else {
+ *lp = 0;
+ inode->i_dirt = 1;
+ xiafs_free_zone(inode->i_sb, tmp);
+ }
+ brelse(bh);
+ }
+ return retry;
+}
+
+static int trunc_indirect(struct inode * inode, int addr_off, u_long * lp)
+{
+
+#define INDT_ZONE (DT_ZONE - addr_off)
+
+ struct buffer_head * bh, * ind_bh;
+ int i, tmp;
+ u_long * indp;
+ int retry = 0;
+
+ if ( !(tmp=*lp) )
+ return 0;
+ ind_bh = bread(inode->i_dev, tmp, XIAFS_ZSIZE(inode->i_sb));
+ if (tmp != *lp) {
+ brelse(ind_bh);
+ return 1;
+ }
+ if (!ind_bh) {
+ *lp = 0;
+ return 0;
+ }
+repeat:
+ for (i = INDT_ZONE<0?0:INDT_ZONE; i < XIAFS_ADDRS_PER_Z(inode->i_sb); i++) {
+ if (i < INDT_ZONE)
+ goto repeat;
+ indp = i+(u_long *) ind_bh->b_data;
+ if (!(tmp=*indp))
+ continue;
+ bh = getblk(inode->i_dev, tmp, XIAFS_ZSIZE(inode->i_sb));
+ if (i < INDT_ZONE) {
+ brelse(bh);
+ goto repeat;
+ }
+ if ((bh && bh->b_count != 1) || tmp != *indp)
+ retry = 1;
+ else {
+ *indp = 0;
+ ind_bh->b_dirt = 1;
+ xiafs_free_zone(inode->i_sb, tmp);
+ }
+ brelse(bh);
+ }
+ indp = (u_long *) ind_bh->b_data;
+ for (i = 0; i < XIAFS_ADDRS_PER_Z(inode->i_sb) && !(*indp++); i++) ;
+ if (i >= XIAFS_ADDRS_PER_Z(inode->i_sb)) {
+ if (ind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = *lp;
+ *lp = 0;
+ xiafs_free_zone(inode->i_sb, tmp);
+ }
+ }
+ brelse(ind_bh);
+ return retry;
+}
+
+static int trunc_dindirect(struct inode * inode)
+{
+
+#define DINDT_ZONE \
+ ((DT_ZONE-XIAFS_ADDRS_PER_Z(inode->i_sb)-8)>>XIAFS_ADDRS_PER_Z_BITS(inode->i_sb))
+
+ int i, tmp;
+ struct buffer_head * dind_bh;
+ u_long * dindp, * lp;
+ int retry = 0;
+
+ lp = &(inode->u.xiafs_i.i_dind_zone);
+ if (!(tmp = *lp))
+ return 0;
+ dind_bh = bread(inode->i_dev, tmp, XIAFS_ZSIZE(inode->i_sb));
+ if (tmp != *lp) {
+ brelse(dind_bh);
+ return 1;
+ }
+ if (!dind_bh) {
+ *lp = 0;
+ return 0;
+ }
+repeat:
+ for (i=DINDT_ZONE<0?0:DINDT_ZONE ; i < XIAFS_ADDRS_PER_Z(inode->i_sb) ; i ++) {
+ if (i < DINDT_ZONE)
+ goto repeat;
+ dindp = i+(u_long *) dind_bh->b_data;
+ retry |= trunc_indirect(inode,
+ 8+((1+i)<<XIAFS_ADDRS_PER_Z_BITS(inode->i_sb)),
+ dindp);
+ dind_bh->b_dirt = 1;
+ }
+ dindp = (u_long *) dind_bh->b_data;
+ for (i = 0; i < XIAFS_ADDRS_PER_Z(inode->i_sb) && !(*dindp++); i++);
+ if (i >= XIAFS_ADDRS_PER_Z(inode->i_sb)) {
+ if (dind_bh->b_count != 1)
+ retry = 1;
+ else {
+ tmp = *lp;
+ *lp = 0;
+ inode->i_dirt = 1;
+ xiafs_free_zone(inode->i_sb, tmp);
+ }
+ }
+ brelse(dind_bh);
+ return retry;
+}
+
+void xiafs_truncate(struct inode * inode)
+{
+ int retry;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return;
+ while (1) {
+ retry = trunc_direct(inode);
+ retry |= trunc_indirect(inode, 8, &(inode->u.xiafs_i.i_ind_zone));
+ retry |= trunc_dindirect(inode);
+ if (!retry)
+ break;
+ current->counter = 0;
+ schedule();
+ }
+ inode->i_mtime = CURRENT_TIME;
+ inode->i_dirt = 1;
+}
+
+
+
diff --git a/fs/xiafs/xiafs_mac.h b/fs/xiafs/xiafs_mac.h
new file mode 100644
index 0000000..f3cfdae
--- /dev/null
+++ b/fs/xiafs/xiafs_mac.h
@@ -0,0 +1,20 @@
+/*
+ * linux/fs/xiafs/xiafs_mac.h
+ *
+ * Copyright (C) Q. Frank Xia, 1993.
+ */
+
+extern char internal_error_message[];
+#define INTERN_ERR internal_error_message, __FILE__, __LINE__
+#define WHERE_ERR __FILE__, __LINE__
+
+#define XIAFS_ZSHIFT(sp) ((sp)->u.xiafs_sb.s_zone_shift)
+#define XIAFS_ZSIZE(sp) (BLOCK_SIZE << XIAFS_ZSHIFT(sp))
+#define XIAFS_ZSIZE_BITS(sp) (BLOCK_SIZE_BITS + XIAFS_ZSHIFT(sp))
+#define XIAFS_ADDRS_PER_Z(sp) (BLOCK_SIZE >> (2 - XIAFS_ZSHIFT(sp)))
+#define XIAFS_ADDRS_PER_Z_BITS(sp) (BLOCK_SIZE_BITS - 2 + XIAFS_ZSHIFT(sp))
+#define XIAFS_BITS_PER_Z(sp) (BLOCK_SIZE << (3 + XIAFS_ZSHIFT(sp)))
+#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))
+
+
diff --git a/include/asm/irq.h b/include/asm/irq.h
index ea8685c..4c696b1 100644
--- a/include/asm/irq.h
+++ b/include/asm/irq.h
@@ -107,7 +107,7 @@
"movb _cache_A1,%al\n\t" \
"outb %al,$0xA1\n\t"
-#define IRQ_NAME2(nr) nr##_interrupt()
+#define IRQ_NAME2(nr) nr##_interrupt(void)
#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
#define FAST_IRQ_NAME(nr) IRQ_NAME2(fast_IRQ##nr)
#define BAD_IRQ_NAME(nr) IRQ_NAME2(bad_IRQ##nr)
diff --git a/include/asm/segment.h b/include/asm/segment.h
index 17a1cfe..884fdaa 100644
--- a/include/asm/segment.h
+++ b/include/asm/segment.h
@@ -78,14 +78,14 @@ __asm__("cld\n\t"
* [ nothing wrong here, Linus: I just changed the ax to be any reg ]
*/
-extern inline unsigned long get_fs()
+extern inline unsigned long get_fs(void)
{
unsigned short _v;
__asm__("mov %%fs,%0":"=r" (_v):);
return _v;
}
-extern inline unsigned long get_ds()
+extern inline unsigned long get_ds(void)
{
unsigned short _v;
__asm__("mov %%ds,%0":"=r" (_v):);
diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h
new file mode 100644
index 0000000..145e62a
--- /dev/null
+++ b/include/linux/ext2_fs.h
@@ -0,0 +1,278 @@
+#ifndef _LINUX_EXT2_FS_H
+#define _LINUX_EXT2_FS_H
+
+/*
+ * The second extended filesystem constants/structures
+ */
+
+/*
+ * Define EXT2FS_DEBUG to produce debug messages
+ */
+#undef EXT2FS_DEBUG
+
+/*
+ * Define DONT_USE_DCACHE to inhibit the directory cache
+ */
+#undef DONT_USE_DCACHE
+
+/*
+ * Define EXT2FS_DEBUG_CACHE to produce cache debug messages
+ */
+#undef EXT2FS_DEBUG_CACHE
+
+/*
+ * The second extended file system version
+ */
+#define EXT2FS_VERSION "0.2c, 93/03/06"
+
+/*
+ * Special inodes numbers
+ */
+#define EXT2_BAD_INO 1 /* Bad blocks inode */
+#define EXT2_ROOT_INO 2 /* Root inode */
+#define EXT2_ACL_INO 3 /* ACL inode */
+#define EXT2_FIRST_INO 11 /* First non reserved inode */
+
+/*
+ * The second extended file system magic number
+ */
+#define EXT2_OLD_SUPER_MAGIC 0xEF51
+#define EXT2_SUPER_MAGIC 0xEF53
+
+/*
+ * Macro-instructions used to manage several block sizes
+ */
+#define EXT2_MIN_BLOCK_SIZE 1024
+#define EXT2_MAX_BLOCK_SIZE 4096
+#define EXT2_MIN_BLOCK_LOG_SIZE 10
+#ifdef KERNEL
+# define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize)
+#else
+# define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+#endif
+#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (unsigned long))
+#ifdef KERNEL
+# define EXT2_BLOCK_SIZE_BITS(s) ((s)->u.ext2_sb.s_log_block_size + 10)
+#else
+# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
+#endif
+#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_inode))
+
+/*
+ * Macro-instructions used to manage fragments
+ */
+#define EXT2_MIN_FRAG_SIZE 1024
+#define EXT2_MAX_FRAG_SIZE 1024
+#define EXT2_MIN_FRAG_LOG_SIZE 10
+#ifdef KERNEL
+# define EXT2_FRAG_SIZE(s) ((s)->u.ext2_sb.s_frag_size)
+# define EXT2_FRAGS_PER_BLOCK(s) ((s)->u.ext2_sb.s_frags_per_block)
+#else
+# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
+# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
+#endif
+
+/*
+ * Structure of a blocks group descriptor
+ */
+struct ext2_old_group_desc
+{
+ unsigned long bg_block_bitmap; /* Blocks bitmap block */
+ unsigned long bg_inode_bitmap; /* Inodes bitmap block */
+ unsigned long bg_inode_table; /* Inodes table block */
+ unsigned short bg_free_blocks_count; /* Free blocks count */
+ unsigned short bg_free_inodes_count; /* Free inodes count */
+};
+
+struct ext2_group_desc
+{
+ unsigned long bg_block_bitmap; /* Blocks bitmap block */
+ unsigned long bg_inode_bitmap; /* Inodes bitmap block */
+ unsigned long bg_inode_table; /* Inodes table block */
+ unsigned short bg_free_blocks_count; /* Free blocks count */
+ unsigned short bg_free_inodes_count; /* Free inodes count */
+ unsigned short bg_used_dirs_count; /* Directories count */
+ unsigned short bg_pad;
+ unsigned long bg_reserved[3];
+};
+
+/*
+ * Macro-instructions used to manage group descriptors
+ */
+#ifdef KERNEL
+# define EXT2_BLOCKS_PER_GROUP(s) ((s)->u.ext2_sb.s_blocks_per_group)
+# define EXT2_DESC_PER_BLOCK(s) ((s)->u.ext2_sb.s_desc_per_block)
+# define EXT2_INODES_PER_GROUP(s) ((s)->u.ext2_sb.s_inodes_per_group)
+#else
+# define EXT2_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group)
+# define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc))
+# define EXT2_INODES_PER_GROUP(s) ((s)->s_inodes_per_group)
+#endif
+
+/*
+ * Constants relative to the data blocks
+ */
+#define EXT2_NDIR_BLOCKS 12
+#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
+#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
+#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
+#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext2_inode {
+ unsigned short i_mode; /* File mode */
+ unsigned short i_uid; /* Owner Uid */
+ unsigned long i_size; /* Size in bytes */
+ unsigned long i_atime; /* Access time */
+ unsigned long i_ctime; /* Creation time */
+ unsigned long i_mtime; /* Modification time */
+ unsigned long i_dtime; /* Deletion Time */
+ unsigned short i_gid; /* Group Id */
+ unsigned short i_links_count; /* Links count */
+ unsigned long i_blocks; /* Blocks count */
+ unsigned long i_flags; /* File flags */
+ unsigned long i_reserved1;
+ unsigned long i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+ unsigned long i_version; /* File version (for NFS) */
+ unsigned long i_file_acl; /* File ACL */
+ unsigned long i_dir_acl; /* Directory ACL */
+ unsigned short i_faddr; /* Fragment address */
+ unsigned char i_frag; /* Fragment number */
+ unsigned char i_fsize; /* Fragment size */
+ unsigned long i_reserved2[3];
+};
+
+/*
+ * Structure of the super block
+ */
+struct ext2_super_block {
+ unsigned long s_inodes_count; /* Inodes count */
+ unsigned long s_blocks_count; /* Blocks count */
+ unsigned long s_r_blocks_count; /* Reserved blocks count */
+ unsigned long s_free_blocks_count;/* Free blocks count */
+ unsigned long s_free_inodes_count;/* Free inodes count */
+ unsigned long s_first_data_block;/* First Data Block */
+ unsigned long s_log_block_size; /* Block size */
+ unsigned long s_log_frag_size; /* Fragment size */
+ unsigned long s_blocks_per_group;/* # Blocks per group */
+ unsigned long s_frags_per_group;/* # Fragments per group */
+ unsigned long s_inodes_per_group;/* # Inodes per group */
+ unsigned long s_mtime; /* Mount time */
+ unsigned long s_wtime; /* Write time */
+ unsigned long s_pad; /* Padding to get the magic signature*/
+ /* at the same offset as in the */
+ /* previous ext fs */
+ unsigned short s_magic; /* Magic signature */
+ unsigned short s_valid; /* Flag */
+ unsigned long s_reserved[243]; /* Padding to the end of the block */
+};
+
+/*
+ * Structure of a directory entry
+ */
+#define EXT2_NAME_LEN 255
+
+struct ext2_dir_entry {
+ unsigned long inode; /* Inode number */
+ unsigned short rec_len; /* Directory entry length */
+ unsigned short name_len; /* Name length */
+ char name[EXT2_NAME_LEN]; /* File name */
+};
+
+/*
+ * EXT2_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 4
+ */
+#define EXT2_DIR_PAD 4
+#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
+#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
+ ~EXT2_DIR_ROUND)
+
+/*
+ * Function prototypes
+ */
+
+/* balloc.c */
+extern int ext2_new_block (struct super_block *, unsigned long);
+extern void ext2_free_block (struct super_block *, unsigned long);
+extern unsigned long ext2_count_free_blocks (struct super_block *);
+
+/* bitmap.c */
+extern unsigned long ext2_count_free (struct buffer_head *, unsigned);
+
+/* dcache.c */
+extern void ext2_dcache_invalidate (unsigned short);
+extern unsigned long ext2_dcache_lookup (unsigned short, unsigned long,
+ const char *, int);
+extern void ext2_dcache_add (unsigned short, unsigned long, const char *,
+ int, int);
+extern void ext2_dcache_remove (unsigned short, unsigned long, const char *,
+ int);
+
+/* file.c */
+extern int ext2_read (struct inode *, struct file *, char *, int);
+extern int ext2_write (struct inode *, struct file *, char *, int);
+
+/* ialloc.c */
+extern struct inode * ext2_new_inode (const struct inode *, int);
+extern void ext2_free_inode (struct inode *);
+extern unsigned long ext2_count_free_inodes (struct super_block *);
+
+/* inode.c */
+extern int ext2_bmap (struct inode *, int);
+
+extern struct buffer_head * ext2_getblk (struct inode *, int, int);
+extern struct buffer_head * ext2_bread (struct inode *, int, int);
+
+extern void ext2_truncate (struct inode *);
+extern void ext2_put_super (struct super_block *);
+extern void ext2_write_super (struct super_block *);
+extern struct super_block * ext2_read_super (struct super_block *,void *);
+extern void ext2_read_inode (struct inode *);
+extern void ext2_write_inode (struct inode *);
+extern void ext2_put_inode (struct inode *);
+extern void ext2_statfs (struct super_block *, struct statfs *);
+
+/* namei.c */
+extern int ext2_open (struct inode *, struct file *);
+extern void ext2_release (struct inode *, struct file *);
+extern int ext2_lookup (struct inode *,const char *, int, struct inode **);
+extern int ext2_create (struct inode *,const char *, int, int,
+ struct inode **);
+extern int ext2_mkdir (struct inode *, const char *, int, int);
+extern int ext2_rmdir (struct inode *, const char *, int);
+extern int ext2_unlink (struct inode *, const char *, int);
+extern int ext2_symlink (struct inode *, const char *, int, const char *);
+extern int ext2_link (struct inode *, struct inode *, const char *, int);
+extern int ext2_mknod (struct inode *, const char *, int, int, int);
+extern int ext2_rename (struct inode *, const char *, int,
+ struct inode *, const char *, int);
+
+/*
+ * Inodes and files operations
+ */
+
+/* blkdev.c */
+extern struct inode_operations ext2_blkdev_inode_operations;
+
+/* chrdev.c */
+extern struct inode_operations ext2_chrdev_inode_operations;
+
+/* dir.c */
+extern struct inode_operations ext2_dir_inode_operations;
+extern struct file_operations ext2_dir_operations;
+
+/* fifo.c */
+extern struct inode_operations ext2_fifo_inode_operations;
+
+/* file.c */
+extern struct inode_operations ext2_file_inode_operations;
+extern struct file_operations ext2_file_operations;
+
+/* symlink.c */
+extern struct inode_operations ext2_symlink_inode_operations;
+
+#endif
diff --git a/include/linux/ext2_fs_i.h b/include/linux/ext2_fs_i.h
new file mode 100644
index 0000000..09d1371
--- /dev/null
+++ b/include/linux/ext2_fs_i.h
@@ -0,0 +1,20 @@
+#ifndef _EXT2_FS_I
+#define _EXT2_FS_I
+
+/*
+ * second extended file system inode data in memory
+ */
+struct ext2_inode_info {
+ unsigned long i_flags;
+ unsigned short i_faddr;
+ unsigned char i_frag;
+ unsigned char i_fsize;
+ unsigned long i_file_acl;
+ unsigned long i_dir_acl;
+ unsigned long i_dtime;
+ unsigned long i_version;
+ unsigned long i_block_group;
+ unsigned long i_data[15];
+};
+
+#endif
diff --git a/include/linux/ext2_fs_sb.h b/include/linux/ext2_fs_sb.h
new file mode 100644
index 0000000..69d2bc1
--- /dev/null
+++ b/include/linux/ext2_fs_sb.h
@@ -0,0 +1,37 @@
+#ifndef _EXT2_FS_SB
+#define _EXT2_FS_SB
+
+#define EXT2_MAX_GROUP_DESC 4
+#define EXT2_MAX_GROUP_LOADED 8
+
+/*
+ * second extended-fs super-block data in memory
+ */
+struct ext2_sb_info {
+ unsigned long s_inodes_count; /* Inodes count */
+ unsigned long s_blocks_count; /* Blocks count */
+ unsigned long s_r_blocks_count; /* Reserved blocks count */
+ unsigned long s_first_data_block;/* First data block */
+ unsigned long s_log_block_size; /* Log of block size */
+ unsigned long s_log_frag_size; /* Log of fragment size */
+ unsigned long s_frag_size; /* Size of a fragment in bytes */
+ unsigned long s_frags_per_block;/* Number of fragments per block */
+ unsigned long s_inodes_per_block;/* Number of inodes per block */
+ unsigned long s_frags_per_group;/* Number of fragments in a group */
+ unsigned long s_blocks_per_group;/* Number of blocks in a group */
+ unsigned long s_inodes_per_group;/* Number of inodes in a group */
+ unsigned long s_desc_per_block; /* Number of group descriptors per block */
+ unsigned long s_groups_count; /* Number of groups in the fs */
+ struct buffer_head * s_sbh; /* Buffer containing the super block */
+ struct buffer_head * s_group_desc[EXT2_MAX_GROUP_DESC];
+ unsigned short s_loaded_inode_bitmaps;
+ unsigned short s_loaded_block_bitmaps;
+ unsigned long s_inode_bitmap_number[EXT2_MAX_GROUP_LOADED];
+ struct buffer_head * s_inode_bitmap[EXT2_MAX_GROUP_LOADED];
+ unsigned long s_block_bitmap_number[EXT2_MAX_GROUP_LOADED];
+ struct buffer_head * s_block_bitmap[EXT2_MAX_GROUP_LOADED];
+ int s_rename_lock;
+ struct wait_queue * s_rename_wait;
+};
+
+#endif
diff --git a/include/linux/ext_fs.h b/include/linux/ext_fs.h
index d758843..a8b455d 100644
--- a/include/linux/ext_fs.h
+++ b/include/linux/ext_fs.h
@@ -102,9 +102,6 @@ extern int ext_write(struct inode *, struct file *, char *, int);
extern struct inode_operations ext_file_inode_operations;
extern struct inode_operations ext_dir_inode_operations;
extern struct inode_operations ext_symlink_inode_operations;
-extern struct inode_operations ext_chrdev_inode_operations;
-extern struct inode_operations ext_blkdev_inode_operations;
-extern struct inode_operations ext_fifo_inode_operations;
extern struct file_operations ext_file_operations;
extern struct file_operations ext_dir_operations;
diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
index cea4f5f..e7ec6c0 100644
--- a/include/linux/fcntl.h
+++ b/include/linux/fcntl.h
@@ -38,7 +38,6 @@
#define F_EXLCK 4 /* or 3 */
#define F_SHLCK 8 /* or 4 */
-/* Once again - not implemented, but ... */
struct flock {
short l_type;
short l_whence;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 24904ac..c1259b0 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -157,9 +157,11 @@ struct buffer_head {
#include <linux/pipe_fs_i.h>
#include <linux/minix_fs_i.h>
#include <linux/ext_fs_i.h>
+#include <linux/ext2_fs_i.h>
#include <linux/msdos_fs_i.h>
#include <linux/iso_fs_i.h>
#include <linux/nfs_fs_i.h>
+#include <linux/xia_fs_i.h>
struct inode {
dev_t i_dev;
@@ -195,9 +197,11 @@ struct inode {
struct pipe_inode_info pipe_i;
struct minix_inode_info minix_i;
struct ext_inode_info ext_i;
+ struct ext2_inode_info ext2_i;
struct msdos_inode_info msdos_i;
struct iso_inode_info isofs_i;
struct nfs_inode_info nfs_i;
+ struct xiafs_inode_info xiafs_i;
} u;
};
@@ -224,9 +228,11 @@ struct file_lock {
#include <linux/minix_fs_sb.h>
#include <linux/ext_fs_sb.h>
+#include <linux/ext2_fs_sb.h>
#include <linux/msdos_fs_sb.h>
#include <linux/iso_fs_sb.h>
#include <linux/nfs_fs_sb.h>
+#include <linux/xia_fs_sb.h>
struct super_block {
dev_t s_dev;
@@ -244,9 +250,11 @@ struct super_block {
union {
struct minix_sb_info minix_sb;
struct ext_sb_info ext_sb;
+ struct ext2_sb_info ext2_sb;
struct msdos_sb_info msdos_sb;
struct isofs_sb_info isofs_sb;
struct nfs_sb_info nfs_sb;
+ struct xiafs_sb_info xiafs_sb;
} u;
};
@@ -257,7 +265,7 @@ struct file_operations {
int (*readdir) (struct inode *, struct file *, struct dirent *, int);
int (*select) (struct inode *, struct file *, int, select_table *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned int);
- int (*mmap) (void);
+ int (*mmap) (struct inode *, struct file *, unsigned long, size_t, int, unsigned long);
int (*open) (struct inode *, struct file *);
void (*release) (struct inode *, struct file *);
int (*fsync) (struct inode *, struct file *);
@@ -297,6 +305,9 @@ struct file_system_type {
int requires_dev;
};
+extern int getname(const char * filename, char **result);
+extern void putname(char * name);
+
extern int register_blkdev(unsigned int, const char *, struct file_operations *);
extern int blkdev_open(struct inode * inode, struct file * filp);
extern struct file_operations def_blk_fops;
@@ -355,7 +366,7 @@ extern void ll_rw_page(int rw, int dev, int nr, char * buffer);
extern void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buffer);
extern void brelse(struct buffer_head * buf);
extern struct buffer_head * bread(dev_t dev, int block, int size);
-extern void bread_page(unsigned long addr,dev_t dev,int b[4]);
+extern unsigned long bread_page(unsigned long addr,dev_t dev,int b[],int size,int prot);
extern struct buffer_head * breada(dev_t dev,int block,...);
extern void put_super(dev_t dev);
extern dev_t ROOT_DEV;
diff --git a/include/linux/iso_fs.h b/include/linux/iso_fs.h
index f95dc8b..a144561 100644
--- a/include/linux/iso_fs.h
+++ b/include/linux/iso_fs.h
@@ -94,7 +94,7 @@ struct hs_primary_descriptor {
/* We use this to help us look up the parent inode numbers. */
struct iso_path_table{
- char name_len[2]; /* 721 */
+ unsigned char name_len[2]; /* 721 */
char extent[4]; /* 731 */
char parent[2]; /* 721 */
char name[0];
@@ -113,7 +113,7 @@ struct iso_directory_record {
char file_unit_size [ISODCL (27, 27)]; /* 711 */
char interleave [ISODCL (28, 28)]; /* 711 */
char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
- char name_len [ISODCL (33, 33)]; /* 711 */
+ unsigned char name_len [ISODCL (33, 33)]; /* 711 */
char name [0];
};
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index fbfec68..aa507f8 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -5,7 +5,11 @@
* 'kernel.h' contains some often-used function prototypes etc
*/
-void verify_area(void * addr,int count);
+#define VERIFY_READ 0
+#define VERIFY_WRITE 1
+
+int verify_area(int type, void * addr, unsigned long count);
+
volatile void panic(const char * str);
volatile void do_exit(long error_code);
unsigned long simple_strtoul(const char *,char **,unsigned int);
diff --git a/include/linux/lp.h b/include/linux/lp.h
index 68c8a01..de3061e 100644
--- a/include/linux/lp.h
+++ b/include/linux/lp.h
@@ -4,6 +4,7 @@
/*
* usr/include/linux/lp.h c.1991-1992 James Wiegand
* many modifications copyright (C) 1992 Michael K. Johnson
+ * Interrupt support added 1993 Nigel Gamble
*/
/*
@@ -54,13 +55,17 @@
#define LPTIME 0x0002 /* corresponds to LP_INIT_TIME */
#define LPABORT 0x0004 /* call with TRUE arg to abort on error,
FALSE to retry. Default is retry. */
+#define LPSETIRQ 0x0005 /* call with new IRQ number,
+ or 0 for polling (no IRQ) */
+#define LPGETIRQ 0x0006 /* get the current IRQ number */
#define LPWAIT 0x0008 /* corresponds to LP_INIT_WAIT */
/* timeout for printk'ing a timeout, in jiffies (100ths of a second).
This is also used for re-checking error conditions if LP_ABORT is
not set. This is the default behavior. */
-#define LP_TIMEOUT 1000
+#define LP_TIMEOUT_INTERRUPT (60 * HZ)
+#define LP_TIMEOUT_POLLED (10 * HZ)
#define LP_B(minor) lp_table[(minor)].base /* IO address */
#define LP_F(minor) lp_table[(minor)].flags /* flags for busy, etc. */
@@ -69,17 +74,20 @@
#define LP_CHAR(minor) lp_table[(minor)].chars /* busy timeout */
#define LP_TIME(minor) lp_table[(minor)].time /* wait time */
#define LP_WAIT(minor) lp_table[(minor)].wait /* strobe wait */
+#define LP_IRQ(minor) lp_table[(minor)].irq /* interrupt # */
+ /* 0 means polled */
+
+#define LP_BUFFER_SIZE 256
-/*
-since we are dealing with a horribly slow device
-I don't see the need for a queue
-*/
struct lp_struct {
int base;
+ unsigned int irq;
int flags;
unsigned int chars;
unsigned int time;
unsigned int wait;
+ struct wait_queue *lp_wait_q;
+ char *lp_buffer;
};
/* the BIOS manuals say there can be up to 4 lpt devices
@@ -89,9 +97,9 @@ struct lp_struct {
* if you have more than 3 printers, remember to increase LP_NO
*/
struct lp_struct lp_table[] = {
- { 0x3bc, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, },
- { 0x378, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, },
- { 0x278, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, },
+ { 0x3bc, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
+ { 0x378, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
+ { 0x278, 0, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, },
};
#define LP_NO 3
@@ -111,6 +119,7 @@ struct lp_struct lp_table[] = {
* base + 2
* accessed with LP_C(minor)
*/
+#define LP_PINTEN 0x10
#define LP_PSELECP 0x08
#define LP_PINITP 0x04 /* active low */
#define LP_PAUTOLF 0x02
diff --git a/include/linux/minix_fs.h b/include/linux/minix_fs.h
index a8f3211..90d2252 100644
--- a/include/linux/minix_fs.h
+++ b/include/linux/minix_fs.h
@@ -12,16 +12,18 @@
* it.
*/
-#define MINIX_NAME_LEN 14
#define MINIX_ROOT_INO 1
+/* Not the same as the bogus LINK_MAX in <linux/limits.h>. Oh well. */
+#define MINIX_LINK_MAX 250
+
#define MINIX_I_MAP_SLOTS 8
#define MINIX_Z_MAP_SLOTS 8
-#define MINIX_SUPER_MAGIC 0x137F
-#define NEW_MINIX_SUPER_MAGIC 0x2468
+#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */
+#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */
+#define NEW_MINIX_SUPER_MAGIC 0x2468 /* minix V2 - not implemented */
#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
-#define MINIX_DIR_ENTRIES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_dir_entry)))
struct minix_inode {
unsigned short i_mode;
@@ -67,7 +69,7 @@ struct minix_super_block {
struct minix_dir_entry {
unsigned short inode;
- char name[MINIX_NAME_LEN];
+ char name[0];
};
extern int minix_lookup(struct inode * dir,const char * name, int len,
@@ -106,9 +108,6 @@ extern void minix_statfs(struct super_block *, struct statfs *);
extern struct inode_operations minix_file_inode_operations;
extern struct inode_operations minix_dir_inode_operations;
extern struct inode_operations minix_symlink_inode_operations;
-extern struct inode_operations minix_chrdev_inode_operations;
-extern struct inode_operations minix_blkdev_inode_operations;
-extern struct inode_operations minix_fifo_inode_operations;
extern struct file_operations minix_file_operations;
extern struct file_operations minix_dir_operations;
diff --git a/include/linux/minix_fs_sb.h b/include/linux/minix_fs_sb.h
index 18de6ef..c6f0625 100644
--- a/include/linux/minix_fs_sb.h
+++ b/include/linux/minix_fs_sb.h
@@ -14,6 +14,8 @@ struct minix_sb_info {
unsigned long s_max_size;
struct buffer_head * s_imap[8];
struct buffer_head * s_zmap[8];
+ unsigned long s_dirsize;
+ unsigned long s_namelen;
};
#endif
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 146e569..e69c24d 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -49,9 +49,11 @@ struct vm_operations_struct {
extern unsigned long __bad_page(void);
extern unsigned long __bad_pagetable(void);
+extern unsigned long __zero_page(void);
#define BAD_PAGETABLE __bad_pagetable()
#define BAD_PAGE __bad_page()
+#define ZERO_PAGE __zero_page()
extern volatile short free_page_ptr; /* used by malloc and tcp/ip. */
@@ -62,27 +64,37 @@ extern unsigned long secondary_page_list;
#define MAX_SECONDARY_PAGES 10
-extern void rw_swap_page(int rw, unsigned int nr, char * buf);
-
-#define read_swap_page(nr,buf) \
- rw_swap_page(READ,(nr),(buf))
-#define write_swap_page(nr,buf) \
- rw_swap_page(WRITE,(nr),(buf))
+/*
+ * This is timing-critical - most of the time in getting a new page
+ * goes to clearing the page. If you want a page without the clearing
+ * overhead, just use __get_free_page() directly..
+ */
+extern unsigned long __get_free_page(int priority);
+extern inline unsigned long get_free_page(int priority)
+{
+ unsigned long page;
+
+ page = __get_free_page(priority);
+ if (page)
+ __asm__ __volatile__("rep ; stosl"
+ ::"a" (0),"c" (1024),"D" (page)
+ :"di","cx");
+ return page;
+}
/* mmap.c */
/* memory.c */
-
-extern unsigned long get_free_page(int priority);
+
+extern void free_page(unsigned long addr);
extern unsigned long put_dirty_page(struct task_struct * tsk,unsigned long page,
unsigned long address);
-extern void free_page(unsigned long addr);
extern void free_page_tables(struct task_struct * tsk);
extern void clear_page_tables(struct task_struct * tsk);
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 permiss);
+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,
@@ -100,10 +112,16 @@ extern void si_meminfo(struct sysinfo * val);
/* swap.c */
-extern void swap_free(unsigned int page_nr);
-extern void swap_duplicate(unsigned int page_nr);
+extern void swap_free(unsigned long page_nr);
+extern unsigned long swap_duplicate(unsigned long page_nr);
extern void swap_in(unsigned long *table_ptr);
extern void si_swapinfo(struct sysinfo * val);
+extern void rw_swap_page(int rw, unsigned long nr, char * buf);
+
+#define read_swap_page(nr,buf) \
+ rw_swap_page(READ,(nr),(buf))
+#define write_swap_page(nr,buf) \
+ rw_swap_page(WRITE,(nr),(buf))
#define invalidate() \
__asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3":::"ax")
@@ -115,11 +133,20 @@ extern unsigned long high_memory;
extern unsigned short * mem_map;
-#define PAGE_DIRTY 0x40
-#define PAGE_ACCESSED 0x20
-#define PAGE_USER 0x04
-#define PAGE_RW 0x02
-#define PAGE_PRESENT 0x01
+#define PAGE_PRESENT 0x001
+#define PAGE_RW 0x002
+#define PAGE_USER 0x004
+#define PAGE_PWT 0x008 /* 486 only - not used currently */
+#define PAGE_PCD 0x010 /* 486 only - not used currently */
+#define PAGE_ACCESSED 0x020
+#define PAGE_DIRTY 0x040
+#define PAGE_COW 0x200 /* implemented in software (one of the AVL bits) */
+
+#define PAGE_PRIVATE (PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_ACCESSED | PAGE_COW)
+#define PAGE_SHARED (PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_ACCESSED)
+#define PAGE_COPY (PAGE_PRESENT | PAGE_USER | PAGE_ACCESSED | PAGE_COW)
+#define PAGE_READONLY (PAGE_PRESENT | PAGE_USER | PAGE_ACCESSED)
+#define PAGE_TABLE (PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_ACCESSED)
#define GFP_BUFFER 0x00
#define GFP_ATOMIC 0x01
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 3ba206c..ba8dff7 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -60,7 +60,7 @@ extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
extern int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_sattr *sattr, struct nfs_fattr *fattr);
extern int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir,
- char *name, struct nfs_fh *fhandle,
+ const char *name, struct nfs_fh *fhandle,
struct nfs_fattr *fattr);
extern int nfs_proc_readlink(struct nfs_server *server, struct nfs_fh *fhandle,
char *res);
@@ -71,22 +71,22 @@ extern int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle,
int offset, int count, char *data,
struct nfs_fattr *fattr);
extern int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
- char *name, struct nfs_sattr *sattr,
+ const char *name, struct nfs_sattr *sattr,
struct nfs_fh *fhandle, struct nfs_fattr *fattr);
extern int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir,
- char *name);
+ const char *name);
extern int nfs_proc_rename(struct nfs_server *server,
- struct nfs_fh *old_dir, char *old_name,
- struct nfs_fh *new_dir, char *new_name);
+ struct nfs_fh *old_dir, const char *old_name,
+ struct nfs_fh *new_dir, const char *new_name);
extern int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
- struct nfs_fh *dir, char *name);
+ struct nfs_fh *dir, const char *name);
extern int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
- char *name, char *path, struct nfs_sattr *sattr);
+ const char *name, const char *path, struct nfs_sattr *sattr);
extern int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
- char *name, struct nfs_sattr *sattr,
+ const char *name, struct nfs_sattr *sattr,
struct nfs_fh *fhandle, struct nfs_fattr *fattr);
extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir,
- char *name);
+ const char *name);
extern int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
int cookie, int count, struct nfs_entry *entry);
extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
@@ -115,16 +115,4 @@ extern struct inode_operations nfs_dir_inode_operations;
extern struct inode_operations nfs_symlink_inode_operations;
-/* linux/fs/nfs/chrdev.c */
-
-extern struct inode_operations nfs_chrdev_inode_operations;
-
-/* linux/fs/nfs/blkdev.c */
-
-extern struct inode_operations nfs_blkdev_inode_operations;
-
-/* linux/fs/nfs/fifo.c */
-
-extern struct inode_operations nfs_fifo_inode_operations;
-
#endif
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
index 7dc742d..fc5c4e6 100644
--- a/include/linux/pipe_fs_i.h
+++ b/include/linux/pipe_fs_i.h
@@ -7,6 +7,8 @@ struct pipe_inode_info {
char * base;
unsigned int head;
unsigned int tail;
+ unsigned int rd_openers;
+ unsigned int wr_openers;
unsigned int readers;
unsigned int writers;
};
@@ -16,6 +18,8 @@ struct pipe_inode_info {
#define PIPE_BASE(inode) ((inode).u.pipe_i.base)
#define PIPE_HEAD(inode) ((inode).u.pipe_i.head)
#define PIPE_TAIL(inode) ((inode).u.pipe_i.tail)
+#define PIPE_RD_OPENERS(inode) ((inode).u.pipe_i.rd_openers)
+#define PIPE_WR_OPENERS(inode) ((inode).u.pipe_i.wr_openers)
#define PIPE_READERS(inode) ((inode).u.pipe_i.readers)
#define PIPE_WRITERS(inode) ((inode).u.pipe_i.writers)
#define PIPE_SIZE(inode) ((PIPE_HEAD(inode)-PIPE_TAIL(inode))&(PAGE_SIZE-1))
diff --git a/include/linux/serial.h b/include/linux/serial.h
index 21804a5..db5fd47 100644
--- a/include/linux/serial.h
+++ b/include/linux/serial.h
@@ -23,11 +23,12 @@ struct async_struct {
int flags; /* defined in tty.h */
int type; /* UART type */
struct tty_struct *tty;
- unsigned long timer;
+ int read_status_mask;
int timeout;
int xmit_fifo_size;
int custom_divisor;
int x_char; /* xon/xoff characater */
+ int close_delay;
int event;
int line;
int count; /* # of fd on device */
@@ -43,10 +44,9 @@ struct async_struct {
*/
#define RS_EVENT_READ_PROCESS 0
#define RS_EVENT_WRITE_WAKEUP 1
-#define RS_EVENT_HUP_PGRP 2
-#define RS_EVENT_BREAK_INT 3
-#define RS_EVENT_DO_SAK 4
-#define RS_EVENT_OPEN_WAKEUP 5
+#define RS_EVENT_HANGUP 2
+#define RS_EVENT_BREAK 3
+#define RS_EVENT_OPEN_WAKEUP 4
/*
* These are the UART port assignments, expressed as offsets from the base
diff --git a/include/linux/sock_ioctl.h b/include/linux/sock_ioctl.h
index d7384f1..cd824e1 100644
--- a/include/linux/sock_ioctl.h
+++ b/include/linux/sock_ioctl.h
@@ -1,14 +1,9 @@
-/* ip.h */
-/* Contains the structures for communicating with the ip level of the
- sockets. Currently just for configuration. */
#ifndef _LINUX_SOCK_IOCTL_H
#define _LINUX_SOCK_IOCTL_H
#define MAX_IP_NAME 20
/* some ioctl. Their values are not special. */
#define IP_SET_DEV 0x2401
-#define IP_ADD_ROUTE 0x2402
-#define IP_HANDOFF 0x2403
struct ip_config
{
diff --git a/include/linux/termios.h b/include/linux/termios.h
index b6d284d..59b89c5 100644
--- a/include/linux/termios.h
+++ b/include/linux/termios.h
@@ -40,6 +40,7 @@
#define TIOCNOTTY 0x5422
#define TIOCSETD 0x5423
#define TIOCGETD 0x5424
+#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
#define FIOASYNC 0x5452
@@ -69,7 +70,7 @@ struct termio {
unsigned char c_cc[NCC]; /* control characters */
};
-#define NCCS 17
+#define NCCS 19
struct termios {
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 767c347..4f7eaf0 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -78,7 +78,9 @@ struct serial_struct {
int xmit_fifo_size;
int custom_divisor;
int baud_base;
- int reserved[7];
+ char close_delay;
+ char reserved_char[3];
+ int reserved[6];
};
/*
@@ -94,6 +96,7 @@ struct serial_struct {
/*
* Definitions for async_struct (and serial_struct) flags field
*/
+#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) */
@@ -102,7 +105,7 @@ struct serial_struct {
#define ASYNC_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
#define ASYNC_SPD_CUST 0x0030 /* Use user-specified divisor */
-#define ASYNC_FLAGS 0x0036 /* Possible legal async flags */
+#define ASYNC_FLAGS 0x0037 /* Possible legal async flags */
/* Internal flags used only by kernel/chr_drv/serial.c */
#define ASYNC_INITIALIZED 0x80000000 /* Serial port was initialized */
@@ -143,6 +146,7 @@ extern int get_tty_queue(struct tty_queue * queue);
#define _L_FLAG(tty,f) ((tty)->termios->c_lflag & f)
#define _I_FLAG(tty,f) ((tty)->termios->c_iflag & f)
#define _O_FLAG(tty,f) ((tty)->termios->c_oflag & f)
+#define _C_FLAG(tty,f) ((tty)->termios->c_cflag & f)
#define L_CANON(tty) _L_FLAG((tty),ICANON)
#define L_ISIG(tty) _L_FLAG((tty),ISIG)
@@ -154,6 +158,11 @@ extern int get_tty_queue(struct tty_queue * queue);
#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE)
#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP)
+#define I_IGNBRK(tty) _I_FLAG((tty),IGNBRK)
+#define I_BRKINT(tty) _I_FLAG((tty),BRKINT)
+#define I_IGNPAR(tty) _I_FLAG((tty),IGNPAR)
+#define I_PARMRK(tty) _I_FLAG((tty),PARMRK)
+#define I_INPCK(tty) _I_FLAG((tty),INPCK)
#define I_UCLC(tty) _I_FLAG((tty),IUCLC)
#define I_NLCR(tty) _I_FLAG((tty),INLCR)
#define I_CRNL(tty) _I_FLAG((tty),ICRNL)
@@ -168,6 +177,8 @@ extern int get_tty_queue(struct tty_queue * queue);
#define O_NLRET(tty) _O_FLAG((tty),ONLRET)
#define O_LCUC(tty) _O_FLAG((tty),OLCUC)
+#define C_LOCAL(tty) _C_FLAG((tty),CLOCAL)
+#define C_RTSCTS(tty) _C_FLAG((tty),CRTSCTS)
#define C_SPEED(tty) ((tty)->termios->c_cflag & CBAUD)
#define C_HUP(tty) (C_SPEED((tty)) == B0)
@@ -190,6 +201,7 @@ struct tty_struct {
int pgrp;
int session;
unsigned char stopped:1, status_changed:1, packet:1, lnext:1;
+ unsigned char char_error:2;
unsigned char ctrl_status;
short line;
int disc;
@@ -208,6 +220,7 @@ struct tty_struct {
int write_data_cnt;
void (*write_data_callback)(void * data);
void * write_data_arg;
+ int readq_flags[TTY_BUF_SIZE/32];
struct tty_queue read_q;
struct tty_queue write_q;
struct tty_queue secondary;
@@ -280,6 +293,15 @@ struct tty_ldisc {
#define TTY_RQ_THROTTLED 4
#define TTY_IO_ERROR 5
+/*
+ * When a break, frame error, or parity error happens, these codes are
+ * stuffed into the read queue, and the relevant bit in readq_flag bit
+ * array is set.
+ */
+#define TTY_BREAK 1
+#define TTY_FRAME 2
+#define TTY_PARITY 3
+
#define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
#define TTY_READ_FLUSH(tty) tty_read_flush((tty))
@@ -324,7 +346,9 @@ extern int tty_signal(int sig, struct tty_struct *tty);
extern int kill_pg(int pgrp, int sig, int priv);
extern int kill_sl(int sess, int sig, int priv);
extern void tty_hangup(struct tty_struct * tty);
+extern void tty_vhangup(struct tty_struct * tty);
extern void tty_unhangup(struct file *filp);
+extern int tty_hung_up_p(struct file * filp);
extern void do_SAK(struct tty_struct *tty);
/* tty write functions */
diff --git a/include/linux/types.h b/include/linux/types.h
index 968a49e..70071ce 100644
--- a/include/linux/types.h
+++ b/include/linux/types.h
@@ -102,12 +102,12 @@ typedef struct fd_set {
"=m" (*(fd_set *) (fdsetp)):"r" ((int) (fd)))
#undef __FD_ISSET
-#define __FD_ISSET(fd,fdsetp) ({ \
- char __result; \
+#define __FD_ISSET(fd,fdsetp) (__extension__ ({ \
+ unsigned char __result; \
__asm__ __volatile__("btl %1,%2 ; setb %0" \
:"=q" (__result) :"r" ((int) (fd)), \
"m" (*(fd_set *) (fdsetp))); \
- __result; })
+ __result; }))
#undef __FD_ZERO
#define __FD_ZERO(fdsetp) \
diff --git a/include/linux/unistd.h b/include/linux/unistd.h
index e5ef1be..c422f64 100644
--- a/include/linux/unistd.h
+++ b/include/linux/unistd.h
@@ -123,6 +123,8 @@
#define __NR_wait4 114
#define __NR_swapoff 115
#define __NR_sysinfo 116
+#define __NR_ipc 117 /* not implemented yet */
+#define __NR_fsync 118 /* not implemented yet */
extern int errno;
diff --git a/include/linux/vfs.h b/include/linux/vfs.h
index 6d1f625..463df2f 100644
--- a/include/linux/vfs.h
+++ b/include/linux/vfs.h
@@ -14,7 +14,8 @@ struct statfs {
long f_files;
long f_ffree;
fsid_t f_fsid;
- long f_spare[7];
+ long f_namelen;
+ long f_spare[6];
};
#endif
diff --git a/include/linux/xia_fs.h b/include/linux/xia_fs.h
new file mode 100644
index 0000000..9d9b5f3
--- /dev/null
+++ b/include/linux/xia_fs.h
@@ -0,0 +1,116 @@
+#ifndef _XIA_FS_H
+#define _XIA_FS_H
+
+/*
+ * include/linux/xia_fs.h
+ *
+ * Copyright (C) Q. Frank Xia, 1993.
+ *
+ * Based on Linus' minix_fs.h.
+ * Copyright (C) Linus Torvalds, 1991, 1992.
+ */
+
+#define _XIAFS_SUPER_MAGIC 0x012FD16D
+#define _XIAFS_ROOT_INO 1
+#define _XIAFS_BAD_INO 2
+
+#define _XIAFS_NAME_LEN 248
+
+#define _XIAFS_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof(struct xiafs_inode)))
+
+struct xiafs_inode { /* 64 bytes */
+ mode_t i_mode;
+ nlink_t i_nlinks;
+ uid_t i_uid;
+ gid_t i_gid;
+ size_t i_size; /* 8 */
+ time_t i_ctime;
+ time_t i_atime;
+ time_t i_mtime;
+ daddr_t i_zone[8];
+ daddr_t i_ind_zone;
+ daddr_t i_dind_zone;
+};
+
+/*
+ * linux super-block data on disk
+ */
+struct xiafs_super_block {
+ u_char s_boot_segment[512]; /* 1st sector reserved for boot */
+ u_long s_zone_size; /* 0: the name says it */
+ u_long s_nzones; /* 1: volume size, zone aligned */
+ u_long s_ninodes; /* 2: # of inodes */
+ u_long s_ndatazones; /* 3: # of data zones */
+ u_long s_imap_zones; /* 4: # of imap zones */
+ u_long s_zmap_zones; /* 5: # of zmap zones */
+ u_long s_firstdatazone; /* 6: first data zone */
+ u_long s_zone_shift; /* 7: z size = 1KB << z shift */
+ u_long s_max_size; /* 8: max size of a single file */
+ u_long s_reserved0; /* 9: reserved */
+ u_long s_reserved1; /* 10: */
+ u_long s_reserved2; /* 11: */
+ u_long s_reserved3; /* 12: */
+ u_long s_firstkernzone; /* 13: first kernel zone */
+ u_long s_kernzones; /* 14: kernel size in zones */
+ u_long s_magic; /* 15: magic number for xiafs */
+};
+
+struct xiafs_direct {
+ ino_t d_ino;
+ u_short d_rec_len;
+ u_char d_name_len;
+ char d_name[_XIAFS_NAME_LEN+1];
+};
+
+extern int xiafs_lookup(struct inode * dir,const char * name, int len,
+ struct inode ** result);
+extern int xiafs_create(struct inode * dir,const char * name, int len, int mode,
+ struct inode ** result);
+extern int xiafs_mkdir(struct inode * dir, const char * name, int len, int mode);
+extern int xiafs_rmdir(struct inode * dir, const char * name, int len);
+extern int xiafs_unlink(struct inode * dir, const char * name, int len);
+extern int xiafs_symlink(struct inode * inode, const char * name, int len,
+ const char * symname);
+extern int xiafs_link(struct inode * oldinode, struct inode * dir,
+ const char * name, int len);
+extern int xiafs_mknod(struct inode * dir, const char * name, int len,
+ int mode, int rdev);
+extern int xiafs_rename(struct inode * old_dir, const char * old_name,
+ int old_len, struct inode * new_dir,
+ const char * new_name, int new_len);
+extern struct inode * xiafs_new_inode(struct inode * dir);
+extern void xiafs_free_inode(struct inode * inode);
+extern unsigned long xiafs_count_free_inodes(struct super_block *sb);
+extern int xiafs_new_zone(struct super_block * sb, u_long prev_addr);
+extern void xiafs_free_zone(struct super_block * sb, int block);
+extern unsigned long xiafs_count_free_zones(struct super_block *sb);
+
+extern int xiafs_bmap(struct inode *,int);
+
+extern struct buffer_head * xiafs_getblk(struct inode *, int, int);
+extern struct buffer_head * xiafs_bread(struct inode *, int, int);
+
+extern void xiafs_truncate(struct inode *);
+extern void xiafs_put_super(struct super_block *);
+extern struct super_block *xiafs_read_super(struct super_block *,void *);
+extern void xiafs_read_inode(struct inode *);
+extern void xiafs_write_inode(struct inode *);
+extern void xiafs_put_inode(struct inode *);
+extern void xiafs_statfs(struct super_block *, struct statfs *);
+
+extern struct inode_operations xiafs_file_inode_operations;
+extern struct inode_operations xiafs_dir_inode_operations;
+extern struct inode_operations xiafs_symlink_inode_operations;
+
+extern struct file_operations xiafs_file_operations;
+extern struct file_operations xiafs_dir_operations;
+
+#endif /* _XIA_FS_H */
+
+
+
+
+
+
+
+
diff --git a/include/linux/xia_fs_i.h b/include/linux/xia_fs_i.h
new file mode 100644
index 0000000..b176771
--- /dev/null
+++ b/include/linux/xia_fs_i.h
@@ -0,0 +1,19 @@
+#ifndef _XIA_FS_I_H
+#define _XIA_FS_I_H
+
+/*
+ * include/linux/xia_fs_i.h
+ *
+ * Copyright (C) Q. Frank Xia, 1993.
+ *
+ * Based on Linus' minix_fs_i.h.
+ * Copyright (C) Linus Torvalds, 1991, 1992.
+ */
+
+struct xiafs_inode_info { /* for data zone pointers */
+ daddr_t i_zone[8];
+ daddr_t i_ind_zone;
+ daddr_t i_dind_zone;
+};
+
+#endif /* _XIA_FS_I_H */
diff --git a/include/linux/xia_fs_sb.h b/include/linux/xia_fs_sb.h
new file mode 100644
index 0000000..1166ae9
--- /dev/null
+++ b/include/linux/xia_fs_sb.h
@@ -0,0 +1,36 @@
+#ifndef _XIA_FS_SB_H
+#define _XIA_FS_SB_H
+
+/*
+ * include/linux/xia_fs_sb.h
+ *
+ * Copyright (C) Q. Frank Xia, 1993.
+ *
+ * Based on Linus' minix_fs_sb.h.
+ * Copyright (C) Linus Torvalds, 1991, 1992.
+ */
+
+#define _XIAFS_IMAP_SLOTS 8
+#define _XIAFS_ZMAP_SLOTS 32
+
+struct xiafs_sb_info {
+ u_long s_nzones;
+ u_long s_ninodes;
+ u_long s_ndatazones;
+ u_long s_imap_zones;
+ u_long s_zmap_zones;
+ u_long s_firstdatazone;
+ u_long s_zone_shift;
+ u_long s_max_size; /* 32 bytes */
+ struct buffer_head * s_imap_buf[_XIAFS_IMAP_SLOTS]; /* 32 bytes */
+ struct buffer_head * s_zmap_buf[_XIAFS_ZMAP_SLOTS]; /* 128 bytes */
+ int s_imap_iznr[_XIAFS_IMAP_SLOTS]; /* 32 bytes */
+ int s_zmap_zznr[_XIAFS_ZMAP_SLOTS]; /* 128 bytes */
+ u_char s_imap_cached; /* flag for cached imap */
+ u_char s_zmap_cached; /* flag for cached imap */
+};
+
+#endif /* _XIA_FS_SB_H */
+
+
+
diff --git a/init/main.c b/init/main.c
index f8ac1af..72b03d4 100644
--- a/init/main.c
+++ b/init/main.c
@@ -56,7 +56,7 @@ static inline pid_t wait(int * wait_stat)
static char printbuf[1024];
-extern int vsprintf();
+extern int vsprintf(char *,const char *,va_list);
extern void init(void);
extern void init_IRQ(void);
extern long blk_dev_init(long,long);
@@ -328,7 +328,7 @@ void init(void)
/* nothing */;
while (1) {
if ((pid = fork()) < 0) {
- printf("Fork failed in init\r\n");
+ printf("Fork failed in init\n\r");
continue;
}
if (!pid) {
diff --git a/kernel/FPU-emu/Reg_constant.c b/kernel/FPU-emu/Reg_constant.c
new file mode 100644
index 0000000..aadf589
--- /dev/null
+++ b/kernel/FPU-emu/Reg_constant.c
@@ -0,0 +1,111 @@
+/*---------------------------------------------------------------------------+
+ | reg_constant.c |
+ | |
+ | All of the constant FPU_REGs |
+ | |
+ | Copyright (C) 1992 W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
+ | Australia. E-mail apm233m@vaxc.cc.monash.edu.au |
+ | |
+ | |
+ +---------------------------------------------------------------------------*/
+
+#include "fpu_system.h"
+#include "fpu_emu.h"
+#include "status_w.h"
+#include "reg_constant.h"
+
+
+FPU_REG CONST_1 = { SIGN_POS, TW_Valid, EXP_BIAS,
+ 0x00000000, 0x80000000 };
+FPU_REG CONST_2 = { SIGN_POS, TW_Valid, EXP_BIAS+1,
+ 0x00000000, 0x80000000 };
+FPU_REG CONST_HALF = { SIGN_POS, TW_Valid, EXP_BIAS-1,
+ 0x00000000, 0x80000000 };
+FPU_REG CONST_L2T = { SIGN_POS, TW_Valid, EXP_BIAS+1,
+ 0xcd1b8afe, 0xd49a784b };
+FPU_REG CONST_L2E = { SIGN_POS, TW_Valid, EXP_BIAS,
+ 0x5c17f0bc, 0xb8aa3b29 };
+FPU_REG CONST_PI = { SIGN_POS, TW_Valid, EXP_BIAS+1,
+ 0x2168c235, 0xc90fdaa2 };
+FPU_REG CONST_PI2 = { SIGN_POS, TW_Valid, EXP_BIAS,
+ 0x2168c235, 0xc90fdaa2 };
+FPU_REG CONST_PI4 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
+ 0x2168c235, 0xc90fdaa2 };
+FPU_REG CONST_LG2 = { SIGN_POS, TW_Valid, EXP_BIAS-2,
+ 0xfbcff799, 0x9a209a84 };
+FPU_REG CONST_LN2 = { SIGN_POS, TW_Valid, EXP_BIAS-1,
+ 0xd1cf79ac, 0xb17217f7 };
+
+/* Only the sign (and tag) is used in internal zeroes */
+FPU_REG CONST_Z = { SIGN_POS, TW_Zero, 0, 0x0, 0x0 };
+
+/* Only the sign and significand (and tag) are used in internal NaNs */
+/* The 80486 never generates one of these
+FPU_REG CONST_SNAN = { SIGN_POS, TW_NaN, EXP_OVER, 0x00000001, 0x80000000 };
+ */
+/* This is the real indefinite QNaN */
+FPU_REG CONST_QNaN = { SIGN_NEG, TW_NaN, EXP_OVER, 0x00000000, 0xC0000000 };
+
+/* Only the sign (and tag) is used in internal infinities */
+FPU_REG CONST_INF = { SIGN_POS, TW_Infinity, EXP_OVER, 0x00000000, 0x80000000 };
+
+
+
+static void fld_const(FPU_REG *c)
+{
+ FPU_REG *st_new_ptr;
+
+ if ( STACK_OVERFLOW )
+ {
+ stack_overflow();
+ return;
+ }
+ push();
+ reg_move(c, FPU_st0_ptr);
+ status_word &= ~SW_C1;
+}
+
+
+static void fld1()
+{
+ fld_const(&CONST_1);
+}
+
+static void fldl2t()
+{
+ fld_const(&CONST_L2T);
+}
+
+static void fldl2e()
+{
+ fld_const(&CONST_L2E);
+}
+
+static void fldpi()
+{
+ fld_const(&CONST_PI);
+}
+
+static void fldlg2()
+{
+ fld_const(&CONST_LG2);
+}
+
+static void fldln2()
+{
+ fld_const(&CONST_LN2);
+}
+
+static void fldz()
+{
+ fld_const(&CONST_Z);
+}
+
+static FUNC constants_table[] = {
+ fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, Un_impl
+};
+
+void fconst()
+{
+ (constants_table[FPU_rm])();
+}
diff --git a/kernel/FPU-emu/errors.c b/kernel/FPU-emu/errors.c
index e789740..07d5d12 100644
--- a/kernel/FPU-emu/errors.c
+++ b/kernel/FPU-emu/errors.c
@@ -243,7 +243,7 @@ void exception(int n)
{
#ifdef PRINT_MESSAGES
/* My message from the sponsor */
- printk(FPU_VERSION" "__DATE__" (C) W. Metzenthen.\r\n");
+ printk(FPU_VERSION" "__DATE__" (C) W. Metzenthen.\n");
#endif PRINT_MESSAGES
/* Get a name string for error reporting */
diff --git a/kernel/FPU-emu/fpu_aux.c b/kernel/FPU-emu/fpu_aux.c
index 4bcd630..8d6043f 100644
--- a/kernel/FPU-emu/fpu_aux.c
+++ b/kernel/FPU-emu/fpu_aux.c
@@ -16,7 +16,7 @@
-static void fclex()
+static void fclex(void)
{
status_word &= ~(SW_B|SW_ES|SW_SF|SW_PE|SW_UE|SW_OE|SW_ZE|SW_DE|SW_IE);
FPU_entry_eip = ip_offset; /* We want no net effect */
@@ -50,7 +50,7 @@ void finit_()
}
-static void fstsw_ax()
+static void fstsw_ax(void)
{
status_word &= ~SW_TOP;
@@ -71,7 +71,7 @@ void fstsw_()
-static void fnop()
+static void fnop(void)
{
}
diff --git a/kernel/FPU-emu/fpu_emu.h b/kernel/FPU-emu/fpu_emu.h
index a984f45..43354e4 100644
--- a/kernel/FPU-emu/fpu_emu.h
+++ b/kernel/FPU-emu/fpu_emu.h
@@ -51,7 +51,7 @@ extern char emulating;
# define RE_ENTRANT_CHECK_ON
#endif PARANOID
-typedef void (*FUNC)();
+typedef void (*FUNC)(void);
typedef struct fpu_reg FPU_REG;
#define st(x) ( regs[((top+x) &7 )] )
diff --git a/kernel/FPU-emu/fpu_entry.c b/kernel/FPU-emu/fpu_entry.c
index 222a190..b425849 100644
--- a/kernel/FPU-emu/fpu_entry.c
+++ b/kernel/FPU-emu/fpu_entry.c
@@ -140,7 +140,7 @@ void math_emulate(long arg)
#ifdef PARANOID
if ( emulating )
{
- printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\r\n");
+ printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
}
RE_ENTRANT_CHECK_ON
#endif PARANOID
@@ -162,7 +162,7 @@ void math_emulate(long arg)
/* 0x000f means user code space */
if (FPU_CS != 0x000f)
{
- printk("math_emulate: %04x:%08x\n\r",FPU_CS,FPU_EIP);
+ printk("math_emulate: %04x:%08x\n",FPU_CS,FPU_EIP);
panic("Math emulation needed in kernel");
}
diff --git a/kernel/FPU-emu/fpu_etc.c b/kernel/FPU-emu/fpu_etc.c
index ce160a1..f209694 100644
--- a/kernel/FPU-emu/fpu_etc.c
+++ b/kernel/FPU-emu/fpu_etc.c
@@ -16,7 +16,7 @@
#include "reg_constant.h"
-static void fchs()
+static void fchs(void)
{
if ( NOT_EMPTY_0 )
{
@@ -27,7 +27,7 @@ static void fchs()
stack_underflow();
}
-static void fabs()
+static void fabs(void)
{
if ( FPU_st0_tag ^ TW_Empty )
{
@@ -39,7 +39,7 @@ static void fabs()
}
-static void ftst_()
+static void ftst_(void)
{
switch (FPU_st0_tag)
{
@@ -75,7 +75,7 @@ static void ftst_()
}
}
-static void fxam()
+static void fxam(void)
{
int c=0;
switch (FPU_st0_tag)
diff --git a/kernel/FPU-emu/fpu_trig.c b/kernel/FPU-emu/fpu_trig.c
index 27e80c5..fa74c1b 100644
--- a/kernel/FPU-emu/fpu_trig.c
+++ b/kernel/FPU-emu/fpu_trig.c
@@ -93,7 +93,7 @@ static void single_arg_error(void)
/*---------------------------------------------------------------------------*/
-static void f2xm1()
+static void f2xm1(void)
{
switch ( FPU_st0_tag )
{
@@ -139,7 +139,7 @@ static void f2xm1()
}
}
-static void fptan()
+static void fptan(void)
{
FPU_REG *st_new_ptr;
int q;
@@ -192,7 +192,7 @@ static void fptan()
}
-static void fxtract()
+static void fxtract(void)
{
FPU_REG *st_new_ptr;
register FPU_REG *st1_ptr = FPU_st0_ptr; /* anticipate */
@@ -260,18 +260,18 @@ static void fxtract()
}
-static void fdecstp()
+static void fdecstp(void)
{
top--; /* FPU_st0_ptr will be fixed in math_emulate() before the next instr */
}
-static void fincstp()
+static void fincstp(void)
{
top++; /* FPU_st0_ptr will be fixed in math_emulate() before the next instr */
}
-static void fsqrt_()
+static void fsqrt_(void)
{
if ( !(FPU_st0_tag ^ TW_Valid) )
{
@@ -305,7 +305,7 @@ static void fsqrt_()
}
-static void frndint_()
+static void frndint_(void)
{
if ( !(FPU_st0_tag ^ TW_Valid) )
{
@@ -324,7 +324,7 @@ static void frndint_()
}
-static void fsin()
+static void fsin(void)
{
if ( FPU_st0_tag == TW_Valid )
{
@@ -428,13 +428,13 @@ static int f_cos(FPU_REG *arg)
}
-static void fcos()
+static void fcos(void)
{
f_cos(FPU_st0_ptr);
}
-static void fsincos()
+static void fsincos(void)
{
FPU_REG *st_new_ptr;
FPU_REG arg;
@@ -543,7 +543,7 @@ static void fprem_kernel(int round)
/* ST(1) <- ST(1) * log ST; pop ST */
-static void fyl2x()
+static void fyl2x(void)
{
FPU_REG *st1_ptr = &st(1);
char st1_tag = st1_ptr->tag;
@@ -652,7 +652,7 @@ static void fyl2x()
}
-static void fpatan()
+static void fpatan(void)
{
FPU_REG *st1_ptr = &st(1);
char st1_tag = st1_ptr->tag;
@@ -756,19 +756,19 @@ static void fpatan()
}
-static void fprem()
+static void fprem(void)
{
fprem_kernel(RC_CHOP);
}
-static void fprem1()
+static void fprem1(void)
{
fprem_kernel(RC_RND);
}
-static void fyl2xp1()
+static void fyl2xp1(void)
{
FPU_REG *st1_ptr = &st(1);
char st1_tag = st1_ptr->tag;
@@ -834,7 +834,7 @@ static void fyl2xp1()
}
-static void fscale()
+static void fscale(void)
{
FPU_REG *st1_ptr = &st(1);
char st1_tag = st1_ptr->tag;
@@ -940,7 +940,7 @@ static FUNC trig_table_a[] = {
f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp
};
-void trig_a()
+void trig_a(void)
{
(trig_table_a[FPU_rm])();
}
@@ -951,7 +951,7 @@ static FUNC trig_table_b[] =
fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos
};
-void trig_b()
+void trig_b(void)
{
(trig_table_b[FPU_rm])();
}
diff --git a/kernel/FPU-emu/load_store.c b/kernel/FPU-emu/load_store.c
index 51ea3ef..4a6357d 100644
--- a/kernel/FPU-emu/load_store.c
+++ b/kernel/FPU-emu/load_store.c
@@ -174,7 +174,7 @@ switch ( type )
break;
case 034: /* fstcw m16int */
RE_ENTRANT_CHECK_OFF
- verify_area(FPU_data_address,2);
+ verify_area(VERIFY_WRITE,FPU_data_address,2);
put_fs_word(control_word, (short *) FPU_data_address);
RE_ENTRANT_CHECK_ON
FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
@@ -189,7 +189,7 @@ switch ( type )
status_word &= ~SW_TOP;
status_word |= (top&7) << SW_TOPS;
RE_ENTRANT_CHECK_OFF
- verify_area(FPU_data_address,2);
+ verify_area(VERIFY_WRITE,FPU_data_address,2);
put_fs_word(status_word,(short *) FPU_data_address);
RE_ENTRANT_CHECK_ON
FPU_data_address = (void *)data_operand_offset; /* We want no net effect */
diff --git a/kernel/FPU-emu/reg_constant.c b/kernel/FPU-emu/reg_constant.c
index aadf589..b61e39d 100644
--- a/kernel/FPU-emu/reg_constant.c
+++ b/kernel/FPU-emu/reg_constant.c
@@ -66,37 +66,37 @@ static void fld_const(FPU_REG *c)
}
-static void fld1()
+static void fld1(void)
{
fld_const(&CONST_1);
}
-static void fldl2t()
+static void fldl2t(void)
{
fld_const(&CONST_L2T);
}
-static void fldl2e()
+static void fldl2e(void)
{
fld_const(&CONST_L2E);
}
-static void fldpi()
+static void fldpi(void)
{
fld_const(&CONST_PI);
}
-static void fldlg2()
+static void fldlg2(void)
{
fld_const(&CONST_LG2);
}
-static void fldln2()
+static void fldln2(void)
{
fld_const(&CONST_LN2);
}
-static void fldz()
+static void fldz(void)
{
fld_const(&CONST_Z);
}
@@ -105,7 +105,7 @@ static FUNC constants_table[] = {
fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz, Un_impl
};
-void fconst()
+void fconst(void)
{
(constants_table[FPU_rm])();
}
diff --git a/kernel/FPU-emu/reg_ld_str.c b/kernel/FPU-emu/reg_ld_str.c
index b6e87db..1972d3d 100644
--- a/kernel/FPU-emu/reg_ld_str.c
+++ b/kernel/FPU-emu/reg_ld_str.c
@@ -485,7 +485,7 @@ int reg_store_extended(void)
ms = 0x80000000;
}
RE_ENTRANT_CHECK_OFF
- verify_area(d,10);
+ verify_area(VERIFY_WRITE,d,10);
put_fs_long(ls, (unsigned long *) d);
put_fs_long(ms, 1 + (unsigned long *) d);
put_fs_word((unsigned short)e | sign, 4 + (short *) d);
@@ -620,7 +620,7 @@ int reg_store_double(void)
/* Put out the QNaN indefinite */
put_indefinite:
RE_ENTRANT_CHECK_OFF
- verify_area((void *)dfloat,8);
+ verify_area(VERIFY_WRITE,(void *)dfloat,8);
put_fs_long(0, (unsigned long *) dfloat);
put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat);
RE_ENTRANT_CHECK_ON
@@ -639,7 +639,7 @@ put_indefinite:
l[1] |= 0x80000000;
RE_ENTRANT_CHECK_OFF
- verify_area((void *)dfloat,8);
+ verify_area(VERIFY_WRITE,(void *)dfloat,8);
put_fs_long(l[0], (unsigned long *)dfloat);
put_fs_long(l[1], 1 + (unsigned long *)dfloat);
RE_ENTRANT_CHECK_ON
@@ -747,7 +747,7 @@ int reg_store_single(void)
/* Put out the QNaN indefinite */
put_indefinite:
RE_ENTRANT_CHECK_OFF
- verify_area((void *)single,4);
+ verify_area(VERIFY_WRITE,(void *)single,4);
put_fs_long(0xffc00000, (unsigned long *) single);
RE_ENTRANT_CHECK_ON
return 1;
@@ -772,7 +772,7 @@ put_indefinite:
templ |= 0x80000000;
RE_ENTRANT_CHECK_OFF
- verify_area((void *)single,4);
+ verify_area(VERIFY_WRITE,(void *)single,4);
put_fs_long(templ,(unsigned long *) single);
RE_ENTRANT_CHECK_ON
@@ -824,7 +824,7 @@ put_indefinite:
tll = - tll;
RE_ENTRANT_CHECK_OFF
- verify_area((void *)d,8);
+ verify_area(VERIFY_WRITE,(void *)d,8);
put_fs_long(((long *)&tll)[0],(unsigned long *) d);
put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d);
RE_ENTRANT_CHECK_ON
@@ -848,7 +848,7 @@ int reg_store_int32(void)
/* The masked response */
/* Put out the QNaN indefinite */
RE_ENTRANT_CHECK_OFF
- verify_area(d,4);
+ verify_area(VERIFY_WRITE,d,4);
put_fs_long(0x80000000, (unsigned long *) d);
RE_ENTRANT_CHECK_ON
return 1;
@@ -877,7 +877,7 @@ int reg_store_int32(void)
t.sigl = -(long)t.sigl;
RE_ENTRANT_CHECK_OFF
- verify_area(d,4);
+ verify_area(VERIFY_WRITE,d,4);
put_fs_long(t.sigl, (unsigned long *) d);
RE_ENTRANT_CHECK_ON
@@ -901,7 +901,7 @@ int reg_store_int16(void)
/* The masked response */
/* Put out the QNaN indefinite */
RE_ENTRANT_CHECK_OFF
- verify_area(d,2);
+ verify_area(VERIFY_WRITE,d,2);
put_fs_word(0x8000, (unsigned short *) d);
RE_ENTRANT_CHECK_ON
return 1;
@@ -930,7 +930,7 @@ int reg_store_int16(void)
t.sigl = -t.sigl;
RE_ENTRANT_CHECK_OFF
- verify_area(d,2);
+ verify_area(VERIFY_WRITE,d,2);
put_fs_word((short)t.sigl,(short *) d);
RE_ENTRANT_CHECK_ON
@@ -977,7 +977,7 @@ int reg_store_bcd(void)
put_indefinite:
/* Produce "indefinite" */
RE_ENTRANT_CHECK_OFF
- verify_area(d,10);
+ verify_area(VERIFY_WRITE,d,10);
put_fs_byte(0xff,(unsigned char *) d+7);
put_fs_byte(0xff,(unsigned char *) d+8);
put_fs_byte(0xff,(unsigned char *) d+9);
@@ -988,7 +988,7 @@ put_indefinite:
return 0;
}
- verify_area(d,10);
+ verify_area(VERIFY_WRITE,d,10);
for ( i = 0; i < 9; i++)
{
b = div_small(&ll, 10);
@@ -1149,7 +1149,7 @@ char *fstenv(void)
unsigned char tag;
int i;
- verify_area(d,28);
+ verify_area(VERIFY_WRITE,d,28);
for ( i = 7; i >= 0; i-- )
{
@@ -1195,7 +1195,7 @@ void fsave(void)
short e;
d = fstenv();
- verify_area(d,80);
+ verify_area(VERIFY_WRITE,d,80);
for ( i = 0; i < 8; i++ )
{
/* store each register */
diff --git a/kernel/blk_drv/blk.h b/kernel/blk_drv/blk.h
index 1fb05b8..e054c49 100644
--- a/kernel/blk_drv/blk.h
+++ b/kernel/blk_drv/blk.h
@@ -81,8 +81,8 @@ extern int ramdisk_size;
#define RO_IOCTLS(dev,where) \
case BLKROSET: if (!suser()) return -EPERM; \
set_device_ro((dev),get_fs_long((long *) (where))); return 0; \
- case BLKROGET: verify_area((void *) (where), sizeof(long)); \
- put_fs_long(is_read_only(dev),(long *) (where)); return 0;
+ case BLKROGET: { int __err = verify_area(VERIFY_WRITE, (void *) (where), sizeof(long)); \
+ if (!__err) put_fs_long(is_read_only(dev),(long *) (where)); return __err; }
#ifdef MAJOR_NR
@@ -205,8 +205,8 @@ static void end_request(int uptodate)
req = CURRENT;
req->errors = 0;
if (!uptodate) {
- printk(DEVICE_NAME " I/O error\n\r");
- printk("dev %04x, sector %d\n\r",req->dev,req->sector);
+ printk(DEVICE_NAME " I/O error\n");
+ printk("dev %04x, sector %d\n",req->dev,req->sector);
req->nr_sectors--;
req->nr_sectors &= ~SECTOR_MASK;
req->sector += (BLOCK_SIZE / 512);
diff --git a/kernel/blk_drv/floppy.c b/kernel/blk_drv/floppy.c
index 382b837..4b67fb0 100644
--- a/kernel/blk_drv/floppy.c
+++ b/kernel/blk_drv/floppy.c
@@ -1033,7 +1033,7 @@ void do_fd_request(void)
static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
unsigned int param)
{
- int drive,cnt,okay;
+ int i,drive,cnt,okay;
struct floppy_struct *this;
switch (cmd) {
@@ -1058,7 +1058,9 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
if (drive > 3) this = &floppy_type[drive >> 2];
else if ((this = current_type[drive & 3]) == NULL)
return -ENODEV;
- verify_area((void *) param,sizeof(struct floppy_struct));
+ i = verify_area(VERIFY_WRITE,(void *) param,sizeof(struct floppy_struct));
+ if (i)
+ return i;
for (cnt = 0; cnt < sizeof(struct floppy_struct); cnt++)
put_fs_byte(((char *) this)[cnt],
(char *) param+cnt);
diff --git a/kernel/blk_drv/hd.c b/kernel/blk_drv/hd.c
index 632eae8..187a5ee 100644
--- a/kernel/blk_drv/hd.c
+++ b/kernel/blk_drv/hd.c
@@ -209,7 +209,7 @@ static int drive_busy(void)
if (c == (READY_STAT | SEEK_STAT))
return 0;
}
- printk("HD controller times out, status = 0x%02x\n\r",c);
+ printk("HD controller times out, status = 0x%02x\n",c);
return 1;
}
@@ -217,14 +217,14 @@ static void reset_controller(void)
{
int i;
- printk("HD-controller reset\r\n");
+ printk("HD-controller reset\n");
outb(4,HD_CMD);
for(i = 0; i < 1000; i++) nop();
outb(hd_info[0].ctl & 0x0f ,HD_CMD);
if (drive_busy())
- printk("HD-controller still busy\n\r");
+ printk("HD-controller still busy\n");
if ((hd_error = inb(HD_ERROR)) != 1)
- printk("HD-controller reset failed: %02x\n\r",hd_error);
+ printk("HD-controller reset failed: %02x\n",hd_error);
}
static void reset_hd(void)
@@ -259,7 +259,7 @@ repeat:
void unexpected_hd_interrupt(void)
{
sti();
- printk("Unexpected HD interrupt\n\r");
+ printk("Unexpected HD interrupt\n");
SET_TIMER;
}
@@ -407,7 +407,7 @@ static void hd_times_out(void)
reset = 1;
if (!CURRENT)
return;
- printk("HD timeout\n\r");
+ printk("HD timeout\n");
cli();
if (++CURRENT->errors >= MAX_ERRORS) {
#ifdef DEBUG
@@ -506,7 +506,7 @@ static int hd_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned int arg)
{
struct hd_geometry *loc = (void *) arg;
- int dev;
+ int dev, err;
if (!inode)
return -EINVAL;
@@ -516,7 +516,9 @@ static int hd_ioctl(struct inode * inode, struct file * file,
switch (cmd) {
case HDIO_GETGEO:
if (!loc) return -EINVAL;
- verify_area(loc, sizeof(*loc));
+ err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+ if (err)
+ return err;
put_fs_byte(hd_info[dev].head,
(char *) &loc->heads);
put_fs_byte(hd_info[dev].sect,
@@ -528,7 +530,9 @@ static int hd_ioctl(struct inode * inode, struct file * file,
return 0;
case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
- verify_area((long *) arg, sizeof(long));
+ err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+ if (err)
+ return err;
put_fs_long(hd[MINOR(inode->i_rdev)].nr_sects,
(long *) arg);
return 0;
@@ -565,7 +569,7 @@ static void hd_release(struct inode * inode, struct file * file)
}
-static void hd_geninit();
+static void hd_geninit(void);
static struct gendisk hd_gendisk = {
MAJOR_NR, /* Major number */
@@ -657,16 +661,29 @@ static void hd_geninit(void)
else
NR_HD = 0;
#endif
+ i = NR_HD;
+ while (i-- > 0) {
+ hd[i<<6].nr_sects = 0;
+ if (hd_info[i].head > 16) {
+ printk("hd.c: ST-506 interface disk with more than 16 heads detected,\n");
+ printk(" probably due to non-standard sector translation. Giving up.\n");
+ printk(" (disk %d: cyl=%d, sect=%d, head=%d)\n", i,
+ hd_info[i].cyl,
+ hd_info[i].sect,
+ hd_info[i].head);
+ if (i+1 == NR_HD)
+ NR_HD--;
+ continue;
+ }
+ hd[i<<6].nr_sects = hd_info[i].head*
+ hd_info[i].sect*hd_info[i].cyl;
+ }
if (NR_HD) {
if (irqaction(HD_IRQ,&hd_sigaction)) {
- printk("Unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
+ printk("hd.c: unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
NR_HD = 0;
}
}
- for (i = 0 ; i < NR_HD ; i++)
- hd[i<<6].nr_sects = hd_info[i].head*
- hd_info[i].sect*hd_info[i].cyl;
-
hd_gendisk.nr_real = NR_HD;
}
diff --git a/kernel/blk_drv/ll_rw_blk.c b/kernel/blk_drv/ll_rw_blk.c
index 99d09be..583d501 100644
--- a/kernel/blk_drv/ll_rw_blk.c
+++ b/kernel/blk_drv/ll_rw_blk.c
@@ -229,7 +229,7 @@ void ll_rw_page(int rw, int dev, int page, char * buffer)
if (rw!=READ && rw!=WRITE)
panic("Bad block dev command, must be R/W");
if (rw == WRITE && is_read_only(dev)) {
- printk("Can't page to read-only device 0x%X\n\r",dev);
+ printk("Can't page to read-only device 0x%X\n",dev);
return;
}
cli();
@@ -297,7 +297,7 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
return;
}
if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
- printk("Can't write to read-only device 0x%X\n\r",bh[0]->b_dev);
+ printk("Can't write to read-only device 0x%X\n",bh[0]->b_dev);
for (i=0;i<nr; i++)
if (bh[i]) bh[i]->b_dirt = bh[i]->b_uptodate = 0;
return;
@@ -333,7 +333,7 @@ void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
unsigned int major = MAJOR(dev);
if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
- printk("ll_rw_swap_file: trying to swap nonexistent block-device\n\r");
+ printk("ll_rw_swap_file: trying to swap nonexistent block-device\n");
return;
}
@@ -342,7 +342,7 @@ void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
return;
}
if (rw == WRITE && is_read_only(dev)) {
- printk("Can't swap to read-only device 0x%X\n\r",dev);
+ printk("Can't swap to read-only device 0x%X\n",dev);
return;
}
diff --git a/kernel/blk_drv/scsi/aha1542.c b/kernel/blk_drv/scsi/aha1542.c
index defad12..2a1a448 100644
--- a/kernel/blk_drv/scsi/aha1542.c
+++ b/kernel/blk_drv/scsi/aha1542.c
@@ -297,14 +297,15 @@ void aha1542_intr_handle(int foo)
#endif
SCtmp = SCint[mbo];
- my_done = SCtmp->scsi_done;
- if (SCtmp->host_scribble) scsi_free(SCtmp->host_scribble, 512);
-
- if (!my_done) {
+
+ if (!SCtmp || !SCtmp->scsi_done) {
printk("aha1542_intr_handle: Unexpected interrupt\n");
return;
}
+ my_done = SCtmp->scsi_done;
+ if (SCtmp->host_scribble) scsi_free(SCtmp->host_scribble, 512);
+
/* Fetch the sense data, and tuck it away, in the required slot. The
Adaptec automatically fetches it, and there is no guarantee that
we will still have it in the cdb when we come back */
diff --git a/kernel/blk_drv/scsi/hosts.c b/kernel/blk_drv/scsi/hosts.c
index 98b5f54..02da3fc 100644
--- a/kernel/blk_drv/scsi/hosts.c
+++ b/kernel/blk_drv/scsi/hosts.c
@@ -129,13 +129,13 @@ void scsi_init(void)
if ((scsi_hosts[i].detect) && (scsi_hosts[i].present = scsi_hosts[i].detect(i)))
{
- printk ("scsi%d : %s.\n\r",
+ printk ("scsi%d : %s.\n",
count, scsi_hosts[i].name);
printk ("%s", scsi_hosts[i].info());
++count;
}
}
- printk ("scsi : %d hosts. \n\r", count);
+ printk ("scsi : %d hosts.\n", count);
}
}
diff --git a/kernel/blk_drv/scsi/scsi.c b/kernel/blk_drv/scsi/scsi.c
index b4cf946..13187ac 100644
--- a/kernel/blk_drv/scsi/scsi.c
+++ b/kernel/blk_drv/scsi/scsi.c
@@ -143,7 +143,7 @@ static void scan_scsis_done (Scsi_Cmnd * SCpnt)
{
#ifdef DEBUG
- printk ("scan_scsis_done(%d, %06x)\n\r", SCpnt->host, SCpnt->result);
+ printk ("scan_scsis_done(%d, %06x)\n", SCpnt->host, SCpnt->result);
#endif
SCpnt->request.dev = 0xfffe;
}
@@ -379,19 +379,19 @@ static void scsi_times_out (Scsi_Cmnd * SCpnt)
{
case NORMAL_TIMEOUT:
if (!in_scan)
- printk("SCSI host %d timed out - aborting command \r\n",
+ printk("SCSI host %d timed out - aborting command\n",
SCpnt->host);
if (!scsi_abort (SCpnt, DID_TIME_OUT))
return;
case IN_ABORT:
- printk("SCSI host %d abort() timed out - reseting \r\n",
+ printk("SCSI host %d abort() timed out - reseting\n",
SCpnt->host);
if (!scsi_reset (SCpnt))
return;
case IN_RESET:
case (IN_ABORT | IN_RESET):
- printk("Unable to reset scsi host %d\r\n",SCpnt->host);
+ printk("Unable to reset scsi host %d\n",SCpnt->host);
panic("");
default:
INTERNAL_ERROR;
@@ -963,6 +963,16 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
case DID_ABORT:
exit = (DRIVER_INVALID | SUGGEST_ABORT);
break;
+ case DID_RESET:
+ if(msg_byte(result) == GOOD &&
+ status_byte(result) == CHECK_CONDITION) {
+ scsi_request_sense (SCpnt);
+ status = PENDING;
+ break;
+ };
+ status=REDO;
+ exit = SUGGEST_RETRY;
+ break;
default :
exit = (DRIVER_ERROR | SUGGEST_DIE);
}
@@ -975,7 +985,7 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
case MAYREDO:
#ifdef DEBUG
- printk("In MAYREDO, allowing %d retries, have %d\n\r",
+ printk("In MAYREDO, allowing %d retries, have %d\n",
SCpnt->allowed, SCpnt->retries);
#endif
diff --git a/kernel/blk_drv/scsi/scsi.h b/kernel/blk_drv/scsi/scsi.h
index 4ed86ed..e86b141 100644
--- a/kernel/blk_drv/scsi/scsi.h
+++ b/kernel/blk_drv/scsi/scsi.h
@@ -422,8 +422,8 @@ static void end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors)
req = &SCpnt->request;
req->errors = 0;
if (!uptodate) {
- printk(DEVICE_NAME " I/O error\n\r");
- printk("dev %04x, sector %d\n\r",req->dev,req->sector);
+ printk(DEVICE_NAME " I/O error\n");
+ printk("dev %04x, sector %d\n",req->dev,req->sector);
}
do {
diff --git a/kernel/blk_drv/scsi/scsi_ioctl.c b/kernel/blk_drv/scsi/scsi_ioctl.c
index b8a5661..b3724ef 100644
--- a/kernel/blk_drv/scsi/scsi_ioctl.c
+++ b/kernel/blk_drv/scsi/scsi_ioctl.c
@@ -137,7 +137,7 @@ static int ioctl_internal_command(Scsi_Device *dev, char * cmd)
static int ioctl_command(Scsi_Device *dev, void *buffer)
{
char * buf;
- char cmd[10];
+ char cmd[12];
char * cmd_in;
Scsi_Cmnd * SCpnt;
unsigned char opcode;
@@ -164,8 +164,9 @@ static int ioctl_command(Scsi_Device *dev, void *buffer)
buf = NULL;
memcpy_fromfs ((void *) cmd, cmd_in, cmdlen = COMMAND_SIZE (opcode));
- memcpy_fromfs ((void *) buf, (void *) (cmd_in + cmdlen), inlen);
+ memcpy_fromfs ((void *) buf, (void *) (cmd_in + cmdlen), inlen > MAX_BUF ? MAX_BUF : inlen);
host = dev->host_no;
+ cmd[1] = ( cmd[1] & 0x1f ) | (dev->lun << 5);
#ifndef DEBUG_NO_CMD
@@ -181,7 +182,9 @@ static int ioctl_command(Scsi_Device *dev, void *buffer)
while (SCpnt->request.dev != 0xfffe) schedule();
};
- verify_area(cmd_in, (outlen > MAX_BUF) ? MAX_BUF : outlen);
+ result = verify_area(VERIFY_WRITE, cmd_in, (outlen > MAX_BUF) ? MAX_BUF : outlen);
+ if (result)
+ return result;
memcpy_tofs ((void *) cmd_in, buf, (outlen > MAX_BUF) ? MAX_BUF : outlen);
result = SCpnt->result;
SCpnt->request.dev = -1; /* Mark as not busy */
@@ -194,10 +197,10 @@ static int ioctl_command(Scsi_Device *dev, void *buffer)
printk("scsi_ioctl : device %d. command = ", dev->id);
for (i = 0; i < 10; ++i)
printk("%02x ", cmd[i]);
- printk("\r\nbuffer =");
+ printk("\nbuffer =");
for (i = 0; i < 20; ++i)
printk("%02x ", buf[i]);
- printk("\r\n");
+ printk("\n");
printk("inlen = %d, outlen = %d, cmdlen = %d\n",
inlen, outlen, cmdlen);
printk("buffer = %d, cmd_in = %d\n", buffer, cmd_in);
diff --git a/kernel/blk_drv/scsi/sd.c b/kernel/blk_drv/scsi/sd.c
index 1481795..47d3767 100644
--- a/kernel/blk_drv/scsi/sd.c
+++ b/kernel/blk_drv/scsi/sd.c
@@ -435,7 +435,7 @@ repeat:
cmd[0] = READ_6;
break;
default :
- printk ("Unknown sd command %d\r\n", SCpnt->request.cmd);
+ printk ("Unknown sd command %d\n", SCpnt->request.cmd);
panic("");
}
diff --git a/kernel/blk_drv/scsi/sd_ioctl.c b/kernel/blk_drv/scsi/sd_ioctl.c
index 1d60189..a658f4e 100644
--- a/kernel/blk_drv/scsi/sd_ioctl.c
+++ b/kernel/blk_drv/scsi/sd_ioctl.c
@@ -17,14 +17,16 @@ extern int revalidate_scsidisk(int, int);
int sd_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsigned long arg)
{
int dev = inode->i_rdev;
- int host;
+ int host, error;
int diskinfo[4];
struct hd_geometry *loc = (void *) arg;
switch (cmd) {
case HDIO_REQ: /* Return BIOS disk parameters */
if (!loc) return -EINVAL;
- verify_area(loc, sizeof(*loc));
+ error = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+ if (error)
+ return error;
host = rscsi_disks[MINOR(dev) >> 4].device->host_no;
diskinfo[0] = 0;
diskinfo[1] = 0;
@@ -44,7 +46,9 @@ int sd_ioctl(struct inode * inode, struct file * file, unsigned long cmd, unsign
return 0;
case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
- verify_area((long *) arg, sizeof(long));
+ error = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+ if (error)
+ return error;
put_fs_long(sd[MINOR(inode->i_rdev)].nr_sects,
(long *) arg);
return 0;
diff --git a/kernel/blk_drv/scsi/seagate.c b/kernel/blk_drv/scsi/seagate.c
index a09d4a0..f9598e9 100644
--- a/kernel/blk_drv/scsi/seagate.c
+++ b/kernel/blk_drv/scsi/seagate.c
@@ -571,7 +571,7 @@ static int internal_command(unsigned char target, unsigned char lun, const void
printk("seagate_st0x_timeout : %d \r",temp);
}
- printk("Done. \n\r");
+ printk("Done. \n");
printk("scsi%d : status = %02x, seagate_st0x_timeout = %d, aborted = %02x \n",
hostno, status_read, temp, st0x_aborted);
#else
diff --git a/kernel/blk_drv/scsi/sr.c b/kernel/blk_drv/scsi/sr.c
index 5b483fc..24140d9 100644
--- a/kernel/blk_drv/scsi/sr.c
+++ b/kernel/blk_drv/scsi/sr.c
@@ -381,7 +381,7 @@ void requeue_sr_request (Scsi_Cmnd * SCpnt)
cmd[0] = READ_6;
break;
default :
- printk ("Unknown sr command %d\r\n", SCpnt->request.cmd);
+ printk ("Unknown sr command %d\n", SCpnt->request.cmd);
panic("");
}
diff --git a/kernel/blk_drv/scsi/st.c b/kernel/blk_drv/scsi/st.c
index ec8f77d..29e37a1 100644
--- a/kernel/blk_drv/scsi/st.c
+++ b/kernel/blk_drv/scsi/st.c
@@ -1109,7 +1109,9 @@ static int st_ioctl(struct inode * inode,struct file * file,
if (((cmd_in & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(mtc))
return (-EINVAL);
- verify_area((void *)arg, sizeof(mtc));
+ i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(mtc));
+ if (i)
+ return i;
memcpy_fromfs((char *) &mtc, (char *)arg, sizeof(struct mtop));
@@ -1125,7 +1127,9 @@ static int st_ioctl(struct inode * inode,struct file * file,
if (((cmd_in & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtget))
return (-EINVAL);
- verify_area((void *)arg, sizeof(struct mtget));
+ i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct mtget));
+ if (i)
+ return i;
memcpy_tofs((char *)arg, (char *)scsi_tapes[dev].buffer->mt_status,
sizeof(struct mtget));
@@ -1142,7 +1146,9 @@ static int st_ioctl(struct inode * inode,struct file * file,
if (i < 0)
return i;
- verify_area((void *)arg, sizeof(struct mtpos));
+ i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct mtpos));
+ if (i)
+ return i;
SCpnt = allocate_device(NULL, scsi_tapes[dev].device->index, 1);
diff --git a/kernel/blk_drv/scsi/ultrastor.c b/kernel/blk_drv/scsi/ultrastor.c
index 0eac5a3..f02dd4b 100644
--- a/kernel/blk_drv/scsi/ultrastor.c
+++ b/kernel/blk_drv/scsi/ultrastor.c
@@ -3,36 +3,42 @@
* Low-level SCSI driver for UltraStor 14F
* by David B. Gentzel, Whitfield Software Services, Carnegie, PA
* (gentzel@nova.enet.dec.com)
+ * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu)
* Thanks to UltraStor for providing the necessary documentation
*/
/*
+ * TODO:
+ * 1. Cleanup error handling & reporting.
+ * 2. Find out why scatter/gather is limited to 16 requests per command.
+ * 3. Add multiple outstanding requests.
+ * 4. See if we can make good use of having more than one command per lun.
+ * 5. Test/improve/fix abort & reset functions.
+ * 6. Look at command linking (mscp.command_link and
+ * mscp.command_link_id).
+ */
+
+/*
* NOTES:
- * The UltraStor 14F is an intelligent, high performance ISA SCSI-2 host
- * adapter. It is essentially an ISA version of the UltraStor 24F EISA
- * adapter. It supports first-party DMA, command queueing, and
- * scatter/gather I/O. It can also emulate the standard AT MFM/RLL/IDE
- * interface for use with OS's which don't support SCSI.
+ * The UltraStor 14F is one of a family of intelligent, high performance
+ * SCSI-2 host adapters. They all support command queueing and
+ * scatter/gather I/O. Some of them can also emulate the standard
+ * WD1003 interface for use with OS's which don't support SCSI.
+ * Here is the scoop on the various models:
+ * 14F - ISA first-party DMA HA with floppy support and WD1003 emulation.
+ * 14N - ISA HA with floppy support. I think that this is a non-DMA
+ * HA. Nothing further known.
+ * 24F - EISA Bus Master HA with floppy support and WD1003 emulation.
+ * 34F - VL-Bus Bus Master HA with floppy support (no WD1003 emulation).
*
- * This driver may also work (with some small changes) with the UltraStor
- * 24F. I have no way of confirming this...
+ * The 14F is supported by this driver. An effort has been made to support
+ * the 34F. It should work, but is untested. The 24F does not work at
+ * present.
*
* Places flagged with a triple question-mark are things which are either
* unfinished, questionable, or wrong.
*/
-/*
- * CAVEATS: ???
- * This driver is VERY stupid. It takes no advantage of much of the power
- * of the UltraStor controller. I hope to go back and beat it into shape,
- * but PLEASE, anyone else who would like to, please make improvements!
- *
- * By defining NO_QUEUEING in ultrastor.h, you disable the queueing feature
- * of the mid-level SCSI driver. Once I'm satisfied that the queueing
- * version is as stable as the non-queueing version, I'll eliminate this
- * option.
- */
-
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/sched.h>
@@ -48,10 +54,11 @@
#include "hosts.h"
#include "ultrastor.h"
+#define ULTRASTOR_DEBUG 0
+
#define VERSION "1.1 alpha"
#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
-#define BIT(n) (1ul << (n))
#define BYTE(num, n) ((unsigned char)((unsigned int)(num) >> ((n) * 8)))
/* Simply using "unsigned long" in these structures won't work as it causes
@@ -65,7 +72,7 @@ typedef struct {
then store (in a friendlier format) in config. */
struct config_1 {
unsigned char bios_segment: 3;
- unsigned char reserved: 1;
+ unsigned char removable_disks_as_fixed: 1;
unsigned char interrupt: 2;
unsigned char dma_channel: 2;
};
@@ -79,14 +86,15 @@ struct config_2 {
/* Used to store configuration info read from config i/o registers. Most of
this is not used yet, but might as well save it. */
struct config {
- unsigned short port_address;
const void *bios_segment;
+ unsigned short port_address;
unsigned char interrupt: 4;
unsigned char dma_channel: 3;
- unsigned char ha_scsi_id: 3;
+ unsigned char bios_drive_number: 1;
unsigned char heads: 6;
unsigned char sectors: 6;
- unsigned char bios_drive_number: 1;
+ unsigned char ha_scsi_id: 3;
+ unsigned char subversion: 4;
};
/* MailBox SCSI Command Packet. Basic command structure for communicating
@@ -113,23 +121,35 @@ struct mscp {
Longword sense_data;
};
+/* The 14F uses an array of unaligned 4-byte ints for its scatter/gather list. */
+typedef struct {
+ Longword address;
+ Longword num_bytes;
+} ultrastor_sg_list;
+
+/* This is our semaphore for mscp block availability */
+int mscp_free = TRUE;
+
/* Allowed BIOS base addresses for 14f (NULL indicates reserved) */
-static const void *const bios_segment_table[8] = {
+static const void *const bios_segment_table_14f[8] = {
NULL, (void *)0xC4000, (void *)0xC8000, (void *)0xCC000,
(void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000,
};
/* Allowed IRQs for 14f */
-static const unsigned char interrupt_table[4] = { 15, 14, 11, 10 };
+static const unsigned char interrupt_table_14f[4] = { 15, 14, 11, 10 };
/* Allowed DMA channels for 14f (0 indicates reserved) */
-static const unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
+static const unsigned char dma_channel_table_14f[4] = { 5, 6, 7, 0 };
/* Head/sector mappings allowed by 14f */
static const struct {
unsigned char heads;
unsigned char sectors;
-} mapping_table[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 0, 0 } };
+} mapping_table_14f[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 0, 0 } };
+
+/* Subversions of the 14F */
+static const char *const subversion_names[] = { "14F", "34F" };
/* Config info */
static struct config config;
@@ -146,31 +166,23 @@ static int host_number;
static volatile int aborted = 0;
-/* A probe of address 0x310 screws up NE2000 cards */
-
#ifndef PORT_OVERRIDE
-static const unsigned short ultrastor_ports[] = {
- 0x330, 0x340, /* 0x310,*/ 0x230, 0x240, 0x210, 0x130, 0x140,
+/* ??? A probe of address 0x310 screws up NE2000 cards */
+static const unsigned short ultrastor_ports_14f[] = {
+ 0x330, 0x340, /*0x310,*/ 0x230, 0x240, 0x210, 0x130, 0x140,
};
#endif
static void ultrastor_interrupt(int cpl);
+static inline void build_sg_list(Scsi_Cmnd *SCpnt);
static void (*ultrastor_done)(Scsi_Cmnd *) = 0;
static Scsi_Cmnd *SCint = NULL;
-static const struct {
- const char *signature;
- size_t offset;
- size_t length;
-} signatures[] = {
- { "SBIOS 1.01 COPYRIGHT (C) UltraStor Corporation,1990-1992.", 0x10, 57 },
-};
-
-int ultrastor_14f_detect(int hostnum)
+int ultrastor_detect(int hostnum)
{
size_t i;
- unsigned char in_byte;
+ unsigned char in_byte, version_byte = 0;
struct config_1 config_1;
struct config_2 config_2;
@@ -180,8 +192,8 @@ int ultrastor_14f_detect(int hostnum)
#ifndef PORT_OVERRIDE
PORT_ADDRESS = 0;
- for (i = 0; i < ARRAY_SIZE(ultrastor_ports); i++) {
- PORT_ADDRESS = ultrastor_ports[i];
+ for (i = 0; i < ARRAY_SIZE(ultrastor_ports_14f); i++) {
+ PORT_ADDRESS = ultrastor_ports_14f[i];
#endif
#if (ULTRASTOR_DEBUG & UD_DETECT)
@@ -204,7 +216,7 @@ int ultrastor_14f_detect(int hostnum)
#endif
}
in_byte = inb(PRODUCT_ID(PORT_ADDRESS + 1));
- /* Only upper nibble is defined for Product ID 1 */
+ /* Only upper nibble is significant for Product ID 1 */
if ((in_byte & 0xF0) != US14F_PRODUCT_ID_1) {
#if (ULTRASTOR_DEBUG & UD_DETECT)
# ifdef PORT_OVERRIDE
@@ -219,10 +231,11 @@ int ultrastor_14f_detect(int hostnum)
continue;
#endif
}
+ version_byte = in_byte;
#ifndef PORT_OVERRIDE
break;
}
- if (i == ARRAY_SIZE(ultrastor_ports)) {
+ if (i == ARRAY_SIZE(ultrastor_ports_14f)) {
# if (ULTRASTOR_DEBUG & UD_DETECT)
printk("US14F: detect: no port address found!\n");
# endif
@@ -239,26 +252,18 @@ int ultrastor_14f_detect(int hostnum)
info. */
*(char *)&config_1 = inb(CONFIG(PORT_ADDRESS + 0));
*(char *)&config_2 = inb(CONFIG(PORT_ADDRESS + 1));
- config.bios_segment = bios_segment_table[config_1.bios_segment];
- config.interrupt = interrupt_table[config_1.interrupt];
- config.dma_channel = dma_channel_table[config_1.dma_channel];
+ config.bios_segment = bios_segment_table_14f[config_1.bios_segment];
+ config.interrupt = interrupt_table_14f[config_1.interrupt];
config.ha_scsi_id = config_2.ha_scsi_id;
- config.heads = mapping_table[config_2.mapping_mode].heads;
- config.sectors = mapping_table[config_2.mapping_mode].sectors;
+ config.heads = mapping_table_14f[config_2.mapping_mode].heads;
+ config.sectors = mapping_table_14f[config_2.mapping_mode].sectors;
config.bios_drive_number = config_2.bios_drive_number;
+ config.subversion = (version_byte & 0x0F);
+ if (config.subversion == U34F)
+ config.dma_channel = 0;
+ else
+ config.dma_channel = dma_channel_table_14f[config_1.dma_channel];
- /* To verify this card, we simply look for the UltraStor SCSI from the
- BIOS version notice. */
- if (config.bios_segment != NULL) {
- int found = 0;
-
- for (i = 0; !found && i < ARRAY_SIZE(signatures); i++)
- if (memcmp((char *)config.bios_segment + signatures[i].offset,
- signatures[i].signature, signatures[i].length))
- found = 1;
- if (!found)
- config.bios_segment = NULL;
- }
if (!config.bios_segment) {
#if (ULTRASTOR_DEBUG & UD_DETECT)
printk("US14F: detect: not detected.\n");
@@ -267,12 +272,13 @@ int ultrastor_14f_detect(int hostnum)
}
/* Final consistancy check, verify previous info. */
- if (!config.dma_channel || !(config_2.tfr_port & 0x2)) {
+ if (config.subversion != U34F)
+ if (!config.dma_channel || !(config_2.tfr_port & 0x2)) {
#if (ULTRASTOR_DEBUG & UD_DETECT)
- printk("US14F: detect: consistancy check failed\n");
+ printk("US14F: detect: consistancy check failed\n");
#endif
- return FALSE;
- }
+ return FALSE;
+ }
/* If we were TRULY paranoid, we could issue a host adapter inquiry
command here and verify the data returned. But frankly, I'm
@@ -285,42 +291,75 @@ int ultrastor_14f_detect(int hostnum)
" BIOS segment: %05X\n"
" Interrupt: %u\n"
" DMA channel: %u\n"
- " H/A SCSI ID: %u\n",
+ " H/A SCSI ID: %u\n"
+ " Subversion: %u\n",
PORT_ADDRESS, config.bios_segment, config.interrupt,
- config.dma_channel, config.ha_scsi_id);
+ config.dma_channel, config.ha_scsi_id, config.subversion);
#endif
host_number = hostnum;
scsi_hosts[hostnum].this_id = config.ha_scsi_id;
+ scsi_hosts[hostnum].unchecked_isa_dma = (config.subversion != U34F);
-#ifndef NO_QUEUEING
if (request_irq(config.interrupt, ultrastor_interrupt)) {
printk("Unable to allocate IRQ%u for UltraStor controller.\n",
config.interrupt);
return FALSE;
}
-#endif
- if (request_dma(config.dma_channel)) {
+ if (config.dma_channel && request_dma(config.dma_channel)) {
printk("Unable to allocate DMA channel %u for UltraStor controller.\n",
config.dma_channel);
-#ifndef NO_QUEUEING
free_irq(config.interrupt);
-#endif
return FALSE;
}
+ scsi_hosts[hostnum].sg_tablesize = ULTRASTOR_14F_MAX_SG;
+ printk("UltraStor: scatter/gather enabled. Using %d SG lists.\n", ULTRASTOR_14F_MAX_SG);
return TRUE;
}
-const char *ultrastor_14f_info(void)
+const char *ultrastor_info(void)
{
- return "UltraStor 14F SCSI driver version " VERSION;
+ static char buf[64];
+
+ (void)sprintf(buf, "UltraStor %s SCSI @ Port %03X BIOS %05X IRQ%u DMA%u\n",
+ ((config.subversion < ARRAY_SIZE(subversion_names))
+ ? subversion_names[config.subversion] : "14F?"),
+ PORT_ADDRESS, (int)config.bios_segment, config.interrupt,
+ config.dma_channel);
+ return buf;
}
static struct mscp mscp = {
OP_SCSI, DTD_SCSI, 0, 1, 0 /* This stuff doesn't change */
};
-int ultrastor_14f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+static inline void build_sg_list(Scsi_Cmnd *SCpnt)
+{
+ ultrastor_sg_list *sglist;
+ struct scatterlist *sl;
+ long transfer_length = 0;
+ int i;
+
+ sl = (struct scatterlist *) SCpnt->request_buffer;
+ SCpnt->host_scribble = scsi_malloc(512);
+ if (SCpnt->host_scribble == NULL)
+ /* Not sure what to do here; just panic for now */
+ panic("US14F: Can't allocate DMA buffer for scatter-gather list!\n");
+ /* Save ourselves some casts; can eliminate when we don't have to look at it anymore! */
+ sglist = (ultrastor_sg_list *) SCpnt->host_scribble;
+ for (i = 0; i < SCpnt->use_sg; i++) {
+ sglist[i].address = *(Longword *)&(sl[i].address);
+ sglist[i].num_bytes = *(Longword *)&(sl[i].length);
+ transfer_length += sl[i].length;
+ }
+ mscp.number_of_sg_list = (char) SCpnt->use_sg;
+ mscp.transfer_data = *(Longword *)&sglist;
+ /* ??? May not be necessary. Docs are unclear as to whether transfer length field is */
+ /* ignored or whether it should be set to the total number of bytes of the transfer. */
+ mscp.transfer_data_length = *(Longword *)&transfer_length;
+}
+
+int ultrastor_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
unsigned char in_byte;
@@ -328,15 +367,47 @@ int ultrastor_14f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
printk("US14F: queuecommand: called\n");
#endif
- /* Skip first (constant) byte */
- memset((char *)&mscp + 1, 0, sizeof (struct mscp) - 1);
+ /* We want to be sure that a command queued while another command */
+ /* is running doesn't overwrite the mscp block until the running */
+ /* command is finished. mscp_free is set in the interrupt handler. */
+ /* I'm not sure if the upper level driver will send another command */
+ /* with a command pending; this is just insurance. */
+ while (1) {
+ cli();
+ if (mscp_free) {
+ mscp_free = FALSE;
+ sti();
+ break;
+ }
+ sti();
+ }
+ mscp.opcode = OP_SCSI;
+ mscp.xdir = DTD_SCSI;
+ mscp.dcn = FALSE;
+ mscp.ca = TRUE;
mscp.target_id = SCpnt->target;
+ mscp.ch_no = 0;
mscp.lun = SCpnt->lun;
- mscp.transfer_data = *(Longword *)&SCpnt->request_buffer;
- mscp.transfer_data_length = *(Longword *)&SCpnt->request_bufflen;
- mscp.length_of_scsi_cdbs
- = ((*(unsigned char *)SCpnt->cmnd <= 0x1F) ? 6 : 10);
+ if (SCpnt->use_sg) {
+ /* Set scatter/gather flag in SCSI command packet */
+ mscp.sg = TRUE;
+ build_sg_list(SCpnt);
+ }
+ else {
+ /* Unset scatter/gather flag in SCSI command packet */
+ mscp.sg = FALSE;
+ mscp.transfer_data = *(Longword *)&SCpnt->request_buffer;
+ mscp.transfer_data_length = *(Longword *)&SCpnt->request_bufflen;
+ SCpnt->host_scribble = NULL;
+ }
+ memset(&mscp.command_link, 0, sizeof(mscp.command_link)); /*???*/
+ mscp.scsi_command_link_id = 0; /*???*/
+ mscp.length_of_sense_byte = 0; /*???*/
+ mscp.length_of_scsi_cdbs = ((SCpnt->cmnd[0] <= 0x1F) ? 6 : 10);
memcpy(mscp.scsi_cdbs, SCpnt->cmnd, mscp.length_of_scsi_cdbs);
+ mscp.adapter_status = 0;
+ mscp.target_status = 0;
+ memset(&mscp.sense_data, 0, sizeof(mscp.sense_data)); /*???*/
/* Find free OGM slot (OGMINT bit is 0) */
do
@@ -369,43 +440,7 @@ int ultrastor_14f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
return 0;
}
-#ifdef NO_QUEUEING
-int ultrastor_14f_command(Scsi_Cmnd SCpnt)
-{
- unsigned char in_byte;
-
-#if (ULTRASTOR_DEBUG & UD_COMMAND)
- printk("US14F: command: called\n");
-#endif
-
- (void)ultrastor_14f_queuecommand(SCpnt, NULL);
-
- /* Wait for ICM interrupt */
- do
- in_byte = inb_p(SYS_DOORBELL_INTR(PORT_ADDRESS));
- while (!aborted && !(in_byte & 1));
- if (aborted) {
-#if (ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT))
- printk("US14F: command: aborted\n");
-#endif
- /* ??? is this right? */
- return (aborted << 16);
- }
-
- /* Clean ICM slot (set ICMINT bit to 0) */
- outb_p(0x1, SYS_DOORBELL_INTR(PORT_ADDRESS));
-
-#if (ULTRASTOR_DEBUG & UD_COMMAND)
- printk("US14F: command: returning %08X\n",
- (mscp.adapter_status << 16) | mscp.target_status);
-#endif
-
- /* ??? not right, but okay for now? */
- return (mscp.adapter_status << 16) | mscp.target_status;
-}
-#endif
-
-int ultrastor_14f_abort(Scsi_Cmnd *SCpnt, int code)
+int ultrastor_abort(Scsi_Cmnd *SCpnt, int code)
{
#if (ULTRASTOR_DEBUG & UD_ABORT)
printk("US14F: abort: called\n");
@@ -413,6 +448,13 @@ int ultrastor_14f_abort(Scsi_Cmnd *SCpnt, int code)
aborted = (code ? code : DID_ABORT);
+ /* Free DMA buffer used for scatter/gather list */
+ if (SCpnt->host_scribble)
+ scsi_free(SCpnt->host_scribble, 512);
+
+ /* Free up mscp block for next command */
+ mscp_free = TRUE;
+
#if (ULTRASTOR_DEBUG & UD_ABORT)
printk("US14F: abort: returning\n");
#endif
@@ -420,11 +462,7 @@ int ultrastor_14f_abort(Scsi_Cmnd *SCpnt, int code)
return 0;
}
-/* Most of this is commented out because people were getting kernel crashes
- with it enabled. If you want to re-enable this, please figure out why
- the kernel was panicing. ERY */
-
-int ultrastor_14f_reset(void)
+int ultrastor_reset(void)
{
unsigned char in_byte;
@@ -432,6 +470,7 @@ int ultrastor_14f_reset(void)
printk("US14F: reset: called\n");
#endif
+ /* ??? SCSI bus reset causes problems on some systems. */
#if 0
/* Issue SCSI BUS reset */
outb_p(0x20, LCL_DOORBELL_INTR(PORT_ADDRESS));
@@ -443,13 +482,25 @@ int ultrastor_14f_reset(void)
aborted = DID_RESET;
#endif
+
#if (ULTRASTOR_DEBUG & UD_RESET)
printk("US14F: reset: returning\n");
#endif
return 0;
}
-#ifndef NO_QUEUEING
+int ultrastor_biosparam(int size, int dev, int *info)
+{
+ unsigned int s = config.heads * config.sectors;
+
+ info[0] = config.heads;
+ info[1] = config.sectors;
+ info[2] = (size + (s - 1)) / s;
+ if (info[2] > 1024)
+ info[2] = 1024;
+ return 0;
+}
+
static void ultrastor_interrupt(int cpl)
{
#if (ULTRASTOR_DEBUG & UD_INTERRUPT)
@@ -458,27 +509,36 @@ static void ultrastor_interrupt(int cpl)
#endif
if (ultrastor_done == 0)
- panic("US14F: interrupt: unexpected interrupt!\n");
+ panic("US14F: interrupt: unexpected interrupt");
else {
void (*done)(Scsi_Cmnd *);
+ Scsi_Cmnd *SCtmp;
/* Save ultrastor_done locally and zero before calling. This is needed
as once we call done, we may get another command queued before this
interrupt service routine can return. */
done = ultrastor_done;
ultrastor_done = 0;
+ SCtmp = SCint;
/* Clean ICM slot (set ICMINT bit to 0) */
outb_p(0x1, SYS_DOORBELL_INTR(PORT_ADDRESS));
/* Let the higher levels know that we're done */
/* ??? status is wrong here... */
- SCint->result = (mscp.adapter_status << 16) | mscp.target_status;
- done(SCint);
+ SCtmp->result = (mscp.adapter_status << 16) | mscp.target_status;
+
+ /* Free temp space used for scatter-gather list */
+ if (SCtmp->host_scribble)
+ scsi_free(SCtmp->host_scribble, 512);
+
+ /* Free up mscp block for next command */
+ mscp_free = TRUE;
+
+ done(SCtmp);
}
#if (ULTRASTOR_DEBUG & UD_INTERRUPT)
printk("US14F: interrupt: returning\n");
#endif
}
-#endif
diff --git a/kernel/blk_drv/scsi/ultrastor.h b/kernel/blk_drv/scsi/ultrastor.h
index 99b9cde..d306c74 100644
--- a/kernel/blk_drv/scsi/ultrastor.h
+++ b/kernel/blk_drv/scsi/ultrastor.h
@@ -3,6 +3,7 @@
* Low-level scsi driver for UltraStor 14F
* by David B. Gentzel, Whitfield Software Services, Carnegie, PA
* (gentzel@nova.enet.dec.com)
+ * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu)
* Thanks to UltraStor for providing the necessary documentation
*/
@@ -17,33 +18,24 @@
# define FALSE 0
#endif
-/* ??? This should go eventually, once I'm convinced the queueing stuff is
- stable enough... */
-/* #define NO_QUEUEING */
+int ultrastor_detect(int);
+const char *ultrastor_info(void);
+int ultrastor_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int ultrastor_abort(Scsi_Cmnd *, int);
+int ultrastor_reset(void);
+int ultrastor_biosparam(int, int, int *);
-int ultrastor_14f_detect(int);
-const char *ultrastor_14f_info(void);
-int ultrastor_14f_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-#ifdef NO_QUEUEING
-int ultrastor_14f_command(Scsi_Cmnd *);
-#endif
-int ultrastor_14f_abort(Scsi_Cmnd *, int);
-int ultrastor_14f_reset(void);
+#define ULTRASTOR_14F_MAX_CMDS 1 /*???*/
+#define ULTRASTOR_14F_MAX_SG 16 /* Should be 32, but 32 doesn't work */
+#define ULTRASTOR_14F_MAX_CMDS_PER_LUN 1 /*???*/
-#ifndef NO_QUEUEING
-#define ULTRASTOR_14F \
- { "UltraStor 14F", ultrastor_14f_detect, ultrastor_14f_info, 0, \
- ultrastor_14f_queuecommand, ultrastor_14f_abort, ultrastor_14f_reset, \
- NULL, NULL, 1, 0, SG_NONE, 1, 0, 1}
- /* ??? What should can_queue be set to? Currently 1... */
-/* Set it to the number of outstanding requests that the host adapter can keep
- track of at one time. ERY */
-#else
#define ULTRASTOR_14F \
- { "UltraStor 14F", ultrastor_14f_detect, ultrastor_14f_info, \
- ultrastor_14f_command, 0, ultrastor_14f_abort, ultrastor_14f_reset, \
- NULL, NULL, 0, 0, SG_NONE, 1, 0, 1}
-#endif
+ { "UltraStor 14F", ultrastor_detect, ultrastor_info, 0, \
+ ultrastor_queuecommand, ultrastor_abort, ultrastor_reset, \
+ 0, ultrastor_biosparam, ULTRASTOR_14F_MAX_CMDS, 0, \
+ ULTRASTOR_14F_MAX_SG, ULTRASTOR_14F_MAX_CMDS_PER_LUN, 0, 0 }
+
+#ifdef ULTRASTOR_PRIVATE
#define UD_ABORT 0x0001
#define UD_COMMAND 0x0002
@@ -51,8 +43,6 @@ int ultrastor_14f_reset(void);
#define UD_INTERRUPT 0x0008
#define UD_RESET 0x0010
-#ifdef ULTRASTOR_PRIVATE
-
/* #define PORT_OVERRIDE 0x330 */
/* Port addresses (relative to the base address) */
@@ -69,6 +59,10 @@ int ultrastor_14f_reset(void);
#define US14F_PRODUCT_ID_0 0x56
#define US14F_PRODUCT_ID_1 0x40 /* NOTE: Only upper nibble is used */
+/* Subversion values */
+#define U14F 0
+#define U34F 1
+
/* MSCP field values */
/* Opcode */
diff --git a/kernel/chr_drv/console.c b/kernel/chr_drv/console.c
index c722e84..a0aa4dd 100644
--- a/kernel/chr_drv/console.c
+++ b/kernel/chr_drv/console.c
@@ -1435,7 +1435,9 @@ int do_screendump(int arg)
char *sptr, *buf = (char *)arg;
int currcons, l;
- verify_area(buf,2+video_num_columns*video_num_lines);
+ l = verify_area(VERIFY_WRITE, buf,2+video_num_columns*video_num_lines);
+ if (l)
+ return l;
currcons = get_fs_byte(buf+1);
if ((currcons<0) || (currcons>=NR_CONSOLES))
return -EIO;
diff --git a/kernel/chr_drv/lp.c b/kernel/chr_drv/lp.c
index f7bcf21..eb8c00b 100644
--- a/kernel/chr_drv/lp.c
+++ b/kernel/chr_drv/lp.c
@@ -1,5 +1,10 @@
-/* Copyright (C) 1992 by Jim Weigand, Linus Torvalds, and Michael K. Johnson
-*/
+/*
+ * Copyright (C) 1992 by Jim Weigand and Linus Torvalds
+ * Copyright (C) 1992,1993 by Michael K. Johnson
+ * - Thanks much to Gunter Windau for pointing out to me where the error
+ * checking ought to be.
+ * Copyright (C) 1993 by Nigel Gamble (added interrupt code)
+ */
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -8,6 +13,7 @@
#include <asm/io.h>
#include <asm/segment.h>
+#include <asm/system.h>
/*
* All my debugging code assumes that you debug with only one printer at
@@ -19,12 +25,15 @@
static int lp_reset(int minor)
{
int testvalue;
+ unsigned char command;
+
+ command = LP_PSELECP | LP_PINITP;
/* reset value */
outb_p(0, LP_C(minor));
for (testvalue = 0 ; testvalue < LP_DELAY ; testvalue++)
;
- outb_p(LP_PSELECP | LP_PINITP, LP_C(minor));
+ outb_p(command, LP_C(minor));
return LP_S(minor);
}
@@ -32,18 +41,18 @@ static int lp_reset(int minor)
static int lp_max_count = 1;
#endif
-static int lp_char(char lpchar, int minor)
+static int lp_char_polled(char lpchar, int minor)
{
- int retval = 0, wait = 0;
+ int status = 0, wait = 0;
unsigned long count = 0;
outb_p(lpchar, LP_B(minor));
do {
- retval = LP_S(minor);
+ status = LP_S(minor);
count ++;
if(need_resched)
schedule();
- } while(!(retval & LP_PBUSY) && count < LP_CHAR(minor));
+ } while(!(status & LP_PBUSY) && count < LP_CHAR(minor));
if (count == LP_CHAR(minor)) {
return 0;
@@ -63,8 +72,33 @@ static int lp_char(char lpchar, int minor)
while(wait) wait--;
/* take strobe low */
outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
- /* get something meaningful for return value */
- return LP_S(minor);
+
+ return 1;
+}
+
+static int lp_char_interrupt(char lpchar, int minor)
+{
+ int wait = 0;
+ unsigned char status;
+
+ outb_p(lpchar, LP_B(minor));
+
+ if (!((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
+ || !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)
+ || !((status = LP_S(minor)) & LP_PACK) || (status & LP_PBUSY)) {
+
+ /* must wait before taking strobe high, and after taking strobe
+ low, according spec. Some printers need it, others don't. */
+ while(wait != LP_WAIT(minor)) wait++;
+ /* control port takes strobe high */
+ outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
+ while(wait) wait--;
+ /* take strobe low */
+ outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
+ return 1;
+ }
+
+ return 0;
}
#ifdef LP_DEBUG
@@ -72,7 +106,88 @@ static int lp_char(char lpchar, int minor)
unsigned int lp_last_call = 0;
#endif
-static int lp_write(struct inode * inode, struct file * file, char * buf, int count)
+static void lp_interrupt(int irq)
+{
+ struct lp_struct *lp = &lp_table[0];
+ struct lp_struct *lp_end = &lp_table[LP_NO];
+
+ while (irq != lp->irq) {
+ if (++lp >= lp_end)
+ return;
+ }
+
+ wake_up(&lp->lp_wait_q);
+}
+
+static int lp_write_interrupt(struct inode * inode, struct file * file, char * buf, int count)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ unsigned long copy_size;
+ unsigned long total_bytes_written = 0;
+ unsigned long bytes_written;
+ struct lp_struct *lp = &lp_table[minor];
+ unsigned char status;
+
+ do {
+ bytes_written = 0;
+ copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
+ memcpy_fromfs(lp->lp_buffer, buf, copy_size);
+
+ while (copy_size) {
+ if (lp_char_interrupt(lp->lp_buffer[bytes_written], minor)) {
+ --copy_size;
+ ++bytes_written;
+ } else {
+ if (!((status = LP_S(minor)) & LP_PERRORP)) {
+ int rc = total_bytes_written + bytes_written;
+
+ if ((status & LP_POUTPA)) {
+ printk("lp%d out of paper\n", minor);
+ if (!rc)
+ rc = -ENOSPC;
+ } else if (!(status & LP_PSELECD)) {
+ printk("lp%d off-line\n", minor);
+ if (!rc)
+ rc = -EIO;
+ } else {
+ printk("lp%d printer error\n", minor);
+ if (!rc)
+ rc = -EIO;
+ }
+ if(LP_F(minor) & LP_ABORT)
+ return rc;
+ }
+ cli();
+ outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
+ status = LP_S(minor);
+ if (!(status & LP_PACK) || (status & LP_PBUSY)) {
+ outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
+ sti();
+ continue;
+ }
+ current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
+ interruptible_sleep_on(&lp->lp_wait_q);
+ outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
+ if (current->signal & ~current->blocked) {
+ if (total_bytes_written + bytes_written)
+ return total_bytes_written + bytes_written;
+ else
+ return -EINTR;
+ }
+ }
+ }
+
+ total_bytes_written += bytes_written;
+ buf += bytes_written;
+ count -= bytes_written;
+
+ } while (count > 0);
+
+ return total_bytes_written;
+}
+
+static int lp_write_polled(struct inode * inode, struct file * file,
+ char * buf, int count)
{
int retval;
unsigned int minor = MINOR(inode->i_rdev);
@@ -89,7 +204,7 @@ static int lp_write(struct inode * inode, struct file * file, char * buf, int co
temp = buf;
while (count > 0) {
c = get_fs_byte(temp);
- retval = lp_char(c, minor);
+ retval = lp_char_polled(c, minor);
/* only update counting vars if character was printed */
if (retval) { count--; temp++;
#ifdef LP_DEBUG
@@ -97,62 +212,61 @@ static int lp_write(struct inode * inode, struct file * file, char * buf, int co
#endif
}
if (!retval) { /* if printer timed out */
- /* check for signals before going to sleep */
- if (current->signal & ~current->blocked) {
- if (count > 0) return -EINTR;
- }
-#ifdef LP_DEBUG
- printk("lp sleeping at %d characters for %d jiffies\n",
- lp_total_chars, LP_TIME(minor));
- lp_total_chars=0;
-#endif
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + LP_TIME(minor);
- schedule();
+ int status = LP_S(minor);
- /* If nothing is getting to the printer
- for a considerable length of time,
- someone oughtta know. */
- if (!(LP_S(minor) & LP_BUSY)) {
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + LP_TIMEOUT;
- schedule();
- if (!(LP_S(minor) & LP_BUSY))
- printk("lp%d timeout\n", minor);
- }
- } else {
- if (retval & LP_POUTPA) {
+ if (status & LP_POUTPA) {
printk("lp%d out of paper\n", minor);
- if(LP_F(minor) && LP_ABORT)
+ if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-ENOSPC;
current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + LP_TIMEOUT;
+ current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
} else
-
- if (!(retval & LP_PSELECD)) {
+ if (!(status & LP_PSELECD)) {
printk("lp%d off-line\n", minor);
- if(LP_F(minor) && LP_ABORT)
+ if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-EIO;
current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + LP_TIMEOUT;
+ current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
} else
-
/* not offline or out of paper. on fire? */
- if (!(retval & LP_PERRORP)) {
+ if (!(status & LP_PERRORP)) {
printk("lp%d on fire\n", minor);
- if(LP_F(minor) && LP_ABORT)
+ if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-EFAULT;
current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + LP_TIMEOUT;
+ current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
}
+
+ /* check for signals before going to sleep */
+ if (current->signal & ~current->blocked) {
+ if (temp != buf)
+ return temp-buf;
+ else
+ return -EINTR;
+ }
+#ifdef LP_DEBUG
+ printk("lp sleeping at %d characters for %d jiffies\n",
+ lp_total_chars, LP_TIME(minor));
+ lp_total_chars=0;
+#endif
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + LP_TIME(minor);
+ schedule();
}
}
return temp-buf;
}
+static int lp_write(struct inode * inode, struct file * file, char * buf, int count)
+{
+ if (LP_IRQ(MINOR(inode->i_rdev)))
+ return lp_write_interrupt(inode, file, buf, count);
+ else
+ return lp_write_polled(inode, file, buf, count);
+}
static int lp_lseek(struct inode * inode, struct file * file,
off_t offset, int origin)
@@ -163,6 +277,9 @@ static int lp_lseek(struct inode * inode, struct file * file,
static int lp_open(struct inode * inode, struct file * file)
{
unsigned int minor = MINOR(inode->i_rdev);
+ int ret;
+ unsigned int irq;
+ struct sigaction sa;
if (minor >= LP_NO)
return -ENODEV;
@@ -170,13 +287,37 @@ static int lp_open(struct inode * inode, struct file * file)
return -ENODEV;
if (LP_F(minor) & LP_BUSY)
return -EBUSY;
+
+ if ((irq = LP_IRQ(minor))) {
+ if (!(lp_table[minor].lp_buffer = kmalloc(LP_BUFFER_SIZE,
+ GFP_KERNEL)))
+ return -ENOMEM;
+
+ sa.sa_handler = lp_interrupt;
+ sa.sa_flags = SA_INTERRUPT;
+ sa.sa_mask = 0;
+ sa.sa_restorer = NULL;
+ ret = irqaction(irq, &sa);
+ if (ret) {
+ printk("lp%d unable to use interrupt %d, error %d\n", irq, ret);
+ return ret;
+ }
+ }
+
LP_F(minor) |= LP_BUSY;
+
return 0;
}
static void lp_release(struct inode * inode, struct file * file)
{
unsigned int minor = MINOR(inode->i_rdev);
+ unsigned int irq;
+
+ if ((irq = LP_IRQ(minor))) {
+ free_irq(irq);
+ kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
+ }
LP_F(minor) &= ~LP_BUSY;
}
@@ -209,6 +350,37 @@ static int lp_ioctl(struct inode *inode, struct file *file,
case LPWAIT:
LP_WAIT(minor) = arg;
break;
+ case LPSETIRQ: {
+ int ret;
+ int oldirq;
+ struct sigaction sa;
+
+ if (!suser())
+ return -EPERM;
+
+ if ((oldirq = LP_IRQ(minor))) {
+ free_irq(oldirq);
+ }
+ if (arg) {
+ /* 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)
+ /* restore old irq */
+ irqaction(oldirq, &sa);
+ return ret;
+ }
+ }
+ LP_IRQ(minor) = arg;
+ lp_reset(minor);
+ break;
+ }
+ case LPGETIRQ:
+ arg = LP_IRQ(minor);
+ break;
default: arg = -EINVAL;
}
return arg;
@@ -245,7 +417,11 @@ long lp_init(long kmem_start)
if (testvalue != 255) {
LP_F(offset) |= LP_EXIST;
lp_reset(offset);
- printk("lp_init: lp%d exists (%d)\n", offset, testvalue);
+ printk("lp_init: lp%d exists (%d), ", offset, testvalue);
+ if (LP_IRQ(offset))
+ printk("using IRQ%d\n", LP_IRQ(offset));
+ else
+ printk("using polling driver\n");
count++;
}
}
diff --git a/kernel/chr_drv/mem.c b/kernel/chr_drv/mem.c
index 186e830..3212305 100644
--- a/kernel/chr_drv/mem.c
+++ b/kernel/chr_drv/mem.c
@@ -124,6 +124,18 @@ static int write_mem(struct inode * inode, struct file * file,char * buf, int co
return count;
}
+static int mmap_mem(struct inode * inode, struct file * file,
+ unsigned long addr, size_t len, int prot, unsigned long off)
+{
+ if (len > high_memory || off > high_memory - len) /* avoid overflow */
+ return -ENXIO;
+
+ if (remap_page_range(addr, off, len, prot))
+ return -EAGAIN;
+
+ return 0;
+}
+
static int read_port(struct inode * inode,struct file * file,char * buf, int count)
{
unsigned int i = file->f_pos;
@@ -178,6 +190,16 @@ static int write_zero(struct inode * inode,struct file * file,char * buf, int co
return count;
}
+static int mmap_zero(struct inode * inode, struct file * file,
+ unsigned long addr, size_t len, int prot, unsigned long off)
+{
+ if (prot & PAGE_RW)
+ return -EINVAL;
+ if (zeromap_page_range(addr, len, prot))
+ return -EAGAIN;
+ return 0;
+}
+
/*
* The memory devices use the full 32 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
@@ -205,6 +227,7 @@ static int memory_lseek(struct inode * inode, struct file * file, off_t offset,
#define read_kmem read_mem
#define write_kmem write_mem
+#define mmap_kmem mmap_mem
static struct file_operations ram_fops = {
memory_lseek,
@@ -225,7 +248,7 @@ static struct file_operations mem_fops = {
NULL, /* mem_readdir */
NULL, /* mem_select */
NULL, /* mem_ioctl */
- NULL, /* mem_mmap */
+ mmap_mem,
NULL, /* no special open code */
NULL /* no special release code */
};
@@ -237,7 +260,7 @@ static struct file_operations kmem_fops = {
NULL, /* kmem_readdir */
NULL, /* kmem_select */
NULL, /* kmem_ioctl */
- NULL, /* kmem_mmap */
+ mmap_kmem,
NULL, /* no special open code */
NULL /* no special release code */
};
@@ -273,7 +296,7 @@ static struct file_operations zero_fops = {
NULL, /* zero_readdir */
NULL, /* zero_select */
NULL, /* zero_ioctl */
- NULL, /* zero_mmap */
+ mmap_zero,
NULL, /* no special open code */
NULL /* no special release code */
};
diff --git a/kernel/chr_drv/psaux.c b/kernel/chr_drv/psaux.c
index 3dc5a26..7cd33c1 100644
--- a/kernel/chr_drv/psaux.c
+++ b/kernel/chr_drv/psaux.c
@@ -117,7 +117,7 @@ static void aux_write_cmd(int val)
}
-static unsigned int get_from_queue()
+static unsigned int get_from_queue(void)
{
unsigned int result;
unsigned long flags;
@@ -130,7 +130,7 @@ static unsigned int get_from_queue()
}
-static inline int queue_empty()
+static inline int queue_empty(void)
{
return queue->head == queue->tail;
}
diff --git a/kernel/chr_drv/pty.c b/kernel/chr_drv/pty.c
index dc071ab..9316d5e 100644
--- a/kernel/chr_drv/pty.c
+++ b/kernel/chr_drv/pty.c
@@ -29,8 +29,11 @@ static void pty_close(struct tty_struct * tty, struct file * filp)
if (!tty->link)
return;
wake_up_interruptible(&tty->link->write_q.proc_list);
- if (IS_A_PTY_MASTER(tty->line))
+ if (IS_A_PTY_MASTER(tty->line)) {
tty_hangup(tty->link);
+ flush_input(tty);
+ flush_output(tty);
+ }
}
static inline void pty_copy(struct tty_struct * from, struct tty_struct * to)
diff --git a/kernel/chr_drv/serial.c b/kernel/chr_drv/serial.c
index c6af7c6..b5926d7 100644
--- a/kernel/chr_drv/serial.c
+++ b/kernel/chr_drv/serial.c
@@ -27,6 +27,7 @@
#include <linux/config.h>
#include <linux/string.h>
#include <linux/fcntl.h>
+#include <linux/ptrace.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -56,19 +57,17 @@
/*
* rs_event - Bitfield of serial lines that events pending
* to be processed at the next clock tick.
- * rs_write_active - Bitfield of serial lines that are actively
- * transmitting (and therefore have a
- * write timeout pending, in case the
- * THRE interrupt gets lost.)
*
* We assume here that int's are 32 bits, so an array of two gives us
* 64 lines, which is the maximum we can support.
*/
static int rs_event[2];
-static int rs_write_active[2];
static struct async_struct *IRQ_ports[16];
-
+static int IRQ_active;
+static unsigned long IRQ_timer[16];
+static int IRQ_timeout[16];
+
/*
* This assumes you have a 1.8432 MHz clock for your UART.
*
@@ -142,12 +141,7 @@ static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200, 0 };
-static void startup(struct async_struct * info);
-static void shutdown(struct async_struct * info);
static void rs_throttle(struct tty_struct * tty, int status);
-static void restart_port(struct async_struct *info);
-static int block_til_ready(struct tty_struct *tty, struct file * filp,
- struct async_struct *info);
static inline unsigned int serial_in(struct async_struct *info, int offset)
{
@@ -170,17 +164,31 @@ static inline void serial_outp(struct async_struct *info, int offset,
outb_p(value, info->port+offset);
}
-static void send_break( struct async_struct * info)
-{
- if (!info->port)
- return;
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + 25;
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
- schedule();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
-}
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines. All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt(). They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off. People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible. After you are done making modifications, it is not a bad
+ * idea to do:
+ *
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
static inline void rs_sched_event(struct async_struct *info,
int event)
{
@@ -189,160 +197,172 @@ static inline void rs_sched_event(struct async_struct *info,
mark_bh(SERIAL_BH);
}
+static inline void receive_chars(struct async_struct *info,
+ int *status)
+{
+ struct tty_queue * queue;
+ int head, tail, ch;
+
+/*
+ * Just like the LEFT(x) macro, except it uses the loal tail
+ * and head variables.
+ */
+#define VLEFT ((tail-head-1)&(TTY_BUF_SIZE-1))
+
+ queue = &info->tty->read_q;
+ head = queue->head;
+ tail = queue->tail;
+ do {
+ ch = serial_in(info, UART_RX);
+ /*
+ * There must be at least 2 characters
+ * free in the queue; otherwise we punt.
+ */
+ if (VLEFT < 2)
+ continue;
+ if (*status & info->read_status_mask) {
+ set_bit(head, &info->tty->readq_flags);
+ if (*status & (UART_LSR_BI)) {
+ queue->buf[head++]= TTY_BREAK;
+ rs_sched_event(info, RS_EVENT_BREAK);
+ } else if (*status & UART_LSR_PE)
+ queue->buf[head++]= TTY_PARITY;
+ else if (*status & UART_LSR_FE)
+ queue->buf[head++]= TTY_FRAME;
+ head &= TTY_BUF_SIZE-1;
+ }
+ queue->buf[head++] = ch;
+ head &= TTY_BUF_SIZE-1;
+ } while ((*status = serial_inp(info, UART_LSR)) & UART_LSR_DR);
+ queue->head = head;
+ if ((VLEFT < RQ_THRESHOLD_LW) && !set_bit(TTY_RQ_THROTTLED,
+ &info->tty->flags))
+ rs_throttle(info->tty, TTY_THROTTLE_RQ_FULL);
+ rs_sched_event(info, RS_EVENT_READ_PROCESS);
+}
+
+static inline void transmit_chars(struct async_struct *info, int *done_work)
+{
+ struct tty_queue * queue;
+ int head, tail, count;
+
+ queue = &info->tty->write_q;
+ head = queue->head;
+ tail = queue->tail;
+ if (head==tail && !info->x_char)
+ return;
+ count = info->xmit_fifo_size;
+ if (info->x_char) {
+ serial_outp(info, UART_TX, info->x_char);
+ info->x_char = 0;
+ count--;
+ }
+ while (count-- && (tail != head)) {
+ serial_outp(info, UART_TX, queue->buf[tail++]);
+ tail &= TTY_BUF_SIZE-1;
+ }
+ queue->tail = tail;
+ if (VLEFT > WAKEUP_CHARS) {
+ rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+ if (info->tty->write_data_cnt) {
+ set_bit(info->tty->line, &tty_check_write);
+ mark_bh(TTY_BH);
+ }
+ }
+#ifdef SERIAL_INT_DEBUG
+ printk("THRE...");
+#endif
+ (*done_work)++;
+}
+
+static inline void check_modem_status(struct async_struct *info,
+ int *done_work)
+{
+ int status;
+
+ status = serial_in(info, UART_MSR);
+
+ if ((status & UART_MSR_DDCD) && !C_LOCAL(info->tty)) {
+ if (status & UART_MSR_DCD)
+ rs_sched_event(info, RS_EVENT_OPEN_WAKEUP);
+ else
+ rs_sched_event(info, RS_EVENT_HANGUP);
+ }
+ if (C_RTSCTS(info->tty)) {
+ if (info->tty->stopped) {
+ if (status & UART_MSR_CTS) {
+ info->tty->stopped = 0;
+ done_work++;
+ }
+ } else
+ info->tty->stopped = !(status & UART_MSR_CTS);
+ }
+}
+
+static inline void figure_RS_timer(void)
+{
+ int timeout = 6000; /* 60 seconds; really big :-) */
+ int i, mask;
+
+ if (!IRQ_active)
+ return;
+ for (i=0, mask = 1; mask <= IRQ_active; i++, mask <<= 1) {
+ if (!(mask & IRQ_active))
+ continue;
+ if (IRQ_timer[i] < timeout)
+ timeout = IRQ_timer[i];
+ }
+ timer_table[RS_TIMER].expires = timeout;
+ timer_active |= 1 << RS_TIMER;
+}
+
/*
* This is the serial driver's generic interrupt routine
*/
static void rs_interrupt(int irq)
{
- unsigned char status;
+ int status;
struct async_struct * info;
- struct tty_queue * queue;
- int head, tail, count, ch;
- int done;
-
- /*
- * Just like the LEFT(x) macro, except it uses the loal tail
- * and head variables.
- */
-#define VLEFT ((tail-head-1)&(TTY_BUF_SIZE-1))
-#define IFLAG (info->tty->termios->c_iflag)
-#define CFLAG (info->tty->termios->c_cflag)
+ int done, done_work, pass_number;
info = IRQ_ports[irq];
done = 1;
+ done_work = 0;
+ pass_number = 0;
while (info) {
-#ifdef SERIAL_INT_DEBUG
- printk("rsint(%d)...", info->line);
-#endif
- if (serial_inp(info, UART_IIR) & UART_IIR_NO_INT)
- goto next_loop;
- done = 0;
+ 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) {
-#ifdef SERIAL_INT_DEBUG
- printk("DR...");
-#endif
- queue = &info->tty->read_q;
- head = queue->head;
- tail = queue->tail;
- do {
- ch = serial_in(info, UART_RX);
- /*
- * There must be at least 3 characters
- * free in the queue; otherwise we punt.
- */
- if (VLEFT < 3)
- continue;
- if (status & (UART_LSR_BI |
- UART_LSR_FE |
- UART_LSR_PE)) {
- if (status & (UART_LSR_BI)) {
- if (info->flags & ASYNC_SAK)
- rs_sched_event(info, RS_EVENT_DO_SAK);
- else if (IFLAG & IGNBRK)
- continue;
- else if (IFLAG & BRKINT)
- rs_sched_event(info, RS_EVENT_BREAK_INT);
- else
- ch = 0;
- } else if (IFLAG & IGNPAR)
- continue;
- if (IFLAG & PARMRK) {
- queue->buf[head++] = 0xff;
- head &= TTY_BUF_SIZE-1;
- queue->buf[head++] = 0;
- head &= TTY_BUF_SIZE-1;
- } else
- ch = 0;
- } else if ((IFLAG & PARMRK) && (ch == 0xff)) {
- queue->buf[head++] = 0xff;
- head &= TTY_BUF_SIZE-1;
- }
- queue->buf[head++] = ch;
- head &= TTY_BUF_SIZE-1;
- } while ((status = serial_inp(info, UART_LSR)) &
- UART_LSR_DR);
- queue->head = head;
- if ((VLEFT < RQ_THRESHOLD_LW)
- && !set_bit(TTY_RQ_THROTTLED, &info->tty->flags))
- rs_throttle(info->tty, TTY_THROTTLE_RQ_FULL);
- rs_sched_event(info, RS_EVENT_READ_PROCESS);
- }
- if ((status & UART_LSR_THRE) &&
- !info->tty->stopped) {
- queue = &info->tty->write_q;
- head = queue->head;
- tail = queue->tail;
- if (head==tail && !info->x_char)
- goto no_xmit;
- if (info->x_char) {
- serial_outp(info, UART_TX, info->x_char);
- info->x_char = 0;
- } else {
- count = info->xmit_fifo_size;
- while (count--) {
- if (tail == head)
- break;
- serial_outp(info, UART_TX,
- queue->buf[tail++]);
- tail &= TTY_BUF_SIZE-1;
- }
+ status = serial_inp(info, UART_LSR);
+ if (status & UART_LSR_DR) {
+ receive_chars(info, &status);
+ done_work++;
}
- queue->tail = tail;
- if (VLEFT > WAKEUP_CHARS) {
- rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
- if (info->tty->write_data_cnt) {
- set_bit(info->tty->line,
- &tty_check_write);
- mark_bh(TTY_BH);
- }
+ if ((status & UART_LSR_THRE) &&
+ !info->tty->stopped) {
+ transmit_chars(info, &done_work);
}
- info->timer = jiffies + info->timeout;
- if (info->timer < timer_table[RS_TIMER].expires)
- timer_table[RS_TIMER].expires = info->timer;
- set_bit(info->line, rs_write_active);
- timer_active |= 1 << RS_TIMER;
-#ifdef SERIAL_INT_DEBUG
- printk("THRE...");
-#endif
+ check_modem_status(info, &done_work);
}
- no_xmit:
- status = serial_in(info, UART_MSR);
- if (!(CFLAG & CLOCAL) && (status & UART_MSR_DDCD)) {
- if (status & UART_MSR_DCD) {
-#ifdef SERIAL_INT_DEBUG
- printk("DCD on...");
-#endif
- rs_sched_event(info, RS_EVENT_OPEN_WAKEUP);
- } else {
-#ifdef SERIAL_INT_DEBUG
- printk("DCD off...");
-#endif
- rs_sched_event(info, RS_EVENT_HUP_PGRP);
- }
- }
- if (CFLAG & CRTSCTS) {
- if (info->tty->stopped) {
- if (status & UART_MSR_CTS) {
- info->tty->stopped = 0;
- restart_port(info);
- }
- } else
- info->tty->stopped = !(status & UART_MSR_CTS);
- }
- next_loop:
info = info->next_port;
if (!info && !done) {
-#ifdef SERIAL_INT_DEBUG
- printk("repeating...");
-#endif
info = IRQ_ports[irq];
done = 1;
+ if (pass_number++ > 64)
+ break; /* Prevent infinite loops */
}
}
+ if (IRQ_ports[irq]) {
+ if (irq && !done_work)
+ IRQ_timer[irq] = jiffies + 1500;
+ else
+ IRQ_timer[irq] = jiffies + IRQ_timeout[irq];
+ IRQ_active |= 1 << irq;
+ }
+ figure_RS_timer();
}
#ifdef CONFIG_AUTO_IRQ
@@ -361,7 +381,39 @@ static void rs_probe(int irq)
}
#endif
-static void do_softint()
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is called when we receive a break on a serial line.
+ * It is executed out of the software interrupt routine.
+ */
+static inline void handle_rs_break(struct async_struct *info)
+{
+ if (info->flags & ASYNC_SAK)
+ do_SAK(info->tty);
+
+ if (I_BRKINT(info->tty)) {
+ flush_input(info->tty);
+ flush_output(info->tty);
+ if (info->tty->pgrp > 0)
+ kill_pg(info->tty->pgrp, SIGINT,1);
+ }
+}
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_softint(void *unused)
{
int i;
struct async_struct *info;
@@ -376,17 +428,14 @@ static void do_softint()
if (!clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
wake_up_interruptible(&info->tty->write_q.proc_list);
}
- if (!clear_bit(RS_EVENT_HUP_PGRP, &info->event))
+ if (!clear_bit(RS_EVENT_HANGUP, &info->event)) {
tty_hangup(info->tty);
- if (!clear_bit(RS_EVENT_BREAK_INT, &info->event)) {
- flush_input(info->tty);
- flush_output(info->tty);
- if (info->tty->pgrp > 0)
- kill_pg(info->tty->pgrp, SIGINT,1);
- }
- if (!clear_bit(RS_EVENT_DO_SAK, &info->event)) {
- do_SAK(info->tty);
+ wake_up_interruptible(&info->open_wait);
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|
+ ASYNC_CALLOUT_ACTIVE);
}
+ if (!clear_bit(RS_EVENT_BREAK, &info->event))
+ handle_rs_break(info);
if (!clear_bit(RS_EVENT_OPEN_WAKEUP, &info->event)) {
wake_up_interruptible(&info->open_wait);
}
@@ -395,154 +444,70 @@ static void do_softint()
}
/*
- * This subroutine handles all of the timer functionality required for
- * the serial ports.
+ * This subroutine is called when the RS_TIMER goes off. It is used
+ * by the serial driver to run the rs_interrupt routine at certain
+ * intervals, either because a serial interrupt might have been lost,
+ * or because (in the case of IRQ=0) the serial port does not have an
+ * interrupt, and is being checked only via the timer interrupts.
*/
-
static void rs_timer(void)
{
- int i;
- struct async_struct *info;
-
- for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
- if (test_bit(i, rs_write_active) && (info->timer <= jiffies)) {
- clear_bit(i, rs_write_active);
- rs_write(info->tty);
+ int i, mask;
+ int timeout = 0;
+
+ for (i = 0, mask = 1; mask <= IRQ_active; i++, mask <<= 1) {
+ if ((mask & IRQ_active) && (IRQ_timer[i] <= jiffies)) {
+ IRQ_active &= ~mask;
+ if (i) {
+ cli();
+ rs_interrupt(i);
+ sti();
+ } else
+ rs_interrupt(i);
}
- }
-}
-
-/*
- * Note: this subroutine must be called with the interrupts *off*
- */
-static void restart_port(struct async_struct *info)
-{
- struct tty_queue * queue;
- int head, tail, count;
-
- if (serial_inp(info, UART_LSR) & UART_LSR_THRE) {
- if (info->x_char) {
- serial_outp(info, UART_TX, info->x_char);
- info->x_char = 0;
- } else {
- queue = &info->tty->write_q;
- head = queue->head;
- tail = queue->tail;
- count = info->xmit_fifo_size;
- while (count--) {
- if (tail == head)
- break;
- serial_outp(info, UART_TX, queue->buf[tail++]);
- tail &= TTY_BUF_SIZE-1;
- }
- queue->tail = tail;
+ if (mask & IRQ_active) {
+ if (!timeout || (IRQ_timer[i] < timeout))
+ timeout = IRQ_timer[i];
}
}
-}
+ if (timeout) {
+ timer_table[RS_TIMER].expires = timeout;
+ timer_active |= 1 << RS_TIMER;
+ }
+}
/*
- * This routine gets called when tty_write has put something into
- * the write_queue.
+ * ---------------------------------------------------------------
+ * Low level utility subroutines for the serial driver: routines to
+ * figure out the appropriate timeout for an interrupt chain, routines
+ * to initialize and startup a serial port, and routines to shutdown a
+ * serial port. Useful stuff like that.
+ * ---------------------------------------------------------------
*/
-void rs_write(struct tty_struct * tty)
-{
- struct async_struct *info;
-
- if (!tty || tty->stopped)
- return;
- info = rs_table + DEV_TO_SL(tty->line);
- cli();
- restart_port(info);
- sti();
-}
-
-static void rs_throttle(struct tty_struct * tty, int status)
-{
- struct async_struct *info;
- unsigned char mcr;
-
-#ifdef notdef
- printk("throttle tty%d: %d (%d, %d)....\n", DEV_TO_SL(tty->line),
- status, LEFT(&tty->read_q), LEFT(&tty->secondary));
-#endif
- switch (status) {
- case TTY_THROTTLE_RQ_FULL:
- info = rs_table + DEV_TO_SL(tty->line);
- if (tty->termios->c_iflag & IXOFF) {
- info->x_char = STOP_CHAR(tty);
- } else {
- mcr = serial_inp(info, UART_MCR);
- mcr &= ~UART_MCR_RTS;
- serial_out(info, UART_MCR, mcr);
- }
- break;
- case TTY_THROTTLE_RQ_AVAIL:
- info = rs_table + DEV_TO_SL(tty->line);
- if (tty->termios->c_iflag & IXOFF) {
- cli();
- if (info->x_char)
- info->x_char = 0;
- else
- info->x_char = START_CHAR(tty);
- sti();
- } else {
- mcr = serial_in(info, UART_MCR);
- mcr |= UART_MCR_RTS;
- serial_out(info, UART_MCR, mcr);
- }
- break;
- }
-}
/*
- * This routine is called when the serial port gets closed. First, we
- * wait for the last remaining data to be sent. Then, we unlink its
- * async structure from the interrupt chain if necessary, and we free
- * that IRQ if nothing is left in the chain.
+ * This routine figures out the correct timeout for a particular IRQ.
+ * It uses the smallest timeout of all of the serial ports in a
+ * particular interrupt chain.
*/
-static void rs_close(struct tty_struct *tty, struct file * filp)
+static void figure_IRQ_timeout(int irq)
{
- struct async_struct * info;
- int irq, line;
+ struct async_struct *info;
+ int timeout = 6000; /* 60 seconds === a long time :-) */
- line = DEV_TO_SL(tty->line);
- if ((line < 0) || (line >= NR_PORTS))
- return;
- info = rs_table + line;
-#ifdef SERIAL_DEBUG_OPEN
- printk("rs_close ttys%d, count = %d\n", info->line, info->count);
-#endif
- if (--info->count > 0)
- return;
- tty->stopped = 0; /* Force flush to succeed */
- wait_until_sent(tty);
- clear_bit(line, rs_write_active);
- clear_bit(line, rs_event);
- info->event = 0;
- info->count = 0;
- info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
- if (info->blocked_open) {
- shutdown(info);
- startup(info);
+ info = IRQ_ports[irq];
+ if (!info) {
+ IRQ_timeout[irq] = 0;
return;
}
- if (info->flags & ASYNC_INITIALIZED) {
- shutdown(info);
- irq = info->irq;
- if (irq == 2)
- irq = 9;
- if (irq) {
- if (info->next_port)
- info->next_port->prev_port = info->prev_port;
- if (info->prev_port)
- info->prev_port->next_port = info->next_port;
- else
- IRQ_ports[irq] = info->next_port;
- if (!IRQ_ports[irq])
- free_irq(irq);
- }
+ while (info) {
+ if (info->timeout < timeout)
+ timeout = info->timeout;
+ info = info->next_port;
}
- info->tty = 0;
+ if (!irq)
+ timeout = timeout / 2;
+ IRQ_timeout[irq] = timeout;
}
static void startup(struct async_struct * info)
@@ -550,7 +515,7 @@ static void startup(struct async_struct * info)
unsigned short ICP;
unsigned long flags;
- __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ save_flags(flags); cli();
/*
* First, clear the FIFO buffers and disable them
@@ -608,14 +573,26 @@ static void startup(struct async_struct * info)
info->flags |= ASYNC_INITIALIZED;
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
- __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+ restore_flags(flags);
+ /*
+ * Set up parity check flag
+ */
+ if (I_INPCK(info->tty))
+ info->read_status_mask = UART_LSR_BI | UART_LSR_FE |
+ UART_LSR_PE;
+ else
+ info->read_status_mask = UART_LSR_BI | UART_LSR_FE;
}
+/*
+ * This routine shutsdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
static void shutdown(struct async_struct * info)
{
unsigned long flags;
- __asm__ __volatile__("pushfl ; popl %0 ; cli":"=r" (flags));
+ save_flags(flags); cli();
serial_outp(info, UART_IER, 0x00); /* disable all intrs */
if (info->tty && !(info->tty->termios->c_cflag & HUPCL))
serial_outp(info, UART_MCR, UART_MCR_DTR);
@@ -627,9 +604,13 @@ static void shutdown(struct async_struct * info)
info->flags &= ~ASYNC_INITIALIZED;
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
- __asm__ __volatile__("pushl %0 ; popfl"::"r" (flags));
+ restore_flags(flags);
}
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
static void change_speed(unsigned int line)
{
struct async_struct * info;
@@ -679,9 +660,9 @@ static void change_speed(unsigned int line)
cval = cflag & (CSIZE | CSTOPB);
cval >>= 4;
if (cflag & PARENB)
- cval |= 8;
+ cval |= UART_LCR_PARITY;
if (!(cflag & PARODD))
- cval |= 16;
+ cval |= UART_LCR_EPAR;
cli();
serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */
@@ -690,6 +671,113 @@ static void change_speed(unsigned int line)
sti();
}
+/*
+ * ------------------------------------------------------------
+ * rs_write() and friends
+ * ------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by rs_write to restart transmitter interrupts,
+ * which are disabled after we have a transmitter interrupt which went
+ * unacknowledged because we had run out of data to transmit.
+ *
+ * Note: this subroutine must be called with the interrupts *off*
+ */
+static void restart_port(struct async_struct *info)
+{
+ struct tty_queue * queue;
+ int head, tail, count;
+
+ if (serial_inp(info, UART_LSR) & UART_LSR_THRE) {
+ if (info->x_char) {
+ serial_outp(info, UART_TX, info->x_char);
+ info->x_char = 0;
+ } else {
+ queue = &info->tty->write_q;
+ head = queue->head;
+ tail = queue->tail;
+ count = info->xmit_fifo_size;
+ while (count--) {
+ if (tail == head)
+ break;
+ serial_outp(info, UART_TX, queue->buf[tail++]);
+ tail &= TTY_BUF_SIZE-1;
+ }
+ queue->tail = tail;
+ }
+ }
+}
+
+/*
+ * This routine gets called when tty_write has put something into
+ * the write_queue.
+ */
+void rs_write(struct tty_struct * tty)
+{
+ struct async_struct *info;
+
+ if (!tty || tty->stopped)
+ return;
+ info = rs_table + DEV_TO_SL(tty->line);
+ cli();
+ restart_port(info);
+ sti();
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled (and that the throttled
+ * should be released).
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty, int status)
+{
+ struct async_struct *info;
+ unsigned char mcr;
+
+#ifdef notdef
+ printk("throttle tty%d: %d (%d, %d)....\n", DEV_TO_SL(tty->line),
+ status, LEFT(&tty->read_q), LEFT(&tty->secondary));
+#endif
+ switch (status) {
+ case TTY_THROTTLE_RQ_FULL:
+ info = rs_table + DEV_TO_SL(tty->line);
+ if (tty->termios->c_iflag & IXOFF) {
+ info->x_char = STOP_CHAR(tty);
+ } else {
+ mcr = serial_inp(info, UART_MCR);
+ mcr &= ~UART_MCR_RTS;
+ serial_out(info, UART_MCR, mcr);
+ }
+ break;
+ case TTY_THROTTLE_RQ_AVAIL:
+ info = rs_table + DEV_TO_SL(tty->line);
+ if (tty->termios->c_iflag & IXOFF) {
+ cli();
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ info->x_char = START_CHAR(tty);
+ sti();
+ } else {
+ mcr = serial_in(info, UART_MCR);
+ mcr |= UART_MCR_RTS;
+ serial_out(info, UART_MCR, mcr);
+ }
+ break;
+ }
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
static int get_serial_info(struct async_struct * info,
struct serial_struct * retinfo)
{
@@ -697,12 +785,14 @@ static int get_serial_info(struct async_struct * info,
if (!retinfo)
return -EFAULT;
+ memset(&tmp, 0, sizeof(tmp));
tmp.type = info->type;
tmp.line = info->line;
tmp.port = info->port;
tmp.irq = info->irq;
tmp.flags = info->flags;
tmp.baud_base = info->baud_base;
+ tmp.close_delay = info->close_delay;
memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
return 0;
}
@@ -740,6 +830,7 @@ static int set_serial_info(struct async_struct * info,
(new.flags & ASYNC_FLAGS));
info->custom_divisor = new.custom_divisor;
info->type = new.type;
+ info->close_delay = new.close_delay;
if (new.irq == 2)
new.irq = 9;
@@ -752,9 +843,9 @@ static int set_serial_info(struct async_struct * info,
* interrupts. (We have to do this early, since we may get an
* error trying to do this.)
*/
- if (new.port && new.irq && info->type &&
+ if (new.port && info->type &&
((irq != new.irq) || !(info->flags & ASYNC_INITIALIZED))) {
- if (!IRQ_ports[new.irq]) {
+ if (new.irq && !IRQ_ports[new.irq]) {
sa.sa_handler = rs_interrupt;
sa.sa_flags = (SA_INTERRUPT);
sa.sa_mask = 0;
@@ -781,6 +872,7 @@ static int set_serial_info(struct async_struct * info,
info->prev_port->next_port = info->next_port;
else
IRQ_ports[irq] = info->next_port;
+ figure_IRQ_timeout(irq);
check_irq = irq; /* Check later if we need to */
/* free the IRQ */
}
@@ -789,7 +881,7 @@ static int set_serial_info(struct async_struct * info,
}
check_and_exit:
- if (new.port && new.irq && info->type &&
+ if (new.port && info->type &&
!(info->flags & ASYNC_INITIALIZED)) {
/*
* Link the port into the new interrupt chain.
@@ -799,6 +891,7 @@ check_and_exit:
if (info->next_port)
info->next_port->prev_port = info;
IRQ_ports[info->irq] = info;
+ figure_IRQ_timeout(info->irq);
startup(info);
change_speed(info->line);
} else if (((old_info.flags & ASYNC_SPD_MASK) !=
@@ -862,10 +955,24 @@ static int set_modem_info(struct async_struct * info, unsigned int cmd,
return 0;
}
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break( struct async_struct * info, int duration)
+{
+ if (!info->port)
+ return;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + duration;
+ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
+ schedule();
+ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+}
+
static int rs_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned int arg)
{
- int line;
+ int error, line;
struct async_struct * info;
line = DEV_TO_SL(tty->line);
@@ -874,20 +981,42 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
info = rs_table + line;
switch (cmd) {
- case TCSBRK:
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
wait_until_sent(tty);
if (!arg)
- send_break(info);
+ send_break(info, HZ/4); /* 1/4 second */
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ wait_until_sent(tty);
+ send_break(info, arg ? HZ/4 : arg*(HZ/10));
+ return 0;
+ case TIOCGSOFTCAR:
+ error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *));
+ if (error)
+ return error;
+ put_fs_long(C_LOCAL(tty) ? 1 : 0,
+ (unsigned long *) arg);
+ return 0;
+ case TIOCSSOFTCAR:
+ arg = get_fs_long((unsigned long *) arg);
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
return 0;
case TIOCMGET:
- verify_area((void *) arg,sizeof(unsigned int *));
+ error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *));
+ if (error)
+ return error;
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
case TIOCMBIC:
case TIOCMSET:
return set_modem_info(info, cmd, (unsigned int *) arg);
case TIOCGSERIAL:
- verify_area((void *) arg,sizeof(struct serial_struct));
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(struct serial_struct));
+ if (error)
+ return error;
return get_serial_info(info,
(struct serial_struct *) arg);
case TIOCSSERIAL:
@@ -902,9 +1031,13 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
+ struct async_struct *info;
+
if (tty->termios->c_cflag == old_termios->c_cflag)
return;
+ info = &rs_table[DEV_TO_SL(tty->line)];
+
change_speed(DEV_TO_SL(tty->line));
if ((old_termios->c_cflag & CRTSCTS) &&
@@ -915,75 +1048,78 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
if (!(old_termios->c_cflag & CLOCAL) &&
(tty->termios->c_cflag & CLOCAL))
- wake_up_interruptible(&rs_table[DEV_TO_SL(tty->line)].open_wait);
+ wake_up_interruptible(&info->open_wait);
+
+ if (I_INPCK(tty))
+ info->read_status_mask = UART_LSR_BI | UART_LSR_FE |
+ UART_LSR_PE;
+ else
+ info->read_status_mask = UART_LSR_BI | UART_LSR_FE;
}
/*
- * This routine is called whenever a serial port is opened. It
- * enables interrupts for a serial port, linking in its async structure into
- * the IRQ chain. It also performs the serial-speicific
- * initalization for the tty structure.
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed. First, we
+ * wait for the last remaining data to be sent. Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
*/
-int rs_open(struct tty_struct *tty, struct file * filp)
+static void rs_close(struct tty_struct *tty, struct file * filp)
{
- struct async_struct *info;
- int irq, retval, line;
- struct sigaction sa;
+ struct async_struct * info;
+ int irq, line;
line = DEV_TO_SL(tty->line);
if ((line < 0) || (line >= NR_PORTS))
- return -ENODEV;
+ return;
info = rs_table + line;
#ifdef SERIAL_DEBUG_OPEN
- printk("rs_open ttys%d, count = %d\n", info->line, info->count);
+ printk("rs_close ttys%d, count = %d\n", info->line, info->count);
#endif
- info->count++;
- info->tty = tty;
-
- tty->write = rs_write;
- tty->close = rs_close;
- tty->ioctl = rs_ioctl;
- tty->throttle = rs_throttle;
- tty->set_termios = rs_set_termios;
-
- if (!(info->flags & ASYNC_INITIALIZED)) {
- if (!info->port || !info->irq || !info->type) {
- set_bit(TTY_IO_ERROR, &tty->flags);
- return 0;
+ if (--info->count > 0)
+ return;
+ tty->stopped = 0; /* Force flush to succeed */
+ wait_until_sent(tty);
+ clear_bit(line, rs_event);
+ info->event = 0;
+ info->count = 0;
+ info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+ if (info->blocked_open) {
+ shutdown(info);
+ if (info->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + info->close_delay;
+ schedule();
}
+ startup(info);
+ return;
+ }
+ if (info->flags & ASYNC_INITIALIZED) {
+ shutdown(info);
irq = info->irq;
if (irq == 2)
irq = 9;
- if (!IRQ_ports[irq]) {
- sa.sa_handler = rs_interrupt;
- sa.sa_flags = (SA_INTERRUPT);
- sa.sa_mask = 0;
- sa.sa_restorer = NULL;
- retval = irqaction(irq,&sa);
- if (retval)
- return retval;
- }
- /*
- * Link in port to IRQ chain
- */
- info->prev_port = 0;
- info->next_port = IRQ_ports[irq];
if (info->next_port)
- info->next_port->prev_port = info;
- IRQ_ports[irq] = info;
-
- startup(info);
- change_speed(info->line);
+ info->next_port->prev_port = info->prev_port;
+ if (info->prev_port)
+ info->prev_port->next_port = info->next_port;
+ else
+ IRQ_ports[irq] = info->next_port;
+ if (irq && !IRQ_ports[irq])
+ free_irq(irq);
+ figure_IRQ_timeout(irq);
}
-
- retval = block_til_ready(tty, filp, info);
- if (retval)
- return retval;
-
- return 0;
-
+ info->tty = 0;
}
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
static int block_til_ready(struct tty_struct *tty, struct file * filp,
struct async_struct *info)
{
@@ -1031,8 +1167,15 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
serial_out(info, UART_MCR,
serial_inp(info, UART_MCR) | UART_MCR_DTR);
current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp)) {
+ if (info->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTNOINTR;
+ break;
+ }
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
- ((tty->termios->c_cflag & CLOCAL) ||
+ (C_LOCAL(tty) ||
(serial_in(info, UART_MSR) & UART_MSR_DCD)))
break;
if (current->signal & ~current->blocked) {
@@ -1056,14 +1199,96 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
if (retval)
return retval;
info->flags |= ASYNC_NORMAL_ACTIVE;
- tty_unhangup(filp); /* To make sure fops is OK */
return 0;
}
+/*
+ * This routine is called whenever a serial port is opened. It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain. It also performs the serial-speicific
+ * initalization for the tty structure.
+ */
+int rs_open(struct tty_struct *tty, struct file * filp)
+{
+ struct async_struct *info;
+ int irq, retval, line;
+ struct sigaction sa;
+
+ line = DEV_TO_SL(tty->line);
+ if ((line < 0) || (line >= NR_PORTS))
+ return -ENODEV;
+ info = rs_table + line;
+#ifdef SERIAL_DEBUG_OPEN
+ printk("rs_open ttys%d, count = %d\n", info->line, info->count);
+#endif
+ info->count++;
+ info->tty = tty;
+
+ tty->write = rs_write;
+ tty->close = rs_close;
+ tty->ioctl = rs_ioctl;
+ tty->throttle = rs_throttle;
+ tty->set_termios = rs_set_termios;
+
+ if (!(info->flags & ASYNC_INITIALIZED)) {
+ if (!info->port || !info->type) {
+ set_bit(TTY_IO_ERROR, &tty->flags);
+ return 0;
+ }
+ irq = info->irq;
+ if (irq == 2)
+ irq = 9;
+ if (irq && !IRQ_ports[irq]) {
+ sa.sa_handler = rs_interrupt;
+ sa.sa_flags = (SA_INTERRUPT);
+ sa.sa_mask = 0;
+ sa.sa_restorer = NULL;
+ retval = irqaction(irq,&sa);
+ if (retval)
+ return retval;
+ }
+ /*
+ * Link in port to IRQ chain
+ */
+ info->prev_port = 0;
+ info->next_port = IRQ_ports[irq];
+ if (info->next_port)
+ info->next_port->prev_port = info;
+ IRQ_ports[irq] = info;
+ figure_IRQ_timeout(irq);
+
+ startup(info);
+ change_speed(info->line);
+ if (!irq) {
+ cli();
+ figure_RS_timer();
+ sti();
+ }
+ }
-static void show_serial_version()
+ retval = block_til_ready(tty, filp, info);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * rs_init() and friends
+ *
+ * rs_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static void show_serial_version(void)
{
- printk("Serial driver version 3.91 with");
+ printk("Serial driver version 3.93 with");
#ifdef CONFIG_AST_FOURPORT
printk(" AST_FOURPORT");
#define SERIAL_OPT
@@ -1085,6 +1310,11 @@ static void show_serial_version()
}
#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
+ * fool-proof, but it works a large part of the time.
+ */
static int get_auto_irq(struct async_struct *info)
{
unsigned char save_MCR, save_IER, save_ICP=0;
@@ -1133,17 +1363,29 @@ static int get_auto_irq(struct async_struct *info)
}
#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
+ *
+ * It also determines what type of UART ship this serial port is
+ * using: 8250, 16450, 16550, 16550A. The important question is
+ * whether or not this UART is a 16550A or not, since this will
+ * determine whether or not we can use its FIFO features or not.
+ */
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;
for (retries = 0; retries < 5; retries++) {
if (!scratch)
@@ -1162,12 +1404,7 @@ static void init(struct async_struct * info)
info->type = PORT_UNKNOWN;
return;
}
-
#else /* CONFIG_AUTO_IRQ */
-
- if (!port)
- return;
-
/*
* Check to see if a UART is really there.
*/
@@ -1215,6 +1452,9 @@ static void init(struct async_struct * info)
shutdown(info);
}
+/*
+ * The serial driver boot-time initialization code!
+ */
long rs_init(long kmem_start)
{
int i;
@@ -1235,11 +1475,15 @@ long rs_init(long kmem_start)
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;
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;
@@ -1263,8 +1507,8 @@ long rs_init(long kmem_start)
info->line = i;
info->tty = 0;
info->type = PORT_UNKNOWN;
- info->timer = 0;
info->custom_divisor = 0;
+ info->close_delay = 50;
info->x_char = 0;
info->event = 0;
info->count = 0;
@@ -1307,9 +1551,6 @@ long rs_init(long kmem_start)
free_irq(i);
}
#endif
- bh_base[SERIAL_BH].routine = do_softint;
- memset(&rs_event, 0, sizeof(rs_event));
- memset(&rs_write_active, 0, sizeof(rs_write_active));
return kmem_start;
}
diff --git a/kernel/chr_drv/sound/Makefile b/kernel/chr_drv/sound/Makefile
index 703254e..85e5ccd 100644
--- a/kernel/chr_drv/sound/Makefile
+++ b/kernel/chr_drv/sound/Makefile
@@ -1,4 +1,4 @@
-# Makefile for the Linux sound card driver
+# Makefile for the stub version of Linux sound card driver
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
@@ -7,7 +7,6 @@
# Note 2! The CFLAGS definitions are now inherited from the
# parent makes. (hopefully)
#
-# Stolen from the kernel Makefiles, Craig Metz - cmetz@thor.tjhsst.edu
#
.c.s:
@@ -15,30 +14,33 @@
.s.o:
$(AS) -c -o $*.o $<
.c.o:
- $(CC) $(CFLAGS) $(SOUND_SUPPORT) -c $<
+ $(CC) $(CFLAGS) -c $<
OBJS = sound_stub.o
-all: sound.a
-
sound.a: $(OBJS)
$(AR) rcs sound.a $(OBJS)
sync
clean:
- rm -f core *.o *.a *.BAK *.BA *.B
- rm -f soundload sounddrv
- for i in *.c;do rm -f `basename $$i .c`.s;done
-
-indent:
- for n in *.c;do echo indent $$n;indent $$n;done
+ rm -f core *.o *.a
+
+config:
+ -@if [ -n "$(CONFIG_SOUND)" ] ;then make notice; fi
+
+notice:
+ @echo
+ @echo
+ @echo "WARNING! You have attempted to compile the sound driver"
+ @echo " to your kernel. The driver is not included in"
+ @echo " the kernel distribution. You have to install"
+ @echo " the sound driver and to run "make config" again."
+ @echo " Otherwise the sound driver will not be included"
+ @echo
+ @echo " The sound driver is available at nic.funet.fi"
+ @echo " in directory pub/OS/Linux/xtra/snd-kit. It "
+ @echo " should be also at the other Linux ftp sites."
+ @echo
dep:
- $(CPP) -M $(SOUND_SUPPORT) *.c > .depend
-
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
+ @touch .depend
diff --git a/kernel/chr_drv/tty_io.c b/kernel/chr_drv/tty_io.c
index ed8f18f..5ffd395 100644
--- a/kernel/chr_drv/tty_io.c
+++ b/kernel/chr_drv/tty_io.c
@@ -181,6 +181,12 @@ static int hung_up_tty_select(struct inode * inode, struct file * filp, int sel_
return 1;
}
+static int hung_up_tty_ioctl(struct inode * inode, struct file * file,
+ unsigned int cmd, unsigned int arg)
+{
+ return -EIO;
+}
+
static int tty_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
{
return -ESPIPE;
@@ -210,7 +216,19 @@ static struct file_operations hung_up_tty_fops = {
tty_release
};
-void tty_hangup(struct tty_struct * tty)
+static struct file_operations vhung_up_tty_fops = {
+ tty_lseek,
+ hung_up_tty_read,
+ hung_up_tty_write,
+ NULL, /* hung_up_tty_readdir */
+ hung_up_tty_select,
+ hung_up_tty_ioctl,
+ NULL, /* hung_up_tty_mmap */
+ tty_open,
+ tty_release
+};
+
+void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
{
struct file * filp;
struct task_struct **p;
@@ -227,7 +245,7 @@ void tty_hangup(struct tty_struct * tty)
continue;
if (filp->f_op != &tty_fops)
continue;
- filp->f_op = &hung_up_tty_fops;
+ filp->f_op = fops;
}
wake_up_interruptible(&tty->secondary.proc_list);
wake_up_interruptible(&tty->read_q.proc_list);
@@ -242,14 +260,25 @@ void tty_hangup(struct tty_struct * tty)
}
}
+void tty_hangup(struct tty_struct * tty)
+{
+ do_tty_hangup(tty, &hung_up_tty_fops);
+}
+
+void tty_vhangup(struct tty_struct * tty)
+{
+ do_tty_hangup(tty, &vhung_up_tty_fops);
+}
+
void tty_unhangup(struct file *filp)
{
filp->f_op = &tty_fops;
}
-static inline int hung_up(struct file * filp)
+inline int tty_hung_up_p(struct file * filp)
{
- return filp->f_op == &hung_up_tty_fops;
+ return ((filp->f_op == &hung_up_tty_fops) ||
+ (filp->f_op == &vhung_up_tty_fops));
}
/*
@@ -434,7 +463,8 @@ void wait_for_keypress(void)
void copy_to_cooked(struct tty_struct * tty)
{
- int c;
+ int c, special_flag;
+ unsigned long flags;
if (!tty) {
printk("copy_to_cooked: called with NULL tty\n");
@@ -456,11 +486,51 @@ void copy_to_cooked(struct tty_struct * tty)
tty->throttle(tty, TTY_THROTTLE_SQ_FULL);
if (c == 0)
break;
- c = get_tty_queue(&tty->read_q);
- if (c < 0)
+ save_flags(flags); cli();
+ if (tty->read_q.tail != tty->read_q.head) {
+ c = 0xff & tty->read_q.buf[tty->read_q.tail];
+ special_flag = !clear_bit(tty->read_q.tail,
+ &tty->readq_flags);
+ tty->read_q.tail = (tty->read_q.tail + 1) &
+ (TTY_BUF_SIZE-1);
+ restore_flags(flags);
+ } else {
+ restore_flags(flags);
break;
+ }
+ if (special_flag) {
+ tty->char_error = c & 3;
+ continue;
+ }
+ if (tty->char_error) {
+ if (tty->char_error == TTY_BREAK) {
+ tty->char_error = 0;
+ if (I_IGNBRK(tty))
+ continue;
+ if (I_PARMRK(tty)) {
+ put_tty_queue(0377, &tty->secondary);
+ put_tty_queue(0, &tty->secondary);
+ }
+ put_tty_queue(0, &tty->secondary);
+ continue;
+ }
+ /* If not a break, then a parity or frame error */
+ tty->char_error = 0;
+ if (I_IGNPAR(tty)) {
+ continue;
+ }
+ if (I_PARMRK(tty)) {
+ put_tty_queue(0377, &tty->secondary);
+ put_tty_queue(0, &tty->secondary);
+ put_tty_queue(c, &tty->secondary);
+ } else
+ put_tty_queue(0, &tty->secondary);
+ continue;
+ }
if (I_STRP(tty))
c &= 0x7f;
+ else if (I_PARMRK(tty) && (c == 0377))
+ put_tty_queue(0377, &tty->secondary);
if (c==13) {
if (I_CRNL(tty))
c=10;
@@ -660,7 +730,7 @@ static int read_chan(struct tty_struct * tty, struct file * file, char * buf, in
}
add_wait_queue(&tty->secondary.proc_list, &wait);
while (nr>0) {
- if (hung_up(file)) {
+ if (tty_hung_up_p(file)) {
file->f_flags &= ~O_NONBLOCK;
break; /* force read() to return 0 */
}
@@ -739,7 +809,7 @@ static void __wait_for_canon_input(struct file * file, struct tty_struct * tty)
break;
if (current->signal & ~current->blocked)
break;
- if (hung_up(file))
+ if (tty_hung_up_p(file))
break;
schedule();
}
@@ -775,7 +845,7 @@ static int write_chan(struct tty_struct * tty, struct file * file, char * buf, i
while (nr>0) {
if (current->signal & ~current->blocked)
break;
- if (hung_up(file))
+ if (tty_hung_up_p(file))
break;
if (tty->link && !tty->link->count) {
send_sig(SIGPIPE,current,0);
@@ -1307,7 +1377,7 @@ int tty_write_data(struct tty_struct *tty, char *bufp, int buflen,
* tty_check_write[8] is a bitstring which indicates which ttys
* needs to be processed.
*/
-void tty_bh_routine()
+void tty_bh_routine(void * unused)
{
int i, j, line, mask;
int head, tail, count;
@@ -1357,8 +1427,8 @@ static void initialize_tty_struct(int line, struct tty_struct *tty)
tty->line = line;
tty->disc = N_TTY;
tty->pgrp = -1;
- tty->winsize.ws_row = 24;
- tty->winsize.ws_col = 80;
+ tty->winsize.ws_row = 0;
+ tty->winsize.ws_col = 0;
if (IS_A_CONSOLE(line)) {
tty->open = con_open;
tty->winsize.ws_row = video_num_lines;
diff --git a/kernel/chr_drv/tty_ioctl.c b/kernel/chr_drv/tty_ioctl.c
index c34b91a..0ff7f97 100644
--- a/kernel/chr_drv/tty_ioctl.c
+++ b/kernel/chr_drv/tty_ioctl.c
@@ -104,8 +104,9 @@ static int do_get_ps_info(int arg)
char *c, *d;
int i, n = 0;
- verify_area((void *)arg, sizeof(struct tstruct));
-
+ i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct));
+ if (i)
+ return i;
for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
if (*p)
{
@@ -124,7 +125,9 @@ static int get_termios(struct tty_struct * tty, struct termios * termios)
{
int i;
- verify_area(termios, sizeof (*termios));
+ i = verify_area(VERIFY_WRITE, termios, sizeof (*termios));
+ if (i)
+ return i;
for (i=0 ; i< (sizeof (*termios)) ; i++)
put_fs_byte( ((char *)tty->termios)[i] , i+(char *)termios );
return 0;
@@ -175,7 +178,9 @@ static int get_termio(struct tty_struct * tty, struct termio * termio)
int i;
struct termio tmp_termio;
- verify_area(termio, sizeof (*termio));
+ i = verify_area(VERIFY_WRITE, termio, sizeof (*termio));
+ if (i)
+ return i;
tmp_termio.c_iflag = tty->termios->c_iflag;
tmp_termio.c_oflag = tty->termios->c_oflag;
tmp_termio.c_cflag = tty->termios->c_cflag;
@@ -261,7 +266,9 @@ static int get_window_size(struct tty_struct * tty, struct winsize * ws)
if (!ws)
return -EINVAL;
- verify_area(ws, sizeof (*ws));
+ i = verify_area(VERIFY_WRITE, ws, sizeof (*ws));
+ if (i)
+ return i;
tmp = (char *) ws;
for (i = 0; i < sizeof (struct winsize) ; i++,tmp++)
put_fs_byte(((char *) &tty->winsize)[i], tmp);
@@ -389,9 +396,10 @@ int tty_ioctl(struct inode * inode, struct file * file,
}
return -EPERM;
case TIOCGPGRP:
- verify_area((void *) arg,4);
- put_fs_long(termios_tty->pgrp,(unsigned long *) arg);
- return 0;
+ retval = verify_area(VERIFY_WRITE, (void *) arg,4);
+ if (!retval)
+ put_fs_long(termios_tty->pgrp,(unsigned long *) arg);
+ return retval;
case TIOCSPGRP:
if ((current->tty < 0) ||
(current->tty != termios_dev) ||
@@ -405,12 +413,15 @@ int tty_ioctl(struct inode * inode, struct file * file,
termios_tty->pgrp = pgrp;
return 0;
case TIOCOUTQ:
- verify_area((void *) arg,4);
- put_fs_long(CHARS(&tty->write_q),
+ retval = verify_area(VERIFY_WRITE, (void *) arg,4);
+ if (!retval)
+ put_fs_long(CHARS(&tty->write_q),
(unsigned long *) arg);
- return 0;
+ return retval;
case TIOCINQ:
- verify_area((void *) arg,4);
+ retval = verify_area(VERIFY_WRITE, (void *) arg,4);
+ if (retval)
+ return retval;
if (L_CANON(tty) && !tty->secondary.data)
put_fs_long(0, (unsigned long *) arg);
else
@@ -476,9 +487,10 @@ int tty_ioctl(struct inode * inode, struct file * file,
}
return 0;
case TIOCGETD:
- verify_area((void *) arg,4);
- put_fs_long(tty->disc, (unsigned long *) arg);
- return 0;
+ retval = verify_area(VERIFY_WRITE, (void *) arg,4);
+ if (!retval)
+ put_fs_long(tty->disc, (unsigned long *) arg);
+ return retval;
case TIOCSETD:
arg = get_fs_long((unsigned long *) arg);
return tty_set_ldisc(tty, arg);
@@ -486,8 +498,10 @@ int tty_ioctl(struct inode * inode, struct file * file,
{
int on;
if (!IS_A_PTY_MASTER(dev))
- return (-EINVAL);
- verify_area ((unsigned long *)arg, sizeof (int));
+ return -EINVAL;
+ retval = verify_area(VERIFY_READ, (unsigned long *)arg, sizeof (int));
+ if (retval)
+ return retval;
on=get_fs_long ((unsigned long *)arg);
if (on )
tty->packet = 1;
diff --git a/kernel/chr_drv/vt.c b/kernel/chr_drv/vt.c
index 0df42af..ec9cbf5 100644
--- a/kernel/chr_drv/vt.c
+++ b/kernel/chr_drv/vt.c
@@ -137,9 +137,10 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
/*
* this is naive.
*/
- verify_area((void *) arg, sizeof(unsigned char));
- put_fs_byte(KB_101, (unsigned char *) arg);
- return 0;
+ i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
+ if (!i)
+ put_fs_byte(KB_101, (unsigned char *) arg);
+ return i;
case KDADDIO:
case KDDELIO:
@@ -190,9 +191,10 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
return 0;
case KDGETMODE:
- verify_area((void *) arg, sizeof(unsigned long));
- put_fs_long(vt_cons[console].vc_mode, (unsigned long *) arg);
- return 0;
+ i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
+ if (!i)
+ put_fs_long(vt_cons[console].vc_mode, (unsigned long *) arg);
+ return i;
case KDMAPDISP:
case KDUNMAPDISP:
@@ -214,13 +216,17 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
return 0;
case KDGKBMODE:
- verify_area((void *) arg, sizeof(unsigned long));
- ucval = vc_kbd_flag(kbd, VC_RAW);
- put_fs_long(ucval ? K_RAW : K_XLATE, (unsigned long *) arg);
- return 0;
+ i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long));
+ if (!i) {
+ ucval = vc_kbd_flag(kbd, VC_RAW);
+ put_fs_long(ucval ? K_RAW : K_XLATE, (unsigned long *) arg);
+ }
+ return i;
case KDGETLED:
- verify_area((void *) arg, sizeof(unsigned char));
+ i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
+ if (i)
+ return i;
ucval = 0;
if (vc_kbd_flag(kbd, VC_SCROLLOCK))
ucval |= LED_SCR;
@@ -254,7 +260,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
struct vt_mode *vtmode = (struct vt_mode *)arg;
char mode;
- verify_area((void *)vtmode, sizeof(struct vt_mode));
+ i = verify_area(VERIFY_WRITE, (void *)vtmode, sizeof(struct vt_mode));
+ if (i)
+ return i;
mode = get_fs_byte(&vtmode->mode);
if (mode != VT_AUTO && mode != VT_PROCESS)
return -EINVAL;
@@ -273,7 +281,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
{
struct vt_mode *vtmode = (struct vt_mode *)arg;
- verify_area((void *)arg, sizeof(struct vt_mode));
+ i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct vt_mode));
+ if (i)
+ return i;
put_fs_byte(vt_cons[console].vt_mode.mode, &vtmode->mode);
put_fs_byte(vt_cons[console].vt_mode.waitv, &vtmode->waitv);
put_fs_word(vt_cons[console].vt_mode.relsig, &vtmode->relsig);
@@ -291,7 +301,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
struct vt_stat *vtstat = (struct vt_stat *)arg;
unsigned short state, mask;
- verify_area((void *)vtstat, sizeof(struct vt_stat));
+ i = verify_area(VERIFY_WRITE,(void *)vtstat, sizeof(struct vt_stat));
+ if (i)
+ return i;
put_fs_word(fg_console + 1, &vtstat->v_active);
state = 1; /* /dev/tty0 is always open */
for (i = 1, mask = 2; i <= NR_CONSOLES; ++i, mask <<= 1)
@@ -305,7 +317,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
* Returns the first available (non-opened) console.
*/
case VT_OPENQRY:
- verify_area((void *) arg, sizeof(long));
+ i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+ if (i)
+ return i;
for (i = 1; i <= NR_CONSOLES; ++i)
if (!tty_table[i] || tty_table[i]->count == 0)
break;
diff --git a/kernel/exit.c b/kernel/exit.c
index 2088f0c..bd3020f 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -18,7 +18,7 @@
#include <asm/segment.h>
int sys_close(int fd);
-void getrusage(struct task_struct *, int, struct rusage *);
+int getrusage(struct task_struct *, int, struct rusage *);
int send_sig(long sig,struct task_struct * p,int priv)
{
@@ -63,7 +63,7 @@ void release(struct task_struct * p)
if (!p)
return;
if (p == current) {
- printk("task releasing itself\n\r");
+ printk("task releasing itself\n");
return;
}
for (i=1 ; i<NR_TASKS ; i++)
@@ -103,7 +103,7 @@ int bad_task_ptr(struct task_struct *p)
* and checking it corresponds with the process tree defined by p_cptr and
* p_pptr;
*/
-void audit_ptree()
+void audit_ptree(void)
{
int i;
@@ -449,8 +449,11 @@ int sys_wait4(pid_t pid,unsigned long * stat_addr, int options, struct rusage *
struct task_struct *p;
unsigned long oldblocked;
- if (stat_addr)
- verify_area(stat_addr,4);
+ if (stat_addr) {
+ flag = verify_area(VERIFY_WRITE, stat_addr, 4);
+ if (flag)
+ return flag;
+ }
repeat:
current->signal &= ~(1<<(SIGCHLD-1));
flag=0;
diff --git a/kernel/fork.c b/kernel/fork.c
index 856d864..ddaaef7 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -24,18 +24,25 @@
long last_pid=0;
-void verify_area(void * addr,int size)
+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;
- while (size>0) {
- size -= 4096;
+ do {
write_verify(start);
start += 4096;
- }
+ } while (size--);
+ return 0;
}
static int find_empty_process(void)
@@ -103,7 +110,6 @@ int sys_fork(long ebx,long ecx,long edx,
p->p_pptr = p->p_opptr = current;
p->p_cptr = NULL;
SET_LINKS(p);
- p->counter = p->priority;
p->signal = 0;
p->it_real_value = p->it_virt_value = p->it_prof_value = 0;
p->it_real_incr = p->it_virt_incr = p->it_prof_incr = 0;
diff --git a/kernel/info.c b/kernel/info.c
index 0a373a9..452afe2 100644
--- a/kernel/info.c
+++ b/kernel/info.c
@@ -16,10 +16,13 @@
int sys_sysinfo(struct sysinfo *info)
{
+ int error;
struct sysinfo val;
struct task_struct **p;
- verify_area(info, sizeof(struct sysinfo));
+ error = verify_area(VERIFY_WRITE, info, sizeof(struct sysinfo));
+ if (error)
+ return error;
memset((char *)&val, 0, sizeof(struct sysinfo));
val.uptime = (jiffies + jiffies_offset) / HZ;
diff --git a/kernel/irq.c b/kernel/irq.c
index 0c03383..9d503ae 100644
--- a/kernel/irq.c
+++ b/kernel/irq.c
@@ -57,7 +57,7 @@ void do_bottom_half(int nr)
if (bh->routine != NULL)
bh->routine(bh->data);
else
- printk ("irq.c:bad bottom half entry.\n");
+ printk ("irq.c:bad bottom half entry (%d).\n",nr);
}
/*
diff --git a/kernel/itimer.c b/kernel/itimer.c
index b69e1da..e9d42fa 100644
--- a/kernel/itimer.c
+++ b/kernel/itimer.c
@@ -55,15 +55,17 @@ int _getitimer(int which, struct itimerval *value)
int sys_getitimer(int which, struct itimerval *value)
{
+ int error;
struct itimerval get_buffer;
- int k;
if (!value)
return -EFAULT;
- k = _getitimer(which, &get_buffer);
- if (k < 0)
- return k;
- verify_area(value, sizeof(struct itimerval));
+ error = _getitimer(which, &get_buffer);
+ if (error)
+ return error;
+ error = verify_area(VERIFY_WRITE, value, sizeof(struct itimerval));
+ if (error)
+ return error;
memcpy_tofs(value, &get_buffer, sizeof(get_buffer));
return 0;
}
@@ -98,17 +100,18 @@ int _setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
int sys_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
{
+ int error;
struct itimerval set_buffer, get_buffer;
- int k;
if (!value)
memset((char *) &set_buffer, 0, sizeof(set_buffer));
else
memcpy_fromfs(&set_buffer, value, sizeof(set_buffer));
- k = _setitimer(which, &set_buffer, ovalue ? &get_buffer : 0);
- if (k < 0 || !ovalue)
- return k;
- verify_area(ovalue, sizeof(struct itimerval));
- memcpy_tofs(ovalue, &get_buffer, sizeof(get_buffer));
- return 0;
+ error = _setitimer(which, &set_buffer, ovalue ? &get_buffer : 0);
+ if (error || !ovalue)
+ return error;
+ error = verify_area(VERIFY_WRITE, ovalue, sizeof(struct itimerval));
+ if (!error)
+ memcpy_tofs(ovalue, &get_buffer, sizeof(get_buffer));
+ return error;
}
diff --git a/kernel/panic.c b/kernel/panic.c
index 2459e76..01b43ef 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -15,9 +15,9 @@ void sys_sync(void); /* it's really int */
volatile void panic(const char * s)
{
- printk("Kernel panic: %s\n\r",s);
+ printk("Kernel panic: %s\n",s);
if (current == task[0])
- printk("In swapper task - not syncing\n\r");
+ printk("In swapper task - not syncing\n");
else
sys_sync();
for(;;);
diff --git a/kernel/printk.c b/kernel/printk.c
index bc105d6..0f99367 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -52,7 +52,9 @@ int sys_syslog(int type, char * buf, int len)
return -EINVAL;
if (!len)
return 0;
- verify_area(buf,len);
+ i = verify_area(VERIFY_WRITE, buf, len);
+ if (i)
+ return i;
while (!log_size) {
if (!log_page)
return -EIO;
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index eca31b8..84fd1b8 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -274,18 +274,21 @@ int sys_ptrace(long request, long pid, long addr, long data)
res = read_long(child, addr, &tmp);
if (res < 0)
return res;
- verify_area((void *) data, 4);
- put_fs_long(tmp,(unsigned long *) data);
- return 0;
+ res = verify_area(VERIFY_WRITE, (void *) data, 4);
+ if (!res)
+ put_fs_long(tmp,(unsigned long *) data);
+ return res;
}
/* read the word at location addr in the USER area. */
case PTRACE_PEEKUSR: {
- int tmp;
+ int tmp, res;
addr = addr >> 2; /* temporary hack. */
if (addr < 0 || addr >= 17)
return -EIO;
- verify_area((void *) data, 4);
+ res = verify_area(VERIFY_WRITE, (void *) data, 4);
+ if (res)
+ return res;
tmp = get_stack_long(child, 4*addr - MAGICNUMBER);
put_fs_long(tmp,(unsigned long *) data);
return 0;
diff --git a/kernel/sched.c b/kernel/sched.c
index 77e323b..ad207e2 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -36,7 +36,6 @@ unsigned long * prof_buffer = NULL;
unsigned long prof_len = 0;
#define _S(nr) (1<<((nr)-1))
-#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
#define LATCH (1193180/HZ)
@@ -71,7 +70,7 @@ struct {
* 'math_state_restore()' saves the current math information in the
* old math state array, and gets the new ones from the current task
*/
-void math_state_restore()
+void math_state_restore(void)
{
if (last_task_used_math == current)
return;
@@ -139,18 +138,35 @@ void schedule(void)
switch_to(next);
}
+/*
+ * This is a little tricky because POSIX pause (3.4.2.1) should only
+ * return for a caught signal or a signal that terminates the process.
+ * We just block ignored signals or "harmless" default signals.
+ * For suspending signals, we must return so that they are handled
+ * and then pause again. -- jrs
+ */
+
int sys_pause(void)
{
unsigned long old_blocked;
unsigned long mask;
+ int sig;
struct sigaction * sa = current->sigaction;
old_blocked = current->blocked;
- for (mask=1 ; mask ; sa++,mask += mask)
- if (sa->sa_handler == SIG_IGN)
+ /* block everything we can that shouldn't interrupt pause */
+ for (mask = 1, sig = 1; mask; sa++, mask += mask, sig++)
+ 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();
+ /* if a suspending signal interrupted us we must restart */
+ if (!(current->signal & ~current->blocked &
+ ~(_S(SIGSTOP) | _S(SIGTSTP) | _S(SIGTTIN) | _S(SIGTTOU)))) {
+ current->blocked = old_blocked;
+ return -ERESTARTSYS;
+ }
current->blocked = old_blocked;
return -EINTR;
}
@@ -308,7 +324,7 @@ void do_floppy_timer(void)
static struct timer_list {
long jiffies;
- void (*fn)();
+ void (*fn)(void);
struct timer_list * next;
} timer_list[TIME_REQUESTS] = { { 0, NULL, NULL }, };
@@ -532,21 +548,21 @@ static void show_task(int nr,struct task_struct * p)
}
while (i<j && !*(stack++))
i++;
- printk("%d/%d chars free in kstack\n\r",i,j);
+ printk("%d/%d chars free in kstack\n",i,j);
printk(" PC=%08X.", *(1019 + (unsigned long *) p));
if (p->p_ysptr || p->p_osptr)
- printk(" Younger sib=%d, older sib=%d\n\r",
+ printk(" Younger sib=%d, older sib=%d\n",
p->p_ysptr ? p->p_ysptr->pid : -1,
p->p_osptr ? p->p_osptr->pid : -1);
else
- printk("\n\r");
+ printk("\n");
}
void show_state(void)
{
int i;
- printk("\rTask-info:\n\r");
+ printk("Task-info:\n");
for (i=0 ; i<NR_TASKS ; i++)
if (task[i])
show_task(i,task[i]);
diff --git a/kernel/signal.c b/kernel/signal.c
index 8343267..ffa74e2 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -13,9 +13,13 @@
#include <asm/segment.h>
+#define _S(nr) (1<<((nr)-1))
+
+#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+
extern int core_dump(long signr,struct pt_regs * regs);
-int sys_sgetmask()
+int sys_sgetmask(void)
{
return current->blocked;
}
@@ -24,16 +28,18 @@ int sys_ssetmask(int newmask)
{
int old=current->blocked;
- current->blocked = newmask & ~(1<<(SIGKILL-1)) & ~(1<<(SIGSTOP-1));
+ current->blocked = newmask & _BLOCKABLE;
return old;
}
int sys_sigpending(sigset_t *set)
{
- /* fill in "set" with signals pending but blocked. */
- verify_area(set,4);
- put_fs_long(current->blocked & current->signal, (unsigned long *)set);
- return 0;
+ int error;
+ /* fill in "set" with signals pending but blocked. */
+ error = verify_area(VERIFY_WRITE, set, 4);
+ if (!error)
+ put_fs_long(current->blocked & current->signal, (unsigned long *)set);
+ return error;
}
/* atomically swap in the new signal mask, and wait for a signal.
@@ -45,25 +51,72 @@ int sys_sigpending(sigset_t *set)
* "set" is just the sigmask as described in 1003.1-1988, 3.3.7.
* It is assumed that sigset_t can be passed as a 32 bit quantity.
*
- * "restart" holds a restart indication. If it's non-zero, then we
+ * "restart" holds a restart indication. If it's 1, then we
* install the old mask, and return normally. If it's zero, we store
* the current mask in old_mask and block until a signal comes in.
+ * If it's 2, then it's a signal we must handle but not return from.
+ *
+ * We are careful to prevent a rouge restart from user space from fooling
+ * us into blocking SIGKILL or SIGSTOP.
*/
-int sys_sigsuspend(int restart, unsigned long old_mask, unsigned long set)
+int sys_sigsuspend(volatile int restart, volatile unsigned long old_mask, unsigned long set)
{
- extern int sys_pause(void);
-
- if (restart) {
- /* we're restarting */
- current->blocked = old_mask;
- return -EINTR;
- }
- /* we're not restarting. do the work */
- *(&restart) = 1;
- *(&old_mask) = current->blocked;
- current->blocked = set;
- (void) sys_pause(); /* return after a signal arrives */
- return -ERESTARTNOINTR; /* handle the signal, and come back */
+ extern int sys_pause(void);
+
+ switch (restart) {
+ case 0:
+ /* we're not restarting. do the work */
+ restart = 1;
+ old_mask = current->blocked;
+ current->blocked = set & _BLOCKABLE;
+ break;
+ case 1:
+ /* we're restarting to restore and exit */
+ current->blocked = old_mask & _BLOCKABLE;
+ return -EINTR;
+ case 2:
+ /* we're restarting but staying paused */
+ restart = 1;
+ break;
+ }
+ /* pause returns after a signal arrives */
+ if (sys_pause() == -ERESTARTSYS)
+ restart = 2;
+ return -ERESTARTNOINTR; /* handle the signal, and come back */
+}
+
+/*
+ * POSIX 3.3.1.3:
+ * "Setting a signal action to SIG_IGN for a signal that is pending
+ * shall cause the pending signal to be discarded, whether or not
+ * it is blocked" (but SIGCHLD is unspecified: linux leaves it alone).
+ *
+ * "Setting a signal action to SIG_DFL for a signal that is pending
+ * and whose default action is to ignore the signal (for example,
+ * SIGCHLD), shall cause the pending signal to be discarded, whether
+ * or not it is blocked"
+ *
+ * Note the silly behaviour of SIGCHLD: SIG_IGN means that the signal
+ * isn't actually ignored, but does automatic child reaping, while
+ * SIG_DFL is explicitly said by POSIX to force the signal to be ignored..
+ */
+static void check_pending(int signum)
+{
+ struct sigaction *p;
+
+ p = signum - 1 + current->sigaction;
+ if (p->sa_handler == SIG_IGN) {
+ if (signum == SIGCHLD)
+ return;
+ current->signal &= ~_S(signum);
+ return;
+ }
+ if (p->sa_handler == SIG_DFL) {
+ if (signum != SIGCONT && signum != SIGCHLD && signum != SIGWINCH)
+ return;
+ current->signal &= ~_S(signum);
+ return;
+ }
}
int sys_signal(int signum, long handler, long restorer)
@@ -78,6 +131,7 @@ int sys_signal(int signum, long handler, long restorer)
tmp.sa_restorer = (void (*)(void)) restorer;
handler = (long) current->sigaction[signum-1].sa_handler;
current->sigaction[signum-1] = tmp;
+ check_pending(signum);
return handler;
}
@@ -93,15 +147,19 @@ int sys_sigaction(int signum, const struct sigaction * action,
memcpy_fromfs(&new, action, sizeof(struct sigaction));
if (new.sa_flags & SA_NOMASK)
new.sa_mask = 0;
- else
- new.sa_mask |= (1<<(signum-1));
+ else {
+ new.sa_mask |= _S(signum);
+ new.sa_mask &= _BLOCKABLE;
+ }
}
if (oldaction) {
- verify_area(oldaction, sizeof(struct sigaction));
- memcpy_tofs(oldaction, p, sizeof(struct sigaction));
+ if (!verify_area(VERIFY_WRITE,oldaction, sizeof(struct sigaction)))
+ memcpy_tofs(oldaction, p, sizeof(struct sigaction));
}
- if (action)
+ if (action) {
*p = new;
+ check_pending(signum);
+ }
return 0;
}
@@ -155,7 +213,7 @@ int do_signal(long signr,struct pt_regs * regs)
current->exit_code = signr;
if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags &
SA_NOCLDSTOP))
- send_sig(SIGCHLD, current->p_pptr, 1);
+ send_sig(SIGCHLD, current->p_pptr, 1);
return(1); /* Reschedule another event */
case SIGQUIT:
@@ -168,7 +226,7 @@ int do_signal(long signr,struct pt_regs * regs)
signr |= 0x80;
/* fall through */
default:
- current->signal |= 1<<((signr & 0x7f)-1);
+ current->signal |= _S(signr & 0x7f);
do_exit(signr);
}
}
@@ -181,7 +239,7 @@ int do_signal(long signr,struct pt_regs * regs)
longs = (sa->sa_flags & SA_NOMASK)?(7*4):(8*4);
regs->esp -= longs;
tmp_esp = (unsigned long *) regs->esp;
- verify_area(tmp_esp,longs);
+ 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))
diff --git a/kernel/sys.c b/kernel/sys.c
index 22d22cb..a7433b4 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -100,32 +100,32 @@ int sys_getpriority(int which, int who)
return(max_prio ? max_prio : -ESRCH);
}
-int sys_profil()
+int sys_profil(void)
{
return -ENOSYS;
}
-int sys_ftime()
+int sys_ftime(void)
{
return -ENOSYS;
}
-int sys_break()
+int sys_break(void)
{
return -ENOSYS;
}
-int sys_stty()
+int sys_stty(void)
{
return -ENOSYS;
}
-int sys_gtty()
+int sys_gtty(void)
{
return -ENOSYS;
}
-int sys_prof()
+int sys_prof(void)
{
return -ENOSYS;
}
@@ -293,38 +293,40 @@ int sys_setgid(gid_t gid)
return 0;
}
-int sys_acct()
+int sys_acct(void)
{
return -ENOSYS;
}
-int sys_phys()
+int sys_phys(void)
{
return -ENOSYS;
}
-int sys_lock()
+int sys_lock(void)
{
return -ENOSYS;
}
-int sys_mpx()
+int sys_mpx(void)
{
return -ENOSYS;
}
-int sys_ulimit()
+int sys_ulimit(void)
{
return -ENOSYS;
}
int sys_time(long * tloc)
{
- int i;
+ int i, error;
i = CURRENT_TIME;
if (tloc) {
- verify_area(tloc,4);
+ error = verify_area(VERIFY_WRITE, tloc, 4);
+ if (error)
+ return error;
put_fs_long(i,(unsigned long *)tloc);
}
return i;
@@ -403,7 +405,9 @@ int sys_stime(long * tptr)
int sys_times(struct tms * tbuf)
{
if (tbuf) {
- verify_area(tbuf,sizeof *tbuf);
+ int error = verify_area(VERIFY_WRITE,tbuf,sizeof *tbuf);
+ if (error)
+ return error;
put_fs_long(current->utime,(unsigned long *)&tbuf->tms_utime);
put_fs_long(current->stime,(unsigned long *)&tbuf->tms_stime);
put_fs_long(current->cutime,(unsigned long *)&tbuf->tms_cutime);
@@ -412,17 +416,28 @@ int sys_times(struct tms * tbuf)
return jiffies;
}
-int sys_brk(unsigned long end_data_seg)
+int sys_brk(unsigned long newbrk)
{
unsigned long rlim;
+ unsigned long oldbrk;
+ oldbrk = current->brk;
rlim = current->rlim[RLIMIT_DATA].rlim_cur;
if (rlim >= RLIM_INFINITY)
rlim = 0xffffffff;
- if (end_data_seg >= current->end_code &&
- end_data_seg-current->end_code <= rlim &&
- end_data_seg < current->start_stack - 16384)
- current->brk = end_data_seg;
+ if (newbrk >= current->end_code &&
+ newbrk - current->end_code <= rlim &&
+ newbrk < current->start_stack - 16384) {
+ current->brk = newbrk;
+ newbrk += 0x00000fff;
+ newbrk &= 0xfffff000;
+ oldbrk += 0x00000fff;
+ oldbrk &= 0xfffff000;
+ if (newbrk < oldbrk)
+ unmap_page_range(newbrk, oldbrk-newbrk);
+ else
+ zeromap_page_range(oldbrk, newbrk-oldbrk, PAGE_COPY);
+ }
return current->brk;
}
@@ -481,11 +496,13 @@ int sys_setsid(void)
*/
int sys_getgroups(int gidsetsize, gid_t *grouplist)
{
- int i;
-
- if (gidsetsize)
- verify_area(grouplist, sizeof(gid_t) * gidsetsize);
+ int i;
+ if (gidsetsize) {
+ i = verify_area(VERIFY_WRITE, grouplist, sizeof(gid_t) * gidsetsize);
+ if (i)
+ return i;
+ }
for (i = 0; (i < NGROUPS) && (current->groups[i] != NOGROUP);
i++, grouplist++) {
if (gidsetsize) {
@@ -531,18 +548,24 @@ int in_group_p(gid_t grp)
int sys_newuname(struct new_utsname * name)
{
+ int error;
+
if (!name)
return -EFAULT;
- verify_area(name, sizeof *name);
- memcpy_tofs(name,&system_utsname,sizeof *name);
- return 0;
+ error = verify_area(VERIFY_WRITE, name, sizeof *name);
+ if (!error)
+ memcpy_tofs(name,&system_utsname,sizeof *name);
+ return error;
}
int sys_uname(struct old_utsname * name)
{
+ int error;
if (!name)
- return -EINVAL;
- verify_area(name,sizeof *name);
+ return -EFAULT;
+ error = verify_area(VERIFY_WRITE, name,sizeof *name);
+ if (error)
+ return error;
memcpy_tofs(&name->sysname,&system_utsname.sysname,__OLD_UTS_LEN);
put_fs_byte(0,name->sysname+__OLD_UTS_LEN);
memcpy_tofs(&name->nodename,&system_utsname.nodename,__OLD_UTS_LEN);
@@ -577,9 +600,13 @@ int sys_sethostname(char *name, int len)
int sys_getrlimit(unsigned int resource, struct rlimit *rlim)
{
+ int error;
+
if (resource >= RLIM_NLIMITS)
return -EINVAL;
- verify_area(rlim,sizeof *rlim);
+ error = verify_area(VERIFY_WRITE,rlim,sizeof *rlim);
+ if (error)
+ return error;
put_fs_long(current->rlim[resource].rlim_cur,
(unsigned long *) rlim);
put_fs_long(current->rlim[resource].rlim_max,
@@ -612,12 +639,15 @@ int sys_setrlimit(unsigned int resource, struct rlimit *rlim)
* a lot simpler! (Which we're not doing right now because we're not
* measuring them yet).
*/
-void getrusage(struct task_struct *p, int who, struct rusage *ru)
+int getrusage(struct task_struct *p, int who, struct rusage *ru)
{
+ int error;
struct rusage r;
unsigned long *lp, *lpend, *dest;
- verify_area(ru, sizeof *ru);
+ error = verify_area(VERIFY_WRITE, ru, sizeof *ru);
+ if (error)
+ return error;
memset((char *) &r, 0, sizeof(r));
switch (who) {
case RUSAGE_SELF:
@@ -650,28 +680,34 @@ void getrusage(struct task_struct *p, int who, struct rusage *ru)
dest = (unsigned long *) ru;
for (; lp < lpend; lp++, dest++)
put_fs_long(*lp, dest);
+ return 0;
}
int sys_getrusage(int who, struct rusage *ru)
{
if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN)
return -EINVAL;
- getrusage(current, who, ru);
- return(0);
+ return getrusage(current, who, ru);
}
int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
{
+ int error;
+
if (tv) {
unsigned long nowtime = jiffies+jiffies_offset;
- verify_area(tv, sizeof *tv);
+ error = verify_area(VERIFY_WRITE, tv, sizeof *tv);
+ if (error)
+ return error;
put_fs_long(startup_time + CT_TO_SECS(nowtime),
(unsigned long *) tv);
put_fs_long(CT_TO_USECS(nowtime),
((unsigned long *) tv)+1);
}
if (tz) {
- verify_area(tz, sizeof *tz);
+ error = verify_area(VERIFY_WRITE, tz, sizeof *tz);
+ if (error)
+ return error;
put_fs_long(sys_tz.tz_minuteswest, (unsigned long *) tz);
put_fs_long(sys_tz.tz_dsttime, ((unsigned long *) tz)+1);
}
@@ -690,7 +726,7 @@ int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
int sys_settimeofday(struct timeval *tv, struct timezone *tz)
{
static int firsttime = 1;
- void adjust_clock();
+ void adjust_clock(void);
if (!suser())
return -EPERM;
@@ -731,7 +767,7 @@ int sys_settimeofday(struct timeval *tv, struct timezone *tz)
* clock at all, but get the time via NTP or timed if you're on a
* network.... - TYT, 1/1/92
*/
-void adjust_clock()
+void adjust_clock(void)
{
startup_time += sys_tz.tz_minuteswest*60;
}
diff --git a/kernel/traps.c b/kernel/traps.c
index f4076c3..0e35019 100644
--- a/kernel/traps.c
+++ b/kernel/traps.c
@@ -65,15 +65,15 @@ void alignment_check(void);
if ((esp[2] & VM_MASK) || ((0xffff & esp[1]) == 0xf))
return;
- printk("%s: %04x\n\r",str,nr&0xffff);
+ printk("%s: %04x\n",str,nr&0xffff);
printk("EIP: %04x:%p\nEFLAGS: %p\n", 0xffff & esp[1],esp[0],esp[2]);
printk("fs: %04x\n",_fs());
printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17));
store_TR(i);
- printk("Pid: %d, process nr: %d\n\r",current->pid,0xffff & i);
+ printk("Pid: %d, process nr: %d\n",current->pid,0xffff & i);
for(i=0;i<10;i++)
printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0])));
- printk("\n\r");
+ printk("\n");
do_exit(SIGSEGV);
}
diff --git a/mm/memory.c b/mm/memory.c
index c9b973a..899869c 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -80,12 +80,11 @@ static void free_one_table(unsigned long * page_dir)
if (!pg_table)
return;
- if (pg_table >= high_memory || !(pg_table & 1)) {
+ *page_dir = 0;
+ if (pg_table >= high_memory || !(pg_table & PAGE_PRESENT)) {
printk("Bad page table: [%08x]=%08x\n",page_dir,pg_table);
- *page_dir = 0;
return;
}
- *page_dir = 0;
if (mem_map[MAP_NR(pg_table)] & MAP_PAGE_RESERVED)
return;
page_table = (unsigned long *) (pg_table & 0xfffff000);
@@ -95,10 +94,10 @@ static void free_one_table(unsigned long * page_dir)
if (!pg)
continue;
*page_table = 0;
- if (1 & pg)
+ if (pg & PAGE_PRESENT)
free_page(0xfffff000 & pg);
else
- swap_free(pg >> 1);
+ swap_free(pg);
}
free_page(0xfffff000 & pg_table);
}
@@ -118,9 +117,9 @@ void clear_page_tables(struct task_struct * tsk)
if (!tsk)
return;
if (tsk == task[0])
- panic("task[0] (swapper) doesn't support exec() yet\n");
+ panic("task[0] (swapper) doesn't support exec()\n");
page_dir = (unsigned long *) tsk->tss.cr3;
- if (!page_dir) {
+ if (!page_dir || page_dir == swapper_pg_dir) {
printk("Trying to clear kernel page-directory: not good\n");
return;
}
@@ -146,7 +145,7 @@ void free_page_tables(struct task_struct * tsk)
panic("Trying to free up swapper memory space");
}
pg_dir = tsk->tss.cr3;
- if (!pg_dir) {
+ if (!pg_dir || pg_dir == (unsigned long) swapper_pg_dir) {
printk("Trying to free kernel page-directory: not good\n");
return;
}
@@ -186,7 +185,7 @@ int copy_page_tables(struct task_struct * tsk)
old_pg_table = *old_page_dir;
if (!old_pg_table)
continue;
- if (old_pg_table >= high_memory || !(1 & old_pg_table)) {
+ if (old_pg_table >= high_memory || !(old_pg_table & PAGE_PRESENT)) {
printk("copy_page_tables: bad page table: "
"probable memory corruption");
*old_page_dir = 0;
@@ -201,7 +200,7 @@ int copy_page_tables(struct task_struct * tsk)
free_page_tables(tsk);
return -ENOMEM;
}
- *new_page_dir = new_pg_table | PAGE_ACCESSED | 7;
+ *new_page_dir = new_pg_table | PAGE_TABLE;
old_page_table = (unsigned long *) (0xfffff000 & old_pg_table);
new_page_table = (unsigned long *) (0xfffff000 & new_pg_table);
for (j = 0 ; j < 1024 ; j++,old_page_table++,new_page_table++) {
@@ -210,11 +209,11 @@ int copy_page_tables(struct task_struct * tsk)
if (!pg)
continue;
if (!(pg & PAGE_PRESENT)) {
- swap_duplicate(pg>>1);
- *new_page_table = pg;
+ *new_page_table = swap_duplicate(pg);
continue;
}
- pg &= ~2;
+ if ((pg & (PAGE_RW | PAGE_COW)) == (PAGE_RW | PAGE_COW))
+ pg &= ~PAGE_RW;
*new_page_table = pg;
if (mem_map[MAP_NR(pg)] & MAP_PAGE_RESERVED)
continue;
@@ -236,10 +235,10 @@ int unmap_page_range(unsigned long from, unsigned long size)
unsigned long *page_table, *dir;
unsigned long poff, pcnt, pc;
- if (from & 0xfff)
- panic("unmap_page_range called with wrong alignment");
- if (!from)
- panic("unmap_page_range trying to free swapper memory space");
+ if (from & 0xfff) {
+ printk("unmap_page_range called with wrong alignment\n");
+ return -EINVAL;
+ }
size = (size + 0xfff) >> PAGE_SHIFT;
dir = (unsigned long *) (current->tss.cr3 + ((from >> 20) & 0xffc));
poff = (from >> PAGE_SHIFT) & 0x3ff;
@@ -252,7 +251,7 @@ int unmap_page_range(unsigned long from, unsigned long size)
poff = 0;
continue;
}
- if (!(page_dir & 1)) {
+ if (!(page_dir & PAGE_PRESENT)) {
printk("unmap_page_range: bad page directory.");
continue;
}
@@ -268,7 +267,7 @@ int unmap_page_range(unsigned long from, unsigned long size)
--current->rss;
free_page(0xfffff000 & page);
} else
- swap_free(page >> 1);
+ swap_free(page);
}
}
if (pcnt == 1024) {
@@ -280,29 +279,84 @@ int unmap_page_range(unsigned long from, unsigned long size)
return 0;
}
+int zeromap_page_range(unsigned long from, unsigned long size, int mask)
+{
+ unsigned long *page_table, *dir;
+ unsigned long poff, pcnt;
+ unsigned long page;
+
+ if (mask) {
+ if ((mask & 0xfffff001) != PAGE_PRESENT) {
+ printk("zeromap_page_range: mask = %08x\n",mask);
+ return -EINVAL;
+ }
+ mask |= ZERO_PAGE;
+ }
+ if (from & 0xfff) {
+ printk("zeromap_page_range: from = %08x\n",from);
+ return -EINVAL;
+ }
+ dir = (unsigned long *) (current->tss.cr3 + ((from >> 20) & 0xffc));
+ size = (size + 0xfff) >> PAGE_SHIFT;
+ poff = (from >> PAGE_SHIFT) & 0x3ff;
+ if ((pcnt = 1024 - poff) > size)
+ pcnt = size;
+
+ while (size > 0) {
+ if (!(PAGE_PRESENT & *dir)) {
+ if (!(page_table = (unsigned long *)get_free_page(GFP_KERNEL))) {
+ invalidate();
+ return -ENOMEM;
+ }
+ if (PAGE_PRESENT & *dir) {
+ free_page((unsigned long) page_table);
+ page_table = (unsigned long *)(0xfffff000 & *dir++);
+ } else
+ *dir++ = ((unsigned long) page_table) | PAGE_TABLE;
+ } else
+ page_table = (unsigned long *)(0xfffff000 & *dir++);
+ page_table += poff;
+ poff = 0;
+ for (size -= pcnt; pcnt-- ;) {
+ if ((page = *page_table) != 0) {
+ *page_table = 0;
+ if (page & PAGE_PRESENT) {
+ --current->rss;
+ free_page(0xfffff000 & page);
+ } else
+ swap_free(page);
+ }
+ if (mask)
+ ++current->rss;
+ *page_table++ = mask;
+ }
+ pcnt = (size > 1024 ? 1024 : size);
+ }
+ invalidate();
+ return 0;
+}
+
/*
* maps a range of physical memory into the requested pages. the old
* mappings are removed. any references to nonexistent pages results
* in null mappings (currently treated as "copy-on-access")
- *
- * permiss is encoded as cxwr (copy,exec,write,read) where copy modifies
- * the behavior of write to be copy-on-write.
- *
- * due to current limitations, we actually have the following
- * on off
- * read: yes yes
- * write/copy: yes/copy copy/copy
- * exec: yes yes
*/
-int remap_page_range(unsigned long from, unsigned long to, unsigned long size,
- int permiss)
+int remap_page_range(unsigned long from, unsigned long to, unsigned long size, int mask)
{
unsigned long *page_table, *dir;
unsigned long poff, pcnt;
unsigned long page;
- if ((from & 0xfff) || (to & 0xfff))
- panic("remap_page_range called with wrong alignment");
+ if (mask) {
+ if ((mask & 0xfffff001) != PAGE_PRESENT) {
+ printk("remap_page_range: mask = %08x\n",mask);
+ return -EINVAL;
+ }
+ }
+ if ((from & 0xfff) || (to & 0xfff)) {
+ printk("remap_page_range: from = %08x, to=%08x\n",from,to);
+ return -EINVAL;
+ }
dir = (unsigned long *) (current->tss.cr3 + ((from >> 20) & 0xffc));
size = (size + 0xfff) >> PAGE_SHIFT;
poff = (from >> PAGE_SHIFT) & 0x3ff;
@@ -310,12 +364,12 @@ int remap_page_range(unsigned long from, unsigned long to, unsigned long size,
pcnt = size;
while (size > 0) {
- if (!(1 & *dir)) {
+ if (!(PAGE_PRESENT & *dir)) {
if (!(page_table = (unsigned long *)get_free_page(GFP_KERNEL))) {
invalidate();
return -1;
}
- *dir++ = ((unsigned long) page_table) | PAGE_ACCESSED | 7;
+ *dir++ = ((unsigned long) page_table) | PAGE_TABLE;
}
else
page_table = (unsigned long *)(0xfffff000 & *dir++);
@@ -325,27 +379,13 @@ int remap_page_range(unsigned long from, unsigned long to, unsigned long size,
}
for (size -= pcnt; pcnt-- ;) {
- int mask;
-
- mask = 4;
- if (permiss & 1)
- mask |= 1;
- if (permiss & 2) {
- if (permiss & 8)
- mask |= 1;
- else
- mask |= 3;
- }
- if (permiss & 4)
- mask |= 1;
-
if ((page = *page_table) != 0) {
*page_table = 0;
- --current->rss;
- if (1 & page)
+ if (PAGE_PRESENT & page) {
+ --current->rss;
free_page(0xfffff000 & page);
- else
- swap_free(page >> 1);
+ } else
+ swap_free(page);
}
/*
@@ -355,7 +395,7 @@ int remap_page_range(unsigned long from, unsigned long to, unsigned long size,
* when the page is referenced. current assumptions
* cause it to be treated as demand allocation.
*/
- if (mask == 4 || to >= high_memory || !mem_map[MAP_NR(to)])
+ if (!mask || to >= high_memory || !mem_map[MAP_NR(to)])
*page_table++ = 0; /* not present */
else {
++current->rss;
@@ -378,12 +418,15 @@ int remap_page_range(unsigned long from, unsigned long to, unsigned long size,
* page.)
* if wp = 1 the page will be write protected
*/
-static unsigned long put_page(struct task_struct * tsk,unsigned long page,unsigned long address,int wp)
+static unsigned long put_page(struct task_struct * tsk,unsigned long page,
+ unsigned long address,int prot)
{
unsigned long tmp, *page_table;
/* NOTE !!! This uses the fact that _pg_dir=0 */
+ if ((prot & 0xfffff001) != PAGE_PRESENT)
+ printk("put_page: prot = %08x\n",prot);
if (page >= high_memory) {
printk("put_page: trying to put page %p at %p\n",page,address);
return 0;
@@ -399,7 +442,7 @@ static unsigned long put_page(struct task_struct * tsk,unsigned long page,unsign
else {
printk("put_page: bad page directory entry\n");
oom(tsk);
- *page_table = BAD_PAGETABLE | PAGE_ACCESSED | 7;
+ *page_table = BAD_PAGETABLE | PAGE_TABLE;
return 0;
}
page_table += (address >> PAGE_SHIFT) & 0x3ff;
@@ -408,7 +451,7 @@ static unsigned long put_page(struct task_struct * tsk,unsigned long page,unsign
*page_table = 0;
invalidate();
}
- *page_table = page | PAGE_ACCESSED | 5 | (!wp << 1);
+ *page_table = page | prot;
/* no need for invalidate */
return page;
}
@@ -423,19 +466,17 @@ unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsig
{
unsigned long tmp, *page_table;
-/* NOTE !!! This uses the fact that _pg_dir=0 */
-
if (page >= high_memory)
printk("put_dirty_page: trying to put page %p at %p\n",page,address);
if (mem_map[MAP_NR(page)] != 1)
printk("mem_map disagrees with %p at %p\n",page,address);
page_table = (unsigned long *) (tsk->tss.cr3 + ((address>>20) & 0xffc));
- if ((*page_table)&1)
+ if ((*page_table)&PAGE_PRESENT)
page_table = (unsigned long *) (0xfffff000 & *page_table);
else {
if (!(tmp=get_free_page(GFP_KERNEL)))
return 0;
- *page_table = tmp|7;
+ *page_table = tmp | PAGE_TABLE;
page_table = (unsigned long *) tmp;
}
page_table += (address >> PAGE_SHIFT) & 0x3ff;
@@ -444,7 +485,7 @@ unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsig
*page_table = 0;
invalidate();
}
- *page_table = page | (PAGE_DIRTY | PAGE_ACCESSED | 7);
+ *page_table = page | (PAGE_DIRTY | PAGE_PRIVATE);
/* no need for invalidate */
return page;
}
@@ -453,26 +494,14 @@ unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsig
* This routine handles present pages, when users try to write
* to a shared page. It is done by copying the page to a new address
* and decrementing the shared-page counter for the old page.
- *
- * Fixed the routine to repeat a bit more: this is slightly slower,
- * but there were race-conditions in the old code..
*/
void do_wp_page(unsigned long error_code, unsigned long address,
struct task_struct * tsk, unsigned long user_esp)
{
- unsigned long pde, pte, old_page, dirty;
- unsigned long new_page = 0;
+ unsigned long pde, pte, old_page, prot;
+ unsigned long new_page;
- /* check code space write */
- if (tsk == current && tsk->executable && CODE_SPACE(address, current)) {
- /* don't send SIGSEGV when in kernel or v86 mode */
- if (user_esp)
- send_sig(SIGSEGV, tsk, 1);
- /* Note that we still do the copy-on-write: if the process catches
- * SIGSEGV we want things to work..
- */
- }
-repeat:
+ new_page = __get_free_page(GFP_KERNEL);
pde = tsk->tss.cr3 + ((address>>20) & 0xffc);
pte = *(unsigned long *) pde;
if (!(pte & PAGE_PRESENT)) {
@@ -480,9 +509,9 @@ repeat:
free_page(new_page);
return;
}
- if ((pte & 7) != 7 || pte >= high_memory) {
+ if ((pte & PAGE_TABLE) != PAGE_TABLE || pte >= high_memory) {
printk("do_wp_page: bogus page-table at address %08x (%08x)\n",address,pte);
- *(unsigned long *) pde = BAD_PAGETABLE | 7;
+ *(unsigned long *) pde = BAD_PAGETABLE | PAGE_TABLE;
send_sig(SIGKILL, tsk, 1);
if (new_page)
free_page(new_page);
@@ -498,7 +527,7 @@ repeat:
}
if (old_page >= high_memory) {
printk("do_wp_page: bogus page at address %08x (%08x)\n",address,old_page);
- *(unsigned long *) pte = BAD_PAGE | 7;
+ *(unsigned long *) pte = BAD_PAGE | PAGE_SHARED;
send_sig(SIGKILL, tsk, 1);
if (new_page)
free_page(new_page);
@@ -509,8 +538,12 @@ repeat:
free_page(new_page);
return;
}
+ if (!(old_page & PAGE_COW)) {
+ if (user_esp && tsk == current)
+ send_sig(SIGSEGV, tsk, 1);
+ }
tsk->min_flt++;
- dirty = old_page & PAGE_DIRTY;
+ prot = (old_page & 0x00000fff) | PAGE_RW;
old_page &= 0xfffff000;
if (mem_map[MAP_NR(old_page)]==1) {
*(unsigned long *) pte |= 2;
@@ -519,15 +552,13 @@ repeat:
free_page(new_page);
return;
}
- if (!new_page && (new_page=get_free_page(GFP_KERNEL)))
- goto repeat;
if (new_page)
copy_page(old_page,new_page);
else {
new_page = BAD_PAGE;
oom(tsk);
}
- *(unsigned long *) pte = new_page | dirty | PAGE_ACCESSED | 7;
+ *(unsigned long *) pte = new_page | prot;
free_page(old_page);
invalidate();
}
@@ -547,7 +578,7 @@ static void get_empty_page(struct task_struct * tsk, unsigned long address)
oom(tsk);
tmp = BAD_PAGE;
}
- if (!put_page(tsk,tmp,address,0))
+ if (!put_page(tsk,tmp,address,PAGE_PRIVATE))
free_page(tmp);
}
@@ -560,50 +591,50 @@ static void get_empty_page(struct task_struct * tsk, unsigned long address)
* share the same executable or library.
*/
static int try_to_share(unsigned long address, struct task_struct * tsk,
- struct task_struct * p)
+ struct task_struct * p, unsigned long error_code, unsigned long newpage)
{
unsigned long from;
unsigned long to;
unsigned long from_page;
unsigned long to_page;
- unsigned long phys_addr;
from_page = p->tss.cr3 + ((address>>20) & 0xffc);
to_page = tsk->tss.cr3 + ((address>>20) & 0xffc);
/* is there a page-directory at from? */
from = *(unsigned long *) from_page;
- if (!(from & 1))
+ if (!(from & PAGE_PRESENT))
return 0;
from &= 0xfffff000;
from_page = from + ((address>>10) & 0xffc);
- phys_addr = *(unsigned long *) from_page;
+ from = *(unsigned long *) from_page;
/* is the page clean and present? */
- if ((phys_addr & 0x41) != 0x01)
+ if ((from & (PAGE_PRESENT | PAGE_DIRTY)) != PAGE_PRESENT)
return 0;
- phys_addr &= 0xfffff000;
- if (phys_addr >= high_memory)
+ if (from >= high_memory)
return 0;
- if (mem_map[MAP_NR(phys_addr)] & MAP_PAGE_RESERVED)
+ if (mem_map[MAP_NR(from)] & MAP_PAGE_RESERVED)
return 0;
-/* share them: write-protect */
- *(unsigned long *) from_page &= ~2;
- invalidate();
- phys_addr >>= PAGE_SHIFT;
- mem_map[phys_addr]++;
+/* is the destination ok? */
to = *(unsigned long *) to_page;
- if (!(to & 1)) {
- to = get_free_page(GFP_KERNEL);
- if (!to) {
- mem_map[phys_addr]--;
- return 0;
- }
- *(unsigned long *) to_page = to | PAGE_ACCESSED | 7;
- }
+ if (!(to & PAGE_PRESENT))
+ return 0;
to &= 0xfffff000;
to_page = to + ((address>>10) & 0xffc);
- if (1 & *(unsigned long *) to_page)
- panic("try_to_share: to_page already exists");
- *(unsigned long *) to_page = *(unsigned long *) from_page;
+ if (*(unsigned long *) to_page)
+ return 0;
+/* share them if read - do COW immediately otherwise */
+ if (error_code & PAGE_RW) {
+ copy_page((from & 0xfffff000),newpage);
+ to = newpage | PAGE_PRIVATE;
+ } else {
+ mem_map[MAP_NR(from)]++;
+ from &= ~PAGE_RW;
+ to = from;
+ free_page(newpage);
+ }
+ *(unsigned long *) from_page = from;
+ *(unsigned long *) to_page = to;
+ invalidate();
return 1;
}
@@ -615,7 +646,8 @@ static int try_to_share(unsigned long address, struct task_struct * tsk,
* We first check if it is at all feasible by checking executable->i_count.
* It should be >1 if there are other tasks sharing this inode.
*/
-static int share_page(struct task_struct * tsk, struct inode * inode, unsigned long address)
+static int share_page(struct task_struct * tsk, struct inode * inode,
+ unsigned long address, unsigned long error_code, unsigned long newpage)
{
struct task_struct ** p;
int i;
@@ -634,7 +666,7 @@ static int share_page(struct task_struct * tsk, struct inode * inode, unsigned l
if (i >= (*p)->numlibraries)
continue;
}
- if (try_to_share(address,tsk,*p))
+ if (try_to_share(address,tsk,*p,error_code,newpage))
return 1;
}
return 0;
@@ -649,7 +681,7 @@ static unsigned long get_empty_pgtable(struct task_struct * tsk,unsigned long ad
unsigned long *p;
repeat:
p = (unsigned long *) (tsk->tss.cr3 + ((address >> 20) & 0xffc));
- if (1 & *p) {
+ if (PAGE_PRESENT & *p) {
free_page(page);
return *p;
}
@@ -658,20 +690,20 @@ repeat:
*p = 0;
}
if (page) {
- *p = page | PAGE_ACCESSED | 7;
+ *p = page | PAGE_TABLE;
return *p;
}
if ((page = get_free_page(GFP_KERNEL)) != 0)
goto repeat;
oom(current);
- *p = BAD_PAGETABLE | 7;
+ *p = BAD_PAGETABLE | PAGE_TABLE;
return 0;
}
void do_no_page(unsigned long error_code, unsigned long address,
struct task_struct *tsk, unsigned long user_esp)
{
- int nr[4];
+ int nr[8], prot;
unsigned long tmp;
unsigned long page;
unsigned int block,i;
@@ -683,7 +715,7 @@ void do_no_page(unsigned long error_code, unsigned long address,
page &= 0xfffff000;
page += (address >> 10) & 0xffc;
tmp = *(unsigned long *) page;
- if (tmp & 1)
+ if (tmp & PAGE_PRESENT)
return;
++tsk->rss;
if (tmp) {
@@ -725,26 +757,27 @@ void do_no_page(unsigned long error_code, unsigned long address,
send_sig(SIGSEGV,tsk,1);
return;
}
- if (share_page(tsk,inode,address)) {
+ page = get_free_page(GFP_KERNEL);
+ if (share_page(tsk,inode,address,error_code,page)) {
++tsk->min_flt;
return;
}
++tsk->maj_flt;
- page = get_free_page(GFP_KERNEL);
if (!page) {
oom(current);
- put_page(tsk,BAD_PAGE,address,0);
+ put_page(tsk,BAD_PAGE,address,PAGE_PRIVATE);
return;
}
+ prot = PAGE_PRIVATE;
+ if (CODE_SPACE(address, tsk))
+ prot = PAGE_READONLY;
if (block) {
for (i=0 ; i<4 ; block++,i++)
nr[i] = bmap(inode,block);
- bread_page(page,inode->i_dev,nr);
+ page = bread_page(page,inode->i_dev,nr,1024,prot);
}
- if (share_page(tsk,inode,address)) {
- free_page(page);
+ if (!(error_code & PAGE_RW) && share_page(tsk,inode,address, error_code,page))
return;
- }
i = address + PAGE_SIZE - tsk->end_data;
if (i > PAGE_SIZE-1)
i = 0;
@@ -753,7 +786,7 @@ void do_no_page(unsigned long error_code, unsigned long address,
tmp--;
*(char *)tmp = 0;
}
- if (put_page(tsk,page,address,CODE_SPACE(address, tsk)))
+ if (put_page(tsk,page,address,prot))
return;
free_page(page);
oom(current);
@@ -770,7 +803,7 @@ void do_page_fault(unsigned long *esp, unsigned long error_code)
unsigned long user_esp = 0;
unsigned long stack_limit;
unsigned int bit;
- extern void die_if_kernel();
+ extern void die_if_kernel(char *,long,long);
/* get the address */
__asm__("movl %%cr2,%0":"=r" (address));
@@ -800,7 +833,7 @@ void do_page_fault(unsigned long *esp, unsigned long error_code)
return;
}
printk("Unable to handle kernel paging request at address %08x\n",address);
- die_if_kernel("Oops",esp,error_code);
+ die_if_kernel("Oops",(long)esp,error_code);
do_exit(SIGKILL);
}
@@ -813,13 +846,16 @@ void do_page_fault(unsigned long *esp, unsigned long error_code)
*
* BAD_PAGETABLE is the accompanying page-table: it is initialized
* to point to BAD_PAGE entries.
+ *
+ * ZERO_PAGE is a special page that is used for zero-initialized
+ * data and COW.
*/
unsigned long __bad_pagetable(void)
{
extern char empty_bad_page_table[PAGE_SIZE];
__asm__ __volatile__("cld ; rep ; stosl"
- ::"a" (7+BAD_PAGE),
+ ::"a" (BAD_PAGE + PAGE_TABLE),
"D" ((long) empty_bad_page_table),
"c" (1024)
:"di","cx");
@@ -838,6 +874,18 @@ unsigned long __bad_page(void)
return (unsigned long) empty_bad_page;
}
+unsigned long __zero_page(void)
+{
+ extern char empty_zero_page[PAGE_SIZE];
+
+ __asm__ __volatile__("cld ; rep ; stosl"
+ ::"a" (0),
+ "D" ((long) empty_zero_page),
+ "c" (1024)
+ :"di","cx");
+ return (unsigned long) empty_zero_page;
+}
+
void show_mem(void)
{
int i,free = 0,total = 0,reserved = 0;
@@ -892,14 +940,14 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
tmp = *pg_dir;
if (!tmp) {
tmp = start_mem;
- *pg_dir = tmp | 7;
+ *pg_dir = tmp | PAGE_TABLE;
start_mem += 4096;
}
pg_dir++;
pg_table = (unsigned long *) (tmp & 0xfffff000);
for (tmp = 0 ; tmp < 1024 ; tmp++,pg_table++) {
if (address && address < end_mem)
- *pg_table = 7 + address;
+ *pg_table = address | PAGE_SHARED;
else
*pg_table = 0;
address += 4096;
diff --git a/mm/mmap.c b/mm/mmap.c
index 9776cb5..79f0e0f 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -29,67 +29,17 @@
* w: (no) copy w: (no) copy w: (copy) copy w: (no) no
* x: (no) no x: (no) no x: (no) no x: (yes) no
*
- * the permissions are encoded as cxwr (copy,exec,write,read)
*/
-#define MTYP(T) ((T) & MAP_TYPE)
-#define PREAD(T,P) (((P) & PROT_READ) ? 1 : 0)
-#define PWRITE(T,P) (((P) & PROT_WRITE) ? (MTYP(T) == MAP_SHARED ? 2 : 10) : 0)
-#define PEXEC(T,P) (((P) & PROT_EXEC) ? 4 : 0)
-#define PERMISS(T,P) (PREAD(T,P)|PWRITE(T,P)|PEXEC(T,P))
#define CODE_SPACE(addr) ((((addr)+4095)&~4095) < \
current->start_code + current->end_code)
-static caddr_t
-mmap_chr(unsigned long addr, size_t len, int prot, int flags,
- struct inode *inode, unsigned long off)
-{
- int major, minor;
-
- major = MAJOR(inode->i_rdev);
- minor = MINOR(inode->i_rdev);
-
- /*
- * for character devices, only /dev/[k]mem may be mapped. when the
- * swapping code is modified to allow arbitrary sources of pages,
- * then we can open it up to regular files.
- */
-
- if (major != 1 || (minor != 1 && minor != 2))
- return (caddr_t)-ENODEV;
-
- /*
- * we only allow mappings from address 0 to high_memory, since thats
- * the range of our memory [actually this is a lie. the buffer cache
- * and ramdisk occupy higher memory, but the paging stuff won't
- * let us map to it anyway, so we break it here].
- *
- * this call is very dangerous! because of the lack of adequate
- * tagging of frames, it is possible to mmap over a frame belonging
- * to another (innocent) process. with MAP_SHARED|MAP_WRITE, this
- * rogue process can trample over the other's data! we ignore this :{
- * for now, we hope people will malloc the required amount of space,
- * then mmap over it. the mm needs serious work before this can be
- * truly useful.
- */
-
- if (len > high_memory || off > high_memory - len) /* avoid overflow */
- return (caddr_t)-ENXIO;
-
- if (remap_page_range(addr, off, len, PERMISS(flags, prot)))
- return (caddr_t)-EAGAIN;
-
- return (caddr_t)addr;
-}
-
-caddr_t
-sys_mmap(unsigned long *buffer)
+int sys_mmap(unsigned long *buffer)
{
unsigned long base, addr;
unsigned long len, limit, off;
- int prot, flags, fd;
+ int prot, flags, mask, fd, error;
struct file *file;
- struct inode *inode;
addr = (unsigned long) get_fs_long(buffer); /* user address space*/
len = (size_t) get_fs_long(buffer+1); /* nbytes of mapping */
@@ -99,10 +49,9 @@ sys_mmap(unsigned long *buffer)
off = (unsigned long) get_fs_long(buffer+5); /* offset in object */
if (fd >= NR_OPEN || fd < 0 || !(file = current->filp[fd]))
- return (caddr_t) -EBADF;
- if (addr > TASK_SIZE || (addr+(unsigned long) len) > TASK_SIZE)
- return (caddr_t) -EINVAL;
- inode = file->f_inode;
+ return -EBADF;
+ if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len)
+ return -EINVAL;
/*
* do simple checking here so the lower-level routines won't have
@@ -113,15 +62,15 @@ sys_mmap(unsigned long *buffer)
switch (flags & MAP_TYPE) {
case MAP_SHARED:
if ((prot & PROT_WRITE) && !(file->f_mode & 2))
- return (caddr_t)-EINVAL;
+ return -EINVAL;
/* fall through */
case MAP_PRIVATE:
if (!(file->f_mode & 1))
- return (caddr_t)-EINVAL;
+ return -EINVAL;
break;
default:
- return (caddr_t)-EINVAL;
+ return -EINVAL;
}
/*
@@ -144,9 +93,9 @@ sys_mmap(unsigned long *buffer)
*/
if ((addr & 0xfff) || addr > 0x7fffffff || addr == 0 ||
(off & 0xfff))
- return (caddr_t)-EINVAL;
+ return -EINVAL;
if (addr + len > limit)
- return (caddr_t)-ENOMEM;
+ return -ENOMEM;
} else {
/*
* we're given a hint as to where to put the address.
@@ -159,7 +108,7 @@ sys_mmap(unsigned long *buffer)
* the stack will cause stack overflow. because of this
* we don't allow nonspecified mappings...
*/
- return (caddr_t)-ENOMEM;
+ return -ENOMEM;
}
/*
@@ -167,15 +116,23 @@ sys_mmap(unsigned long *buffer)
* specific mapper. the address has already been validated, but
* not unmapped
*/
- if (S_ISCHR(inode->i_mode))
- addr = (unsigned long)mmap_chr(base + addr, len, prot, flags,
- inode, off);
- else
- addr = (unsigned long)-ENODEV;
- if ((long)addr > 0)
- addr -= base;
-
- return (caddr_t)addr;
+ if (!file->f_op || !file->f_op->mmap)
+ return -ENODEV;
+ mask = 0;
+ if (prot & (PROT_READ | PROT_EXEC))
+ mask |= PAGE_READONLY;
+ if (prot & PROT_WRITE)
+ mask |= PAGE_RW;
+ if (!mask)
+ return -EINVAL;
+ if ((flags & MAP_TYPE) == MAP_PRIVATE) {
+ mask |= PAGE_COW;
+ mask &= ~PAGE_RW;
+ }
+ error = file->f_op->mmap(file->f_inode, file, base + addr, len, mask, off);
+ if (error)
+ return error;
+ return addr;
}
int sys_munmap(unsigned long addr, size_t len)
diff --git a/mm/swap.c b/mm/swap.c
index ed441f1..39272f6 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -23,6 +23,10 @@
#define SWP_USED 1
#define SWP_WRITEOK 3
+#define SWP_TYPE(entry) (((entry) & 0xffe) >> 1)
+#define SWP_OFFSET(entry) ((entry) >> PAGE_SHIFT)
+#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << PAGE_SHIFT))
+
static int nr_swapfiles = 0;
static struct wait_queue * lock_queue = NULL;
@@ -61,17 +65,19 @@ bitop(bit,"")
bitop(setbit,"s")
bitop(clrbit,"r")
-void rw_swap_page(int rw, unsigned int nr, char * buf)
+void rw_swap_page(int rw, unsigned long entry, char * buf)
{
+ unsigned long type, offset;
struct swap_info_struct * p;
- if ((nr >> 24) >= nr_swapfiles) {
+ type = SWP_TYPE(entry);
+ if (type >= nr_swapfiles) {
printk("Internal error: bad swap-device\n");
return;
}
- p = swap_info + (nr >> 24);
- nr &= 0x00ffffff;
- if (nr >= SWAP_BITS) {
+ p = &swap_info[type];
+ offset = SWP_OFFSET(entry);
+ if (offset >= SWAP_BITS) {
printk("rw_swap_page: weirdness\n");
return;
}
@@ -79,13 +85,13 @@ void rw_swap_page(int rw, unsigned int nr, char * buf)
printk("Trying to swap to unused swap-device\n");
return;
}
- while (setbit(p->swap_lockmap,nr))
+ while (setbit(p->swap_lockmap,offset))
sleep_on(&lock_queue);
if (p->swap_device) {
- ll_rw_page(rw,p->swap_device,nr,buf);
+ ll_rw_page(rw,p->swap_device,offset,buf);
} else if (p->swap_file) {
unsigned int zones[4];
- unsigned int block = nr << 2;
+ unsigned int block = offset << 2;
int i;
for (i = 0; i < 4; i++)
@@ -96,7 +102,7 @@ void rw_swap_page(int rw, unsigned int nr, char * buf)
ll_rw_swap_file(rw,p->swap_file->i_dev, zones,4,buf);
} else
printk("re_swap_page: no swap file or device\n");
- if (!clrbit(p->swap_lockmap,nr))
+ if (!clrbit(p->swap_lockmap,offset))
printk("rw_swap_page: lock already cleared\n");
wake_up(&lock_queue);
}
@@ -104,61 +110,66 @@ void rw_swap_page(int rw, unsigned int nr, char * buf)
static unsigned int get_swap_page(void)
{
struct swap_info_struct * p;
- unsigned int block_nr, swap_nr;
+ unsigned int offset, type;
p = swap_info;
- for (swap_nr = 0 ; swap_nr < nr_swapfiles ; swap_nr++,p++) {
+ for (type = 0 ; type < nr_swapfiles ; type++,p++) {
if ((p->flags & SWP_WRITEOK) != SWP_WRITEOK)
continue;
- for (block_nr = p->lowest_bit; block_nr <= p->highest_bit ; block_nr++) {
- if (p->swap_map[block_nr])
+ for (offset = p->lowest_bit; offset <= p->highest_bit ; offset++) {
+ if (p->swap_map[offset])
continue;
- p->swap_map[block_nr] = 1;
- if (block_nr == p->highest_bit)
+ p->swap_map[offset] = 1;
+ if (offset == p->highest_bit)
p->highest_bit--;
- p->lowest_bit = block_nr;
- return block_nr + (swap_nr << 24);
+ p->lowest_bit = offset;
+ return SWP_ENTRY(type,offset);
}
}
return 0;
}
-void swap_duplicate(unsigned int nr)
+unsigned long swap_duplicate(unsigned long entry)
{
struct swap_info_struct * p;
+ unsigned long offset, type;
- if (!nr)
- return;
- if ((nr >> 24) >= nr_swapfiles) {
- printk("Trying to free nonexistent swap-page\n");
- return;
+ if (!entry)
+ return 0;
+ offset = SWP_OFFSET(entry);
+ type = SWP_TYPE(entry);
+ if (type >= nr_swapfiles) {
+ printk("Trying to duplicate nonexistent swap-page\n");
+ return 0;
}
- p = (nr >> 24) + swap_info;
- nr &= 0x00ffffff;
- if (nr >= SWAP_BITS) {
+ p = type + swap_info;
+ if (offset >= SWAP_BITS) {
printk("swap_free: weirness\n");
- return;
+ return 0;
}
- if (!p->swap_map[nr]) {
+ if (!p->swap_map[offset]) {
printk("swap_duplicate: trying to duplicate unused page\n");
- return;
+ return 0;
}
- p->swap_map[nr]++;
+ p->swap_map[offset]++;
+ return entry;
}
-void swap_free(unsigned int nr)
+void swap_free(unsigned long entry)
{
struct swap_info_struct * p;
+ unsigned long offset, type;
- if (!nr)
+ if (!entry)
return;
- if ((nr >> 24) >= nr_swapfiles) {
+ type = SWP_TYPE(entry);
+ if (type >= nr_swapfiles) {
printk("Trying to free nonexistent swap-page\n");
return;
}
- p = (nr >> 24) + swap_info;
- nr &= 0x00ffffff;
- if (nr >= SWAP_BITS) {
+ p = & swap_info[type];
+ offset = SWP_OFFSET(entry);
+ if (offset >= SWAP_BITS) {
printk("swap_free: weirness\n");
return;
}
@@ -166,33 +177,33 @@ void swap_free(unsigned int nr)
printk("Trying to free swap from unused swap-device\n");
return;
}
- while (setbit(p->swap_lockmap,nr))
+ while (setbit(p->swap_lockmap,offset))
sleep_on(&lock_queue);
- if (nr < p->lowest_bit)
- p->lowest_bit = nr;
- if (nr > p->highest_bit)
- p->highest_bit = nr;
- if (!p->swap_map[nr])
- printk("swap_free: swap-space map bad (page %d)\n",nr);
+ if (offset < p->lowest_bit)
+ p->lowest_bit = offset;
+ if (offset > p->highest_bit)
+ p->highest_bit = offset;
+ if (!p->swap_map[offset])
+ printk("swap_free: swap-space map bad (entry %08x)\n",entry);
else
- p->swap_map[nr]--;
- if (!clrbit(p->swap_lockmap,nr))
+ p->swap_map[offset]--;
+ if (!clrbit(p->swap_lockmap,offset))
printk("swap_free: lock already cleared\n");
wake_up(&lock_queue);
}
void swap_in(unsigned long *table_ptr)
{
- unsigned long swap_nr;
+ unsigned long entry;
unsigned long page;
- swap_nr = *table_ptr;
- if (1 & swap_nr) {
- printk("trying to swap in present page\n\r");
+ entry = *table_ptr;
+ if (PAGE_PRESENT & entry) {
+ printk("trying to swap in present page\n");
return;
}
- if (!swap_nr) {
- printk("No swap page in swap_in\n\r");
+ if (!entry) {
+ printk("No swap page in swap_in\n");
return;
}
page = get_free_page(GFP_KERNEL);
@@ -200,20 +211,20 @@ void swap_in(unsigned long *table_ptr)
oom(current);
page = BAD_PAGE;
} else
- read_swap_page(swap_nr>>1, (char *) page);
- if (*table_ptr != swap_nr) {
+ read_swap_page(entry, (char *) page);
+ if (*table_ptr != entry) {
free_page(page);
return;
}
- *table_ptr = page | (PAGE_DIRTY | 7);
- swap_free(swap_nr>>1);
+ *table_ptr = page | (PAGE_DIRTY | PAGE_PRIVATE);
+ swap_free(entry);
}
static int try_to_swap_out(unsigned long * table_ptr)
{
int i;
unsigned long page;
- unsigned long swap_nr;
+ unsigned long entry;
page = *table_ptr;
if (!(PAGE_PRESENT & page))
@@ -236,11 +247,11 @@ static int try_to_swap_out(unsigned long * table_ptr)
page &= 0xfffff000;
if (mem_map[MAP_NR(page)] != 1)
return 0;
- if (!(swap_nr = get_swap_page()))
+ if (!(entry = get_swap_page()))
return 0;
- *table_ptr = swap_nr<<1;
+ *table_ptr = entry;
invalidate();
- write_swap_page(swap_nr, (char *) page);
+ write_swap_page(entry, (char *) page);
free_page(page);
return 1;
}
@@ -303,8 +314,8 @@ check_dir:
swap_table++;
goto check_dir;
}
- if (!(1 & pg_table)) {
- printk("bad page-table at pg_dir[%d]: %08x\n\r",
+ if (!(PAGE_PRESENT & pg_table)) {
+ printk("bad page-table at pg_dir[%d]: %08x\n",
swap_table,pg_table);
((unsigned long *) p->tss.cr3)[swap_table] = 0;
swap_table++;
@@ -414,9 +425,6 @@ void free_page(unsigned long addr)
nr--; \
last_free_pages[index = (index + 1) & (NR_LAST_FREE_PAGES - 1)] = result; \
restore_flags(flag); \
- __asm__ __volatile__("cld ; rep ; stosl" \
- ::"a" (0),"c" (1024),"D" (result) \
- :"di","cx"); \
return result; \
} \
printk("Free page %08x has mem_map = %d\n", \
@@ -440,7 +448,7 @@ last_free_pages[index = (index + 1) & (NR_LAST_FREE_PAGES - 1)] = result; \
* in it). See the above macro which does most of the work, and which is
* optimized for a fast normal path of execution.
*/
-unsigned long get_free_page(int priority)
+unsigned long __get_free_page(int priority)
{
unsigned long result, flag;
static unsigned long index = 0;
@@ -466,12 +474,8 @@ repeat:
* Trying to stop swapping from a file is fraught with races, so
* we repeat quite a bit here when we have to pause. swapoff()
* isn't exactly timing-critical, so who cares?
- *
- * Note the '>> 25' instead of '>> 24' when checking against
- * swap_nr: remember that the low bit in a page-address is used
- * for the PAGE_PRESENT bit, and is not part of the swap address.
*/
-static int try_to_unuse(unsigned int swap_nr)
+static int try_to_unuse(unsigned int type)
{
int nr, pgt, pg;
unsigned long page, *ppage;
@@ -512,7 +516,7 @@ repeat:
}
continue;
}
- if ((page >> 25) != swap_nr)
+ if (SWP_TYPE(page) != type)
continue;
if (!tmp) {
tmp = get_free_page(GFP_KERNEL);
@@ -520,11 +524,11 @@ repeat:
return -ENOMEM;
goto repeat;
}
- read_swap_page(page>>1, (char *) tmp);
+ read_swap_page(page, (char *) tmp);
if (*ppage == page) {
- *ppage = tmp | (PAGE_DIRTY | 7);
+ *ppage = tmp | (PAGE_DIRTY | PAGE_PRIVATE);
++p->rss;
- swap_free(page>>1);
+ swap_free(page);
tmp = 0;
}
goto repeat;
@@ -539,7 +543,7 @@ int sys_swapoff(const char * specialfile)
{
struct swap_info_struct * p;
struct inode * inode;
- unsigned int swap_nr;
+ unsigned int type;
int i;
if (!suser())
@@ -548,7 +552,7 @@ int sys_swapoff(const char * specialfile)
if (i)
return i;
p = swap_info;
- for (swap_nr = 0 ; swap_nr < nr_swapfiles ; swap_nr++,p++) {
+ for (type = 0 ; type < nr_swapfiles ; type++,p++) {
if ((p->flags & SWP_WRITEOK) != SWP_WRITEOK)
continue;
if (p->swap_file) {
@@ -562,10 +566,10 @@ int sys_swapoff(const char * specialfile)
}
}
iput(inode);
- if (swap_nr >= nr_swapfiles)
+ if (type >= nr_swapfiles)
return -EINVAL;
p->flags = SWP_USED;
- i = try_to_unuse(swap_nr);
+ i = try_to_unuse(type);
if (i) {
p->flags = SWP_WRITEOK;
return i;
@@ -590,20 +594,20 @@ int sys_swapon(const char * specialfile)
{
struct swap_info_struct * p;
struct inode * swap_inode;
- unsigned int swap_nr;
+ unsigned int type;
char * tmp;
int i,j;
if (!suser())
return -EPERM;
p = swap_info;
- for (swap_nr = 0 ; swap_nr < nr_swapfiles ; swap_nr++,p++)
+ for (type = 0 ; type < nr_swapfiles ; type++,p++)
if (!(p->flags & SWP_USED))
break;
- if (swap_nr >= MAX_SWAPFILES)
+ if (type >= MAX_SWAPFILES)
return -EPERM;
- if (swap_nr >= nr_swapfiles)
- nr_swapfiles = swap_nr+1;
+ if (type >= nr_swapfiles)
+ nr_swapfiles = type+1;
p->flags = SWP_USED;
p->swap_file = NULL;
p->swap_device = 0;
@@ -629,7 +633,7 @@ int sys_swapon(const char * specialfile)
return -ENODEV;
}
for (i = 0 ; i < nr_swapfiles ; i++) {
- if (i == swap_nr)
+ if (i == type)
continue;
if (p->swap_device == swap_info[i].swap_device) {
p->swap_device = 0;
@@ -658,9 +662,9 @@ int sys_swapon(const char * specialfile)
p->flags = 0;
return -ENOMEM;
}
- read_swap_page(swap_nr << 24,tmp);
+ read_swap_page(type < 1,tmp);
if (strncmp("SWAP-SPACE",tmp+4086,10)) {
- printk("Unable to find swap-space signature\n\r");
+ printk("Unable to find swap-space signature\n");
free_page((long) tmp);
free_page((long) p->swap_lockmap);
iput(p->swap_file);
@@ -703,7 +707,7 @@ int sys_swapon(const char * specialfile)
tmp[0] = 128;
p->swap_map = tmp;
p->flags = SWP_WRITEOK;
- printk("Adding Swap: %dk swap-space\n\r",j<<2);
+ printk("Adding Swap: %dk swap-space\n",j<<2);
return 0;
}
diff --git a/net/socket.c b/net/socket.c
index d34db75..8b192bb 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -493,7 +493,7 @@ sock_socketpair(int family, int type, int protocol, int usockvec[2])
sock1->state = SS_CONNECTED;
sock2->state = SS_CONNECTED;
- verify_area(usockvec, 2 * sizeof(int));
+ verify_area(VERIFY_WRITE,usockvec, 2 * sizeof(int));
put_fs_long(fd1, &usockvec[0]);
put_fs_long(fd2, &usockvec[1]);
@@ -835,62 +835,62 @@ sys_socketcall(int call, unsigned long *args)
{
switch (call) {
case SYS_SOCKET:
- verify_area(args, 3 * sizeof(long));
+ verify_area(VERIFY_WRITE,args, 3 * sizeof(long));
return sock_socket(get_fs_long(args+0),
get_fs_long(args+1),
get_fs_long(args+2));
case SYS_BIND:
- verify_area(args, 3 * sizeof(long));
+ verify_area(VERIFY_WRITE,args, 3 * sizeof(long));
return sock_bind(get_fs_long(args+0),
(struct sockaddr *)get_fs_long(args+1),
get_fs_long(args+2));
case SYS_CONNECT:
- verify_area(args, 3 * sizeof(long));
+ verify_area(VERIFY_WRITE,args, 3 * sizeof(long));
return sock_connect(get_fs_long(args+0),
(struct sockaddr *)get_fs_long(args+1),
get_fs_long(args+2));
case SYS_LISTEN:
- verify_area(args, 2 * sizeof(long));
+ verify_area(VERIFY_WRITE,args, 2 * sizeof(long));
return sock_listen(get_fs_long(args+0),
get_fs_long(args+1));
case SYS_ACCEPT:
- verify_area(args, 3 * sizeof(long));
+ verify_area(VERIFY_WRITE,args, 3 * sizeof(long));
return sock_accept(get_fs_long(args+0),
(struct sockaddr *)get_fs_long(args+1),
(int *)get_fs_long(args+2));
case SYS_GETSOCKNAME:
- verify_area(args, 3 * sizeof(long));
+ verify_area(VERIFY_WRITE,args, 3 * sizeof(long));
return sock_getsockname(get_fs_long(args+0),
(struct sockaddr *)get_fs_long(args+1),
(int *)get_fs_long(args+2));
case SYS_GETPEERNAME:
- verify_area(args, 3 * sizeof(long));
+ verify_area(VERIFY_WRITE,args, 3 * sizeof(long));
return sock_getpeername(get_fs_long(args+0),
(struct sockaddr *)get_fs_long(args+1),
(int *)get_fs_long(args+2));
case SYS_SOCKETPAIR:
- verify_area(args, 4 * sizeof(long));
+ verify_area(VERIFY_WRITE,args, 4 * sizeof(long));
return sock_socketpair(get_fs_long(args+0),
get_fs_long(args+1),
get_fs_long(args+2),
(int *)get_fs_long(args+3));
case SYS_SEND:
- verify_area(args, 4 * sizeof (unsigned long));
+ verify_area(VERIFY_WRITE,args, 4 * sizeof (unsigned long));
return ( sys_send (get_fs_long(args+0),
(void *)get_fs_long(args+1),
get_fs_long(args+2),
get_fs_long(args+3)));
case SYS_SENDTO:
- verify_area(args, 6 * sizeof (unsigned long));
+ verify_area(VERIFY_WRITE,args, 6 * sizeof (unsigned long));
return ( sys_sendto (get_fs_long(args+0),
(void *)get_fs_long(args+1),
get_fs_long(args+2),
@@ -900,14 +900,14 @@ sys_socketcall(int call, unsigned long *args)
case SYS_RECV:
- verify_area(args, 4 * sizeof (unsigned long));
+ verify_area(VERIFY_WRITE,args, 4 * sizeof (unsigned long));
return ( sys_recv (get_fs_long(args+0),
(void *)get_fs_long(args+1),
get_fs_long(args+2),
get_fs_long(args+3)));
case SYS_RECVFROM:
- verify_area(args, 6 * sizeof (unsigned long));
+ verify_area(VERIFY_WRITE,args, 6 * sizeof (unsigned long));
return ( sys_recvfrom (get_fs_long(args+0),
(void *)get_fs_long(args+1),
get_fs_long(args+2),
@@ -916,12 +916,12 @@ sys_socketcall(int call, unsigned long *args)
(int *)get_fs_long(args+5)));
case SYS_SHUTDOWN:
- verify_area (args, 2* sizeof (unsigned long));
+ verify_area(VERIFY_WRITE, args, 2* sizeof (unsigned long));
return ( sys_shutdown (get_fs_long (args+0),
get_fs_long (args+1)));
case SYS_SETSOCKOPT:
- verify_area (args, 5*sizeof (unsigned long));
+ verify_area(VERIFY_WRITE, args, 5*sizeof (unsigned long));
return (sys_setsockopt (get_fs_long (args+0),
get_fs_long (args+1),
get_fs_long (args+2),
@@ -930,7 +930,7 @@ sys_socketcall(int call, unsigned long *args)
case SYS_GETSOCKOPT:
- verify_area (args, 5*sizeof (unsigned long));
+ verify_area(VERIFY_WRITE, args, 5*sizeof (unsigned long));
return (sys_getsockopt (get_fs_long (args+0),
get_fs_long (args+1),
get_fs_long (args+2),
diff --git a/net/tcp/arp.c b/net/tcp/arp.c
index 451d316..cbb9cdb 100644
--- a/net/tcp/arp.c
+++ b/net/tcp/arp.c
@@ -509,6 +509,10 @@ arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
memcpy (haddr, apt->hard, dev->addr_len);
return (0);
}
+ else
+ {
+ arp_destroy(paddr);
+ }
}
/* this assume haddr are at least 4 bytes.
diff --git a/net/tcp/ip.c b/net/tcp/ip.c
index 584765a..2aa7a44 100644
--- a/net/tcp/ip.c
+++ b/net/tcp/ip.c
@@ -382,7 +382,7 @@ ip_set_dev (struct ip_config *u_ipc)
-/* verify_area (u_ipc, sizeof (ipc));*/
+/* verify_area (VERIFY_WRITE, u_ipc, sizeof (ipc));*/
memcpy_fromfs(&ipc, u_ipc, sizeof (ipc));
ipc.name[MAX_IP_NAME-1] = 0;
dev = get_dev (ipc.name);
diff --git a/net/tcp/loopback.c b/net/tcp/loopback.c
index afe8889..21849c4 100644
--- a/net/tcp/loopback.c
+++ b/net/tcp/loopback.c
@@ -86,6 +86,7 @@ static int
loopback_xmit(struct sk_buff *skb, struct device *dev)
{
int done;
+ if (!skb || !dev) return 0;
PRINTK (("loopback_xmit (dev = %X)\n", dev));
cli();
if (dev->tbusy != 0)
diff --git a/net/tcp/packet.c b/net/tcp/packet.c
index 6004480..339abf0 100644
--- a/net/tcp/packet.c
+++ b/net/tcp/packet.c
@@ -147,7 +147,7 @@ packet_sendto (volatile struct sock *sk, unsigned char *from, int len,
{
if (addr_len < sizeof (saddr))
return (-EINVAL);
-/* verify_area (usin, sizeof (saddr));*/
+/* verify_area (VERIFY_WRITE, usin, sizeof (saddr));*/
memcpy_fromfs (&saddr, usin, sizeof(saddr));
}
else
@@ -173,7 +173,7 @@ packet_sendto (volatile struct sock *sk, unsigned char *from, int len,
sk->prot->wfree (sk, skb->mem_addr, skb->mem_len);
return (-ENXIO);
}
-/* verify_area (from, len);*/
+/* verify_area (VERIFY_WRITE, from, len);*/
memcpy_fromfs (skb+1, from, len);
skb->len = len;
skb->next = NULL;
@@ -240,7 +240,7 @@ packet_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
if (addr_len)
{
- verify_area (addr_len, sizeof(*addr_len));
+ verify_area (VERIFY_WRITE, addr_len, sizeof(*addr_len));
put_fs_long (sizeof (*saddr), addr_len);
}
@@ -281,7 +281,7 @@ packet_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
}
}
copied = min (len, skb->len);
- verify_area (to, copied);
+ verify_area (VERIFY_WRITE, to, copied);
memcpy_tofs (to, skb+1, copied);
/* copy the address. */
if (saddr)
@@ -289,7 +289,7 @@ packet_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
struct sockaddr addr;
addr.sa_family = skb->dev->type;
memcpy (addr.sa_data,skb->dev->name, 14);
- verify_area (saddr, sizeof (*saddr));
+ verify_area (VERIFY_WRITE, saddr, sizeof (*saddr));
memcpy_tofs(saddr, &addr, sizeof (*saddr));
}
diff --git a/net/tcp/raw.c b/net/tcp/raw.c
index 56257b5..a7c40d9 100644
--- a/net/tcp/raw.c
+++ b/net/tcp/raw.c
@@ -245,7 +245,7 @@ raw_sendto (volatile struct sock *sk, unsigned char *from, int len,
{
if (addr_len < sizeof (sin))
return (-EINVAL);
-/* verify_area (usin, sizeof (sin));*/
+/* verify_area (VERIFY_WRITE, usin, sizeof (sin));*/
memcpy_fromfs (&sin, usin, sizeof(sin));
if (sin.sin_family &&
sin.sin_family != AF_INET)
@@ -309,7 +309,7 @@ raw_sendto (volatile struct sock *sk, unsigned char *from, int len,
return (tmp);
}
-/* verify_area (from, len);*/
+/* verify_area (VERIFY_WRITE, from, len);*/
memcpy_fromfs ((unsigned char *)(skb+1)+tmp, from, len);
skb->len = tmp + len;
sk->prot->queue_xmit (sk, dev, skb, 1);
@@ -380,7 +380,7 @@ raw_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
if (sk->shutdown & RCV_SHUTDOWN) return (0);
if (addr_len)
{
- verify_area (addr_len, sizeof(*addr_len));
+ verify_area (VERIFY_WRITE, addr_len, sizeof(*addr_len));
put_fs_long (sizeof (*sin), addr_len);
}
sk->inuse = 1;
@@ -422,7 +422,7 @@ raw_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
}
}
copied = min (len, skb->len);
- verify_area (to, copied);
+ verify_area (VERIFY_WRITE, to, copied);
memcpy_tofs (to, skb->h.raw, copied);
/* copy the address. */
if (sin)
@@ -430,7 +430,7 @@ raw_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = skb->daddr;
- verify_area (sin, sizeof (*sin));
+ verify_area (VERIFY_WRITE, sin, sizeof (*sin));
memcpy_tofs(sin, &addr, sizeof (*sin));
}
diff --git a/net/tcp/sock.c b/net/tcp/sock.c
index fd0f329..43ed5aa 100644
--- a/net/tcp/sock.c
+++ b/net/tcp/sock.c
@@ -303,7 +303,7 @@ sk_inuse( struct proto *prot, int num)
for (sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];
sk != NULL; sk=sk->next)
{
- if (sk->dummy_th.source == num) return (1);
+ if (sk->num == num) return (1);
}
return (0);
}
@@ -719,7 +719,7 @@ ip_proto_setsockopt(struct socket *sock, int level, int optname,
}
if (optval == NULL) return (-EINVAL);
-/* verify_area (optval, sizeof (int));*/
+/* verify_area (VERIFY_WRITE, optval, sizeof (int));*/
val = get_fs_long ((unsigned long *)optval);
switch (optname)
{
@@ -837,10 +837,10 @@ ip_proto_getsockopt(struct socket *sock, int level, int optname,
val = sk->priority;
break;
}
- verify_area (optlen, sizeof (int));
+ verify_area (VERIFY_WRITE, optlen, sizeof (int));
put_fs_long (sizeof(int),(unsigned long *) optlen);
- verify_area(optval, sizeof (int));
+ verify_area(VERIFY_WRITE, optval, sizeof (int));
put_fs_long (val, (unsigned long *)optval);
return (0);
}
@@ -1019,7 +1019,6 @@ ip_proto_create (struct socket *sock, int protocol)
sk->urginline = 0;
sk->intr = 0;
sk->linger = 0;
- sk->rtt = 0;
sk->destroy = 0;
sk->reuse = 0;
sk->priority = 1;
@@ -1177,7 +1176,7 @@ ip_proto_bind (struct socket *sock, struct sockaddr *uaddr,
if (sk->state != TCP_CLOSE) return (-EIO);
if (sk->num != 0) return (-EINVAL);
-/* verify_area (uaddr, addr_len);*/
+/* verify_area (VERIFY_WRITE, uaddr, addr_len);*/
memcpy_fromfs (&addr, uaddr, min (sizeof (addr), addr_len));
#if 0
@@ -1413,9 +1412,9 @@ ip_proto_getname(struct socket *sock, struct sockaddr *uaddr,
sin.sin_addr.s_addr = sk->saddr;
}
len = sizeof (sin);
- verify_area (uaddr, len);
+ verify_area (VERIFY_WRITE, uaddr, len);
memcpy_tofs(uaddr, &sin, sizeof (sin));
- verify_area(uaddr_len, sizeof (len));
+ verify_area(VERIFY_WRITE, uaddr_len, sizeof (len));
put_fs_long (len, uaddr_len);
return (0);
}
@@ -1677,7 +1676,7 @@ ip_proto_ioctl (struct socket *sock, unsigned int cmd,
case FIOGETOWN:
case SIOCGPGRP:
{
- verify_area ((void *)arg, sizeof (long));
+ verify_area (VERIFY_WRITE, (void *)arg, sizeof (long));
put_fs_long (sk->proc, (void *)arg);
return (0);
}
diff --git a/net/tcp/tcp.c b/net/tcp/tcp.c
index 7307778..3765bba 100644
--- a/net/tcp/tcp.c
+++ b/net/tcp/tcp.c
@@ -401,7 +401,7 @@ tcp_ioctl (volatile struct sock *sk, int cmd, unsigned long arg)
}
release_sock (sk);
PRINTK (("returning %d\n", amount));
- verify_area ((void *)arg, sizeof (unsigned long));
+ verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
put_fs_long (amount, (unsigned long *)arg);
return (0);
}
@@ -419,7 +419,7 @@ tcp_ioctl (volatile struct sock *sk, int cmd, unsigned long arg)
answ = 1;
}
release_sock (sk);
- verify_area ((void *) arg, sizeof (unsigned long));
+ verify_area (VERIFY_WRITE, (void *) arg, sizeof (unsigned long));
put_fs_long (answ, (void *) arg);
return (0);
}
@@ -430,7 +430,7 @@ tcp_ioctl (volatile struct sock *sk, int cmd, unsigned long arg)
if (sk->state == TCP_LISTEN)
return (-EINVAL);
amount = sk->prot->wspace(sk)/2;
- verify_area ((void *)arg, sizeof (unsigned long));
+ verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
put_fs_long (amount, (unsigned long *)arg);
return (0);
}
@@ -1184,7 +1184,7 @@ tcp_read_urg(volatile struct sock * sk, int nonblock,
skb->h.th->urg_ptr = net16(skb->len);
}
amt = min(net16(skb->h.th->urg_ptr),len);
- verify_area (to, amt);
+ verify_area (VERIFY_WRITE, to, amt);
memcpy_tofs (to, (unsigned char *)(skb->h.th) +
skb->h.th->doff*4, amt);
@@ -1377,7 +1377,7 @@ tcp_read (volatile struct sock *sk, unsigned char *to,
}
used = min(skb->len - offset, len);
- verify_area (to, used);
+ verify_area (VERIFY_WRITE, to, used);
memcpy_tofs(to, ((unsigned char *)skb->h.th) +
skb->h.th->doff*4 +
offset,
@@ -1540,9 +1540,9 @@ tcp_recvfrom (volatile struct sock *sk, unsigned char *to,
sin.sin_family = AF_INET;
sin.sin_port = sk->dummy_th.dest;
sin.sin_addr.s_addr = sk->daddr;
- verify_area (addr, len);
+ verify_area (VERIFY_WRITE, addr, len);
memcpy_tofs (addr, &sin, len);
- verify_area (addr_len, sizeof (len));
+ verify_area (VERIFY_WRITE, addr_len, sizeof (len));
put_fs_long (len, addr_len);
return (result);
}
@@ -1664,6 +1664,7 @@ tcp_conn_request(volatile struct sock *sk, struct sk_buff *skb,
newsk->send_head = NULL;
newsk->send_tail = NULL;
newsk->back_log = NULL;
+ newsk->rtt = TCP_CONNECT_TIME;
newsk->blog = 0;
newsk->intr = 0;
newsk->proc = 0;
@@ -2712,7 +2713,7 @@ tcp_connect (volatile struct sock *sk, struct sockaddr_in *usin, int addr_len)
if (sk->state != TCP_CLOSE) return (-EISCONN);
if (addr_len < 8) return (-EINVAL);
-/* verify_area (usin, addr_len);*/
+/* verify_area (VERIFY_WRITE, usin, addr_len);*/
memcpy_fromfs (&sin,usin, min(sizeof (sin), addr_len));
if (sin.sin_family && sin.sin_family != AF_INET) return (-EAFNOSUPPORT);
@@ -2803,10 +2804,10 @@ tcp_sequence (volatile struct sock *sk, struct tcp_header *th, short len,
sk, th, len, opt, saddr));
if (between(th->seq, sk->acked_seq, sk->acked_seq + sk->window)||
- between(th->seq + len-sizeof (*th), sk->acked_seq,
+ between(th->seq + len-(th->doff * 4), sk->acked_seq + 1,
sk->acked_seq + sk->window) ||
(before (th->seq, sk->acked_seq) &&
- after (th->seq + len - sizeof (*th), sk->acked_seq + sk->window)))
+ after (th->seq + len - (th->doff * 4), sk->acked_seq + sk->window)))
{
return (1);
}
@@ -2822,7 +2823,7 @@ tcp_sequence (volatile struct sock *sk, struct tcp_header *th, short len,
}
/* in case it's just a late ack, let it through */
- if (th->ack && len == th->doff*4 && after (th->seq, sk->acked_seq - 32767) &&
+ if (th->ack && len == (th->doff * 4) && after (th->seq, sk->acked_seq - 32767) &&
!th->fin && !th->syn) return (1);
if (!th->rst)
diff --git a/net/tcp/udp.c b/net/tcp/udp.c
index b4c60b9..d07425a 100644
--- a/net/tcp/udp.c
+++ b/net/tcp/udp.c
@@ -286,7 +286,7 @@ udp_loopback (volatile struct sock *sk, unsigned short port,
uh -> source = sk->dummy_th.source;
uh -> dest = port;
uh -> len = len + sizeof (*uh);
-/* verify_area (from , len); */
+/* verify_area (VERIFY_WRITE, from , len); */
memcpy_fromfs(uh+1, from, len);
pair->inuse = 1;
if (pair->rqueue == NULL)
@@ -336,7 +336,7 @@ udp_sendto (volatile struct sock *sk, unsigned char *from, int len,
{
if (addr_len < sizeof (sin))
return (-EINVAL);
-/* verify_area (usin, sizeof (sin));*/
+/* verify_area (VERIFY_WRITE, usin, sizeof (sin));*/
memcpy_fromfs (&sin, usin, sizeof(sin));
if (sin.sin_family &&
sin.sin_family != AF_INET)
@@ -446,7 +446,7 @@ udp_sendto (volatile struct sock *sk, unsigned char *from, int len,
return (copied);
}
-/* verify_area (from, amt);*/
+/* verify_area (VERIFY_WRITE, from, amt);*/
memcpy_fromfs( buff, from, amt);
len -= amt;
@@ -483,7 +483,7 @@ udp_ioctl (volatile struct sock *sk, int cmd, unsigned long arg)
if (sk->state == TCP_LISTEN)
return (-EINVAL);
amount = sk->prot->wspace(sk)/2;
- verify_area ((void *)arg, sizeof (unsigned long));
+ verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
put_fs_long (amount, (unsigned long *)arg);
return (0);
}
@@ -505,7 +505,7 @@ udp_ioctl (volatile struct sock *sk, int cmd, unsigned long arg)
amount = skb->len;
}
- verify_area ((void *)arg, sizeof (unsigned long));
+ verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
put_fs_long (amount, (unsigned long *)arg);
return (0);
}
@@ -536,7 +536,7 @@ udp_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
}
if (addr_len)
{
- verify_area (addr_len, sizeof(*addr_len));
+ verify_area (VERIFY_WRITE, addr_len, sizeof(*addr_len));
put_fs_long (sizeof (*sin), addr_len);
}
sk->inuse = 1;
@@ -581,7 +581,7 @@ udp_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
}
}
copied = min (len, skb->len);
- verify_area (to, copied);
+ verify_area (VERIFY_WRITE, to, copied);
memcpy_tofs (to, skb->h.raw + sizeof (struct udp_header), copied);
/* copy the address. */
if (sin)
@@ -590,7 +590,7 @@ udp_recvfrom (volatile struct sock *sk, unsigned char *to, int len,
addr.sin_family = AF_INET;
addr.sin_port = skb->h.uh->source;
addr.sin_addr.s_addr = skb->daddr;
- verify_area (sin, sizeof (*sin));
+ verify_area (VERIFY_WRITE, sin, sizeof (*sin));
memcpy_tofs(sin, &addr, sizeof (*sin));
}
@@ -616,7 +616,7 @@ udp_connect (volatile struct sock *sk, struct sockaddr_in *usin, int addr_len)
{
struct sockaddr_in sin;
if (addr_len < sizeof (sin)) return (-EINVAL);
-/* verify_area (usin, sizeof (sin)); */
+/* verify_area (VERIFY_WRITE, usin, sizeof (sin)); */
memcpy_fromfs (&sin, usin, sizeof (sin));
if (sin.sin_family && sin.sin_family != AF_INET)
return (-EAFNOSUPPORT);
diff --git a/net/unix.c b/net/unix.c
index 9ace268..2e077f4 100644
--- a/net/unix.c
+++ b/net/unix.c
@@ -333,7 +333,6 @@ unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
struct unix_proto_data *upd = UN_DATA(sock);
char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1];
int i;
- unsigned long old_fs;
PRINTK(("unix_proto_bind: socket 0x%x, len=%d\n", sock,
sockaddr_len));
@@ -346,7 +345,7 @@ unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
printk("unix_proto_bind: already bound!\n");
return -EINVAL;
}
- verify_area(umyaddr, sockaddr_len);
+ verify_area(VERIFY_WRITE,umyaddr, sockaddr_len);
memcpy_fromfs(&upd->sockaddr_un, umyaddr, sockaddr_len);
if (upd->sockaddr_un.sun_family != AF_UNIX) {
PRINTK(("unix_proto_bind: family is %d, not AF_UNIX (%d)\n",
@@ -356,12 +355,9 @@ unix_proto_bind(struct socket *sock, struct sockaddr *umyaddr,
memcpy(fname, upd->sockaddr_un.sun_path, sockaddr_len-UN_PATH_OFFSET);
fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
- old_fs = get_fs();
- set_fs(get_ds());
i = do_mknod(fname, S_IFSOCK | 0777, 0);
if (i == 0)
i = open_namei(fname, 0, S_IFSOCK, &upd->inode, NULL);
- set_fs(old_fs);
if (i < 0) {
printk("unix_proto_bind: can't open socket %s\n", fname);
return i;
@@ -388,7 +384,6 @@ unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
struct unix_proto_data *serv_upd;
struct sockaddr_un sockun;
char fname[sizeof(((struct sockaddr_un *)0)->sun_path) + 1];
- unsigned long old_fs;
struct inode *inode;
PRINTK(("unix_proto_connect: socket 0x%x, servlen=%d\n", sock,
@@ -406,7 +401,7 @@ unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
if (sock->state == SS_CONNECTED)
return (-EISCONN);
- verify_area(uservaddr, sockaddr_len);
+ verify_area(VERIFY_WRITE,uservaddr, sockaddr_len);
memcpy_fromfs(&sockun, uservaddr, sockaddr_len);
if (sockun.sun_family != AF_UNIX) {
PRINTK(("unix_proto_connect: family is %d, not AF_UNIX (%d)\n",
@@ -422,10 +417,7 @@ unix_proto_connect(struct socket *sock, struct sockaddr *uservaddr,
*/
memcpy(fname, sockun.sun_path, sockaddr_len-UN_PATH_OFFSET);
fname[sockaddr_len-UN_PATH_OFFSET] = '\0';
- old_fs = get_fs();
- set_fs(get_ds());
i = open_namei(fname, 0, S_IFSOCK, &inode, NULL);
- set_fs(old_fs);
if (i < 0) {
PRINTK(("unix_proto_connect: can't open socket %s\n", fname));
return i;
@@ -530,13 +522,13 @@ unix_proto_getname(struct socket *sock, struct sockaddr *usockaddr,
}
else
upd = UN_DATA(sock);
- verify_area(usockaddr_len, sizeof(*usockaddr_len));
+ verify_area(VERIFY_WRITE,usockaddr_len, sizeof(*usockaddr_len));
if ((len = get_fs_long(usockaddr_len)) <= 0)
return -EINVAL;
if (len > upd->sockaddr_len)
len = upd->sockaddr_len;
if (len) {
- verify_area(usockaddr, len);
+ verify_area(VERIFY_WRITE,usockaddr, len);
memcpy_tofs(usockaddr, &upd->sockaddr_un, len);
}
put_fs_long(len, usockaddr_len);
@@ -593,7 +585,7 @@ unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock)
cando = part;
PRINTK(("unix_proto_read: avail=%d, todo=%d, cando=%d\n",
avail, todo, cando));
- verify_area(ubuf, cando);
+ verify_area(VERIFY_WRITE,ubuf, cando);
memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando);
upd->bp_tail = (upd->bp_tail + cando) & (BUF_SIZE-1);
ubuf += cando;
@@ -671,7 +663,7 @@ unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock)
cando = part;
PRINTK(("unix_proto_write: space=%d, todo=%d, cando=%d\n",
space, todo, cando));
- verify_area(ubuf, cando);
+ verify_area(VERIFY_WRITE,ubuf, cando);
memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando);
pupd->bp_head = (pupd->bp_head + cando) & (BUF_SIZE-1);
ubuf += cando;
@@ -749,7 +741,7 @@ unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case TIOCINQ:
if (sock->flags & SO_ACCEPTCON)
return -EINVAL;
- verify_area((void *)arg, sizeof(unsigned long));
+ verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
if (UN_BUF_AVAIL(upd) || peerupd)
put_fs_long(UN_BUF_AVAIL(upd), (unsigned long *)arg);
else
@@ -759,7 +751,7 @@ unix_proto_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case TIOCOUTQ:
if (sock->flags & SO_ACCEPTCON)
return -EINVAL;
- verify_area((void *)arg, sizeof(unsigned long));
+ verify_area(VERIFY_WRITE,(void *)arg, sizeof(unsigned long));
if (peerupd)
put_fs_long(UN_BUF_SPACE(peerupd),
(unsigned long *)arg);
diff --git a/tools/build.c b/tools/build.c
index 86ba0c3..d8cddfd 100644
--- a/tools/build.c
+++ b/tools/build.c
@@ -85,6 +85,7 @@ void usage(void)
int main(int argc, char ** argv)
{
int i,c,id, sz;
+ unsigned long sys_size;
char buf[1024];
struct exec *ex = (struct exec *)buf;
char major_root, minor_root;
@@ -184,7 +185,8 @@ int main(int argc, char ** argv)
die("Non-GCC header of 'system'");
sz = N_SYMOFF(*ex) - GCC_HEADER + 4;
fprintf(stderr,"System is %d bytes.\n", sz);
- if (sz > SYS_SIZE*16)
+ sys_size = (sz + 15) / 16;
+ if (sys_size > SYS_SIZE)
die("System is too big");
while (sz > 0) {
int l, n;
@@ -203,5 +205,10 @@ int main(int argc, char ** argv)
sz -= l;
}
close(id);
+ if (lseek(1,500,0) == 500) {
+ buf[0] = (sys_size & 0xff);
+ buf[1] = ((sys_size >> 8) & 0xff);
+ write(1, buf, 2);
+ }
return(0);
}
diff --git a/zBoot/misc.c b/zBoot/misc.c
index d641ba4..179e6c8 100644
--- a/zBoot/misc.c
+++ b/zBoot/misc.c
@@ -280,6 +280,10 @@ struct {
void decompress_kernel()
{
+ if (SCREEN_INFO.orig_video_mode == 7)
+ vidmem = (char *) 0xb0000;
+ else
+ vidmem = (char *) 0xb8000;
vidp = 0;
vidmem[0] = '0';