--- arch/alpha/kernel/entry.S.~1~	Mon Dec  7 23:11:00 1998
+++ arch/alpha/kernel/entry.S	Mon Dec  7 23:11:05 1998
@@ -10,7 +10,7 @@
 #define rti	.long PAL_rti
 #define SIGCHLD	20
 
-#define NR_SYSCALLS 370
+#define NR_SYSCALLS 371
 #define osf_vfork sys_fork
 
 /*
@@ -1127,4 +1127,5 @@
 	.quad sys_getcwd
 	.quad sys_capget
 	.quad sys_capset
-	.quad sys_ni_syscall			/* 370 */
+	.quad sys_fsattr			/* 370 */
+	.quad sys_ni_syscall
--- arch/arm/kernel/calls.S.~1~	Mon Dec  7 23:11:00 1998
+++ arch/arm/kernel/calls.S	Mon Dec  7 23:11:05 1998
@@ -196,8 +196,9 @@
 		.long	SYMBOL_NAME(sys_capget)
 /* 185 */	.long	SYMBOL_NAME(sys_capset)
 		.long	SYMBOL_NAME(sys_sigaltstack_wrapper)
+		.long	SYMBOL_NAME(sys_fsattr)
 
-		.rept	NR_syscalls-186
+		.rept	NR_syscalls-187
 			.long	SYMBOL_NAME(sys_ni_syscall)
 		.endr
 #endif
--- arch/i386/kernel/entry.S.~1~	Mon Dec  7 23:11:00 1998
+++ arch/i386/kernel/entry.S	Mon Dec  7 23:11:05 1998
@@ -558,6 +558,7 @@
 	.long SYMBOL_NAME(sys_sendfile)
 	.long SYMBOL_NAME(sys_ni_syscall)		/* streams1 */
 	.long SYMBOL_NAME(sys_ni_syscall)		/* streams2 */
+	.long SYMBOL_NAME(sys_fsattr)		
 
 	/*
 	 * NOTE!! This doesn' thave to be exact - we just have
@@ -565,6 +566,6 @@
 	 * entries. Don't panic if you notice that this hasn't
 	 * been shrunk every time we add a new system call.
 	 */
-	.rept NR_syscalls-189
+	.rept NR_syscalls-190
 		.long SYMBOL_NAME(sys_ni_syscall)
 	.endr
--- arch/m68k/kernel/entry.S.~1~	Mon Dec  7 23:11:00 1998
+++ arch/m68k/kernel/entry.S	Mon Dec  7 23:11:05 1998
@@ -586,6 +586,7 @@
 	.long SYMBOL_NAME(sys_sendfile)
 	.long SYMBOL_NAME(sys_ni_syscall)		/* streams1 */
 	.long SYMBOL_NAME(sys_ni_syscall)		/* streams2 */
+	.long SYMBOL_NAME(sys_fsattr)
 
 	.rept NR_syscalls-(.-SYMBOL_NAME(sys_call_table))/4
 		.long SYMBOL_NAME(sys_ni_syscall)
--- arch/mips/kernel/syscalls.h.~1~	Mon Dec  7 23:11:00 1998
+++ arch/mips/kernel/syscalls.h	Mon Dec  7 23:11:05 1998
@@ -225,3 +225,4 @@
 SYS(sys_sendfile, 3)
 SYS(sys_ni_syscall, 0)
 SYS(sys_ni_syscall, 0)
+SYS(sys_fsattr, 4)
--- arch/ppc/kernel/misc.S.~1~	Mon Dec  7 23:11:00 1998
+++ arch/ppc/kernel/misc.S	Mon Dec  7 23:11:05 1998
@@ -677,4 +677,5 @@
 	.long sys_sendfile
 	.long sys_ni_syscall		/* streams1 */
 	.long sys_ni_syscall		/* streams2 */
-	.space (NR_syscalls-183)*4
+ 	.long sys_fsattr
+	.space (NR_syscalls-189)*4
--- arch/sparc/kernel/systbls.S.~1~	Mon Dec  7 23:11:00 1998
+++ arch/sparc/kernel/systbls.S	Mon Dec  7 23:11:05 1998
@@ -68,7 +68,7 @@
 /*240*/	.long sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler
 /*245*/	.long sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep
 /*250*/	.long sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
-/*255*/	.long sys_aplib, sys_nis_syscall
+/*255*/	.long sys_aplib, sys_fsattr, sys_nis_syscall
 
 	/* Now the SunOS syscall table. */
 
--- arch/sparc64/kernel/systbls.S.~1~	Mon Dec  7 23:11:00 1998
+++ arch/sparc64/kernel/systbls.S	Mon Dec  7 23:11:05 1998
@@ -127,7 +127,7 @@
 /*240*/	.word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler
 	.word sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep
 /*250*/	.word sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
-	.word sys_aplib
+	.word sys_aplib, sys_fsattr
 
 	/* Now the 32-bit SunOS syscall table. */
 
--- fs/Makefile.~1~	Mon Aug 31 21:01:35 1998
+++ fs/Makefile	Tue Dec  8 16:03:44 1998
@@ -13,7 +13,7 @@
 O_OBJS    = open.o read_write.o devices.o file_table.o buffer.o \
 		super.o  block_dev.o stat.o exec.o pipe.o namei.o fcntl.o \
 		ioctl.o readdir.o select.o fifo.o locks.o filesystems.o \
-		dcache.o inode.o attr.o bad_inode.o $(BINFMTS) 
+		dcache.o inode.o attr.o bad_inode.o raw.o $(BINFMTS) 
 
 MOD_LIST_NAME := FS_MODULES
 ALL_SUB_DIRS = coda minix ext2 fat msdos vfat proc isofs nfs umsdos ntfs \
--- fs/block_dev.c.~1~	Mon Dec  7 23:11:00 1998
+++ fs/block_dev.c	Wed Dec  9 17:15:53 1998
@@ -53,6 +53,11 @@
 		size = ((loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS) >> blocksize_bits;
 	else
 		size = INT_MAX;
+
+	if (filp->f_flags & O_DIRECT)
+		return rw_raw_io(WRITE, blocksize, 0, inode, 
+				 buf, count, ppos, size); 
+
 	while (count>0) {
 		if (block >= size)
 			return written ? written : -ENOSPC;
@@ -205,6 +210,10 @@
 		if (blocks == 0)
 			return 0;
 	}
+
+	if (filp->f_flags & O_DIRECT)
+		return rw_raw_io(READ, blocksize, 0, inode, 
+				 buf, count, ppos, size); 
 
 	/* We do this in a two stage process.  We first try to request
 	   as many blocks as we can, then we wait for the first one to
--- fs/buffer.c.~1~	Mon Dec  7 23:11:00 1998
+++ fs/buffer.c	Tue Dec  8 17:49:24 1998
@@ -891,7 +891,7 @@
 	bh->b_dev_id = dev_id;
 }
 
-static void end_buffer_io_sync(struct buffer_head *bh, int uptodate)
+void end_buffer_io_sync(struct buffer_head *bh, int uptodate)
 {
 	mark_buffer_uptodate(bh, uptodate);
 	unlock_buffer(bh);
@@ -1048,8 +1048,10 @@
 	remove_from_hash_queue(buf);
 	buf->b_dev = NODEV;
 	refile_buffer(buf);
-	if (!--buf->b_count)
+	if (!--buf->b_count) {
+		try_to_free_buffer(buf, &buf, 6);
 		return;
+	}
 	printk("VFS: forgot an in-use buffer! (count=%d)\n",
 		buf->b_count);
 }
@@ -1192,7 +1194,7 @@
  * no-buffer-head deadlock.  Return NULL on failure; waiting for
  * buffer heads is now handled in create_buffers().
  */ 
-static struct buffer_head * get_unused_buffer_head(int async)
+struct buffer_head * get_unused_buffer_head(int async)
 {
 	struct buffer_head * bh;
 
@@ -1345,7 +1347,7 @@
  * Free all temporary buffers belonging to a page.
  * This needs to be called with interrupts disabled.
  */
-static inline void free_async_buffers (struct buffer_head * bh)
+void free_async_buffers (struct buffer_head * bh)
 {
 	struct buffer_head *tmp, *tail;
 
--- fs/ext2/inode.c.~1~	Mon Dec  7 23:11:00 1998
+++ fs/ext2/inode.c	Mon Dec  7 23:11:05 1998
@@ -599,6 +672,10 @@
 	if (inode->u.ext2_i.i_flags & EXT2_NOATIME_FL) {
 		inode->i_attr_flags |= ATTR_FLAG_NOATIME;
 		inode->i_flags |= MS_NOATIME;
+	}
+	if (inode->u.ext2_i.i_flags & EXT2_DIRECT_FL) {
+		inode->i_attr_flags |= ATTR_FLAG_DIRECT;
+		inode->i_flags |= S_DIRECT;
 	}
 	return;
 	
--- fs/ext2/ioctl.c.~1~	Mon Dec  7 23:11:00 1998
+++ fs/ext2/ioctl.c	Tue Dec  8 17:41:08 1998
@@ -16,6 +16,65 @@
 #include <linux/sched.h>
 #include <linux/mm.h>
 
+static int ext2_set_flags(struct inode *inode, unsigned int flags) 
+{
+	flags = flags & EXT2_FL_USER_MODIFIABLE;
+	/*
+	 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
+	 * the super user when the security level is zero.
+	 */
+	if ((flags & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) ^
+	    (inode->u.ext2_i.i_flags &
+	     (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) {
+		/* This test looks nicer. Thanks to Pauline Middelink */
+		if (!capable(CAP_LINUX_IMMUTABLE))
+			return -EPERM;
+	} else
+		if ((current->fsuid != inode->i_uid) && 
+		    !capable(CAP_FOWNER))
+			return -EPERM;
+	if (IS_RDONLY(inode))
+		return -EROFS;
+	inode->u.ext2_i.i_flags = (inode->u.ext2_i.i_flags &
+				   ~EXT2_FL_USER_MODIFIABLE) | flags;
+	if (flags & EXT2_SYNC_FL)
+		inode->i_flags |= MS_SYNCHRONOUS;
+	else
+		inode->i_flags &= ~MS_SYNCHRONOUS;
+	if (flags & EXT2_APPEND_FL)
+		inode->i_flags |= S_APPEND;
+	else
+		inode->i_flags &= ~S_APPEND;
+	if (flags & EXT2_IMMUTABLE_FL)
+		inode->i_flags |= S_IMMUTABLE;
+	else
+		inode->i_flags &= ~S_IMMUTABLE;
+	if (flags & EXT2_NOATIME_FL)
+		inode->i_flags |= MS_NOATIME;
+	else
+		inode->i_flags &= ~MS_NOATIME;
+	if (flags & EXT2_DIRECT_FL)
+		inode->i_flags |= S_DIRECT;
+	else
+		inode->i_flags &= ~S_DIRECT;
+	inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty(inode);
+	return 0;
+}
+
+static int ext2_set_version (struct inode *inode, unsigned long arg)
+{
+	if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+		return -EPERM;
+	if (IS_RDONLY(inode))
+		return -EROFS;
+	if (get_user(inode->u.ext2_i.i_version, (int *) arg))
+		return -EFAULT;	
+	inode->i_ctime = CURRENT_TIME;
+	mark_inode_dirty(inode);
+	return 0;
+}
+
 int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
 		unsigned long arg)
 {
@@ -30,57 +89,72 @@
 	case EXT2_IOC_SETFLAGS:
 		if (get_user(flags, (int *) arg))
 			return -EFAULT;
-		flags = flags & EXT2_FL_USER_MODIFIABLE;
-		/*
-		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
-		 * the super user when the security level is zero.
-		 */
-		if ((flags & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) ^
-		    (inode->u.ext2_i.i_flags &
-		     (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL))) {
-			/* This test looks nicer. Thanks to Pauline Middelink */
-			if (!capable(CAP_LINUX_IMMUTABLE))
-				return -EPERM;
-		} else
-			if ((current->fsuid != inode->i_uid) && 
-			    !capable(CAP_FOWNER))
-				return -EPERM;
-		if (IS_RDONLY(inode))
-			return -EROFS;
-		inode->u.ext2_i.i_flags = (inode->u.ext2_i.i_flags &
-					   ~EXT2_FL_USER_MODIFIABLE) | flags;
-		if (flags & EXT2_SYNC_FL)
-			inode->i_flags |= MS_SYNCHRONOUS;
-		else
-			inode->i_flags &= ~MS_SYNCHRONOUS;
-		if (flags & EXT2_APPEND_FL)
-			inode->i_flags |= S_APPEND;
-		else
-			inode->i_flags &= ~S_APPEND;
-		if (flags & EXT2_IMMUTABLE_FL)
-			inode->i_flags |= S_IMMUTABLE;
-		else
-			inode->i_flags &= ~S_IMMUTABLE;
-		if (flags & EXT2_NOATIME_FL)
-			inode->i_flags |= MS_NOATIME;
-		else
-			inode->i_flags &= ~MS_NOATIME;
-		inode->i_ctime = CURRENT_TIME;
-		mark_inode_dirty(inode);
-		return 0;
+		return ext2_set_flags(inode, flags);
 	case EXT2_IOC_GETVERSION:
 		return put_user(inode->u.ext2_i.i_version, (int *) arg);
 	case EXT2_IOC_SETVERSION:
-		if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
-			return -EPERM;
-		if (IS_RDONLY(inode))
-			return -EROFS;
-		if (get_user(inode->u.ext2_i.i_version, (int *) arg))
-			return -EFAULT;	
-		inode->i_ctime = CURRENT_TIME;
-		mark_inode_dirty(inode);
-		return 0;
+		return ext2_set_version(inode, arg);
 	default:
 		return -ENOTTY;
 	}
 }
+
+int ext2_fsattr (struct inode * inode, unsigned int len, int *in, int *out)
+{
+	int ret = 0;
+
+	ext2_debug ("len = %d, arg = %p/%p\n", len, in, out);
+
+	/* We have already checked that len >= 1 */
+	if (in) {
+		int e2flags = 0;
+		if (in[0] & ATTR_FLAG_SYNCRONOUS)
+			e2flags |= EXT2_SYNC_FL;
+		if (in[0] & ATTR_FLAG_NOATIME)
+			e2flags |= EXT2_NOATIME_FL;
+		if (in[0] & ATTR_FLAG_APPEND)
+			e2flags |= EXT2_APPEND_FL;
+		if (in[0] & ATTR_FLAG_IMMUTABLE)
+			e2flags |= EXT2_IMMUTABLE_FL;
+		if (in[0] & ATTR_FLAG_NODIRATIME && S_ISDIR(inode->i_mode))
+			e2flags |= EXT2_NOATIME_FL;
+		if (in[0] & ATTR_FLAG_DIRECT)
+			e2flags |= EXT2_DIRECT_FL;
+		if (in[0] & ATTR_FLAG_NODUMP)
+			e2flags |= EXT2_NODUMP_FL;
+		if (in[0] & ATTR_FLAG_COMPRESS)
+			e2flags |= EXT2_COMPR_FL;
+		if (in[0] & ATTR_FLAG_BTREE)
+			e2flags |= EXT2_BTREE_FL;
+		
+		ret = ext2_set_flags(inode, e2flags);
+	}
+	
+	if (out && !ret) {
+		int e2flags = inode->u.ext2_i.i_flags;
+		int attr = 0;
+		
+		if (e2flags & EXT2_SYNC_FL)
+			attr |= ATTR_FLAG_SYNCRONOUS;
+		if (e2flags & EXT2_NOATIME_FL)
+			attr |= ATTR_FLAG_NOATIME;
+		if (e2flags & EXT2_APPEND_FL)
+			attr |= ATTR_FLAG_APPEND;
+		if (e2flags & EXT2_IMMUTABLE_FL)
+			attr |= ATTR_FLAG_IMMUTABLE;
+		/* ext2fs has no internal NODIRATIME flag */
+		if (e2flags & EXT2_DIRECT_FL)
+			attr |= ATTR_FLAG_DIRECT;
+		if (e2flags & EXT2_NODUMP_FL)
+			attr |= ATTR_FLAG_NODUMP;
+		if (e2flags & EXT2_COMPR_FL)
+			attr |= ATTR_FLAG_COMPRESS;
+		if (e2flags & EXT2_BTREE_FL)
+			attr |= ATTR_FLAG_BTREE;
+
+		out[0] = attr;
+	}
+	
+	return ret;
+}
+
--- fs/ext2/super.c.~1~	Mon Dec  7 23:11:00 1998
+++ fs/ext2/super.c	Mon Dec  7 23:11:05 1998
@@ -138,7 +138,10 @@
 	ext2_put_super,
 	ext2_write_super,
 	ext2_statfs,
-	ext2_remount
+	ext2_remount,
+	NULL,			/* clear_inode */
+	NULL,			/* umount_begin */
+	ext2_fsattr,
 };
 
 /*
--- fs/ioctl.c.~1~	Mon Dec  7 23:11:01 1998
+++ fs/ioctl.c	Mon Dec  7 23:11:05 1998
@@ -99,3 +99,73 @@
 	unlock_kernel();
 	return error;
 }
+
+
+asmlinkage int sys_fsattr(const char * filename, unsigned int len,
+			  const char *in, char *out)
+{
+	struct dentry * dentry;
+	struct inode * inode;
+	int error = 0;
+	char * tmp;
+	__u32 inbuf[MAXATTRLEN], outbuf[MAXATTRLEN];
+	int i, ilen, ulen;
+	
+	/* The buffer length comes in as a byte count, but we'll be
+	 * dealing with __u32[] internally. */
+	if (len & (sizeof(__u32)-1)) {
+		error = -EINVAL;
+		goto out;
+	}
+
+	/* How many attribute words do we know about? */
+	ilen = len / sizeof(__u32);
+	if (ilen > MAXATTRLEN)
+		ilen = MAXATTRLEN;
+	if (!ilen)
+		goto out;
+	
+	for (i=0; i<ilen; i++)
+		outbuf[i] = 0;
+	
+	lock_kernel();
+
+	/* Work out how much of the user buffer we can deal with */
+	ulen = ilen * sizeof(__u32);
+	if (in) {
+		error = copy_from_user(inbuf, in, ulen);
+		if (error)
+			goto out_unlock;
+	}
+	error = out && verify_area(VERIFY_WRITE, out, ulen);
+	if (error)
+		goto out_unlock;
+
+	tmp = getname(filename);
+	error = PTR_ERR(tmp);
+	if (IS_ERR(tmp))
+		goto out_unlock;
+	
+	dentry = namei(filename);
+	putname(tmp);
+	error = PTR_ERR(dentry);
+	if (IS_ERR(dentry))
+		goto out_unlock;
+
+	inode = dentry->d_inode;
+	if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->fsattr)
+		error = inode->i_sb->s_op->fsattr(inode, ilen, 
+						  in ? inbuf : 0, 
+						  out ? outbuf : 0);
+	dput(dentry);
+	
+	if (!error && out)
+		error = copy_to_user(out, outbuf, ulen);
+
+out_unlock:
+	unlock_kernel();
+out:
+	return error;
+}
+
+
--- fs/open.c.~1~	Mon Dec  7 23:11:01 1998
+++ fs/open.c	Mon Dec  7 23:11:05 1998
@@ -656,6 +656,8 @@
 			goto cleanup_all;
 	}
 	f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
+	if (inode->i_flags & S_DIRECT)
+		f->f_flags |= O_DIRECT;
 
 	return f;
 
--- fs/raw.c.~1~	Tue Dec  8 00:28:40 1998
+++ fs/raw.c	Wed Dec  9 20:22:13 1998
@@ -0,0 +1,323 @@
+/*
+ *  linux/fs/raw.c	Stephen C. Tweedie, 1998
+ *
+ *  Copyright (C) 1998, Red Hat Software
+ *
+ *  This file may be redistributed under the terms of the GNU General
+ *  Public License version 2 or later.
+ */
+
+#include <linux/mm.h>
+#include <linux/locks.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+
+/* We split the raw IO into chunks of at most 64K to avoid reserving too
+   many resources for one single IO. */
+#define MAX_RAW_SECTORS 128
+#define MAX_RAW_PAGES (128 >> (PAGE_SHIFT - 9))
+
+#if 0
+#define debugit(str, args...) printk(KERN_ERR "DEBUG, %s(%s:%d) " str "\n", __FUNCTION__, __FILE__, __LINE__ , ## args)
+#else
+#define debugit(x...) 
+#endif
+
+static struct page *get_page(unsigned long address) 
+{
+	pgd_t *pgd;
+	pmd_t *pmd;
+	struct page *page;
+	unsigned long va;
+
+	pgd = pgd_offset(current->mm, address);
+	pmd = pmd_alloc(pgd, address);
+	if (pmd) {
+		pte_t * pte = pte_alloc(pmd, address);
+		if (pte && pte_present(*pte)) {
+			va = pte_page(*pte);
+			page = &mem_map[MAP_NR(va)];
+			debugit("Found user va %lx at kernel va %lx", 
+				address, va);
+			return page;
+		}
+	}
+	
+	printk(KERN_ERR "Missing page in lock_down_page\n");
+	return 0;
+}
+
+static void release_pages(unsigned long ptr, unsigned long end)
+{
+	struct page *page;
+	for (; ptr < end; ptr += PAGE_SIZE) {
+		page = get_page(ptr);
+		if (page) {
+			clear_bit(PG_locked, &page->flags);
+			wake_up(&page->wait);
+			__free_page(page);
+		}
+	}
+}
+
+static int lock_down_pages(int rw, struct page **page_array,
+			   unsigned long start, unsigned long end)
+{
+	int repeat = 0, doublepage = 0;
+	int err;
+	unsigned long ptr;
+	struct vm_area_struct *vma;
+	struct page *page;
+	int i;
+	
+	err = verify_area((rw == READ) ? VERIFY_WRITE : VERIFY_READ,
+			  (void *) start, end-start);
+	if (err)
+		goto out;
+
+ repeat:
+	debugit("Getting semaphore");
+	
+	down(&current->mm->mmap_sem);
+	
+	/* 
+	 * First of all, try to fault in all of the necessary pages
+	 */
+
+	vma = NULL;
+	err = -EFAULT;
+
+	debugit("Faulting pages");
+	
+	for (ptr = start; ptr < end; ptr += PAGE_SIZE) {
+		if (!vma || ptr >= vma->vm_end) {
+			vma = find_vma(current->mm, ptr);
+			if (!vma) 
+				goto out_unlock;
+		}
+		if (!handle_mm_fault(current, vma, ptr, (rw==READ))) 
+			goto out_unlock;
+	}
+	
+	/* 
+	 * Now, try to lock them.
+	 */
+
+	debugit("Locking pages");
+
+	for (ptr = start, i = 0; ptr < end; ptr += PAGE_SIZE, i++) {
+		page = get_page(ptr);
+		if (!page) {
+			printk (KERN_ERR "Missing page in rw_raw_io\n");
+			release_pages(start, ptr);
+			goto out_unlock;
+		}
+		if (PageLocked(page))
+			goto retry;
+		atomic_inc(&page->count);
+		set_bit(PG_locked, &page->flags);
+		page_array[i] = page;
+	}
+	err = 0;
+	
+ out_unlock:
+	up(&current->mm->mmap_sem);
+ out:
+
+	debugit("Returning %d", err);
+	return err;
+
+ retry:
+	/* 
+	 * Undo the locking so far, wait on the page we got to, and try again.
+	 */
+	release_pages(start, ptr);
+	up(&current->mm->mmap_sem);
+
+	/* 
+	 * Did we also unlock the page we got stuck on while we did this?
+	 */
+	if (!PageLocked(page)) {
+		/* If so, we may well have the page mapped twice in the
+		 * IO address range.  Bad news.  Of course, it _might_
+		 * just be a coincidence, but if it happens more than
+		 * once, chances are we have a double-mapped page. */
+		if (++doublepage >= 3) {
+			err = -EINVAL;
+			goto out;
+		}
+	}
+	
+	/*
+	 * Try again...
+	 */
+	wait_on_page(page);
+	if (++repeat < 16)
+		goto repeat;
+	err = -EAGAIN;
+	goto out;
+}
+
+static int rw_raw_io_chunk (int rw, int size, 
+			    int (*bmap) (struct inode *, int), 
+			    struct inode * inode, const char * buf,
+			    size_t count, loff_t offset, int max)
+{
+	struct page *page_array[MAX_RAW_PAGES];
+	struct buffer_head *tmp, *bh[MAX_RAW_SECTORS];
+	int bufs = 0;
+	int err = 0;
+	unsigned long start, userva, end;
+	int block = 0;
+	int page = 0, pages = 0, i, transferred;
+	char *data;
+	int blocksize_bits;
+	
+	/* Work out the start page for the user buffer, and the first
+           page beyond the end of it. */
+
+	start = ((unsigned long) buf) & PAGE_MASK;
+	end = ((unsigned long) buf + count + PAGE_SIZE - 1) & PAGE_MASK;
+	
+	err = lock_down_pages(rw, page_array, start, end);
+	if (err)
+		return err;
+
+	i = size;
+	blocksize_bits = 0;
+	while(i != 1) {
+		blocksize_bits++;
+		i >>= 1;
+	}
+
+	if (!bmap)
+		block = offset >> blocksize_bits;
+	
+	transferred = 0;
+		
+	/* Go through the chunk page by page, creating buffers for the
+           IO as we go. */
+	for (userva = start; ; userva += PAGE_SIZE) {
+		
+		data = (char *) page_address(page_array[page]);
+		data += ((unsigned long) buf + transferred) & ~PAGE_MASK;
+		
+		for (; (unsigned long) buf < (userva + PAGE_SIZE); ) {
+			tmp = get_unused_buffer_head(0);
+			if (!tmp) {
+				err = -ENOMEM;
+				goto error;
+			}
+			
+			if (bmap) {
+				block = bmap(inode, offset);
+				if (!block) {
+					err = -EINVAL;
+					goto error;
+				}
+			}
+			
+			/* If we have an upper limit on the device size,
+                           enforce that now.  File overflow is always
+                           indicated by a null block from bmap(), so we
+                           don't care about that.  */
+			if (max && block >= max)
+				goto do_it;
+
+			tmp->b_dev = B_FREE;
+			tmp->b_size = size;
+			tmp->b_data = data;
+			tmp->b_this_page = tmp;
+
+			debugit ("Buffer %p(%s+%d) at %p",
+				 tmp, kdevname(inode->i_rdev), block, data);
+			
+			init_buffer(tmp, inode->i_rdev, block,
+				    end_buffer_io_sync, NULL);
+			if (rw == WRITE) {
+				set_bit(BH_Uptodate, &tmp->b_state);
+				set_bit(BH_Dirty, &tmp->b_state);
+			}
+
+			bh[bufs++] = tmp;
+			buf += size;
+			transferred += size;
+			count -= size;
+			offset += size;
+			data += size;
+			block++;
+
+			if (count <= 0)
+				goto do_it;
+		}
+		page++;
+		pages++;
+	}
+
+ do_it:	
+	debugit("Submitting %d ios...", bufs);
+	ll_rw_block(rw, bufs, bh);
+
+ error:
+	for (i=bufs; --i >= 0; ) {
+		tmp = bh[i];
+		debugit("Wait for buffer %p", tmp);
+		wait_on_buffer(tmp);
+		if (!err) {
+			if (rw == READ && !buffer_uptodate(tmp))
+				err = -EIO;
+		}
+		free_async_buffers(tmp);
+	}
+
+	debugit("Releasing pages");
+
+	release_pages(start, end);
+	if (err < 0)
+		return err;
+	return transferred;
+}
+
+/*
+ * Perform raw IO.  We assume we already have the kernel lock at this point. 
+ */
+
+int rw_raw_io (int rw, int size, int (*bmap) (struct inode *, int), 
+	       struct inode * inode, const char * buf,
+	       size_t count, loff_t *ppos, int max)
+{
+	int chunk;
+	int retval;
+	int complete = 0;
+	loff_t offset = *ppos;
+	
+	/* 
+	 * Check that the buffer is blocksize-aligned, 
+	 * and that we are requesting a whole number of blocks. 
+	 */
+
+	if (((unsigned long) buf) & (size-1) || count & (size-1))
+		return -EINVAL;
+
+	while (count > 0) {
+		chunk = count;
+		if (chunk > (512 * MAX_RAW_SECTORS))
+			chunk = (512 * MAX_RAW_SECTORS);
+		retval = rw_raw_io_chunk (rw, size, bmap, inode, 
+					  buf, chunk, offset, max);
+		if (retval < 0)
+			return retval;
+		complete += retval;
+		if (retval < chunk)
+			break;
+		count -= chunk;
+		buf += chunk;
+		offset += chunk;
+	}
+	*ppos += complete;
+	return complete;
+}
+
--- include/asm-alpha/unistd.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-alpha/unistd.h	Mon Dec  7 23:11:05 1998
@@ -307,6 +307,7 @@
 #define __NR_getcwd			367
 #define __NR_capget			368
 #define __NR_capset			369
+#define __NR_fsattr			370
 
 #if defined(__LIBRARY__) && defined(__GNUC__)
 
--- include/asm-arm/fcntl.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-arm/fcntl.h	Mon Dec  7 23:11:05 1998
@@ -18,6 +18,7 @@
 #define FASYNC		020000	/* fcntl, for BSD compatibility */
 #define O_DIRECTORY	040000	/* must be a directory */
 #define O_NOFOLLOW	0100000	/* don't follow links */
+#define O_DIRECT	0200000	/* direct disk access hint */
 
 #define F_DUPFD		0	/* dup */
 #define F_GETFD		1	/* get f_flags */
--- include/asm-arm/unistd.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-arm/unistd.h	Mon Dec  7 23:11:05 1998
@@ -191,6 +191,10 @@
 #define __NR_pwrite			(__NR_SYSCALL_BASE+181)
 #define __NR_xstat			(__NR_SYSCALL_BASE+182)
 #define __NR_xmknod			(__NR_SYSCALL_BASE+183)
+#define __NR_capget			(__NR_SYSCALL_BASE+184)
+#define __NR_capset			(__NR_SYSCALL_BASE+185)
+#define __NR_sigaltstack		(__NR_SYSCALL_BASE+186)
+#define __NR_fsattr			(__NR_SYSCALL_BASE+187)
 
 #define __sys2(x) #x
 #define __sys1(x) __sys2(x)
--- include/asm-i386/fcntl.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-i386/fcntl.h	Mon Dec  7 23:11:05 1998
@@ -16,7 +16,7 @@
 #define O_NDELAY	O_NONBLOCK
 #define O_SYNC		 010000
 #define FASYNC		 020000	/* fcntl, for BSD compatibility */
-#define O_DIRECT	 040000	/* direct disk access hint - currently ignored */
+#define O_DIRECT	 040000	/* direct disk access hint */
 #define O_LARGEFILE	0100000
 #define O_DIRECTORY	0200000	/* must be a directory */
 #define O_NOFOLLOW	0400000 /* don't follow links */
--- include/asm-i386/unistd.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-i386/unistd.h	Mon Dec  7 23:11:05 1998
@@ -194,6 +194,7 @@
 #define __NR_sendfile		187
 #define __NR_getpmsg		188	/* some people actually want streams */
 #define __NR_putpmsg		189	/* some people actually want streams */
+#define __NR_fsattr		190
 
 /* user-visible error numbers are in the range -1 - -122: see <asm-i386/errno.h> */
 
--- include/asm-m68k/fcntl.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-m68k/fcntl.h	Mon Dec  7 23:11:05 1998
@@ -18,6 +18,7 @@
 #define FASYNC		020000	/* fcntl, for BSD compatibility */
 #define O_DIRECTORY	040000	/* must be a directory */
 #define O_NOFOLLOW	0100000	/* don't follow links */
+#define O_DIRECT	0200000	/* direct disk access hint */
 
 #define F_DUPFD		0	/* dup */
 #define F_GETFD		1	/* get f_flags */
--- include/asm-m68k/unistd.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-m68k/unistd.h	Mon Dec  7 23:11:05 1998
@@ -193,6 +193,7 @@
 #define __NR_sendfile		187
 #define __NR_getpmsg		188	/* some people actually want streams */
 #define __NR_putpmsg		189	/* some people actually want streams */
+#define __NR_fsattr		190
 
 /* user-visible error numbers are in the range -1 - -122: see
    <asm-m68k/errno.h> */
--- include/asm-mips/fcntl.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-mips/fcntl.h	Mon Dec  7 23:11:05 1998
@@ -25,7 +25,7 @@
 #define FASYNC		0x1000	/* fcntl, for BSD compatibility */
 #define O_LARGEFILE	0x2000	/* allow large file opens - currently ignored */
 #define O_NOFOLLOW	0x4000	/* Don't follow symbolic links */
-#define O_DIRECT	0x8000	/* direct disk access hint - currently ignored */
+#define O_DIRECT	0x8000	/* direct disk access hint */
 #define O_DIRECTORY	0x10000	/* must be a directory */
 
 #define O_NDELAY	O_NONBLOCK
--- include/asm-mips/unistd.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-mips/unistd.h	Mon Dec  7 23:11:05 1998
@@ -1196,11 +1196,12 @@
 #define __NR_sendfile			(__NR_Linux + 207)
 #define __NR_getpmsg			(__NR_Linux + 208)
 #define __NR_putpmsg			(__NR_Linux + 209)
+#define __NR_fsattr			(__NR_Linux + 210)
 
 /*
  * Offset of the last Linux flavoured syscall
  */
-#define __NR_Linux_syscalls		209
+#define __NR_Linux_syscalls		210
 
 #ifndef _LANGUAGE_ASSEMBLY
 
--- include/asm-ppc/fcntl.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-ppc/fcntl.h	Mon Dec  7 23:11:05 1998
@@ -17,6 +17,7 @@
 #define O_SYNC		010000
 #define FASYNC		020000	/* fcntl, for BSD compatibility */
 #define O_DIRECTORY	040000	/* must be a directory */
+#define O_DIRECT	080000	/* direct disk access hint */
 #define O_NOFOLLOW	0100000	/* don't follow links */
 
 #define F_DUPFD		0	/* dup */
--- include/asm-ppc/unistd.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-ppc/unistd.h	Mon Dec  7 23:11:05 1998
@@ -193,6 +193,7 @@
 #define __NR_sendfile		186
 #define __NR_getpmsg		187	/* some people actually want streams */
 #define __NR_putpmsg		188	/* some people actually want streams */
+#define __NR_fsattr             189
 
 #define __NR(n)	#n
 
--- include/asm-sparc/fcntl.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-sparc/fcntl.h	Mon Dec  7 23:11:05 1998
@@ -19,6 +19,7 @@
 #define O_NOCTTY	0x8000	/* not fcntl */
 #define O_DIRECTORY	0x10000	/* must be a directory */
 #define O_NOFOLLOW	0x20000	/* don't follow links */
+#define O_DIRECT	0x40000	/* raw, unbuffered access requested */
 
 #define F_DUPFD		0	/* dup */
 #define F_GETFD		1	/* get f_flags */
--- include/asm-sparc/unistd.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-sparc/unistd.h	Mon Dec  7 23:11:05 1998
@@ -271,6 +271,7 @@
 #define __NR_fdatasync          253
 #define __NR_nfsservctl         254
 #define __NR_aplib              255
+#define __NR_fsattr             256 /* Linux Specific                              */
 
 #define _syscall0(type,name) \
 type name(void) \
--- include/asm-sparc64/fcntl.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-sparc64/fcntl.h	Mon Dec  7 23:11:05 1998
@@ -19,6 +19,7 @@
 #define O_NOCTTY	0x8000	/* not fcntl */
 #define O_DIRECTORY	0x10000	/* must be a directory */
 #define O_NOFOLLOW	0x20000	/* don't follow links */
+#define O_DIRECT	0x40000	/* direct disk access hint */
 
 #define F_DUPFD		0	/* dup */
 #define F_GETFD		1	/* get f_flags */
--- include/asm-sparc64/unistd.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/asm-sparc64/unistd.h	Mon Dec  7 23:11:05 1998
@@ -271,6 +271,7 @@
 #define __NR_fdatasync          253
 #define __NR_nfsservctl         254
 #define __NR_aplib              255
+#define __NR_fsattr             256 /* Linux Specific                              */
 
 #define _syscall0(type,name) \
 type name(void) \
--- include/linux/ext2_fs.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/linux/ext2_fs.h	Tue Dec  8 17:36:16 1998
@@ -198,10 +198,11 @@
 #define EXT2_ECOMPR_FL			0x00000800 /* Compression error */
 /* End compression flags --- maybe not all used */	
 #define EXT2_BTREE_FL			0x00001000 /* btree format dir */
+#define EXT2_DIRECT_FL			0x00002000 /* force unbuffered io */
 #define EXT2_RESERVED_FL		0x80000000 /* reserved for ext2 lib */
 
-#define EXT2_FL_USER_VISIBLE		0x00001FFF /* User visible flags */
-#define EXT2_FL_USER_MODIFIABLE		0x000000FF /* User modifiable flags */
+#define EXT2_FL_USER_VISIBLE		0x00003FFF /* User visible flags */
+#define EXT2_FL_USER_MODIFIABLE		0x000020FF /* User modifiable flags */
 
 /*
  * ioctl commands
@@ -569,6 +570,7 @@
 /* ioctl.c */
 extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
 		       unsigned long);
+extern int ext2_fsattr (struct inode *, unsigned int, int *, int *);
 
 /* namei.c */
 extern void ext2_release (struct inode *, struct file *);
--- include/linux/fs.h.~1~	Mon Dec  7 23:11:01 1998
+++ include/linux/fs.h	Wed Dec  9 17:17:20 1998
@@ -95,6 +95,7 @@
 #define S_IMMUTABLE	512	/* Immutable file */
 #define MS_NOATIME	1024	/* Do not update access times. */
 #define MS_NODIRATIME   2048    /* Do not update directory access times */
+#define S_DIRECT	4096	/* Force raw (O_DIRECT) access */
 
 /*
  * Flags that can be altered by MS_REMOUNT
@@ -298,6 +299,12 @@
 #define ATTR_FORCE	512	/* Not a change, but a change it */
 #define ATTR_ATTR_FLAG	1024
 
+/* 
+ * How many __u32s are required to encode the maximum legal attribute?
+ */
+#define MAXATTRLEN	1
+
+
 /*
  * This is the Inode Attributes structure, used for notify_change().  It
  * uses the above definitions as flags, to know which values have changed.
@@ -327,6 +334,10 @@
 #define ATTR_FLAG_APPEND	4 	/* Append-only file */
 #define ATTR_FLAG_IMMUTABLE	8 	/* Immutable file */
 #define ATTR_FLAG_NODIRATIME	16 	/* Don't update atime for directory */
+#define ATTR_FLAG_DIRECT	32 	/* Force unbuffered IO */
+#define ATTR_FLAG_NODUMP	64 	/* Do not dump(8) this file */
+#define ATTR_FLAG_COMPRESS	128 	/* Try to compress this file */
+#define ATTR_FLAG_BTREE		256 	/* Use btree format on dir */
 
 /*
  * Includes for diskquotas and mount structures.
@@ -438,6 +449,7 @@
 #define FL_BROKEN	4	/* broken flock() emulation */
 #define FL_ACCESS	8	/* for processes suspended by mandatory locking */
 #define FL_LOCKD	16	/* lock held by rpc.lockd */
+#define FL_DIRECT	32	/* opened for raw (O_DIRECT) access */
 
 /*
  * The POSIX file lock owner is determined by
@@ -621,6 +633,7 @@
 	int (*remount_fs) (struct super_block *, int *, char *);
 	void (*clear_inode) (struct inode *);
 	void (*umount_begin) (struct super_block *);
+	int (*fsattr) (struct inode *, unsigned int, int *, int *);
 };
 
 struct dquot_operations {
@@ -832,8 +845,13 @@
 extern struct buffer_head * bread(kdev_t dev, int block, int size);
 extern struct buffer_head * breada(kdev_t dev,int block, int size, 
 				   unsigned int pos, unsigned int filesize);
+extern void end_buffer_io_sync(struct buffer_head *bh, int uptodate);
+extern struct buffer_head * get_unused_buffer_head(int async);
+extern void free_async_buffers (struct buffer_head * bh);
 
 extern int brw_page(int, struct page *, kdev_t, int [], int, int);
+extern int rw_raw_io (int, int, int (*bmap) (struct inode *, int), 
+		      struct inode *, const char *, size_t, loff_t *, int);
 
 extern int generic_readpage(struct file *, struct page *);
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
--- include/linux/swap.h.~1~	Mon Dec  7 12:05:54 1998
+++ include/linux/swap.h	Mon Dec  7 18:55:55 1998
@@ -90,6 +90,7 @@
 extern struct page * read_swap_cache_async(unsigned long, int);
 #define read_swap_cache(entry) read_swap_cache_async(entry, 1);
 extern int FASTCALL(swap_count(unsigned long));
+extern struct page * lookup_swap_cache(unsigned long); 
 /*
  * Make these inline later once they are working properly.
  */
