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

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

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