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. Brandon Index: sbin/newfs/newfs.c =================================================================== RCS file: /cvs/src/sbin/newfs/newfs.c,v retrieving revision 1.95 diff -u -p -u -r1.95 newfs.c --- sbin/newfs/newfs.c 22 Nov 2013 04:12:48 -0000 1.95 +++ sbin/newfs/newfs.c 16 Apr 2014 17:47:02 -0000 @@ -112,7 +112,7 @@ u_short dkcksum(struct disklabel *); int mfs; /* run as the memory based filesystem */ int Nflag; /* run without writing file system */ -int Oflag = 1; /* 0 = 4.3BSD ffs, 1 = 4.4BSD ffs, 2 = ffs2 */ +int Oflag = 2; /* 0 = 4.3BSD ffs, 1 = 4.4BSD ffs, 2 = ffs2 */ daddr_t fssize; /* file system size in 512-byte blocks */ long long sectorsize; /* bytes/sector */ int fsize = 0; /* fragment size */ Index: arch/amd64/stand/biosboot/biosboot.S =================================================================== RCS file: /cvs/src/sys/arch/amd64/stand/biosboot/biosboot.S,v retrieving revision 1.7 diff -u -p -r1.7 biosboot.S --- arch/amd64/stand/biosboot/biosboot.S 5 Jul 2011 17:38:54 -0000 1.7 +++ arch/amd64/stand/biosboot/biosboot.S 16 Apr 2014 17:22:25 -0000 @@ -108,6 +108,9 @@ * While this can be calculated as * howmany(di_size, fs_bsize) it takes us too * many code bytes to do it. + * blkskew uint8t the skew used to parse di_db[]. this is set to four by + * installboot for ffs2 (due to 64-bit blocks) and should + * be zero for ffs1. * * All of these are patched directly into the code where they are used * (once only, each), to save space. @@ -121,7 +124,7 @@ */ .globl inodeblk, inodedbl, fs_bsize_p, fsbtodb, p_offset, nblocks - .globl fs_bsize_s, force_chs + .globl fs_bsize_s, force_chs, blkskew .type inodeblk, @function .type inodedbl, @function .type fs_bsize_p, @function @@ -130,6 +133,7 @@ .type p_offset, @function .type nblocks, @function .type force_chs, @function + .type blkskew, @function /* Clobbers %ax, maybe more */ @@ -460,7 +464,8 @@ load_blocks: /* Get the next filesystem block number into %eax */ lodsl /* %eax = *(%si++), make sure 0x66 0xad */ - +blkskew = .+2 + addw $0x90, %si /* adjust %si if needed (for ffs2) */ pushal /* Save all 32-bit registers */ /* Index: arch/amd64/stand/boot/Makefile =================================================================== RCS file: /cvs/src/sys/arch/amd64/stand/boot/Makefile,v retrieving revision 1.26 diff -u -p -r1.26 Makefile --- arch/amd64/stand/boot/Makefile 28 Dec 2013 15:16:28 -0000 1.26 +++ arch/amd64/stand/boot/Makefile 16 Apr 2014 17:22:51 -0000 @@ -38,7 +38,7 @@ SRCS+= alloc.c ctime.c exit.c memcmp.c m SRCS+= close.c closeall.c cons.c cread.c dev.c disklabel.c dkcksum.c fstat.c \ lseek.c open.c read.c readdir.c stat.c SRCS+= elf32.c elf64.c loadfile.c -SRCS+= ufs.c +SRCS+= ufs.c ufs2.c .if ${SOFTRAID:L} == "yes" SRCS+= aes_xts.c explicit_bzero.c hmac_sha1.c pbkdf2.c rijndael.c sha1.c .endif Index: arch/amd64/stand/boot/conf.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/stand/boot/conf.c,v retrieving revision 1.31 diff -u -p -r1.31 conf.c --- arch/amd64/stand/boot/conf.c 18 Feb 2014 13:56:02 -0000 1.31 +++ arch/amd64/stand/boot/conf.c 16 Apr 2014 17:25:17 -0000 @@ -31,6 +31,7 @@ #include <netinet/in.h> #include <libsa.h> #include <lib/libsa/ufs.h> +#include <lib/libsa/ufs2.h> #ifdef notdef #include <lib/libsa/cd9660.h> #include <lib/libsa/fat.h> @@ -66,6 +67,8 @@ int nibprobes = nitems(probe_list); struct fs_ops file_system[] = { { ufs_open, ufs_close, ufs_read, ufs_write, ufs_seek, ufs_stat, ufs_readdir }, + { ufs2_open, ufs2_close, ufs2_read, ufs2_write, ufs2_seek, + ufs2_stat, ufs2_readdir }, #ifdef notdef { fat_open, fat_close, fat_read, fat_write, fat_seek, fat_stat, fat_readdir }, Index: arch/amd64/stand/installboot/installboot.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/stand/installboot/installboot.c,v retrieving revision 1.27 diff -u -p -r1.27 installboot.c --- arch/amd64/stand/installboot/installboot.c 13 Nov 2013 04:11:34 -0000 1.27 +++ arch/amd64/stand/installboot/installboot.c 16 Apr 2014 17:38:00 -0000 @@ -2,6 +2,7 @@ /* $NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */ /* + * Copyright (c) 2013 Pedro Martelletto * Copyright (c) 2011 Joel Sing <js...@openbsd.org> * Copyright (c) 2010 Otto Moerbeek <o...@openbsd.org> * Copyright (c) 2003 Tom Cosgrove <tom.cosgr...@arches-consulting.com> @@ -91,6 +92,7 @@ struct sym_data pbr_symbols[] = { {"_inodeblk", 4}, {"_inodedbl", 4}, {"_nblocks", 2}, + {"_blkskew", 1}, {NULL} }; @@ -104,6 +106,8 @@ struct sym_data pbr_symbols[] = { static char *loadproto(char *, long *); static int getbootparams(char *, int, struct disklabel *); static void devread(int, void *, daddr_t, size_t, char *); +static int sbchk(struct fs *, daddr_t); +static void sbread(int, daddr_t, struct fs **); static void sym_set_value(struct sym_data *, char *, u_int32_t); static void pbr_set_symbols(char *, char *, struct sym_data *); static void usage(void); @@ -412,6 +416,76 @@ devread(int fd, void *buf, daddr_t blk, } static char sblock[SBSIZE]; +static const daddr_t sbtry[] = SBLOCKSEARCH; + +static int +sbchk(struct fs *fs, daddr_t sbloc) +{ + if (verbose) + warnx("looking for superblock at %lld", sbloc); + + if (fs->fs_magic != FS_UFS2_MAGIC && fs->fs_magic != FS_UFS1_MAGIC) { + if (verbose) + warnx("bad superblock magic 0x%x", fs->fs_magic); + return (0); + } + + /* + * Looking for an FFS1 file system at SBLOCK_UFS2 will find the + * wrong superblock for file systems with 64k block size. + */ + if (fs->fs_magic == FS_UFS1_MAGIC && sbloc == SBLOCK_UFS2) { + if (verbose) + warnx("skipping ffs1 superblock at %lld", sbloc); + return (0); + } + + if (fs->fs_bsize <= 0 || + fs->fs_bsize < sizeof(struct fs) || + fs->fs_bsize > MAXBSIZE) { + if (verbose) + warnx("invalid superblock block size %d", + fs->fs_bsize); + return (0); + } + + if (fs->fs_sbsize <= 0 || fs->fs_sbsize > SBSIZE) { + if (verbose) + warnx("invalid superblock size %d", fs->fs_sbsize); + return (0); + } + + if (fs->fs_inopb <= 0) { + if (verbose) + warnx("invalid superblock inodes/block %d", + fs->fs_inopb); + return (0); + } + + if (verbose) + warnx("found valid %s superblock", + fs->fs_magic == FS_UFS2_MAGIC ? "ffs2" : "ffs1"); + + return (1); +} + +static void +sbread(int fd, daddr_t poffset, struct fs **fs) +{ + int i; + daddr_t sboff; + + for (i = 0; sbtry[i] != -1; i++) { + sboff = sbtry[i] / DEV_BSIZE; + devread(fd, sblock, poffset + sboff, SBSIZE, "superblock"); + *fs = (struct fs *)sblock; + if (sbchk(*fs, sbtry[i])) + break; + } + + if (sbtry[i] == -1) + errx(1, "couldn't find ffs superblock"); +} /* * Read information about /boot's inode, then put this and filesystem @@ -427,11 +501,13 @@ getbootparams(char *boot, int devfd, str struct fs *fs; char *buf; u_int blk, *ap; - struct ufs1_dinode *ip; + struct ufs1_dinode *ip1; + struct ufs2_dinode *ip2; int ndb; int mib[3]; size_t size; dev_t dev; + int skew; /* * Open 2nd-level boot program and record enough details about @@ -488,16 +564,7 @@ getbootparams(char *boot, int devfd, str pp = &dl->d_partitions[DISKPART(statbuf.st_dev)]; close(fd); - /* Read superblock. */ - devread(devfd, sblock, DL_SECTOBLK(dl, DL_GETPOFFSET(pp)) + SBLOCK, - SBSIZE, "superblock"); - fs = (struct fs *)sblock; - - /* Sanity-check super-block. */ - if (fs->fs_magic != FS_MAGIC) - errx(1, "Bad magic number in superblock"); - if (fs->fs_inopb <= 0) - err(1, "Bad inopb=%d in superblock", fs->fs_inopb); + sbread(devfd, DL_SECTOBLK(dl, pp->p_offset), &fs); /* Read inode. */ if ((buf = malloc(fs->fs_bsize)) == NULL) @@ -505,15 +572,25 @@ getbootparams(char *boot, int devfd, str blk = fsbtodb(fs, ino_to_fsba(fs, statbuf.st_ino)); - devread(devfd, buf, DL_SECTOBLK(dl, DL_GETPOFFSET(pp)) + blk, - fs->fs_bsize, "inode"); - ip = (struct ufs1_dinode *)(buf) + ino_to_fsbo(fs, statbuf.st_ino); - /* * Have the inode. Figure out how many filesystem blocks (not disk * sectors) there are for biosboot to load. */ - ndb = howmany(ip->di_size, fs->fs_bsize); + devread(devfd, buf, DL_SECTOBLK(dl, pp->p_offset) + blk, + fs->fs_bsize, "inode"); + if (fs->fs_magic == FS_UFS2_MAGIC) { + ip2 = (struct ufs2_dinode *)(buf) + + ino_to_fsbo(fs, statbuf.st_ino); + ndb = howmany(ip2->di_size, fs->fs_bsize); + ap = (u_int *)ip2->di_db; + skew = sizeof(u_int32_t); + } else { + ip1 = (struct ufs1_dinode *)(buf) + + ino_to_fsbo(fs, statbuf.st_ino); + ndb = howmany(ip1->di_size, fs->fs_bsize); + ap = (u_int *)ip1->di_db; + skew = 0; + } if (ndb <= 0) errx(1, "No blocks to load"); @@ -542,10 +619,10 @@ getbootparams(char *boot, int devfd, str sym_set_value(pbr_symbols, "_p_offset", pp->p_offset); sym_set_value(pbr_symbols, "_inodeblk", ino_to_fsba(fs, statbuf.st_ino)); - ap = ip->di_db; sym_set_value(pbr_symbols, "_inodedbl", ((((char *)ap) - buf) + INODEOFF)); sym_set_value(pbr_symbols, "_nblocks", ndb); + sym_set_value(pbr_symbols, "_blkskew", skew); if (verbose) { fprintf(stderr, "%s is %d blocks x %d bytes\n", @@ -555,6 +632,8 @@ getbootparams(char *boot, int devfd, str ffs(fs->fs_fsize / dl->d_secsize) - 1, DL_GETPOFFSET(pp), ino_to_fsba(fs, statbuf.st_ino), (unsigned int)((((char *)ap) - buf) + INODEOFF)); + fprintf(stderr, "expecting %d-bit fs blocks (skew %d)\n", + skew ? 64 : 32, skew); } return 0; @@ -762,6 +841,7 @@ sr_installboot(int devfd) sym_set_value(pbr_symbols, "_inodeblk", inodeblk); sym_set_value(pbr_symbols, "_inodedbl", inodedbl); sym_set_value(pbr_symbols, "_nblocks", nblocks); + sym_set_value(pbr_symbols, "_blkskew", 0); if (verbose) fprintf(stderr, "%s is %d blocks x %d bytes\n", 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 19:21:00 -0000 @@ -0,0 +1,713 @@ +/*- + * 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 software.distribut...@cs.cmu.edu + * 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; + char *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) + 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 = 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) + 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) + 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) + 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); + } + + /* + * 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) + 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; + if (fp == (struct file *)0) + 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); +} + +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) + 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 17:39:44 -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);