> On Apr 16, 2014, at 17:03, Brandon Mercer <[email protected]> wrote:
>
> On Wed, Apr 16, 2014 at 08:05:57PM +0000, Miod Vallat wrote:
>>> The other day I was doing an install in qemu-kvm and newfs was taking
>>> forever, to the tune of hours. This is similar to formatting on arm
>>> boards. In my quest to track down why, I discovered that ffs2 takes far
>>> less time to format than ffs1 (about 30 seconds for the entire disk).
>>>
>>> I've put together a diff that updates the boot blocks on amd64 to be
>>> able to boot ffs2. From there it's a one line change to make newfs
>>> format ffs2 by default. Obviously this would need to happen for other
>>> architectures as well and I'd be glad to tackle that if others see
>>> this as worthwhile. Please let me know your thoughts.
>>
>> Awesome. You've just trimmed my todolist by a few lines (-:
>> And you've done it so that you do not force UFS2 support on
>> tight-space-challenged boot blocks on other arches.
>>
>> All you need now is to merge your installboot changes into the MI
>> installboot, and we'll be able to add ffs2 support in the installer on a
>> per-platform basis.
>>
>> However, it is way too early to make ffs2 the default for newfs.
>>
>> Please commit your libsa changes at the earliest opportunity.
>
> Slightly revised diff. In my haste to make the compiler happy I likely
> broke things. This diff properly casts the pointer. This work was done
> by Pedro Martelletto for bitrig. I'll commit the diff below per miod's
> request.
>
> Index: lib/libsa/ufs2.c
> ===================================================================
> RCS file: lib/libsa/ufs2.c
> diff -N lib/libsa/ufs2.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ lib/libsa/ufs2.c 16 Apr 2014 21:43:19 -0000
> @@ -0,0 +1,712 @@
> +/*-
> + * 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 <sys/param.h>
> +#include <sys/time.h>
> +#include <sys/stat.h>
> +#include <ufs/ffs/fs.h>
> +#include <ufs/ufs/dinode.h>
> +#include <ufs/ufs/dir.h>
> +#include <lib/libkern/libkern.h>
> +
> +#include "stand.h"
> +#include "ufs2.h"
> +
> +/*
> + * In-core open file.
> + */
> +struct file {
> + off_t f_seekp; /* seek pointer */
> + struct fs *f_fs; /* pointer to super-block */
> + struct ufs2_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 */
> + daddr_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 */
> + daddr_t f_buf_blkno; /* block number of data block */
> +};
> +
> +static int read_inode(ufsino_t, struct open_file *);
> +static int block_map(struct open_file *, daddr_t, daddr_t *);
> +static int buf_read_file(struct open_file *, char **, size_t *);
> +static int search_directory(char *, struct open_file *, ufsino_t *);
> +static int ufs2_close_internal(struct file *);
> +#ifdef COMPAT_UFS
> +static void ffs_oldfscompat(struct fs *);
> +#endif
> +
> +/*
> + * Read a new inode into a file structure.
> + */
> +static int
> +read_inode(ufsino_t inumber, struct open_file *f)
> +{
> + struct file *fp = (struct file *)f->f_fsdata;
> + struct fs *fs = fp->f_fs;
> + char *buf;
> + size_t rsize;
> + int rc;
> +
> + /*
> + * Read inode and save it.
> + */
> + buf = alloc(fs->fs_bsize);
> + twiddle();
> + rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
> + fsbtodb(fs, 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 ufs2_dinode *dp;
> +
> + dp = (struct ufs2_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, fs->fs_bsize);
> + return (rc);
> +}
> +
> +/*
> + * Given an offset in a file, find the disk block number that
> + * contains that block.
> + */
> +static int
> +block_map(struct open_file *f, daddr_t file_block, daddr_t *disk_block_p)
> +{
> + struct file *fp = (struct file *)f->f_fsdata;
> + daddr_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] == (char *)0)
== NULL?
> + fp->f_blk[level] =
> + alloc(fs->fs_bsize);
> + twiddle();
> + rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
> + 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 = (d_addrt *)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 open_file *f, char **buf_p, size_t *size_p)
> +{
> + struct file *fp = (struct file *)f->f_fsdata;
> + struct fs *fs = fp->f_fs;
> + daddr_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(f, file_block, &disk_block);
> + if (rc)
> + return (rc);
> +
> + if (fp->f_buf == (char *)0)
== NULL?
> + fp->f_buf = alloc(fs->fs_bsize);
> +
> + if (disk_block == 0) {
> + bzero(fp->f_buf, block_size);
> + fp->f_buf_size = block_size;
> + } else {
> + twiddle();
> + rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
> + 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 open_file *f, ufsino_t *inumber_p)
> +{
> + struct file *fp = (struct file *)f->f_fsdata;
> + 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(f, &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 BYTE_ORDER == LITTLE_ENDIAN
> + if (fp->f_fs->fs_maxsymlinklen <= 0)
> + namlen = dp->d_type;
> + else
> +#endif
> + 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
> +ufs2_open(char *path, struct open_file *f)
> +{
> + char namebuf[MAXPATHLEN+1], *cp, *ncp, *buf = NULL;
> + ufsino_t inumber, parent_inumber;
> + int rc, c, nlinks = 0;
> + struct file *fp;
> + size_t buf_size;
> + struct fs *fs;
> +
> + /* allocate file system specific data structure */
> + fp = alloc(sizeof(struct file));
> + bzero(fp, sizeof(struct file));
> + f->f_fsdata = (void *)fp;
> +
> + /* allocate space and read super block */
> + fs = alloc(SBSIZE);
> + fp->f_fs = fs;
> + twiddle();
> + rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
> + SBLOCK_UFS2 / DEV_BSIZE, SBSIZE, (char *)fs, &buf_size);
> + if (rc)
> + goto out;
> +
> + if (buf_size != SBSIZE || fs->fs_magic != FS_UFS2_MAGIC ||
> + fs->fs_bsize > MAXBSIZE || fs->fs_bsize < sizeof(struct fs)) {
> + rc = EINVAL;
> + goto out;
> + }
> +#ifdef COMPAT_UFS
> + ffs_oldfscompat(fs);
> +#endif
> +
> + /*
> + * 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, f)) != 0)
if ((rc = read_inode(inumber, f)))?
> + goto out;
> +
> + cp = 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;
> + 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.
> + */
> + parent_inumber = inumber;
> + rc = search_directory(ncp, f, &inumber);
> + *cp = c;
> + if (rc)
> + goto out;
> +
> + /*
> + * Open next component.
> + */
> + if ((rc = read_inode(inumber, f)) != 0)
if ((rc = read_inode(inumber, f)))?
> + goto out;
> +
> + /*
> + * Check for symbolic link.
> + */
> + if ((fp->f_di.di_mode & IFMT) == IFLNK) {
> + int link_len = fp->f_di.di_size;
> + int len;
> +
> + len = strlen(cp);
> +
> + if (link_len + len > MAXPATHLEN ||
> + ++nlinks > MAXSYMLINKS) {
> + rc = ENOENT;
> + goto out;
> + }
> +
> + bcopy(cp, &namebuf[link_len], len + 1);
> +
> + if (link_len < fs->fs_maxsymlinklen) {
> + bcopy(fp->f_di.di_shortlink, namebuf,
> + (unsigned) link_len);
> + } else {
> + /*
> + * Read file for symbolic link
> + */
> + size_t buf_size;
> + daddr_t disk_block;
> + struct fs *fs = fp->f_fs;
> +
> + if (!buf)
> + buf = alloc(fs->fs_bsize);
> + rc = block_map(f, 0, &disk_block);
> + if (rc)
> + goto out;
> +
> + twiddle();
> + rc = (f->f_dev->dv_strategy)(f->f_devdata,
> + F_READ, fsbtodb(fs, disk_block),
> + fs->fs_bsize, buf, &buf_size);
> + if (rc)
> + goto out;
> +
> + bcopy((char *)buf, namebuf, (unsigned)link_len);
No need to cast buf, it was declared as char *.
> + }
> +
> + /*
> + * 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, f)) != 0)
if ((rc = read_inode(inumber, f)))?
> + goto out;
> + }
> + }
> +
> + /*
> + * Found terminal component.
> + */
> + rc = 0;
> +out:
> + if (buf)
> + free(buf, fs->fs_bsize);
> + if (rc)
> + (void)ufs2_close_internal(fp);
> +
> + return (rc);
> +}
> +
> +int
> +ufs2_close(struct open_file *f)
> +{
> + struct file *fp = (struct file *)f->f_fsdata;
> +
> + f->f_fsdata = (void *)0;
NULL?
> + if (fp == (struct file *)0)
NULL?
> + return (0);
> +
> + return (ufs2_close_internal(fp));
> +}
> +
> +static int
> +ufs2_close_internal(struct file *fp)
> +{
> + int level;
> +
> + for (level = 0; level < NIADDR; level++) {
> + if (fp->f_blk[level])
> + free(fp->f_blk[level], fp->f_fs->fs_bsize);
> + }
> + if (fp->f_buf)
> + free(fp->f_buf, fp->f_fs->fs_bsize);
> + free(fp->f_fs, SBSIZE);
> + free(fp, sizeof(struct file));
> + return (0);
> +}
> +
> +/*
> + * Copy a portion of a file into kernel memory.
> + * Cross block boundaries when necessary.
> + */
> +int
> +ufs2_read(struct open_file *f, void *start, size_t size, size_t *resid)
> +{
> + struct file *fp = (struct file *)f->f_fsdata;
> + 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(f, &buf, &buf_size);
> + if (rc)
> + break;
> +
> + csize = size;
> + if (csize > buf_size)
> + csize = buf_size;
> +
> + bcopy(buf, addr, csize);
> +
> + fp->f_seekp += csize;
> + addr += csize;
> + size -= csize;
> + }
> + if (resid)
> + *resid = size;
> + return (rc);
> +}
> +
> +/*
> + * Not implemented.
> + */
> +int
> +ufs2_write(struct open_file *f, void *start, size_t size, size_t *resid)
> +{
> +
> + return (EROFS);
> +}
Extra blank line.
> +
> +off_t
> +ufs2_seek(struct open_file *f, off_t offset, int where)
> +{
> + struct file *fp = (struct file *)f->f_fsdata;
> +
> + 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);
> +}
> +
> +int
> +ufs2_stat(struct open_file *f, struct stat *sb)
> +{
> + struct file *fp = (struct file *)f->f_fsdata;
> +
> + /* only important stuff */
> + sb->st_mode = fp->f_di.di_mode;
> + sb->st_uid = fp->f_di.di_uid;
> + sb->st_gid = fp->f_di.di_gid;
> + sb->st_size = fp->f_di.di_size;
> + return (0);
> +}
> +
> +#ifndef NO_READDIR
> +int
> +ufs2_readdir(struct open_file *f, char *name)
> +{
> + struct file *fp = (struct file *)f->f_fsdata;
> + 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(f, &buf, &buf_size)) != 0)
if ((rc = buf_read_file(f, &buf, &buf_size)))?
> + 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 BYTE_ORDER == LITTLE_ENDIAN
> + if (fp->f_fs->fs_maxsymlinklen <= 0)
> + namlen = dp->d_type;
> + else
> +#endif
> + namlen = dp->d_namlen;
> + strncpy(name, dp->d_name, namlen + 1);
> +
> + fp->f_seekp += dp->d_reclen;
> + }
> +
> + return 0;
> +}
> +#endif
> +
> +#ifdef COMPAT_UFS
> +/*
> + * Sanity checks for old file systems.
> + *
> + * XXX - goes away some day.
> + */
> +static void
> +ffs_oldfscompat(struct fs *fs)
> +{
> + int i;
> +
> + fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect); /* XXX */
> + fs->fs_interleave = max(fs->fs_interleave, 1); /* XXX */
> + if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */
> + fs->fs_nrpos = 8; /* XXX */
> + if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */
> + quad_t sizepb = fs->fs_bsize; /* XXX */
> + /* XXX */
> + fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1; /* XXX */
> + for (i = 0; i < NIADDR; i++) { /* XXX */
> + sizepb *= NINDIR(fs); /* XXX */
> + fs->fs_maxfilesize += sizepb; /* XXX */
> + } /* XXX */
> + fs->fs_qbmask = ~fs->fs_bmask; /* XXX */
> + fs->fs_qfmask = ~fs->fs_fmask; /* XXX */
> + } /* XXX */
> +}
> +#endif
> Index: lib/libsa/ufs2.h
> ===================================================================
> RCS file: lib/libsa/ufs2.h
> diff -N lib/libsa/ufs2.h
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ lib/libsa/ufs2.h 16 Apr 2014 21:41:42 -0000
> @@ -0,0 +1,38 @@
> +/*-
> + * Copyright (c) 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.
> + *
> + * @(#)ufs.h 8.1 (Berkeley) 6/11/93
> + */
> +
> +int ufs2_open(char *, struct open_file *);
> +int ufs2_close(struct open_file *);
> +int ufs2_read(struct open_file *, void *, size_t, size_t *);
> +int ufs2_write(struct open_file *, void *, size_t, size_t *);
> +int ufs2_stat(struct open_file *, struct stat *);
> +int ufs2_readdir(struct open_file *, char *);
> +off_t ufs2_seek(struct open_file *, off_t, int);
>