diff -urN -X dontdiff linux/Documentation/filesystems/bfs.txt linux-bfs/Documentation/filesystems/bfs.txt
--- linux/Documentation/filesystems/bfs.txt     Fri Nov  5 18:30:13 1999
+++ linux-bfs/Documentation/filesystems/bfs.txt Sat Feb  5 09:06:53 2000
@@ -1,13 +1,9 @@
-The BFS filesystem is used on SCO UnixWare machines for /stand slice.
-By default, if you attempt to mount it read-write it will be automatically
-mounted read-only. If you want to enable (limited) write support, you need
-to select "BFS write support" when configuring the kernel. The write support
-at this stage is limited to the blocks preallocated for a given inode.
-This means that writes beyond the value of inode->iu_eblock will fail with EIO.
-In particular, this means you can create empty files but not write data to them
-or you can write data to the existing files and increase their size but not the
-number of blocks allocated to them. I am currently working on removing this
-limitation, i.e. ability to migrate inodes within BFS filesystem.
+BFS FILESYSTEM FOR LINUX
+========================
+
+The BFS filesystem is used by SCO UnixWare OS for the /stand slice, which
+usually contains the kernel image and a few other files required for the
+boot process.

In order to access /stand partition under Linux you obviously need to
know the partition number and the kernel must support UnixWare disk slices
@@ -29,7 +25,9 @@
# mount -t bfs -o loop stand.img /mnt/stand

this will allocate the first available loopback device (and load loop.o
-kernel module if necessary) automatically. Beware that umount will not
+kernel module if necessary) automatically. If the loopback driver is not
+loaded automatically, make sure that your kernel is compiled with kmod
+support (CONFIG_KMOD) enabled. Beware that umount will not
deallocate /dev/loopN device if /etc/mtab file on your system is a
symbolic link to /proc/mounts. You will need to do it manually using
"-d" switch of losetup(8). Read losetup(8) manpage for more info.
@@ -51,9 +49,9 @@

# od -Ad -tx4 stand.img | more

-The first 4 bytes should be 0x1BADFACE.
+The first 4 bytes should be 0x1badface.

-If you have any questions or suggestions regarding this BFS implementation
-please contact me:
+If you have any patches, questions or suggestions regarding this BFS
+implementation please contact the author:

Tigran A. Aivazian <[email protected]>.
diff -urN -X dontdiff linux/fs/Config.in linux-bfs/fs/Config.in
--- linux/fs/Config.in  Sat Jan 29 12:11:53 2000
+++ linux-bfs/fs/Config.in      Sat Feb  5 09:58:12 2000
@@ -15,8 +15,7 @@

dep_tristate 'Apple Macintosh filesystem support (EXPERIMENTAL)' CONFIG_HFS_FS $CONFIG_EXPERIMENTAL

-dep_tristate 'BFS filesystem (read only) support (EXPERIMENTAL)' CONFIG_BFS_FS $CONFIG_EXPERIMENTAL
-dep_bool '  BFS filesystem write support (DANGEROUS)' CONFIG_BFS_FS_WRITE $CONFIG_BFS_FS
+dep_tristate 'BFS filesystem support (EXPERIMENTAL)' CONFIG_BFS_FS $CONFIG_EXPERIMENTAL

# msdos filesystems
tristate 'DOS FAT fs support' CONFIG_FAT_FS
diff -urN -X dontdiff linux/fs/bfs/bfs_defs.h linux-bfs/fs/bfs/bfs_defs.h
--- linux/fs/bfs/bfs_defs.h     Fri Nov  5 18:30:13 1999
+++ linux-bfs/fs/bfs/bfs_defs.h Sat Feb  5 09:09:20 2000
@@ -5,7 +5,6 @@
#define su_lf_ioff     u.bfs_sb.si_lf_ioff
#define su_lf_sblk     u.bfs_sb.si_lf_sblk
#define su_lf_eblk     u.bfs_sb.si_lf_eblk
-#define su_bmap                u.bfs_sb.si_bmap
#define su_imap                u.bfs_sb.si_imap
#define su_sbh         u.bfs_sb.si_sbh
#define su_bfs_sb      u.bfs_sb.si_bfs_sb
@@ -13,3 +12,6 @@
#define iu_dsk_ino     u.bfs_i.i_dsk_ino
#define iu_sblock      u.bfs_i.i_sblock
#define iu_eblock      u.bfs_i.i_eblock
+
+#define printf(format, args...) \
+       printk(KERN_ERR "BFS-fs: " __FUNCTION__ "(): " format, ## args)
diff -urN -X dontdiff linux/fs/bfs/dir.c linux-bfs/fs/bfs/dir.c
--- linux/fs/bfs/dir.c  Thu Jan 13 21:21:07 2000
+++ linux-bfs/fs/bfs/dir.c      Sat Feb  5 09:07:43 2000
@@ -14,9 +14,9 @@
#undef DEBUG

#ifdef DEBUG
-#define DBG(x...)      printk(x)
+#define dprintf(x...)  printf(x)
#else
-#define DBG(x...)
+#define dprintf(x...)
#endif

static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int ino);
@@ -38,14 +38,13 @@
       int block;

       if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
-               printk(KERN_ERR "BFS-fs: %s(): Bad inode or not a directory %s:%08lx\n",
-                       __FUNCTION__, bdevname(dev), dir->i_ino);
+               printf("Bad inode or not a directory %s:%08lx\n", bdevname(dev), dir->i_ino);
               return -EBADF;
       }

       if (f->f_pos & (BFS_DIRENT_SIZE-1)) {
-               printk(KERN_ERR "BFS-fs: %s(): Bad f_pos=%08lx for %s:%08lx\n",
-                       __FUNCTION__, (unsigned long)f->f_pos, bdevname(dev), dir->i_ino);
+               printf("Bad f_pos=%08lx for %s:%08lx\n", (unsigned long)f->f_pos,
+                       bdevname(dev), dir->i_ino);
               return -EBADF;
       }

@@ -189,9 +188,8 @@
               goto out_brelse;

       if (!inode->i_nlink) {
-               printk(KERN_WARNING
-               "BFS-fs: %s(): unlinking non-existent file %s:%lu (nlink=%d)\n",
-               __FUNCTION__, bdevname(inode->i_dev), inode->i_ino, inode->i_nlink);
+               printf("unlinking non-existent file %s:%lu (nlink=%d)\n", bdevname(inode->i_dev),
+                               inode->i_ino, inode->i_nlink);
               inode->i_nlink = 1;
       }
       de->ino = 0;
@@ -294,7 +292,7 @@
       kdev_t dev;
       int i;

-       DBG(KERN_ERR "BFS-fs: %s(%s,%d)\n", __FUNCTION__, name, namelen);
+       dprintf("name=%s, namelen=%d\n", name, namelen);

       if (!namelen)
               return -ENOENT;
diff -urN -X dontdiff linux/fs/bfs/file.c linux-bfs/fs/bfs/file.c
--- linux/fs/bfs/file.c Thu Jan 13 21:21:07 2000
+++ linux-bfs/fs/bfs/file.c     Mon Feb  7 19:18:16 2000
@@ -5,15 +5,17 @@
 */

#include <linux/fs.h>
+#include <linux/locks.h>
#include <linux/bfs_fs.h>
+#include <linux/smp_lock.h>
#include "bfs_defs.h"

#undef DEBUG

#ifdef DEBUG
-#define DBG(x...)      printk(x)
+#define dprintf(x...)  printf(x)
#else
-#define DBG(x...)
+#define dprintf(x...)
#endif

static ssize_t bfs_file_write(struct file * f, const char * buf, size_t count, loff_t *ppos)
@@ -36,18 +38,104 @@
       fasync:                 NULL,
};

+static int bfs_move_block(unsigned long from, unsigned long to, kdev_t dev)
+{
+       struct buffer_head *bh, *new = NULL;
+
+       bh = bread(dev, from, BFS_BSIZE);
+       if (!bh)
+               return -EIO;
+       new = getblk(dev, to, BFS_BSIZE);
+       memcpy(new->b_data, bh->b_data, bh->b_size);
+       mark_buffer_dirty(new, 1);
+       bforget(bh);
+       brelse(new);
+       return 0;
+}
+
+static int bfs_move_blocks(kdev_t dev, unsigned long start, unsigned long end,
+                               unsigned long where)
+{
+       unsigned long i;
+
+       dprintf("%08lx-%08lx->%08lx\n", start, end, where);
+       for (i = start; i <= end; i++)
+               if(bfs_move_block(i, where + i, dev)) {
+                       dprintf("failed to move block %08lx -> %08lx\n", i, where + i);
+                       return -EIO;
+               }
+       return 0;
+}
+
static int bfs_get_block(struct inode * inode, long block,
       struct buffer_head * bh_result, int create)
{
-       long phys = inode->iu_sblock + block;
-       if (!create || phys <= inode->iu_eblock) {
+       long phys, next_free_block;
+       int err;
+       struct super_block *s = inode->i_sb;
+
+       if (block < 0 || block > s->su_blocks)
+               return -EIO;
+
+       phys = inode->iu_sblock + block;
+       if (!create) {
+               if (phys <= inode->iu_eblock) {
+                       dprintf("c=%d, b=%08lx, phys=%08lx (granted)\n", create, block, phys);
+                       bh_result->b_dev = inode->i_dev;
+                       bh_result->b_blocknr = phys;
+                       bh_result->b_state |= (1UL << BH_Mapped);
+               }
+               return 0;
+       }
+
+       /* if the file is not empty and the requested block is within the range
+          of blocks allocated for this file, we can grant it */
+       if (inode->i_size && phys <= inode->iu_eblock) {
+               dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n", create, block, phys);
               bh_result->b_dev = inode->i_dev;
               bh_result->b_blocknr = phys;
               bh_result->b_state |= (1UL << BH_Mapped);
               return 0;
-       }
-       /* no support for file migration, working on it */
-       return -EIO;
+       }
+
+       /* the rest has to be protected against itself */
+       lock_kernel();
+
+       /* if the last data block for this file is the last allocated block, we can
+          extend the file trivially, without moving it anywhere */
+       if (inode->iu_eblock == s->su_lf_eblk) {
+               dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n", create, block, phys);
+               bh_result->b_dev = inode->i_dev;
+               bh_result->b_blocknr = phys;
+               bh_result->b_state |= (1UL << BH_Mapped);
+               s->su_lf_eblk = inode->iu_eblock = inode->iu_sblock + block;
+               mark_inode_dirty(inode);
+               mark_buffer_dirty(s->su_sbh, 1);
+               err = 0;
+               goto out;
+       }
+
+       /* Ok, we have to move this entire file to the next free block */
+       next_free_block = s->su_lf_eblk + 1;
+       if (inode->iu_sblock) {
+               err = bfs_move_blocks(inode->i_dev, inode->iu_sblock, inode->iu_eblock, next_free_block);
+               if (err) {
+                       dprintf("failed to move ino=%08lx -> possible fs corruption\n", inode->i_ino);
+                       goto out;
+               }
+       } else
+               err = 0;
+
+       inode->iu_sblock = next_free_block;
+       s->su_lf_eblk = inode->iu_eblock = next_free_block + block;
+       mark_inode_dirty(inode);
+       mark_buffer_dirty(s->su_sbh, 1);
+       bh_result->b_dev = inode->i_dev;
+       bh_result->b_blocknr = inode->iu_sblock + block;
+       bh_result->b_state |= (1UL << BH_Mapped);
+out:
+       unlock_kernel();
+       return err;
}

struct inode_operations bfs_file_inops = {
diff -urN -X dontdiff linux/fs/bfs/inode.c linux-bfs/fs/bfs/inode.c
--- linux/fs/bfs/inode.c        Tue Nov  9 18:02:33 1999
+++ linux-bfs/fs/bfs/inode.c    Sat Feb  5 09:32:52 2000
@@ -5,7 +5,6 @@
 *     From fs/minix, Copyright (C) 1991, 1992 Linus Torvalds.
 */

-#include <linux/config.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/slab.h>
@@ -18,15 +17,15 @@
#include "bfs_defs.h"

MODULE_AUTHOR("Tigran A. Aivazian");
-MODULE_DESCRIPTION("UnixWare BFS filesystem for Linux");
+MODULE_DESCRIPTION("SCO UnixWare BFS filesystem for Linux");
EXPORT_NO_SYMBOLS;

#undef DEBUG

#ifdef DEBUG
-#define DBG(x...)      printk(x)
+#define dprintf(x...)  printf(x)
#else
-#define DBG(x...)
+#define dprintf(x...)
#endif

void dump_imap(const char *prefix, struct super_block * s);
@@ -40,8 +39,7 @@
       int block, off;

       if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) {
-               printk(KERN_ERR "BFS-fs: %s(): Bad inode number %s:%08lx\n",
-                       __FUNCTION__, bdevname(dev), ino);
+               printf("Bad inode number %s:%08lx\n", bdevname(dev), ino);
               make_bad_inode(inode);
               return;
       }
@@ -49,8 +47,7 @@
       block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
       bh = bread(dev, block, BFS_BSIZE);
       if (!bh) {
-               printk(KERN_ERR "BFS-fs: %s(): Unable to read inode %s:%08lx\n",
-                       __FUNCTION__, bdevname(dev), ino);
+               printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino);
               make_bad_inode(inode);
               return;
       }
@@ -94,16 +91,14 @@
       int block, off;

       if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) {
-               printk(KERN_ERR "BFS-fs: %s(): Bad inode number %s:%08lx\n",
-                       __FUNCTION__, bdevname(dev), ino);
+               printf("Bad inode number %s:%08lx\n", bdevname(dev), ino);
               return;
       }

       block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
       bh = bread(dev, block, BFS_BSIZE);
       if (!bh) {
-               printk(KERN_ERR "BFS-fs: %s(): Unable to read inode %s:%08lx\n",
-                       __FUNCTION__, bdevname(dev), ino);
+               printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino);
               return;
       }

@@ -140,30 +135,12 @@
       int block, off;
       struct super_block * s = inode->i_sb;

-       DBG(KERN_ERR "%s(ino=%08lx)\n", __FUNCTION__, inode->i_ino);
+       dprintf("ino=%08lx\n", inode->i_ino);

-       if (!inode)
+       if (!inode || !inode->i_dev || inode->i_count > 1 || inode->i_nlink || !s)
               return;
-       if (!inode->i_dev) {
-               printk(KERN_ERR "BFS-fs: free_inode(%08lx) !dev\n", inode->i_ino);
-               return;
-       }
-       if (inode->i_count > 1) {
-               printk(KERN_ERR "BFS-fs: free_inode(%08lx) count=%d\n",
-                       inode->i_ino, inode->i_count);
-               return;
-       }
-       if (inode->i_nlink) {
-               printk(KERN_ERR "BFS-fs: free_inode(%08lx) nlink=%d\n",
-                       inode->i_ino, inode->i_nlink);
-               return;
-       }
-       if (!inode->i_sb) {
-               printk(KERN_ERR "BFS-fs: free_inode(%08lx) !sb\n", inode->i_ino);
-               return;
-       }
       if (inode->i_ino < BFS_ROOT_INO || inode->i_ino > inode->i_sb->su_lasti) {
-               printk(KERN_ERR "BFS-fs: free_inode(%08lx) invalid ino\n", inode->i_ino);
+               printf("invalid ino=%08lx\n", inode->i_ino);
               return;
       }

@@ -173,8 +150,7 @@
       block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
       bh = bread(dev, block, BFS_BSIZE);
       if (!bh) {
-               printk(KERN_ERR "BFS-fs: %s(): Unable to read inode %s:%08lx\n",
-                       __FUNCTION__, bdevname(dev), ino);
+               printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino);
               return;
       }
       off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK;
@@ -189,6 +165,14 @@
       di->i_sblock = 0;
       mark_buffer_dirty(bh, 1);
       brelse(bh);
+
+       /* if this was the last file, make the previous
+          block "last files last block" even if there is no real file there,
+          saves us 1 gap */
+       if (s->su_lf_eblk == inode->iu_eblock) {
+               s->su_lf_eblk = inode->iu_sblock - 1;
+               mark_buffer_dirty(s->su_sbh, 1);
+       }
       clear_inode(inode);
}

@@ -196,7 +180,6 @@
{
       brelse(s->su_sbh);
       kfree(s->su_imap);
-       kfree(s->su_bmap);
       MOD_DEC_USE_COUNT;
}

@@ -251,7 +234,7 @@
               else
                       strcat(tmpbuf, "0");
       }
-       printk(KERN_ERR "BFS-fs: %s: lasti=%d <%s>\n", prefix, s->su_lasti, tmpbuf);
+       printk(KERN_ERR "BFS-fs: %s: lasti=%08lx <%s>\n", prefix, s->su_lasti, tmpbuf);
       free_page((unsigned long)tmpbuf);
#endif
}
@@ -263,7 +246,7 @@
       struct buffer_head * bh;
       struct bfs_super_block * bfs_sb;
       struct inode * inode;
-       int i, imap_len, bmap_len;
+       int i, imap_len;

       MOD_INC_USE_COUNT;
       lock_super(s);
@@ -272,58 +255,43 @@
       s->s_blocksize = BFS_BSIZE;
       s->s_blocksize_bits = BFS_BSIZE_BITS;

-       /* read ahead 8K to get inodes as we'll need them in a tick */
-       bh = breada(dev, 0, BFS_BSIZE, 0, 8192);
+       bh = bread(dev, 0, BFS_BSIZE);
       if(!bh)
               goto out;
       bfs_sb = (struct bfs_super_block *)bh->b_data;
       if (bfs_sb->s_magic != BFS_MAGIC) {
               if (!silent)
-                       printk(KERN_ERR "BFS-fs: No BFS filesystem on %s (magic=%08x)\n",
-                                       bdevname(dev), bfs_sb->s_magic);
+                       printf("No BFS filesystem on %s (magic=%08x)\n",
+                               bdevname(dev), bfs_sb->s_magic);
               goto out;
       }
       if (BFS_UNCLEAN(bfs_sb, s) && !silent)
-               printk(KERN_WARNING "BFS-fs: %s is unclean\n", bdevname(dev));
+               printf("%s is unclean, continuing\n", bdevname(dev));

-#ifndef CONFIG_BFS_FS_WRITE
-       s->s_flags |= MS_RDONLY;
-#endif
       s->s_magic = BFS_MAGIC;
       s->su_bfs_sb = bfs_sb;
       s->su_sbh = bh;
       s->su_lasti = (bfs_sb->s_start - BFS_BSIZE)/sizeof(struct bfs_inode)
                       + BFS_ROOT_INO - 1;

-       bmap_len = sizeof(struct bfs_bmap) * s->su_lasti;
-       s->su_bmap = kmalloc(bmap_len, GFP_KERNEL);
-       if (!s->su_bmap)
-               goto out;
-       memset(s->su_bmap, 0, bmap_len);
       imap_len = s->su_lasti/8 + 1;
       s->su_imap = kmalloc(imap_len, GFP_KERNEL);
-       if (!s->su_imap) {
-               kfree(s->su_bmap);
+       if (!s->su_imap)
               goto out;
-       }
       memset(s->su_imap, 0, imap_len);
-       for (i=0; i<BFS_ROOT_INO; i++) {
-               s->su_bmap[i].start = s->su_bmap[i].end = 0;
+       for (i=0; i<BFS_ROOT_INO; i++)
               set_bit(i, s->su_imap);
-       }

       s->s_op = &bfs_sops;
       inode = iget(s, BFS_ROOT_INO);
       if (!inode) {
               kfree(s->su_imap);
-               kfree(s->su_bmap);
               goto out;
       }
       s->s_root = d_alloc_root(inode);
       if (!s->s_root) {
               iput(inode);
               kfree(s->su_imap);
-               kfree(s->su_bmap);
               goto out;
       }

@@ -335,10 +303,9 @@
       s->su_lf_ioff = 0;
       for (i=BFS_ROOT_INO; i<=s->su_lasti; i++) {
               inode = iget(s,i);
-               if (inode->iu_dsk_ino == 0) {
+               if (inode->iu_dsk_ino == 0)
                       s->su_freei++;
-                       s->su_bmap[i].start = s->su_bmap[i].end = 0;
-               } else {
+               else {
                       set_bit(i, s->su_imap);
                       s->su_freeb -= inode->i_blocks;
                       if (inode->iu_eblock > s->su_lf_eblk) {
@@ -346,8 +313,6 @@
                               s->su_lf_sblk = inode->iu_sblock;
                               s->su_lf_ioff = BFS_INO2OFF(i);
                       }
-                       s->su_bmap[i].start = inode->iu_sblock;
-                       s->su_bmap[i].end = inode->iu_eblock;
               }
               iput(inode);
       }
diff -urN -X dontdiff linux/include/linux/bfs_fs_sb.h linux-bfs/include/linux/bfs_fs_sb.h
--- linux/include/linux/bfs_fs_sb.h     Fri Nov  5 18:30:13 1999
+++ linux-bfs/include/linux/bfs_fs_sb.h Sat Feb  5 09:09:10 2000
@@ -7,13 +7,6 @@
#define _LINUX_BFS_FS_SB

/*
- * BFS block map entry, an array of these is kept in bfs_sb_info.
- */
- struct bfs_bmap {
-       unsigned long start, end;
- };
-
-/*
 * BFS file system in-core superblock info
 */
struct bfs_sb_info {
@@ -24,7 +17,6 @@
       unsigned long si_lf_sblk;
       unsigned long si_lf_eblk;
       unsigned long si_lasti;
-       struct bfs_bmap * si_bmap;
       char * si_imap;
       struct buffer_head * si_sbh;            /* buffer header w/superblock */
       struct bfs_super_block * si_bfs_sb;     /* superblock in si_sbh->b_data */