--- 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.
 */