On Mon, Jul 18, 2016 at 12:25:27PM +0200, Mark Kettenis wrote:
> > Date: Mon, 18 Jul 2016 11:52:15 +0300
> > From: Artturi Alm <[email protected]>
> > 
> > iHi,
> > 
> > optimistic as always, this would be one item off the list making me own
> > this shit.:) octeon might benefit also, but haven't tested on one.
> > didn't figure out how to make a proper diff for ports/sysutils/u-boot/
> > against cvs, so here is diff to be applied directly on top of recent
> > u-boot master. whacked out of sys/lib/libsa/.
> 
> Having ffs support in u-boot would indeed be useful.  That said, the
> recommended way to load OpenBSD kernels is through the efiboot
> bootloader as it will provide the kernel with initial entropy.

Loading directly from ffs would still require a u-boot header with a soc
specific load address as well.  And it would need to understand disklabels.

> 
> Ultimately this code should be contributed back upstream.  We don't
> really want to maintain large patches like this in the ports tree.

Indeed, ideally we'd not have any patches in u-boot or dtbs at all.
Look at all the pain FreeBSD seems to go through with CONFIG_API.

> 
> Cheers,
> 
> Mark
> 
> 
> > diff --git a/cmd/Kconfig b/cmd/Kconfig
> > index d69b817..deaf9f3 100644
> > --- a/cmd/Kconfig
> > +++ b/cmd/Kconfig
> > @@ -689,6 +689,12 @@ config CMD_FAT
> >     help
> >       Support for the FAT fs
> >  
> > +config CMD_FFS
> > +   bool "FFS command support"
> > +   default y
> > +   help
> > +     Support for the FFS fs
> > +
> >  config CMD_FS_GENERIC
> >     bool "filesystem commands"
> >     help
> > diff --git a/cmd/Makefile b/cmd/Makefile
> > index a1731be..7c45710 100644
> > --- a/cmd/Makefile
> > +++ b/cmd/Makefile
> > @@ -53,6 +53,7 @@ obj-$(CONFIG_HUSH_PARSER) += exit.o
> >  obj-$(CONFIG_CMD_EXT4) += ext4.o
> >  obj-$(CONFIG_CMD_EXT2) += ext2.o
> >  obj-$(CONFIG_CMD_FAT) += fat.o
> > +obj-$(CONFIG_CMD_FFS) += ffs.o
> >  obj-$(CONFIG_CMD_FDC) += fdc.o
> >  obj-$(CONFIG_CMD_FDT) += fdt.o
> >  obj-$(CONFIG_CMD_FITUPD) += fitupd.o
> > diff --git a/cmd/ffs.c b/cmd/ffs.c
> > new file mode 100644
> > index 0000000..d0536bd
> > --- /dev/null
> > +++ b/cmd/ffs.c
> > @@ -0,0 +1,67 @@
> > +/*
> > + * (C) Copyright 2002
> > + * Richard Jones, [email protected]
> > + *
> > + * SPDX-License-Identifier:        GPL-2.0+
> > + */
> > +
> > +/*
> > + * Boot support
> > + */
> > +#include <common.h>
> > +#include <blk.h>
> > +#include <command.h>
> > +#include <s_record.h>
> > +#include <net.h>
> > +#include <ata.h>
> > +#include <asm/io.h>
> > +#include <mapmem.h>
> > +#include <part.h>
> > +#include <fs.h>
> > +#include <ffs.h>
> > +
> > +int do_ffs_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> > +{
> > +   return do_size(cmdtp, flag, argc, argv, FS_TYPE_FFS);
> > +}
> > +
> > +U_BOOT_CMD(
> > +   ffssize,        4,      0,      do_ffs_size,
> > +   "determine a file's size",
> > +   "<interface> <dev[:part]> <filename>\n"
> > +   "    - Find file 'filename' from 'dev' on 'interface'\n"
> > +   "      and determine its size."
> > +);
> > +
> > +int do_ffs_fsload (cmd_tbl_t *cmdtp, int flag, int argc, char * const 
> > argv[])
> > +{
> > +   return do_load(cmdtp, flag, argc, argv, FS_TYPE_FFS);
> > +}
> > +
> > +
> > +U_BOOT_CMD(
> > +   ffsload,        7,      0,      do_ffs_fsload,
> > +   "load binary file from a ufs filesystem",
> > +   "<interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]\n"
> > +   "    - Load binary file 'filename' from 'dev' on 'interface'\n"
> > +   "      to address 'addr' from ffs filesystem.\n"
> > +   "      'pos' gives the file position to start loading from.\n"
> > +   "      If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
> > +   "      'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
> > +   "      the load stops on end of file.\n"
> > +   "      If either 'pos' or 'bytes' are not aligned to\n"
> > +   "      ARCH_DMA_MINALIGN then a misaligned buffer warning will\n"
> > +   "      be printed and performance will suffer for the load."
> > +);
> > +
> > +static int do_ffs_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const 
> > argv[])
> > +{
> > +   return do_ls(cmdtp, flag, argc, argv, FS_TYPE_FFS);
> > +}
> > +
> > +U_BOOT_CMD(
> > +   ffsls,  4,      1,      do_ffs_ls,
> > +   "list files in a directory (default /)",
> > +   "<interface> [<dev[:part]>] [directory]\n"
> > +   "    - list files from 'dev' on 'interface' in a 'directory'"
> > +);
> > diff --git a/fs/Makefile b/fs/Makefile
> > index 51d06fc..7e53006 100644
> > --- a/fs/Makefile
> > +++ b/fs/Makefile
> > @@ -16,6 +16,7 @@ obj-$(CONFIG_CMD_CBFS) += cbfs/
> >  obj-$(CONFIG_CMD_CRAMFS) += cramfs/
> >  obj-$(CONFIG_FS_EXT4) += ext4/
> >  obj-y += fat/
> > +obj-$(CONFIG_CMD_FFS) += ffs/
> >  obj-$(CONFIG_CMD_JFFS2) += jffs2/
> >  obj-$(CONFIG_CMD_REISER) += reiserfs/
> >  obj-$(CONFIG_SANDBOX) += sandbox/
> > diff --git a/fs/ffs/Makefile b/fs/ffs/Makefile
> > new file mode 100644
> > index 0000000..ff1e371
> > --- /dev/null
> > +++ b/fs/ffs/Makefile
> > @@ -0,0 +1 @@
> > +obj-y := ufs.o
> > diff --git a/fs/ffs/dinode.h b/fs/ffs/dinode.h
> > new file mode 100644
> > index 0000000..5ad69be
> > --- /dev/null
> > +++ b/fs/ffs/dinode.h
> > @@ -0,0 +1,156 @@
> > +/* $OpenBSD: dinode.h,v 1.18 2013/05/30 19:19:09 guenther Exp $    */
> > +/* $NetBSD: dinode.h,v 1.7 1995/06/15 23:22:48 cgd Exp $   */
> > +
> > +/*
> > + * Copyright (c) 1982, 1989, 1993
> > + * The Regents of the University of California.  All rights reserved.
> > + * (c) UNIX System Laboratories, Inc.
> > + * All or some portions of this file are derived from material licensed
> > + * to the University of California by American Telephone and Telegraph
> > + * Co. or Unix System Laboratories, Inc. and are reproduced herein with
> > + * the permission of UNIX System Laboratories, Inc.
> > + *
> > + * Redistribution and use in source and binary forms, with or without
> > + * modification, are permitted provided that the following conditions
> > + * are met:
> > + * 1. Redistributions of source code must retain the above copyright
> > + *    notice, this list of conditions and the following disclaimer.
> > + * 2. Redistributions in binary form must reproduce the above copyright
> > + *    notice, this list of conditions and the following disclaimer in the
> > + *    documentation and/or other materials provided with the distribution.
> > + * 3. Neither the name of the University nor the names of its contributors
> > + *    may be used to endorse or promote products derived from this software
> > + *    without specific prior written permission.
> > + *
> > + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
> > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
> > PURPOSE
> > + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
> > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
> > CONSEQUENTIAL
> > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
> > STRICT
> > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 
> > WAY
> > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> > + * SUCH DAMAGE.
> > + *
> > + * @(#)dinode.h    8.9 (Berkeley) 3/29/95
> > + */
> > +
> > +#ifndef _UFS_DINODE_H_
> > +#define _UFS_DINODE_H_
> > +
> > +/*
> > + * UFS directories use 32bit inode numbers internally, regardless
> > + * of what the system on top of it uses.
> > + */
> > +typedef u_int32_t  ufsino_t;
> > +
> > +/*
> > + * The root inode is the root of the file system.  Inode 0 can't be used 
> > for
> > + * normal purposes and historically bad blocks were linked to inode 1, thus
> > + * the root inode is 2.  (Inode 1 is no longer used for this purpose, 
> > however
> > + * numerous dump tapes make this assumption, so we are stuck with it).
> > + */
> > +#define    ROOTINO ((ufsino_t)2)
> > +
> > +/*
> > + * A dinode contains all the meta-data associated with a UFS file.
> > + * This structure defines the on-disk format of a dinode. Since
> > + * this structure describes an on-disk structure, all its fields
> > + * are defined by types with precise widths.
> > + */
> > +#define    NXADDR  2                       /* External addresses in inode 
> > */
> > +#define    NDADDR  12                      /* Direct addresses in inode. */
> > +#define    NIADDR  3                       /* Indirect addresses in inode. 
> > */
> > +
> > +struct     ufs1_dinode {
> > +   u_int16_t       di_mode;        /*   0: IFMT, permissions; see below. */
> > +   int16_t         di_nlink;       /*   2: File link count. */
> > +   union {
> > +           u_int16_t oldids[2];    /*   4: Ffs: old user and group ids. */
> > +           u_int32_t inumber;      /*   4: Lfs: inode number. */
> > +   } di_u;
> > +   u_int64_t       di_size;        /*   8: File byte count. */
> > +   int32_t         di_atime;       /*  16: Last access time. */
> > +   int32_t         di_atimensec;   /*  20: Last access time. */
> > +   int32_t         di_mtime;       /*  24: Last modified time. */
> > +   int32_t         di_mtimensec;   /*  28: Last modified time. */
> > +   int32_t         di_ctime;       /*  32: Last inode change time. */
> > +   int32_t         di_ctimensec;   /*  36: Last inode change time. */
> > +   int32_t         di_db[NDADDR];  /*  40: Direct disk blocks. */
> > +   int32_t         di_ib[NIADDR];  /*  88: Indirect disk blocks. */
> > +   u_int32_t       di_flags;       /* 100: Status flags (chflags). */
> > +   int32_t         di_blocks;      /* 104: Blocks actually held. */
> > +   int32_t         di_gen;         /* 108: Generation number. */
> > +   u_int32_t       di_uid;         /* 112: File owner. */
> > +   u_int32_t       di_gid;         /* 116: File group. */
> > +   int32_t         di_spare[2];    /* 120: Reserved; currently unused */
> > +};
> > +
> > +struct ufs2_dinode {
> > +   u_int16_t       di_mode;        /*   0: IFMT, permissions; see below. */
> > +   int16_t         di_nlink;       /*   2: File link count. */
> > +   u_int32_t       di_uid;         /*   4: File owner. */
> > +   u_int32_t       di_gid;         /*   8: File group. */
> > +   u_int32_t       di_blksize;     /*  12: Inode blocksize. */
> > +   u_int64_t       di_size;        /*  16: File byte count. */
> > +   u_int64_t       di_blocks;      /*  24: Bytes actually held. */
> > +   int64_t         di_atime;       /*  32: Last access time. */
> > +   int64_t         di_mtime;       /*  40: Last modified time. */
> > +   int64_t         di_ctime;       /*  48: Last inode change time. */
> > +   int64_t         di_birthtime;   /*  56: Inode creation time. */
> > +   int32_t         di_mtimensec;   /*  64: Last modified time. */
> > +   int32_t         di_atimensec;   /*  68: Last access time. */
> > +   int32_t         di_ctimensec;   /*  72: Last inode change time. */
> > +   int32_t         di_birthnsec;   /*  76: Inode creation time. */
> > +   int32_t         di_gen;         /*  80: Generation number. */
> > +   u_int32_t       di_kernflags;   /*  84: Kernel flags. */
> > +   u_int32_t       di_flags;       /*  88: Status flags (chflags). */
> > +   int32_t         di_extsize;     /*  92: External attributes block. */
> > +   int64_t         di_extb[NXADDR];/*  96: External attributes block. */
> > +   int64_t         di_db[NDADDR];  /* 112: Direct disk blocks. */
> > +   int64_t         di_ib[NIADDR];  /* 208: Indirect disk blocks. */
> > +   int64_t         di_spare[3];    /* 232: Reserved; currently unused */
> > +};
> > +
> > +/*
> > + * The di_db fields may be overlaid with other information for
> > + * file types that do not have associated disk storage. Block
> > + * and character devices overlay the first data block with their
> > + * dev_t value. Short symbolic links place their path in the
> > + * di_db area.
> > + */
> > +#define    di_inumber      di_u.inumber
> > +#define    di_ogid         di_u.oldids[1]
> > +#define    di_ouid         di_u.oldids[0]
> > +#define    di_rdev         di_db[0]
> > +#define    di_shortlink    di_db
> > +
> > +#define MAXSYMLINKLEN_UFS1 ((NDADDR + NIADDR) * sizeof(int32_t))
> > +#define MAXSYMLINKLEN_UFS2 ((NDADDR + NIADDR) * sizeof(int64_t))
> > +
> > +#define MAXSYMLINKLEN(ip) \
> > +   ((ip)->i_ump->um_fstype == UM_UFS1) ? \
> > +   MAXSYMLINKLEN_UFS1 : MAXSYMLINKLEN_UFS2
> > +
> > +/* File permissions. */
> > +#define    IEXEC           0000100         /* Executable. */
> > +#define    IWRITE          0000200         /* Writeable. */
> > +#define    IREAD           0000400         /* Readable. */
> > +#define    ISVTX           0001000         /* Sticky bit. */
> > +#define    ISGID           0002000         /* Set-gid. */
> > +#define    ISUID           0004000         /* Set-uid. */
> > +
> > +/* File types. */
> > +#define    IFMT            0170000         /* Mask of file type. */
> > +#define    IFIFO           0010000         /* Named pipe (fifo). */
> > +#define    IFCHR           0020000         /* Character device. */
> > +#define    IFDIR           0040000         /* Directory file. */
> > +#define    IFBLK           0060000         /* Block device. */
> > +#define    IFREG           0100000         /* Regular file. */
> > +#define    IFLNK           0120000         /* Symbolic link. */
> > +#define    IFSOCK          0140000         /* UNIX domain socket. */
> > +#define    IFWHT           0160000         /* Whiteout. */
> > +
> > +#endif /* _UFS_DINODE_H_ */
> > diff --git a/fs/ffs/dir.h b/fs/ffs/dir.h
> > new file mode 100644
> > index 0000000..64ddb02
> > --- /dev/null
> > +++ b/fs/ffs/dir.h
> > @@ -0,0 +1,156 @@
> > +/* $OpenBSD: dir.h,v 1.11 2009/01/31 21:21:45 grange Exp $ */
> > +/* $NetBSD: dir.h,v 1.8 1996/03/09 19:42:41 scottr Exp $   */
> > +
> > +/*
> > + * Copyright (c) 1982, 1986, 1989, 1993
> > + * The Regents of the University of California.  All rights reserved.
> > + * (c) UNIX System Laboratories, Inc.
> > + * All or some portions of this file are derived from material licensed
> > + * to the University of California by American Telephone and Telegraph
> > + * Co. or Unix System Laboratories, Inc. and are reproduced herein with
> > + * the permission of UNIX System Laboratories, Inc.
> > + *
> > + * Redistribution and use in source and binary forms, with or without
> > + * modification, are permitted provided that the following conditions
> > + * are met:
> > + * 1. Redistributions of source code must retain the above copyright
> > + *    notice, this list of conditions and the following disclaimer.
> > + * 2. Redistributions in binary form must reproduce the above copyright
> > + *    notice, this list of conditions and the following disclaimer in the
> > + *    documentation and/or other materials provided with the distribution.
> > + * 3. Neither the name of the University nor the names of its contributors
> > + *    may be used to endorse or promote products derived from this software
> > + *    without specific prior written permission.
> > + *
> > + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
> > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
> > PURPOSE
> > + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
> > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
> > CONSEQUENTIAL
> > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
> > STRICT
> > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 
> > WAY
> > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> > + * SUCH DAMAGE.
> > + *
> > + * @(#)dir.h       8.4 (Berkeley) 8/10/94
> > + */
> > +
> > +#ifndef _DIR_H_
> > +#define    _DIR_H_
> > +
> > +/*
> > + * Theoretically, directories can be more than 2Gb in length, however, in
> > + * practice this seems unlikely. So, we define the type doff_t as a 32-bit
> > + * quantity to keep down the cost of doing lookup on a 32-bit machine.
> > + */
> > +#define    doff_t          int32_t
> > +#define    MAXDIRSIZE      (0x7fffffff)
> > +
> > +/*
> > + * A directory consists of some number of blocks of DIRBLKSIZ
> > + * bytes, where DIRBLKSIZ is chosen such that it can be transferred
> > + * to disk in a single atomic operation (e.g. 512 bytes on most machines).
> > + *
> > + * Each DIRBLKSIZ byte block contains some number of directory entry
> > + * structures, which are of variable length.  Each directory entry has
> > + * a struct direct at the front of it, containing its inode number,
> > + * the length of the entry, and the length of the name contained in
> > + * the entry.  These are followed by the name padded to a 4 byte boundary
> > + * with null bytes.  All names are guaranteed null terminated.
> > + * The maximum length of a name in a directory is MAXNAMLEN.
> > + *
> > + * The macro DIRSIZ(fmt, dp) gives the amount of space required to 
> > represent
> > + * a directory entry.  Free space in a directory is represented by
> > + * entries which have dp->d_reclen > DIRSIZ(fmt, dp).  All DIRBLKSIZ bytes
> > + * in a directory block are claimed by the directory entries.  This
> > + * usually results in the last entry in a directory having a large
> > + * dp->d_reclen.  When entries are deleted from a directory, the
> > + * space is returned to the previous entry in the same directory
> > + * block by increasing its dp->d_reclen.  If the first entry of
> > + * a directory block is free, then its dp->d_ino is set to 0.
> > + * Entries other than the first in a directory do not normally have
> > + * dp->d_ino set to 0.
> > + */
> > +#define DIRBLKSIZ  DEV_BSIZE
> > +#define    MAXNAMLEN       255
> > +
> > +struct     direct {
> > +   u_int32_t d_ino;                /* inode number of entry */
> > +   u_int16_t d_reclen;             /* length of this record */
> > +   u_int8_t  d_type;               /* file type, see below */
> > +   u_int8_t  d_namlen;             /* length of string in d_name */
> > +   char      d_name[MAXNAMLEN + 1];/* name with length <= MAXNAMLEN */
> > +};
> > +
> > +/*
> > + * File types
> > + */
> > +#define    DT_UNKNOWN       0
> > +#define    DT_FIFO          1
> > +#define    DT_CHR           2
> > +#define    DT_DIR           4
> > +#define    DT_BLK           6
> > +#define    DT_REG           8
> > +#define    DT_LNK          10
> > +#define    DT_SOCK         12
> > +
> > +/*
> > + * Convert between stat structure types and directory types.
> > + */
> > +#define    IFTODT(mode)    (((mode) & 0170000) >> 12)
> > +#define    DTTOIF(dirtype) ((dirtype) << 12)
> > +
> > +/*
> > + * The DIRSIZ macro gives the minimum record length which will hold
> > + * the directory entry.  This requires the amount of space in struct direct
> > + * without the d_name field, plus enough space for the name with a 
> > terminating
> > + * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
> > + */
> > +#define DIRECTSIZ(namlen)                                          \
> > +   ((offsetof(struct direct, d_name) +                             \
> > +     ((namlen)+1)*sizeof(((struct direct *)0)->d_name[0]) + 3) & ~3)
> > +#if (BYTE_ORDER == LITTLE_ENDIAN)
> > +#define DIRSIZ(oldfmt, dp) \
> > +    ((oldfmt) ? \
> > +    ((sizeof(struct direct) - (MAXNAMLEN+1)) + (((dp)->d_type+1 + 3) &~ 
> > 3)) : \
> > +    ((sizeof(struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 
> > 3)))
> > +#else
> > +#define DIRSIZ(oldfmt, dp) \
> > +    ((sizeof(struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 
> > 3))
> > +#endif
> > +#define OLDDIRFMT  1
> > +#define NEWDIRFMT  0
> > +
> > +/*
> > + * Template for manipulating directories.  Should use struct direct's,
> > + * but the name field is MAXNAMLEN - 1, and this just won't do.
> > + */
> > +struct dirtemplate {
> > +   u_int32_t       dot_ino;
> > +   int16_t         dot_reclen;
> > +   u_int8_t        dot_type;
> > +   u_int8_t        dot_namlen;
> > +   char            dot_name[4];    /* must be multiple of 4 */
> > +   u_int32_t       dotdot_ino;
> > +   int16_t         dotdot_reclen;
> > +   u_int8_t        dotdot_type;
> > +   u_int8_t        dotdot_namlen;
> > +   char            dotdot_name[4]; /* ditto */
> > +};
> > +
> > +/*
> > + * This is the old format of directories, sanz type element.
> > + */
> > +struct odirtemplate {
> > +   u_int32_t       dot_ino;
> > +   int16_t         dot_reclen;
> > +   u_int16_t       dot_namlen;
> > +   char            dot_name[4];    /* must be multiple of 4 */
> > +   u_int32_t       dotdot_ino;
> > +   int16_t         dotdot_reclen;
> > +   u_int16_t       dotdot_namlen;
> > +   char            dotdot_name[4]; /* ditto */
> > +};
> > +#endif /* !_DIR_H_ */
> > diff --git a/fs/ffs/fs.h b/fs/ffs/fs.h
> > new file mode 100644
> > index 0000000..43c5c8a
> > --- /dev/null
> > +++ b/fs/ffs/fs.h
> > @@ -0,0 +1,592 @@
> > +/* $OpenBSD: fs.h,v 1.41 2015/01/20 18:08:16 deraadt Exp $ */
> > +/* $NetBSD: fs.h,v 1.6 1995/04/12 21:21:02 mycroft Exp $   */
> > +
> > +/*
> > + * Copyright (c) 1982, 1986, 1993
> > + * The Regents of the University of California.  All rights reserved.
> > + *
> > + * Redistribution and use in source and binary forms, with or without
> > + * modification, are permitted provided that the following conditions
> > + * are met:
> > + * 1. Redistributions of source code must retain the above copyright
> > + *    notice, this list of conditions and the following disclaimer.
> > + * 2. Redistributions in binary form must reproduce the above copyright
> > + *    notice, this list of conditions and the following disclaimer in the
> > + *    documentation and/or other materials provided with the distribution.
> > + * 3. Neither the name of the University nor the names of its contributors
> > + *    may be used to endorse or promote products derived from this software
> > + *    without specific prior written permission.
> > + *
> > + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
> > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
> > PURPOSE
> > + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
> > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
> > CONSEQUENTIAL
> > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
> > STRICT
> > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 
> > WAY
> > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> > + * SUCH DAMAGE.
> > + *
> > + * @(#)fs.h        8.10 (Berkeley) 10/27/94
> > + */
> > +
> > +/*
> > + * Each disk drive contains some number of file systems.
> > + * A file system consists of a number of cylinder groups.
> > + * Each cylinder group has inodes and data.
> > + *
> > + * A file system is described by its super-block, which in turn
> > + * describes the cylinder groups.  The super-block is critical
> > + * data and is replicated in each cylinder group to protect against
> > + * catastrophic loss.  This is done at `newfs' time and the critical
> > + * super-block data does not change, so the copies need not be
> > + * referenced further unless disaster strikes.
> > + *
> > + * For file system fs, the offsets of the various blocks of interest
> > + * are given in the super block as:
> > + * [fs->fs_sblkno]         Super-block
> > + * [fs->fs_cblkno]         Cylinder group block
> > + * [fs->fs_iblkno]         Inode blocks
> > + * [fs->fs_dblkno]         Data blocks
> > + * The beginning of cylinder group cg in fs, is given by
> > + * the ``cgbase(fs, cg)'' macro.
> > + *
> > + * The first boot and super blocks are given in absolute disk addresses.
> > + * The byte-offset forms are preferred, as they don't imply a sector size.
> > + */
> > +#define BBSIZE             8192
> > +#define SBSIZE             8192
> > +#define    BBOFF           ((off_t)(0))
> > +#define    SBOFF           ((off_t)(BBOFF + BBSIZE))
> > +#define    BBLOCK          ((daddr_t)(0))
> > +#define    SBLOCK          ((daddr_t)(BBLOCK + BBSIZE / DEV_BSIZE))
> > +#define    SBLOCK_UFS1     8192
> > +#define    SBLOCK_UFS2     65536
> > +#define    SBLOCK_PIGGY    262144
> > +#define    SBLOCKSIZE      8192
> > +#define    SBLOCKSEARCH \
> > +   { SBLOCK_UFS2, SBLOCK_UFS1, SBLOCK_PIGGY, -1 }
> > +
> > +/*
> > + * Addresses stored in inodes are capable of addressing fragments
> > + * of `blocks'. File system blocks of at most size MAXBSIZE can 
> > + * be optionally broken into 2, 4, or 8 pieces, each of which is
> > + * addressible; these pieces may be DEV_BSIZE, or some multiple of
> > + * a DEV_BSIZE unit.
> > + *
> > + * Large files consist of exclusively large data blocks.  To avoid
> > + * undue wasted disk space, the last data block of a small file may be
> > + * allocated as only as many fragments of a large block as are
> > + * necessary.  The file system format retains only a single pointer
> > + * to such a fragment, which is a piece of a single large block that
> > + * has been divided.  The size of such a fragment is determinable from
> > + * information in the inode, using the ``blksize(fs, ip, lbn)'' macro.
> > + *
> > + * The file system records space availability at the fragment level;
> > + * to determine block availability, aligned fragments are examined.
> > + */
> > +
> > +#define MAXFRAG    8
> > +
> > +/*
> > + * MINBSIZE is the smallest allowable block size.
> > + * In order to insure that it is possible to create files of size
> > + * 2^32 with only two levels of indirection, MINBSIZE is set to 4096.
> > + * MINBSIZE must be big enough to hold a cylinder group block,
> > + * thus changes to (struct cg) must keep its size within MINBSIZE.
> > + * Note that super blocks are always of size SBSIZE,
> > + * and that both SBSIZE and MAXBSIZE must be >= MINBSIZE.
> > + */
> > +#define MINBSIZE   4096
> > +
> > +/*
> > + * The path name on which the file system is mounted is maintained
> > + * in fs_fsmnt. MAXMNTLEN defines the amount of space allocated in
> > + * the super block for this name.
> > + */
> > +#define MAXMNTLEN  468
> > +
> > +/*
> > + * The volume name for this file system is kept in fs_volname.
> > + * MAXVOLLEN defines the length of the buffer allocated.
> > + */
> > +#define MAXVOLLEN  32
> > +
> > +/*
> > + * There is a 128-byte region in the superblock reserved for in-core
> > + * pointers to summary information. Originally this included an array
> > + * of pointers to blocks of struct csum; now there are just three
> > + * pointers and the remaining space is padded with fs_ocsp[].
> > + *
> > + * NOCSPTRS determines the size of this padding. One pointer (fs_csp)
> > + * is taken away to point to a contiguous array of struct csum for
> > + * all cylinder groups; a second (fs_maxcluster) points to an array
> > + * of cluster sizes that is computed as cylinder groups are inspected,
> > + * and the third points to an array that tracks the creation of new
> > + * directories.
> > + */
> > +#define NOCSPTRS   ((128 / sizeof(void *)) - 4)
> > +
> > +/*
> > + * A summary of contiguous blocks of various sizes is maintained
> > + * in each cylinder group. Normally this is set by the initial
> > + * value of fs_maxcontig. To conserve space, a maximum summary size
> > + * is set by FS_MAXCONTIG.
> > + */
> > +#define FS_MAXCONTIG       16
> > +
> > +/*
> > + * MINFREE gives the minimum acceptable percentage of file system
> > + * blocks which may be free. If the freelist drops below this level
> > + * only the superuser may continue to allocate blocks. This may
> > + * be set to 0 if no reserve of free blocks is deemed necessary,
> > + * however throughput drops by fifty percent if the file system
> > + * is run at between 95% and 100% full; thus the minimum default
> > + * value of fs_minfree is 5%. However, to get good clustering
> > + * performance, 10% is a better choice. With 5% free space,
> > + * fragmentation is not a problem, so we choose to optimize for time.
> > + */
> > +#define MINFREE            5
> > +#define DEFAULTOPT FS_OPTTIME
> > +
> > +/*
> > + * The directory preference algorithm(dirpref) can be tuned by adjusting
> > + * the following parameters which tell the system the average file size
> > + * and the average number of files per directory. These defaults are well
> > + * selected for typical filesystems, but may need to be tuned for odd
> > + * cases like filesystems being used for squid caches or news spools.
> > + */
> > +#define AVFILESIZ  16384   /* expected average file size */
> > +#define AFPDIR             64      /* expected number of files per 
> > directory */
> > +
> > +/*
> > + * Size of superblock space reserved for snapshots.
> > + */
> > +#define FSMAXSNAP  20
> > +
> > +/*
> > + * Per cylinder group information; summarized in blocks allocated
> > + * from first cylinder group data blocks.  These blocks have to be
> > + * read in from fs_csaddr (size fs_cssize) in addition to the
> > + * super block.
> > + */
> > +struct csum {
> > +   int32_t cs_ndir;                /* number of directories */
> > +   int32_t cs_nbfree;              /* number of free blocks */
> > +   int32_t cs_nifree;              /* number of free inodes */
> > +   int32_t cs_nffree;              /* number of free frags */
> > +};
> > +
> > +struct csum_total {
> > +   int64_t cs_ndir;                /* number of directories */
> > +   int64_t cs_nbfree;              /* number of free blocks */
> > +   int64_t cs_nifree;              /* number of free inodes */
> > +   int64_t cs_nffree;              /* number of free frags */
> > +   int64_t cs_spare[4];            /* future expansion */
> > +};
> > +
> > +/*
> > + * Super block for an FFS file system.
> > + */
> > +struct fs {
> > +   int32_t  fs_firstfield;         /* historic file system linked list, */
> > +   int32_t  fs_unused_1;           /*     used for incore super blocks */
> > +   int32_t  fs_sblkno;             /* addr of super-block / frags */
> > +   int32_t  fs_cblkno;             /* offset of cyl-block / frags */
> > +   int32_t  fs_iblkno;             /* offset of inode-blocks / frags */
> > +   int32_t  fs_dblkno;             /* offset of first data / frags */
> > +   int32_t  fs_cgoffset;           /* cylinder group offset in cylinder */
> > +   int32_t  fs_cgmask;             /* used to calc mod fs_ntrak */
> > +   int32_t  fs_ffs1_time;          /* last time written */
> > +   int32_t  fs_ffs1_size;          /* # of blocks in fs / frags */
> > +   int32_t  fs_ffs1_dsize;         /* # of data blocks in fs */
> > +   int32_t  fs_ncg;                /* # of cylinder groups */
> > +   int32_t  fs_bsize;              /* size of basic blocks / bytes */
> > +   int32_t  fs_fsize;              /* size of frag blocks / bytes */
> > +   int32_t  fs_frag;               /* # of frags in a block in fs */
> > +/* these are configuration parameters */
> > +   int32_t  fs_minfree;            /* minimum percentage of free blocks */
> > +   int32_t  fs_rotdelay;           /* # of ms for optimal next block */
> > +   int32_t  fs_rps;                /* disk revolutions per second */
> > +/* these fields can be computed from the others */
> > +   int32_t  fs_bmask;              /* ``blkoff'' calc of blk offsets */
> > +   int32_t  fs_fmask;              /* ``fragoff'' calc of frag offsets */
> > +   int32_t  fs_bshift;             /* ``lblkno'' calc of logical blkno */
> > +   int32_t  fs_fshift;             /* ``numfrags'' calc # of frags */
> > +/* these are configuration parameters */
> > +   int32_t  fs_maxcontig;          /* max # of contiguous blks */
> > +   int32_t  fs_maxbpg;             /* max # of blks per cyl group */
> > +/* these fields can be computed from the others */
> > +   int32_t  fs_fragshift;          /* block to frag shift */
> > +   int32_t  fs_fsbtodb;            /* fsbtodb and dbtofsb shift constant */
> > +   int32_t  fs_sbsize;             /* actual size of super block */
> > +   int32_t  fs_csmask;             /* csum block offset (now unused) */
> > +   int32_t  fs_csshift;            /* csum block number (now unused) */
> > +   int32_t  fs_nindir;             /* value of NINDIR */
> > +   int32_t  fs_inopb;              /* inodes per file system block */
> > +   int32_t  fs_nspf;               /* DEV_BSIZE sectors per frag */
> > +/* yet another configuration parameter */
> > +   int32_t  fs_optim;              /* optimization preference, see below */
> > +/* these fields are derived from the hardware */
> > +   int32_t  fs_npsect;             /* DEV_BSIZE sectors/track + spares */
> > +   int32_t  fs_interleave;         /* DEV_BSIZE sector interleave */
> > +   int32_t  fs_trackskew;          /* sector 0 skew, per track */
> > +/* fs_id takes the space of the unused fs_headswitch and fs_trkseek fields 
> > */
> > +   int32_t  fs_id[2];              /* unique filesystem id */
> > +/* sizes determined by number of cylinder groups and their sizes */
> > +   int32_t  fs_ffs1_csaddr;        /* blk addr of cyl grp summary area */
> > +   int32_t  fs_cssize;             /* cyl grp summary area size / bytes */
> > +   int32_t  fs_cgsize;             /* cyl grp block size / bytes */
> > +/* these fields are derived from the hardware */
> > +   int32_t  fs_ntrak;              /* tracks per cylinder */
> > +   int32_t  fs_nsect;              /* DEV_BSIZE sectors per track */
> > +   int32_t  fs_spc;                /* DEV_BSIZE sectors per cylinder */
> > +/* this comes from the disk driver partitioning */
> > +   int32_t  fs_ncyl;               /* cylinders in file system */
> > +/* these fields can be computed from the others */
> > +   int32_t  fs_cpg;                /* cylinders per group */
> > +   int32_t  fs_ipg;                /* inodes per group */
> > +   int32_t  fs_fpg;                /* blocks per group * fs_frag */
> > +/* this data must be re-computed after crashes */
> > +   struct  csum fs_ffs1_cstotal;   /* cylinder summary information */
> > +/* these fields are cleared at mount time */
> > +   int8_t   fs_fmod;               /* super block modified flag */
> > +   int8_t   fs_clean;              /* file system is clean flag */
> > +   int8_t   fs_ronly;              /* mounted read-only flag */
> > +   int8_t   fs_ffs1_flags;         /* see FS_ below */
> > +   u_char   fs_fsmnt[MAXMNTLEN];   /* name mounted on */
> > +   u_char   fs_volname[MAXVOLLEN]; /* volume name */
> > +   u_int64_t fs_swuid;             /* system-wide uid */
> > +   int32_t  fs_pad;                /* due to alignment of fs_swuid */
> > +/* these fields retain the current block allocation info */
> > +   int32_t  fs_cgrotor;            /* last cg searched */
> > +   void    *fs_ocsp[NOCSPTRS];     /* padding; was list of fs_cs buffers */
> > +   u_int8_t *fs_contigdirs;        /* # of contiguously allocated dirs */
> > +   struct csum *fs_csp;            /* cg summary info buffer for fs_cs */
> > +   int32_t *fs_maxcluster;         /* max cluster in each cyl group */
> > +   u_char  *fs_active;             /* reserved for snapshots */
> > +   int32_t  fs_cpc;                /* cyl per cycle in postbl */
> > +/* this area is only allocated if fs_ffs1_flags & FS_FLAGS_UPDATED */
> > +   int32_t  fs_maxbsize;           /* maximum blocking factor permitted */
> > +   int64_t  fs_spareconf64[17];    /* old rotation block list head */
> > +   int64_t  fs_sblockloc;          /* offset of standard super block */
> > +   struct  csum_total fs_cstotal;  /* cylinder summary information */
> > +   int64_t  fs_time;               /* time last written */
> > +   int64_t  fs_size;               /* number of blocks in fs */
> > +   int64_t  fs_dsize;              /* number of data blocks in fs */
> > +   int64_t  fs_csaddr;             /* blk addr of cyl grp summary area */
> > +   int64_t  fs_pendingblocks;      /* blocks in process of being freed */
> > +   int32_t  fs_pendinginodes;      /* inodes in process of being freed */
> > +   int32_t  fs_snapinum[FSMAXSNAP];/* space reserved for snapshots */
> > +/* back to stuff that has been around a while */
> > +   int32_t  fs_avgfilesize;        /* expected average file size */
> > +   int32_t  fs_avgfpdir;           /* expected # of files per directory */
> > +   int32_t  fs_sparecon[26];       /* reserved for future constants */
> > +   u_int32_t fs_flags;             /* see FS_ flags below */
> > +   int32_t  fs_fscktime;           /* last time fsck(8)ed */
> > +   int32_t  fs_contigsumsize;      /* size of cluster summary array */ 
> > +   int32_t  fs_maxsymlinklen;      /* max length of an internal symlink */
> > +   int32_t  fs_inodefmt;           /* format of on-disk inodes */
> > +   u_int64_t fs_maxfilesize;       /* maximum representable file size */
> > +   int64_t  fs_qbmask;             /* ~fs_bmask - for use with quad size */
> > +   int64_t  fs_qfmask;             /* ~fs_fmask - for use with quad size */
> > +   int32_t  fs_state;              /* validate fs_clean field */
> > +   int32_t  fs_postblformat;       /* format of positional layout tables */
> > +   int32_t  fs_nrpos;              /* number of rotational positions */
> > +   int32_t  fs_postbloff;          /* (u_int16) rotation block list head */
> > +   int32_t  fs_rotbloff;           /* (u_int8) blocks for each rotation */
> > +   int32_t  fs_magic;              /* magic number */
> > +   u_int8_t fs_space[1];           /* list of blocks for each rotation */
> > +/* actually longer */
> > +};
> > +
> > +/*
> > + * Filesystem identification
> > + */
> > +#define    FS_MAGIC        0x011954        /* the fast filesystem magic 
> > number */
> > +#define    FS_UFS1_MAGIC   0x011954        /* the fast filesystem magic 
> > number */
> > +#define    FS_UFS2_MAGIC   0x19540119      /* UFS fast filesystem magic 
> > number */
> > +#define    FS_OKAY         0x7c269d38      /* superblock checksum */
> > +#define FS_42INODEFMT      -1              /* 4.2BSD inode format */
> > +#define FS_44INODEFMT      2               /* 4.4BSD inode format */
> > +
> > +/*
> > + * Filesystem clean flags
> > + */
> > +#define    FS_ISCLEAN      0x01
> > +#define    FS_WASCLEAN     0x02
> > +
> > +/*
> > + * Preference for optimization.
> > + */
> > +#define FS_OPTTIME 0       /* minimize allocation time */
> > +#define FS_OPTSPACE        1       /* minimize disk fragmentation */
> > +
> > +/* 
> > + * Filesystem flags.
> > + */
> > +#define FS_UNCLEAN 0x01    /* filesystem not clean at mount */
> > +#define FS_DOSOFTDEP       0x02    /* filesystem using soft dependencies */
> > +/*
> > + * The following flag is used to detect a FFS1 file system that had its 
> > flags
> > + * moved to the new (FFS2) location for compatibility.
> > + */
> > +#define FS_FLAGS_UPDATED   0x80    /* file system has FFS2-like flags */
> > +
> > +/*
> > + * Rotational layout table format types
> > + */
> > +#define FS_42POSTBLFMT             -1      /* 4.2BSD rotational table 
> > format */
> > +#define FS_DYNAMICPOSTBLFMT        1       /* dynamic rotational table 
> > format */
> > +/*
> > + * Macros for access to superblock array structures
> > + */
> > +#define fs_rotbl(fs) \
> > +    (((fs)->fs_postblformat == FS_42POSTBLFMT) \
> > +    ? ((fs)->fs_space) \
> > +    : ((u_int8_t *)((u_int8_t *)(fs) + (fs)->fs_rotbloff)))
> > +
> > +/*
> > + * The size of a cylinder group is calculated by CGSIZE. The maximum size
> > + * is limited by the fact that cylinder groups are at most one block.
> > + * Its size is derived from the size of the maps maintained in the
> > + * cylinder group and the (struct cg) size.
> > + */
> > +#define CGSIZE(fs) \
> > +    /* base cg */  (sizeof(struct cg) + sizeof(int32_t) + \
> > +    /* blktot size */      (fs)->fs_cpg * sizeof(int32_t) + \
> > +    /* blks size */        (fs)->fs_cpg * (fs)->fs_nrpos * sizeof(int16_t) 
> > + \
> > +    /* inode map */        howmany((fs)->fs_ipg, NBBY) + \
> > +    /* block map */        howmany((fs)->fs_fpg, NBBY) + \
> > +    /* if present */       ((fs)->fs_contigsumsize <= 0 ? 0 : \
> > +    /* cluster sum */      (fs)->fs_contigsumsize * sizeof(int32_t) + \
> > +    /* cluster map */      howmany(fragstoblks(fs, (fs)->fs_fpg), NBBY)))
> > +
> > +/*
> > + * Convert cylinder group to base address of its global summary info.
> > + */
> > +#define fs_cs(fs, indx) fs_csp[indx]
> > +
> > +/*
> > + * Cylinder group block for a file system.
> > + */
> > +#define    CG_MAGIC        0x090255
> > +struct cg {
> > +   int32_t  cg_firstfield;         /* historic cyl groups linked list */
> > +   int32_t  cg_magic;              /* magic number */
> > +   int32_t  cg_time;               /* time last written */
> > +   int32_t  cg_cgx;                /* we are the cgx'th cylinder group */
> > +   int16_t  cg_ncyl;               /* number of cyl's this cg */
> > +   int16_t  cg_niblk;              /* number of inode blocks this cg */
> > +   int32_t  cg_ndblk;              /* number of data blocks this cg */
> > +   struct  csum cg_cs;             /* cylinder summary information */
> > +   int32_t  cg_rotor;              /* position of last used block */
> > +   int32_t  cg_frotor;             /* position of last used frag */
> > +   int32_t  cg_irotor;             /* position of last used inode */
> > +   int32_t  cg_frsum[MAXFRAG];     /* counts of available frags */
> > +   int32_t  cg_btotoff;            /* (int32) block totals per cylinder */
> > +   int32_t  cg_boff;               /* (u_int16) free block positions */
> > +   int32_t  cg_iusedoff;           /* (u_int8) used inode map */
> > +   int32_t  cg_freeoff;            /* (u_int8) free block map */
> > +   int32_t  cg_nextfreeoff;        /* (u_int8) next available space */
> > +   int32_t  cg_clustersumoff;      /* (u_int32) counts of avail clusters */
> > +   int32_t  cg_clusteroff;         /* (u_int8) free cluster map */
> > +   int32_t  cg_nclusterblks;       /* number of clusters this cg */
> > +   int32_t  cg_ffs2_niblk;         /* number of inode blocks this cg */
> > +   int32_t  cg_initediblk;         /* last initialized inode */
> > +   int32_t  cg_sparecon32[3];      /* reserved for future use */
> > +   int64_t  cg_ffs2_time;          /* time last written */
> > +   int64_t  cg_sparecon64[3];      /* reserved for future use */
> > +/* actually longer */
> > +};
> > +
> > +/*
> > + * Macros for access to cylinder group array structures
> > + */
> > +#define cg_blktot(cgp) \
> > +    (((cgp)->cg_magic != CG_MAGIC) \
> > +    ? (((struct ocg *)(cgp))->cg_btot) \
> > +    : ((int32_t *)((u_int8_t *)(cgp) + (cgp)->cg_btotoff)))
> > +#define cg_blks(fs, cgp, cylno) \
> > +    (((cgp)->cg_magic != CG_MAGIC) \
> > +    ? (((struct ocg *)(cgp))->cg_b[cylno]) \
> > +    : ((int16_t *)((u_int8_t *)(cgp) + \
> > +   (cgp)->cg_boff) + (cylno) * (fs)->fs_nrpos))
> > +#define cg_inosused(cgp) \
> > +    (((cgp)->cg_magic != CG_MAGIC) \
> > +    ? (((struct ocg *)(cgp))->cg_iused) \
> > +    : ((u_int8_t *)((u_int8_t *)(cgp) + (cgp)->cg_iusedoff)))
> > +#define cg_blksfree(cgp) \
> > +    (((cgp)->cg_magic != CG_MAGIC) \
> > +    ? (((struct ocg *)(cgp))->cg_free) \
> > +    : ((u_int8_t *)((u_int8_t *)(cgp) + (cgp)->cg_freeoff)))
> > +#define cg_chkmagic(cgp) \
> > +    ((cgp)->cg_magic == CG_MAGIC || ((struct ocg *)(cgp))->cg_magic == 
> > CG_MAGIC)
> > +#define cg_clustersfree(cgp) \
> > +    ((u_int8_t *)((u_int8_t *)(cgp) + (cgp)->cg_clusteroff))
> > +#define cg_clustersum(cgp) \
> > +    ((int32_t *)((u_int8_t *)(cgp) + (cgp)->cg_clustersumoff))
> > +
> > +/*
> > + * The following structure is defined
> > + * for compatibility with old file systems.
> > + */
> > +struct ocg {
> > +   int32_t  cg_firstfield;         /* historic linked list of cyl groups */
> > +   int32_t  cg_unused_1;           /*     used for incore cyl groups */
> > +   int32_t  cg_time;               /* time last written */
> > +   int32_t  cg_cgx;                /* we are the cgx'th cylinder group */
> > +   int16_t  cg_ncyl;               /* number of cyl's this cg */
> > +   int16_t  cg_niblk;              /* number of inode blocks this cg */
> > +   int32_t  cg_ndblk;              /* number of data blocks this cg */
> > +   struct  csum cg_cs;             /* cylinder summary information */
> > +   int32_t  cg_rotor;              /* position of last used block */
> > +   int32_t  cg_frotor;             /* position of last used frag */
> > +   int32_t  cg_irotor;             /* position of last used inode */
> > +   int32_t  cg_frsum[8];           /* counts of available frags */
> > +   int32_t  cg_btot[32];           /* block totals per cylinder */
> > +   int16_t  cg_b[32][8];           /* positions of free blocks */
> > +   u_int8_t cg_iused[256];         /* used inode map */
> > +   int32_t  cg_magic;              /* magic number */
> > +   u_int8_t cg_free[1];            /* free block map */
> > +/* actually longer */
> > +};
> > +
> > +/*
> > + * Turn file system block numbers into disk block addresses.
> > + * This maps file system blocks to DEV_BSIZE (a.k.a. 512-byte) size disk
> > + * blocks.
> > + */
> > +#define fsbtodb(fs, b)     ((b) << (fs)->fs_fsbtodb)
> > +#define    dbtofsb(fs, b)  ((b) >> (fs)->fs_fsbtodb)
> > +
> > +/*
> > + * Cylinder group macros to locate things in cylinder groups.
> > + * They calc file system addresses of cylinder group data structures.
> > + */
> > +#define    cgbase(fs, c)   ((daddr_t)(fs)->fs_fpg * (c))
> > +#define    cgdata(fs, c)   (cgdmin(fs, c) + (fs)->fs_minfree)      /* data 
> > zone */
> > +#define    cgmeta(fs, c)   (cgdmin(fs, c))                         /* meta 
> > data */
> > +#define    cgdmin(fs, c)   (cgstart(fs, c) + (fs)->fs_dblkno)      /* 1st 
> > data */
> > +#define    cgimin(fs, c)   (cgstart(fs, c) + (fs)->fs_iblkno)      /* 
> > inode blk */
> > +#define    cgsblock(fs, c) (cgstart(fs, c) + (fs)->fs_sblkno)      /* 
> > super blk */
> > +#define    cgtod(fs, c)    (cgstart(fs, c) + (fs)->fs_cblkno)      /* cg 
> > block */
> > +#define cgstart(fs, c)                                                     
> > \
> > +   (cgbase(fs, c) + (fs)->fs_cgoffset * ((c) & ~((fs)->fs_cgmask)))
> > +
> > +/*
> > + * Macros for handling inode numbers:
> > + *     inode number to file system block offset.
> > + *     inode number to cylinder group number.
> > + *     inode number to file system block address.
> > + */
> > +#define    ino_to_cg(fs, x)        ((x) / (fs)->fs_ipg)
> > +#define    ino_to_fsba(fs, x)                                              
> > \
> > +   ((daddr_t)(cgimin(fs, ino_to_cg(fs, x)) +                       \
> > +       (blkstofrags((fs), (((x) % (fs)->fs_ipg) / INOPB(fs))))))
> > +#define    ino_to_fsbo(fs, x)      ((x) % INOPB(fs))
> > +
> > +/*
> > + * Give cylinder group number for a file system block.
> > + * Give frag block number in cylinder group for a file system block.
> > + */
> > +#define    dtog(fs, d)     ((d) / (fs)->fs_fpg)
> > +#define    dtogd(fs, d)    ((d) % (fs)->fs_fpg)
> > +
> > +/*
> > + * Extract the bits for a block from a map.
> > + * Compute the cylinder and rotational position of a cyl block addr.
> > + */
> > +#define blkmap(fs, map, loc) \
> > +    (((map)[(loc) / NBBY] >> ((loc) % NBBY)) & (0xff >> (NBBY - 
> > (fs)->fs_frag)))
> > +#define cbtocylno(fs, bno) \
> > +    (fsbtodb(fs, bno) / (fs)->fs_spc)
> > +#define cbtorpos(fs, bno) \
> > +    ((fs)->fs_nrpos <= 1 ? 0 : \
> > +     (fsbtodb(fs, bno) % (fs)->fs_spc / (fs)->fs_nsect * 
> > (fs)->fs_trackskew + \
> > +     fsbtodb(fs, bno) % (fs)->fs_spc % (fs)->fs_nsect * 
> > (fs)->fs_interleave) % \
> > +     (fs)->fs_nsect * (fs)->fs_nrpos / (fs)->fs_npsect)
> > +
> > +/*
> > + * The following macros optimize certain frequently calculated
> > + * quantities by using shifts and masks in place of divisions
> > + * modulos and multiplications.
> > + */
> > +#define blkoff(fs, loc)            /* calculates (loc % fs->fs_bsize) */ \
> > +   ((loc) & (fs)->fs_qbmask)
> > +#define fragoff(fs, loc)   /* calculates (loc % fs->fs_fsize) */ \
> > +   ((loc) & (fs)->fs_qfmask)
> > +#define lblktosize(fs, blk)        /* calculates ((off_t)blk * 
> > fs->fs_bsize) */ \
> > +   ((off_t)(blk) << (fs)->fs_bshift)
> > +#define lblkno(fs, loc)            /* calculates (loc / fs->fs_bsize) */ \
> > +   ((loc) >> (fs)->fs_bshift)
> > +#define numfrags(fs, loc)  /* calculates (loc / fs->fs_fsize) */ \
> > +   ((loc) >> (fs)->fs_fshift)
> > +#define blkroundup(fs, size)       /* calculates roundup(size, 
> > fs->fs_bsize) */ \
> > +   (((size) + (fs)->fs_qbmask) & (fs)->fs_bmask)
> > +#define fragroundup(fs, size)      /* calculates roundup(size, 
> > fs->fs_fsize) */ \
> > +   (((size) + (fs)->fs_qfmask) & (fs)->fs_fmask)
> > +#define fragstoblks(fs, frags)     /* calculates (frags / fs->fs_frag) */ \
> > +   ((frags) >> (fs)->fs_fragshift)
> > +#define blkstofrags(fs, blks)      /* calculates (blks * fs->fs_frag) */ \
> > +   ((blks) << (fs)->fs_fragshift)
> > +#define fragnum(fs, fsb)   /* calculates (fsb % fs->fs_frag) */ \
> > +   ((fsb) & ((fs)->fs_frag - 1))
> > +#define blknum(fs, fsb)            /* calculates rounddown(fsb, 
> > fs->fs_frag) */ \
> > +   ((fsb) &~ ((fs)->fs_frag - 1))
> > +
> > +/*
> > + * Determine the number of available frags given a
> > + * percentage to hold in reserve.
> > + */
> > +#define freespace(fs, percentreserved) \
> > +   (blkstofrags((fs), (fs)->fs_cstotal.cs_nbfree) + \
> > +   (fs)->fs_cstotal.cs_nffree - ((fs)->fs_dsize * (percentreserved) / 100))
> > +
> > +/*
> > + * Determining the size of a file block in the file system.
> > + */
> > +#define blksize(fs, ip, lbn) \
> > +   (((lbn) >= NDADDR || DIP((ip), size) >= ((lbn) + 1) << (fs)->fs_bshift) 
> > \
> > +       ? (fs)->fs_bsize \
> > +       : (fragroundup(fs, blkoff(fs, DIP((ip), size)))))
> > +#define dblksize(fs, dip, lbn) \
> > +   (((lbn) >= NDADDR || (dip)->di_size >= ((lbn) + 1) << (fs)->fs_bshift) \
> > +       ? (fs)->fs_bsize \
> > +       : (fragroundup(fs, blkoff(fs, (dip)->di_size))))
> > +
> > +#define sblksize(fs, size, lbn) \
> > +        (((lbn) >= NDADDR || (size) >= ((lbn) + 1) << (fs)->fs_bshift) \
> > +            ? (fs)->fs_bsize \
> > +            : (fragroundup(fs, blkoff(fs, (size)))))
> > +
> > +
> > +/*
> > + * Number of disk sectors per block/fragment; assumes DEV_BSIZE byte
> > + * sector size.
> > + */
> > +#define    NSPB(fs)        ((fs)->fs_nspf << (fs)->fs_fragshift)
> > +#define    NSPF(fs)        ((fs)->fs_nspf)
> > +
> > +/* Number of inodes per file system block (fs->fs_bsize) */
> > +#define    INOPB(fs)       ((fs)->fs_inopb)
> > +/* Number of inodes per file system fragment (fs->fs_fsize) */
> > +#define    INOPF(fs)       ((fs)->fs_inopb >> (fs)->fs_fragshift)
> > +
> > +/*
> > + * Number of indirects in a file system block.
> > + */
> > +#define    NINDIR(fs)      ((fs)->fs_nindir)
> > +
> > +/* Maximum file size the kernel allows.
> > + * Even though ffs can handle files up to 16TB, we do limit the max file
> > + * to 2^31 pages to prevent overflow of a 32-bit unsigned int.  The buffer
> > + * cache has its own checks but a little added paranoia never hurts.
> > + */
> > +#define FS_KERNMAXFILESIZE(pgsiz, fs)      ((u_int64_t)0x80000000 * \
> > +    MIN((pgsiz), (fs)->fs_bsize) - 1)
> > +
> > +extern const int inside[], around[];
> > +extern const u_char *fragtbl[];
> > diff --git a/fs/ffs/stat.h b/fs/ffs/stat.h
> > new file mode 100644
> > index 0000000..bce381e
> > --- /dev/null
> > +++ b/fs/ffs/stat.h
> > @@ -0,0 +1,133 @@
> > +/* $OpenBSD: stat.h,v 1.28 2015/04/04 18:06:08 jca Exp $   */
> > +/* $NetBSD: stat.h,v 1.20 1996/05/16 22:17:49 cgd Exp $    */
> > +
> > +/*-
> > + * Copyright (c) 1982, 1986, 1989, 1993
> > + * The Regents of the University of California.  All rights reserved.
> > + * (c) UNIX System Laboratories, Inc.
> > + * All or some portions of this file are derived from material licensed
> > + * to the University of California by American Telephone and Telegraph
> > + * Co. or Unix System Laboratories, Inc. and are reproduced herein with
> > + * the permission of UNIX System Laboratories, Inc.
> > + *
> > + * Redistribution and use in source and binary forms, with or without
> > + * modification, are permitted provided that the following conditions
> > + * are met:
> > + * 1. Redistributions of source code must retain the above copyright
> > + *    notice, this list of conditions and the following disclaimer.
> > + * 2. Redistributions in binary form must reproduce the above copyright
> > + *    notice, this list of conditions and the following disclaimer in the
> > + *    documentation and/or other materials provided with the distribution.
> > + * 3. Neither the name of the University nor the names of its contributors
> > + *    may be used to endorse or promote products derived from this software
> > + *    without specific prior written permission.
> > + *
> > + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
> > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
> > PURPOSE
> > + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
> > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
> > CONSEQUENTIAL
> > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
> > STRICT
> > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 
> > WAY
> > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> > + * SUCH DAMAGE.
> > + *
> > + * @(#)stat.h      8.9 (Berkeley) 8/17/94
> > + */
> > +
> > +#ifndef _SYS_STAT_H_
> > +#define    _SYS_STAT_H_
> > +
> > +struct stat {
> > +   mode_t    st_mode;              /* inode protection mode */
> > +   dev_t     st_dev;               /* inode's device */
> > +   ino_t     st_ino;               /* inode's number */
> > +   nlink_t   st_nlink;             /* number of hard links */
> > +   uid_t     st_uid;               /* user ID of the file's owner */
> > +   gid_t     st_gid;               /* group ID of the file's group */
> > +   dev_t     st_rdev;              /* device type */
> > +   time_t    st_atime;             /* time of last access */
> > +   long      st_atimensec;         /* nsec of last access */
> > +   time_t    st_mtime;             /* time of last data modification */
> > +   long      st_mtimensec;         /* nsec of last data modification */
> > +   time_t    st_ctime;             /* time of last file status change */
> > +   long      st_ctimensec;         /* nsec of last file status change */
> > +   off_t     st_size;              /* file size, in bytes */
> > +   blkcnt_t  st_blocks;            /* blocks allocated for file */
> > +   blksize_t st_blksize;           /* optimal blocksize for I/O */
> > +   u_int32_t st_flags;             /* user defined flags for file */
> > +   u_int32_t st_gen;               /* file generation number */
> > +   time_t    __st_birthtime;       /* time of file creation */
> > +   long      __st_birthtimensec;   /* nsec of file creation */
> > +};
> > +
> > +#define    S_ISUID 0004000                 /* set user id on execution */
> > +#define    S_ISGID 0002000                 /* set group id on execution */
> > +#define    S_ISTXT 0001000                 /* sticky bit */
> > +
> > +#define    S_IRWXU 0000700                 /* RWX mask for owner */
> > +#define    S_IRUSR 0000400                 /* R for owner */
> > +#define    S_IWUSR 0000200                 /* W for owner */
> > +#define    S_IXUSR 0000100                 /* X for owner */
> > +
> > +#define    S_IREAD         S_IRUSR
> > +#define    S_IWRITE        S_IWUSR
> > +#define    S_IEXEC         S_IXUSR
> > +
> > +#define    S_IRWXG 0000070                 /* RWX mask for group */
> > +#define    S_IRGRP 0000040                 /* R for group */
> > +#define    S_IWGRP 0000020                 /* W for group */
> > +#define    S_IXGRP 0000010                 /* X for group */
> > +
> > +#define    S_IRWXO 0000007                 /* RWX mask for other */
> > +#define    S_IROTH 0000004                 /* R for other */
> > +#define    S_IWOTH 0000002                 /* W for other */
> > +#define    S_IXOTH 0000001                 /* X for other */
> > +
> > +#define    S_IFMT   0170000                /* type of file mask */
> > +#define    S_IFIFO  0010000                /* named pipe (fifo) */
> > +#define    S_IFCHR  0020000                /* character special */
> > +#define    S_IFDIR  0040000                /* directory */
> > +#define    S_IFBLK  0060000                /* block special */
> > +#define    S_IFREG  0100000                /* regular */
> > +#define    S_IFLNK  0120000                /* symbolic link */
> > +#define    S_IFSOCK 0140000                /* socket */
> > +#define    S_ISVTX  0001000                /* save swapped text even after 
> > use */
> > +
> > +#define    S_ISDIR(m)      ((m & 0170000) == 0040000)      /* directory */
> > +#define    S_ISCHR(m)      ((m & 0170000) == 0020000)      /* char special 
> > */
> > +#define    S_ISBLK(m)      ((m & 0170000) == 0060000)      /* block 
> > special */
> > +#define    S_ISREG(m)      ((m & 0170000) == 0100000)      /* regular file 
> > */
> > +#define    S_ISFIFO(m)     ((m & 0170000) == 0010000)      /* fifo */
> > +#define    S_ISLNK(m)      ((m & 0170000) == 0120000)      /* symbolic 
> > link */
> > +#define    S_ISSOCK(m)     ((m & 0170000) == 0140000)      /* socket */
> > +
> > +#define    ACCESSPERMS     (S_IRWXU|S_IRWXG|S_IRWXO)       /* 00777 */
> > +                                                   /* 07777 */
> > +#define    ALLPERMS        
> > (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
> > +                                                   /* 00666 */
> > +#define    DEFFILEMODE     
> > (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
> > +
> > +#define    S_BLKSIZE       512             /* block size used in the stat 
> > struct */
> > +
> > +/*
> > + * Definitions of flags stored in file flags word.
> > + *
> > + * Super-user and owner changeable flags.
> > + */
> > +#define    UF_SETTABLE     0x0000ffff      /* mask of owner changeable 
> > flags */
> > +#define    UF_NODUMP       0x00000001      /* do not dump file */
> > +#define    UF_IMMUTABLE    0x00000002      /* file may not be changed */
> > +#define    UF_APPEND       0x00000004      /* writes to file may only 
> > append */
> > +#define    UF_OPAQUE       0x00000008      /* directory is opaque wrt. 
> > union */
> > +/*
> > + * Super-user changeable flags.
> > + */
> > +#define    SF_SETTABLE     0xffff0000      /* mask of superuser changeable 
> > flags */
> > +#define    SF_ARCHIVED     0x00010000      /* file is archived */
> > +#define    SF_IMMUTABLE    0x00020000      /* file may not be changed */
> > +#define    SF_APPEND       0x00040000      /* writes to file may only 
> > append */
> > +
> > +#endif /* !_SYS_STAT_H_ */
> > diff --git a/fs/ffs/ufs.c b/fs/ffs/ufs.c
> > new file mode 100644
> > index 0000000..1bcd400
> > --- /dev/null
> > +++ b/fs/ffs/ufs.c
> > @@ -0,0 +1,885 @@
> > +/* $OpenBSD: ufs.c,v 1.25 2015/07/17 18:55:00 kspillner Exp $      */
> > +/* $NetBSD: ufs.c,v 1.16 1996/09/30 16:01:22 ws Exp $      */
> > +
> > +/*-
> > + * Copyright (c) 1993
> > + * The Regents of the University of California.  All rights reserved.
> > + *
> > + * This code is derived from software contributed to Berkeley by
> > + * The Mach Operating System project at Carnegie-Mellon University.
> > + *
> > + * Redistribution and use in source and binary forms, with or without
> > + * modification, are permitted provided that the following conditions
> > + * are met:
> > + * 1. Redistributions of source code must retain the above copyright
> > + *    notice, this list of conditions and the following disclaimer.
> > + * 2. Redistributions in binary form must reproduce the above copyright
> > + *    notice, this list of conditions and the following disclaimer in the
> > + *    documentation and/or other materials provided with the distribution.
> > + * 3. Neither the name of the University nor the names of its contributors
> > + *    may be used to endorse or promote products derived from this software
> > + *    without specific prior written permission.
> > + *
> > + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
> > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
> > PURPOSE
> > + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
> > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
> > CONSEQUENTIAL
> > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
> > STRICT
> > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 
> > WAY
> > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> > + * SUCH DAMAGE.
> > + *
> > + *
> > + * Copyright (c) 1990, 1991 Carnegie Mellon University
> > + * All Rights Reserved.
> > + *
> > + * Author: David Golub
> > + *
> > + * Permission to use, copy, modify and distribute this software and its
> > + * documentation is hereby granted, provided that both the copyright
> > + * notice and this permission notice appear in all copies of the
> > + * software, derivative works or modified versions, and any portions
> > + * thereof, and that both notices appear in supporting documentation.
> > + *
> > + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
> > + * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
> > + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
> > + *
> > + * Carnegie Mellon requests users of this software to return to
> > + *
> > + *  Software Distribution Coordinator  or  [email protected]
> > + *  School of Computer Science
> > + *  Carnegie Mellon University
> > + *  Pittsburgh PA 15213-3890
> > + *
> > + * any improvements or extensions that they make and grant Carnegie the
> > + * rights to redistribute these changes.
> > + */
> > +
> > +/*
> > + * Stand-alone file reading package.
> > + */
> > +#include <common.h>
> > +#include <blk.h>
> > +#include <config.h>
> > +#include <exports.h>
> > +#include <asm/byteorder.h>
> > +#include <part.h>
> > +#include <malloc.h>
> > +#include <memalign.h>
> > +#include <linux/compiler.h>
> > +#include <linux/ctype.h>
> > +
> > +typedef    int32_t daddr32_t;      /* 32-bit disk address */
> > +typedef    int64_t blkcnt_t;
> > +typedef    int32_t blksize_t;
> > +
> > +#include "dinode.h"
> > +#include "stat.h"
> > +#include "dir.h"
> > +#include "fs.h"
> > +
> > +#define MAXPATHLEN 1024
> > +#define DEV_BSIZE       (1 << 9)
> > +#define MAXBSIZE        (64 * 1024)
> > +#define MAXNAMLEN       255
> > +#define MAXSYMLINKS     32
> > +#define    SOPEN_MAX       16
> > +
> > +#define    SEEK_SET        0       /* set file offset to offset */
> > +#define    SEEK_CUR        1       /* set file offset to current plus 
> > offset */
> > +#define    SEEK_END        2       /* set file offset to EOF plus offset */
> > +/*
> > + * In-core open file.
> > + */
> > +struct file {
> > +   off_t           f_seekp;        /* seek pointer */
> > +   struct fs       *f_fs;          /* pointer to super-block */
> > +   struct ufs1_dinode      f_di;           /* copy of on-disk inode */
> > +   int             f_nindir[NIADDR];
> > +                                   /* number of blocks mapped by
> > +                                      indirect block at level i */
> > +   char            *f_blk[NIADDR]; /* buffer for indirect block at
> > +                                      level i */
> > +   size_t          f_blksize[NIADDR];
> > +                                   /* size of buffer */
> > +   daddr32_t       f_blkno[NIADDR];/* disk address of block in buffer */
> > +   char            *f_buf;         /* buffer for data block */
> > +   size_t          f_buf_size;     /* size of data block */
> > +   daddr32_t       f_buf_blkno;    /* block number of data block */
> > +};
> > +
> > +int        _ufs_open(const char *);
> > +void       _ufs_close(int);
> > +int        _ufs_read(int, void *, size_t, size_t *);
> > +off_t      _ufs_seek(int, off_t, int);
> > +void       _ufs_stat(int, struct stat *);
> > +int        _ufs_readdir(int, char *);
> > +int        dread(uint32_t, uint32_t, void *, uint32_t *);
> > +
> > +static int read_inode(ufsino_t, struct file *);
> > +static int block_map(struct file *, daddr32_t, daddr32_t *);
> > +static int buf_read_file(struct file *, char **, size_t *);
> > +static int search_directory(char *, struct file *, ufsino_t *);
> > +static void        ufs_close_internal(struct file *);
> > +
> > +struct file *files[SOPEN_MAX];
> > +
> > +struct blk_desc *cur_dev;
> > +disk_partition_t *cur_pi;
> > +
> > +/*
> > + * Read a new inode into a file structure.
> > + */
> > +static int
> > +read_inode(ufsino_t inumber, struct file *fp)
> > +{
> > +   struct fs *fs = fp->f_fs;
> > +   char *buf;
> > +   size_t rsize;
> > +   int rc;
> > +
> > +   /*
> > +    * Read inode and save it.
> > +    */
> > +   buf = malloc_cache_aligned(fs->fs_bsize);
> > +   rc = dread(fsbtodb(fs, (daddr32_t)ino_to_fsba(fs, inumber)),
> > +       fs->fs_bsize, buf, &rsize);
> > +   if (rc)
> > +           goto out;
> > +   if (rsize != (size_t)fs->fs_bsize) {
> > +           rc = EIO;
> > +           goto out;
> > +   }
> > +
> > +   {
> > +           struct ufs1_dinode *dp;
> > +
> > +           dp = (struct ufs1_dinode *)buf;
> > +           fp->f_di = dp[ino_to_fsbo(fs, inumber)];
> > +   }
> > +
> > +   /*
> > +    * Clear out the old buffers
> > +    */
> > +   {
> > +           int level;
> > +
> > +           for (level = 0; level < NIADDR; level++)
> > +                   fp->f_blkno[level] = -1;
> > +           fp->f_buf_blkno = -1;
> > +           fp->f_seekp = 0;
> > +   }
> > +out:
> > +   free(buf);
> > +   return rc;
> > +}
> > +
> > +/*
> > + * Given an offset in a file, find the disk block number that
> > + * contains that block.
> > + */
> > +static int
> > +block_map(struct file *fp, daddr32_t file_block, daddr32_t *disk_block_p)
> > +{
> > +   daddr32_t ind_block_num, *ind_p;
> > +   struct fs *fs = fp->f_fs;
> > +   int level, idx, rc;
> > +
> > +   /*
> > +    * Index structure of an inode:
> > +    *
> > +    * di_db[0..NDADDR-1]   hold block numbers for blocks
> > +    *                      0..NDADDR-1
> > +    *
> > +    * di_ib[0]             index block 0 is the single indirect block
> > +    *                      holds block numbers for blocks
> > +    *                      NDADDR .. NDADDR + NINDIR(fs)-1
> > +    *
> > +    * di_ib[1]             index block 1 is the double indirect block
> > +    *                      holds block numbers for INDEX blocks for blocks
> > +    *                      NDADDR + NINDIR(fs) ..
> > +    *                      NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
> > +    *
> > +    * di_ib[2]             index block 2 is the triple indirect block
> > +    *                      holds block numbers for double-indirect
> > +    *                      blocks for blocks
> > +    *                      NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
> > +    *                      NDADDR + NINDIR(fs) + NINDIR(fs)**2
> > +    *                              + NINDIR(fs)**3 - 1
> > +    */
> > +
> > +   if (file_block < NDADDR) {
> > +           /* Direct block. */
> > +           *disk_block_p = fp->f_di.di_db[file_block];
> > +           return 0;
> > +   }
> > +
> > +   file_block -= NDADDR;
> > +
> > +   /*
> > +    * nindir[0] = NINDIR
> > +    * nindir[1] = NINDIR**2
> > +    * nindir[2] = NINDIR**3
> > +    *      etc
> > +    */
> > +   for (level = 0; level < NIADDR; level++) {
> > +           if (file_block < fp->f_nindir[level])
> > +                   break;
> > +           file_block -= fp->f_nindir[level];
> > +   }
> > +   if (level == NIADDR) {
> > +           /* Block number too high */
> > +           return EFBIG;
> > +   }
> > +
> > +   ind_block_num = fp->f_di.di_ib[level];
> > +
> > +   for (; level >= 0; level--) {
> > +           if (ind_block_num == 0) {
> > +                   *disk_block_p = 0;      /* missing */
> > +                   return 0;
> > +           }
> > +
> > +           if (fp->f_blkno[level] != ind_block_num) {
> > +                   if (fp->f_blk[level] == NULL)
> > +                           fp->f_blk[level] =
> > +                               malloc_cache_aligned(fs->fs_bsize);
> > +                   rc = dread(fsbtodb(fp->f_fs, ind_block_num),
> > +                       fs->fs_bsize, fp->f_blk[level],
> > +                       &fp->f_blksize[level]);
> > +                   if (rc)
> > +                           return rc;
> > +                   if (fp->f_blksize[level] != (size_t)fs->fs_bsize)
> > +                           return EIO;
> > +                   fp->f_blkno[level] = ind_block_num;
> > +           }
> > +
> > +           ind_p = (daddr32_t *)fp->f_blk[level];
> > +
> > +           if (level > 0) {
> > +                   idx = file_block / fp->f_nindir[level - 1];
> > +                   file_block %= fp->f_nindir[level - 1];
> > +           } else
> > +                   idx = file_block;
> > +
> > +           ind_block_num = ind_p[idx];
> > +   }
> > +
> > +   *disk_block_p = ind_block_num;
> > +   return 0;
> > +}
> > +
> > +/*
> > + * Read a portion of a file into an internal buffer.  Return
> > + * the location in the buffer and the amount in the buffer.
> > + */
> > +static int
> > +buf_read_file(struct file *fp, char **buf_p, size_t *size_p)
> > +{
> > +   struct fs *fs = fp->f_fs;
> > +   daddr32_t file_block, disk_block;
> > +   size_t block_size;
> > +   long off;
> > +   int rc;
> > +
> > +   off = blkoff(fs, fp->f_seekp);
> > +   file_block = lblkno(fs, fp->f_seekp);
> > +   block_size = dblksize(fs, &fp->f_di, file_block);
> > +
> > +   if (file_block != fp->f_buf_blkno) {
> > +           rc = block_map(fp, file_block, &disk_block);
> > +           if (rc)
> > +                   return rc;
> > +
> > +           if (fp->f_buf == NULL)
> > +                   fp->f_buf = malloc_cache_aligned(fs->fs_bsize);
> > +
> > +           if (disk_block == 0) {
> > +                   memset(fp->f_buf, 0, block_size);
> > +                   fp->f_buf_size = block_size;
> > +           } else {
> > +                   rc = dread(fsbtodb(fs, disk_block),
> > +                       block_size, fp->f_buf, &fp->f_buf_size);
> > +                   if (rc)
> > +                           return rc;
> > +           }
> > +
> > +           fp->f_buf_blkno = file_block;
> > +   }
> > +
> > +   /*
> > +    * Return address of byte in buffer corresponding to
> > +    * offset, and size of remainder of buffer after that
> > +    * byte.
> > +    */
> > +   *buf_p = fp->f_buf + off;
> > +   *size_p = block_size - off;
> > +
> > +   /*
> > +    * But truncate buffer at end of file.
> > +    */
> > +   if (*size_p > fp->f_di.di_size - fp->f_seekp)
> > +           *size_p = fp->f_di.di_size - fp->f_seekp;
> > +
> > +   return 0;
> > +}
> > +
> > +/*
> > + * Search a directory for a name and return its
> > + * i_number.
> > + */
> > +static int
> > +search_directory(char *name, struct file *fp, ufsino_t *inumber_p)
> > +{
> > +   int namlen, length, rc;
> > +   struct direct *dp, *edp;
> > +   size_t buf_size;
> > +   char *buf;
> > +
> > +   length = strlen(name);
> > +
> > +   fp->f_seekp = 0;
> > +   while (fp->f_seekp < fp->f_di.di_size) {
> > +           rc = buf_read_file(fp, &buf, &buf_size);
> > +           if (rc)
> > +                   return rc;
> > +
> > +           dp = (struct direct *)buf;
> > +           edp = (struct direct *)(buf + buf_size);
> > +           while (dp < edp) {
> > +                   if (dp->d_ino == 0)
> > +                           goto next;
> > +                   if (fp->f_fs->fs_maxsymlinklen <= 0)
> > +                           namlen = dp->d_type;
> > +                   else
> > +                           namlen = dp->d_namlen;
> > +                   if (namlen == length &&
> > +                       !strcmp(name, dp->d_name)) {
> > +                           /* found entry */
> > +                           *inumber_p = dp->d_ino;
> > +                           return 0;
> > +                   }
> > +           next:
> > +                   dp = (struct direct *)((char *)dp + dp->d_reclen);
> > +           }
> > +           fp->f_seekp += buf_size;
> > +   }
> > +   return ENOENT;
> > +}
> > +
> > +/*
> > + * Open a file.
> > + */
> > +int
> > +_ufs_open(const char *path)
> > +{
> > +   char *cp, *ncp;
> > +   ufsino_t inumber;
> > +   int rc, c, fd;
> > +   struct file *fp;
> > +   size_t buf_size;
> > +   struct fs *fs;
> > +#if 1 /* XXX symbolic links support */
> > +   char namebuf[MAXPATHLEN+1], *buf = NULL;
> > +   ufsino_t parent_inumber;
> > +   int nlinks = 0;
> > +#endif
> > +
> > +   for (fd = 0; fd < SOPEN_MAX; fd++)
> > +           if (files[fd] == NULL)
> > +                   goto gotfree;
> > +   return -1;
> > +gotfree:
> > +
> > +   /* allocate file system specific data structure */
> > +   fp = malloc_cache_aligned(sizeof(struct file));
> > +   memset(fp, 0, sizeof(struct file));
> > +   files[fd] = fp;
> > +
> > +   /* allocate space and read super block */
> > +   fs = malloc_cache_aligned(SBSIZE);
> > +   fp->f_fs = fs;
> > +   rc = dread(SBLOCK, SBSIZE, (char *)fs, &buf_size);
> > +   if (rc)
> > +           goto out;
> > +
> > +   if (buf_size != SBSIZE || fs->fs_magic != FS_MAGIC ||
> > +       fs->fs_bsize > MAXBSIZE || fs->fs_bsize < sizeof(struct fs)) {
> > +           rc = EINVAL;
> > +           goto out;
> > +   }
> > +
> > +   /*
> > +    * Calculate indirect block levels.
> > +    */
> > +   {
> > +           int mult;
> > +           int level;
> > +
> > +           mult = 1;
> > +           for (level = 0; level < NIADDR; level++) {
> > +                   mult *= NINDIR(fs);
> > +                   fp->f_nindir[level] = mult;
> > +           }
> > +   }
> > +
> > +   inumber = ROOTINO;
> > +   if ((rc = read_inode(inumber, fp)) != 0)
> > +           goto out;
> > +
> > +   cp = (char *)path;
> > +   while (*cp) {
> > +
> > +           /*
> > +            * Remove extra separators
> > +            */
> > +           while (*cp == '/')
> > +                   cp++;
> > +           if (*cp == '\0')
> > +                   break;
> > +
> > +           /*
> > +            * Check that current node is a directory.
> > +            */
> > +           if ((fp->f_di.di_mode & IFMT) != IFDIR) {
> > +                   rc = ENOTDIR;
> > +                   printf("%s: cp \"%s\" != IFDIR\n", __func__, cp);
> > +                   goto out;
> > +           }
> > +
> > +           /*
> > +            * Get next component of path name.
> > +            */
> > +           {
> > +                   int len = 0;
> > +
> > +                   ncp = cp;
> > +                   while ((c = *cp) != '\0' && c != '/') {
> > +                           if (++len > MAXNAMLEN) {
> > +                                   rc = ENOENT;
> > +                                   goto out;
> > +                           }
> > +                           cp++;
> > +                   }
> > +                   *cp = '\0';
> > +           }
> > +
> > +           /*
> > +            * Look up component in current directory.
> > +            * Save directory inumber in case we find a
> > +            * symbolic link.
> > +            */
> > +#if 1 /* XXX symbolic links support */
> > +           parent_inumber = inumber;
> > +#endif
> > +           rc = search_directory(ncp, fp, &inumber);
> > +           *cp = c;
> > +           if (rc) {
> > +                   printf("%s: search_directory() %d failed\n", __func__,
> > +                       inumber);
> > +                   goto out;
> > +           }
> > +
> > +           /*
> > +            * Open next component.
> > +            */
> > +           if ((rc = read_inode(inumber, fp)) != 0) {
> > +                   printf("%s: reading inode %d failed\n", __func__,
> > +                       inumber);
> > +                   goto out;
> > +           }
> > +
> > +#if 1 /* XXX symbolic links support */
> > +           /*
> > +            * Check for symbolic link.
> > +            */
> > +           if ((fp->f_di.di_mode & IFMT) == IFLNK) {
> > +                   u_int64_t link_len = fp->f_di.di_size;
> > +                   size_t len;
> > +
> > +                   len = strlen(cp);
> > +
> > +                   if (link_len + len > MAXPATHLEN ||
> > +                       ++nlinks > MAXSYMLINKS) {
> > +                           rc = ENOENT;
> > +                           goto out;
> > +                   }
> > +
> > +                   memcpy(&namebuf[link_len], cp, len + 1);
> > +
> > +                   if (link_len < fs->fs_maxsymlinklen) {
> > +                           memcpy(namebuf, fp->f_di.di_shortlink, 
> > link_len);
> > +                   } else {
> > +                           /*
> > +                            * Read file for symbolic link
> > +                            */
> > +                           size_t buf_size;
> > +                           daddr32_t disk_block;
> > +                           struct fs *fs = fp->f_fs;
> > +
> > +                           if (!buf)
> > +                                   buf = 
> > malloc_cache_aligned(fs->fs_bsize);
> > +                           rc = block_map(fp, (daddr32_t)0, &disk_block);
> > +                           if (rc)
> > +                                   goto out;
> > +
> > +                           rc = dread(fsbtodb(fs, disk_block),
> > +                               fs->fs_bsize, buf, &buf_size);
> > +                           if (rc)
> > +                                   goto out;
> > +
> > +                           memcpy(namebuf, buf, link_len);
> > +                   }
> > +
> > +                   /*
> > +                    * If relative pathname, restart at parent directory.
> > +                    * If absolute pathname, restart at root.
> > +                    */
> > +                   cp = namebuf;
> > +                   if (*cp != '/')
> > +                           inumber = parent_inumber;
> > +                   else
> > +                           inumber = ROOTINO;
> > +
> > +                   if ((rc = read_inode(inumber, fp)) != 0)
> > +                           goto out;
> > +           }
> > +#endif
> > +   }
> > +
> > +   /*
> > +    * Found terminal component.
> > +    */
> > +out:
> > +#if 1 /* XXX symbolic links support */
> > +   if (buf)
> > +           free(buf);
> > +#endif
> > +   if (rc) {
> > +           ufs_close_internal(fp);
> > +           files[fd] = NULL;
> > +           fd = -1;
> > +   }
> > +   return fd;
> > +}
> > +
> > +void
> > +_ufs_close(int fd)
> > +{
> > +   if (files[fd] != NULL)
> > +           ufs_close_internal(files[fd]);
> > +   files[fd] = NULL;
> > +}
> > +
> > +static void
> > +ufs_close_internal(struct file *fp)
> > +{
> > +   int level;
> > +   for (level = 0; level < NIADDR; level++) {
> > +           if (fp->f_blk[level])
> > +                   free(fp->f_blk[level]);
> > +   }
> > +   if (fp->f_buf)
> > +           free(fp->f_buf);
> > +   free(fp->f_fs);
> > +   free(fp);
> > +}
> > +
> > +/*
> > + * Copy a portion of a file into kernel memory.
> > + * Cross block boundaries when necessary.
> > + */
> > +int
> > +_ufs_read(int fd, void *start, size_t size, size_t *resid)
> > +{
> > +   struct file *fp = files[fd];
> > +   char *buf, *addr = start;
> > +   size_t csize, buf_size;
> > +   int rc = 0;
> > +
> > +   while (size != 0) {
> > +           if (fp->f_seekp >= fp->f_di.di_size)
> > +                   break;
> > +
> > +           rc = buf_read_file(fp, &buf, &buf_size);
> > +           if (rc)
> > +                   break;
> > +
> > +           csize = size;
> > +           if (csize > buf_size)
> > +                   csize = buf_size;
> > +
> > +           memcpy(addr, buf, csize);
> > +
> > +           fp->f_seekp += csize;
> > +           addr += csize;
> > +           size -= csize;
> > +   }
> > +   if (resid)
> > +           *resid = size;
> > +   return rc;
> > +}
> > +
> > +off_t
> > +_ufs_seek(int fd, off_t offset, int where)
> > +{
> > +   struct file *fp = files[fd];
> > +   switch (where) {
> > +   case SEEK_SET:
> > +           fp->f_seekp = offset;
> > +           break;
> > +   case SEEK_CUR:
> > +           fp->f_seekp += offset;
> > +           break;
> > +   case SEEK_END:
> > +           fp->f_seekp = fp->f_di.di_size - offset;
> > +           break;
> > +   default:
> > +           return -1;
> > +   }
> > +   return fp->f_seekp;
> > +}
> > +
> > +void
> > +_ufs_stat(int fd, struct stat *sb)
> > +{
> > +   struct file *fp = files[fd];
> > +   /* only important stuff */
> > +   sb->st_mode = fp->f_di.di_mode;
> > +   sb->st_size = fp->f_di.di_size;
> > +}
> > +
> > +int
> > +_ufs_readdir(int fd, char *name)
> > +{
> > +   struct file *fp = files[fd];
> > +   struct direct *dp, *edp;
> > +   size_t buf_size;
> > +   int rc, namlen;
> > +   char *buf;
> > +
> > +   if (name == NULL)
> > +           fp->f_seekp = 0;
> > +   else {
> > +                   /* end of dir */
> > +           if (fp->f_seekp >= fp->f_di.di_size) {
> > +                   *name = '\0';
> > +                   return -1;
> > +           }
> > +
> > +           do {
> > +                   if ((rc = buf_read_file(fp, &buf, &buf_size)) != 0)
> > +                           return rc;
> > +
> > +                   dp = (struct direct *)buf;
> > +                   edp = (struct direct *)(buf + buf_size);
> > +                   while (dp < edp && dp->d_ino == 0)
> > +                           dp = (struct direct *)((char *)dp + 
> > dp->d_reclen);
> > +                   fp->f_seekp += buf_size -
> > +                       ((u_int8_t *)edp - (u_int8_t *)dp);
> > +           } while (dp >= edp);
> > +
> > +           if (fp->f_fs->fs_maxsymlinklen <= 0)
> > +                   namlen = dp->d_type;
> > +           else
> > +                   namlen = dp->d_namlen;
> > +           strncpy(name, dp->d_name, namlen + 1);
> > +
> > +           fp->f_seekp += dp->d_reclen;
> > +   }
> > +
> > +   return 0;
> > +}
> > +
> > +/* 
> > _________________________________________________________________________ */
> > +int
> > +dread(uint32_t off, uint32_t bsize, void *buf, uint32_t *bread)
> > +{
> > +   u_long rv;
> > +   uint32_t blks = bsize / cur_pi->blksz;
> > +
> > +   if (!cur_dev)
> > +           return -1;
> > +
> > +   rv = blk_dread(cur_dev, cur_pi->start + off, blks, buf);
> > +   if (rv != blks)
> > +           return -1;
> > +   if (bread)
> > +           *bread = rv * cur_pi->blksz;
> > +   return 0;
> > +}
> > +int
> > +ffs_set_blk_dev(struct blk_desc *dev_desc, disk_partition_t *info)
> > +{
> > +   struct fs *fs;
> > +   uint32_t br;
> > +   cur_dev = dev_desc;
> > +   cur_pi = info;
> > +   int i;
> > +   for (i = 0; i < SOPEN_MAX; i++)
> > +           files[i] = NULL;
> > +
> > +   /* allocate space and read super block */
> > +   fs = malloc_cache_aligned(SBSIZE);
> > +
> > +   /* read super block */
> > +   if (dread(SBLOCK, SBSIZE, fs, &br) < 0) {
> > +           printf("%s: failed to read super block\n", __func__);
> > +           goto errout;
> > +   }
> > +   if (br != SBSIZE) {
> > +           printf("%s: failed to read super block, bytesread %x\n",
> > +               __func__, br);
> > +           goto errout;
> > +   }
> > +   if (fs->fs_magic != FS_MAGIC) {
> > +           printf("%s: failed to read super block, fs_magic %x\n",
> > +               __func__, (u_int)fs->fs_magic);
> > +           goto errout;
> > +   }
> > +   if (fs->fs_bsize > MAXBSIZE) {
> > +           printf("%s: failed to read super block, fs_bsize %x >\n",
> > +               __func__, (u_int)fs->fs_bsize);
> > +           goto errout;
> > +   }
> > +   if (fs->fs_bsize < sizeof(struct fs)) {
> > +           printf("%s: failed to read super block, fs_bsize %x <\n",
> > +               __func__, (u_int)fs->fs_bsize);
> > +           goto errout;
> > +   }
> > +   if (fs)
> > +           free(fs);
> > +   return 0;
> > +errout:
> > +   if (fs)
> > +           free(fs);
> > +   cur_dev = NULL;
> > +   cur_pi = NULL;
> > +   return -1;
> > +}
> > +
> > +/* 
> > _________________________________________________________________________ */
> > +#define lsrwx(mode,s) \
> > +   putc((mode) & S_IROTH? 'r' : '-'); \
> > +   putc((mode) & S_IWOTH? 'w' : '-'); \
> > +   putc((mode) & S_IXOTH? *(s): (s)[1]);
> > +
> > +static void
> > +ls(char *name, struct stat *sb)
> > +{
> > +   putc("-fc-d-b---l-s-w-"[(sb->st_mode & S_IFMT) >> 12]);
> > +   lsrwx(sb->st_mode >> 6, (sb->st_mode & S_ISUID? "sS" : "x-"));
> > +   lsrwx(sb->st_mode >> 3, (sb->st_mode & S_ISGID? "sS" : "x-"));
> > +   lsrwx(sb->st_mode     , (sb->st_mode & S_ISTXT? "tT" : "x-"));
> > +
> > +   printf ("\t%lu%s%s\n", (u_long)sb->st_size,
> > +       (sb->st_size > 9999999) ? "\t" : "\t\t", name);
> > +}
> > +#undef lsrwx
> > +
> > +static int
> > +ffs_stat(const char *str, struct stat *sb)
> > +{
> > +   int fd;
> > +   if ((fd = _ufs_open(str)) < 0)
> > +           return -1;
> > +   _ufs_stat(fd, sb);
> > +   _ufs_close(fd);
> > +   return 0;
> > +}
> > +
> > +int
> > +ffs_ls(const char *dir)
> > +{
> > +   struct stat sb;
> > +   char *_dir = dir ? (char *)dir : "/.";
> > +   char *p, *tp;
> > +   int dlen;
> > +   int fd;
> > +
> > +
> > +   if ((dlen = strnlen(_dir, MAXPATHLEN)) >= MAXPATHLEN)
> > +           return -1;
> > +   if (ffs_stat(_dir, &sb) < 0)
> > +           return -1;
> > +   if (!S_ISDIR(sb.st_mode)) {
> > +           ls(_dir, &sb);
> > +           return -1;
> > +   }
> > +
> > +   if ((fd = _ufs_open(_dir)) < 0)
> > +           return -1;
> > +   tp = p = malloc(MAXPATHLEN+MAXNAMLEN);
> > +   memset(p, 0, MAXPATHLEN+MAXNAMLEN);
> > +   strncpy(p, _dir,  dlen+1);
> > +   p += dlen;
> > +   *p++ = '/';
> > +   *p = '\0';
> > +   while (_ufs_readdir(fd, p) >= 0) {
> > +           if (ffs_stat(tp, &sb) < 0) {
> > +                   printf("ffs_stat %s failed\n", tp);
> > +           } else
> > +                   ls(p, &sb);
> > +   }
> > +   _ufs_close(fd);
> > +   free(tp);
> > +   return 0;
> > +}
> > +
> > +int
> > +ffs_read(char *fname, void *buf, loff_t offset, loff_t len, loff_t 
> > *actread)
> > +{
> > +   struct stat sb;
> > +   size_t tsz, br;
> > +   int fd, rv = 0;
> > +
> > +   if ((fd = _ufs_open(fname)) < 0)
> > +           return -1;
> > +   if (offset > 0)
> > +           _ufs_seek(fd, offset, SEEK_CUR);
> > +
> > +   _ufs_stat(fd, &sb);
> > +   if (len == 0 || len > sb.st_size)
> > +           tsz = sb.st_size;
> > +   else
> > +           tsz = len;
> > +   br = tsz;
> > +   printf("reading %s\n", fname);
> > +   if (_ufs_read(fd, buf, tsz, &br))
> > +           rv = -1;
> > +   if (actread)
> > +           *actread = tsz - br;
> > +
> > +   _ufs_close(fd);
> > +   return rv;
> > +}
> > +
> > +int
> > +ffs_exists(const char *fname)
> > +{
> > +   struct stat sb;
> > +   if (ffs_stat(fname, &sb) < 0)
> > +           return 0;
> > +   return 1;
> > +}
> > +
> > +int
> > +ffs_size(const char *fname, loff_t *size)
> > +{
> > +   struct stat sb;
> > +   if (ffs_stat(fname, &sb) < 0)
> > +           return -1;
> > +   printf("%s: %lubytes\n", __func__, sb.st_size);
> > +   if (size)
> > +           *size = (loff_t)sb.st_size;
> > +   return 0;
> > +}
> > +
> > +void
> > +ffs_close(void)
> > +{
> > +}
> > diff --git a/fs/fs.c b/fs/fs.c
> > index 595ff1f..0d84987 100644
> > --- a/fs/fs.c
> > +++ b/fs/fs.c
> > @@ -17,6 +17,7 @@
> >  #include <asm/io.h>
> >  #include <div64.h>
> >  #include <linux/math64.h>
> > +#include <ffs.h>
> >  
> >  DECLARE_GLOBAL_DATA_PTR;
> >  
> > @@ -163,6 +164,21 @@ static struct fstype_info fstypes[] = {
> >             .uuid = fs_uuid_unsupported,
> >     },
> >  #endif
> > +#ifdef CONFIG_CMD_FFS
> > +   {
> > +           .fstype = FS_TYPE_FFS,
> > +           .name = "ffs",
> > +           .null_dev_desc_ok = false,
> > +           .probe = ffs_set_blk_dev,
> > +           .close = ffs_close,
> > +           .ls = ffs_ls,
> > +           .exists = ffs_exists,
> > +           .size = ffs_size,
> > +           .read = ffs_read,
> > +           .write = fs_write_unsupported,
> > +           .uuid = fs_uuid_unsupported,
> > +   },
> > +#endif
> >     {
> >             .fstype = FS_TYPE_ANY,
> >             .name = "unsupported",
> > diff --git a/include/ffs.h b/include/ffs.h
> > new file mode 100644
> > index 0000000..2f086c3
> > --- /dev/null
> > +++ b/include/ffs.h
> > @@ -0,0 +1,10 @@
> > +/* XXX */
> > +#ifndef _FFS_H_
> > +#define    _FFS_H_
> > +int        ffs_ls(const char *dir);
> > +int        ffs_exists(const char *);
> > +void       ffs_close(void);
> > +int        ffs_set_blk_dev(struct blk_desc *, disk_partition_t *);
> > +int        ffs_size(const char *, loff_t *);
> > +int        ffs_read(const char *, void *, loff_t, loff_t, loff_t *);
> > +#endif     /* !_FFS_H_ */
> > diff --git a/include/fs.h b/include/fs.h
> > index 2f2aca8..d97077e 100644
> > --- a/include/fs.h
> > +++ b/include/fs.h
> > @@ -13,6 +13,7 @@
> >  #define FS_TYPE_EXT        2
> >  #define FS_TYPE_SANDBOX    3
> >  #define FS_TYPE_UBIFS      4
> > +#define FS_TYPE_FFS        5
> >  
> >  /*
> >   * Tell the fs layer which block device an partition to use for future
> > 
> > 
> 

Reply via email to